[Python, Программирование, Машинное обучение] NLP: ВЫДЕЛЯЕМ ФАКТЫ ИЗ ТЕКСТОВ С ПОМОЩЬЮ ТОМИТА-ПАРСЕРА

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
17-Мар-2021 12:32

NLP - natural language processingБольшая часть данных в мире не структурирована – это просто тексты на русском или на любом другом языке. Извлеченные факты из таких текстов могут представлять особый интерес для бизнеса, поэтому подобные задачи возникают сплошь и рядом. Этим вопросом занимается отдельное направление искусственного интеллекта: обработка естественного языка, тот самый NLP (Natural Language Processing).
Существует много способов выделить факты из текста и у всех свои плюсы и минусы:
  • Регулярные выражения
Высокая скорость работы и стабильность нивелируется сложностью синтаксиса и низким покрытием.
  • Нейронные сети
Модно, хорошее качество при обучении на большой выборке, однако для работы требуется много разметки, при этом каждая новая задача требует новой разметки.
  • КС-грамматики
Предсказуемость результата, легко писать правила, но сложно запускать в ПРОМеВ этой статье мы поговорим о последних, а именно о Томита – парсере, инструменте с открытым исходным кодом, разработанном в Яндексе, а также в рамках статьи, разберемся как это работает на конкретном примере. Итак, возьмем для примера абстрактные неструктурированные данные в виде наименований платежных поручений, и постараемся извлечь из них некоторые факты, например, назначение платежа, адрес и период:Исходный текстОплата за аренду торгового места №2 по адресу ул Маршала Жукова,15 за июнь 2020г., в сумме 5400,00 рПеречисление денежных средств на Шустрик У.С. в субаренду автомобиля в сумме 15299,00 руб, без НДСОплата за найм общежития №575 по адресу пр Обуховской обороны, 145 от 17.09.2020 за февраль 2020 года, с ндс 18% — 5300 рублей.Оплата за аренду нежилого помещения по адресу Малая Садовая ул, 23, договор 51 от 01.09.2020 — 7500 рублей.Частичная оплата по Договору аренды №1-03а от 01.07.2020 за аренду помещения по проспекту Дальневосточный 211 в октябре 2020 Сумма 23000 в т.ч.НДС(18%)Что такое Томита-парсер?Томита-парсер – это инструмент для извлечения структурированных данных (фактов) из русского текста, позволяющий создавать и быстро прототипировать систему извлечения фактов с помощью шаблонов (контекстно-свободных грамматик) и словарей ключевых слов. Исходный код проекта открыт и размещен на GitHub, собственно отсюда мы скачиваем проект и проводим сборку для дальнейшей работы.Где можно использовать Томита-парсер?
  • Обработка транзакций – аренда, покупка
  • Обработка транскрипций звонков – выставление задач
  • Новостной мониторинг – оценка состояния кредитующейся компании
  • Парсинг текста резюме – автоматизация выделения навыка и опытов кандидата
  • Парсинг текста судебных дел
Как работает Томита-парсер?Томита-парсер распространяется в виде консольной утилиты, получает на вход текст на естественном языке и далее с помощью словарей и грамматик, составленных пользователем, преобразует этот текст в набор структурированных данных. Сразу отметим, что у Яндекса есть очень подробная документация, а также видеокурс, который позволяет осуществить быстрый старт в Томитный мир.Для запуска необходима сама программа tomitaparser.exe (рекомендации по сборке см. здесь) и следующие файлы:
  • config.proto — конфигурационный файл парсера. Сообщает парсеру всю информацию о том, где искать все остальные файлы и как их интерпретировать. Этот файл обязательный и выступает в роли единственного аргумента для tomitaparser.exe;
  • dic.gzt – корневой словарь. Содержит перечень всех используемых в проекте словарей и грамматик. Другими словами, это некий агрегатор, который собирает все, что создается в рамках проекта. Без этого файла парсер работать не будет;
  • mygram.cxx – грамматика. Содержит набор правил, которые описывают текстовые цепочки. Таких файлов может быть несколько. Взаимодействует с парсером через корневой словарь;
  • facttypes.proto – описание типов фактов;
  • kwtypes.proto – описание типов ключевых слов. Нужен, если в проекте создаются новые типы ключевых слов.
Все эти файлы необходимо начинать с явного указания кодировки utf8 везде, где планируется использовать кириллические символы (русский текст).Корневой словарьНачинаем с создания корневого словаря «dic.gzt», где выполняем импорт служебных файлов и грамматики, которую мы создадим чуть позже.
encoding "utf8"; // явно указываем кодировку
// импортируем зашитые в парсер файлы с базовыми типами, используемыми в словарях и грамматиках
import "base.proto";
import "articles_base.proto";
// оставляем ссылку на нашу грамматику
TAuxDicArticle "payment" {
    key = { "tomita:mygram.cxx" type=CUSTOM }
};
ГрамматикаДля создания своей грамматики разберемся с простейшими правилами и понятиями. Томитные грамматики работают с цепочками, где грамматика — это набор правил, которые описывают цепочки слов в тексте. Грамматика пишется на специальном формальном языке. Структурно правило разделяется символом «->» на левую и правую части. В левой части указывается один терминал, в правой – последовательность терминалов и нетерминалов. Нетерминал строится из терминалов и должен хотя бы один раз встретиться в правой части правила. Если нетерминал встречается только в левой части это означает вершину грамматики. В роли терминалов выступают названия частей речи (Noun, Verb, Adj), символы (Comma, Punct, Ampersand, PlusSign) и леммы. Полный перечень терминалов см. по ссылке.  Правая часть правила может сопровождаться операторами. Например, оператор «+»после (не)терминала означает, что символ повторяется один или более раз. Этот и другие операторы подробно описаны в документации.Для наложения ограничений на (не)терминал используются специальные пометы, которые уточняют свойства (не)терминала, например, определение регистра символов или связей по роду и падежу между словами. Записываются пометы после (не)терминала в угловых скобках «< >» через запятую. С полным перечнем ограничений-помет можно ознакомится по ссылке. Теперь, когда мы обладаем необходимым теоретическим минимумом создадим в папке с парсером файл формата «cxx», где мы будем описывать свою грамматику – «mygram.cxx». Ссылку на этот файл мы уже оставили в корневом словаре. Для начала создадим правило для выделения назначения платежа. В нашем случае наименование оплаченного объекта — это существительное, перед которым может стоять прилагательное, стоящее за словами «аренда», «субаренда», «найм».
#encoding "utf8" // явно указываем кодировку
// оператор "|" работает аналогично оператору "или"
Rent -> 'аренда' | 'субаренда' | 'найм';
// оператор "" означает, что символ может встречаться в тексте 0 или более раз
// помета <gnc-agr[1]> говорит о том, что прилагательное должно быть согласовано с существительным по роду, числу и падежу
Purpose -> Rent Adj<gnc-agr[1]> Noun<gnc-agr[1]>;
Далее нам нужен нетерминал для распознавания адреса. Как правило, название улиц состоит из прилагательного согласованного с дескриптором улицы, например, Московский проспект. Или это может быть именная группа, например,улица Красных зорь.
// в нетерминале StreetW указываем названия дескрипторов улицы, а в StreetAbbr - перечисляем известные сокращения
StreetW -> 'улица' | 'проспект' | 'шоссе' | 'линия';
StreetAbbr -> 'ул' | 'пр' | 'просп' | 'пр-т' | 'ш';
// объединяем два нетерминала в один нетерминал StreetDescr, который будет обозначать либо полнозначную лемму StreetW либо сокращение StreetAbbr
StreetDescr -> StreetW | StreetAbbr;
StreetNameNoun -> (Adj<gnc-agr[1]>) Word<gnc-agr[1], rt> (Word<gram="род">);
StreetNameAdj -> Adj<h-reg1> Adj*;
Нетерминалом «StreetNameNoun» мы описали названия улиц, выраженных существительным. Основным элементом в данной цепочке выступает слово, для этого, обозначаем его пометой «<rt>». Перед ним опционально может стоять или не стоять прилагательное, согласованное по роду, числу и падежу. После основного слова может стоять или не стоять слово в родительном падеже, например, пр. Обуховской обороны. Чтобы указать на то, что прилагательное и слово в родительном падеже слева и справа от основного текста являются опциональными, т.е. не обязательными, используем оператор «()». Нетерминал «StreetNameAdj» описывает названия улиц, выраженных прилагательным. Первое прилагательное в такой цепочке начинается с большой буквы. Добиваемся этого результата благодаря помете «<h-reg1>». Далее может встречаться еще некоторое количество прилагательных, для этого применяем оператор «*». Переходим к описанию правил, определяющих адрес.
Address -> StreetDescr StreetNameNoun<gram="род", h-reg1>;
Address -> StreetDescr StreetNameNoun<gram="им", h-reg1>;
Address -> StreetNameAdj<gnc-agr[1]> StreetW<gnc-agr[1]>;
Address -> StreetNameAdj StreetAbbr;
Первая цепочка начинается с дескриптора улицы и далее в родительном падеже с большой буквы идет название улицы. Второе правило аналогично первому с той лишь разницей, что название улицы после дескриптора идет в именительном падеже. Третье и четвертое правила для названий улиц, выраженных прилагательным. Теперь нам нужен нетерминал для выделения периода. Период состоит из месяца и года, поэтому нам понадобиться список месяцев. Добавляем в корневой словарь соответствующую статью:
// добавляем список месяцев в корневой словарь «dic.gzt»
TAuxDicArticle "month" {
    key = { "январь" | "февраль" | "март" | "апрель" | "май" | "июнь" | "июль" | "август" | "сентябрь" | "октябрь" | "ноябрь" | "декабрь" }
};
В файл с грамматикой добавляем следующее:
Month -> Noun<kwtype="month">;
Year -> AnyWord<wff=/[1-2]?[0-9]{1,3}г?\.?/>;
Period -> Month Year;
Пометка «kwtype» означает, что существительное ограничено статьей «month» в корневом словаре, а благодаря регулярным выражениям мы выделяем год как число от 0 до 2999 с возможными «г» или «г.» в конце. Переходим к определению корневого нетерминала, который соберет вместе все созданные ранее правила. Корневой нетерминал назовем «Result» и составим несколько возможных вариантов:
Result -> Purpose AnyWord* Address AnyWord* Period;
Result -> Purpose AnyWord* Address;
Result -> Purpose;
Терминал «AnyWord» с оператором «*» означает, что между соседними нетерминалами может встречаться любая последовательность символов 0 или более раз. В первой цепочке встречаются все выделенные нами атрибуты: назначение, адрес и период. Во второй: назначение и адрес, а в третьей только назначение.ФактыНа данном этапе мы научили парсер выделять цепочки слов в тексте. Для извлечения фактов из полученных цепочек создаем отдельный файл – «facttypes.proto» и сразу же добавляем в корневой словарь «dic.gzt» строчку с импортом (помним, что корневой словарь- это агрегатор всего, что создается в проекте).
import "facttypes.proto"; // импортируем в словарь «dic.gzt» факты
В файле «facttypes.proto» определяем новый факт «Payment» и добавляем три атрибута (поля): назначение, адрес и период платежа. Запишем в файл следующее:
// импорт базовых типов
import "base.proto";
import "facttypes_base.proto";
message Payment: NFactType.TFact {
    required string Purpose = 1;
    optional string Address = 2;
    optional string Period = 3;
};
Факт «Payment» наследуется от базового типа «NFactType.TFact», а «required» и «optional» означает, что атрибут является обязательными или опциональным соответственно. Для того, чтобы интерпретировать подцепочку в факт, необходимо написать слово «interp» и после него в скобках указать имя факта и имя поля, в которое должна попасть подцепочка. Теперь внесем изменения в корневые правила грамматики, добавив процедуру интерпретации.
// подцепочка «Purpose» интерпретируется в поле «Purpose» факта «Payment»
// подцепочка «Address» интерпретируется в поле «Address» факта «Payment»
// подцепочка «Period» интерпретируется в поле «Period» факта «Payment»
Result -> Purpose interp(Payment.Purpose) AnyWord* Address interp(Payment.Address) AnyWord* Period interp(Payment.Period);
Result -> Purpose interp(Payment.Purpose) AnyWord* Address interp(Payment.Address);
Result -> Purpose interp(Payment.Purpose);
Конфигурационный файлДалее создаем конфигурационный файл и сообщаем парсеру, где искать исходный текст, куда записывать результат, какие грамматики использовать и какие факты извлекать.
encoding "utf8"; // явно указываем кодировку
TTextMinerConfig {
    // указываем корневой словарь
    Dictionary = "dic.gzt";
    // входные данные
    Input = {File = "input.txt"}
    // указываем куда записывать результат работы парсера
    Output = {File = "output.txt"
            Format = text}
    // грамматики, которые будут использоваться при парсинге
    Articles = [
        { Name = "payment" }
        ]
    // факты, которые извлекаем
    Facts = [
        { Name = "Payment" }
        ]
    // показать отладочный вывод с результатами работы грамматики
    PrettyOutput = "pretty.html"
}
Запуск парсераЗапускается парсер из командной строки:
> tomitaparser.exe config.proto
В файл «input.txt» мы поместили исходный текст, размещенный в самом начале статьи. После работы парсер записал результат в файл «output.txt»:
Оплата за аренду торгового места № 2 по адресу ул Маршала Жукова , 15 за июнь 2020г . , в сумме 5400,00 р
    Payment
    {
        Purpose = аренда торгового места
        Address = ул Маршала Жукова
        Period = июнь 2020г
    }
Перечисление денежных средств на Шустрик У. С. в субаренду автомобиля в сумме 15299,00 руб , без НДС
    Payment
    {
        Purpose = субаренда автомобиля
    }
Оплата за найм общежития № 575 по адресу пр Обуховской обороны , 145 от 17.09.2020 за февраль 2020 года , с ндс 18% - 5300 рублей .
    Payment
    {
        Purpose = найм общежития
        Address = пр Обуховской обороны
        Period = февраль 2020
    }
Оплата за аренду нежилого помещения по адресу Малая Садовая ул , 23 , договор 51 от 01.09.2020 - 7500 рублей .
    Payment
    {
        Purpose = аренда нежилого помещения
        Address = Садовая ул
    }
Частичная оплата по Договору аренды № 1-03а от 01.07.2020 за аренду помещения по проспекту Дальневосточный 211 в октябре 2020 Сумма 23000 в т.ч.НДС ( 18% )
    Payment
    {
        Purpose = аренда помещения
        Address = проспект Дальневосточный
        Period = октябрь 2020
    }
Извлечение фактов из естественного языка довольно нетривиальная задача в IT мире по сей день. Теперь в наших руках появился еще один доступный инструмент. Как видите, создать свою первую грамматику можно довольно легко, потратив при этом немного времени на изучение, т.к. по Томите приведена подробная и исчерпывающая документация. Тем не менее, качество выделенных фактов сильно зависит от самого разработчика и его знаний в экспертной области.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_python, #_programmirovanie (Программирование), #_mashinnoe_obuchenie (Машинное обучение), #_nlp, [url=https://torrents-local.xyz/search.php?nm=%23_tomita_–_parser&to=0&allw=0&o=1&s=0&f%5B%5D=820&f%5B%5D=959&f%5B%5D=958&f%5B%5D=872&f%5B%5D=967&f%5B%5D=954&f%5B%5D=885&f%5B%5D=882&f%5B%5D=863&f%5B%5D=881&f%5B%5D=860&f%5B%5D=884&f%5B%5D=865&f%5B%5D=873&f%5B%5D=861&f%5B%5D=864&f%5B%5D=883&f%5B%5D=957&f%5B%5D=859&f%5B%5D=966&f%5B%5D=956&f%5B%5D=955]#_tomita_–_parser (Томита – парсер)[/url], #_python, #_programmirovanie (
Программирование
)
, #_mashinnoe_obuchenie (
Машинное обучение
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 13-Май 14:23
Часовой пояс: UTC + 5