You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从字符串位置定位spaCy的Span对象?

解决方法

一、直接用spaCy内置工具转换字符索引到Token索引

spaCy的Doc对象自带char_span()方法,能直接把文本的字符起始/结束位置转换成对应的Span对象,完美解决你遇到的索引不匹配问题。

修改后的核心代码:

@Language.component("addresses")
def searchNames(doc: Doc) -> Doc:
    """识别带称谓的姓名并标记为PERSON实体"""
    # 修正正则,匹配完整的称谓+姓名(比如Mr. Smith)
    pattern = re.compile(r"(Mr|Mrs|Ms|Dr|Prof)\.\s[A-Z][a-zA-Z]+")
    new_ents = []
    for match in pattern.finditer(doc.text):
        # 转换字符索引为Span,alignment_mode="expand"处理边界不完全匹配的情况
        span = doc.char_span(match.start(), match.end(), label="PERSON", alignment_mode="expand")
        if span:  # 确保转换成功才加入
            new_ents.append(span)
    # 合并原有实体和新识别的实体
    doc.ents = list(doc.ents) + new_ents
    return doc

二、更推荐:用spaCy的Matcher替代正则(贴合spaCy生态)

spaCy的Matcher是基于token的匹配工具,无需处理字符索引,直接针对token的属性(文本、词性等)定义规则,稳定性更高:

from spacy.matcher import Matcher

@Language.component("addresses")
def searchNames(doc: Doc) -> Doc:
    """识别带称谓的姓名并标记为PERSON实体"""
    matcher = Matcher(doc.vocab)
    # 定义匹配规则:称谓(Mr./Mrs./Dr.等) + 大写开头的姓名
    pattern = [
        {"TEXT": {"IN": ["Mr.", "Mrs.", "Ms.", "Dr.", "Prof."]}},
        {"TEXT": {"REGEX": "^[A-Z][a-zA-Z]+$"}}
    ]
    matcher.add("PERSON_WITH_TITLE", [pattern])
    matches = matcher(doc)
    
    new_ents = []
    for match_id, start, end in matches:
        span = Span(doc, start, end, label="PERSON")
        new_ents.append(span)
    
    # 合并实体时去重,避免和内置NER识别的内容重复
    existing_ents = [(ent.start, ent.end) for ent in doc.ents]
    for span in new_ents:
        if (span.start, span.end) not in existing_ents:
            doc.ents = list(doc.ents) + [span]
    return doc

关键注意事项

  • 正则要匹配完整姓名而非仅称谓,否则标记的实体只是Dr./Mr.这类前缀,不是完整人名。
  • 使用char_span时,alignment_mode="expand"可以避免因字符范围和token边界不完全对齐导致的转换失败。
  • 合并实体时建议去重,防止和spaCy内置NER识别的内容重复标记。

内容的提问来源于stack exchange,提问作者Владислав Король

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:10:40