web-dev-qa-db-de.com

Liste der Personen und Organisationen extrahieren, die Stanford NER Tagger in NLTK verwenden

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?

23
user1680859

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:

  1. 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.

  2. 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). 

  3. Finden Sie heraus, wie Sie Ihr eigenes Chunking zusätzlich zu den Ergebnissen durchführen können, die der Stanford-Tagger liefert.

  4. 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. 

27
alexis

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')]
24
alvas

WARNUNG: Auch wenn Sie dieses Modell "all.3class.distsim.crf.ser.gz" erhalten, verwenden Sie es bitte nicht, da

    Erster grund:

Für dieses Modell haben sich die Leute von Stanford nlp offen für schlechte Genauigkeit entschuldigt 

    2. grund:

Es hat eine schlechte Genauigkeit, da die Groß- und Kleinschreibung beachtet wird. 

    LÖSUNG

verwenden Sie das Modell "english.all.3class.caseless.distsim.crf.ser.gz".

1
yunus

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
1

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

0
Akash Tyagi

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.

0
Abhishek Bisht