Время прочтения: 4 мин.

Для решения задач обработки текстов на естественном языке на сегодняшний день существует множество библиотек для python. В данной статье обратимся к библиотеке Stanza от StanfordNLPGroup, основанной на PyTorch. И, т.к. анализ текста является важной NLP-задачей, рассмотрим основные методы, реализованные в данной библиотеке.

Для установки библиотеки используем команду pip install stanza.

Для работы с русскоязычным текстом скачиваем соответствующую модель:

import stanza
stanza.download('ru')

Теперь рассмотрим различные этапы анализа (предобработки) текстовых данных.

1. Токенизация.

Разобьём текст на предложения (sentences) и предложения на токены (tokens)

import stanza
ppln = stanza.Pipeline('ru', processors='tokenize') #инициируем нейронный конвеер (Pipeline)
txt = 'Об орясину осёл топорище точит. А факир выгнав гостей выть акулой хочет.'
doc = ppln(txt)
for i, sentence in enumerate(doc.sentences):
    print(f'====== Предложение {i+1} =======')
    print(*[f'id: {token.id}\ttext: {token.text}' for token in sentence.tokens], sep='\n')

Получаем следующий список предложений и токенов:

====== Предложение 1 =======
id: (1,)	text: Об
id: (2,)	text: орясину
id: (3,)	text: осёл
id: (4,)	text: топорище
id: (5,)	text: точит
id: (6,)	text: .
====== Предложение 2 =======
id: (1,)	text: А
id: (2,)	text: факир
id: (3,)	text: выгнав
id: (4,)	text: гостей
id: (5,)	text: выть
id: (6,)	text: акулой
id: (7,)	text: хочет
id: (8,)	text: .

2. POS-tagging

Теперь проведём пример с частеречной разметкой (part of speech tagging). При инициализации Pipeline в параметр processors добавляем значение ‘pos’. В качестве результата выводим соответствующие частям речи значения (тэги) в формате universalPOS (upos), а так же дополнительные лексические и грамматические свойства в формате universalmorphologicalfeatures (UFeats)

import stanza
ppln = stanza.Pipeline('ru', processors='tokenize,pos')
txt = 'Об орясину осёл топорище точит. А факир выгнав гостей выть акулой хочет.'
doc = ppln(txt)
print(*[f'word: {word.text}\tupos: {word.upos}\tfeats: {word.feats if word.feats else "_"}' for snt in doc.sentences for word in snt.words], sep='\n')

Результат будет следующим:

word: Об	upos: ADP	feats: _
word: орясину	upos: NOUN	feats: Animacy=Inan|Case=Loc|Gender=Masc|Number=Sing
word: осёл	upos: NOUN	feats: Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
word: топорище	upos: NOUN	feats: Animacy=Inan|Case=Nom|Gender=Masc|Number=Sing
word: точит	upos: VERB	feats: Aspect=Imp|Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin|Voice=Act
word: .	        upos: PUNCT	feats: _
word: А	        upos: CCONJ	feats: _
word: факир	upos: NOUN	feats: Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
word: выгнав	upos: VERB	feats: Aspect=Perf|Tense=Past|VerbForm=Conv|Voice=Act
word: гостей	upos: NOUN	feats: Animacy=Anim|Case=Acc|Gender=Masc|Number=Plur
word: выть	upos: VERB	feats: Aspect=Imp|VerbForm=Inf|Voice=Act
word: акулой	upos: NOUN	feats: Animacy=Anim|Case=Ins|Gender=Fem|Number=Sing
word: хочет	upos: VERB	feats: Aspect=Imp|Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin|Voice=Act
word: .	        upos: PUNCT	feats:

3. Лемматизация.

Для получения нормальных словарных форм слов, необходимо при инициализации Pipeline в параметр processors добавить значение ‘lemma’.  И в результат выводим свойство lemma каждого слова (word).

import stanza
ppln = stanza.Pipeline('ru', processors='tokenize,pos,lemma')
txt = 'Об орясину осёл топорище точит. А факир выгнав гостей выть акулой хочет.'
doc = ppln(txt)
print(*[f'word: {word.text}\tupos: {word.lemma}' for snt in doc.sentences for word in snt.words], sep='\n')

Результатом выполнения кода будет:

word: Об	    upos: о
word: орясину	    upos: орясина
word: осёл	    upos: осел
word: топорище      upos: топорищ
word: точит	    upos: точить
word: .	            upos: .
word: А	            upos: а
word: факир	    upos: факир
word: выгнав	    upos: выгнать
word: гостей	    upos: гость
word: выть	    upos: выть
word: акулой	    upos: акула
word: хочет	    upos: хотеть
word: .	            upos: .

4 Анализ зависимостей.

По аналогии с предыдущими пунктами добавляем в параметр processors значение  ‘depparse’ при инициализации Pipeline. Обработчик depparse строит дерево зависимостей между словами предложений.

import stanza
ppln = stanza.Pipeline('ru', processors='tokenize,pos,lemma,depparse')
txt = 'Об орясину осёл топорище точит. А факир выгнав гостей выть акулой хочет.'
doc = ppln(txt)
print(*[f'word: {word.text}\thead: {snt.words[word.head-1].text if word.head > 0 else "root"}\tdeprel: {word.deprel}' for snt in doc.sentences for word in snt.words], sep='\n')

В результате видим список с зависимостями — каждому слову сопоставляется вершина и тип синтаксического отношения (deprel) в формате Universal Dependencies:

word: Об	head id: 2	head: орясину	deprel: case
word: орясину	head id: 5	head: точит	deprel: obl
word: осёл	head id: 5	head: точит	deprel: nsubj
word: топорище	head id: 3	head: осёл	deprel: appos
word: точит	head id: 0	head: root	deprel: root
word: .	        head id: 5	head: точит	deprel: punct
word: А	        head id: 7	head: хочет	deprel: cc
word: факир	head id: 7	head: хочет	deprel: nsubj
word: выгнав	head id: 7	head: хочет	deprel: advcl
word: гостей	head id: 3	head: выгнав	deprel: obj
word: выть	head id: 3	head: выгнав	deprel: xcomp
word: акулой	head id: 5	head: выть	deprel: obl
word: хочет	head id: 0	head: root	deprel: root
word: .	        head id: 7	head: хочет	deprel: punct

В данной статье мы рассмотрели инструментарий библиотеки Stanza по анализу (предобработке) текстовых данных.

С учетом мультиязычности данный проект может составить серьёзную конкуренцию другим NLP-библиотекам на python.