Python中基于crf-suite自定义标注后如何实现句子分块与依存匹配
自定义CRF标注结合依存关系的结构化分块实现方案
不需要硬改spaCy原生分块规则,按以下步骤实现即可,灵活度完全匹配自定义标注需求,不需要额外重训模型:
1. 预切分同标签连续块
先修正现有Token、Tag序列的对齐问题(你给出的示例列表里"two"和"pears"、"ate"和"ate"之间漏了逗号,先保证两个序列长度一致、逐位对应),之后做第一轮粗切分:
- 逐位遍历对齐后的Token、Tag序列,直接跳过所有标签为
O的无意义token - 把连续出现的相同非O标签token拼接成初始块,同时记录每个块的文本内容、标签类型、块在原token序列中的起始/结束索引
这一步跑完就能拿到无结构的基础块集合,示例句处理后结果如下:
[ {"text": "Due to health concerns", "tag": "M", "start": 0, "end": 3}, {"text": "Donald", "tag": "A", "start": 5, "end": 5}, {"text": "stopped eating", "tag": "V", "start": 6, "end": 7}, {"text": "sweets", "tag": "P", "start": 8, "end": 8}, {"text": "ate", "tag": "V", "start": 10, "end": 10}, {"text": "one apple", "tag": "P", "start": 11, "end": 12}, {"text": "two pears", "tag": "P", "start": 14, "end": 15} ]
2. 仅调用spaCy的依存解析能力,不使用其自带分块
你之前用spaCy效果差的核心原因是直接套用了它内置的名词短语、动词短语分块规则,完全可以只取它的依存解析结果,分块边界100%以你自己训练的CRF标注结果为准:
- 将原句输入spaCy处理,拿到每个token的依存关系属性(
dep_)、父节点索引属性(head.i)即可 - 为每个初始块匹配中心词:单个token组成的块,中心词就是token本身;多个token组成的块,找块内依存弧指向块外的那个token作为中心词。比如
stopped eating块中,eating的父节点是stopped,stopped的父节点是句根,因此这个块的中心词是stopped;one apple块中one的父节点是apple,apple的父节点是句中的动词ate,因此中心词是apple
3. 基于依存关系做块间绑定
以动作类(V)块为核心谓词,按依存规则绑定其他类别的块:
- 主语绑定:如果A块中心词的依存关系是
nsubj(名词主语),将这个A块绑定到它父节点所在的V块 - 宾语绑定:如果P块中心词的依存关系是
dobj(直接宾语),将这个P块绑定到它父节点所在的V块 - 状语绑定:如果M块中心词的依存关系是原因/动机类状语(比如
prep_due_to),且父节点是句根,就将这个M块绑定到全句所有V块 - 并列结构处理:如果两个V块之间通过
cc关系(并列连接词,比如例句里的and)连接,说明两个动作是并列关系,共享所有绑定到前一个V块的A、M类块
对应你给出的示例句,绑定关系如下:
- M块
Due to health concerns绑定所有V块(stopped eating、ate) - A块
Donald绑定所有V块 - P块
sweets绑定V块stopped eating - P块
one apple、two pears绑定V块ate
4. 按绑定关系输出结构化结果
绑定完成后按标签类别归集内容即可,共享论元按绑定的V块数量重复填充,最终就能得到你需要的结构化输出。
避坑提示
- 不要依赖spaCy的内置分块结果,所有块边界以CRF输出为准,从根源上适配你的自定义标注规则
- 数词、量词修饰名词的结构如果被CRF误标为O,可以加极轻量的后处理规则:如果数词/量词的依存父节点是P块里的名词,直接把它合并到对应P块即可,成本远低于重新训练CRF模型
- 并列连接词(and/or等)本身标签为O,切分时直接跳过,不会影响两侧块的独立拆分
内容的提问来源于stack exchange,提问作者user9092346
相关产品推荐
相关产品推荐

