Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentatoin, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction.
Library is just a wrapper for lower level tools from Natasha project:
Consider using these lower level tools for realword tasks. Natasha models are optimized for news articles, on other domains quality may be worse.
In [1]:
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
DatesExtractor,
MoneyExtractor,
AddrExtractor,
Doc
)
segmenter = Segmenter()
morph_vocab = MorphVocab()
emb = NewsEmbedding()
morph_tagger = NewsMorphTagger(emb)
syntax_parser = NewsSyntaxParser(emb)
ner_tagger = NewsNERTagger(emb)
names_extractor = NamesExtractor(morph_vocab)
dates_extractor = DatesExtractor(morph_vocab)
money_extractor = MoneyExtractor(morph_vocab)
addr_extractor = AddrExtractor(morph_vocab)
Doc
aggregates annotators, initially it has just text
field defined:
In [2]:
text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу понять, как прославление тех, кто непосредственно принимал участие в ужасных антисемитских преступлениях, помогает бороться с антисемитизмом и ксенофобией. Украина не должна забывать о преступлениях, совершенных против украинских евреев, и никоим образом не отмечать их через почитание их исполнителей», — написал дипломат. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В июле аналогичное решение принял Житомирский областной совет. В начале месяца с предложением к президенту страны Петру Порошенко вернуть Бандере звание Героя Украины обратились депутаты Верховной Рады. Парламентарии уверены, что признание Бандеры национальным героем поможет в борьбе с подрывной деятельностью против Украины в информационном поле, а также остановит «распространение мифов, созданных российской пропагандой». Степан Бандера (1909-1959) был одним из лидеров Организации украинских националистов, выступающей за создание независимого государства на территориях с украиноязычным населением. В 2010 году в период президентства Виктора Ющенко Бандера был посмертно признан Героем Украины, однако впоследствии это решение было отменено судом. '
doc = Doc(text)
doc
Out[2]:
After applying segmenter
two new fields appear sents
and tokens
:
In [3]:
doc.segment(segmenter)
display(doc)
display(doc.sents[:2])
display(doc.tokens[:5])
After applying morph_tagger
and syntax_parser
, tokens get 5 new fields id
, pos
, feats
, head_id
, rel
— annotation in Universal Dependencies format:
In [4]:
doc.tag_morph(morph_tagger)
doc.parse_syntax(syntax_parser)
display(doc.tokens[:5])
After applying ner_tagger
doc gets spans
field with PER, LOC, ORG annotation:
In [5]:
doc.tag_ner(ner_tagger)
display(doc.spans[:5])
Natasha wraps Ipymarkup to provide ASCII visualizations for morphology, syntax and NER annotations. doc
and sents
have 3 methods: morph.print()
, syntax.print()
and ner.print()
:
In [6]:
doc.ner.print()
In [7]:
sent = doc.sents[0]
sent.morph.print()
In [8]:
sent.syntax.print()
Tokens have lemmatize
method, it uses pos
and feats
assigned by morph_tagger
to get word normal form. morph_vocab
is just a wrapper for Pymorphy2:
In [9]:
for token in doc.tokens:
token.lemmatize(morph_vocab)
{_.text: _.lemma for _ in doc.tokens[:10]}
Out[9]:
Consider phrase "Организации украинских националистов", one can not just inflect every word independently to get normal form: "Организация украинский националист". Spans have method normalize
that uses syntax annotation by syntax_parser
to inflect phrases:
In [10]:
for span in doc.spans:
span.normalize(morph_vocab)
{_.text: _.normal for _ in doc.spans}
Out[10]:
To split names like "Виктор Ющенко", "Бандера" and "Йоэль Лион" into parts use names_extractor
and spans method extract_fact
:
In [11]:
for span in doc.spans:
if span.type == PER:
span.extract_fact(names_extractor)
{_.normal: _.fact.as_dict for _ in doc.spans if _.fact}
Out[11]:
One may use Natasha components independently. It is not mandatory to construct Doc
object.
Segmenter
is just a wrapper for Razdel, it has two methods tokenize
and sentenize
:
In [12]:
tokens = list(segmenter.tokenize('Кружка-термос на 0.5л (50/64 см³, 516;...)'))
for token in tokens[:5]:
print(token)
In [13]:
text = '''
- "Так в чем же дело?" - "Не ра-ду-ют".
И т. д. и т. п. В общем, вся газета
'''
sents = list(segmenter.sentenize(text))
for sent in sents:
print(sent)
MorphVocab
is a wrapper for Pymorphy2. MorphVocab
adds cache and adapts grammems to Universal Dependencies format:
In [14]:
forms = morph_vocab('стали')
forms
Out[14]:
In [15]:
morph_vocab.__call__.cache_info()
Out[15]:
Also MorphVocab
adds method lemmatize
. Given pos
and feats
it selects the most suitable morph form and returns its normal
field:
In [16]:
morph_vocab.lemmatize('стали', 'VERB', {})
Out[16]:
In [17]:
morph_vocab.lemmatize('стали', 'X', {'Case': 'Gen'})
Out[17]:
Embedding
is a wrapper for Navec — compact pretrained word embeddings for Russian language:
In [18]:
print('Words in vocab + 2 for pad and unk: %d' % len(emb.vocab.words) )
In [19]:
emb['навек'][:10]
Out[19]:
MorphTagger
wraps Slovnet morphology tagger. Tagger has list of words as input and returns markup object. Markup has print
method that outputs morph tags ASCII visualization:
In [20]:
words = ['Европейский', 'союз', 'добавил', 'в', 'санкционный', 'список', 'девять', 'политических', 'деятелей']
markup = morph_tagger(words)
markup.print()
SyntaxParser
wraps Slovnet syntax parser. Interface is similar to MorphTagger
:
In [21]:
words = ['Европейский', 'союз', 'добавил', 'в', 'санкционный', 'список', 'девять', 'политических', 'деятелей']
markup = syntax_parser(words)
markup.print()
NERTagger
wraps Slovnet NER tagger. Interface is similar to MorphTagger
but has untokenized text as input:
In [22]:
text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года).'
markup = ner_tagger(text)
markup.print()
In addition to names_extractor
Natasha bundles several other extractors: dates_extractor
, money_extractor
and addr_extractor
. All extractors are based on Yargy-parser, meaning that they work correctly only on small predefined set of texts. For realword tasks consider writing your own grammar, see Yargy docs for more.
In [23]:
text = '24.01.2017, 2015 год, 2014 г, 1 апреля, май 2017 г., 9 мая 2017 года'
list(dates_extractor(text))
Out[23]:
In [24]:
text = '1 599 059, 38 Евро, 420 долларов, 20 млн руб, 20 т. р., 881 913 (Восемьсот восемьдесят одна тысяча девятьсот тринадцать) руб. 98 коп.'
list(money_extractor(text))
Out[24]:
names_extractor
should be applied only to spans of text. To extract single fact use method find
:
In [25]:
lines = [
'Мустафа Джемилев',
'О. Дерипаска',
'Фёдор Иванович Шаляпин',
'Янукович'
]
for line in lines:
display(names_extractor.find(line))
In [26]:
lines = [
'Россия, Вологодская обл. г. Череповец, пр.Победы 93 б',
'692909, РФ, Приморский край, г. Находка, ул. Добролюбова, 18',
'ул. Народного Ополчения д. 9к.3'
]
for line in lines:
display(addr_extractor.find(line))
In [ ]: