Время прочтения: 4 мин.
Морфологические анализаторы (они же морфологизаторы) в NLP выполняют базовые задачи, а именно определяют грамматические характеристики отдельных слов. На сегодня самые популярные python морфологизаторы это pymorphy и mystem.
По своей сути морфологизатор это словарь слов и их характеристик, это означает, что каждая словоформа каждого слова должна быть в нем представлена (для справки, в русском языке около 5 млн. словоформ). Сразу встает вопрос о структуре данных: как хранить данные используя меньше памяти и как быстро получать характеристики слов по ключам (словам). Упомянутый ранее pymorphy основан на конечных автоматах, а mystem – на префиксных деревьях. Ниже речь пойдёт о конечных автоматах.
Конечный автомат — это абстракция, состоящая из набора состояний (узлов) и переходов между ними. В один момент времени активным может быть только одно состояние, условия смены состояний представлены ребрами и помечаются меткой. Различают детерминированные и недетерминированные автоматы. В детерминированном по одной метке возможен переход только в одно состояние, в недетерминированном же наоборот – на одну метку может приходиться более одного перехода.
Автомат состоит из трёх видов состояний:
1) Начальное, круг с номером 0 – с него начинается работа автомата, может быть только одно;
2) Финальные, двойной круг – на них заканчивается работа конечного автомата (в них мы будем хранить информацию о слове);
3) Простые, обозначены кругом – промежуточные состояния.
Рассмотрим небольшой пример детерминированного конечного автомата, реализующего хранение трёх слов: деревня, дерево и дебри.
Как видно из рисунка слова «деревня» и «дерево» имеют общее начало «дерев», что позволяет «сэкономить» на хранении, также слово «дебри» имеет общую часть «де» с двумя другими. В финальных состояниях 6, 8 и 11 можно возможно хранение морфологической информации слов, если она общая для всех слов (например, мы храним только часть речи), то состояния 6, 8 и 11 можно заменить одним. Функционал конечных автоматов для языка python реализован в модуле DAWG. Установка модуля выполняется командой:
pip install DAWG-Python
Ниже будет приведена реализация простого морфологизатора при помощи DAWG, который будет для слова, переданного на вход, возвращать его нормальную форму и часть речи. Для наполнения конечно автомата будет использован открытый корпус русского языка OpenCorpora, из которого возьмем все словоформы, их нормальных формы и части речи (список частей речи).
from dawg import BytesDAWG
import xml.etree.ElementTree as ET
ElementTree используется для считывания файла xml в котором хранится корпуса текста. Чтобы не считывать файл целиком, реализуем генератор, возвращающий кортеж. Первый элемент кортежа — словоформа, второе – строка с нормальной формой слова и его частью речи, разделённые нижним подчёркиванием. Например, (“деревом”, “дерево_NOUN”), где NOUN – существительное.
def fill_dawg(fpath):
for event, elem in ET.iterparse(fpath):
if elem.tag == 'lemma':
lemma_node = [node for node in elem if node.tag == 'l'][0]
lemma = lemma_node.get('t')
gramemma = lemma_node[0].get('v')
for ch_node in elem:
if ch_node.tag == 'f':
word = ch_node.get('t')
yield word, bytes(str(f'{lemma}_{gramemma}'), 'utf8')
где fpath – путь к корпусу текста.
Создание конечного автомата и его наполнение реализует следующий код:
bytes_dawg = BytesDAWG(fill_dawg (fpath))
Класс BytesDAWG принимает на вход итерируемые объекты, состоящие из пар ключ-значение. Теперь можем протестировать автомат. Посмотрим выдачу на одну из форм слова «деревня»:
bytes_dawg[‘деревней’]
Вывод программы:
[b’\xd0\xb4\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb2\xd0\xbd\xd1\x8f_NOUN’]
Результат храниться в байтах, поэтому применим метод decode:
bytes_dawg[‘деревней’][0].decode(‘utf8’)
Вывод программы:
‘деревня_NOUN’
Результат соответствует ожиданиям: нормальная форма – деревня, часть речи – существительное.
Модуль DAWG можно использовать и в более практических задачах, например, для хранения больших словарей. Модуль оптимизирует хранение за счёт исключения дублей общих частей слов. Кроме того, в DAWG предоставляет возможность выполнять и другие операции, например, поиск по префиксу или поиск с заменами. Применение конечных автоматов не ограничивается морфологизаторами: они лежат в основе регулярных выражений, применяются в прописывание логики искусственного интеллекта в играх, используют в валидации UI моделей и т.д.