Далее будут кратко описаны три модели. Две модели для частичной разметки соответствуют последовательным экспериментам по выбору общей архитектуры. Условно они называются Model1 и Ensemble. Модель лемматизации общая, ансамбль для нее я не делал, хотя это безусловно возможно и даже желательно.
Исходные тексты можно найти на гитхабе:
https://github.com/Koziev/MorphoRuEval2017Подбор дискриминирующих их признаков
В основе построения всех моделей лежит стремление избежать feature engineering - ручного подбора признаков, по которым классификаторы принимают решение.
Для символьной сеточной модели вопрос выбора фич вообще не возникает, так как эта модель работает с минимальным структурным элементом данных и сама выбирает внутренние представления данных, минимизирующие невязку. Это делает char-level сетки очень простыми и удобными в плане автоматизации, хотя и лишает нас простой возможности увидеть те структуры, которые в итоге использует классификатор для определения частей речи и других морфологических признаков.
Для ансамбля применен немного другой подход, впрочем тоже сводящийся к символьному представлению слов. Если посмотреть на словоизменение и словообразование в русском языке, то легко увидеть преимущественно постфиксальный его характер. Правая часть слова, окончание и суффиксы, служит замечательным признаком, отличающим разные грамматические формы слова. Приставочные правила настолько редки, что им можно даже пренебречь. В частности, вид глагола можно определить с какой-то достоверностью, если взять его начальные символы (делать - попеределать). Компаратив прилагательных и наречий тоже может быть опознан по регулярному использованию приставки “по-”.
Но правильное выделение окончания и суффикса (суффиксов) само по себе является сложной и неоднозначной задачей. Особенно если поставить задачу учесть всевозможные нюансы типа беглых гласных и чередования согласных.
Поэтому приходится использовать грубую силу и полагаться на всевозможные простые регуляризации в моделях, которые автоматически отсеют лишние признаки (псевдопостфиксы).
В итоге, используется следующий подход. Для каждого слова достаточной длины мы берем у него последние 2...4 символа и используем эти цепочки как признаки. Получается достаточно много признаков (несколько тысяч). Но матрица входного датасета получается очень разряженной, поэтому если реализация конкретного алгоритма машобучения допускает эффективную работу со sparse matrix, то этим можно пользоваться.
Декомпозиция задачи MorphoRuEval
В идеале задача морфологического анализа для токенизированного текста должна бы включать 3 части: частеречный разбор, лемматизацию и определение правильного варианта aaa/Aaaa/AAA написания слова. Но так как по условиям соревнования регистр символов не учитывается в оценке качества, то остается две части - частеречный анализ и лемматизация.
Обе части выполняются последовательно. Лемматизация использует результаты частеречного анализа, так как это позволяет снять практически все неоднозначности русского языка. Например, “рой” может лемматизировать как “рыть” или как “рой” в зависимости от того, является ли слово формой глагола или существительного.
Построение словаря (мемоизация)
Важная особенность статистики естественного языка - крайняя неравномерность частот слов (см. так называемый закон
Ципфа). Это означает, что небольшое количество слов, менее 1000, дают более 50% слов в среднем для среднестатистического текста, если, конечно, это не узкоспециальная тема с обилием спецтерминов. Для задачи лемматизации важно также то, что среди этих top 1000 слов подавляющее большинство - служебные слова, местоимения, связочные глаголы и предикативы, для которых характерна нерегулярность словоизменения. С другой стороны, слова в хвосте частотной кривой подчиняются небольшому числу регулярных правил словоизменения и могут быть эффективно обработане сеточной моделью.
Сводя все это вместе, возникает простая и полезная вещь - мемоизация слов из обучающего корпуса. Иначе говоря, в ходе анализа обучающего корпуса мы запоминаем все появления слов и для каждого слова сохраняем информацию - имеет ли оно однозначную лемму, какими частями речи является, какие грамматические теги может нести. Для достаточно большогообучающего корпуса такая статистика, дополненная нижней частотой отсечения в 2-5 появлений, дает достаточно качественный словарь лемм и морфологии.
В исходном тексте лемматизатора можно найти расширенную версию вышеописанного подхода. Если слово в обучающем корпусе имеет несколько вариантов лемматизации, то мы запоминаем частоту каждого варианта. Когда модель выполняет лемматизацию, она сравнивает полученную нейросеткой лемму и запомненные варианты лемматизации. Если сгенерированная лемма совпала с одним из вариантов, то берется она. Если нейросеть предложила неизвестный вариант лемматизации слова, то считаем, что сетка ошиблась. В этом случае лемму выбираем простым сэмплингом по списку частот вариантов лемматизации. Этот подход позволяет компенсировать некоторые ошибки лемматизации в обучающих корпусах.
Model1
Ядром алгоритма является сеточный классификатор, построенный с использованием рекуррентной сетки на LSTM и реализованный на python с использованием
библиотеки Keras. Исходный текст можно взять здесь:
https://github.com/Koziev/MorphoRuEval2017/tree/master/MODEL1 В основу модели положена следующая идея. Для определения тега у слова мы берем его контекст - несколько слов слева и справа. Каждый символ кодируется 1-hot вектором. Вектор слова получается сцеплением векторов символов. От идеи использовать встраивание символов в малоразмерное векторное пространство для сокращения размерности я отказался с учетом своих давних экспериментов в этом направлении, но возможно это улучшило бы показатели данной модели. Получив вектор для каждого слова контекста, мы прогоняем эту цепочку через слой LSTM элементов. Промежуточный relu-слой с dropout-регуляризацией немного улучшает качество модели. Далее полносвязный слой с softmax активацией выполняет финальную классификацию.
Следует упомянуть важный хинт, значительно поднимающий качество сеточной модели. Символы слова рассматриваются справа налево (см. параметр INVERT в исходном коде модели). Таким образом, окончание и суффикс слова после векторизации поступают на одни и те же входы нейросети. Это облегчает тренировку весов модели, особенно учитывая относительно небольшое количество паттернов в обучающей модели и вытекающее из этого ограничение на глубину сетки. Обратное тоже справедливо: если рассматривать символы слова слева направо, то облегчается распознавание корней слова (и приставки в случае глаголов). Возможно, хорошие результаты показала “bidirectional” векторизация, то есть объединение векторов для обоих направлений перебора символов, но такой эксперимент оставлен на будущее.
Теперь рассмотрим задачу классификации, к которой сведена частеречная разметка. Для каждого слова в обучающей выборке мы имеем часть речи и набор тегов. Если объединить их в единый набор (tagset), то получится чуть меньше 300 классов для базового обучающего корпуса соревнования. Именно эти классы составляют задачу классификации. Нетрудно заметить главную неприятную особенность получившихся классов - крайнюю несбалансированность и, в некоторых случаях, пересечение. Это мешает сеточный модели, так как выгода от точной настройки на малонаселенные классы меньше, чем подстройка весов для самых густонаселенных. Эту проблему удалось отчасти решить во второй модели (ансамбль), а пока просто упомянем этот важный нюанс.
Достоинством данной схемы классификации является простота и быстрота обучения. Если сеточная рекуррентная модель обучается на CPU, без использования GPU, то увеличение времени обучения с пары часов до суток для модели Ensemble может оказаться неприятным недостатком, особенно на стадии экспериментов.
На машине с NVidia GTX 980 данная модель обучается примерно за 1 час по корпусу gikrya_train.txt.
Лемматизатор
Лемматизатор также реализован как классификатор на базе нейросети и написан полностью на питоне с использованием библиотеки Keras. Никаких других внешних зависимостей в модели нет. Исходные тексты можно найти здесь:
https://github.com/Koziev/MorphoRuEval2017/tree/master/LEMMATIZER Результаты обучения (опция learn) модель сохраняет в несколько файлов, которые затем используются при разметке нового корпуса (опция tag1). Также в программе есть режим оценки качества построенной модели (опция eval1 в командной строке).
Дополнительно в модель введена возможность использовать внешний словарь лемматизаций. Этот режим включается при использовании опций eval2 и tag2. Словарь лемм должен представляет из себя текстовый файл, в каждое строке которого помимо слова дается его часть речи и лемма:
алетиями алетия СУЩЕСТВИТЕЛЬНОЕ
Варфоломееве Варфоломеев СУЩЕСТВИТЕЛЬНОЕ
анимистках анимистка СУЩЕСТВИТЕЛЬНОЕ
Галицию Галиция СУЩЕСТВИТЕЛЬНОЕ
неприметному неприметный ПРИЛАГАТЕЛЬНОЕ
жиросливочным жиросливочный ПРИЛАГАТЕЛЬНОЕ
исключавшимся исключавшийся ПРИЛАГАТЕЛЬНОЕ
Столбцы разделяются символом табуляции. Я сгенерировал такой словарь из своей словарной базы (
http://www.solarix.ru/grammatical-dictionary-api.shtml), но не использовал этот режим для соревнования.
Базовая идея алгоритма проста и использует априорную информацию о словоизменении в русском языке. Словоизменение в большинстве случаев выражается изменением окончания. В редких случаях меняется также корень слова (лед-льда), но такими ситуациями мы можем сейчас пренебречь. Имея слово и его лемму, находим количество символов, которые надо отрезать справа, и цепочку нового окончания, которое надо добавить. Этот подход работает достаточно хорошо, если слов с изменениями корня немного.
Пройдя по всем словам и леммам в обучающем корпусе, мы составляем список правил лемматизации. В тексте программы они называются “трансдьюсеры” (см.
https://en.wikipedia.org/wiki/Finite-state_transducer).
Сами по себе правила лемматизации дают много ошибок из-за омонимии (рыл - рыло или рыть). Качество можно увеличить, если учитывать при выборе применяемого правила морфологическую информацию, полученную к моменту лемматизации в результаты работы модели частеречной разметки. В исходном коде модели можно увидеть, что в ходе анализа обучающего корпуса составляется справочник тегов. Теги - это метки части речи и прочие грамматические пометки, доступные в обучающем корпусе. Каждый тег помечается уникальным id. На вход сетки теги подаются как единички в векторе, помимо собственно символьного представления слова.
В результате, нейросеть выбирает лемматизирующее правило по следующим данным:
Символьное представление слова
Морфологическая информация о слове
Сам выбор выполняется последним слоем сетки с softmax активацией. Каждый трансдьюсер представлен в выходном векторе одним битом. В режиме лемматизации (опции командной строки eval1 или tag1) модель выбирает трансдьюсер с максимальной вероятностью для слова и применяет его - отрезает от слова определенное количество символов справа и добавляет новую цепочку символов.
Замечу, что режимы разметки и оценки реализованы в данной версии далеко не оптимально. Для каждого слова в обрабатываемом корпусе заполняется входной вектор, он прогоняется через нейросеть, получается вектор вероятностей, который дает номер выбранного правила лемматизации. Намного эффективнее было бы готовить порцию входных векторов для некоторого количества слов, скажем 100-200, и прогонять этот тензор через Keras-модель целой порцией.
PS: Ансамбль будет описан отдельно.