SpaCy:为包含在多个跨度中的令牌设置实体信息

问题描述 投票:0回答:3

我正在尝试使用 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)   

  
spacy spacy-3
3个回答
13
投票

现在可能有点晚了,但是,补充一下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”的更多信息此处

致以诚挚的问候。


4
投票

spacy
v3 开始,您可以使用
doc.spans
来存储可能重叠的实体。
doc.ents
不支持此功能。

所以你有两个选择:

  • 实现一个
    on_match
    回调,在使用结果设置
    doc.ents
    之前过滤掉匹配器的结果。快速浏览一下您的代码(以及后来的编辑),我认为
    resolve_substrings
    实际上并没有解决冲突?理想情况下,
    on_match
    函数应该检查是否与现有实体存在冲突,并决定保留哪些实体。
  • 如果适合您的用例,请使用
    doc.spans
    而不是
    doc.ents

0
投票

另一种选择是覆盖

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

© www.soinside.com 2019 - 2024. All rights reserved.