Compare el trabajo de Python de código abierto: bibliotecas para el reconocimiento de entidades con nombre

Introducción


En la empresa, estamos creando un servicio que le permite crear, administrar y almacenar de forma segura acuerdos de licencia y otros acuerdos entre freelancers y sus clientes.

Para resolver este problema, probé docenas de soluciones en el campo del procesamiento del lenguaje natural, incluidas las soluciones de código abierto, y me gustaría compartir mi experiencia con Python de código abierto: bibliotecas para reconocer entidades con nombre.

Entidades nombradas reconocidas


Algunas palabras sobre el problema en sí. El reconocimiento de entidad con nombre (NER) es una rama de la tecnología de procesamiento del lenguaje humano, cuya implementación de software le permite encontrar categorías objetivadas de palabras y frases en el habla y el texto. Al principio, estos eran nombres geográficos, nombres de personas, organizaciones, direcciones, pero ahora este concepto se ha ampliado en gran medida y con la ayuda de NER estamos buscando en el texto fechas relativas y absolutas, números, números, etc.
La identificación de entidades nombradas es la "puerta de entrada" al lenguaje humano, le permite identificar y procesar las intenciones de una persona, para establecer la conexión de las palabras en su discurso y el mundo real.


Desigualdad de lenguaje


Para empezar, me gustaría llamar la atención sobre la obvia desigualdad en las soluciones de software para diferentes idiomas. Por lo tanto, la mayoría de los desarrollos (incluidos los creados por programadores rusos) funcionan con inglés. Encontrar modelos listos para bahasa, hindi o árabe es una tarea desagradecida.

Los idiomas europeos están al menos representados en las bibliotecas más populares; los idiomas africanos no existen en principio en el procesamiento moderno del lenguaje natural. Mientras tanto, por mi propia experiencia, sé que el continente africano es un mercado enorme y rico, y esta actitud es probablemente la inercia del mercado.

Hay varias soluciones para el idioma ruso que son sorprendentes en su calidad, sin embargo, no sienten el poder comercial y el potencial académico como para las bibliotecas desarrolladas "construidas" para procesar el inglés.

Texto a procesar


Tomé varias sugerencias de diferentes fuentes y las reuní en un texto algo hipnótico para probar qué tan bien las bibliotecas seleccionadas harían su trabajo.

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


NLTK es una biblioteca clásica para el procesamiento del lenguaje natural, es fácil de usar, no requiere estudio a largo plazo y realiza el 99% de las tareas que pueden surgir al resolver los problemas de los estudiantes.
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, NLTK hizo un buen trabajo, sin embargo, para que el resultado sea "más rico" tendremos que entrenar a nuestro propio etiquetador (o elegir otro de una lista bastante amplia). ¿Pero vale la pena en 2020 si hay soluciones más simples?
(GPE Indian/JJ)
(ORGANIZATION PHP/NNP)
(GPE Laravel/NNP)
(PERSON Need/NNP)
(ORGANIZATION SAP/NNP)
(ORGANIZATION FI/NNP)




Stanford CoreNLP


imagen

Una forma de ampliar las capacidades de NLTK es usar la biblioteca clásica de Java de Stanford CoreNLP con la biblioteca clásica de Python. La calidad mejora significativamente, con requisitos relativamente bajos.
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)

Salida: Como podemos ver, la calidad de la salida ha mejorado significativamente, y ahora, dada la velocidad y facilidad de uso, es obvio que NLTK también es bastante adecuado para aplicaciones industriales.
('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



imagen

Spacy es una biblioteca de Python de código abierto para el procesamiento del lenguaje natural, se publica bajo la licencia MIT (!), Fue creada y desarrollada por Matthew Hannibal e Ines Montany, fundadores de la compañía desarrolladora Explosion.
Como regla general, todos los que se enfrentan a la necesidad de resolver algunos problemas para procesar un lenguaje natural, tarde o temprano aprenderán sobre esta biblioteca. La mayoría de las funciones son accesibles "fuera de la caja", los desarrolladores se aseguran de que la biblioteca sea fácil de usar.
Space ofrece 18 etiquetas que marcan entidades con nombre, así como una forma sencilla de volver a capacitar a su propio modelo. Agregue documentación excelente, una gran comunidad y un buen soporte aquí, y quedará claro por qué esta solución se ha vuelto tan popular en los últimos años.

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

Salida: como puede ver, el resultado es mucho mejor y el código es mucho más simple y más comprensible. Contras del trabajo: un gran peso de modelos, funcionamiento lento, "etiquetas" relativamente ilógicas, la falta de modelos para muchos idiomas, incluido el ruso (aunque hay modelos multilingües).
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



Instinto


imagen

Flair ofrece una inmersión mucho más profunda en el área temática, la biblioteca fue creada, de hecho, para resolver problemas de investigación, la documentación no es mala, pero con algunas fallas, hay integración con una gran cantidad de otras bibliotecas, código claro, lógico y legible.
La biblioteca tiene una comunidad desarrollada, y no solo está orientada al inglés, debido a la gran cantidad de modelos disponibles, Flair es significativamente más democrático en la elección de idiomas que 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)

Resultado: Como puede ver, el modelo entrenado no funcionó de la mejor manera. Sin embargo, pocas personas usan Flair "fuera de la caja", es principalmente una biblioteca para crear sus propias herramientas. Lo mismo, con reservas, se puede decir sobre la 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


imagen
DeepPavlov es una biblioteca de código abierto construida por TensorFlow y Keras.

Los desarrolladores sugieren usar el sistema principalmente para sistemas "conversacionales", bots de chat, etc., pero la biblioteca también es excelente para resolver problemas de investigación. Usarlo "en producción" sin un trabajo serio para "personalizar" y "terminar" la solución es una tarea, al parecer, ni siquiera pensada por los creadores de MIPT.
Sin embargo, el enfoque extraño e ilógico de la arquitectura del código, que contradice a Python Zen, trae buenos resultados si dedica suficiente tiempo a lidiar con él.
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])

Resultado: El resultado es predecible, comprensible, detallado y uno de los mejores. El modelo en sí también se puede usar directamente en Hugging Face Transformers, que elimina, de muchas maneras, las afirmaciones de la arquitectura del 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 es, de hecho, la biblioteca con la que comenzó Deep Pavlov. Se puede utilizar para comprender la dirección de los pensamientos de los desarrolladores y el progreso que han realizado.
import ner
example = russian_text
def deepmint_ner(text):
  extractor = ner.Extractor()
  for m in extractor(text):
     print(m)
deepmint_ner(example)

Salida:
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')


Polígloto


Una de las bibliotecas más antiguas, el trabajo rápido y una gran cantidad de idiomas compatibles lo hacen aún popular. Por otro lado, la licencia viral GPLv3 no permite su uso completo en el desarrollo comercial.
from polyglot.text import Text
for ent in Text(english_text).entities:
 print(ent[0],ent.tag)

Salida: Y para el idioma ruso:
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)

Salida: el resultado no es el mejor, pero la velocidad y el buen soporte pueden mejorarlo si hace un esfuerzo.
24 I-ORG
I-PER
I-LOC
I-PER
I-ORG
I-PER




AdaptNLP


imagen
Otra nueva biblioteca con un umbral de entrada extremadamente bajo para el investigador.
AdaptNLP permite a los usuarios, desde estudiantes hasta ingenieros de datos experimentados, utilizar modelos modernos de PNL y métodos de capacitación.
La biblioteca está construida sobre las populares bibliotecas Flair y 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)

Salida: El resultado es aceptable, pero la biblioteca le permite usar una variedad de modelos para completar la tarea, y puede mejorarse repetidamente si realiza esfuerzos (pero, por qué, si tiene Flair y Hugging Face Transformers directamente). Sin embargo, la simplicidad, una gran lista de tareas y una buena arquitectura, así como los esfuerzos sistemáticos de los desarrolladores nos permiten esperar que la biblioteca tenga un 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"




Estrofa


imagen
Stanza de StanfordNlp es un regalo para desarrolladores en 2020 de la Universidad de Stanford. Lo que le faltaba a Spacy era el multilingüismo, una inmersión profunda en el idioma junto con la facilidad de uso.
Si la comunidad admite esta biblioteca, tiene todas las posibilidades de convertirse en una de las más 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)

Salida: Y para el idioma ruso:
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)

Salida: trabajo rápido, código hermoso, buen 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


imagen
Biblioteca para investigación, construida en PyTorch /
Por un lado: arquitectura simple y alta velocidad, por otro lado, los desarrolladores están cambiando constantemente algo en la arquitectura, lo que afecta el trabajo de la biblioteca en su conjunto.
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)

Salida:
('U-MISC', 'Indian') ('U-MISC', '
PHP')
('U-MISC', 'Laravel')
('U-MISC', 'Codeigniter')
('B- ORG ',' SAP ')
(' L-ORG ',' FICO ')
El módulo funciona rápidamente, pero el resultado es inaceptablemente pobre.

Hanlp


imagen
HanLP es una de las bibliotecas de código abierto de desarrolladores de China. Un proyecto inteligente, bien desarrollado y activo que, según me parece, encontrará su nicho más allá de las fronteras del Imperio Celestial.
Biblioteca de PNL para investigadores y empresas creada en TensorFlow 2.0.
HanLP viene con modelos preparados previamente para diferentes idiomas, incluidos inglés, chino y muchos otros.
El único problema es la calidad de los "saltos" de salida después de cada actualización de la biblioteca.
recognizer = hanlp.load(hanlp.pretrained.ner.MSRA_NER_BERT_BASE_ZH)
recognizer([list('上海华安工业(集团)公司董事长谭旭光和秘书张晚霞来到美国纽约现代艺术博物馆参观。'),
                list('萨哈夫说,伊拉克将同联合国销毁伊拉克大规模杀伤性武器特别委员会继续保持合作。')])

Salida:

[[('上海华安工业(集团)公司', '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)

Salida: para inglés, el resultado es inestable, pero esto se resuelve usando el tokenizer de NLTK.
[('SAP FICO', 'ORG', 1, 3)]




PullEnti


Biblioteca C # para NER en ruso. En 2016, ganó el primer lugar en la competencia factRuEval-2016. En 2018, el autor portó el código a Java y Python.
Probablemente la solución más bonita para el idioma ruso.
Rápidamente, profundamente, con atención al detalle. La decisión se basa en reglas, lo que naturalmente limita su desarrollo, pero su autonomía, velocidad y resultados nos permiten esperar el desarrollo del proyecto.
Hay un contenedor de python para la biblioteca, aunque parece "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


Salida:imagen

Natasha


imagen
Natasha, este parece ser uno de los principales proyectos de PNL para el idioma ruso. Tiene una larga historia y comenzó con una solución basada en reglas que se desarrolló a través del popular Yargy Parser, y ahora resuelve las principales tareas de PNL para el idioma ruso: tokenización, segmentación de oraciones, lematización, normalización de frases, análisis, etiquetado NER, extracción de hechos.
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)

Salida: El resultado, desafortunadamente, no es estable, en contraste con las reglas personalizadas Yargy Parser del mismo desarrollador, sin embargo, el proyecto se está desarrollando activamente y muestra un 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


El último módulo es un proyecto privado, no muy popular, construido sobre Spacy y Thinc y, sin embargo, merece atención al enfoque elegido para la arquitectura (énfasis en la facilidad 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 los proyectos, el más "equilibrado" y conveniente, con resultados aceptables y facilidad de uso, me parece que es Stanza de StanfordNlp: el trabajo de la mayoría de los idiomas, el estudio académico de alta calidad y el apoyo de la comunidad científica de la universidad hacen que el proyecto sea el más prometedor, en mi opinión. . La próxima vez compartiré mi experiencia sobre trabajar con soluciones "cerradas" y la API propuesta para el procesamiento del lenguaje natural. Todo el código disponible por 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