Для алфавитных языков характерно большое разнообразие длин слов на письме. Простой анализ статистики длин токенов в большой корпусе русскоязычных текстов дает среднюю длину слова ~5.6 символов. Но кривая распределения частот такова, что с одной стороны есть немного очень коротких и очень частотных слов (я, и, да), а с другой неопределенное количество очень длинных слов с единичным употреблением (наиквалифицированнейшими). Насколько сильна асимметрия распределения?
Если взять достаточно большой корпус текстов, в котором слова заранее отделены пробелами, то можно быстро выяснить разброс длин слов с помощью следующего простого скрипта на питоне третьей ветки (для справки - под Windows использую анаконду
https://www.continuum.io/downloads).
Накапливаем статистику:
Корпус достаточно большой, примерно 8 Гб в utf-8, более 600 миллионов слов:
Средняя длина слова:
Загружаем статистику в pandas табличку:
Далее можно выполнять анализ распределения стандартными средствами. Простой просмотр распределения частот показывает, что длинные слова достаточно редки, особенно если учесть некоторое количество опечаток из-за слившихся токенов:
В результате, слова длиннее 30 символов можно спокойно выбрасывать из обучающих датасетов, так как вносимые искажения и ограничения будут меньше погрешности самих сеточных моделей.
Чтобы работать с таким разбросом размеров входных паттернов в сеточных моделях, надо либо использовать рекуррентные элементы, либо ограничивать максимальную длину обрабатываемых слов каким-то разумным значением и использовать feedforward сетку.
Модели, которые рассматривают слово как цепочку символов неограниченной в принципе длины и обрабатывают эту цепочку через рекуррентные алгоритмы, я далее буду обозначать char-rnn.
Модели, которые разворачивают цепочку символов в длинный паттерн фиксированной длины, при необходимости добавляя нули, и используют простую feedforward сетку, я буду далее обозначать char-feedforward.
Нет ничего удивительного в том, что char-feedforward модели обучаются намного быстрее, поскольку у них нет необходимости заниматься организацией кратковременной памяти, чтобы к концу цепочки помнить о первых символах. На эквивалентных моделях для лемматизации разница превышает 10 раз - 13 секунд у char-feedforward против 196 секунд для char-rnn на одну эпоху.
С другой стороны, на практике LSTM-ячейки обеспечивают эффективное обслуживание слов разумной длины без потерь. А точность char-rnn моделей, по некоторым результатам, даже немного выше. Минус в том, что повышенная ресурсоемкость char-rnn моделей требует использования GPU для обучения. Я выполнял расчеты на GTX 980, но даже на такой аппаратуре обучение иногда длиться многие часы.
Но неожиданным оказался результат, что char-rnn модели обеспечивают лучшее качество. Наглядно это видно при сравнении графика ошибки модели лемматизации:
Видно, что рекуррентная модель быстро, за ~40 эпох, увеличивает свою точность и прекращает работу, так как validation loss перестает падать в течении 5 эпох (EarlyStopping в Keras). Char-feedforward улучшается эффективно медленнее, и не достигает точность char-rnn даже после 1000 эпох.
Наконец, есть еще один важный технический нюанс при использовании char-rnn моделей. Все модели я проверяю с использованием библиотеки Keras на питоне. Чтобы перенести обученную модель в C++ или C# программу, требуется обеспечить реализацию одинаковых алгоритмов, которые возьмут матрицы готовых весов связей. Для char-feedforward такой перенос обеспечить, как мне кажется, гораздо проще. Для C++ существует не там много библиотек с реализацией RNN, в частности LSTM, которые без проблем можно “таскать” в составе большого приложения. Кроме того, для LSTM есть вариации, заключающиеся в разных внутренних моделях вентилей, так что без трудоемкого переписывания высокоуровневого Keras-кода на TensorFlow, скорее всего, обойтись не удастся.