DeepMorphy is a neural network based morphological analyzer for Russian language.
DeepMorphy - морфологический анализатор для русского языка. Доступен как .Net Standart 2.0 библиотека. Умеет проводить морфологический разбор слова (определяет часть речи, род, число, падеж, время, лицо) и приводить слова к нормальной форме.
Терминология в DeepMorphy частично заимствована из морфологического анализатора pymorphy2.
Граммема (англ. grammeme) - значение одной из грамматических категорий слова (например прошедшее время, единственное число, мужской род).
Грамматическая категория (англ. grammatical category) - множество из взаимоисключающих друг друга граммем, характеризующих какой-то общий признак (например род, время, падеж и тп). Список всех поддерживаемых в DeepMorphy категорий и граммем тут.
Тег (англ. tag) - набор граммем, характеризующих данное слово (например, тег для слова еж - существительное, единственное число, именительный падеж, мужской род).
Лемма (англ. lemma) - нормальная форма слова.
Лемматизация (англ. lemmatization) - приведение слова к нормальной форме.
Основным элементом DeepMorphy является нейронная сеть. Для большинства слов морфологический анализ и лемматизация выполняется сетью. Некоторые виды слов обрабатываются препроцессорами (если слово обработано препроцессором, то результат сети не учитывается).
Имеется 3 препроцессора:
- Словарь. Часть токенов просто смотрится в словаре. Используется для местоимений, предикативов, предлогов, союзов, частиц, междометий и числительных. Так же в словарь добавляются слова из датасета, в которых сеть после обучения все еще делает ошибки.
- Препроцессор для наращенных числительных (например 1-й, 1917-й).
- Препроцессор на регулярных выражениях для пунктуации, целых цифр, римских цифр и неизвестных токенов (если токен в основном состоит не из кириллицы).
Сеть построена и обучена на фреймворке tensorflow. В качестве датасета выступает словарь Opencorpora. В .Net интегрирована через TensorFlowSharp.
Граф вычислений DeepMorphy состоит из 8 "подсетей":
- 6 двунаправленных рекурентных сетей, по одной для каждой поддерживаемой грамматической категории (определяет граммему в категории);
- 1 двунаправленная рекурентная сеть для определения самых вероятных тегов. Для каждой комбинации граммем из датасета заведен 1 класс (всего 172 класса), сеть обучается на определение к каким классам может принадлежать данное слово. На этапе работы берется 4 самых вероятных класса;
- 1 seq2seq модель для лемматизации.
Обучение сетей производится последовательно, сначала обучаются сети по категориям (порядок не имеет значения). Далее обучается главная классификация по тегам и затем лемматизация. Обучение проводилось на 3-ех GPU Titan X. Метрики работы сети на тестовой датасете для последнего релиза можно посмотреть тут.
DeepMorphy для .NET представляет собой библиотеку .Net Standart 2.0. В зависимостях только библиотека TensorflowSharp (через нее запускается нейронная сеть).
Библиотека опубликована в Nuget, поэтому проще всего устанавливать через него.
Если есть менеджер пакетов:
Install-Package DeepMorphy
Если проект поддерживает PackageReference:
<PackageReference Include="DeepMorphy"/>
Если кто-то хочет собрать из исходников, то C# исходники лежат тут. Для разработки используется Rider (без проблем все должно собраться и в студии).
Все действия осуществляются через объект класса MorphAnalyzer:
var morph = new MorphAnalyzer();
В идеале, лучше использовать его как синглтон, при создании объекта какое-то время уходит на загрузку словарей и сети. Потокобезопасен. При создании в конструктор можно передать следующие параметры:
- withLemmatization - возвращать ли леммы слов (по умолчанию - false). Если нужна лемматизация, то необходимо выставить в true, иначе лучше не включать (без флага работает быстрее).
- useEnGrams - использовать английские названия граммем и грамматических категорий (по умолчанию - false).
- withTrimAndLower - производить ли обрезку пробелов и приведение слов к нижнему регистру (по умолчанию - true).
- withPreprocessors - использовать ли препроцессоры перед нейронной сетью (по умолчанию - true). По идее, всегда должно быть true, false ставится только для тестов.
- maxBatchSize - максимальный батч, который скармливается нейронной сети (по умолчанию - 4096). Если есть уверенность, что оперативной памяти хватит, то можно увеличивать (увеличит скорость обработки для большого количества слов).
Для анализа необходимо вызвать метод Parse (на вход принимает IEnumerable со словами для анализа, возвращает IEnumerable с результатом анализа).
var results = morph.Parse(new string[]
{
"королёвские",
"тысячу",
"миллионных",
"красотка",
"1-ый"
}).ToArray();
var morphInfo = results[0];
Примеры использования тут.
Список поддерживаемых грамматических категорий, граммем и их ключей тут. Если необходимо узнать самую вероятную комбинацию граммем, то нужно использовать свойство BestTag объекта MorphInfo.
// выводим лучшую комбинацию граммем для слова
Console.WriteLine(morphInfo.BestTag);
По самому слову не всегда возможно однозначно установить значения его грамматических категорий (см. омонимы), поэтому DeepMorphy позволяет посмотреть топ тегов для данного слова (свойство Tags).
// выводим все теги для слова + их вероятность
foreach (var tag in morphInfo.Tags)
Console.WriteLine($"{tag} : {tag.Power}");
Есть ли комбинация граммем в каком-нибудь из тегов:
// есть ли в каком-нибудь из тегов прилагательные единственного числа
morphInfo.HasCombination("прил", "ед");
Есть ли комбинация граммем в самом вероятном теге:
// ясляется ли лучший тег прилагательным единственного числа
morphInfo.BestTag.Has("прил", "ед");
Получение определенных грамматических категорий:
// выводит часть речи лучшего тега и число
Console.WriteLine(morphInfo.BestTag["чр"]);
Console.WriteLine(morphInfo.BestTag["число"]);
Теги применяются для случаев, если нужна информация сразу о нескольких грамматических категориях (например часть речи и число). Если вас интересует только одна категория, то лучше использовать интерфейс к вероятностям значений грамматических категорий объектов MorphInfo (на тестовом датасете точность на несколько процентов больше).
// выводит самую вероятную часть речи
Console.WriteLine(morphInfo["чр"].BestGramKey);
Так же можно получить распределение вероятностей по грамматической категории:
// выводит распределение вероятностей для падежа
foreach (var gram in morphInfo["падеж"].Grams)
{
Console.WriteLine($"{gram.Key}:{gram.Power}");
}
Если необходимо приведение слов к нормальной форме, то анализатор надо создавать следующим образом:
var morph = new MorphAnalyzer(withLemmatization: true);
Проверка, есть ли у данного слова лемма:
morphInfo.HasLemma("королевский");
Метод CanBeSameLexeme может быть использован для нахождения слов одной лексемы:
// выводим все слова, которые могут быть формой слова королевский
var words = new string[]
{
"королевский",
"королевские",
"корабли",
"пересказывают",
"королевского"
};
var results = m.Parse(words).ToArray();
var mainWord = results[0];
foreach (var morphInfo in results)
{
if (mainWord.CanBeSameLexeme(morphInfo))
Console.WriteLine(morphInfo.Text);
}
- Python код модели, обучения и разные утилиты.
- C# код DeepMorphy.
- Проект с C# примерами.
- Файлы последнего релиза.
- Подумать над оптимизацией модели на этапе применения (подумать над квантованием или обрезкой графа вычислений).