news 2026/9/12 6:58:32

spaCy 如何用 spancat 组件构建 Span 级文本分类流水线?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
spaCy 如何用 spancat 组件构建 Span 级文本分类流水线?

spaCy 如何用 spancat 组件构建 Span 级文本分类流水线?

【免费下载链接】spaCy💫 Industrial-strength Natural Language Processing (NLP) in Python项目地址: https://gitcode.com/GitHub_Trending/sp/spaCy

如果你需要的不是"整句分一个类",而是"文本中任意位置的若干 Span 各打一个标签"——比如一句话里同时标出人名和机构名,或者给文档里出现的每个条款片段打上状态标签——spaCy 提供的spancat组件就是为这种 Span 级多标签分类设计的。它由两部分组成:一个 suggester 函数负责提出候选 Span(可以重叠),一个 labeler 模型负责给每个候选预测零个或多个标签。预测结果保存在doc.spans[spans_key]这个SpanGroup中,各 Span 的分数存放在doc.spans[spans_key].attrs["scores"]

需要注意:spancat目前被文档标注为实验性(experimental)组件。spancatspancat_singlelabel(v3.5.1 新增)是两种形态:一个 Span 可能同时命中多个类别时用spancat(内部用Logistic层,各类别概率独立);一个 Span 至多属于一个类别时用spancat_singlelabel(内部用Softmax层,按多分类问题处理)。本文按spancat多标签场景组织,单标签分支在结尾给出。

第一步:用 init config 生成带 spancat 的配置文件

spaCy 推荐的训练入口是命令行spacy train,它只依赖一个config.cfg配置文件,包含全部设置与超参数。配置文件由 quickstart 模板生成,模板中已经内置了spancat的组件块,所以可以直接通过--pipeline参数把它包含进来:

$ python -m spacy init config base_config.cfg --lang en --pipeline spancat

--pipeline是逗号分隔的可训练组件列表,--lang指定语言。生成的是基础配置,还要用init fill-config填充其余默认值——spaCy 要求训练配置完整、没有隐藏默认值,实验才可复现:

$ python -m spacy init fill-config base_config.cfg config.cfg

生成的config.cfg中,[components.spancat]块对应文档给出的默认配置:factory = "spancat"spans_key = "sc"threshold = 0.5max_positive = null(不限制每个 Span 的正类数量),模型为spacy.SpanCategorizer.v1,suggester 为spacy.ngram_suggester.v1sizes = [1, 2, 3](即候选 Span 长度为 1 到 3 个 token)。

调整候选 Span 的范围:更换 suggester

默认ngram_suggester会提出所有指定长度的 Span,文档提供了三种注册 suggester,按需替换[components.spancat.suggester]块:

# 指定长度集合:提出 1 或 2 个 token 组成的 Span [components.spancat.suggester] @misc = "spacy.ngram_suggester.v1" sizes = [1, 2] # 长度区间:提出 min_size 到 max_size(含两端)之间的所有长度 [components.spancat.suggester] @misc = "spacy.ngram_range_suggester.v1" min_size = 2 max_size = 4 # 只评估上游组件已经写入 doc.spans 的 Span [components.spancat.suggester] @misc = "spacy.preset_spans_suggester.v1" spans_key = "my_spans"

preset_spans_suggester适用于前一个组件(如SpanRulerSpanFinder)已经在doc.spans[spans_key]里写好了 Span 的情况,spancat 只对这些既有 Span 打分。

第二步:准备带 spans 标注的训练数据

spaCy 的训练数据是二进制的.spacy文件(序列化的DocBin)。对 spancat 来说,关键是在Doc上把标注写到与spans_key相同的 key 下——初始化和训练时,组件会在参考文档的同一个 key 下查找 Span。

用 Python 构造数据的做法:对每段文本,先用nlp(text)得到Doc,再用字符偏移(char_span)创建Span,写进doc.spans[spans_key],然后加入DocBin并落盘:

import spacy from spacy.tokens import DocBin nlp = spacy.blank("en") # (文本, [(start_char, end_char, 标签), ...]) training_data = [ ("Apple is looking at buying U.K. startup", [(0, 5, "ORG"), (34, 41, "GPE")]), ] db = DocBin() for text, spans in training_data: doc = nlp(text) doc.spans["sc"] = [ doc.char_span(start, end, label=label) for start, end, label in spans ] db.add(doc) db.to_disk("./train.spacy")

这里spans_key使用默认值"sc",与你训练用的 key 保持一致。开发集dev.spacy用同样方法生成,spacy train会用它在每个 epoch 结束后评估。

数据格式的文档定义也印证了这个结构:spans字段是spans_key -> List[Tuple]的字典,每个 tuple 为(start_char, end_char, label, kb_id)(数据格式说明)。

数据就绪后,可以用debug data命令分析训练与开发数据、统计标签分布、发现无效标注等问题:

$ python -m spacy debug data config.cfg

第三步:确认 score_weights 与 spans_key 一致

spancat 文档给出一个必须注意的坑:如果你把spans_key改成非默认值,必须同步更新[training.score_weights],否则指标权重无法正确计算。例如spans_key = "myspankey"时,配置里要写成:

[training.score_weights] spans_myspankey_f = 1.0 spans_myspankey_p = 0.0 spans_myspankey_r = 0.0

score_weights决定训练日志中显示哪些指标、以及它们如何加权进决定"最佳模型"的最终得分;设为null的权重会排除在日志和加权之外。保持默认spans_key = "sc"则无需改动,由init config生成的权重直接可用。

第四步:运行训练

训练命令只需传入 config 和输出目录,数据路径写在[paths]段或直接在命令行传入:

$ python -m spacy train config.cfg --output ./output --paths.train ./train.spacy --paths.dev ./dev.spacy

训练过程中每个 pass 结束会打印指标表。与 spancat 相关的指标包括 Loss(训练损失,代表优化器剩余的工作量,应下降但通常不会到 0)、Precision(预测标注中正确的比例)、Recall(参考标注被召回的比例)、F-Score(两者的调和平均)与 Speed(words per second,应保持平稳)。spacy train结束时会把最终填充完成的config.cfg与管道一起导出,所以你始终留有一份实际使用的设置记录。

可选:如果训练使用 GPU,加--gpu-id

$ python -m spacy train config.cfg --gpu-id 0

标签初始化方面,组件可以通过get_examples回调自动从训练数据中读取标签;为了加速,也可以先用init labels生成标签 JSON,再在[initialize.components.spancat.labels]中通过@readers = "spacy.read_labels.v1"path指过去(参考 SpanCategorizer.initialize 的示例)。文档强调标签 JSON 的格式因组件而异,应始终让init labels自动生成,不要手写。

第五步:加载模型并验证预测

训练产出是./output下的一个模型目录(未打包的数据目录)。加载后对文本做预测,结果落在doc.spans下你配置的 key 中:

import spacy nlp = spacy.load("./output") doc = nlp("Apple is looking at buying U.K. startup") for span in doc.spans["sc"]: score = doc.spans["sc"].attrs["scores"] print(span.text, span.label_, span.start_char, span.end_char)

判断方式很直接:doc.spans["sc"]中出现的每个Span都是一个正预测,span.label_是命中的类别,span.start_char/span.end_char是字符偏移;attrs["scores"]里保存的是各 Span 的分数(threshold默认 0.5,低于该值的预测不会被写入 Doc)。如果doc.spans["sc"]为空,说明模型在当前threshold下没有产生正预测——此时先确认训练数据里 Span 标注确实写在同一个 key 下,再考虑训练轮数或候选范围是否覆盖了目标 Span 长度。

另外两个文档给出的验证/调试手段:

  • spancat.set_candidates(docs, "candidates"):用 suggester 把候选 Span 写入指定 key,文档明确说明该方法是调试用途——用它检查模型实际在评估哪些候选 Span;
  • 单标签的spancat_singlelabel默认add_negative_label = True,未标注 Span 会被学成特殊负标签,这些负标签 Span 不会作为标注存储。

单标签场景(spancat_singlelabel,v3.5.1+)

若一个 Span 至多属于一个类别,把管道中的组件换成spancat_singlelabel即可,配置与 spancat 类似但多了几个参数(见 SpanCategorizer 文档的示例):

from spacy.pipeline.spancat import DEFAULT_SPANCAT_SINGLELABEL_MODEL config = { "spans_key": "labeled_spans", "model": DEFAULT_SPANCAT_SINGLELABEL_MODEL, "suggester": {"@misc": "spacy.ngram_suggester.v1", "sizes": [1, 2, 3]}, "negative_weight": 0.8, "allow_overlap": True, } nlp.add_pipe("spancat_singlelabel", config=config)

相关设置的含义以文档为准:negative_weight是损失项乘子,负样本过多时可用它降权;allow_overlap表示数据中允许重叠 Span,仅当max_positive恰好为 1 时可用;add_negative_label在使用Softmax层时应当为True,这也是spancat_singlelabel的默认值。训练路径不变:init config --pipeline spancat_singlelabelinit fill-configtrain

限制与边界

  • spancat组件带 experimental 标记,API 可能随版本调整。
  • spancat组件会覆盖doc.spans[spans_key]下已有的内容;如果你的上游组件(如SpanRulerSpanFinder)也写doc.spans,请让它们用不同的 key,再用preset_spans_suggester读取上游 key,避免互相覆盖。
  • 候选范围完全由 suggester 决定:目标 Span 如果超出sizesmin_size/max_size范围,模型根本没有机会为它打分,预测为空不是"模型没学会"而是"候选不存在"。
  • 改了spans_key却没同步[training.score_weights],指标计算会不对——这是 spancat 文档单独用警告框提示的点。

更多组件方法(predictset_annotationsupdate、序列化字段等)见 SpanCategorizer API 文档;训练配置系统(变量插值、CLI 覆盖、自定义函数)见 Training Pipelines & Models。

【免费下载链接】spaCy💫 Industrial-strength Natural Language Processing (NLP) in Python项目地址: https://gitcode.com/GitHub_Trending/sp/spaCy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 6:57:34

Upscayl AI图像放大实战:批量放大一整文件夹500px图片到4倍

Upscayl AI图像放大实战:批量放大一整文件夹500px图片到4倍 【免费下载链接】upscayl 🆙 Upscayl - #1 Free and Open Source AI Image Upscaler for Linux, MacOS and Windows. 项目地址: https://gitcode.com/GitHub_Trending/up/upscayl Upsca…

作者头像 李华
网站建设 2026/9/12 6:54:56

CLAUDE.md:AI协作项目的结构化记忆中枢设计

1. 项目概述:CLAUDE.md 如何成为AI项目的"记忆中枢"在多人协作的AI项目开发中,最头疼的问题莫过于"规范失忆"——新加入的开发者总要反复询问"这个参数为什么设0.7?""那段异常处理逻辑是谁加的&#xff1…

作者头像 李华
网站建设 2026/9/12 6:53:57

Android消息循环机制:Looper、Handler与线程通信解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 6:52:53

Stagehand x CrewAI 集成实战:基于 MCP/stdio 的 Facade 桥接方案

Stagehand x CrewAI 集成实战:基于 MCP/stdio 的 Facade 桥接方案 【免费下载链接】stagehand The SDK For Browser Agents 项目地址: https://gitcode.com/GitHub_Trending/stag/stagehand 导读 本文讲解如何在 Python CrewAI 框架中接入 Stagehand 浏览器…

作者头像 李华