Az.js: JavaScript-библиотека для обработки текстов на русском языке

Как чуден и глубок русский курлык
— Генератор постов


Обработка естественного языка (natural language processing, NLP) — тема, на мой взгляд, очень интересная. Во-первых, задачи тут чисто алгоритмические: на вход мы принимаем совершенно примитивный объект, строчку, а извлечь из него пытаемся вложенный в него смысл (ну или хотя бы частичку смысла). Во-вторых, необязательно быть профессиональным лингвистом, чтобы решать эти задачи: достаточно знать родной язык на более-менее приличном уровне и любить его.

А ещё с небольшими затратами можно сделать какого-нибудь бестолкового чат-бота — или, как вот я, генератор постов на основе того, что вы сами писали на своей страничке в соцсети. Возможно, кто-то из вас уже видел это приложение — оно довольно глупое, чаще всего выдает бессмысленный и бессвязный текст, но изредка всё же заставляет улыбнуться.

Бессвязность текстов в нынешней версии «Генератора» вызвана тем, что на самом деле никакого анализа он производить не умеет. Просто в одних случаях «предсказывает» продолжение предложения по собранным биграммам, а в других — заменяет в готовом предложении некоторые слова на другие, которые заканчиваются похоже. Вот и вся начинка.

Конечно, хочется сделать что-нибудь поинтереснее. Беда в том, что модные сейчас нейросети не очень-то можно здесь применить: им нужно много ресурсов, большую обучающую выборку, а в браузере у пользователя соцсети всего этого нет. Поэтому я решил изучить вопрос работы с текстами с помощью алгоритмов. К сожалению, готовых инструментов для работы с русским языком на JavaScript найти не удалось, и я решил сделать свой маленький велосипед.

db7c2b920a264b1da0edf60467619e9a.png
Свою библиотеку я назвал Az. С одной стороны — это первая буква кириллицы, «азъ», ну, а с другой — первая и последняя буквы латиницы. Сразу дам все ссылки:
— GitHub;
— Документация: либо на гитхабе же, либо на Doclets.io;
— Демо.
Установить библиотеку вы можете из npm, так и из bower (в обоих местах она имеет название az). На свой страх и риск — так как она ещё не покрыта полностью тестами и до выхода первой версии (который произойдет скоро, я надеюсь) у неё могут измениться публичные API. Лицензия: MIT.

На данный момент библиотека умеет две вещи: токенизацию и анализ морфологии. В некотором отдаленном будущем (но не в первой версии) предполагается реализовать синтаксический анализ и извлечение смыслов из предложений.


Суть токенизации очень проста: как я упоминал выше, на входе мы принимаем строку —, а на выходе получаем «токены», группы символов, которые (вероятно) являются отдельными сущностями в этой строке.

Обычно для этой цели используется что-нибудь типа одного вызова split по простой регулярке, но мне этого показалось мало. Например, если разделить строку по пробелам, мы потеряем сами пробелы — иногда это удобно, но не всегда. Ещё хуже, если мы захотим предварительно разбить строку по точкам, вопросительным и восклицательным знакам (надеясь выделить так предложения): мало того, что теряются конкретные знаки препинания, так ещё и на самом деле точки не всегда завершают предложения. А потом мы понимаем, что в реальных текстах могут встретиться, скажем, ссылки (и точки в них точно никакого отношения к пунктуации не имеют) и регулярка становится совсем страшной.

Поэтому Az предлагает для этого свое, не-деструктивное решение. Гарантируется, что после токенизации каждый символ будет принадлежать некоторому токену и только одному. Пробелы, переводы строк и прочие невидимые символы объединяются в один тип токенов, слова — в другой, пунктуация — в третий. Если все токены склеить вместе — получится в точности исходная строка.

При этом токенизатор достаточно умен, чтобы понимать, что дефис, обрамленный пробелами — это знак препинания (вероятно, на его месте подразумевалось тире), а прижатый хотя бы с одной стороны к слову — часть самого слова. Или что «habrahabr.ru» — это ссылка, а «mail@example.com» — это, вероятно, емэйл (да, полная поддержка соответствующего RFC не гарантируется). #hashtag — это хэштег, user — упоминание.

Ну и, наконец — раз уж RegExp’ы для этой цели использовать не стоит — Az.Tokens умеет парсить HTML (а заодно вики и Markdown). Точнее говоря, никакой древовидной структуры на выходе не будет, но все теги будут выделены в свои токены. Для тегов