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

基于spaCy及Matcher提取作nsubj的NER与对应支配动词的实现咨询

你之前碰到的所有匹配问题,本质是用按文本顺序匹配的普通Token Matcher,去解决需要依赖语法支配关系才能完成的任务。普通Matcher只认token在文本里的前后位置,根本感知不到句子的句法结构,自然适配不了主语在动词后面的场景。spaCy本身自带的依存树结构才是解决这个需求的最优路径,根本不需要靠复杂的顺序匹配规则绕弯。

方案1:不依赖Matcher,直接通过原生依存树提取(准确率最高)

spaCy里每个带有nsubj依存标签的token,都自带.head属性,这个属性直接指向语法上支配它的父节点,也就是我们要找的关联动词。这个逻辑完全不考虑两个元素在文本里的先后顺序,不管主语在动词前、还是在动词后,都能100%基于语法关系匹配,不受中间间隔多少词的影响。
同时spaCy本身集成了句子切分能力,完全可以替代你现在用的NLTK sent_tokenize,避免切分不准导致的跨句错配。

你之前写的普通顺序匹配规则,本质是在找“从一个nsubj实体开始,往后滑到碰到的第一个动词”,根本不检查这个动词是不是语法上真的支配这个实体的节点,所以既适配不了倒序的主语位置,也会把句子里随便碰到的第一个无关动词当成配对结果,这也是为什么会出现"Florent, soulagé"这类和动词完全无关的错配——因为soulagé是形容词,根本没到动词节点就被规则误判为结束了。

对应的可直接运行的实现代码:

import spacy

# 加载法语中型模型,自带词性标注、依存分析、命名实体识别、句子切分能力
nlp = spacy.load("fr_core_news_md")

def extract_pairs(input_text: str) -> list[dict]:
    result = []
    # 直接遍历模型输出的句子,无跨句问题
    doc = nlp(input_text)
    for sent in doc.sents:
        for token in sent:
            # 筛选两个必要条件:
            # 1. 句法角色是名词性主语nsubj
            # 2. 这个主语本身是有命名实体标签的实体
            if token.dep_ == "nsubj" and token.ent_type_:
                # 直接通过head属性拿到支配它的动词,不需要做跨度匹配
                parent_node = token.head
                result.append(
                    {
                        "实体主语": token.text,
                        "实体类型": token.ent_type_,
                        "支配动词": parent_node.text,
                        "原句": sent.text
                    }
                )
    return result

用这个方法处理你提到的两个示例:

  1. Georges and his friends live in Mexico City:遍历到Georges这个token时,它的dep是nsubj,head是动词live,直接配对成功
  2. "Hello !", says Mary:遍历到Mary这个token时,它的dep是nsubj,head是动词says,不管它在句子里的位置在动词后面,都能正确配对

方案2:如果需要用Matcher实现,改用DependencyMatcher适配双向语序

如果必须用Matcher工具完成需求,就替换掉普通的Matcher,改用spaCy自带的DependencyMatcher——这个工具是专门为依存关系匹配设计的,完全基于依存树的节点关系匹配,不受文本顺序影响。

规则调整说明

你拿到的基础参考规则本身已经覆盖了语序无关的匹配,只需要把关系操作符调整得更兼容即可:

  • REL_OP的作用是定义两个节点的依存支配关系,和文本顺序没有任何关联
  • 把原来的<(直接子节点关系)换成>>(任意层级的支配关系),可以兼容法语里的复合时态、助动词带的长依存链路,不会漏配

调整后的可直接用的匹配规则:

from spacy.matcher import DependencyMatcher

dep_matcher = DependencyMatcher(nlp.vocab)
# 匹配规则的逻辑:找所有「作为nsubj的命名实体」,以及它支配链上的核心动词
pattern = [
    # 第一个节点:目标nsubj命名实体
    {
        "RIGHT_ID": "nsubj_entity",
        "RIGHT_ATTRS": {"DEP": "nsubj", "ENT_TYPE": {"NOT_IN": [""]}}
    },
    # 第二个节点:支配这个nsubj的动词节点,不管文本位置在前还是在后
    {
        "LEFT_ID": "nsubj_entity",
        "REL_OP": ">>",
        "RIGHT_ID": "controlling_verb",
        "RIGHT_ATTRS": {"POS": "VERB"}
    }
]
dep_matcher.add("nsubj_verb_pair", [pattern])

# 调用示例
doc = nlp('"Hello !", says Mary')
matches = dep_matcher(doc)
# 匹配返回的元组里,第一个位置是实体token的索引,第二个是动词的索引,完全不受顺序影响
for match_id, (ent_idx, verb_idx) in matches:
    print(f"实体: {doc[ent_idx].text}, 关联动词: {doc[verb_idx].text}")

这个规则可以同时适配两种常见场景:

  • 主语在动词前:比如Florent regardait,规则能识别到Florent是regardait的nsubj,正确配对
  • 主语在动词后:比如Saget est matinale, dit,规则能识别到Saget是dit的nsubj,不管dit在句子里的位置在Saget后面,都能正确匹配

准确率提升的实用技巧

在不需要追求100%准确的场景下,做几个简单调整就能把准确率从90%拉到95%以上:

  • 移除对外部NLTK切句的依赖,统一用spaCy内置的doc.sents切分,避免不同工具的切分逻辑不一致导致的跨句错配
  • 增加一层过滤:只收录head是实义动词的配对,过滤掉支配节点是助动词、形容词的无效匹配,比如之前出现的"Florent accablé"这类错配
  • 针对法语语法做适配:法语里的代词式动词(比如s'était arrêté)的依存标签有时候会把代词标成nsubj,只要过滤掉ent_type为空的条目,就不会把这类非实体的主语混进结果
  • 做结果去重:同一个句子里如果同一个实体和同一个动词被重复匹配,只保留一次结果,避免输出冗余
  • 可以根据自己的语料场景,适当补充命名实体的类型过滤,比如只保留PER(人名)、LOC(地名)类的实体,过滤掉其他类型的无效nsubj

内容的提问来源于stack exchange,提问作者Etienne Armangau

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 22:24:00