Machine Learning, NLP

Извлечение структурированной информации с помощью Yargy-парсера

Время прочтения: 3 мин.

В части задачи NER (извлечении именованных сущностей) для текстов на русском языке, многим знакома библиотека natasha, обладающая большим набором встроенных правил (экстракторов) для извлечения отдельных типов сущностей (Адреса, Имена и т.д.) из текста.  Но бывают случаи, когда необходимо создать новое уникальное правило, для извлечения данных и иметь для этого понятный и гибкий инструмент на Python. В таком случае Yargy-парсер является хорошим решением.

Данная библиотека является частью большого проекта Natasha и, собственно, основой библиотеки natasha. Работа Yargy-парсера основана на контекстно-свободных грамматиках и словарях.  В Yargy-парсере реализован алгоритм Эрли, временная сложность которого Θ(n3).

Yargy-парсер использует словари и правила для извлечения структурированной информации из текстов на русском языке. Правила (rule) состоят из предикатов. Yargy-парсер имеет множество встроенных предикатов таких как:

is_capitalized — слово начинается с большой буквы,

is_single — слово в единственном числе, gram (X), где X – это граммема.

Yargy-парсер использует библиотеку pymorphy для определения формы слова, gram(ADJF’) – означает, что слово является прилагательным. У парсера есть метод findall, который возвращает список-результат сопоставлений.

В качестве примера решим задачу определения в тексте заказа наименований напитков. Перечень напитков определим в правиле R_1 через предикат dictionary, перед названием напитка должно идти прилагательное.

from yargy import Parser, rule
from yargy.predicates import gram, dictionary

R_1 = rule (gram('ADJF'),
    dictionary({'сок', 'морс', 'компот'}))
parser = Parser(R_1)
text = 'В заказ на доставку входили апельсиновый сок, вишнёвый морс и абрикосовый компот.'
for match in parser.findall(text):
    print ([x.value for x in match.tokens])

Результат:

['апельсиновый', 'сок']
['вишнёвый', 'морс']
['абрикосовый', 'компот']

Попробуем отыскать в тексте обращения информацию о заказе.  В данном случае ищем все последовательности, начинающиеся со слова «заказ», после чего должен идти символ или слово «номер» и, непосредственно, сам номер.

from yargy import Parser, rule, and_
from yargy.predicates import gram, dictionary, type as t1

INT = t1('INT')
R_1 = rule (dictionary({'заказ'}), dictionary({'номер', '№'}), INT.repeatable())
parser = Parser(R_1)
text = '''
Просьба дать обратную связь по статусу заказа номер 12535.
В личном кабинете третий день стасус "Ожидает отправки".
Заказ № 54321 уже получен.
'''
for match in parser.findall(text):
    print ([x.value for x in match.tokens])

Результат будет следующим:

['заказа', 'номер', '12535']
['Заказ', '№', '54321']

А теперь решим задачу извлечения из текста свойств банковской карты – это тип карты дебетовая или кредитная, а также классическая, золотая или платиновая.  Для распознавания различных вариации для поиска используем газеттир типа morph_pipeline, Конструктор fact описывает объект-результат интерпретации (interpritation), в нашем случае «Card» (банковская карта), где tarif и type – атрибуты данного объекта.

from yargy import rule, or_
from yargy.predicates import gram, eq
from yargy.interpretation import fact
from yargy.pipelines import morph_pipeline

Card = fact('Card', ['tarif','type'])
classic_tatif = morph_pipeline(['класс', 'классич', 'классическая']).interpretation(Card.tarif.const('классическая'))
gold_tatif = morph_pipeline(['золот', 'золотая']).interpretation(Card.tarif.const('золотая'))
platinum_tatif = morph_pipeline(['платин', 'платинов', 'платиновая']).interpretation(Card.tarif.const('платиновая'))
cr_c = morph_pipeline(['кр', 'кред', 'кредитная']).interpretation(Card.type.const('кредитная'))
deb_c = morph_pipeline(['деб', 'дебет', 'дебетовая']).interpretation(Card.type.const('дебетовая'))
short_c = morph_pipeline(['к', 'карт', 'карта'])
CARD = rule(or_(classic_tatif, gold_tatif, platinum_tatif),or_(cr_c, deb_c), eq('.').optional(), short_c, eq('.').optional()).interpretation(Card)
parser = Parser(CARD)

text = '''
Клиенту была выдана классич дебетовая карта
'''
for match in parser.findall(text):
    print (match.fact)

Результат будет следующим:

Card(tarif='классическая', type='дебетовая')

Yargy-парсер — это мощный инструмент для извлечения структурированной информации из русско-язычных текстов. Вместе с тем, парсер реализован на python, что делает его использование в проекте чаще всего более удобным, в сравнении с аналогами, такими, так Tomita-парсер от Yandex.

Советуем почитать