Время на прочтение
9 мин
Количество просмотров 91K
Пусть, дано предложение “Съешьте еще этих мягких французских булок, да выпейте чаю.”, в котором нам нужно определить часть речи для каждого слова:
[('съешьте', 'глаг.'), ('еще', 'нареч.'), ('этих', 'местоим. прил.'), ('мягких', 'прил.'), ('французских', 'прил.'), ('булок', 'сущ.'), ('да', 'союз'), ('выпейте', 'глаг.'), ('чаю', 'сущ.')]
Зачем это нужно? Например, для автоматического определения тегов для блог-поста (для отбора существительных). Морфологическая разметка является одним из первых этапов компьютерного анализа текста.
Существующие решения
Конечно, все уже придумано до нас. Существует mystem от Яндекса, TreeTagger с поддержкой русского языка, на питоне есть nltk, а также pymorphy от kmike. Все эти утилиты отлично работают, правда, у pymorphy нет поддержки питона 3, а у nltk поддержка третей версии питона только в бете (и там вечно что-то отваливается). Но реальная цель для создания модуля — академическая, понять как работает морфологический анализатор.
Алгоритм
Для начала разберемся, как обычный человек определяет к какой части речи относится слово.
- Обычно мы знаем к какой части речи относится знакомое нам слово. Например, мы знаем, что “съешьте” — это глагол.
- Если нам встречается слово, которое мы не знаем, то мы можем угадать часть речи, сравнивая с уже знакомыми словами. Например, мы можем догадаться, что слово “конгруэнтность” — это существительное, т.е. имеет окончание “-ость”, присущее обычно существительным.
- Мы также можем догадаться какая это часть речи, проследив за цепочкой слов в предложении: “съешьте французских x” — в этом примере, х скорее всего будет существительным.
- Длина слова также может дать полезную информацию. Если слово состоит всего лишь из одной или двух букв, то скорее всего это предлог, местоимение или союз.
Конечно, для компьютера эта задача будет несколько сложнее, т.к. у него нет той базы знаний, которой обладает человек. Но мы постараемся смоделировать обучение компьютера, используя доступные нам данные.
Данные
Для обучения нашего скрипта я использовал национальный корпус русского языка. Часть корпуса, СинТагРус, представляет собой коллекцию текстов с размеченной информацией для каждого слова, такой как, часть речи, число, падеж, время глагола и т.д. Так выглядит часть корпуса в XML формате:
<se>
<w><ana lex="между" gr="PR"></ana>М`ежду</w>
<w><ana lex="то" gr="S-PRO,n,sg=ins"></ana>тем</w>
<w><ana lex="конкурент" gr="S,m,anim=pl,nom"></ana>конкур`енты</w>
<w><ana lex="наступать" gr="V,ipf,intr,act=pl,praes,3p,indic"></ana>наступ`ают</w>
<w><ana lex="на" gr="PR"></ana>на</w>
<w><ana lex="пятка" gr="S,f,inan=pl,acc"></ana>п`ятки</w> .
</se>
<se>
<w><ana lex="вот" gr="PART"></ana>Вот</w>
<w><ana lex="так" gr="ADV-PRO"></ana>так</w>,
<w><ana lex="за" gr="PR"></ana>з`а</w>
<w><ana lex="пять" gr="NUM=acc"></ana>пять</w>
<w><ana lex="минута" gr="S,f,inan=pl,gen"></ana>мин`ут</w>
<w><ana lex="до" gr="PR"></ana>до</w>
<w><ana lex="съемка" gr="S,f,inan=pl,gen"></ana>съёмок</w> ,
<w><ana lex="родиться" gr="V,pf,intr,med=m,sg,praet,indic"></ana>род`илс`я</w>
<w><ana lex="новый" gr="A=m,sg,nom,plen"></ana>н`овый</w>
<w><ana lex="персонаж" gr="S,m,anim=sg,nom"></ana>персон`аж</w> .
</se>
Предложения заключены в теги <se>, внутри которых расположены слова в теге <w>. Информация о каждом слове содержится в теге <ana>, аттрибут lex соответствует лексеме, gr — грамматические категории. Первая категория — это часть речи:
'S': 'сущ.',
'A': 'прил.',
'NUM': 'числ.',
'A-NUM': 'числ.-прил.',
'V': 'глаг.',
'ADV': 'нареч.',
'PRAEDIC': 'предикатив',
'PARENTH': 'вводное',
'S-PRO': 'местоим. сущ.',
'A-PRO': 'местоим. прил.',
'ADV-PRO': 'местоим. нареч.',
'PRAEDIC-PRO': 'местоим. предик.',
'PR': 'предлог',
'CONJ': 'союз',
'PART': 'частица',
'INTJ': 'межд.'
SVM
В качестве алгоритма обучения я выбрал метод опорных векторов (SVM). Если вы не знакомы с SVM или алгоритмами машинного обучения в общем, то представьте, что SVM это некий черный ящик, который принимает на вход характеристики данных, а на выходе классификацию по заранее заданным категориям. В качестве характеристик мы зададим, например, окончание слова, а в качестве категорий — части речи.
Чтобы черный ящик автоматически распознавал часть речи, для начала его нужно обучить, т.е. дать много характеристик примеров на вход, и соответствующие им части речи на выход. SVM построит модель, которая при достаточных данных будет в большинстве случаев корректно определять часть речи.
Даже в академических целях реализовать SVM лень, поэтому воспользуемся готовой библиотекой LIBLINEAR на С++, которая имеет обертку для питона. Для обучения модели используем функцию train(prob, param), которая принимает в качестве первого аргумента задачу: problem(y, x), где y — это массив частей речи для каждого примера из массива x. Каждый пример представлен в свою очередь вектором характеристик. Чтобы добиться такой постановки задачи, нам нужно сначала соотнести каждую часть речи и каждую характеристику с неким числовым номером. Например:
'''
съешьте - глагол
выпейте - глагол
чаю - сущ.
'''
x = [{1001: 1, 2001: 1, 3001: 1}, # 1001 - съешьте, 2001 - ьте, 3001 - те
{1002: 1, 2002: 1, 3001: 1}, # 1002 - выпейте, 2002 - йте, 3001 - те
{1003: 1, 2003: 1, 3002: 1}] # 1003 - чаю, 2003 - чаю, 3002 - аю
y = [1, 1, 2] # 1 - глагол, 2 - сущ.
import liblinearutil as svm
problem = svm.problem(y, x) # создаем задачу
param = svm.parameter('-c 1 -s 4') # параметры обучения
model = svm.train(prob, param) # обучаем модель
# используем модель для распознания слова 'съешьте'
label, acc, vals = svm.predict([0], {1001: 1, 2001: 1, 3001: 1}, model, '') # [0] - обозначает, что часть речи нам неизвестна
В итоге наш алгоритм такой:
- Читаем файл корпуса и для каждого слова определяем его характеристики: само слово, окончание (2 и 3 последних буквы), приставка (2 и 3 первые буквы), а также части речи предыдущих слов
- Каждой части речи и характеристике присваиваем порядковый номер и создаем задачу для обучения SVM
- Обучаем модель SVM
- Используем обученную модель для определения части речи слов в предложении: для этого каждое слово нужно опять представить в виде характеристик и подать на вход SVM модели, которая подберет наиболее подходящий класс, т.е. часть речи.
Реализация
С исходными кодами можете ознакомиться здесь: github.com/irokez/Pyrus/tree/master/src
Корпус
Для начала нужно получить размеченный корпус. Национальный корпус русского языка распространяется очень загадочным образом. На самом сайте корпуса можно только производить поиск по текстам, но при этом скачать целиком корпус нельзя:
“Оффлайновая версия корпуса недоступна, однако для свободного пользования предоставляется случайная выборка предложений (с нарушенным порядком) из корпуса со снятой омонимией объёмом 180 тыс. словоупотреблений (90 тыс. – пресса, по 30 тыс. из художественных текстов, законодательства и научных текстов)”.
При этом в википедии написано
“The corpus will be made available off-line and distributed for non-commercial purposes, but currently due to some technical and/or copyright problems it is accessible only on-line.”
Хотя для наших целей пойдет и небольшая выборка из корпуса, доступная тут: www.ruscorpora.ru/download/shuffled_rnc.zip
Файлы в полученном архиве нужно пропустить через утилиту convert-rnc.py, которая переводит текст в UTF-8 и исправляет XML разметку. После этого, возможно, еще нужно пофиксить XML вручную (xmllint вам в помощь). Файл rnc.py содержит простой класс Reader для чтения нормализованных XML файлов нац. корпуса.
import xml.parsers.expat
class Reader:
def __init__(self):
self._parser = xml.parsers.expat.ParserCreate()
self._parser.StartElementHandler = self.start_element
self._parser.EndElementHandler = self.end_element
self._parser.CharacterDataHandler = self.char_data
def start_element(self, name, attr):
if name == 'ana':
self._info = attr
def end_element(self, name):
if name == 'se':
self._sentences.append(self._sentence)
self._sentence = []
elif name == 'w':
self._sentence.append((self._cdata, self._info))
elif name == 'ana':
self._cdata = ''
def char_data(self, content):
self._cdata += content
def read(self, filename):
f = open(filename)
content = f.read()
f.close()
self._sentences = []
self._sentence = []
self._cdata = ''
self._info = ''
self._parser.Parse(content)
return self._sentences
Метод Reader.read(self, filename) читает файл и выдает список предложений:
[[('Вод`итель', {'lex': 'водитель', 'gr': 'S,m,anim=sg,nom'}), ('дес`ятки', {'lex': 'десятка', 'gr': 'S,f,inan=sg,gen'}), ('кот`орую', {'lex': 'который', 'gr': 'A-PRO=f,sg,acc'}), ('прест`упники', {'lex': 'преступник', 'gr': 'S,m,anim=pl,nom'}), ('пойм`али', {'lex': 'поймать', 'gr': 'V,pf,tran=pl,act,praet,indic'}), ('у', {'lex': 'у', 'gr': 'PR'}), ('ВВЦ', {'lex': 'ВВЦ', 'gr': 'S,m,inan,0=sg,gen'}), ('оказ`ал', {'lex': 'оказать', 'gr': 'V,pf,tran=m,sg,act,praet,indic'}), ('им', {'lex': 'они', 'gr': 'S-PRO,pl,3p=dat'}), ('`яростное', {'lex': 'яростный', 'gr': 'A=n,sg,acc,inan,plen'}), ('сопротивл`ение', {'lex': 'сопротивление', 'gr': 'S,n,inan=sg,acc'}), ('за', {'lex': 'за', 'gr': 'PR'}), ('что', {'lex': 'что', 'gr': 'S-PRO,n,sg=acc'}), ('поплат`ился', {'lex': 'поплатиться', 'gr': 'V,pf,intr,med=m,sg,praet,indic'}), ('ж`изнью', {'lex': 'жизнь', 'gr': 'S,f,inan=sg,ins'})]]
Обучение и разметка текста
Библиотеку SVM можно скачать тут: http://www.csie.ntu.edu.tw/~cjlin/liblinear/. Чтобы обертка под питон заработала под 3-й версией я написал небольшой патч.
Файл pos.py содержит два основных класса: Tagger и TaggerFeatures. Tagger — это, собственно, класс, который осуществляет разметку текста, т.е. определяет для каждого слова его часть речи. Метод Tagger.train(self, sentences, labels) принимает в качестве аргументов список предложений (в том же формате, что и выдает rnc.Reader.read), а также список частей речи для каждого слова, после чего обучает SVM модель, используя библиотеку LIBLINEAR. Обученная модель впоследствии сохраняется (через метод Tagger.save), чтобы не обучать модель каждый раз. Метод Tagger.label(self, sentence) производит разметку предложения.
Класс TaggerFeatures предназначен для генерации характеристик для обучения и разметки. TaggerFeatures.from_body() возвращает характеристику по форме слова, т.е. возвращает ID слова в корпусе. TaggerFeatures.from_suffix() и TaggerFeatures.from_prefix() генерируют характеристики по окончанию и приставке слов.
Чтобы запустить обучение модели, был написан скрипт train.py, который читает файлы корпуса при помощи rnc.Reader, а затем вызывает метод Tagger.train:
import sys
import re
import rnc
import pos
sentences = []
sentences.extend(rnc.Reader().read('tmp/media1.xml'))
sentences.extend(rnc.Reader().read('tmp/media2.xml'))
sentences.extend(rnc.Reader().read('tmp/media3.xml'))
re_pos = re.compile('([w-]+)(?:[^w-]|$)'.format('|'.join(pos.tagset)))
tagger = pos.Tagger()
sentence_labels = []
sentence_words = []
for sentence in sentences:
labels = []
words = []
for word in sentence:
gr = word[1]['gr']
m = re_pos.match(gr)
if not m:
print(gr, file = sys.stderr)
pos = m.group(1)
if pos == 'ANUM':
pos = 'A-NUM'
label = tagger.get_label_id(pos)
if not label:
print(gr, file = sys.stderr)
labels.append(label)
body = word[0].replace('`', '')
words.append(body)
sentence_labels.append(labels)
sentence_words.append(words)
tagger.train(sentence_words, sentence_labels, True)
tagger.train(sentence_words, sentence_labels)
tagger.save('tmp/svm.model', 'tmp/ids.pickle')
После того, как модель обучена и сохранена, мы, наконец, получили скрипт для разметки текста. Пример использования показан в test.py:
import sys
import pos
sentence = sys.argv[1].split(' ')
tagger = pos.Tagger()
tagger.load('tmp/svm.model', 'tmp/ids.pickle')
rus = {
'S': 'сущ.',
'A': 'прил.',
'NUM': 'числ.',
'A-NUM': 'числ.-прил.',
'V': 'глаг.',
'ADV': 'нареч.',
'PRAEDIC': 'предикатив',
'PARENTH': 'вводное',
'S-PRO': 'местоим. сущ.',
'A-PRO': 'местоим. прил.',
'ADV-PRO': 'местоим. нареч.',
'PRAEDIC-PRO': 'местоим. предик.',
'PR': 'предлог',
'CONJ': 'союз',
'PART': 'частица',
'INTJ': 'межд.',
'INIT': 'инит',
'NONLEX': 'нонлекс'
}
tagged = []
for word, label in tagger.label(sentence):
tagged.append((word, rus[tagger.get_label(label)]))
print(tagged)
Работает так:
$ src/test.py "Съешьте еще этих мягких французских булок, да выпейте же чаю"
[('Съешьте', 'глаг.'), ('еще', 'нареч.'), ('этих', 'местоим. прил.'), ('мягких', 'прил.'), ('французских', 'прил.'), ('булок,', 'сущ.'), ('да', 'союз'), ('выпейте', 'глаг.'), ('же', 'частица'), ('чаю', 'сущ.')]
Тестирование
Для оценки точности классификации работы алгоритма, метод обучения Tagger.train() имеет необязательного параметр cross_validation, который, если установлен как True, выполнит перекрестную проверку, т.е. данные обучения разбиваются на K частей, после чего каждая часть по очереди используется для оценки работы метода, в то время как остальная часть используется для обучения. Мне удалось добиться средней точности в 92%, что вполне неплохо, учитывая, что была использована лишь доступная часть нац. корпуса. Обычно точность разметки части речи колеблется в пределах 96-98%.
Заключение и планы на будущее
В общем, было интересно поработать с нац. корпусом. Видно, что работа над ним проделана большая, и в нем содержится большое количество информации, которую хотелось бы использовать в полной мере. Я послал запрос на получение полной версии, но ответа пока, к сожалению, нет.
Полученный скрипт разметки можно легко расширить, чтобы он также определял другие морфологические категории, например, число, род, падеж и др. Чем я и займусь в дальнейшем. В перспективе хотелось бы, конечно, написать синтаксический парсер русского языка, чтобы получить структуру предложения, но для этого нужна полная версия корпуса.
Буду рад ответить на вопросы и предложения.
Исходный код доступен здесь: github.com/irokez/Pyrus
Демо: http://vps11096.ovh.net:8080
{{ word.morphem }}
{{ word.word }}
Характеристика
По цели высказывания: {{ purpose[syn.purpose] }}
По интонации: {{ emotionalColoring[syn.emotionalColoring] }}
Простое
Сложное
По наличию главных членов: {{ mainMembers[syn.mainMembers] }}
По наличию второстепенных членов: {{ minorMembers[syn.minorMembers] }}
Не осложнено обращениями, вводными словами и однородными членами
Осложнено однородными членами
Осложнено вводными словами
Осложнено обращениями
Сложносочинённое предложение. Количество простых предложений: {{ syn.countRoot }}.
Cложноподчиненное предложение.
- {{ error }}
{{ infoService }}
Онлайн программа «Части речи в предложении Текстовод» бесплатно осуществляет помощь в определении частей речи в вашем предложении.
Гости нашего сайта, не прошедшие регистрацию, могут разобрать до 10 000 символов.
Обратите внимание на PRO-версию.
Оформив её, вам становятся доступны для проверки 100 000 знаков, в то время, как обычным зарегистрированным пользователям открыт доступ к 15 000 символов.
Также, для PROдвинутых пользователей выделяется индивидуальная очередь, что ускоряет выполнение задач.
Немаловажным является то, что выбирая PRO-версию, для вас отключается показ рекламы.
Чтобы воспользоваться сервисом, нужно ввести в рабочее поле ваше предложение и нажать кнопку «Выполнить».
Результатом является анализ каждого слова по-отдельности, схема слов с указанием части речи и их выделением как на письме.
Расшифровку аббревиатуры частей речи вы можете посмотреть, нажав на кнопку «словарь сокращений» (под разбором).
Разберём понятие частей речи.
Когда несколько слов имеют единые морфологические признаки, одинаковое грамматическое и лексическое значение, а также выполняют общие синтаксические функции — их можно считать частями речи.
В 1757 г. М.В. Ломоносов первый раз описал части речи русского языка.
На сегодняшний день лингвисты до конца не определились с их количеством.
Выделяют 3 основные группы частей речи:
-
самостоятельные (имена существительные, прилагательные, числительные, местоимения, глаголы, причастия, деепричастия, наречия),
-
служебные (предлоги, союзы, частицы),
-
междометия.
Забавные факты о частях речи.
Имя cyщecтвитeльнoe – «xлeб» pyccкoгo языкa. Оно является основной частью речи. «Все танцы с бубнами» происходят вокруг существительного. Хлебом не только утоляют голод, он считается символом благополучия. B имени cyщecтвитeльнoм находится вcя вaжнocть выcкaзывaния, eгo нaличиe определяет единство вceгo пpeдлoжeния.
Известно около 250 союзов, а в основном используют три: А, И, ИЛИ.
В русском языке всего 3 формы глагола (настоящее, прошлое и будущее), а в английском языке их 6!
Во Вьетнаме существует восемь местоимений для 1 л в ед. числе, в то время как в России используют универсальное «Я». Применение местоимений во вьетнамском языке зависит от пола человека, а также его социального положения.
DeepMorphy
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 состоит из 11 «подсетей»:
- 8 двунаправленных рекурентных сетей, по одной для каждой поддерживаемой грамматической категории
(определяет граммему в категории); - 1 двунаправленная рекурентная сеть для определения самых вероятных тегов. Для каждой комбинации граммем из датасета заведен
1 тег (всего 229 тегов), сеть обучается на определение к каким классам может принадлежать данное слово. На этапе
работы берется 4 самых вероятных тега; - 1 seq2seq модель для лемматизации.
Задача изменения формы слов решается 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).
- 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);
Леммы можно получить из тегов слова:
Console.WriteLine(morphInfo.BestTag.Lemma);
Проверка, есть ли у данного слова лемма:
morphInfo.HasLemma("королевский");
Метод CanBeSameLexeme может быть использован для нахождения слов одной лексемы:
// выводим все слова, которые могут быть формой слова королевский var words = new string[] { "королевский", "королевские", "корабли", "пересказывают", "королевского" }; var results = morph.Parse(words).ToArray(); var mainWord = results[0]; foreach (var morphInfo in results) { if (mainWord.CanBeSameLexeme(morphInfo)) Console.WriteLine(morphInfo.Text); }
Если необходима только лемматизация без морфологического разбора, то нужно использовать метод Lemmatize:
var tasks = new [] { new LemTask("синяя", morph.TagHelper.CreateTag("прил", gndr: "жен", nmbr: "ед", @case: "им")), new LemTask("гуляя", morph.TagHelper.CreateTag("деепр", tens: "наст")) }; var lemmas = morph.Lemmatize(tasks).ToArray(); foreach (var lemma in lemmas) Console.WriteLine(lemma);
Изменение формы слова
DeepMorphy умеет изменять форму слова в рамках лексемы, список поддерживаемых словоизменений тут.
Словарные слова возможно изменять только в рамках тех форм, которые доступны в словаре.
Для изменения формы слов используется метод Inflect,
на вход принимает перечисление объектов InflectTask (содержит исходное слово, тег исходного слова и тег, в который слово нужно поставить).
На выходе перечисление с требуемыми формами (если форму не удалось обработать, то null).
var tasks = new[] { new InflectTask("синяя", morph.TagHelper.CreateTag("прил", gndr: "жен", nmbr: "ед", @case: "им"), morph.TagHelper.CreateTag("прил", gndr: "муж", nmbr: "ед", @case: "им")), new InflectTask("гулять", morph.TagHelper.CreateTag("инф_гл"), morph.TagHelper.CreateTag("деепр", tens: "наст")) }; var results = morph.Inflect(tasks); foreach (var result in results) Console.WriteLine(result);
Так же для слова имеется возможность получить все его формы с помощью метода Lexeme
(для словарных слов возвращает все из словаря, для остальных все формы из поддерживаемых словоизменений).
var word = "лемматизировать"; var tag = m.TagHelper.CreateTag("инф_гл"); var results = m.Lexeme(word, tag).ToArray();
Одной из особенностей алгоритма является то, что при изменении формы или генерации лексемы,
сеть может «выдумать» несуществующую (гипотетическую) форму слова, форму которая не употребляется в языке. Например,
ниже получится слово «побежу», хотя в данный момент в языке оно не особо используется.
var tasks = new[] { new InflectTask("победить", m.TagHelper.CreateTag("инф_гл"), m.TagHelper.CreateTag("гл", nmbr: "ед", tens: "буд", pers: "1л", mood: "изъяв")) }; Console.WriteLine(m.Inflect(tasks).First());
Cтруктура репозитория
- Python код модели, обучения и разные утилиты.
- C# код DeepMorphy.
- Проект с C# примерами.
- Файлы последнего релиза.
Планы по возмодным доработкам
- Обновление версии tensorflow.
- Подумать над использованием трансформеров.
Определение части речи
Как определить части речи
Чтобы определить часть речи слова, вам достаточно ввести слово в форму поиска выше и нажать «Поиск». Система автоматически найдет слово в базе данных и выдаст вам результат.
Признаки частей речи
Части речи это группы слов, имеющие общие признаки значения, синтаксиса и морфологии. Интуитивно мы можем определить часть речи по вопросу, на который отвечает данное слово.
- Имя существительное обозначает предмет и отвечает на вопрос кто ? что ? в исходной форме
- Имя прилагательное обозначает качество, признак или принадлежность предмета и отвечает на вопрос какой ? чей ?
- Глагол обозначает действие или состояние, отвечает на вопрос что делать ? что сделать ?
- Имя числительное обозначает количество или порядковый номер, и отвечает на вопрос сколько ? который по счёту ?
- Местоимение указывает на предмет, но не определяет его, отвечает на вопрос кто ? что ?
- Наречие описывает признак действия и отвечает на вопросы где ? когда ? как ? почему ?
- Союз выражает смысловые отношения между частями речи, служебное слово
- Предлог выражает синтаксическую зависимость объектов от других частей речи, служебное слово
- Частица служит для выражение эмоциональных и смысловых оттенков
- Междометие непосредственно выражает эмоции и желания, часто использует звукоподражание
- Причастие выражает признак предмета по действию
- Деепричастие выражает добавочное действие при основном действии
Прежде всего в разных языках разделяют имя, то есть описание объекта, и глагол, то есть описание действия. Из этого базового деления происходит более точная классификация, разделение имени на прилагательное и существительное, а также выделение различных глагольных форм, таких как причастие и деепричастие.
Некоторые части речи являются спорными, и выделяются не всеми. К ним относятся, например, имена состояния, модальные слова и звукоподражания.
Для начала разберемся с терминологией: для предложения существует только синтаксический разбор. В этом разборе указывается подлежащее, сказуемое и другие члены предложения. Указать часть речи можно только для слова. Тем не менее, в синтаксический разбор предложения часто входит задача указать часть речи для каждого слова. И есть сервисы, где можно ввести либо предложение целиком, либо по одному слову. И они дают вам информацию по частям речи для каждого слова.
Goldlit
В этом сервисе можно ввести предложение целиком и получить морфологический разбор каждого слова. В который, конечно же, входит и часть речи. Например:
Как видите, обнаруженное слово ставится в начальную форму и для него указывается:
- Начальная форма.
- Часть речи.
- Грамматика – что тут указывается зависит от части речи.
- Формы слова.
Нас интересует часть речи. Но будьте внимательны сервис недостаточно умен, чтобы всегда корректно его определить. Например, частица «уж». В приведенном предложении это частица, а никакое не существительное «уж». Про ужей тут речи не идет. Так что результаты пословного разбора предложения надо перепроверять вручную. В этом смысле он ничуть не выигрывает у сервисов, где вводится одно слово. Если б он мог из контекста определить значение, то выигрывал бы, а так – нет. Так что обратите внимание обычные на онлайн-сервисы морфологического разбора, они работают также. У меня один такой сервис описан тоже дальше.
Возьмем другой пример, повествовательное предложение «Времени нет».
Что скажет нам Goldlit?
Начальная форма: ВРЕМЕНИТЬ
Часть речи: глагол в личной форме
Грамматика: второе лицо, действительный залог, единственное число, переходный, несовершенный вид, повелительное наклонение (императив)
То есть слово «времени» он понял как глагол «повремени». Что явно неправда. Дальше правда указывается второе значение:
Начальная форма: ВРЕМЯ
Часть речи: существительное
Грамматика: единственное число, неодушевленное, родительный падеж, средний род
Формы: время, времени, временем, времена, времён, временам, временами, временах
Это уже правильно, существительное. В общем будьте внимательны при использовании подобных сервисов.
Чтобы воспользоваться сервисом:
- Перейдите по адресу http://goldlit.ru/component/slog
- Введите предложение.
- Вы получите морфологический разбор каждого слова.
Викислово
Здесь вы можете указать только одно слово. При этом дополнительно вам скажут, каким членом предложения может быть это слово. Либо один вариант, либо, если их несколько, то так и пишут. Разбор слова «времени» выглядит примерно так же:
А вот разбор слова «добавив». Тут уже однозначно указан член предложения. Это обстоятельство. Потому что деепричастия всегда являются обстоятельствами.
Morphologyonline
Этот сервис мне нравится даже больше: дизайн чище, рекламы меньше, работает не хуже.
Чтобы воспользоваться:
- Перейдите на сайт http://morphologyonline.ru/
- Введите слово в пустое поле.
- Щелкните кнопку «Разобрать».
Вы получите разбор:
Здесь третьей строкой идет «Синтаксическая роль». Это то же самое, что «Член предложения» в сервисе Викислово.
А второй строкой идут «Морфологические признаки», которые в первом сервисы обозначены как «Грамматика». Тут они прописаны лучше, так как разделены на постоянные и непостоянные. Имеется в виду, что в зависимости от формы слова, какие-то признаки могут меняться, а какие-то нет. Например, существительное «время» всегда нарицательное, неодушевленное, среднего рода и 2 склонения. Это постоянные признаки. А падеж и число могут меняться в зависимости от формы слова: «нет времени», «дай время», «с давних времен». Тут родительный, дательный, снова родительный падеж. И единственное, снова единственное и множественное число. Какую форму слова «время» употребишь, такой падеж и число и будет. Потому это непостоянные признаки.
Морфологический vs Морфемный
Морфологический разбор слова следует отличать от морфемного. В морфемный разбор слова входит определение корня, суффикса, окончания, основы слова. Для морфемного разбора тоже есть много сервисов, они описаны тут.
Синтаксический разбор предложения
Если вам надо определить не только части речи всех слов предложения, но и разобрать предложение в целом: по цели высказывания, подчеркнуть члены предложения, то используйте шпаргалки, коих полно в интернете. Кое-где можно даже поупражняться онлайн. Сравнение подобный сервисов у меня тут.