- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
RemBERT(Rethinking embedding coupling in pre-trained language models)是 Google Research 提出的一类基于"解耦词嵌入"思想的多语言预训练模型,其论文发表于 arXiv 2010.12821。本文以 RemBERT 使用文档 为主体,结合 PaddleNLP 仓库中 RemBERT 的模型实现(modeling.py)、配置(configuration.py)、分词器(tokenizer.py)以及 XTREME 微调示例(slm/model_zoo/rembert),系统讲解 RemBERT 的核心设计动机,并给出在 XTREME-XNLI 与 XTREME-PAWS-X 两个多语言下游任务上完整可复现的训练、评估流程。读完本文,你将掌握:RemBERT 架构与标准 BERT 式模型的差异、PaddleNLP 中 RemBERT 的源码级结构,以及如何用一条命令完成多语言文本分类模型的微调与精度复现。
RemBERT 核心思想:解耦词嵌入带来的参数效率
标准预训练语言模型通常让输入词嵌入与输出词嵌入共享同一份参数(即"词嵌入耦合")。RemBERT 的作者指出,分离词嵌入为语言模型建模提供了更好的灵活性,能够显著提高多语言模型中输入词嵌入参数的分配效率。具体来说,通过把原本"喂"给输入词嵌入的部分参数重新分配进 Transformer 编码器层,在参数量完全相同的情况下,RemBERT 在多语言自然语言理解任务上取得了优于同规模模型的性能。
文档还总结了作者的两个关键发现:
- 增大输出词嵌入维度可以提升模型性能:即使预训练结束后输出词嵌入被丢弃,这一收益在微调阶段依然能够保持;
- 更大的输出词嵌入具有正则化效果:可以防止模型在预训练数据集上过拟合,并增强模型在其他 NLP 数据集上的泛化能力。
这两点带来的实际收益是:可以训练出性能更强的模型,而无需在微调阶段增加任何参数。这正是"解耦嵌入"在参数效率上的核心价值——把参数花在真正影响建模能力的地方。
PaddleNLP 中的 RemBERT:配置与源码结构解读
预训练模型配置一览
PaddleNLP 通过RemBertConfig与REMBERT_PRETRAINED_INIT_CONFIGURATION定义了官方rembert预训练模型的全部超参数(见 configuration.py):
| 配置项 | 取值 | 说明 |
|---|---|---|
vocab_size | 250300 | 词表大小(SentencePiece 词表) |
input_embedding_size | 256 | 输入嵌入维度(核心特征,远小于 hidden_size) |
hidden_size | 1152 | 编码器隐藏层维度 |
num_hidden_layers | 32 | Transformer 编码器层数 |
num_attention_heads | 18 | 每层注意力头数 |
intermediate_size | 4608 | FFN 中间层维度(4 × hidden_size) |
hidden_act | gelu | FFN 激活函数 |
hidden_dropout_prob | 0 | 全连接层 dropout 概率 |
attention_probs_dropout_prob | 0 | 注意力概率 dropout |
max_position_embeddings | 512 | 最大序列长度 |
type_vocab_size | 2 | token_type 数量(句子 A/B) |
initializer_range | 0.02 | 参数初始化标准差 |
pad_token_id | 0 | padding token 的 id |
layer_norm_eps | 1e-12 | LayerNorm epsilon |
注意到一个关键细节:input_embedding_size = 256远小于hidden_size = 1152。这正是 RemBERT "参数重新分配"思想的直接体现——输入嵌入只保留较小的维度,省下的参数被分配到更深更宽的 Transformer 编码器中。
网络结构源码解读
RemBERT 的模型主体定义在 paddlenlp/transformers/rembert/modeling.py,整体结构与 BERT 式模型一致,但嵌入层有本质区别:
- RemBertEmbeddings(modeling.py#L64-L103):词嵌入、位置嵌入、token_type 嵌入的维度均为
config.input_embedding_size(256),而非hidden_size; - 维度映射层:
RemBertEncoder在进入 32 层 Transformer 之前,先用一个nn.Linear(config.input_embedding_size, config.hidden_size)的embedding_hidden_mapping_in将 256 维映射到 1152 维(modeling.py#L256-L260); - 标准编码器:随后是 32 层由自注意力(query/key/value 线性层 + softmax + dropout)、FFN(gelu 激活)与残差连接 + LayerNorm 构成的
RemBertLayer; - Pooler:取序列首 token(
[CLS])的隐藏状态,经过一层 Linear + Tanh 得到池化输出(modeling.py#L106-L118)。
围绕RemBertModel基座,仓库提供了 5 种下游任务头(全部定义在 modeling.py 的__all__中):
RemBertForSequenceClassification:文本分类(本文微调示例所用),在[CLS]池化输出上加 dropout 与Linear(hidden_size, num_classes);RemBertForQuestionAnswering:阅读理解,输出 span 的 start/end logits;RemBertForMaskedLM:掩码语言模型;RemBertForTokenClassification:序列标注(如 NER);RemBertForMultipleChoice:多选题型分类。
此外,RemBertLMPredictionHead的 decoder 被实现为Linear(config.hidden_size, config.hidden_size)(modeling.py#L527-L548),即 LM 预测头不再与输入嵌入矩阵共享权重,从源码层面印证了"解耦嵌入"的设计理念。
分词器
RemBertTokenizer(tokenizer.py)基于SentencePiece(词表文件sentencepiece.model),do_lower_case=False,特殊 token 为[CLS]、[UNK]、[SEP]、[PAD]、[MASK],最大输入长度 512。句子对编码格式为[CLS] A [SEP] B [SEP],句子 A 的 token_type 为 0、句子 B 为 1。该分词器对应的测试用例位于 tests/transformers/rembert/test_tokenizer.py。
快速开始:自动下载模型与分词器
在运行微调脚本前,需要先安装 PaddlePaddle 与 PaddleNLP。PaddleNLP 的from_pretrained("rembert")会自动完成预训练权重与分词器资源的下载:
- 模型权重:
model_state.pdparams,资源地址配置在 configuration.py#L44-L48 的REMBERT_PRETRAINED_RESOURCE_FILES_MAP中; - 分词器:
sentencepiece.model,资源地址配置在 tokenizer.py#L74-L79。
可以先用下面这段代码快速验证模型能否正确加载并前向推理(源自 modeling.py 中的使用示例):
import paddle from paddlenlp.transformers import RemBertModel, RemBertTokenizer tokenizer = RemBertTokenizer.from_pretrained("rembert") model = RemBertModel.from_pretrained("rembert") inputs = tokenizer("欢迎使用百度飞桨!") inputs = {k: paddle.to_tensor([v]) for (k, v) in inputs.items()} output = model(**inputs)数据集准备:XTREME-XNLI 与 XTREME-PAWS-X
RemBERT 微调示例使用 XTREME 基准中的两个多语言自然语言理解任务,数据文件的读取逻辑由 data_processor.py 中的XNLIProcessor与MrpcProcessor实现。
XTREME-XNLI
- 训练集:来自
XNLI-MT-1.0.zip,解压后位于XNLI-MT-1.0/multinli/multinli.train.en.tsv; - 测试集:来自
XNLI-1.0.zip,文件为xnli.test.tsv。
对应到代码中(data_processor.py#L75-L112),XNLIProcessor的训练、验证、测试文件分别为multinli.train.en.tsv、xnli.dev.tsv、xnli.test.tsv,标签空间为["neutral", "entailment", "contradictory"](共 3 类)。训练样本从 tsv 的第 1、2 列读取句子对、第 3 列读取标签;测试样本从第 7、8 列读取句子对、第 2 列读取标签(并将contradiction统一映射为contradictory)。
XTREME-PAWS-X
- 解压后每种语言包含
train、dev、test开头的 tsv 文件; - 由于该任务需要在多语言上进行测试,请将全部语言的测试集合并到一个文件。
对应到代码中(data_processor.py#L37-L62),MrpcProcessor读取的合并后文件为train.tsv(训练)、dev_2k.tsv(验证)、test_2k.tsv(测试),标签为二分类["0", "1"]。
两个数据处理器都会使用RemBertTokenizer将文本逐条 token 化,并交由 main.py 中的DataGenerator封装成 PaddleDataset。
下游任务微调:XTREME-XNLI
在slm/model_zoo/rembert目录下,以 XTREME-XNLI 为例,运行以下命令即可训练并评估 RemBert 在 XNLI 数据集上的精度(文档原文命令中--device gpu一行缺少续行符,此处已修正):
python -m paddle.distributed.launch main.py \ --model_type rembert \ --data_dir data/ \ --output_dir output/ \ --device gpu \ --learning_rate 1e-5 \ --num_train_epochs 3 \ --train_batch_size 16 \ --do_train \ --do_eval \ --task xnli \ --eval_step 500说明:README 中给出的示例路径为
examples/language_model/rembert/main.py,在本仓库中的实际位置为 slm/model_zoo/rembert/main.py,运行时请以实际路径为准。
训练过程中会周期性地在测试集上评估模型,并保存当前最优精度对应的 checkpoint。训练结束后将看到如下结果:
Accuracy 0.8089XTREME-XNLI 数据集内部处理细节
从 main.py 的load_example与collate_fn可以看到数据流水线的关键步骤:
- 根据
--task选择处理器:xnli→XNLIProcessor,paws→MrpcProcessor; - 训练集使用
DistributedBatchSampler(按--train_batch_size分片、shuffle),验证/测试集使用普通DataLoader(按--eval_batch_size); collate_fn将 batch 内文本按最长序列动态 padding:句子 A 的 token_type 补 0,句子 B 补 1;- 拼接
text_a与text_b后统一截断到--max_seq_length(默认 512); - 每个 epoch 的步数按
len(dataloader) // gradient_accumulation_steps计算(main.py#L144)。
下游任务微调:XTREME-PAWS-X
在 PAWS-X 数据集上训练使用如下命令(学习率改为 8e-6,任务切换为paws):
python -m paddle.distributed.launch main.py \ --model_type rembert \ --data_dir data/ \ --output_dir output/ \ --device gpu \ --learning_rate 8e-6 \ --num_train_epochs 3 \ --train_batch_size 16 \ --do_train \ --do_eval \ --task paws \ --eval_step 500训练结束后模型会在测试集上完成评估,你将看到如下结果:
Accuracy 0.8778命令行参数详解
以下参数定义均可从 main.py#L32-L73 的 argparse 中逐一对应:
| 参数 | 说明 | 默认值 |
|---|---|---|
model_type | 预训练模型类型,当前支持rembert | rembert |
data_dir | 数据集路径(包含上述 tsv 文件) | 无(需指定) |
task | 训练任务:xnli或paws | 无(必填) |
output_dir | 模型预测结果与 checkpoint 保存路径 | outputs |
device | 设备类型,脚本支持cpu、gpu(README 亦提及 XPU 场景,当前脚本的 choices 限定为 cpu/gpu) | gpu |
learning_rate | 基础学习率,与 scheduler 输出相乘得到实际学习率(XNLI 用 1e-5,PAWS-X 用 8e-6) | 8e-6 |
num_train_epochs | 训练 epoch 数 | 3 |
train_batch_size | 每次迭代每张卡上的样本数目 | 16 |
eval_batch_size | 评估时每张卡上的 batch size | 16 |
max_seq_length | token 化后输入最大长度,超长截断、不足 padding | 512 |
gradient_accumulation_steps | 梯度累积步数(loss 会除以该值再反传) | 2 |
warmup_proportion | 线性学习率 warmup 所占训练步数比例 | 0.02 |
weight_decay | AdamW 权重衰减系数 | 0.01 |
seed | 随机种子(同时设置 paddle / random / numpy) | 42 |
eval_step | 每训练多少步在测试集上评估一次模型 | 2000 |
do_train | 是否开启训练 | 关闭 |
do_eval | 是否开启评估(加载output_dir中模型在测试集评估) | 关闭 |
其中两点值得注意:
- 多 GPU 训练:
--device gpu配合python -m paddle.distributed.launch即可分布式启动;main.py#L140-L142 中当nranks > 1时会初始化并行环境并用paddle.DataParallel包装模型;可通过环境变量CUDA_VISIBLE_DEVICES指定使用的 GPU id; - 类数自动推导:
RemBertForSequenceClassification.from_pretrained(args.model_type, num_classes=num_label)中的num_label取自processor.get_labels()的长度(XNLI 为 3,PAWS-X 为 2),无需手动指定。
训练器源码视角:优化器、混合精度与评估逻辑
微调脚本的训练逻辑封装在 trainer.py,几个关键实现细节如下:
- 优化器:
AdamW,beta1=0.9、beta2=0.99,梯度裁剪ClipGradByNorm(clip_norm=1.0);权重衰减只作用于非 bias、非 LayerNorm 的参数(trainer.py#L85-L99); - 学习率调度:
LinearDecayWithWarmup(线性 warmup + 线性衰减),warmup 比例默认 0.02; - 混合精度:
paddle.amp.decorate(level="O2")开启 AMP O2 训练,配合GradScaler(init_loss_scaling=1024)与paddle.amp.auto_cast完成前向与梯度缩放(trainer.py#L47-L66); - 评估与保存策略:每
eval_step步在测试集上计算一次Accuracy,只有当精度超过历史最优值时才model.save_pretrained(output_dir)保存模型,训练结束时output_dir中保留的即是最优 checkpoint(trainer.py#L71-L77)。
参考资料
RemBERT 原论文的 BibTeX 引用如下(源自 README 的 Reference 部分):
@article{chung2020rethinking, title={Rethinking embedding coupling in pre-trained language models}, author={Chung, Hyung Won and Fevry, Thibault and Tsai, Henry and Johnson, Melvin and Ruder, Sebastian}, journal={arXiv preprint arXiv:2010.12821}, year={2020} }如需进一步深入,可以继续阅读仓库中的以下文件:微调入口 main.py、数据与分词处理 data_processor.py、训练器 trainer.py、模型实现 modeling.py、配置 configuration.py、分词器 tokenizer.py 以及分词器测试 test_tokenizer.py。
- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
相关推荐
PaddleNLP RemBERT 模型源码解析与实战:从轻量词嵌入到多任务微调
PaddleNLP RemBERT 模型源码解析与实战:从轻量词嵌入到多任务微调 本篇技术指南以 PaddleNLP 中 RemBERT 模型的 modelin
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPPaddleNLP 中的 ERNIE-M 多语言预训练模型:从回译预训练原理到跨语言任务实战
PaddleNLP 中的 ERNIE M 多语言预训练模型:从回译预训练原理到跨语言任务实战 导读 ERNIE M 是百度提出的一种基于回译机制的多语言语言模型
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPPaddleNLP 中的 ERNIE-M 多语言预训练模型:权重清单、架构解析与跨语言微调实战
PaddleNLP 中的 ERNIE M 多语言预训练模型:权重清单、架构解析与跨语言微调实战 ERNIE M 是百度提出的多语言预训练语言模型,通过在大规模单
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考