Compare o trabalho do Python de código aberto - bibliotecas para o reconhecimento de entidades nomeadas

Introdução


Na empresa, estamos criando um serviço que permite criar, gerenciar e armazenar automaticamente contratos de licença e outros contratos entre freelancers e seus clientes.

Para resolver esse problema, tentei dezenas de soluções no campo do processamento de linguagem natural, incluindo soluções de código aberto e gostaria de compartilhar minha experiência com as bibliotecas Python de código aberto para reconhecer entidades nomeadas.

Entidades nomeadas reconhecidas


Algumas palavras sobre o problema em si. O NER (Named Entity Recognition) é um ramo da tecnologia de processamento de linguagem humana, cuja implementação de software permite encontrar categorias objetivas de palavras e frases no discurso e no texto. Inicialmente, eram nomes geográficos, nomes de pessoas, organizações, endereços, mas agora esse conceito foi ampliado em grande parte e, com a ajuda do NER, procuramos no texto datas relativas e absolutas, números, números etc.
A identificação de entidades nomeadas é a “porta de entrada” para a linguagem humana, pois permite identificar e processar as intenções de uma pessoa, estabelecer a conexão de palavras em seu discurso e no mundo real.


Desigualdade linguística


Para começar, gostaria de chamar a atenção para a disparidade óbvia em soluções de software para diferentes idiomas. Portanto, a maioria dos desenvolvimentos (incluindo os criados por programadores russos) trabalha com inglês. Encontrar modelos prontos para Bahasa, Hindi ou Árabe é uma tarefa ingrata.

As línguas européias são pelo menos representadas nas bibliotecas mais populares; as línguas africanas não existem no processamento natural de línguas modernas em princípio. Enquanto isso, por experiência própria, sei que o continente africano é um mercado enorme e rico, e essa atitude é provavelmente a inércia do mercado.

Existem várias soluções para a língua russa que surpreendem em sua qualidade; no entanto, elas não sentem tanto poder comercial e potencial acadêmico quanto as bibliotecas desenvolvidas "construídas" para processar o inglês.

Texto a ser processado


Peguei várias sugestões de diferentes fontes e as combinei em um texto um pouco hipnótico para testar o desempenho das bibliotecas selecionadas.

english_text = ''' I want a person available 7 days and with prompt response all most every time. Only Indian freelancer need I need PHP developer who have strong experience in Laravel and Codeigniter framework for daily 4 hours. I need this work by Monday 27th Jan. should be free from plagiarism . 
Need SAP FICO consultant for support project needs to be work on 6 months on FI AREAWe.  Want a same site to be created as the same as this https://www.facebook.com/?ref=logo, please check the site before contacting to me and i want this site to be ready in 10 days. They will be ready at noon tomorrow .'''

russian_text = '''   110     ,     .        https://www.sobyanin.ru/  , 1 .     .51 (   :  , )  ?     2107   47     24,    . 
 c        10  1970 ,     -, . ,  5/1 8 000 ( )  00  .               .              - .'''



Biblioteca NLTK


O NLTK é uma biblioteca clássica para processamento de linguagem natural, é fácil de usar, não requer estudo a longo prazo e executa 99% das tarefas que podem surgir na solução de problemas dos alunos.
import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
nltk.download('maxent_ne_chunker')
nltk.download('words')
for sent in nltk.sent_tokenize(english_text):
   for chunk in nltk.ne_chunk(nltk.pos_tag(nltk.word_tokenize(sent))):
      if hasattr(chunk, 'label'):
         print(chunk)

Resultado: Como podemos ver, o NLTK fez um bom trabalho, mas, para tornar o resultado mais “rico”, teremos que treinar nosso próprio etiquetador (ou escolher outro de uma lista bastante ampla). Mas vale a pena em 2020 se houver soluções mais simples?
(GPE Indian/JJ)
(ORGANIZATION PHP/NNP)
(GPE Laravel/NNP)
(PERSON Need/NNP)
(ORGANIZATION SAP/NNP)
(ORGANIZATION FI/NNP)




Stanford CoreNLP


imagem

Uma maneira de estender os recursos do NLTK é usar a biblioteca Java clássica do Stanford CoreNLP com a biblioteca Python clássica. A qualidade melhora significativamente, com requisitos relativamente baixos.
from nltk.tag.stanford import StanfordNERTagger
jar = "stanford-ner-2015-04-20/stanford-ner-3.5.2.jar"
model = "stanford-ner-2015-04-20/classifiers/" 
st_3class = StanfordNERTagger(model + "english.all.3class.distsim.crf.ser.gz", jar, encoding='utf8') 
st_4class = StanfordNERTagger(model + "english.conll.4class.distsim.crf.ser.gz", jar, encoding='utf8') 
st_7class = StanfordNERTagger(model + "english.muc.7class.distsim.crf.ser.gz", jar, encoding='utf8')
for i in [st_3class.tag(english_text.split()), st_4class.tag(english_text.split()), st_7class.tag(english_text.split())]:
  for b in i:
    if b[1] != 'O':
        print(b)

Saída: Como podemos ver, a qualidade da saída melhorou significativamente e, agora, dada a velocidade e a facilidade de uso, é óbvio que o NLTK também é adequado para aplicações industriais.
('PHP', 'ORGANIZATION')
('Laravel', 'LOCATION')
('Indian', 'MISC')
('PHP', 'ORGANIZATION')
('Laravel', 'LOCATION')
('Codeigniter', 'PERSON')
('SAP', 'ORGANIZATION')
('FICO', 'ORGANIZATION')
('PHP', 'ORGANIZATION')
('Laravel', 'LOCATION')
('Monday', 'DATE')
('27th', 'DATE')
('Jan.', 'DATE')
('SAP', 'ORGANIZATION')



Spacy



imagem

Spacy é uma biblioteca Python de código aberto para processamento de linguagem natural, publicada sob a licença MIT (!). Foi criada e desenvolvida por Matthew Hannibal e Ines Montany, fundadores da empresa de desenvolvimento Explosion.
Como regra, todos os que enfrentam a necessidade de resolver alguns problemas para processar uma linguagem natural aprenderão mais cedo ou mais tarde sobre esta biblioteca. A maioria das funções é acessível "pronto para uso"; os desenvolvedores cuidam para que a biblioteca seja fácil de usar.
O Space oferece 18 tags que marcam entidades nomeadas, além de uma maneira simples de treinar novamente seu próprio modelo. Adicione documentação excelente, uma comunidade enorme e bom suporte aqui - e ficará claro por que essa solução se tornou tão popular nos últimos dois anos.

import spacy
model_sp = en_core_web_lg.load()
for ent in model_sp(english_text).ents:
  print(ent.text.strip(), ent.label_)

Saída: como você pode ver, o resultado é muito melhor e o código é muito mais simples e compreensível. Contras do trabalho - um grande peso de modelos, operação lenta, "tags" relativamente ilógicas, falta de modelos para muitos idiomas, incluindo o russo (embora existam modelos multilíngues).
7 days DATE
New York GPE
Indian NORP
Laravel LOC
Codeigniter NORP
4 hours TIME
Monday 27th Jan. DATE
FICO ORG
6 months DATE
10 days DATE
noon TIME
tomorrow DATE
Iceland GPE



Flair


imagem

Flair oferece uma imersão muito mais profunda na área de assunto, a biblioteca foi criada, de fato, para resolver problemas de pesquisa, a documentação não é ruim, mas com algumas falhas, há integração com um grande número de outras bibliotecas, código claro, lógico e legível.
A biblioteca possui uma comunidade desenvolvida, e não apenas orientada para o inglês, devido ao grande número de modelos disponíveis, Flair é significativamente mais democrático na escolha de idiomas do que o Spacy.
from flair.models import SequenceTagger
tagger = SequenceTagger.load('ner')
from flair.data import Sentence
s = Sentence(english_text)
tagger.predict(s)
for entity in s.get_spans('ner'):
    print(entity)

Saída: como você pode ver, o modelo treinado não funcionou da melhor maneira. No entanto, poucas pessoas usam o Flair “pronto para uso” - é principalmente uma biblioteca para criar suas próprias ferramentas. O mesmo, com reservas, pode ser dito sobre a próxima biblioteca.
Span [6,7]: "7 days" [− Labels: DATE (0.9329)]
Span [17]: "Indian" [− Labels: NORP (0.9994)]
Span [35,36]: "4 hours." [− Labels: TIME (0.7594)]
Span [42,43,44]: "Monday 27th Jan." [− Labels: DATE (0.9109)]
Span [53]: "FICO" [− Labels: ORG (0.6987)]
Span [63,64]: "6 months" [− Labels: DATE (0.9412)]
Span [98,99]: "10 days." [− Labels: DATE (0.9320)]
Span [105,106]: "noon tomorrow" [− Labels: TIME (0.8667)]




Deeppavlov


imagem
O DeepPavlov é uma biblioteca de código aberto criada por TensorFlow e Keras.

Os desenvolvedores sugerem usar o sistema principalmente para sistemas de "conversação", bots de bate-papo etc., mas a biblioteca também é excelente para resolver problemas de pesquisa. Usá-lo “em produção” sem trabalho sério para “personalizar” e “finalizar” a solução é uma tarefa, ao que parece, nem mesmo pretendida pelos criadores do MIPT.
Uma abordagem estranha e ilógica da arquitetura de código, que contradiz o Python Zen, traz bons resultados, se você gastar tempo suficiente para lidar com isso.
from deeppavlov import configs, build_model
from deeppavlov import build_model, configs

ner_model = build_model(configs.ner.ner_ontonotes_bert, download=True)
result = ner_model([english_text])
for i in range(len(result[0][0])):
     if result [1][0][i] != 'O':
         print(result[0][0][i], result[1][0][i])

Saída: O resultado é previsível, compreensível, detalhado e um dos melhores. O próprio modelo também pode ser usado diretamente no Hugging Face Transformers, que remove, de várias maneiras, as reivindicações da arquitetura de código.
7 B-DATE
days I-DATE
Indian B-NORP
Laravel B-PRODUCT
Codeigniter B-PRODUCT
daily B-DATE
4 B-TIME
hours I-TIME
Monday B-DATE
27th I-DATE
Jan I-DATE
6 B-DATE
months I-DATE
FI B-PRODUCT
AREAWe I-PRODUCT
10 B-DATE
days I-DATE
noon B-TIME
tomorrow B-DATE




deepmipt / ner


Esta é, de fato, a biblioteca com a qual Deep Pavlov começou. Pode ser usado para entender a direção dos pensamentos dos desenvolvedores e o progresso que eles fizeram.
import ner
example = russian_text
def deepmint_ner(text):
  extractor = ner.Extractor()
  for m in extractor(text):
     print(m)
deepmint_ner(example)

Resultado:
Match(tokens=[Token(span=(7, 13), text='')], span=Span(start=7, end=13), type='LOC')
Match(tokens=[Token(span=(492, 499), text='')], span=Span(start=492, end=499), type='PER')
Match(tokens=[Token(span=(511, 520), text=''), Token(span=(521, 525), text='')], span=Span(start=511, end=525), type='PER')
Match(tokens=[Token(span=(591, 600), text='')], span=Span(start=591, end=600), type='LOC')
Match(tokens=[Token(span=(814, 820), text=''), Token(span=(821, 829), text='')], span=Span(start=814, end=829), type='PER')


Poliglota


Uma das bibliotecas mais antigas, o trabalho rápido e um grande número de idiomas suportados ainda o tornam popular. Por outro lado, a licença viral da GPLv3 não permite seu uso total no desenvolvimento comercial.
from polyglot.text import Text
for ent in Text(english_text).entities:
 print(ent[0],ent.tag)

Saída: e para o idioma russo:
Laravel I-LOC
SAP I-ORG
FI I-ORG


!polyglot download embeddings2.ru ner2.ru
for ent in Text(russian_text).entities:
 print(ent[0],ent.tag)

Saída: o resultado não é o melhor, mas a velocidade e o bom suporte podem melhorá-lo se você se esforçar.
24 I-ORG
I-PER
I-LOC
I-PER
I-ORG
I-PER




AdaptNLP


imagem
Outra nova biblioteca com um limite de entrada extremamente baixo para o pesquisador.
O AdaptNLP permite que os usuários, de estudantes a engenheiros de dados experientes, usem modelos e métodos de treinamento modernos da PNL.
A biblioteca é construída sobre as populares bibliotecas Flair e Hugging Face Transformers.

from adaptnlp import EasyTokenTagger
tagger = EasyTokenTagger()
sentences = tagger.tag_text(
    text = english_text, model_name_or_path = "ner-ontonotes"
)
spans = sentences[0].get_spans("ner")
for sen in sentences:
    for entity in sen.get_spans("ner"):
        print(entity)

Saída: O resultado é aceitável, mas a biblioteca permite que você use uma variedade de modelos para concluir a tarefa e pode ser aprimorada repetidamente se você fizer esforços (mas por quê, se você tiver transformadores Flair e Hugging Face diretamente). No entanto, a simplicidade, uma grande lista de tarefas e uma boa arquitetura, bem como os esforços sistemáticos dos desenvolvedores, esperam que a biblioteca tenha futuro.
DATE-span [6,7]: "7 days"
NORP-span [18]: "Indian"
PRODUCT-span [30]: "Laravel"
TIME-span [35,36,37]: "daily 4 hours"
DATE-span [44,45,46]: "Monday 27th Jan."
ORG-span [55]: "FICO"
DATE-span [65,66]: "6 months"
DATE-span [108,109]: "10 days"
TIME-span [116,117]: "noon tomorrow"




Estrofe


imagem
Stanza da StanfordNlp é um presente para desenvolvedores em 2020 da Universidade de Stanford. O que faltava a Spacy era o multilinguismo, uma imersão profunda no idioma, além da facilidade de uso.
Se a comunidade suportar esta biblioteca, ela terá todas as chances de se tornar uma das mais populares.
import stanza
stanza.download('en')
def stanza_nlp(text):
  nlp = stanza.Pipeline(lang='en', processors='tokenize,ner')
  doc = nlp(text)
  print(*[f'entity: {ent.text}\ttype: {ent.type}' for sent in doc.sentences for ent in sent.ents], sep='\n')
stanza_nlp(english_text)

Saída: e para o idioma russo:
entity: 7 days type: DATE
entity: Indian type: NORP
entity: Laravel type: ORG
entity: Codeigniter type: PRODUCT
entity: daily 4 hours type: TIME
entity: Monday 27th Jan. type: DATE
entity: SAP type: ORG
entity: FICO type: ORG
entity: 6 months type: DATE
entity: FI AREAWe type: ORG
entity: 10 days type: DATE
entity: noon tomorrow type: TIME



import stanza
stanza.download('ru')
def stanza_nlp_ru(text):
  nlp = stanza.Pipeline(lang='ru', processors='tokenize,ner')
  doc = nlp(text)
  print(*[f'entity: {ent.text}\ttype: {ent.type}' for sent in doc.sentences for ent in sent.ents], sep='\n')
stanza_nlp_ru(russian_text)

Saída: trabalho rápido, código bonito, bom resultado.
2020-05-15 08:01:18 INFO: Use device: cpu
2020-05-15 08:01:18 INFO: Loading: tokenize
2020-05-15 08:01:18 INFO: Loading: ner
2020-05-15 08:01:19 INFO: Done loading processors!
entity: type: LOC
entity: type: LOC
entity: type: PER
entity: 2107 type: MISC
entity: 47 type: MISC
entity: 24 type: MISC
entity: type: PER
entity: - type: LOC
entity: . type: LOC
entity: type: LOC
entity: type: LOC
entity: type: PER




Allennlp


imagem
Biblioteca para pesquisa, construída em PyTorch /
Por um lado - arquitetura simples e velocidade rápida, por outro lado, os desenvolvedores estão constantemente mudando algo na arquitetura, o que afeta o trabalho da biblioteca como um todo.
from allennlp.predictors.predictor import Predictor
import allennlp_models.ner.crf_tagger
predictor = Predictor.from_path("https://storage.googleapis.com/allennlp-public-models/ner-model-2020.02.10.tar.gz")
allen_result = predictor.predict(
  sentence=english_text
)
for i in zip(allen_result['tags'], allen_result['words']):
    if (i[0]) != 'O':
      print(i)

Saída:
('U-MISC', 'Indiano') ('U-MISC', '
PHP')
('U-MISC', 'Laravel')
('U-MISC', 'Codeigniter')
('B- ORG ',' SAP ')
(' L-ORG ',' FICO ')
O módulo funciona rapidamente, mas o resultado é inaceitavelmente ruim.

Hanlp


imagem
O HanLP é uma das bibliotecas de código aberto dos desenvolvedores da China. Um projeto inteligente, bem desenvolvido e ativo que, ao que me parece, encontrará seu nicho além das fronteiras do Império Celestial.
Biblioteca de PNL para pesquisadores e empresas criadas no TensorFlow 2.0.
O HanLP vem com modelos pré-preparados para diferentes idiomas, incluindo inglês, chinês e muitos outros.
O único problema é a qualidade da saída "salta" após cada atualização da biblioteca.
recognizer = hanlp.load(hanlp.pretrained.ner.MSRA_NER_BERT_BASE_ZH)
recognizer([list('上海华安工业(集团)公司董事长谭旭光和秘书张晚霞来到美国纽约现代艺术博物馆参观。'),
                list('萨哈夫说,伊拉克将同联合国销毁伊拉克大规模杀伤性武器特别委员会继续保持合作。')])

Resultado:

[[('上海华安工业(集团)公司', 'NT', 0, 12), ('谭旭光', 'NR', 15, 18),
('张晚霞', 'NR', 21, 24),
('美国', 'NS', 26, 28),
('纽约现代艺术博物馆', 'NS', 28, 37)],
[('萨哈夫', 'NR', 0, 3),
('伊拉克', 'NS', 5, 8),
('联合国销毁伊拉克大规模杀伤性武器特别委员会', 'NT', 10, 31)]]


import hanlp
tokenizer = hanlp.utils.rules.tokenize_english
testing = tokenizer('Need SAP FICO consultant for support project needs to be work on 6 months on FI AREAWe')
recognizer = hanlp.load(hanlp.pretrained.ner.CONLL03_NER_BERT_BASE_UNCASED_EN)
recognizer(testing)

Saída: em inglês, o resultado é instável, mas isso é resolvido usando o tokenizer do NLTK.
[('SAP FICO', 'ORG', 1, 3)]




PullEnti


Biblioteca C # para NER em russo. Em 2016, conquistou o primeiro lugar na competição factRuEval-2016. Em 2018, o autor portou o código para Java e Python.
Provavelmente a solução mais bonita para o idioma russo.
Rapidamente, profundamente, com atenção aos detalhes. A decisão é baseada em regras, o que naturalmente limita seu desenvolvimento, mas sua autonomia, velocidade e resultados nos permitem ter esperança no desenvolvimento do projeto.
Existe um python-wrapper para a biblioteca, embora pareça "abandonado".
from pullenti_wrapper.processor import (
    Processor,
    MONEY,
    URI,
    PHONE,
    DATE,
    KEYWORD,
    DEFINITION,
    DENOMINATION,
    MEASURE,
    BANK,
    GEO,
    ADDRESS,
    ORGANIZATION,
    PERSON,
    MAIL,
    TRANSPORT,
    DECREE,
    INSTRUMENT,
    TITLEPAGE,
    BOOKLINK,
    BUSINESS,
    NAMEDENTITY,
    WEAPON,
)

processor = Processor([PERSON, ORGANIZATION, GEO, DATE, MONEY])
text = russian_text
result = processor(text)
result.graph


Resultado:imagem

Natasha


imagem
Natasha, esse parece ser um dos principais projetos de PNL para o idioma russo. Ele tem uma longa história e começou com uma solução baseada em regras que foi desenvolvida através do popular Yargy Parser, e agora resolve as principais tarefas da PNL para o idioma russo: tokenização, segmentação de frases, lematização, normalização de frases, análise, marcação por NER e extração de fatos.
from natasha import (
    Segmenter,
    MorphVocab,
    
    NewsEmbedding,
    NewsMorphTagger,
    NewsSyntaxParser,
    NewsNERTagger,
    
    PER,
    NamesExtractor,

    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)

doc = Doc(russian_text)

Saída: infelizmente, o resultado não é estável, ao contrário das regras personalizadas do Yargy Parser do mesmo desenvolvedor, no entanto, o projeto está em desenvolvimento ativo e mostra um resultado decente para uso comercial.
110 ,
LOC───
.
https://www.sobyanin.ru/ , 1 .
.51 ( :
LOC───────────────
, ) ?
ORG PER────────────
2107 47
24, .
ORG──
c
PER────────────────
10 1970 ,
─────────
-, . , 5/1 8 000 ( )
LOC──────────── PER─────────
00
LO
.

PER────────────
- .




ner-d


O último módulo é um projeto privado, não muito popular, construído sobre Spacy e Thinc e, no entanto, digno de atenção à abordagem escolhida para a arquitetura (ênfase na facilidade de uso).
from nerd import ner
doc_nerd_d = ner.name(english_text)
text_label = [(X.text, X.label_) for X in doc_nerd_d]
print(text_label)


Resultado: De todos os projetos, o mais "equilibrado" e conveniente, com resultados aceitáveis ​​e facilidade de uso, parece-me o Stanza da StanfordNlp - o trabalho da maioria dos idiomas pronto para uso, o estudo acadêmico de alta qualidade e o apoio da comunidade científica da própria universidade tornam o projeto o mais promissor, na minha opinião . Na próxima vez, compartilharei minha experiência em trabalhar com soluções "fechadas" e a API proposta para processamento de linguagem natural. Todo o código disponível pelo Google Colab
[('7 days', 'DATE'), ('Indian', 'NORP'), ('PHP', 'ORG'), ('Laravel', 'GPE'), ('daily 4 hours', 'DATE'), ('Monday 27th Jan.', 'DATE'), ('Need SAP FICO', 'PERSON'), ('6 months', 'DATE'), ('10 days', 'DATE'), ('noon', 'TIME'), ('tomorrow', 'DATE')]







All Articles