📝 数据标注 · NLP 核心

文本序列标注 · 让 AI 读懂每一个词

在文本中标记出具有特定意义的片段——人名、地名、组织、时间、关系……是 NLP 项目中 NER、关系抽取、信息抽取等任务的基石。比图像标注更快但更需要语言专业能力。

① 什么是文本序列标注?

🏷️

定义

对文本中的每个词/字标记一个标签——可以是实体类型、词性、语义角色,或实体间的关系

💼

应用场景

搜索引擎(关键词提取)、知识图谱(实体+关系)、客服机器人、法律医疗文档结构化等

⚡

难度

★★★☆☆ 中等,需要语言专业能力,中文分词是额外门槛。LLM 预标注已大幅提效

💡 文本序列标注 vs 图像标注的核心区别:图像标注画连续的框/轮廓,文本标注标记离散的词/字。一个连续空间,一个离散序列——思维方式完全不同。

② 三种核心标注类型

1

NER 命名实体识别

在文本中找出具有特定意义的实体——人名、地名、组织名、时间、金额、产品名……是 NLP 中最基础也最常用的序列标注任务。

2024年 B-DATE I-DATE 1月 B-DATE , B-ORG I-ORG I-ORG 在 B-LOC I-LOC I-LOC 举办了 B-PER 与 B-PER 的 B-PROD I-PROD 发布会 , B-MONEY 元
原句:2024年1月,华为技术有限公司在广东省深圳市举办了余承东与李小龙的Mate 60 Pro发布会,定价6999元
PER 人名 ORG 组织 LOC 地点 DATE 时间 MONEY 金额 PROD 产品
BIO 标签体系(NER 标准格式):
B-XXX:实体开始(Begin)
I-XXX:实体内部(Inside)
O:非实体(Outside)
→ 中文常按字标注,英文常按词标注;BIOES 更精细(End/Single)
✅ 典型任务
通用 NER(人名/地名/组织)、金融实体(股票/债券/金额)、医疗实体(疾病/药物/体征)、法律实体(法条/案件/当事人)
⚠️ 标注难点
分词边界争议("复旦大学"是一个 ORG 还是"复旦"ORG+"大学"ORG?);嵌套实体("北京大学中文系"里"北京大学"是 ORG,"中文系"是?);缩写歧义("苹果"是公司还是水果?)
2

POS 词性标注

为文本中每个词标注词性——名词、动词、形容词、介词、代词……是句法分析、依存句法、机器翻译等下游任务的基础。

词词性词词性词词性
华为n 名词 技术n 名词 有限公司n 名词
在p 介词 深圳n 名词 发布v 动词
了d 副词 新款a 形容词 手机n 名词
,wp 标点 它r 代词 很d 副词
受欢迎a 形容词 。wp 标点
常见词性标签集:
n 名词(含人名/地名)
v 动词
a 形容词
p 介词(在/从/到)
d 副词(很/非常/不)
r 代词(我/你/它)
w 助词(的/了/吗)
c 连词(和/但/如果)
中文常用 ICTCLAS 87 标签集 / 英文常用 Penn Treebank 45 标签集
✅ 典型应用
句法分析前置、依存句法、机器翻译(标注词性选译)、信息抽取(只抽名词)、文本分类(名词特征)
⚠️ 标注难点
兼类词("学习"是名词还是动词?"在"是介词还是副词?);分词边界("打羽毛球"是一个动词短语还是分开?);口语化文本标注更难
3

关系抽取 Relation Extraction

从文本中找出实体对及其之间的语义关系——A 与 B 是什么关系?是知识图谱的核心构建单元。比 NER 更复杂,需要同时标注两个实体 + 它们之间的关系类型。

原句: 阿里巴巴集团 的 马云 出生于 浙江省杭州市 , 阿里巴巴集团 总部位于 杭州市余杭区
阿里巴巴集团 → 创始人 → 马云
马云 → 出生地 → 浙江省杭州市
阿里巴巴集团 → 总部位于 → 杭州市余杭区
关系抽取标注的两种范式:
流水线式:先做 NER 找出所有实体 → 再两两配对标注关系(简单但错误传播)
联合标注:同时标注实体 + 关系(标注复杂但质量更高,是主流)
关系类型:可能任职于、创始人、位于、出生于、投资、合作、父子……通常 20-100 种
✅ 典型应用
知识图谱构建(实体+关系三元组)、问答系统("马云在哪出生?"→ 查关系)、金融风控(股权穿透)、社交网络(人际关系)
⚠️ 标注难点
关系边界模糊("合作"与"合资"是同一关系吗?);多实体多关系(一句话 10 个实体,两两配对 45 组);跨句关系(A 在第一句,B 在第三句);隐性关系(需要推理才能得出)

③ 文本序列标注工作全流程

1

定义标签体系 & 边界

确定要标注哪些实体类型/词性/关系,每个标签的精确定义,产出 ner_guide.md

# NER 标签体系示例(中文)
PER: 人名 → 张三、李白、习近平
ORG: 组织 → 华为技术有限公司、北京大学
LOC: 地点 → 广东省深圳市、西湖
规则: "北京大学"整体 ORG,括号不包含实体,歧义按语境
2

准备语料 & 标注工具

文本标注对工具要求比图像低——纯文本即可,但 NLP 专用工具有分词辅助和标签快捷键

📄 语料准备
文本格式 & 预处理
纯文本 .txt / JSONL / CSV/Excel
🛠️ 标注工具 (NLP 专用)
📖 学术经典
brat
🌐 现代化
Doccano
🔬 Prodigy
Prodigy · Label Studio
3

LLM 预标注(NLP 的"SAM")

LLM(GPT/LLaMA)做 NER 预标注是文本标注的革命性提效——直接让大模型先跑一遍,人工只修正

纯手动:准确,慢 LLM 预标 + 人工修正:主流方案 Prodigy 主动学习:智能选样本
⚡ LLM 预标注 Prompt 示例(NER):
请对以下文本进行 NER 标注,标签为 [PER, ORG, LOC, DATE],输出 JSON 格式:
{"text": "2024年1月华为在深圳举办发布会", "entities": [{"text": "华为", "label": "ORG"}, ...]}
4

质量控制(QC)

1 实体级 F1 2 Kappa 系数 3 BIO 格式检查
常见 BIO 格式错误(脚本可自动检出):
❌ O I-PER O (I-PER 前不是 B-PER)→ 应 B-PER I-PER O
❌ B-PER B-PER (连续两个 B-PER 无间隔)→ 应 B-PER I-PER
✅ O B-PER I-PER O 正确格式
5

导出 & 训练对接

文本标注的数据量远小于图像——一段文本就是一行 BIO 标签,导出格式选择灵活

🔍 数据检查
实体分布/类别平衡/长尾检查
💾 格式转换
→ 工具原始格式 → 统一解析 → 模型训练格式

④ 常用 NLP 标注工具对比

工具 类型 费用 NER/POS 亮点
brat ▼ Web 本地 免费 ✅ 强 学术经典,快捷键标注极快
Doccano ▼ Web 服务 免费 ✅ 强 现代化 UI,项目管理完善
Prodigy ▼ 命令行+Web 免费+付费 ✅ 极强 主动学习,标注效率最高
Label Studio ▼ Web 服务 免费 ✅ 强 多模态,Python SDK 强

⑤ 三种数据格式对比

🏷️

BIO/BIOES

每词一个标签
# conll.txt
华为 B-ORG
技术 I-ORG
有限公司 I-ORG
在 O
深圳 B-LOC
发布 O
B-XXX 实体开始
I-XXX 实体内部
O 非实体
→ NER/POS 标准格式
📋

JSONL

每行一个 JSON
// data.jsonl
{"text": "华为在深圳...",
 "entities": [
  {"start":0, "end":4,
   "label":"ORG"},
  {"start":5, "end":7,
   "label":"LOC"}
 ]}
start/end:字符级偏移
label:实体类型
→ 现代 NLP 主流格式
🐍

spaCy Doc

二进制序列化
# 保存
from spacy.tokens import DocBin
db = DocBin(store_user_data=True)
db.add(doc)
db.to_disk("train.spacy")
# 训练时直接读
DocBin:批量保存多个 Doc
二进制:高效读写
→ spaCy 训练首选
💡 格式转换速查: JSONL → BIO · BIO → JSONL · JSONL → spaCy Doc

⑥ 实战:JSONL → BIO → 训练 BERT-NER

# === 1. JSONL → BIO 转换 ===
import json

def jsonl_to_bio(jsonl_path, bio_path):
    with open(jsonl_path) as fin, open(bio_path, 'w') as fout:
        for line in fin:
            item = json.loads(line)
            text = item['text']
            entities = item.get('entities', [])

            # 给每个字符预设 O
            labels = ['O'] * len(text)

            for ent in entities:
                start, end, label = ent['start'], ent['end'], ent['label']
                labels[start] = f'B-{label}'
                for i in range(start+1, end):
                    labels[i] = f'I-{label}'

            # 逐字输出
            for char, label in zip(text, labels):
                if char != '\n':
                    fout.write(f'{char}\t{label}\n')
            fout.write('\n')  # 句间空行

jsonl_to_bio('train.jsonl', 'train.bio')

# === 2. 检查 BIO 格式 ===
def check_bio_format(bio_path):
    errors = []
    prev_tag = 'O'
    for line in open(bio_path):
        line = line.strip()
        if not line:
            prev_tag = 'O'; continue
        char, tag = line.split('\t')
        if tag.startswith('I-') and prev_tag == 'O':
            errors.append(f'❌ I-{tag[2:]} 前是 O')
        prev_tag = tag
    return errors

# === 3. 训练 BERT-NER(HuggingFace) ===
from transformers import AutoTokenizer, AutoModelForTokenClassification
from transformers import Trainer, TrainingArguments

model_name = 'bert-base-chinese'
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 标签映射
label_list = ['O', 'B-PER', 'I-PER',
              'B-ORG', 'I-ORG',
              'B-LOC', 'I-LOC']
id2label = {i: l for i, l in enumerate(label_list)}
label2id = {l: i for i, l in id2label.items()}

model = AutoModelForTokenClassification.from_pretrained(
    model_name, num_labels=len(label_list),
    id2label=id2label, label2id=label2id
)

# 训练...
training_args = TrainingArguments(
    output_dir='./ner_model',
    num_train_epochs=10, per_device_train_batch_size=16
)
trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset)
trainer.train()