☰
PaddleNLP 词法分析实战:基于 GRU-CRF 的中文分词与词性标注完整训练流程
2026/9/25 17:43:17 网站建设 项目流程
  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

本文围绕 PaddleNLP 仓库中的词法分析示例(slm/examples/lexical_analysis/README.md),系统讲解基于「双层双向 GRU + CRF」序列标注模型的完整工作流:数据组织与 IOB2 标注体系、单卡/多卡训练、模型评估、静态图导出与部署预测,以及通过 Taskflow 一键调用 LAC 分词任务。读完本文,你可以从零复现 LAC 类词法分析模型的训练与部署,理解每个命令行参数背后的源码实现。

1. 任务与模型结构

词法分析任务的输入是一个字符串(文档中称为「句子」),输出是句子中的词边界和词性、实体类别。序列标注是词法分析的经典建模方式,该示例使用基于 GRU 的网络结构学习特征,并将学习到的特征接入 CRF 解码层完成序列标注。模型由四部分构成:

  1. 输入层:采用 one-hot 方式表示,每个字以一个 id 表示;
  2. Embedding 层:one-hot 序列通过字表转换为实向量表示的字向量序列;
  3. 特征层:字向量序列作为两层堆叠的双向 GRU的输入,学习输入序列的特征表示(堆叠两层以增加学习能力);
  4. 解码层:CRF 以 GRU 学习到的特征为输入,以标记序列为监督信号,实现序列标注。

对应到源码,整个网络在 model.py 中由BiGruCrf类实现,与上述四个步骤一一对应:

class BiGruCrf(nn.Layer): def __init__(self, word_emb_dim, hidden_size, vocab_size, num_labels, emb_lr=2.0, crf_lr=0.2, with_start_stop_tag=True): # one-hot id -> 实向量 self.word_embedding = nn.Embedding( num_embeddings=self.vocab_size, embedding_dim=self.word_emb_dim, ...) # 两层双向 GRU self.gru = nn.GRU( input_size=self.word_emb_dim, hidden_size=self.hidden_size, num_layers=2, direction="bidirectional", ...) # 发射分数 self.fc = nn.Linear(in_features=self.hidden_size * 2, out_features=self.num_labels + 2 if with_start_stop_tag else self.num_labels, ...) # CRF 解码 self.crf = LinearChainCrf(self.num_labels, self.crf_lr, with_start_stop_tag) self.crf_loss = LinearChainCrfLoss(self.crf) self.viterbi_decoder = ViterbiDecoder(self.crf.transitions, with_start_stop_tag)

forward的行为取决于是否有标签:训练时传入labels,经crf_loss计算负对数似然损失;推理时无标签,则走viterbi_decoder输出最优标签序列。从源码结构看,这里体现了「同一网络、两种前向路径」的典型序列标注写法——CRF 训练时用全局损失监督,解码时用 Viterbi 算法保证全局最优路径。另外两个设计细节值得注意:

  • 分组学习率:Embedding 层的学习率缩放为emb_lr=2.0,CRF 转移层为crf_lr=0.2,网络其余部分使用基础学习率。这源于 LAC 原始设计思路:embedding 参数多、需要学得快,CRF 转移矩阵只依赖标签组合、收敛快,用较小学习率避免振荡。
  • 参数初始化与正则:所有权重采用Uniform(-0.1, 0.1)初始化,并对 GRU/线性层权重加L2Decay(coeff=1e-4)正则。

CRF 相关组件来自 paddlenlp/layers/crf.py,ViterbiDecoder优先从paddle.text导入,不存在时回退到 PaddleNLP 自带实现(见 model.py)。

2. 数据准备

2.1 下载示例数据集

仓库提供了少数样本用于示例输入数据格式,执行以下命令下载并解压示例数据集(lexical_analysis_dataset_tiny):

python download.py --data_dir ./

其实现见 download.py,通过paddle.utils.download.get_path_from_url从百度 BOS 下载并解压数据集压缩包。

2.2 数据格式

训练数据需要用户按应用场景自行组织。格式规定如下:

  • 文件第一行是固定的表头:text_a\tlabel;
  • 之后每行数据由两列组成,以制表符(\t)分隔:第一列是 utf-8 编码的中文文本,字与字之间用\002分隔;第二列是每个字对应的标注,同样以\002分隔;
  • 采用IOB2 标注体系:X-B表示类型为 X 的词的开始,X-I表示类型为 X 的词的持续,O表示不关注的字(在词性、专名联合标注中实际上不存在 O)。

示例(摘自原文档):

除\002了\002他\002续\002任\002十\002二\002届\002政\002协\002委\002员\002,\002马\002化\002腾\002,\002雷\002军\002,\002李\002彦\002宏\002也\002被\002推\002选\002为\002新\002一\002届\002全\002国\002人\002大\002代\002表\002或\002全\002国\002政\002协\002委\002员 p-B\002p-I\002r-B\002v-B\002v-I\002m-B\002m-I\002m-I\002ORG-B\002ORG-I\002n-B\002n-I\002w-B\002PER-B\002PER-I\002PER-I\002w-B\002PER-B\002PER-I\002w-B\002PER-B\002PER-I\002PER-I\002d-B\002p-B\002v-B\002v-I\002v-B\002a-B\002m-B\002m-I\002ORG-B\002ORG-I\002ORG-I\002ORG-I\002n-B\002n-I\002c-B\002n-B\002n-I\002ORG-B\002ORG-I\002n-B\002n-I

解析逻辑在 data.py 的load_dataset中:逐行读取后用\t切分两列,再用CHAR_DELIMITER = "\002"切分字与标签,并通过assert len(words) == len(labels)校验字与标签是否一一对应。文件名中含infer的文件会被当作无标签推理数据,只取文本列。

2.3 标签体系

标签集合包含24 个词性标签(小写字母)和4 个专名类别标签(大写字母),完整定义如下:

标签含义标签含义标签含义标签含义
n普通名词f方位名词s处所名词t时间
nr人名ns地名nt机构名nw作品名
nz其他专名v普通动词vd动副词vn名动词
a形容词ad副形词an名形词d副词
m数量词q量词r代词p介词
c连词u助词xc其他虚词w标点符号
PER人名LOC地名ORG机构名TIME时间

需要特别注意的是:人名、地名、机构名和时间四个类别存在两套标签(PER / LOC / ORG / TIME 与 nr / ns / nt / t)。被标注为第二套小写标签的词,是模型判断为低置信度的人名、地名、机构名和时间词。开发者可以基于这两套标签,在四个类别的准确率与召回率之间做出自己的权衡——例如严格场景下只信任大写专名标签,高召回场景下合并两套标签。

2.4 词表文件

数据集目录中除train.tsv、test.tsv、infer.tsv外,还包含三个词表文件,在 train.py 中被加载:

  • word.dic:字词表,OOV 字统一映射到OOV词元;
  • tag.dic:标签表,OOV 标签回退为O(见 data.py 中convert_tokens_to_ids的oov_replace_token参数);
  • q2b.dic:全角转半角(DBC→SBC)归一化词表,训练、评估、预测前都会先经过normalize_token做文本归一化。

convert_example还会将序列截断到max_seq_len(默认 64 字),并输出(token_ids, length, label_ids)三元组,由Pad/Stack/Tuple完成批组装:word_ids按[PAD]索引填充、length堆叠、label_ids按O标签索引填充(见 train.py)。

3. 模型训练

3.1 单卡训练

python train.py \ --data_dir ./lexical_analysis_dataset_tiny \ --model_save_dir ./save_dir \ --epochs 10 \ --batch_size 32 \ --device gpu \ # --init_checkpoint ./save_dir/final

文档说明的六个核心参数如下:

参数释义
data_dir数据集所在文件夹路径
model_save_dir训练期间模型保存路径
epochs模型训练迭代轮数
batch_size每次迭代每张卡上的样本数目
device训练使用的设备,gpu表示使用 GPU,cpu表示使用 CPU(文档中另提及xpu表示百度昆仑卡,见 3.3 节关于当前源码取值范围的说明)
init_checkpoint模型加载路径,通过设置它可以启动增量训练

3.2 源码中的完整参数表

train.py 的argparse定义中还有一组文档未逐一展开、但对复现实验很关键的模型超参与训练控制参数:

参数默认值释义
max_seq_len64单句最长字数,超长截断
base_lr0.001影响整个网络的基础学习率(Adam 优化器)
crf_lr0.2CRF 层学习率缩放系数,透传给BiGruCrf
emb_dim128字嵌入维度
hidden_size128GRU 隐层节点数
logging_steps10每 X 个 step 打印一次训练日志
save_steps100每 X 个 step 保存一次 checkpoint
do_evalTrue保存 checkpoint 时是否顺带评估

3.3 训练循环的源码细节

从 train.py 的主循环可以看到几处实操要点:

  • checkpoint 命名:每save_steps步保存为model_<global_step>.pdparams,这也是评估/导出命令中model_100.pdparams这类路径的由来;
  • best 模型自动选择:每次保存 checkpoint 后若do_eval=True,会立即在测试集上跑evaluate,F1 高于历史最佳时额外保存一份best_model.pdparams;
  • 增量训练:--init_checkpoint指向的目录若存在,paddle.load后model.load_dict加载参数,实现断点续训;
  • 指标计算:使用paddlenlp.metrics.ChunkEvaluator(label_list=label_vocab.keys(), suffix=True),以「词」为最小评测单位计算 precision / recall / F1——suffix=True即 IOB2 的 B/I 后缀约定。
  • 设备取值范围:当前 train.py 中--device的choices为["cpu", "gpu"];README 中提及的xpu属于文档层面的说明,直接传xpu会被参数校验拒绝,跨平台部署时需注意这一前提。
  • 多卡:训练时通过paddle.distributed.get_world_size()判断卡数,多于 1 卡则init_parallel_env,并使用DistributedBatchSampler保证各卡数据不重叠。

3.4 多卡训练

python -m paddle.distributed.launch --gpus "0,1" train.py \ --data_dir ./lexical_analysis_dataset_tiny \ --model_save_dir ./save_dir \ --epochs 10 \ --batch_size 32 \ --device gpu \ # --init_checkpoint ./save_dir/final

多卡模式下batch_size表示每张卡的批大小,全局批大小 = batch_size × 卡数,调参时需注意等效学习率的影响。

4. 模型评估

加载训练保存的参数文件,在测试集上验证:

python eval.py --data_dir ./lexical_analysis_dataset_tiny \ --init_checkpoint ./save_dir/model_100.pdparams \ --batch_size 32 \ --device gpu

其中./save_dir/model_100.pdparams是训练过程中保存的参数文件,请更换为实际得到的训练保存路径(也可使用自动保存的best_model.pdparams)。

eval.py 的流程与训练中的evaluate一致:重新构建BiGruCrf网络(emb_dim、hidden_size默认各 128,必须与训练一致)→paddle.load加载权重 → 遍历测试集,每批调用model(token_ids, length)得到 Viterbi 解码标签 → 用ChunkEvaluator累加后输出:

eval precision: %f, recall: %f, f1: %f

注意评估、预测、导出脚本各自独立解析参数,因此--emb_dim、--hidden_size、--max_seq_len的默认值必须与训练时保持对齐,否则网络结构与权重形状不匹配。

5. 模型导出

动态图训练结束之后,可以将动态图参数导出为静态图参数,具体代码见 export_model.py。静态图参数保存在output_path指定路径中,运行方式:

python export_model.py --data_dir=./lexical_analysis_dataset_tiny \ --params_path=./save_dir/model_100.pdparams \ --output_path=./infer_model/static_graph_params

参数说明:

  • params_path:动态图训练保存的参数路径;
  • output_path:静态图参数导出路径。

从源码看,导出过程是标准的paddle.jit动态转静态流程(见 export_model.py):

model = paddle.jit.to_static( model, input_spec=[ InputSpec(shape=[None, None], dtype="int64", name="token_ids"), InputSpec(shape=[None], dtype="int64", name="length"), ], ) paddle.jit.save(model, args.output_path)

两个输入token_ids([None, None],动态 batch 与动态句长)和length([None])的名称与形状会在推理侧被固定复用,这正是部署脚本按token_ids/length两个 input handle 喂数的依据。注意导出时模型以无标签模式运行,静态图中固化的是 Viterbi 解码路径。

6. 静态图部署预测

导出模型后用于部署,deploy/predict.py 提供了基于paddle.inference的 Python 部署预测示例。运行方式分两种情况:

开启 PIR(PaddlePaddle 3.0.0 默认):

python deploy/predict.py --model_file=infer_model/static_graph_params.json \ --params_file=infer_model/static_graph_params.pdiparams \ --data_dir lexical_analysis_dataset_tiny

未开启 PIR:

python deploy/predict.py --model_file=infer_model/static_graph_params.pdmodel \ --params_file=infer_model/static_graph_params.pdiparams \ --data_dir lexical_analysis_dataset_tiny

deploy/predict.py中Predictor类的关键实现值得部署时参考:

  • 通过paddle.inference.Config(model_file, params_file)创建配置,GPU 场景调用config.enable_use_gpu(100, 0),CPU 场景调用config.disable_gpu()(可再按需开启 MKL-DNN、设置线程数);
  • switch_use_feed_fetch_ops(False)关闭默认 feed/fetch 算子,改用get_input_handle/get_output_handle直接拷贝数据:copy_from_cpu输入token_ids与length,predictor.run()后从输出 handle 取出标签;
  • 预处理与训练侧完全一致:convert_example中同样执行全角转半角归一化、OOV 替换、max_seq_len截断,再经parse_result把字级 B/I 标签解析成词与词性对,最终以Text: ... / Result: [(词, 词性), ...]的形式打印,并在结尾统计总预测耗时(脚本内置--epochs参数用于多轮 benchmark)。

7. 动态图模型预测

对无标签数据可以直接用动态图权重预测,不需要先导出:

python predict.py --data_dir ./lexical_analysis_dataset_tiny \ --init_checkpoint ./save_dir/model_100.pdparams \ --batch_size 32 \ --device gpu

得到类似以下输出(完整结果写入当前目录results.txt):

(大学, n)(学籍, n)(证明, n)(怎么, r)(开, v) (电车, n)(的, u)(英文, nz) (什么, r)(是, v)(司法, n)(鉴定人, vn)

predict.py 读取的是data_dir下的infer.tsv(无标签文件,data.py 中load_dataset对infer文件只取文本列并跳过首行表头)。输出格式的拼接逻辑在parse_result中:遍历字级标签,遇到-B后缀(或前一非 O 标签后出现 O)即把累积的部分词落盘,取tag.split("-")[0]得到词性,最终zip(sent, tags)组成(词, 词性)序列。这套解析逻辑在动态图预测与静态图部署预测中是共用的。

8. Taskflow 一键预测

如果不想自己走完整训练部署流程,可以直接使用 PaddleNLP 提供的 Taskflow 工具对输入文本进行一键分词:

from paddlenlp import Taskflow lac = Taskflow("lexical_analysis") lac("LAC是个优秀的分词工具") ''' [{'text': 'LAC是个优秀的分词工具', 'segs': ['LAC', '是', '个', '优秀', '的', '分词', '工具'], 'tags': ['nz', 'v', 'q', 'a', 'u', 'n', 'n']}] ''' lac(["LAC是个优秀的分词工具", "三亚是一个美丽的城市"]) ''' [{'text': 'LAC是个优秀的分词工具', 'segs': ['LAC', '是', '个', '优秀', '的', '分词', '工具'], 'tags': ['nz', 'v', 'q', 'a', 'u', 'n', 'n']}, {'text': '三亚是一个美丽的城市', 'segs': ['三亚', '是', '一个', '美丽', '的', '城市'], 'tags': ['LOC', 'v', 'm', 'a', 'u', 'n']}] '''

任务的默认路径为$HOME/.paddlenlp/taskflow/lexical_analysis/lac/,默认路径下包含执行该任务需要的所有文件。从源码 paddlenlp/taskflow/lexical_analysis.py 看,LacTask声明了四个资源文件——model_state.pdparams、tag.dic、q2b.dic、word.dic,首次使用时按内置 URL 与 MD5 校验下载到默认路径,这与第 2.4 节训练数据目录中的词表文件一一对应,说明 Taskflow 内置模型与本训练流程属于同一模型体系。

如果希望得到定制化的分词及标注结果,也可以通过 Taskflow 加载自定义的词法分析模型。通过task_path(model_path)指定用户自定义路径,自定义路径下的文件需要和默认路径的文件一致:

custom_task_path/ ├── model.pdparams ├── word.dic ├── tag.dic └── q2b.dic

加载自定义模型进行一键预测:

from paddlenlp import Taskflow my_lac = Taskflow("lexical_analysis", model_path="./custom_task_path/")

此外,LacTask还支持传入user_dict用户自定义词典(见 lexical_analysis.py),用于业务领域专有词的强制干预,适合行业语料上的定制化部署。更多使用方法请参考 Taskflow 文档。

9. 预训练模型

如果希望直接使用预训练好的 LAC 模型完成词法分析任务,而不用从示例数据训练,可参考仓库指向的 LAC 官方模型与 PaddleHub 分词模型下载渠道(README 中给出外链,以官方渠道发布的权重与词表为准)。使用预训练模型时,word.dic、tag.dic、q2b.dic必须与模型配套版本一致——因为 embedding 维度、标签数量直接决定了网络形状,词表不匹配会在paddle.load/load_dict阶段报错。

10. 小结:从示例脚本到生产部署的路径

阶段入口文件输入输出
数据下载download.py—lexical_analysis_dataset_tiny/
训练train.pytrain.tsv/test.tsv+ 三个词表save_dir/model_*.pdparams、best_model.pdparams
评估eval.pytest.tsv+ checkpointprecision/recall/F1
动态图预测predict.pyinfer.tsv+ checkpointresults.txt、(词, 词性)序列
静态图导出export_model.py动态图参数static_graph_params.*
部署预测deploy/predict.py静态图模型低延迟推理结果
一键调用Taskflow("lexical_analysis")文本text/segs/tags结构

整体来看,这套示例完整覆盖了「数据 → 训练 → 评估 → 导出 → 部署」的典型 Paddle 序列标注工程链路:核心模型只有百余行(model.py),但围绕它的参数分组学习率、IOB2 标签解析(parse_result)、全角半角归一化、Chunk 级指标等细节,都是复现和迁移到其他序列标注任务(NER、POS 等)时可以直接借鉴的工程实践。

  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

相关推荐

上一篇:LLM-as-a-Judge 智能体的 Web Search 工具设计指南:从 Zod 模式契约到研究管线落地(Agent-Skills-for-Context-Engineering 实战)
下一篇:DB-GPT 快速上手指南:从克隆仓库到完成首次对话的 5 步最小配置方案

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询