Наука о частях речи, морфология, – один из сложнейших разделов языкознания. Определение частей речи – чрезвычайно важное и полезное умение. Зачастую орфографические правила основываются на морфологическом принципе, а потому уметь отличить, например, прилагательное от причастия важно каждому грамотному человеку.
Однако если отличить существительное от глагола бывает несложно, то с другими частями речи возникает множество вопросов. Ну-ка, вы определите сходу, какой частью речи является слово в течение? когда? первый? тоже? Сделать это подчас сложно и дипломированным филологам.
Сервис онлайн определения частей речи поможет вам выпутаться из этого затруднительного положения. Все, что вам нужно, – ввести слово, и программа сама безошибочно определит его частеречную характеристику.
Достоинств у нашего инструмента множество:
- Доступность. Бесплатно и в любую секунду вы можете вписать слово и получить ответ.
- Точность. Вы можете полагаться на результаты онлайн определения частей речи. Эта программа вас не подведет.
- Удобство. Поставить курсор на «Ваше слово», набрать искомое слово и кликнуть на «Определить». Это все! С этой задачей справится любой пользователь.
- Простота. Вы получаете четкий ответ на поставленный вопрос: только часть речи, ничего лишнего.

Рядом — какая часть речи? Как определить? Рассказываю за 3 минуты! | Русский язык
Обратная связь
Источник: best-language.ru
Определение части речи слов в русском тексте (POS-tagging) на Python 3
Пусть, дано предложение “Съешьте еще этих мягких французских булок, да выпейте чаю.”, в котором нам нужно определить часть речи для каждого слова:
[(‘съешьте’, ‘глаг.’), (‘еще’, ‘нареч.’), (‘этих’, ‘местоим. прил.’), (‘мягких’, ‘прил.’), (‘французских’, ‘прил.’), (‘булок’, ‘сущ.’), (‘да’, ‘союз’), (‘выпейте’, ‘глаг.’), (‘чаю’, ‘сущ.’)]
Зачем это нужно? Например, для автоматического определения тегов для блог-поста (для отбора существительных). Морфологическая разметка является одним из первых этапов компьютерного анализа текста.
Существующие решения
Конечно, все уже придумано до нас. Существует mystem от Яндекса, TreeTagger с поддержкой русского языка, на питоне есть nltk, а также pymorphy от kmike. Все эти утилиты отлично работают, правда, у pymorphy нет поддержки питона 3, а у nltk поддержка третей версии питона только в бете (и там вечно что-то отваливается). Но реальная цель для создания модуля — академическая, понять как работает морфологический анализатор.
Алгоритм
- Обычно мы знаем к какой части речи относится знакомое нам слово. Например, мы знаем, что “съешьте” — это глагол.
- Если нам встречается слово, которое мы не знаем, то мы можем угадать часть речи, сравнивая с уже знакомыми словами. Например, мы можем догадаться, что слово “конгруэнтность” — это существительное, т.е. имеет окончание “-ость”, присущее обычно существительным.
- Мы также можем догадаться какая это часть речи, проследив за цепочкой слов в предложении: “съешьте французских x” — в этом примере, х скорее всего будет существительным.
- Длина слова также может дать полезную информацию. Если слово состоит всего лишь из одной или двух букв, то скорее всего это предлог, местоимение или союз.
Данные
Для обучения нашего скрипта я использовал национальный корпус русского языка. Часть корпуса, СинТагРус, представляет собой коллекцию текстов с размеченной информацией для каждого слова, такой как, часть речи, число, падеж, время глагола и т.д. Так выглядит часть корпуса в XML формате:
Части речи в русском языке. Как определить часть речи?
М`ежду тем конкур`енты наступ`ают на п`ятки . Вот так, з`а пять мин`ут до съёмок , род`илс`я н`овый персон`аж .
Предложения заключены в теги , внутри которых расположены слова в теге . Информация о каждом слове содержится в теге , аттрибут 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 — съешьте, 2001 — ьте, 3001 — те , # 1002 — выпейте, 2002 — йте, 3001 — те ] # 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], , 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) читает файл и выдает список предложений:
[[(‘Вод`итель’, ), (‘дес`ятки’, ), (‘кот`орую’, ), (‘прест`упники’, ), (‘пойм`али’, ), (‘у’, ), (‘ВВЦ’, ), (‘оказ`ал’, ), (‘им’, ), (‘`яростное’, ), (‘сопротивл`ение’, ), (‘за’, ), (‘что’, ), (‘поплат`ился’, ), (‘ж`изнью’, )]]
Обучение и разметка текста
Библиотеку 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
- natural language processing
- компьютерная лингвистика
- pos-tagging
- морфология
- python3
- Python
- Natural Language Processing
Источник: habr.com