- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
本文围绕 PaddleNLP 仓库中的词法分析示例(slm/examples/lexical_analysis/README.md),系统讲解基于「双层双向 GRU + CRF」序列标注模型的完整工作流:数据组织与 IOB2 标注体系、单卡/多卡训练、模型评估、静态图导出与部署预测,以及通过 Taskflow 一键调用 LAC 分词任务。读完本文,你可以从零复现 LAC 类词法分析模型的训练与部署,理解每个命令行参数背后的源码实现。
1. 任务与模型结构
词法分析任务的输入是一个字符串(文档中称为「句子」),输出是句子中的词边界和词性、实体类别。序列标注是词法分析的经典建模方式,该示例使用基于 GRU 的网络结构学习特征,并将学习到的特征接入 CRF 解码层完成序列标注。模型由四部分构成:
- 输入层:采用 one-hot 方式表示,每个字以一个 id 表示;
- Embedding 层:one-hot 序列通过字表转换为实向量表示的字向量序列;
- 特征层:字向量序列作为两层堆叠的双向 GRU的输入,学习输入序列的特征表示(堆叠两层以增加学习能力);
- 解码层: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_len | 64 | 单句最长字数,超长截断 |
base_lr | 0.001 | 影响整个网络的基础学习率(Adam 优化器) |
crf_lr | 0.2 | CRF 层学习率缩放系数,透传给BiGruCrf |
emb_dim | 128 | 字嵌入维度 |
hidden_size | 128 | GRU 隐层节点数 |
logging_steps | 10 | 每 X 个 step 打印一次训练日志 |
save_steps | 100 | 每 X 个 step 保存一次 checkpoint |
do_eval | True | 保存 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_tinydeploy/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.py | train.tsv/test.tsv+ 三个词表 | save_dir/model_*.pdparams、best_model.pdparams |
| 评估 | eval.py | test.tsv+ checkpoint | precision/recall/F1 |
| 动态图预测 | predict.py | infer.tsv+ checkpoint | results.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.
相关推荐
PaddleFormers 词法分析实战指南:基于 PaddleHub 的 LAC 与 jieba_paddle 中文分词、词性标注与专名识别
PaddleFormers 词法分析实战指南:基于 PaddleHub 的 LAC 与 jieba_paddle 中文分词、词性标注与专名识别 导读 中文自然语
人工智能大模型微调模型推理服务PaddleNLP RoFormerTokenizer 源码级解析:基于 jieba 预分词与 WordPiece 的中文分词实践
PaddleNLP RoFormerTokenizer 源码级解析:基于 jieba 预分词与 WordPiece 的中文分词实践 导读 RoFormer(Ro
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPPaddleNLP RoFormerv2Tokenizer 深度解析:基于 WordPiece 的中文预训练分词器使用与实现
PaddleNLP RoFormerv2Tokenizer 深度解析:基于 WordPiece 的中文预训练分词器使用与实现 RoFormerv2(RoForm
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考