Home

Awesome

Модели для решения задач AGRR-2019

Далее описываются мои подходы к решению задачи AGGR-2019. Я не стал включать реализацию с использованием BERT, так как из-за ограниченных вычислительных ресурсов мне не удалось повысить сложность BERT'а до необходимого уровня и обойти базовую нейросетевую модель (см. далее "Модель для бинарной классификации") по точности.

Используемые внешние библиотеки

Весь код написан на Python, должен работать на 2 и 3 версиях. Для нейросетевых моделей используется Keras, бэкэнд - tensorflow.

Также используются следующие библиотеки и пакеты:

rutokenizer - простой питоновский модуль для токенизации русскоязычных текстов, правильно учитывающий составные слова с дефисами ("какой-либо").

ruword2tags - русская морфологическая база с информацией о грамматических свойствах большого количества слов.

rusyllab - разбивка слов на слоги; это использовалось в ходе исследования задачи как альтернатива SentencePiece.

rupostagger - частеречная разметка предложений (на базе "классической" библиотеки CRFSuite).

gensim - обучение векторных моделей слов.

hyperopt - автоматизированный подбор архитектуры и гиперпараметров моделей

keras-contrib - используется слой CRF для sequence labeling и оптимизатор FTML.

Также использовались стандартные для таких задач pandas, numpy, sklearn и другие библиотеки.

Подготовка векторной модели word2vector

Выбранные в ходе оптимизации архитектуры модели используют векторные модели слов, полученные на русскоязычном корпусе с помощью gensim. Код w2v/w2v.py для тренировки с использованием gensim тривиален. Для решения AGRR выбран вариант с параметрами:

model = gensim.models.word2vec.Word2Vec(sentences,
                                        size=64,
                                        window=5,
                                        cbow_mean=1,
                                        min_count=2,
                                        sorted_vocab=1,
                                        iter=1)

Единственный нюанс, на которых хочется обратить внимание, связан с препроцессингом текста. В отличие от большинства аналогичных моделей, я НЕ убираю пунктуацию из текста, на котором учится w2v. Связано это с тем, что задача AGRR в значительной степени опирается на правильный учет некоторых символов пунктуации, в частности "-". Консистентное встраивание пунктуаторов в общее векторное пространство позволяет решать задачу точнее. Соответствующая подготовка корпуса с разбивкой сырых текстов на токены и сохранением пунктуации выполняется с помощью упомянутого выше пакета rutokenizer (https://github.com/Koziev/rutokenizer).

Векторная аппроксимация морфологии wordchar2vector

Код модели (см. каталог wc2v) с минимальными изменениями взят из проекта чатбота.

Основная идея использования следующая.

Пакет ruword2tags позволяет для большинства слов, включая отсутствующие в исходном словаре, определить грамматические признаки - падеж, число, род, время, вид и так далее, включая категории частей речи - существительное, глагол, прилагательное, наречие.

Но такое деление достаточно грубое, многие важные грамматические отличия слов при этом теряются, не говоря уже об оттенках смысла. Например, глаголы "запеть" и "допеть" отличаются способом глагольного действия и выражают разные идеи законченности действия.

Самый простой способ донести до нейросетевых моделей такую информацию, не занимаясь трудоемкой ручной разметкой слов заключается в разбивке слов на слоги. Действительно, многие приставки легко отделяются формальными правилами слогоделения: за-петь, до-петь. Именно для этого используется библиотечка rusyllab.

Но есть другой способ - посимвольное векторное встраивание слов. Соответствующая модель тренируется как автоэнкодер, который учится для произвольной цепочки символов (слова) давать вектор фиксированного размера, из которого можно восстановить слово. В получающийся вектор попадает информация о морфемном составе слов, так как морфемы хорошо коррелируют с частотными n-граммами и учитываются автоэнкодером для сжатия информации. Кроме того, wc2v векторы неявно содержат информацию о посимвольной близости слов, что позволяет нейросетевым моделям лучше обобщаться: обучившись на сэмпле со словом "быть", модели с большей вероятностью будут делать правильный инференс для сэмплов со словом "бывать". Для некоторых слов такое обобщение очень важно в силу большого дисбаланса в частотах употребления. Например, в моем 120Гб корпусе русских текстов слово "чтобы" встречается 18394181 раз, а вариант "чтоб" - 1109921, то есть более чем на порядок реже. Так как автоэнкодерная модель wc2v учится на словах без учета их частотности, то векторы для "чтобы" и "чтоб" содержат примерно равные по качеству представления.

Обучение модели wc2v идет достаточно долго, на GTX1080 Ti до нескольких часов. В результате на диске сохраняется модель (файл с описанием архитектуры и веса), которая затем загружается и используется для получения векторов слов в задачах AGRR.

Модель для бинарной классификации (первая задача AGRR)

Задача заключается в определении, содержит ли предложение пропущенное сказуемое:

Я превращу твое сердце в траву, а все, что ты любишь, в овец. - есть пропуск Все это сведения не из Библии, а из Протоевангелия Иакова - нет пропуска

Нейросетевая модель реализована в коде gridsearch_model1.py.

Для поиска оптимальной архитектуры сетки и подбора гиперпараметров нужно в строке 80 задать флаг do_hyperopt=True, а в строке 88 - флаг run_best=False.

В ходе поиска программа будет сохранять текущую лучшую архитектуру и прочие параметры в файле tmp\gridsearch_model1.best_params.json. Поиск архитектуры занимает очень много времени (сутки), поэтому по достижении некоторой приемлемой точности можно просто прервать работу, поменять значения флагов на do_hyperopt=False, run_best=True, и запустить модель в режиме обработки тестовых данных соревнования. В результате инференса будет сохранен файл с результатами бинарной классификации предложений в файле data/input_file.txt.

Для итоговой разметки выбрана рекуррентная нейросеть (bidirectional LSTM), работающая с цепочками слов. Слова представляются соединением векторов word2vector, wordchar2vector и грамматических тегов слов (ruword2tags).

Модели для разметки фрагментов предложений (вторая задача AGRR)

Более сложная часть задачи AGRR - посимвольная разметка частей предложения на шесть меток. Вот пример того, как это выглядит:

Я превращу твое сердце в траву, а все, что ты любишь, в овец.
cV=2:10
cR1=11:22
cR2=23:30 V=54:54
R1=34:52
R2=54:60

По символам (cV=1, cR1=2, cR2=3, R1=5, R2=6):

Я превращу твое сердце в траву, а все, что ты любишь, в овец.
--11111111-22222222222-3333333----555555555555555555--666666

Можно увидеть, что разметка для пяти меток (cV, cR1, cR2, R1 и R2) идет строго по границам слов. Для метки V помечается первый символ слова, перед которым нужно вставить пропущенный глагол.

Разметка слов в предложении хорошо решается методами sequence labeling. В частности, для нейросетевых моделей отлично работает слой CRF в keras-contrib. За счет учета широкого контекста, поставляемого рекуррентным слоем LSTM, и глобальной оптимизации цепочки меток, достигается неплохая точность.

Чтобы упростить реализацию моделей, я разделил обучение для меток (cV, cR1, cR2, R1 и R2) и V на два отдельных этапа. В файле gridsearch_model2_kerascrf.py в строках 74 и 75 задается группа меток, для которой выполняется обучение или инференс.

Сначала надо выполнить поиск архитектуры для выбранной группы метод. Для этого в строке 66 выставляется флаг do_hyperopt=True, в строке 71 run_best=False. Поиск будет идти очень долго, вплоть до нескольких суток. По мере нахождения лучшего набора гиперпараметров они сохраняются в json-файле. Поэтому можно прервать поиск по достижению разумного качества.

Для прогона тестовых данных соревнования надо задать флаги do_hyperopt=False и run_best=True, опять для каждой группы меток. Результаты разметки будут сохранены в текстовом файле.