用 SpaCy 中的标签替换实体

Replace entity with its label in SpaCy

SpaCy 有没有用它的标签替换 SpaCy NER 检测到的实体? 例如: 我一边吃苹果一边玩我的 Apple Macbook。

我已经用 SpaCy 训练了 NER 模型来检测 "FRUITS" 实体,并且该模型成功地将第一个 "apple" 检测为 "FRUITS",但没有检测到第二个 "Apple"。

我想通过用标签替换每个实体来对我的数据进行 post 处理,所以我想用 "FRUITS" 替换第一个 "apple"。句子将是“我在玩我的 Apple Macbook 时正在吃水果。

如果我只使用正则表达式,它会将第二个 "Apple" 替换为 "FRUITS",这是不正确的。有什么聪明的方法可以做到这一点吗?

谢谢!

实体标签是令牌的一个属性(参见here

import spacy
from spacy import displacy
nlp = spacy.load('en_core_web_lg')

s = "His friend Nicolas is here."
doc = nlp(s)

print([t.text if not t.ent_type_ else t.ent_type_ for t in doc])
# ['His', 'friend', 'PERSON', 'is', 'here', '.']

print(" ".join([t.text if not t.ent_type_ else t.ent_type_ for t in doc]) )
# His friend PERSON is here .

编辑:

为了处理实体可以跨越多个单词的情况,可以改用以下代码:

s = "His friend Nicolas J. Smith is here with Bart Simpon and Fred."
doc = nlp(s)
newString = s
for e in reversed(doc.ents): #reversed to not modify the offsets of other entities when substituting
    start = e.start_char
    end = start + len(e.text)
    newString = newString[:start] + e.label_ + newString[end:]
print(newString)
#His friend PERSON is here with PERSON and PERSON.

更新:

Jinhua Wang 提醒我,现在有一种更内置、更简单的方法可以使用 merge_entities 管道来执行此操作。 请参阅下面金华的回答。

@DBaker 答案的略短版本,它使用 end_char 而不是计算它:

for ent in reversed(doc.ents):
    text = text[:ent.start_char] + ent.label_ + text[ent.end_char:]

当实体可以跨越多个单词时,对@DBaker 上面的解决方案进行更优雅的修改:

import spacy
from spacy import displacy
nlp = spacy.load('en_core_web_lg')
nlp.add_pipe("merge_entities")

s = "His friend Nicolas J. Smith is here with Bart Simpon and Fred."
doc = nlp(s)

print([t.text if not t.ent_type_ else t.ent_type_ for t in doc])
# ['His', 'friend', 'PERSON', 'is', 'here', 'with', 'PERSON', 'and', 'PERSON', '.']

print(" ".join([t.text if not t.ent_type_ else t.ent_type_ for t in doc]) )
# His friend PERSON is here with PERSON and PERSON .

您可以查看 Spacy here 上的文档。它使用内置的管道来完成这项工作,并且对多处理有很好的支持。我相信这是官方支持的用标签替换实体的方式。