- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
AdamW 优化器是 Transformer 类大模型训练的标配优化器,而"分层学习率衰减(Layer-wise Learning Rate Decay)"则能进一步提升深层模型与预训练模型的收敛质量。本文以 docs/zh/source/paddlenlp.ops.optimizer.AdamwOptimizer.rst 对应的 PaddleNLP 优化器 API 为核心,深入讲解 PaddleNLP 在paddlenlp.ops.optimizer中实现的AdamWDL(AdamW with Dynamic layer-wise lr)与layerwise_lr_decay工具函数。读完本文,你将掌握 AdamW 与分层学习率衰减的数学原理、AdamWDL的全部参数语义与默认值、其底层优化算子执行链路的源码实现,并能在自己的训练脚本中直接落地使用。
一、背景:为什么 Transformer 训练需要 AdamW 与分层学习率衰减
Transformer 模型(尤其是 BERT 系、GPT 系等深层预训练模型)在训练时通常面临两个经典问题:
- 权重衰减(Weight Decay)与 L2 正则的耦合问题:标准 SGD 下的 L2 正则与权重衰减等价,但在 Adam 这类基于自适应学习率的优化器中,L2 正则会被一阶矩/二阶矩估计"放大",导致权重衰减行为偏离预期。AdamW 将权重衰减与梯度自适应更新解耦(decoupled weight decay),即衰减项直接作用于参数本身而非梯度,从而得到更稳定、更可控的正则化效果。
- 不同层对学习率敏感度不同:浅层(如 embedding)通常学习速度慢、需要更保守的学习率;深层则相反。分层学习率衰减(Layer-wise Learning Rate Decay)通过按层指数衰减学习率,让浅层使用更小的学习率、深层使用更大的学习率,从而提升收敛质量。
PaddleNLP 正是围绕这两个需求,在 paddlenlp/ops/optimizer/adamwdl.py 中实现了AdamWDL优化器:它继承自 Paddle 官方paddle.optimizer.AdamW,在标准 AdamW 的基础上叠加"每层动态学习率"能力,默认使用layerwise_lr_decay作为动态学习率设置函数。
说明:
docs/zh/source/paddlenlp.ops.optimizer.AdamwOptimizer.rst是一份由 Sphinxautomodule指令生成的 API 文档页,其内容来源于paddlenlp.ops.optimizer模块对应优化器类的 docstring。当前仓库中该文档对应的实际实现即AdamWDL类(位于 paddlenlp/ops/optimizer/adamwdl.py),同系列 API 文档还包括 paddlenlp.ops.optimizer.adamwdl.rst、paddlenlp.ops.optimizer.adamw.rst 等。
二、模块总览:paddlenlp.ops.optimizer 里有什么
先看 paddlenlp/ops/optimizer/init.py 的导出内容:
from .adamwdl import AdamWDL, layerwise_lr_decay from .ema import ExponentialMovingAverage from .lr import InverseSquareRootSchedule __all__ = ["layerwise_lr_decay", "AdamWDL", "ExponentialMovingAverage", "InverseSquareRootSchedule"]该目录下共四个源文件:
| 文件 | 导出内容 | 作用 |
|---|---|---|
adamwdl.py | AdamWDL、layerwise_lr_decay | 带分层学习率动态设置的 AdamW 优化器及其学习率衰减函数 |
ema.py | ExponentialMovingAverage | 指数滑动平均(EMA) |
lr.py | InverseSquareRootSchedule | 反平方根学习率调度 |
__init__.py | 上述全部 | 统一导出入口 |
使用方式非常简单:
from paddlenlp.ops.optimizer import AdamWDL, layerwise_lr_decay三、AdamWDL:完整的参数语义与默认值
AdamWDL的类 docstring(见 paddlenlp/ops/optimizer/adamwdl.py#L46-L131)完整定义了其数学形式与全部参数。其更新规则为:
t = t + 1 moment_1_out = β1 * moment_1 + (1 - β1) * grad moment_2_out = β2 * moment_2 + (1 - β2) * grad * grad learning_rate = learning_rate * sqrt(1 - β2^t) / (1 - β1^t) param_out = param - learning_rate * (moment_1 / (sqrt(moment_2) + ε) + λ * param)其中β1、β2分别为一阶、二阶矩估计的指数衰减率,ε为数值稳定小量,λ为权重衰减系数。注意公式末尾的λ * param项即解耦权重衰减——它不进入梯度,而是直接作用在参数更新上。
3.1 构造参数全表
AdamWDL.__init__的签名与默认值(源码 paddlenlp/ops/optimizer/adamwdl.py#L133-L150):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
learning_rate | float / LRScheduler | 0.001 | 学习率,可为浮点数或学习率调度器 |
beta1 | float / Tensor | 0.9 | 一阶矩估计指数衰减率(shape 为 [1] 的 float32 Tensor 亦可) |
beta2 | float / Tensor | 0.999 | 二阶矩估计指数衰减率 |
epsilon | float / Tensor | 1e-08 | 数值稳定小量 |
parameters | list / tuple | None | 待更新的参数(动转静模式下必填;静态模式下为 None 时更新全部参数) |
weight_decay | float / Tensor | 0.01 | 权重衰减系数(解耦形式) |
apply_decay_param_fun | function / None | None | 回调函数,仅对apply_decay_param_fun(Tensor.name) == True的参数施加权重衰减 |
grad_clip | GradientClipBase | None | 梯度裁剪策略(如GradientClipByGlobalNorm、GradientClipByNorm、GradientClipByValue),None 表示不裁剪 |
lazy_mode | bool | False | 惰性模式:仅更新当前 mini-batch 中有梯度的元素,可显著加速超大参数更新,但语义与标准 Adam 略有差异 |
multi_precision | bool | False | 是否在权重更新时使用多精度(如 fp16 训练时维护 fp32 master weight) |
layerwise_decay | float | 1.0 | 分层学习率衰减比率(1.0 表示不衰减) |
n_layers | int | 12 | Transformer 编码器总层数 |
set_param_lr_fun | function / None | layerwise_lr_decay | 在 Adam 算子执行前设置参数学习率的回调函数 |
name_dict | dict | None | 键为模型动态名(model.named_parameters()所得)、值为静态名(结构名)的映射 |
name | str | None | 优化器名称,一般无需设置 |
其中layerwise_decay、n_layers、set_param_lr_fun、name_dict四个参数是AdamWDL区别于普通AdamW的核心,用于实现"逐层动态学习率"。
3.2 参数校验逻辑
构造函数开头对layerwise_decay做了严格类型校验(源码 paddlenlp/ops/optimizer/adamwdl.py#L151-L155):
if not isinstance(layerwise_decay, float) and not isinstance(layerwise_decay, paddle.framework.Variable): raise TypeError("coeff should be float or Tensor.") self.layerwise_decay = layerwise_decay self.n_layers = n_layers self.set_param_lr_fun = partial(set_param_lr_fun, layerwise_decay, name_dict, n_layers)也就是说layerwise_decay必须是float或 PaddleVariable(Tensor),否则直接抛出TypeError。同时,set_param_lr_fun通过functools.partial预绑定layerwise_decay、name_dict、n_layers三个参数,后续调用时只需传入param即可。随后super().__init__(...)将剩余参数透传给父类paddle.optimizer.AdamW,因此AdamWDL天然继承 Paddle 官方 AdamW 的全部能力(lazy_mode、multi_precision、grad_clip等)。
四、核心机制:layerwise_lr_decay 分层学习率衰减
4.1 数学原理
分层学习率衰减即"自顶向下指数衰减各层学习率"。例如假设第 24 层使用学习率l,分层衰减率为α,则第m层的学习率为l * α^(24-m)(该思想详见 1906.08237 相关工作)。浅层(靠近 embedding)学习率最小,深层学习率最大。
4.2 源码实现
layerwise_lr_decay的实现位于 paddlenlp/ops/optimizer/adamwdl.py#L23-L43:
def layerwise_lr_decay(decay_rate, name_dict, n_layers, param): ratio = 1.0 static_name = name_dict[param.name] if "encoder.layers" in static_name: idx = static_name.find("encoder.layers.") layer = int(static_name[idx:].split(".")[2]) ratio = decay_rate ** (n_layers - layer) elif "embedding" in static_name: ratio = decay_rate ** (n_layers + 1) return ratio其逻辑可拆解为:
- 名称映射:通过
name_dict将参数的动态名(param.name)映射为静态结构名(如encoder.layers.0.self_attn.q_proj.weight)。 - 编码器层:若静态名包含
encoder.layers.,解析出层号layer,计算ratio = decay_rate ** (n_layers - layer)。即第 0 层学习率为基准学习率的α^n_layers,最后一层(layer = n_layers - 1)为α^1,顶层结构(如 pooler、head)保持ratio = 1.0。 - embedding 层:若静态名包含
embedding,学习率缩放到α^(n_layers+1),是所有层中最小的,符合"embedding 学习最保守"的实践经验。 - 其他结构:
ratio保持 1.0,学习率不做缩放。
最终返回的ratio会被乘到该参数当前的learning_rate上,再执行 Adam 更新算子(详见下一节)。
从源码结构看,
layerwise_lr_decay面向的是静态名中包含encoder.layers/embedding的 Transformer 编码器类模型(如 BERT 系);对于不含这些关键字的模型,所有参数保持 1.0,等同于普通 AdamW,不会产生错误。若你的模型静态名规则不同,可以自定义set_param_lr_fun回调(见第六节示例)。
五、源码解析:优化算子执行链路
AdamWDL通过覆写三个关键方法实现"解耦权重衰减 + 动态学习率"的完整执行链路。
5.1 _append_optimize_op:动态学习率的注入点
源码 paddlenlp/ops/optimizer/adamwdl.py#L182-L194:
def _append_optimize_op(self, block, param_and_grad): if self.set_param_lr_fun is None: return super(AdamWDL, self)._append_optimize_op(block, param_and_grad) self._append_decoupled_weight_decay(block, param_and_grad) prev_lr = param_and_grad[0].optimize_attr["learning_rate"] ratio = self.set_param_lr_fun(param_and_grad[0]) param_and_grad[0].optimize_attr["learning_rate"] *= ratio # execute Adam op res = super(AdamWDL, self)._append_optimize_op(block, param_and_grad) param_and_grad[0].optimize_attr["learning_rate"] = prev_lr return res执行顺序非常清晰:
- 若
set_param_lr_fun为 None,退化为父类标准 AdamW 逻辑; - 先执行解耦权重衰减(
_append_decoupled_weight_decay); - 保存当前学习率
prev_lr,调用set_param_lr_fun(param)得到该参数的缩放系数ratio,将optimize_attr["learning_rate"]临时放大; - 调用父类逻辑执行 Adam 算子(此时算子使用缩放后的学习率);
- 立即恢复
prev_lr,避免影响其他参数。
这一"临时修改、用完即恢复"的设计,保证了每个参数都能携带各自的动态学习率进入 Adam 算子,同时不会污染全局学习率状态。
5.2 _append_decoupled_weight_decay:解耦权重衰减实现
源码 paddlenlp/ops/optimizer/adamwdl.py#L196-L243,核心更新式为:
parameter = parameter - parameter * coeff * lr关键实现细节:
- 参数过滤:若
_apply_decay_param_fun不为 None,则只有_apply_decay_param_fun(param.name) == True的参数才执行衰减; - 学习率获取:学习率为 float 时直接使用;否则调用
_create_param_lr为参数创建对应的学习率张量(该方法必须在optimizer._create_global_learning_rate()之后调用,源码注释对此有明确说明); - 衰减系数缓存:
decay_coeff = 1.0 - learning_rate * self._coeff,并以learning_rate为键缓存在self._lr_to_coeff字典中,同一学习率的参数复用计算结果,避免重复计算; - 多精度支持:当
multi_precision=True且参数为 fp16 时,从self._master_weights中取出 fp32 的 master weight,用decay_coeff缩放并paddle.assign写回 master weight;否则直接对 fp16 参数本体做缩放; - 设备守卫:通过
paddle.static.device_guard(None)保证decay_coeff计算可兼容 pipeline 等场景的设备设置。
5.3 _create_optimization_pass:动转静下的缓存清理
源码 paddlenlp/ops/optimizer/adamwdl.py#L245-L249:
def _create_optimization_pass(self, parameters_and_grads): optimize_ops = super(AdamWDL, self)._create_optimization_pass(parameters_and_grads) # In dygraph mode, clear _lr_to_coeff after applied gradient self._lr_to_coeff = dict() return optimize_ops由于动转静(dygraph)模式下每步都会执行apply_gradient,_lr_to_coeff缓存必须每步清空,否则缓存的decay_coeff会与更新的学习率不一致(源码注释明确说明该动机)。
此外,AdamWDL还覆写了_update_param_group以支持按参数组(param group)更新coeff,以及__str__返回"Weight Decay, params: ..."形式的可读描述。
六、完整使用示例
6.1 标准用法:基于 name_dict 的分层衰减
以下代码来自类 docstring 的官方示例(paddlenlp/ops/optimizer/adamwdl.py#L103-L130),展示了对paddle.nn.Linear应用带分层衰减的AdamWDL:
import paddle from paddlenlp.ops.optimizer import AdamWDL def simple_lr_setting(decay_rate, name_dict, n_layers, param): ratio = 1.0 static_name = name_dict[param.name] if "weight" in static_name: ratio = decay_rate**0.5 param.optimize_attr["learning_rate"] *= ratio linear = paddle.nn.Linear(10, 10) name_dict = dict() for n, p in linear.named_parameters(): name_dict[p.name] = n inp = paddle.rand([10, 10], dtype="float32") out = linear(inp) loss = paddle.mean(out) adamwdl = AdamWDL( learning_rate=1e-4, parameters=linear.parameters(), set_param_lr_fun=simple_lr_setting, layerwise_decay=0.8, name_dict=name_dict) loss.backward() adamwdl.step() adamwdl.clear_grad()要点拆解:
name_dict通过model.named_parameters()构建:键为参数的动态名(param.name),值为结构名(n);- 自定义
simple_lr_setting(decay_rate, name_dict, n_layers, param)与layerwise_lr_decay签名一致,内部直接修改param.optimize_attr["learning_rate"]即可,AdamWDL会在_append_optimize_op中读取并应用; - 对 Transformer 编码器模型,直接使用默认的
layerwise_lr_decay并传入正确的n_layers与name_dict即可,无需自定义回调。
6.2 典型训练循环骨架
将AdamWDL接入自定义训练脚本的最小骨架:
import paddle from paddlenlp.ops.optimizer import AdamWDL model = YourTransformerModel() # 例如 BERT 系模型 n_layers = model.config.num_hidden_layers name_dict = {p.name: n for n, p in model.named_parameters()} optimizer = AdamWDL( learning_rate=paddle.optimizer.lr.LinearDecay(1e-4, int(1e4), 0.0), parameters=model.parameters(), weight_decay=0.01, grad_clip=paddle.nn.ClipGradByGlobalNorm(1.0), layerwise_decay=0.8, # 分层衰减率 n_layers=n_layers, # 与实际层数保持一致 name_dict=name_dict, multi_precision=True, # 开启 fp16 多精度更新 ) for step, batch in enumerate(dataloader): loss = model(batch) loss.backward() optimizer.step() optimizer.clear_grad()注意:layerwise_decay=1.0(默认值)等价于关闭分层衰减,此时AdamWDL退化为普通AdamW,但代码仍可正常运行。
七、优化器状态命名与 checkpoint 的关联
AdamWDL(继承自paddle.optimizer.AdamW)在保存/加载优化器状态时,会使用 Adam 系优化器的标准状态命名后缀。这一点在 PaddleNLP 的 checkpoint 转换工具 paddlenlp/trainer/utils/ckpt_converter.py 中有直接体现——该文件第 39 行定义了:
OPTIMIZER_STATE_NAME_SUFFIX = [".moment1", ".moment2", ".beta1_pow_acc", ".beta2_pow_acc", ".master_weight"]即 Adam 系优化器状态由四类张量组成:
| 状态后缀 | 含义 |
|---|---|
.moment1 | 一阶矩估计(Adam 的 momentum) |
.moment2 | 二阶矩估计(Adam 的 variance) |
.beta1_pow_acc | β1 的幂次累积(用于偏差校正) |
.beta2_pow_acc | β2 的幂次累积(用于偏差校正) |
.master_weight | 多精度(multi_precision)训练时维护的 fp32 master weight |
ckpt_converter 在加载混合并行(TP/PP/Sharding)checkpoint 时会按上述规则对优化器状态做重命名与拼接(参见其rename_using_optimizer_state_order、rename_auto_parallel_state_dict等方法的注释,其中明确提到adamw_optimizer_param_suffix_name_mapping与adamw_optimizer_status_name_suffix_mappings两套命名规则)。这意味着:若你使用AdamWDL(或任一paddle.optimizer.AdamW系优化器)训练,其优化器状态与 PaddleNLP 的 checkpoint 转换/续训链路是天然兼容的,无需额外适配。
八、注意事项与最佳实践
n_layers必须与模型实际层数一致:layerwise_lr_decay用decay_rate ** (n_layers - layer)计算每层缩放,若n_layers传错,衰减幅度会整体偏移。建议从模型 config 的num_hidden_layers字段动态获取。name_dict依赖静态名规则:默认衰减函数要求静态名包含encoder.layers.与embedding关键字。若模型结构命名不同(例如静态名中不含encoder),请自定义set_param_lr_fun,否则所有参数 ratio 恒为 1.0。layerwise_decay的类型约束:仅接受float或 PaddleVariable,传入其他类型(如numpy标量、int)会抛出TypeError("coeff should be float or Tensor.")。- 权重衰减是解耦的:
weight_decay作用于参数本身(param -= param * coeff * lr),而非梯度;配合apply_decay_param_fun可精确控制哪些参数参与衰减(例如常见的"bias 与 LayerNorm 参数不做权重衰减"策略)。 - 多精度训练建议:fp16 训练时建议开启
multi_precision=True,此时衰减与更新都在 fp32 master weight 上进行,数值稳定性更好;同时配合lazy_mode可在超大参数下加速更新。 - API 文档定位:本文对应的 API 文档页 paddlenlp.ops.optimizer.AdamwOptimizer.rst 为
automodule自动生成页,同系列可继续查阅 paddlenlp.ops.optimizer.adamwdl.rst(AdamWDL 详细文档)、paddlenlp.ops.optimizer.ema.rst(EMA)以及 paddlenlp/ops/optimizer/lr.py(InverseSquareRootSchedule反平方根学习率调度),它们可组合出一套完整的 PaddleNLP 训练优化组件。
九、总结
AdamWDL是 PaddleNLP 提供的"AdamW + 分层学习率衰减"一体化优化器:它完整继承paddle.optimizer.AdamW的解耦权重衰减、多精度、梯度裁剪等能力,并通过layerwise_lr_decay默认策略 + 可自定义的set_param_lr_fun回调,在 Adam 算子执行前按层注入动态学习率。其实现中"临时缩放学习率、用完即恢复""decay_coeff 按学习率缓存、动转静每步清空"等设计细节,对理解 Paddle 优化器扩展机制亦有很强的参考价值。对于 BERT 系、GPT 系等深层 Transformer 的预训练与微调任务,AdamWDL是一个开箱即用、参数语义清晰的选择。
- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
相关推荐
PaddleNLP 优化器模块解析:AdamWDL 分层学习率衰减、EMA 与逆平方根调度器实战指南
PaddleNLP 优化器模块解析:AdamWDL 分层学习率衰减、EMA 与逆平方根调度器实战指南 paddlenlp.ops.optimizer 是 Pad
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPPaddleNLP 中的 AdamWDL 优化器:基于层间学习率衰减的 Transformer 训练方案
PaddleNLP 中的 AdamWDL 优化器:基于层间学习率衰减的 Transformer 训练方案 导读 AdamWDL(AdamW with Dynam
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP如何使用 annotated_deep_learning_paper_implementations 配置 AdamW 权重衰减与 warmup 学习率调度
如何使用 annotated_deep_learning_paper_implementations 配置 AdamW 权重衰减与 warmup 学习率调度 在
人工智能深度学习大模型NLP计算机视觉强化学习LoRA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考