stanford spanish recognition ner python nltk stanford-nlp named-entity-recognition

python - spanish - Extracto de la lista de personas y organizaciones que utilizan Stanford NER Tagger en NLTK



named entity recognition spanish (6)

ADVERTENCIA: Incluso si obtiene este modelo "all.3class.distsim.crf.ser.gz", no lo use porque

    1er motivo:

Para este modelo, la gente de Stanford PNL se ha disculpado abiertamente por su mala precisión

    2da razón:

Tiene mala precisión porque es sensible a mayúsculas y minúsculas.

    SOLUCIÓN

use el modelo llamado "english.all.3class.caseless.distsim.crf.ser.gz"

Estoy tratando de extraer una lista de personas y organizaciones que utilizan el Reconocimiento de entidad con nombre de Stanford (NER) en Python NLTK. Cuando corro:

from nltk.tag.stanford import NERTagger st = NERTagger(''/usr/share/stanford-ner/classifiers/all.3class.distsim.crf.ser.gz'', ''/usr/share/stanford-ner/stanford-ner.jar'') r=st.tag(''Rami Eid is studying at Stony Brook University in NY''.split()) print(r)

la salida es:

[(''Rami'', ''PERSON''), (''Eid'', ''PERSON''), (''is'', ''O''), (''studying'', ''O''), (''at'', ''O''), (''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION''), (''in'', ''O''), (''NY'', ''LOCATION'')]

Lo que quiero es extraer de esta lista a todas las personas y organizaciones de esta forma:

Rami Eid Sony Brook University

Traté de recorrer la lista de tuplas:

for x,y in i: if y == ''ORGANIZATION'': print(x)

Pero este código solo imprime cada entidad una por línea:

Sony Brook University

Con datos reales puede haber más de una organización, personas en una oración, ¿cómo puedo poner los límites entre diferentes entidades?


Gracias al link descubierto por @Vaulstein, está claro que el etiquetador Stanford capacitado, tal como se distribuyó (al menos en 2012) no fragmenta las entidades con nombre . De la respuesta aceptada :

Muchos sistemas NER usan etiquetas más complejas, como las etiquetas IOB, donde códigos como B-PERS indican dónde comienza una entidad persona. La clase CRFClassifier y las fábricas de características admiten tales etiquetas, pero no se utilizan en los modelos que distribuimos actualmente (a partir de 2012)

Tienes las siguientes opciones:

  1. Recopila series de palabras con etiquetas idénticas; por ejemplo, todas las palabras adyacentes etiquetadas como PERSON deben tomarse juntas como una entidad con nombre. Eso es muy fácil, pero por supuesto a veces combinará diferentes entidades con nombre. (Por ejemplo, New York, Boston [and] Baltimore se trata de tres ciudades, no una). Editar: Esto es lo que hace el código de Alvas en la respuesta aceptada. Vea a continuación una implementación más simple.

  2. Use nltk.ne_recognize() . No utiliza el reconocedor de Stanford pero sí entidades fragmentadas. (Es un contenedor alrededor de un etiquetador de entidad con nombre IOB).

  3. Encuentre una forma de hacer su propio troceado sobre los resultados que devuelve el etiquetador de Stanford.

  4. Entrene a su propio elemento de entidad con nombre IOB (utilizando las herramientas de Stanford, o el marco de NLTK) para el dominio que le interesa. Si tiene el tiempo y los recursos para hacerlo correctamente, probablemente le dará los mejores resultados.

Editar: si todo lo que desea es extraer ejecuciones de entidades con nombre continuo (opción 1 anterior), debe usar itertools.groupby :

from itertools import groupby for tag, chunk in groupby(netagged_words, lambda x:x[1]): if tag != "O": print("%-12s"%tag, " ".join(w for w, t in chunk))

Si netagged_words es la lista de tuplas (word, type) en su pregunta, esto produce:

PERSON Rami Eid ORGANIZATION Stony Brook University LOCATION NY

Tenga en cuenta nuevamente que si dos entidades con nombre del mismo tipo ocurren una al lado de la otra, este enfoque las combinará. Por ejemplo, New York, Boston [and] Baltimore se trata de tres ciudades, no una.


IOB / BIO significa I nside, O utside, B eginning (IOB), o a veces también B Bginning, I nside, O utside (BIO)

El etiquetador NE de Stanford devuelve etiquetas de estilo IOB / BIO, p. Ej.

[(''Rami'', ''PERSON''), (''Eid'', ''PERSON''), (''is'', ''O''), (''studying'', ''O''), (''at'', ''O''), (''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION''), (''in'', ''O''), (''NY'', ''LOCATION'')]

Los (''Rami'', ''PERSON''), (''Eid'', ''PERSON'') están etiquetados como PERSON y "Rami" es el comienzo o un fragmento NE y "Eid" es el interior. Y luego verá que cualquier no NE se etiquetará con "O".

La idea de extraer un fragmento NE continuo es muy similar al Reconocimiento de entidad con nombre con expresión regular: NLTK, pero debido a que la API Standal NE chunker no devuelve un buen árbol para analizar, debe hacer esto:

def get_continuous_chunks(tagged_sent): continuous_chunk = [] current_chunk = [] for token, tag in tagged_sent: if tag != "O": current_chunk.append((token, tag)) else: if current_chunk: # if the current chunk is not empty continuous_chunk.append(current_chunk) current_chunk = [] # Flush the final current_chunk into the continuous_chunk, if any. if current_chunk: continuous_chunk.append(current_chunk) return continuous_chunk ne_tagged_sent = [(''Rami'', ''PERSON''), (''Eid'', ''PERSON''), (''is'', ''O''), (''studying'', ''O''), (''at'', ''O''), (''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION''), (''in'', ''O''), (''NY'', ''LOCATION'')] named_entities = get_continuous_chunks(ne_tagged_sent) named_entities = get_continuous_chunks(ne_tagged_sent) named_entities_str = [" ".join([token for token, tag in ne]) for ne in named_entities] named_entities_str_tag = [(" ".join([token for token, tag in ne]), ne[0][1]) for ne in named_entities] print named_entities print print named_entities_str print print named_entities_str_tag print

[fuera]:

[[(''Rami'', ''PERSON''), (''Eid'', ''PERSON'')], [(''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION'')], [(''NY'', ''LOCATION'')]] [''Rami Eid'', ''Stony Brook University'', ''NY''] [(''Rami Eid'', ''PERSON''), (''Stony Brook University'', ''ORGANIZATION''), (''NY'', ''LOCATION'')]

Pero tenga en cuenta la limitación de que si dos NEs son continuos, entonces podría estar equivocado, sin embargo, todavía no puedo pensar en ningún ejemplo en el que dos NEs sean continuos sin ninguna "O" entre ellos.

Como sugirió @alexis, es mejor convertir la salida NE de Stanford en árboles NLTK:

from nltk import pos_tag from nltk.chunk import conlltags2tree from nltk.tree import Tree def stanfordNE2BIO(tagged_sent): bio_tagged_sent = [] prev_tag = "O" for token, tag in tagged_sent: if tag == "O": #O bio_tagged_sent.append((token, tag)) prev_tag = tag continue if tag != "O" and prev_tag == "O": # Begin NE bio_tagged_sent.append((token, "B-"+tag)) prev_tag = tag elif prev_tag != "O" and prev_tag == tag: # Inside NE bio_tagged_sent.append((token, "I-"+tag)) prev_tag = tag elif prev_tag != "O" and prev_tag != tag: # Adjacent NE bio_tagged_sent.append((token, "B-"+tag)) prev_tag = tag return bio_tagged_sent def stanfordNE2tree(ne_tagged_sent): bio_tagged_sent = stanfordNE2BIO(ne_tagged_sent) sent_tokens, sent_ne_tags = zip(*bio_tagged_sent) sent_pos_tags = [pos for token, pos in pos_tag(sent_tokens)] sent_conlltags = [(token, pos, ne) for token, pos, ne in zip(sent_tokens, sent_pos_tags, sent_ne_tags)] ne_tree = conlltags2tree(sent_conlltags) return ne_tree ne_tagged_sent = [(''Rami'', ''PERSON''), (''Eid'', ''PERSON''), (''is'', ''O''), (''studying'', ''O''), (''at'', ''O''), (''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION''), (''in'', ''O''), (''NY'', ''LOCATION'')] ne_tree = stanfordNE2tree(ne_tagged_sent) print ne_tree

[fuera]:

(S (PERSON Rami/NNP Eid/NNP) is/VBZ studying/VBG at/IN (ORGANIZATION Stony/NNP Brook/NNP University/NNP) in/IN (LOCATION NY/NNP))

Entonces:

ne_in_sent = [] for subtree in ne_tree: if type(subtree) == Tree: # If subtree is a noun chunk, i.e. NE != "O" ne_label = subtree.label() ne_string = " ".join([token for token, pos in subtree.leaves()]) ne_in_sent.append((ne_string, ne_label)) print ne_in_sent

[fuera]:

[(''Rami Eid'', ''PERSON''), (''Stony Brook University'', ''ORGANIZATION''), (''NY'', ''LOCATION'')]


Intente usar el método " enumerar ".

Cuando aplique NER a la lista de palabras, una vez que se crean las tuplas de (palabra, tipo), enumere esta lista usando el enumerar (lista). Esto asignaría un índice a cada tupla en la lista.

Más tarde, cuando extraiga PERSONA / ORGANIZACIÓN / UBICACIÓN de la lista, tendrán un índice adjunto.

1 Hussein 2 Obama 3 II 6 James 7 Naismith 21 Naismith 19 Tony 20 Hinkle 0 Frank 1 Mahan 14 Naismith 0 Naismith 0 Mahan 0 Mahan 0 Naismith

Ahora, sobre la base del índice consecutivo, se puede filtrar un solo nombre.

Hussein Obama II, James Naismith, Tony Hank, Frank Mahan


No exactamente según el requisito del autor del tema para imprimir lo que quiere, tal vez esto pueda ser de alguna ayuda,

listx = [(''Rami'', ''PERSON''), (''Eid'', ''PERSON''), (''is'', ''O''), (''studying'', ''O''), (''at'', ''O''), (''Stony'', ''ORGANIZATION''), (''Brook'', ''ORGANIZATION''), (''University'', ''ORGANIZATION''), (''in'', ''O''), (''NY'', ''LOCATION'')] def parser(n, string): for i in listx[n]: if i == string: pass else: return i name = parser(0,''PERSON'') lname = parser(1,''PERSON'') org1 = parser(5,''ORGANIZATION'') org2 = parser(6,''ORGANIZATION'') org3 = parser(7,''ORGANIZATION'') print name, lname print org1, org2, org3

La salida sería algo como esto

Rami Eid Stony Brook University


Use el contenedor pycorenlp de python y luego use ''entitymentions'' como clave para obtener la parte continua de persona u organización en una sola cadena.