Определение части речи слов в русском тексте (POS-tagging) на Python 3

    Пусть, дано предложение “Съешьте еще этих мягких французских булок, да выпейте чаю.”, в котором нам нужно определить часть речи для каждого слова:

    [('съешьте', 'глаг.'), ('еще', 'нареч.'), ('этих', 'местоим. прил.'), ('мягких', 'прил.'), ('французских', 'прил.'), ('булок', 'сущ.'), ('да', 'союз'), ('выпейте', 'глаг.'), ('чаю', 'сущ.')]

    Зачем это нужно? Например, для автоматического определения тегов для блог-поста (для отбора существительных). Морфологическая разметка является одним из первых этапов компьютерного анализа текста.

    Существующие решения


    Конечно, все уже придумано до нас. Существует 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] - обозначает, что часть речи нам неизвестна
    


    В итоге наш алгоритм такой:
    1. Читаем файл корпуса и для каждого слова определяем его характеристики: само слово, окончание (2 и 3 последних буквы), приставка (2 и 3 первые буквы), а также части речи предыдущих слов
    2. Каждой части речи и характеристике присваиваем порядковый номер и создаем задачу для обучения SVM
    3. Обучаем модель SVM
    4. Используем обученную модель для определения части речи слов в предложении: для этого каждое слово нужно опять представить в виде характеристик и подать на вход 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
    Метки:
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 74
    • 0
      Лев Толстой, обучая детей грамоте говорил, что в русском языке определить часть речи можно совсем не зная значения ни одного слова в предложении: «Гло́кая ку́здра ште́ко будлану́ла бо́кра и курдя́чит бокрёнка»

      • +18
        И он был прав:
        Глокая куздра штеко будланула бокра и курдячит бокрёнка
        [('Глокая', 'прил.'), ('куздра', 'сущ.'), ('штеко', 'нареч.'), ('будланула', 'глаг.'), ('бокра', 'сущ.'), ('и', 'союз'), ('курдячит', 'глаг.'), ('бокрёнка', 'сущ.')]
        Tagged 8 words in 0.0 sec, 10870 words per sec
        • +1
          Первой идеей тоже было проверить на классике:
          Варкалось. Хливкие шорьки. Пырялись по наве, И хрюкотали зелюки, Как мюмзики в мове.
          [('Варкалось', 'глаг.'), ('Хливкие', 'прил.'), ('шорьки', 'сущ.'), ('Пырялись', 'глаг.'), ('по', 'предлог'), ('наве', 'сущ.'), ('И', 'союз'), ('хрюкотали', 'глаг.'), ('зелюки', 'сущ.'), ('Как', 'союз'), ('мюмзики', 'сущ.'), ('в', 'предлог'), ('мове', 'сущ.')]
          Tagged 13 words in 0.0 sec, 8837 words per sec
          • 0
            «Закусочная турка нагло обманула казака и дурачит казачонка»,
            «Дикая собака динго трепанула волка и увечит волчонка»,
            «Слушая брата сильно психанула сестра и молчит сестренка»,
            «Завидевшая обидчика высоко маханула обезьяна и манит обезьяненка»

            Проверьте, пожалуйста, сами, мне лень.
          • +1
            Указанная вами фраза какое-то отношение ко Льву Николаевичу имеет?
            • НЛО прилетело и опубликовало эту надпись здесь
              • +2
                Я пью кофе.
                [('Я', 'местоим. сущ.'), ('пью', 'сущ.'), ('кофе', 'сущ.')]
              • 0
                Но не всегда это срабатывает:

                Косил косой косой косой.
                [('Косил', 'глаг.'), ('косой', 'сущ.'), ('косой', 'сущ.'), ('косой', 'сущ.')]
                Tagged 4 words in 0.0 sec, 7182 words per sec
              • +6
                Можно еще обратить внимание на пока еще небольшой, но активно растущий открытый русскоязычный корпус opencorpora.org.
                • 0
                  спасибо, гляну обязательно
                • 0
                  интересно как быстро это будет работать на больших объемах текста?
                  • +1
                    Демо выдает приблизительный подсчет производительности в районе 7-10 тыс слов в секунду.
                  • +6
                    Интересно, как скрипт отреагирует на слова:
                    Расстегай (из анекдота про прапорщика, ага),
                    Перестройка,
                    Непроливайка
                    и каламбуры из анекдотов про Штирлица типа «Из окна дуло. Штирлиц подошел к окну. Дуло исчезло»
                    • +4
                      [('Из', 'предлог'), ('окна', 'сущ.'), ('дуло', 'глаг.'), ('Штирлиц', 'сущ.'), ('подошел', 'глаг.'), ('к', 'предлог'), ('окну', 'сущ.'), ('Дуло', 'глаг.'), ('исчезло', 'глаг.')]

                      фейл на втором дуле)
                      • 0
                        Что лишний раз показывает, что распознавание текста/речи прямолинейными числодробительными методами не работает. Нужен контекст.
                        Впрочем, и человек-то не всегда может распознать что сказано или написано, чего уж тут о компьютерах говорить.
                        • +2
                          Контекст также прекрасно отрабатывается машинными методами. Можно использовать n-gram'ы или даже уже размеченные слова. К примеру, после предлога вероятность следования глагола очень мала.
                          • 0
                            Можно прикрутить наивного Байеса и прогнать через него паросочетания частей речи (по большому тексту) для дальнейшего определения корректности классификации.
                            • 0
                              Да. Но можно и SVM тот же использовать для этих целей. Чуть-чуть изменив алгоритм. Гляньте мой коммент ниже.
                    • 0
                      Можно посмотреть ещё АОТ.
                      • 0
                        да, АОТ предоставляет очень хорошие ресурсы для морф. разбора слов. я использовал их словари для лемматизации в другом проекте
                      • +2
                        как на счет фразы про пьяного, раскосого зайца, срезающего траву с помощью кривой косы?

                        косой, косой косой косой косил косой.

                        Хотя и человек такое не сразу осилит…
                        • 0
                          косой, косой косой косой косил косой.

                          [('косой', 'сущ.'), ('косой', 'сущ.'), ('косой', 'сущ.'), ('косой', 'сущ.'), ('косил', 'глаг.'), ('косой', 'сущ.')]

                          • +1
                            вы сломали мне моск
                            огласите верный ответ, пжлст
                            запятая меня сбивает с толку
                            • –1
                              С запятой все понятно — это обращение. А вот дальше больше 2х «косой» объяснить не могу.
                            • +1
                              Косой (пьяный), косой (раскосый) косой (заяц) косой (кривой) косил косой.
                        • 0
                          Полезная статья, добавил в избранное, спасибо. Продолжайте цикл статей.
                          • 0
                            спасибо, постараюсь продолжить
                          • +1
                            В большинстве случаев работает, но вот почему-то на этом примере не хочет:
                            Мама мыла раму
                            [('Мама', 'сущ.'), ('мыла', 'сущ.'), ('раму', 'сущ.')]

                            • 0
                              я помню в яндексе, на этой фразе тоже крышу сносило (где-то в wordstat )
                              • 0
                                Почему-то многие программы на этой фразе застревают, TreeTagger тоже неправильно обрабатывает.
                              • 0
                                Программа знает русский язык лучше меня.
                                • +1
                                  mystem предлагает следующую тестовую строку:
                                  «В мурелки шлепают пельсиски. В стакелках светится мычай.»
                                  пельсиски — наречие

                                  А я взял в качестве тест-кейсов русские скороговорки:
                                  • Карл у Клары украл кораллы, а Клара у Карла украла кларнет.
                                    кларнет — глагол
                                  • Курил турка трубку, клевала курка крупку: не кури, турка, трубки, не клюй, курка, крупки!
                                    клевала — существительное
                                    кури — существительное
                                    клюй — существительное
                                  • Наши поезда — самые поездатые поезда в мире, и никакие другие поезда не перепоездадят наши поезда по поездатости.
                                    поездатые — глагол

                                  Почти круто, всегда можно немножко доработать :-)
                                  • +1
                                    Автор, пара вопросов.

                                    — почему выбраны именно эти фичи?
                                    # 1001 — съешьте, 2001 — ьте, 3001 — те

                                    — почему выбран SVM как классификатор? (а не, к примеру, наивный байес)

                                    И предложение. Попробуйте использовать контекст для разметки. Тут несколько вариантов:
                                    — использовать фичи предыдущего слова для определение метки текущего слова.
                                    пример: для слова «булок» в «съешьте булок»
                                    {'full_suffix': 'булок', '3_suffix': 'лок', '2_suffix': 'ок', 'prev_3_suffix': 'ьте', 'prev_2_suffix': 'те'}

                                    — использовать полученную метку для предыдущего слова чтобы определить метку текущего слова. К примеру, для «съешьте булок»:
                                    {'full_suffix': 'булок', '3_suffix': 'лок', '2_suffix': 'ок', 'prev_tag': 'ГЛ'}

                                    Интересно, как изменится при этом качество.
                                    • 0
                                      — почему выбраны именно эти фичи?
                                      # 1001 — съешьте, 2001 — ьте, 3001 — те

                                      Фичи подобраны интуитивно, я взял само слово, два варианта окончания (2 и 3 буквы), два варианта приставки, а также часть речи предыдущих слов. В большинстве случаев, само слово это уже информативная фича, но если оно отсутствовало в обучающей выборке, то на помощь приходят окончания и приставки. Чтобы снять неоднозначность (когда слово может быть несколькими частями речи) в качестве фич добавлены части речи предыдущих слов (пробовал 2 и 3 слова).

                                      — почему выбран SVM как классификатор? (а не, к примеру, наивный байес)

                                      По-моему, выбор классификатора не настолько влияет на производительность, как например, выбор характеристик и данных для обучения. Можно, конечно, поэкспериментировать с различными алгоритмами обучения, но я привык работать с SVM.

                                      И предложение. Попробуйте использовать контекст для разметки. Тут несколько вариантов

                                      Я как раз таки использую части речи предыдущих слов, наверное, плохо в статье описал. Пробовал также использовать и другие характеристики, но экспериментальные результаты были меньше на 1-2%.
                                      • 0
                                        А, точно, невнимательно прочел. Простите.

                                        Кстати, есть такая штука как Brill Tagger, который в процессе работы инкрементально корректирует проставленные метки. Интересно, можно ли как-нибудь применить здесь то же самое.
                                        • 0
                                          Прочитал про Brill Tagger на вики, интересный концепт. Читаю его статью, возможно, получится ее применить.
                                    • +4
                                      Вот придумал пример русской фразы, где ни одно слово не определяется правильно:
                                      «мигало, моргая, веко нарвала»
                                      [('мигало', 'сущ.'), ('моргая', 'прил.'), ('веко', 'нареч.'), ('нарвала', 'глаг.')]
                                      • +4
                                        А вот, если угодно, в стихах:
                                        «потея, пью киндзмараули, кричу пернатым: гули-гули»
                                        [('потея', 'сущ.'), ('пью', 'сущ.'), ('киндзмараули', 'глаг.'), ('кричу', 'сущ.'), ('пернатым', 'числ.-прил.'), ('гули', 'глаг.'), ('гули', 'глаг.')]

                                        Хотя, «киндзмараули» — слово не русское. Каюсь. ;)
                                        • 0
                                          Думаю, данных для обучения алгоритма было недостаточно для ваших заковыристых примеров :)
                                          Для интереса прогнал через TreeTagger, он выдал следующее:
                                          потея Vmgp---a-p потеть - глагол
                                          , , ,
                                          пью Vmip1s-a-p пить - глагол
                                          киндзмараули Vmis-p-a-e - глагол
                                          , , ,
                                          кричу Vmip1s-a-p кричать - глагол
                                          пернатым Afpmsi пернатый - прилаг.
                                          : - :
                                          гули-гули Vmis-p-a-e - глагол

                                          т.е. чуть получше, но тоже запутался в "киндзмараули" и "гули-гули"
                                          • +2
                                            Да и слово «потея» не верно определено. Это не глагол, а деепричастие.
                                            • +1
                                              обычно таггеры помечают деепричастие как особую форму глагола
                                      • +1
                                        Интерестная статья. Может кто подскажет, какие есть похожие библиотеки для Ruby?
                                      • 0
                                        Замечу, что поход к построению модели можно взять из SVMTool. Он вполне хорошо работает для русского.

                                        Еще замечу, что большинство проблем возникает с [около] служебными словами например: и, как, что тот, все и т.д., а не словами типа бокрёнок и куздра.

                                        Кроме того, state-of-the-art для русской морфологии приведен в: Ляшевская О.Н., Астафьева И., Бонч-Осмоловская А., Гарейшина А., Гришина Ю., Дьячков В., Ионов М., Королева А., Кудринский М., Литягина А., Лучина Е., Сидорова Е., Толдова С., Савчук С., Коваль С. «Оценка методов автоматического анализа текста: морфологические парсеры русского языка». Тогда на Диалоге проводось соревнование по морфологии.

                                        Но наиболее интересно было бы построить pos-tagger с расширенными характеристиками, который предсказывал не только часть речи, но и род, число, падеж и прочие.
                                        • 0
                                          Да, я читал статью как раз перед реализацией модуля. Хотел сравнить результаты работы скрипта на тестовом примере из соревнования, но для этого мне нужно еще реализовать токенизатор.

                                          Но наиболее интересно было бы построить pos-tagger с расширенными характеристиками, который предсказывал не только часть речи, но и род, число, падеж и прочие.

                                          я как раз этим планирую заняться следующим этапом
                                          • +1
                                            Вот ещё одна статья на эту же тему (POS-tagging, русский язык), с диалога 2011 года:
                                            www.dialog-21.ru/dialog2011/materials/html/58.htm (Serge Sharoff, Joakim Nivre «The proper place of men and machines in language technology Processing Russian without any linguistic knowledge»)
                                            • 0
                                              а вы случаем не являетесь участником проекта opencorpora?
                                            • 0
                                              Там как раз отсылка к результатам соревнования :-)
                                              • 0
                                                Да, спасибо, я посмотрел. Не понятно только зачем нужно было анонимизировать результаты, olive, pine cadet…
                                                • 0
                                                  Насколько я понимаю, причин было несколько.
                                                  Во-первых, это первое соревнование в рамках Диалога и поэтому у организаторов не было опыта организации подобных соревнований.
                                                  Во-вторых, это делалось для стимулирования потенциальных участников к участию — это дает свободу получить обратную связь на свою систему, не называя ее.
                                            • 0
                                              посмотрите здесь, если интересно получить больше характеристик:
                                              semanticanalyzer.info/blog/demo/
                                              • 0
                                                интересно было бы ознакомиться с принципом работы
                                                • 0
                                                  основа: правила и словарь Зализняка. Эта система не ставит задачей выявить верную часть речь и другие характеристики. Она даёт все возможные (известные алгоритму) варианты.

                                                  То, что делаете Вы, больше похоже на попытку сделать разбор предложения по составу, т.е. синтаксис.

                                                  Если идти от статистики, то есть ещё одна система (PhD thesis):
                                                  www.cis.hut.fi/projects/morpho/
                                            • +1
                                              А давно НацКорпус начал раздавать выборку? Около года назад я искал, чем бы обучить таггер, но так и не нашел.
                                              Пришлось только по словарю работать.
                                              • 0
                                                с оф. сайта:
                                                22 февраля 2011 года
                                                Для свободного пользования выложена случайная выборка предложений (с нарушенным порядком) из корпуса со снятой омонимией объёмом 180 тыс. словоупотреблений (90 тыс. – пресса, по 30 тыс. из художественных текстов, законодательства и научных текстов).
                                              • 0
                                                А вот проверьте бесподобное:

                                                «Эти типы стали есть на складе.»
                                                • 0
                                                  Кхм, пардон. Итак:

                                                  Эти типы стали есть на складе.
                                                  [('Эти', 'местоим. прил.'), ('типы', 'сущ.'), ('стали', 'глаг.'), ('есть', 'глаг.'), ('на', 'предлог'), ('складе', 'сущ.')]
                                                  Tagged 6 words in 0.0 sec, 1874 words per sec

                                                  Что ж, по мнению программы, «стали» — в данном случае глагол. Окей.
                                                  • 0
                                                    а разве «стали» не глагол в данном примере, по идее его можно заменить на слово «начали». Что сделали? — стали…

                                                    вспомогательный глагол «стали», обозначающий начало действия, значение которого выражено последующим глаголом (с) викисловарь.
                                                    • 0
                                                      Даже люди это предложение неправильно понимают, куда уж там железякам :)
                                                      Я тоже сначала неверно понял.

                                                      СТАЛЬ, -и; ж. [нем. stahl]
                                                      Твёрдый ковкий металл серебристо-серого цвета, сплав железа с углеродом и другими упрочняющими элементами.
                                                      • 0
                                                        Ну да :) Только не _неправильно_, а одним из двух равновозможных вариантов.
                                                        • 0
                                                          На самом деле, в оригинале фраза звучит несколько по другому:
                                                          «ЭТИ ТИПЫ СТАЛИ ЕСТЬ В ЛИТЕЙНОМ ЦЕХЕ»
                                                          она имеет 4 разных смысла и придумал её Илья Сегалович:
                                                          forum.searchengines.ru/showpost.php?p=11845&postcount=13
                                                          • 0
                                                            Точнее, Леонид Иомдин (причём, пруфлинк тот же :-))

                                                            Чёрт, только я в упор не вижу аж _четырёх_ толкований :(
                                                            • 0
                                                              третий смысл напримет такой — эти (данные) типы (виды) стали (сплав железа) есть (употреблять в пишу) в литейном цехе
                                                              тругими словами — данный тип стали можно есть только в литейном цехе…
                                                              • 0
                                                                есть ещё один отличный пример про пьяного, раскосого зайца, срезающего траву с помощью кривой косы:

                                                                «Косой, косой косой косой косил косой.»
                                                • 0
                                                  если говорить о таггерах для русского языка, есть еще такой проект:
                                                  nlp.lsi.upc.edu/freeling/,
                                                  скоро выходит альфа версия 3.0 (доступна из репозитория devel.cpl.upc.edu/freeling/svn/trunk/)
                                                  тесты 4-х дневной давности показали ошибку в 5% если определять только части речи, и 24% если использовать полную информацию (т.е. род, число падеж ...),
                                                  онлайн демо скоро будет так же доступно на сайте.
                                                  • 0
                                                    недавно встретилось:

                                                    брат брату брат
                                                    зомби зомби зомби

                                                    задачка на определение «других характеристик»

                                                    P.S. Это всё можно отнести к «вырожденным случаям» статистически, конечно. Но учитывать их стоит, хотя бы для понимания сложности задач nlp.
                                                    • 0
                                                      Очень интересно, но я не очень понял по каким свойствам вы классифицировали. Неужели только по оканчанию и слову целиком(при этом получилась такая точность 92%)? Насколько я знаю надо так же использовать связь с предыдущими тегами посредством HMM/CRF или просто пред предсказание…
                                                      • 0
                                                        использовались след. характеристики:
                                                        — слово
                                                        — окончание (2 и 3 буквы)
                                                        — приставка (2 и 3 буквы)
                                                        — часть речи предыдущих 3 слов
                                                        • 0
                                                          Я пробовал pymorphy2 натравить на доступную часть НКРЯ (220 тыс токенов) и оценить результаты. Подходы к разметке совпадают не полностью, поэтому некоторые разногласия ошибками не считаются. Ну, например, слова вроде «дальше» в НКРЯ — наречия, а в OpenCorpora (и pymorphy2) — компаративы, но не все компаративы OpenCorpora — это наречия в НКРЯ. Так что 1-к-1 сравнения не получается пока. Но все равно результаты интересные. Если учитывать только части речи, то первый разбор из pymorphy2 правильный в 93-94% случаев, в зависимости от того, как много ошибок мы не учитываем, считая их особенностями подходов НКРЯ и OpenCorpora. При этом pymorphy2 использует только информацию о частоте различных разборов для отдельных слов (оцененную по OpenCorpora), а контекст не использует совсем. Так что часть речи вполне можно определять только по самому слову и получать точность порядка 92%. Без информации о частотности разборов выходит где-то 87% правильных частей речи.

                                                          Другое дело, что кроме части речи есть еще падеж, число и т.д., и вот для полного набора граммем, кажется, без контекста уже трудно. Там pymorphy2 выдает 78-80% правильных разборов с использованием частот и 72-73% — без (опять же, в зависимости от того, как оценивать).

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.