我正在尝试使用 SpaCy 进行本体世界中的实体上下文识别。我是使用 SpaCy 的新手,只是作为初学者使用。
我使用 ENVO Ontology 作为我的“模式”列表来创建用于实体识别的字典。简单来说,数据是一个 ID (CURIE) 以及它对应的实体的名称及其类别。
以下是我初始代码的工作流程:
# Set terms and patterns
terms = {}
patterns = []
for curie, name, category in envoTerms.to_records(index=False):
if name is not None:
terms[name.lower()] = {'id': curie, 'category': category}
patterns.append(nlp(name))
@Language.component('envo_extractor')
def envo_extractor(doc):
matches = matcher(doc)
spans = [Span(doc, start, end, label = 'ENVO') for matchId, start, end in matches]
doc.ents = spans
for i, span in enumerate(spans):
span._.set("has_envo_ids", True)
for token in span:
token._.set("is_envo_term", True)
token._.set("envo_id", terms[span.text.lower()]["id"])
token._.set("category", terms[span.text.lower()]["category"])
return doc
# Setter function for doc level
def has_envo_ids(self, tokens):
return any([t._.get("is_envo_term") for t in tokens])
##EDIT: #################################################################
def resolve_substrings(matcher, doc, i, matches):
# Get the current match and create tuple of entity label, start and end.
# Append entity to the doc's entity. (Don't overwrite doc.ents!)
match_id, start, end = matches[i]
entity = Span(doc, start, end, label="ENVO")
doc.ents += (entity,)
print(entity.text)
#########################################################################
nlp = spacy.load("en_core_web_sm")
matcher = PhraseMatcher(nlp.vocab)
#### EDIT: Added 'on_match' rule ################################
matcher.add("ENVO", None, *patterns, on_match=resolve_substrings)
nlp.add_pipe('envo_extractor', after='ner')
管道看起来像这样
[('tok2vec', <spacy.pipeline.tok2vec.Tok2Vec at 0x7fac00c03bd0>),
('tagger', <spacy.pipeline.tagger.Tagger at 0x7fac0303fcc0>),
('parser', <spacy.pipeline.dep_parser.DependencyParser at 0x7fac02fe7460>),
('ner', <spacy.pipeline.ner.EntityRecognizer at 0x7fac02f234c0>),
('envo_extractor', <function __main__.envo_extractor(doc)>),
('attribute_ruler',
<spacy.pipeline.attributeruler.AttributeRuler at 0x7fac0304a940>),
('lemmatizer',
<spacy.lang.en.lemmatizer.EnglishLemmatizer at 0x7fac03068c40>)]
# Set extensions to tokens, spans and docs
Token.set_extension('is_envo_term', default=False, force=True)
Token.set_extension("envo_id", default=False, force=True)
Token.set_extension("category", default=False, force=True)
Doc.set_extension("has_envo_ids", getter=has_envo_ids, force=True)
Doc.set_extension("envo_ids", default=[], force=True)
Span.set_extension("has_envo_ids", getter=has_envo_ids, force=True)
现在,当我运行文本“组织培养”时,它会抛出一个错误:
nlp('tissue culture')
ValueError: [E1010] Unable to set entity information for token 0 which is included in more than one span in entities, blocked, missing or outside.
我知道为什么会发生错误。这是因为 ENVO 数据库中有 2 个“组织培养”短语条目,如下所示:
理想情况下,我希望根据文本中出现的短语标记适当的 CURIE。我该如何解决这个错误?
我的 SpaCy 信息:
============================== Info about spaCy ==============================
spaCy version 3.0.5
Location *irrelevant*
Platform macOS-10.15.7-x86_64-i386-64bit
Python version 3.9.2
Pipelines en_core_web_sm (3.0.0)
现在可能有点晚了,但是,补充一下Sofie VL的答案,对于任何可能仍然对此感兴趣的人,我(另一个spaCy新手,哈哈)为摆脱重叠跨度所做的事情,如下:
import spacy
from spacy.util import filter_spans
# [Code to obtain 'entity']...
# 'entity' should be a list, i.e.:
# entity = ["Carolina", "North Carolina"]
pat_orig = len(entity)
filtered = filter_spans(ents) # THIS DOES THE TRICK
pat_filt =len(filtered)
doc.ents = filtered
print("\nCONVERSION REPORT:")
print("Original number of patterns:", pat_orig)
print("Number of patterns after overlapping removal:", pat_filt)
值得一提的是,我目前使用的是 spaCy 的最新版本,v3.1.1。此外,只有当您实际上不介意删除重叠跨度时,它才会起作用,但如果您介意,那么您可能想看看此线程。有关“filter_spans”的更多信息此处。
致以诚挚的问候。
从
spacy
v3 开始,您可以使用 doc.spans
来存储可能重叠的实体。 doc.ents
不支持此功能。
所以你有两个选择:
on_match
回调,在使用结果设置 doc.ents
之前过滤掉匹配器的结果。快速浏览一下您的代码(以及后来的编辑),我认为 resolve_substrings
实际上并没有解决冲突?理想情况下,on_match
函数应该检查是否与现有实体存在冲突,并决定保留哪些实体。doc.spans
而不是 doc.ents
。另一种选择是覆盖
doc.etns
并包含实体。
import spacy
from spacy.matcher import PhraseMatcher
from spacy.tokens import Span
def resolve_substrings(matcher, doc, i, matches):
# Get the current match and create tuple of entity label, start and end.
match_id, start, end = matches[i]
entity = Span(doc, start, end, label="ORG")
'''
# doc.ents += (entity,) ==> ValueError: [E1010] Unable to set entity information
for token 1 which is included in more than one span
in entities, blocked, missing or outside.
'''
# Overwrite doc.ents and add entity – be careful not to replace!
doc.ents = list(doc.ents) + [entity]
patterns = [...]
nlp = spacy.load("en_core_web_sm")
matcher = PhraseMatcher(nlp.vocab)
matcher.add("ENVO", patterns, on_match=resolve_substrings)
nlp.add_pipe('envo_extractor', after='ner')
u2064通过将新实体添加到现有
doc.ents
列表中,我们确保保留所有先前标识的实体,并且在添加新实体时现有 doc.ents
不会被覆盖。 u2064u2064此方法还维护实体识别器输出的完整性,避免当令牌是多个跨度的一部分时发生冲突。 u2064