При лемматизации мы берем слово и получаем для него лемму - нормальную (начальную, словарную) форму: словарем - словарь. Для русского языка это означает, что существительное в любой грамматической форме приводится к форме именительного падежа. Для подавляющего большинства русских существительных нормальная форма также означает единственное число, хотя для некоторых существительных, не употребляющихся в единственном числе, это может быть и форма множественного числа: санками - санки. Русский лексикон сформирован таким образом, что названия словарных статей всегда соответствуют начальной форме существительного. Поэтому лемматизация может быть побочным продуктом морфологического анализа. Однако морфологический анализ сам по себе достаточно тяжел и требует наличия очень большой словарной базы. Поэтому в некоторых случаях полезным оказывается описанный лемматизатор - более компактный и простой, требующий минимум ресурсов и внешних зависимостей.
В некоторых приложениях может быть полезным расширить задачу лемматизации так, чтобы заменять уменьшительные и усилительные формы на нейтральные: саночками - саночки, или приводить деепричастия к форме инфинитива: катаясь - кататься. Это выходит за рамки данного алгоритма лемматизации, но может быть реализовано с помощью такого инструмента грамматического словаря, как тезаурус.
Компоненты лемматизатора (динамическая библиотека и файл данных с моделью языка для MS Windows 32/64 бит и Linux 32/64 бит) и примеры его использования входят в состав SDK Грамматического Словаря. Исходные тексты библиотеки на C++ доступны в репозитории проекта. Для прототипирования NLP алгоритмов на Питоне есть библиотека rulemma, доступная на гитхабе.
Данный вариант лемматизатора имеет следующие особенности:
1. Аккуратная лемматизация русских слов - для всех словоформ, присутствующих в текущей версии словаря, лемматизация выполняется безошибочно. Если слово допускает неоднозначную лемматизацию (роем - рыть или рой), то лемматизатор может найти все альтернативные варианты начальной формы слова.
2. Минимальный объем потребляемой памяти, данные хранятся на диске в сжатом виде, после открытия базы она занимает в памяти около 1 Мб в экономном режиме (см. флаги sol_LoadLemmatizator).
3. Поддержка любых языков, реализованных в проекте, таким образом генерируемый при сборке англо-русского словаря лемматизатор будет работать и с английскими, и с русскими словами.
4. Встроенная вероятностная модель морфологии, обучаемая на эталонном корпусе, с помощью которой при лемматизации учитывается контекст слова и уменьшаться количество ошибочных результатов (см. функцию sol_LemmatizePhrase[W,A,8]).
5. Возможна лемматизация даже тех слов, которые отсутствуют в основном лексиконе. Это возможно благодаря тому, что в состав базы данных лемматизатора входят автоматически выводимые правила. Эти правила хорошо справляются с продуктивными суффиксами слов, используемыми в русском языке для образования новых слов.
Динамическая библиотека лемматизации lemmatizator.dll (lemmatizator.so) собирается для 32х и 64х битных платформ Windows и Linux, а также Mac OS X.
Для платформы .NET в состав SDK включена обертка lemmatizator_fx.dll и пример использования на C#.
Лемматизатор не требует наличия других библиотек из состава SDK, так как содержит все необходимые алгоритмы, словарную базу и вероятностную модель морфологии в двух своих файлах.
Очень простой API и отсутствие какой-либо привязки к C++ позволяет легко создавать обертки для вызова лемматизатора из других языков программирования.
В лемматизатор входит вероятностная модель морфологии - обучаемая по специально размеченному корпусу числовая база данных. При первом обращении к функции sol_LemmatizePhrase[W,A,8] движок подгружает эту модель в оперативную память и затем использует ее для выбора более вероятной леммы. Увеличение точности, привносимое этой моделью, полностью определяется процессом обучения и ограничениями самой модели. В лабораторных условиях вероятностная модель морфологии позволяет улучшить точность примерно до 98-99%.
Для ранее упоминавшегося примера неоднозначной лемматизации роем - рой | рыть вероятностная модель стремится опереться на самое частотное употребление в контекста пчелиным роем - пчелиный рой или учесть синтаксические закономерности мы роем - я рыть (после личного местоимения в именительном падеже обычно идет личная форма глагола в соответствущей форме), рой улетел - рой улететь (после императива инфинитив употребляется только для некоторых модальных глаголов, в число которых рыть не входит).
Проиллюстрировать использование морфологической модели в лемматизаторе можно с помощью консольного отладчика Syntax. После его запуска введите директиву #lemmatize и затем предложение "мы ели суп, а вдоль аллеи стояли раскидистые ели". В этом предложении слово "ели" встречается два раза и представляет формы двух разных частей речи - глагола и существительного. Лемматизатор учтет эти факторы и выведет:
В результате первое вхождение слова приведено к инфинитиву "есть", второе - к форме именительного падежа единственного числа "ель".
Для использования вероятностной модели морфологии от прикладной программы не требуется никаких дополнительных действий, помимо вызова функции API sol_LemmatizePhrase[W,A,8]. Если модель морфологии отсутствует в базе данных, то данная функция будет работать фактически через вызов sol_GetLemmas[W,A,8] для каждого слова.
Ограничения вероятностной лемматизации определяются двумя основными факторами.
Во-первых, вероятностная модель не знает ничего о смысле слов, поэтому не всегда может заметить недопустимость применения похожего паттерна.
Во-вторых, алгоритм вероятностной модели учитывает только небольшую окрестность лемматизируемого слова. Поэтому синтаксические связи со словами за пределами данного учитываемого контекста игнорируются. Интересно отметить, что ограничение размера учитываемого контекста имеет и положительный эффект, а именно - это увеличивает устойчивость алгоритма к ошибкам в лемматизируемом предложении, и к появлению в нем неизвестных слов.
Основное и единственное назначение лемматизатора - вернуть для исходного слова его базовую словарную форму. Например, для русских существительных это форма именительного падежа единственного числа, для глаголов - форма инфинитива. Существует и обратная операция - получение нужной грамматической формы слова из базовой, для существительных она называется склонение, для глаголов - спряжение.
В ходе морфологического разбора слова в качестве одного из "побочных" результатов также получается нормальная форма слова, благодаря тому, что морфологический анализатор распознает исходное слово как форму конкретной словарной статьи. Но в отличие от лемматизатора назначение морфологического разбора включает в себя получение множества другой информации о морфологических свойствах слова. Например, при морфологическом разборе существительного определяется его постоянные свойства (грамматические атрибуты) - перечислимость, одушевленность, род, и непостоянные (грамматические измерения) - падеж, число.
Таким образом, лемматизатор - это максимально упрощенный и оптимизированный морфологический анализатор. С точки зрения прикладного программиста использование лемматизатора тоже намного проще, если сравнивать с набором функций полного морфологического анализа. Среди заложенных конструктивных недостатков лемматизатора необходимо отметить то, что он ограниченно учитывает только ближайший контекст слова и таким образом не может сам выбрать часть речи, к которой необходимо нормализовать слово. Например, при лемматизации слова простынь получается две леммы - существительное простынь и глагол простыть. Если лемматизируемое слово употреблено в контексте, который известен встроенной модели морфологии, то лемматизатор выберет более частотный и, возможно, осмысленный вариант, иначе шансы на правильный результат будут определяться скорее случайными факторами, а именно порядком следования созданных при обучении правил. Морфологический анализатор текста умеет учитывать контекст слова и отбрасывать недопустимые варианты.
Некоторые функции API лемматизатора реализованы в 3х вариантах для удобства использования на разных платформах и языках программирования:
суффикс W - юникод, широкие строки (wchar_t, WideChar)
суффикс A - однобайтовая кодировка ASCII текущей локали
суффикс 8 - многобайтовая кодировка utf-8
HLEM sol_LoadLemmatizatorW( const wchar_t * Filepath, int Flags )
HLEM sol_LoadLemmatizatorA( const char * Filepath, int Flags )
HLEM sol_LoadLemmatizator8( const char * Filepath, int Flags )
Открывается база лемматизатора, созданная при компиляции словаря с опцией -save_lemmatizer.
Аргументы:
Filepath - путь к базе лемматизатора, обычно в рамках проекта это файл lemmatizator.db.
Flags - дополнительные параметры для управления работы лемматизатором. Значение LEME_DEFAULT открывает базу в самом экономном, но самом медленном режиме лемматизации. При указании флага LEME_FASTER движок будет работать немного быстрее, несколько увеличив потребление памяти. Наконец, флаг LEME_FASTEST многократно ускорит лемматизацию, но загрузит базу данных в оперативную память, увеличив свои накладные расходы.
При любом значении флагов таблицы вероятностной модели загружаются только при первом вызове функции sol_LemmatizePhrase.
Возвращает:
Возвращаемое значение является дескриптором объекта лемматизатора. Дескриптор указывается почти во всех
остальных функциях API.
void sol_DeleteLemmatizator( HLEM hEngine )
Закрывается база, подсоединенная предыдущим вызовом sol_LoadLemmatizator[W,A,8]. Освобождаенся память, занятая загруженными таблицами. Дескриптор hEngine становится невалидным и не должен более использоваться.
int sol_GetLemmaW( HLEM hEngine, const wchar_t * Word, wchar_t * Result, int BufSize )
int sol_GetLemmaA( HLEM hEngine, const char * Word, char * Result, int BufSize )
int sol_GetLemma8( HLEM hEngine, const char * Word, char * Result, int BufSize )
Выполняется лемматизация слова Word, результат копируется в буфер Result длиной BufSize символов. Слово представляет из себя цепочку символов, заканчивающуюся нулём.
Возвращает:
Число вариантов лемматизации, если 1 - значит найдена и возвращена в буфере единственная нормальная форма, если > 1, то в буфер помещается какая-то первая из списка. Ниже описаны функции, которые могут работать со всеми вариантами лемматизации.
Для оценки количества слов, которые приводятся к более чем одной нормальной форме, можно воспользоваться словарем, загруженным в MS SQL и запросом:
Он дает такую табличку для русского лексикона:
Таким образом, доля слов, которые имеют более одной нормальной формы, не превышает 1%, если не учитывать частотность употребления слов в тексте.
HLEMMAS sol_GetLemmasW( HLEM hEngine, const wchar_t * Word )
HLEMMAS sol_GetLemmasA( HLEM hEngine, const char * Word )
HLEMMAS sol_GetLemmas8( HLEM hEngine, const char * Word )
Выполняется лемматизация слова Word и все альтернативные варианты возвращаются в виде дескриптора списка. Для работы с этим списком далее описано несколько функций.
HLEMMAS sol_LemmatizePhraseW( HLEM hEngine, const wchar_t * Sentence, int Flags, int WordSeparator )
HLEMMAS sol_LemmatizePhraseA( HLEM hEngine, const char * Sentence, int Flags, int WordSeparator )
HLEMMAS sol_LemmatizePhrase8( HLEM hEngine, const char * Sentence, int Flags, int WordSeparator )
Выполняется лемматизация слов в предложении Sentence с учетом контекста каждого слова. Возвращается список выбранных лемм, по одному самому вероятному варианту для каждого слова.
Аргумент Flags в текущей версии должен всегда задаваться как 0.
Аргумент WordSeparator должен передавать код широкого символа (wchar_t), который использован для разделения слов в фразе. Обратите внимание, что задача токенизации, то есть разбивка на слова, должна быть предварительно выполнена прикладным кодом самостоятельно. Лемматизатор берет из входной строки все символы, ограниченные заданным символом WordSeparator или началом/концом строки, и рассматривает их как одно слово.
int sol_CountLemmas( HLEMMAS hList )
Возвращает количество элементов в списке лемматизаций hList.
int sol_CountLemmas( HLEMMAS hList )
Возвращает количество элементов в списке лемматизаций hList.
int sol_GetLemmaStringW( HLEMMAS hList, int Index, wchar_t * Result, int Bufsize )
int sol_GetLemmaStringA( HLEMMAS hList, int Index, char * Result, int Bufsize )
int sol_GetLemmaString8( HLEMMAS hList, int Index, char * Result, int Bufsize )
Копирование в предоставленный буфер Result размером BufSize строки с индексом Index из списка лемматизаций hList.
void sol_DeleteLemmas( HLEMMAS hList )
Удаляется список нормальных форм, возвращенный sol_GetLemmasX.
function Lemmatize( $word )
Возвращает лемму для слова-аргумента, символы должны быть в кодировке cp1251.
function LemmatizePhrase( $str )
Возвращает лемматизированную строку - каждое слово из входной строки проходит нормализацию. Текст должен быть в кодировке cp1251.
Пример на C++:
#include "lemmatizator_engine.h" ... HLEM hEngine = sol_LoadLemmatizatorA( "../../../../../../bin-linux64/lemmatizer.db", LEME_DEFAULT ); char utf8[256]; int nlem = sol_GetLemma8( hEngine, "верифицировали", utf8, sizeof(utf8) ); printf( "%s\n", utf8 ); HLEMMAS hList = sol_GetLemmas8( hEngine, "роем" ); if( hList!=NULL ) { int nlemma = sol_CountLemmas(hList); for( int i=0; i<nlemma; ++i ) { sol_GetLemmaString8( hList, i, utf8, sizeof(utf8) ); printf( "%s\n", utf8 ); } sol_DeleteLemmas(hList); } sol_DeleteLemmatizator(hEngine);
В составе SDK Грамматического Словаря есть исходный код примера вызова вышеописанных процедур для C++ и C#, а также проекты для VisualStudio и make-файлы для сборки примеров.
Основной пример на C++ располагается в подкаталоге ...\demo\ai\solarix\Grammar_Engine\Lemmatizator\C. Пример компилируется для всех поддерживаемых платформ - 32- и 64-битных Windows, Linux и Mac OS X. Он имеет двойное назначение.
Во-первых, он демонстрирует использование всех функций API лемматизатора.
Во-вторых, он проверяет корректность приведения слов к базовым формам по эталонным спискам. Сами эти списки экспортированы из SQL хранилища словаря с помощью несложных запросов, и содержат исчерпывающий перечень для двух случаев - когда слово приводится однозначно к единственной лемме, и когда слово является формой двух словарных статей. Например, список слов, которые лемматизируются двумя альтернативными путями, используется такой запрос для MS SQL:
select rTrim(X1.lexeme) + ';' + rTrim(Y1.lemma) + ';' + rTrim(Y2.lemma)
from lexemes_1 Z1, lexemes_n X1, lemmas Y1, lexemes_n X2, lemmas Y2
where Z1.lemma_count=2 AND
X1.lexeme=Z1.lexeme AND
Y1.id=X1.id_lemma AND
Y2.id=X2.id_lemma AND
X1.lexeme=X2.lexeme AND
X1.id<X2.id
Результаты выполнения запроса выгружаются прямо из SQL Server Management Studio в текстовый файл, в кодировке utf-8.
Приобретение SDK грамматического словаря
Все способы лемматизации в грамматическом словаре и поисковой системе
© Козиев Илья 2019
![]() |
|
изменено 26-Apr-19 |