news 2026/9/10 4:36:48

spaCy 训练数据转换实战:用 spacy convert 将 NER/IOB 与旧版 JSON 转为 DocBin 格式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
spaCy 训练数据转换实战:用 spacy convert 将 NER/IOB 与旧版 JSON 转为 DocBin 格式

spaCy 训练数据转换实战:用 spacy convert 将 NER/IOB 与旧版 JSON 转为 DocBin 格式

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

在 spaCy 中训练命名实体识别(NER)模型前,把标注数据统一成.spacy(DocBin 序列化)格式是标准的第一步。仓库 extra/example_data/ner_example_data/ 目录提供了四组可直接用于练习的 NER/IOB 标注样例,以及配套说明文档 README.md。本文以这份说明文档为主线,结合 convert 命令实现 与各转换器源码,完整讲解:四种 IOB/NER 输入格式长什么样、spacy convert每个参数的真实含义、IOB→BILUO→实体标注的底层转换链路,以及如何把 spaCy v2 时代的 JSON 训练文件平滑迁移到 v3。读完你就能手把手把自己手上的 IOB/JSON 标注数据转成可直接用于spacy train.spacy文件。

目录里有什么:四组 NER 样例数据一览

extra/example_data/ner_example_data/ 共包含 1 份 README 和 8 个数据文件,内容全部来自同一段关于自动驾驶先驱 Sebastian Thrun 的英文访谈文本,标注了PERSON(人名)、ORG(机构)、NORP(民族/政治团体)、DATE(日期)四类实体:

文件格式列结构
ner-sent-per-line.iob每行一个句子词形\|词性\|IOB标签,词条间以空格分隔
ner-token-per-line.iob每行一个词两列(制表符分隔):词形 NER标签
ner-token-per-line-with-pos.iob每行一个词三列:词形 词性 NER标签
ner-token-per-line-conll2003.iobCoNLL-2003 风格四列:词形 词性 _ NER标签,含-DOCSTART-文档分隔符
对应同名 4 个.json文件spaCy v2 旧版 JSON 训练格式id / paragraphs / sentences / tokens(orth, tag, ner)

其中 4 个.json文件并非手写,而是 README 明确说明的:这些 spaCy v2 JSON 训练文件是用 spaCy v2 的spacy convert从上述 IOB 文件自动生成的(生成命令见后文“复现 v2 时代”一节)。因此这 8 个文件构成了一条完整的格式演进链路:手工 IOB 标注 → v2 JSON → v3.spacy

四种 IOB/NER 输入格式详解

1. 每行一句:词条以|分隔(ner-sent-per-line.iob)

ner-sent-per-line.iob 的每一行是一个完整的句子,句中每个词条写作词形|词性|IOB标签,词条之间用空格分隔:

When|WRB|O Sebastian|NNP|B-PERSON Thrun|NNP|I-PERSON started|VBD|O working|VBG|O ... Google|NNP|B-ORG in|IN|O 2007|CD|B-DATE ,|,|O ...

这种格式正是 IOB 转换器 iob_to_docs.py 的直接输入。源码 docstring 给出了完全一致的样例语法,并声明“IOB and IOB2 are accepted”(同时接受 IOB/IOB2 两种变体),且每个词条支持两种字段数量:

  • 三字段词形|词性|IOB(如London|NNP|I-GPE),词性会被写入 Token 的tag_
  • 两字段词形|IOB(如London|I-GPE),此时词性统一置为"-"占位。

解析时按空格line.split()切出词条、再按|切出字段,字段数不是 2 或 3 会抛出Errors.E902。每个非空行都会被标记为一个句子起点(is_sent_start)。

2. 每词一行、无词性(ner-token-per-line.iob)

ner-token-per-line.iob 改为每行一个词、两列(词形与 NER 标签),词与标签之间使用制表符,空行作为句子分隔:

When O Sebastian B-PERSON Thrun I-PERSON ... Google B-ORG in O 2007 B-DATE

注意它没有 POS 词性列。这类“首列为词、末列为 NER 标签”的空白分隔列式格式,对应的是CoNLL NER 转换器conll_ner_to_docs.py,其 docstring 写明:“第一列是 token,最后一列是 IOB 标签;若存在第二列,则第二列是词性标签”。因此它并不适合-c iob(IOB 转换器要求|分隔),应使用-c ner-c conll

3. 每词一行、带词性(ner-token-per-line-with-pos.iob)

ner-token-per-line-with-pos.iob 在上一格式基础上增加了第二列词性标签:

When WRB O Sebastian NNP B-PERSON Thrun NNP I-PERSON

三列对应关系为词形 | 词性(POS) | NER标签。转换时词性会保留为tag_,可用于后续训练 tagger 与 NER 联合模型。

4. CoNLL-2003 风格(ner-token-per-line-conll2003.iob)

ner-token-per-line-conll2003.iob 完全复刻 CoNLL-2003 共享任务的文件约定,四列分别是词形 词性 句法占位(_) NER标签,并用-DOCSTART- -X- O O行分隔文档、空行分隔句子:

-DOCSTART- -X- O O When WRB _ O Sebastian NNP _ B-PERSON Thrun NNP _ I-PERSON started VBD _ O ... Google NNP _ B-ORG ...

conll_ner_to_docs.py 的源码专门处理了这种格式:以-DOCSTART- -X- O O作为文档定界符,空白行作为句子边界。源码还包含两条智能兼容逻辑:

  • 若数据中已有\n\n句子边界且指定了-s,会警告“发现句子边界,自动断句已禁用”,并把seg_sents置为False
  • 若数据中已含-DOCSTART-文档定界符且指定了-n,会警告“发现文档定界符,自动文档切分已禁用”,并把n_sents置为0

也就是说,对于本目录这种已经带完整边界标记的文件,-s/-n参数会被自动安全地忽略,不会破坏原有结构。

spacy convert 命令全解:从 CLI 定义到转换器注册表

spacy convert的入口定义在 spacy/cli/convert.py,其职责在源码 docstring 中写得很清楚:“把文件转换为用于训练的 json 或 DocBin 格式,产出的.spacy文件可被train命令及其他实验管理功能使用”。全部参数如下(参数名与默认值均取自源码):

参数简写默认值含义
--file-type-tspacy输出类型:jsonspacy
--n-sents-n1每个 Doc 包含的句子数,0表示禁用自动切分
--seg-sents-sFalse-c ner启用句子切分
--model/--base-bNone用于句子切分的基础已训练 pipeline
--morphology-mFalse是否把形态特征追加到词性标签后
--merge-subtokens-TFalse合并 CoNLL-U 的子 token
--converter-cAUTO指定转换器:conllubio / conllu / conll / ner / iob / json
--ner-map-nmNoneNER 标签映射(JSON 编码的实体类型字典)
--lang-lNone需要 tokenizer 时指定的语言
--concatenate-CNone把所有输出合并到单个文件

转换器通过 CONVERTERS 注册表 分发:conllubio/conllu走 CoNLL-U 转换器,conll/ner走 CoNLL NER 转换器,iob走 IOB 转换器,json走 JSON 转换器。源码注释还说明了一个自动检测细节:“转换器按文件扩展名匹配,ner/iob除外,它们是按扩展名和内容共同匹配的”——即AUTO模式下,.iob这类文件会根据实际内容嗅探格式。另外,当输出目录缺省为-(stdout)且输出格式为 JSON 时,数据直接写到标准输出,可配合重定向生成文件,例如源码 docstring 中的spacy convert some_file.conllu --file-type json > some_file.json

实战一:把 IOB 文件转为 v3 .spacy(DocBin)

README 给出的 spaCy v3 转换命令为:

python -m spacy convert -c iob -s -n 10 -b en_core_web_sm file.iob .

逐项拆解这条命令:

  • -c iob:强制指定 IOB 转换器。对 ner-sent-per-line.iob 这类词形|词性|IOB|分隔格式是必需的;
  • -s:启用句子切分;-b en_core_web_sm:指定作为切分基础的已训练英文 pipeline。需要说明的是,从 iob_to_docs.py 的函数签名iob_to_docs(input_data, n_sents=10, no_print=False, *args, **kwargs)可以看到,seg_sentsmodel对 IOB 转换器会落入**kwargs被忽略——IOB 格式的句子边界本来就来自行结构;-s/-b真正发挥作用是在-c ner场景;
  • -n 10:每 10 个句子组成一个 Doc。对应read_iob中按n_sents大小对行做minibatch分组的逻辑——组内所有词拼成一个 Doc,首行标记为句子起点,其余为后续句子;
  • .:输出目录为当前目录,默认-t spacy产出.spacy文件。

对于目录中另外三个**列式(制表符/空白分隔)**的 IOB 文件,则应改用 CoNLL NER 转换器:

python -m spacy convert -c ner -b en_core_web_sm ner-token-per-line.iob . python -m spacy convert -c ner -b en_core_web_sm ner-token-per-line-with-pos.iob . python -m spacy convert -c ner -b en_core_web_sm ner-token-per-line-conll2003.iob .

转换过程在底层做了什么?以iob_to_docs为例,核心链路是 iob_to_docs.py 中的read_iob

  1. 逐行解析出词表、词性表、IOB 标签表与句子起点标记;
  2. Doc(vocab, words=words)构造最小 Doc,把词性写入doc[i].tag_
  3. 把 IOB 标签经 iob_to_biluo 转成 spaCy 内部使用的BILUO 方案B-开始、I-中间、L-结尾、U-单实体、O外部),再经 tags_to_entities 合并为(label, start, end)跨度;
  4. 最后doc.ents = [Span(doc, start=s, end=e+1, label=L) ...]写入实体。

最终 Doc 携带了词形、词性、句子边界与实体标注四类信息,由DocBin(定义于 spacy/tokens/_serialize.py,通过to_disk落盘)序列化为.spacy文件,供 spacy/cli/train.py 等下游命令消费。

实战二:把 spaCy v2 JSON 训练文件转为 v3 .spacy

如果你手头还留着 v2 时代的 JSON 训练数据(本目录这 4 个.json就是典型样本),README 给出的迁移命令非常简洁——直接用v3的 convert 即可:

python -m spacy convert file.json .

v3 的 JSON 转换器 json_to_docs.py 内部通过json_iterate/json_to_annotations(见 spacy/training/gold_io.py)解析旧式 JSON,再用_fix_legacy_dict_data兼容历史数据形态,最终用annotations_to_doc还原成 Doc。从源码看,当-b未指定时默认使用MultiLanguage()作为语言兜底(spacy/lang/xx/init.py),即纯规则 tokenizer 环境。

以 ner-sent-per-line.json 为例,v2 JSON 的结构是:

[ { "id": 0, "paragraphs": [ { "sentences": [ { "tokens": [ {"orth": "When", "tag": "WRB", "ner": "O"}, {"orth": "Sebastian", "tag": "NNP", "ner": "B-PERSON"}, {"orth": "Thrun", "tag": "NNP", "ner": "L-PERSON"}, ... {"orth": "Google", "tag": "NNP", "ner": "U-ORG"}, {"orth": "2007", "tag": "CD", "ner": "U-DATE"}, ... {"orth": "earlier", "tag": "RBR", "ner": "B-DATE"}, {"orth": "this", "tag": "DT", "ner": "I-DATE"}, {"orth": "week", "tag": "NN", "ner": "L-DATE"} ] } ] } ] } ]

注意一个印证底层原理的细节:JSON 中ner字段已经是B-/I-/L-/U-组成的BILUO 标签(如L-PERSONU-ORGU-DATEL-DATE),而对应的 IOB 源文件里是B-PERSON I-PERSONB-ORGB-DATE的 IOB 写法——这正是 v2 时代执行 convert 时iob_to_biluo转换留下的痕迹,也解释了为什么 JSON 里单 token 实体写作U-、多 token 实体的最后一个 token 写作L-。四个 JSON 文件中,ner-token-per-line.json 的词性统一为"-"(源文件无词性列),其余三个则保留真实 POS 标签。

实战三:复现 v2 JSON 的生成过程

README 明确记录了这些 JSON 文件的出处——使用spaCy v2生成:

python -m spacy convert -c iob -s -n 10 -b en file.iob

这条 v2 命令与 v3 版本的差异仅在基础模型参数:v2 时代使用短名-b en(对应当时的英语模型),v3 则要求完整模型名(如en_core_web_sm)。如果你需要把旧 IOB 数据批量“复刻”出 v2 JSON 再做迁移,可以先在 v2 环境跑上面的命令得到 JSON,再到 v3 环境执行实战二中的命令。这也解释了本目录“IOB 源文件 + JSON 中间产物”成对存放的用意——它们是同一份标注数据在不同格式时代的存档。

转换结果如何衔接训练

完成转换后,产出物是与输入同名的.spacy文件(如ner-sent-per-line.spacy),内部是一个 DocBin 容器,其中每个 Doc 已具备训练 NER 所需的全部标注:

  • token 文本doc[i].text
  • 词性标签doc[i].tag_(来自 IOB 的第二字段或 JSON 的tag字段);
  • 句子边界doc[i].is_sent_start(来自 IOB 行结构或 JSON 的sentences分组);
  • 实体标注doc.ents(由 IOB/BILUO 标签经tags_to_entities换算出的 Span 集合)。

在 spacy/cli/train.py 对应的训练配置中,将train.corpuspath指向该.spacy文件即可开始训练;旧版 JSON 则建议先统一转成.spacy,这也是 v3 官方推荐的数据流程。需要小样本验证时,-n 10这类分组参数可让你把整份数据切成多个 Doc 以控制 batch 粒度。

使用注意事项

  1. 转换器与格式必须匹配|分隔的每行一句 IOB 用-c iob;空白/Tab 分隔的列式(含 CoNLL-2003)用-c ner-c conll。二者混用会导致解析失败或标签错位。
  2. -s/-b的作用范围:句子切分参数只在-c ner链路生效;-c iob时句子边界来自行结构,-b指定的模型不会参与(源码层面落入**kwargs被忽略)。
  3. -n 0禁用自动切分:若数据已自带文档边界(如-DOCSTART-),转换器会自动禁用-n/-s,无需手动处理。
  4. 标签体系:输入支持 IOB/IOB2,内部统一转为 BILUO 存储,无需手工预转换。
  5. v2 与 v3 命令差异:v3 中-b需使用完整的已训练 pipeline 名(如en_core_web_sm),v2 用短名(en);若需 tokenizer 参与解析而模型不可用,可用-l指定语言(如-l en)。

仓库中的 ner_example_data 目录 是一份可直接复现、零成本的上手数据集:无论你想验证 IOB 转换、练习 v2 JSON 迁移,还是为 NER 训练准备.spacy语料,都可以从本文的几条命令出发,对照源码逐层理解 spaCy 数据管线的工作方式。

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

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

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

Happy-LLM 教程导读:从零开始构建大模型的学习路线与实践指南

Happy-LLM 教程导读:从零开始构建大模型的学习路线与实践指南 【免费下载链接】happy-llm 📚 从零开始构建大模型 项目地址: https://gitcode.com/GitHub_Trending/ha/happy-llm 本篇文章是 Datawhale 开源项目 Happy-LLM(仓库路径 doc…

作者头像 李华
网站建设 2026/9/10 4:36:18

WavLM 全栈语音预训练模型解析与 Transformers 实战指南

WavLM 全栈语音预训练模型解析与 Transformers 实战指南 【免费下载链接】transformers 🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and …

作者头像 李华
网站建设 2026/9/10 4:35:56

SpringBoot+Spark打造汽车销售推荐系统:从协同过滤到冷启动实践

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

作者头像 李华
网站建设 2026/9/10 4:35:24

TradingAgents-CN 任务执行控制与数据同步功能增强实战解析

TradingAgents-CN 任务执行控制与数据同步功能增强实战解析 【免费下载链接】TradingAgents-CN 基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版 项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN 日期: 2025-11-07 作者: TradingAgen…

作者头像 李华
网站建设 2026/9/10 4:34:35

TT马达驱动入门:STM32电机控制的地基三问与硬件闭环实践

1. 为什么TT马达是STM32入门电机控制的“第一块砖”你拆开过玩具车、智能小车套件或者学生实训板吗?十有八九,里面躺着两颗黄铜色、带塑料齿轮箱、直径约13mm的小圆柱——这就是TT马达。它不是工业伺服,不是无刷航模电机,更不是48…

作者头像 李华
网站建设 2026/9/10 4:33:42

大型Java项目Gradle构建提速100倍:从18分钟到3分钟的实战优化

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

作者头像 李华