我正在尝试使用Python NLTK中的斯坦福命名实体识别器(NER)提取人员和组织的列表。当我跑步时:
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)
输出为:
[('Rami', 'PERSON'), ('Eid', 'PERSON'), ('is', 'O'), ('studying', 'O'), ('at', 'O'), ('Stony', 'ORGANIZATION'), ('Brook', 'ORGANIZATION'), ('University', 'ORGANIZATION'), ('in', 'O'), ('NY', 'LOCATION')]
我想要的是从此列表中以这种形式提取所有人员和组织:
Rami Eid Sony Brook University
我试图遍历元组列表:
for x,y in i: if y == 'ORGANIZATION': print(x)
但是此代码仅每行打印一个实体:
Sony Brook University
有了真实的数据,一个句子中可以有多个组织,一个人,那么我该如何限制不同实体之间的界限呢?
感谢@Vaulstein发现的链接,很显然,受过训练的Stanford标记器(至少在2012年发布) 不会对命名实体进行分块 。从接受的答案:
许多NER系统使用更复杂的标签,例如IOB标签,其中B-PERS之类的代码指示人员实体的起始位置。CRFClassifier类和功能工厂支持此类标签, 但我们当前分发的模型(截至2012年)未使用它们。
您有以下选择:
收集带有相同标签的单词;例如,所有被标记的相邻单词PERSON应一起作为一个命名实体。这很容易,但是当然有时会组合不同的命名实体。(例如New York, Boston [and] Baltimore,大约是三个城市,而不是一个。) 编辑: 这是Alvas的代码在公认的答案中所做的事情。参见下面的简单实现。
PERSON
New York, Boston [and] Baltimore
使用nltk.ne_recognize()。它不使用斯坦福识别器,但使用大块实体。(这是一个围绕IOB的名为实体标记器的包装器)。
nltk.ne_recognize()
在斯坦福标记器返回的结果的基础上找出一种方法来进行自己的分块。
为您感兴趣的域训练自己的IOB命名实体分块器(使用Stanford工具或NLTK的框架)。如果您有足够的时间和资源来执行此操作,则可能会获得最佳效果。
编辑: 如果您只想提取连续命名实体的运行(上述选项1),则应使用itertools.groupby:
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))
如果netagged_words是(word, type)您的问题中的元组列表,则会产生:
netagged_words
(word, type)
PERSON Rami Eid ORGANIZATION Stony Brook University LOCATION NY
再次注意,如果两个相同类型的命名实体彼此相邻出现,则此方法会将它们组合在一起。例如New York, Boston [and] Baltimore大约三个城市,而不是一个。