简介这份操作手册面向自然语言处理方向的研究人员、工程师及学生以及希望搭建高质量语料加工流程的技术团队系统讲解如何借助Label Studio完成文本标注并衔接UIE框架的下游训练。内容覆盖安装配置、项目创建、数据上传、标签构建、任务标注、数据导出与格式转换全流程并针对命名实体识别、关系抽取、事件抽取、文本分类、句子级情感分类及实体/评价维度分类等任务给出具体实现方案同时深入解析prompt构造原则对零样本效果的影响如关系类型P需满足“{S}的{P}为{O}的语义合理性。资源包为1个PDF文件约4.68MB结构紧凑便于按章节查阅与实操对照。目前已有810人学习适合需要独立完成标注平台搭建、为模型训练准备高质量数据并优化标注方案的开发者参考。1. 从一堆 txt 到 UIE 能吃的训练集这套标注链路到底值不值得搭手里攒了几百上千条业务文本想微调一个抽取模型结果卡在第一步——数据没有标签。找外包标贵且慢。自己写脚本标规则一多就崩。这时候多数人会想到 Label Studio但真正落地时才发现装完只是开始怎么把标好的数据转成 PaddleNLP UIE 能直接训练的格式才是分水岭。这份操作手册来自 PaddleNLP 仓库的applications/information_extraction/label_studio_text.md它把「安装 Label Studio → 建项目 → 配标签 → 标注 → 导出 JSON → 用label_studio.py转成 UIE 数据格式」整条链路串了起来覆盖命名实体识别、关系抽取、事件抽取、句子级分类、实体/评价维度分类五类任务。适合正在做 NLP 信息抽取、手头有标注需求又不想从零造轮子的工程师和研究者。下面我按自己复现的节奏把每一步的参数、坑和边界拆开讲。2. 环境搭建与项目初始化版本锁死与任务类型选择2.1 为什么必须锁 label-studio1.6.0这份手册明确写了环境配置Python 3.8、label-studio1.6.0、paddleocr 2.6.0.1。很多人看到版本号会习惯性忽略直接pip install label-studio拉最新版然后导出的 JSON 结构和label_studio.py解析逻辑对不上报一堆 KeyError。这不是玄学是 Label Studio 在 1.6 之后调整过导出格式的字段命名。安装命令本身不复杂# 建议在独立虚拟环境里操作避免和已有 paddle 环境冲突 python -m venv lsenv source lsenv/bin/activate # Windows 用 lsenv\Scripts\activate # 锁死版本安装 pip install label-studio1.6.0 pip install paddleocr2.6.0.1 # 启动服务 label-studio start启动后浏览器打开http://localhost:8080/首次使用需要注册一个本地账号。这个账号只存在本地 SQLite 里不联网所以随便填。登录进去之后先别急着建项目确认一下左下角显示的版本号是不是 1.6.0。如果不是后面导出的 JSON 里annotations字段的嵌套层级可能不一样转换脚本会直接挂掉。提示如果你之前装过其他版本的 label-studio先pip uninstall label-studio再装 1.6.0残留的数据库文件在~/.label-studio/下必要时清掉重新初始化。2.2 项目创建时任务类型怎么选才不返工手册里给了一张对应关系命名实体识别、关系抽取、事件抽取、实体/评价维度分类选Relation Extraction文本分类、句子级情感倾向分类也写的是Relation Extraction。这里原文有一处明显的笔误——句子级分类应该选Text Classification但手册写成了Relation Extraction。我实际试过如果句子级分类选了 Relation Extraction 模板后面标签构建阶段根本配不出分类选项只能删项目重来。正确的对应关系我整理成表任务类型项目模板选择标签构建方式命名实体识别Relation ExtractionSpan 标签关系抽取Relation ExtractionSpan Relation 标签事件抽取Relation ExtractionSpan Relation 标签句子级分类Text ClassificationChoices 标签实体/评价维度分类Relation ExtractionSpan Relation Choices创建项目时填名称和描述描述可以写清楚这批数据的来源和标注规范方便多人协作时对齐。标签可以先跳过进项目后在 Setting → Labeling Interface 里配也可以创建时直接加。我一般选择先跳过因为标签 XML 需要根据任务类型仔细写创建向导里的简易编辑器容易漏字段。2.3 数据上传的格式边界手册说「先从本地上传 txt 格式文件选择 List of tasks」。这里有个容易翻车的点Label Studio 1.6.0 对 txt 的解析是按行切分的一行就是一条 task。如果你的原始文本里本身有换行比如一段多行的话术上传后会被拆成多条独立任务标注时上下文就断了。常见做法是先把每条待标文本整理成单行用\n替换掉内部换行再上传。或者直接构造 JSON 格式的 task 列表import json # 把多行文本整理成 Label Studio 能正确解析的单行 task texts [ 张三于2023年5月加入阿里巴巴担任高级算法工程师。, 李四在2022年从清华大学毕业主修计算机科学。, ] tasks [{data: {text: t}} for t in texts] with open(tasks.json, w, encodingutf-8) as f: json.dump(tasks, f, ensure_asciiFalse, indent2)然后在 Label Studio 里选JSON导入方式上传这个文件。这样每条 task 的data.text字段就是完整的一段文本不会被意外切分。参数上注意ensure_asciiFalse否则中文会变成\uXXXX转义虽然不影响解析但人工检查时看着累。3. 标签体系构建Span、Relation 与 Choices 的 XML 写法3.1 Span 类型标签实体抽取的起点实体抽取的标签配置在 Labeling Interface 里用 XML 写。手册里展示了实体类型标签的构建核心是Labels标签下定义LabelView Text nametext value$text/ Labels namelabel toNametext Label value时间 background#FFA39E/ Label value选手 background#D4380D/ Label value赛事名称 background#FFC069/ Label value得分 background#AD8B00/ /Labels /View这段 XML 的逻辑是Text声明了要标注的文本字段value$text对应 task 里的data.textLabels里的每个Label就是一个实体类别。background只是 UI 上的颜色不影响导出数据但建议不同类别用差异明显的颜色标注时肉眼区分快很多。对应的 schema 在转换阶段要写成列表schema [时间, 选手, 赛事名称, 得分]这个 schema 会传给label_studio.py用来告诉转换脚本哪些标签是有效实体类型。如果 XML 里定义了但 schema 里没写转换时会被忽略反过来 schema 里有但 XML 里没标转换脚本会报找不到对应标签。3.2 Relation 类型标签关系抽取的 P 值设计关系抽取需要在 Span 基础上加RelationsView Text nametext value$text/ Labels namelabel toNametext Label value作品名 background#FFA39E/ Label value歌手 background#D4380D/ /Labels Relations Relation value歌手/ Relation value发行时间/ Relation value所属专辑/ /Relations /View手册里特别强调了一段关于 P 类型设置的原则「{S}的{P}为{O}」需要能构成语义合理的短语。比如三元组 (S, 父子, O)关系类别叫「父子」没问题但按 UIE 的 prompt 构造方式「S的父子为O」读起来不通顺改成「孩子」更好即「S的孩子为O」。这个细节直接决定零样本效果——P 类型越自然模型在 prompt 上的语义匹配越准。我自己的经验是配 Relation 标签时先把所有候选 P 值列出来逐个套进「{S}的{P}为{O}」念一遍拗口的就换词。比如「所属专辑」比「专辑」更顺「发行时间」比「发布时间」更常见。这一步花十分钟后面模型效果能差出好几个点。3.3 Choices 类型标签分类任务的配置句子级分类和实体/评价维度分类需要 Choices 标签View Text nametext value$text/ Choices namesentiment toNametext choicesingle Choice value正向/ Choice value负向/ /Choices /Viewchoicesingle表示单选如果是多标签分类改成multiple。对应的 schema 在转换时写成字符串schema 情感倾向[正向负向]注意方括号和逗号都是中文全角这是 UIE 的 prompt 格式要求。手册里句子级分类的 schema 写的就是情感倾向[正向负向]照抄即可别手改成英文标点。实体/评价维度分类的 schema 是嵌套字典schema { 评价维度: [ 观点词, 情感倾向[正向负向] ] }这种结构表示「评价维度」这个实体下面既要抽「观点词」这个 Span又要分类「情感倾向」。转换脚本会根据这个嵌套关系自动构造 prompt比如「XXX的情感倾向[正向,负向]」。4. 标注操作与数据导出从人工点击到 JSON 落地4.1 五类任务的标注界面差异标注本身是在浏览器里点选但不同任务的交互逻辑差别不小。实体抽取就是选中文本片段弹出标签列表选一个关系抽取需要先标出两个实体然后从第一个实体拖一条线到第二个实体再选关系类型事件抽取本质上也是 Span Relation只是 schema 设计上触发词和论元要分开句子级分类是选中整句后点分类按钮实体/评价维度分类最复杂既要标 Span 又要挂分类。手册里给了几个标注示例的 schema我挑事件抽取说一下schema { 地震触发词: [ 时间, 震级 ] }标注时先把「地震」这个词标成「地震触发词」再把时间、震级分别标成对应 Span最后用 Relation 把触发词和论元连起来。导出后转换脚本会根据这个 schema 构造「地震触发词的时间为X」「地震触发词的震级为Y」这样的 prompt。4.2 导出 JSON 的字段结构与重命名标完之后勾选已标注的文本 ID选择导出类型为 JSON。导出的文件默认名字可能是一串 UUID手册要求重命名为label_studio.json并放入./data目录。这一步不是强迫症是因为后面label_studio.py的--label_studio_file参数默认指向这个路径。导出的 JSON 结构大致是[ { id: 1, data: {text: 张三于2023年5月加入阿里巴巴。}, annotations: [ { result: [ { value: {start: 0, end: 2, text: 张三, labels: [人物]}, type: labels } ] } ] } ]转换脚本会读annotations[0].result里的value字段按type区分是 Span 还是 Relation 还是 Choices。如果导出时没勾选「包含标注结果」annotations会是空数组转换出来就是空数据集。这个坑我踩过一次标了一下午导出发现没勾选项血泪经验。5. 数据转换脚本label_studio.py 的参数拆解与避坑5.1 抽取式任务的转换命令抽取式任务命名实体识别、关系抽取、事件抽取的转换命令python label_studio.py \ --label_studio_file ./data/label_studio.json \ --save_dir ./data \ --splits 0.8 0.1 0.1 \ --task_type ext--label_studio_file指向导出的 JSON--save_dir是转换后 train/dev/test 的保存目录--splits按 8:1:1 划分--task_type ext表示抽取式任务。执行完会在./data下生成train.json、dev.json、test.json格式是 UIE 需要的{text, prompt, result_list}结构。这里有个默认行为要注意每次执行脚本会覆盖同名文件。如果你调了参数想对比效果先把上一次的输出重命名或挪走否则后悔药没得吃。5.2 句子级分类任务的 prompt 构造句子级分类的转换命令多了--prompt_prefix和--optionspython label_studio.py \ --label_studio_file ./data/label_studio.json \ --task_type cls \ --save_dir ./data \ --splits 0.8 0.1 0.1 \ --prompt_prefix 情感倾向 \ --options 正向 负向转换脚本会自动构造 prompt比如「情感倾向[正向,负向]」。--prompt_prefix是 prompt 的前缀文本--options是分类标签列表。这两个参数只对task_type cls有效抽取式任务传了也会被忽略。5.3 实体/评价维度分类的 separator 参数实体/评价维度分类的转换命令python label_studio.py \ --label_studio_file ./data/label_studio.json \ --task_type ext \ --save_dir ./data \ --splits 0.8 0.1 0.1 \ --prompt_prefix 情感倾向 \ --options 正向 负向 \ --separator ##--separator是实体类别和分类标签之间的分隔符默认##。比如评价维度是「屏幕」分类是「正向」构造出来的 prompt 可能是「屏幕##情感倾向[正向,负向]」。这个分隔符要和 schema 里的嵌套结构对应改错了模型学到的 prompt 模式就乱了。5.4 完整参数表与默认值手册 2.7 节列了所有参数我整理成表方便对照参数作用默认值生效范围label_studio_file导出的标注 JSON 路径无必填全部save_dir训练数据保存目录./data全部negative_ratio最大负例比例5仅抽取任务splits训练/验证/测试比例[0.8, 0.1, 0.1]全部task_type任务类型 ext/cls无必填全部options分类类别标签[正向, 负向]仅分类任务prompt_prefix分类 prompt 前缀情感倾向仅分类任务is_shuffle是否随机打散True全部seed随机种子1000全部schema_langschema 语言 ch/ench全部separator实体与分类的分隔符##实体/评价维度分类negative_ratio只对训练集有效验证集和测试集默认构造全负例这是为了保证评估指标准确。负例数量 negative_ratio × 正例数量适当构造负例能提升模型区分能力但设太大也会让训练集正负失衡。6. 避坑与排查标注到训练链路上的五个真实翻车点6.1 导出 JSON 后转换报 KeyError: annotations现象运行label_studio.py直接抛KeyError: annotations。 原因导出时没勾选「包含标注结果」或者导出格式选成了 CSV/TSV 而不是 JSON。 解决重新导出确认文件类型是 JSON且每条 task 的annotations数组非空。可以先用python -c import json; djson.load(open(label_studio.json)); print(len(d[0][annotations]))快速检查。6.2 转换后 train.json 为空文件现象脚本跑完没报错但train.json只有几行或直接是空数组。 原因schema 里的标签名和 Label Studio XML 里定义的value不一致转换脚本匹配不到任何标注。 解决把 XML 里的Label value...和转换时用的 schema 列表逐字对照注意中英文标点和空格。比如 XML 里写「赛事名称」schema 里写成「赛事 名称」就匹配不上。6.3 关系抽取的 P 值拗口导致零样本效果差现象模型在没见过的关系类型上几乎抽不出正确三元组。 原因P 类型设置不符合「{S}的{P}为{O}」的自然语义prompt 构造出来模型理解不了。 解决按手册原则逐个念一遍把「父子」改成「孩子」、「所属」改成「所属专辑」这类更自然的表达。P 值越接近日常语言零样本迁移越好。6.4 每次执行转换脚本覆盖已有数据现象调完参数重新跑发现之前的 train.json 被覆盖没法对比。 原因脚本默认按save_dir和固定文件名输出同名直接覆盖。 解决每次转换前把save_dir改成不同目录比如./data_v1、./data_v2或者转换后立刻重命名。我一般会在命令后面加 mv ./data/train.json ./data/train_$(date %s).json做备份。6.5 句子级分类选了 Relation Extraction 模板现象建项目时选了 Relation Extraction进标注界面发现没有分类按钮。 原因手册原文在句子级分类处写的是 Relation Extraction这是笔误实际应选 Text Classification。 解决删掉项目重建模板选 Text Classification标签用 Choices。已经标了的数据可以在导出后手动改 JSON 结构但不如重建省事。7. 进阶技巧用 negative_ratio 和 schema_lang 把数据质量再提一档数据转换跑通只是及格线真正拉开差距的是负例构造和 schema 语言选择。negative_ratio默认 5意思是训练集里自动构造的负例数量是正例的 5 倍。这个值不是越大越好——我试过设到 20模型在验证集上的 F1 反而掉了 3 个点因为负例太多导致正例信号被淹没。比较稳的做法是从 5 开始按 5、8、10 三档做消融看验证集指标拐点。负例的构造逻辑是对于每个正例 prompt随机替换实体或分类标签生成不匹配的样本。比如正例是「张三的出生地为北京」负例会构造成「张三的出生地为上海」或「张三的出生地为歌手」。验证集和测试集默认全负例所以评估指标反映的是模型在「正例极少、负例极多」场景下的判别能力这和真实抽取场景更接近。schema_lang控制 prompt 的构造语言默认ch。如果你的训练数据以英文为主或者下游模型是在英文语料上预训练的改成en会让 prompt 变成英文模板比如「sentiment[positive,negative]」。这个参数影响的是 prompt 文本本身不改变标注数据。我一般中文数据保持ch中英混合数据先试ch如果英文样本的抽取效果明显差一截再切en对比。还有一个容易被忽略的点is_shuffle和seed。默认is_shuffleTrue、seed1000意味着每次转换的数据顺序是固定的。如果你改了splits比例想重新划分但没改seed划分结果可能和上次高度重叠导致验证集泄漏。我的习惯是每次调整划分比例时把seed也换一个值比如--seed 2024确保划分真正独立。最后说一个验证转换结果是否正确的笨办法打开生成的train.json随便抽三条把prompt和result_list字段读一遍看 prompt 是不是符合「{S}的{P}为{O}」或「XXX的情感倾向[正向,负向]」的格式result_list 里的实体位置能不能和原文对上。这个动作花两分钟能挡住后面训练时大半的数据格式问题。从那以后我每次转换完都强制走一遍这个抽查再也没出现过训练到一半发现标签错位的情况。希望帮到你。本文还有配套的精品资源点击获取