Ich versuche, eine Liste von Personen und Organisationen zu extrahieren, die den Stanford Named Entity Recognizer (NER) in Python NLTK ..__ verwenden.
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)
die Ausgabe ist:
[('Rami', 'PERSON'), ('Eid', 'PERSON'), ('is', 'O'), ('studying', 'O'),
('at', 'O'), ('Stony', 'ORGANIZATION'), ('Brook', 'ORGANIZATION'),
('University', 'ORGANIZATION'), ('in', 'O'), ('NY', 'LOCATION')]
ich möchte alle Personen und Organisationen in dieser Form aus dieser Liste extrahieren:
Rami Eid
Sony Brook University
Ich habe versucht, die Liste der Tupel zu durchlaufen:
for x,y in i:
if y == 'ORGANIZATION':
print(x)
Dieser Code gibt jedoch nur jede Entität pro Zeile aus:
Sony
Brook
University
Bei realen Daten kann es mehrere Organisationen geben, Personen in einem Satz. Wie kann ich die Grenzen zwischen verschiedenen Entitäten setzen?
Dank des von @Vaulstein entdeckten link ist es klar, dass der trainierte Stanford-Tagger (mindestens 2012) keine benannten Entitäten enthält. Von der akzeptierten Antwort :
Viele NER-Systeme verwenden komplexere Labels wie IOB-Labels, bei denen Codes wie B-PERS angeben, wo eine Personeneinheit beginnt. Die CRFClassifier-Klasse und die Feature-Factory unterstützen solche Labels, , aber sie werden in den derzeit von uns vertriebenen Modellen nicht verwendet (Stand 2012).
Sie haben folgende Möglichkeiten:
Sammeln Sie Läufe mit identischen Wörtern. beispielsweise sollten alle benachbarten Wörter, die mit PERSON
gekennzeichnet sind, als eine benannte Entität zusammengefasst werden. Das ist sehr einfach, aber manchmal werden verschiedene benannte Entitäten kombiniert. (Zum Beispiel handelt es sich bei New York, Boston [and] Baltimore
um drei Städte, nicht um eine.) Edit: Dies ist der Code von Alvas in der akzeptierten Antwort. Eine einfachere Implementierung finden Sie unten.
Verwenden Sie nltk.ne_recognize()
. Es verwendet zwar nicht den Stanford-Erkenner, aber es sind Chunk-Entitäten. (Es handelt sich um einen Wrapper um einen IOB namens Entity Tagger).
Finden Sie heraus, wie Sie Ihr eigenes Chunking zusätzlich zu den Ergebnissen durchführen können, die der Stanford-Tagger liefert.
Trainieren Sie Ihren eigenen IOB-benannten Entity-Chunker (mithilfe der Stanford-Tools oder des NLTK-Frameworks) für die Domäne, an der Sie interessiert sind. Wenn Sie Zeit und Ressourcen haben, um dieses Recht auszuführen, werden Sie wahrscheinlich die besten Ergebnisse erzielen.
Edit: Wenn Sie nur fortlaufend benannte Entitäten ziehen möchten (Option 1 oben), sollten Sie itertools.groupby
verwenden:
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))
Wenn netagged_words
die Liste der (Word, type)
-Tupel in Ihrer Frage ist, wird Folgendes erzeugt:
PERSON Rami Eid
ORGANIZATION Stony Brook University
LOCATION NY
Wenn zwei benannte Entitäten desselben Typs direkt nebeneinander auftreten, werden sie bei dieser Methode kombiniert. Z.B. New York, Boston [and] Baltimore
handelt von drei Städten, nicht von einer.
IOB/BIO bedeutet I nside, O utside, B eginning (IOB) oder manchmal auch aka B eginning, I nside, O außer (BIO)
Der Stanford NE-Tagger gibt IOB/BIO-Style-Tags zurück, z.
[('Rami', 'PERSON'), ('Eid', 'PERSON'), ('is', 'O'), ('studying', 'O'),
('at', 'O'), ('Stony', 'ORGANIZATION'), ('Brook', 'ORGANIZATION'),
('University', 'ORGANIZATION'), ('in', 'O'), ('NY', 'LOCATION')]
Der ('Rami', 'PERSON'), ('Eid', 'PERSON')
ist als PERSON gekennzeichnet und "Rami" ist der Anfang oder ein NE-Block und "Eid" ist das Innere. Und dann sehen Sie, dass jedes Nicht-NE mit "O" markiert wird.
Die Idee, einen kontinuierlichen NE-Block zu extrahieren, ist der Named Entity Recognition mit Regular Expression: NLTK sehr ähnlich. Da die Stanford NE-Chunker-API jedoch keinen Nice-Baum zum Analysieren zurückgibt, müssen Sie Folgendes tun:
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
[aus]:
[[('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')]
Beachten Sie jedoch die Einschränkung, dass, wenn zwei NEs stetig sind, dies möglicherweise falsch ist. Dennoch kann ich mir kein Beispiel vorstellen, bei dem zwei NEs kontinuierlich sind, ohne ein "O" zwischen ihnen.
Wie @alexis vorgeschlagen hat, ist es besser, die Stanford-NE-Ausgabe in NLTK-Bäume umzuwandeln:
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
[aus]:
(S
(PERSON Rami/NNP Eid/NNP)
is/VBZ
studying/VBG
at/IN
(ORGANIZATION Stony/NNP Brook/NNP University/NNP)
in/IN
(LOCATION NY/NNP))
Dann:
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
[aus]:
[('Rami Eid', 'PERSON'), ('Stony Brook University', 'ORGANIZATION'), ('NY', 'LOCATION')]
WARNUNG: Auch wenn Sie dieses Modell "all.3class.distsim.crf.ser.gz" erhalten, verwenden Sie es bitte nicht, da
Für dieses Modell haben sich die Leute von Stanford nlp offen für schlechte Genauigkeit entschuldigt
Es hat eine schlechte Genauigkeit, da die Groß- und Kleinschreibung beachtet wird.
verwenden Sie das Modell "english.all.3class.caseless.distsim.crf.ser.gz".
Nicht genau so, wie es der Autor des Themas erfordert, um zu drucken, was er will, vielleicht kann dies eine Hilfe sein
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
Ausgabe wäre so etwas
Rami Eid
Stony Brook University
Versuchen Sie es mit der Methode " enumerate ".
Wenn Sie NER auf die Liste der Wörter anwenden, führen Sie nach der Erstellung von Tupeln (Word, Typ) diese Liste mit der Aufzählung (Liste) auf. Dies würde jedem Tuple in der Liste einen Index zuweisen.
Wenn Sie also später PERSON/ORGANIZATION/LOCATION aus der Liste extrahieren, wird ihnen ein Index zugewiesen.
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
Nun kann auf Basis des fortlaufenden Index ein einzelner Name herausgefiltert werden.
Hussein Obama II, James Naismith, Tony Hank, Frank Mahan
Verwenden Sie den pycorenlp-Wrapper von python und verwenden Sie dann 'entitymentions' als Schlüssel, um den fortlaufenden Teil der Person oder Organisation in einer einzigen Zeichenfolge zu erhalten.