PaddleNLP AdamW 优化器实战:AdamWDL 分层学习率衰减的原理、源码解析与使用方法
2026/9/23 23:18:08 网站建设 项目流程
  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

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

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

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.pyAdamWDLlayerwise_lr_decay带分层学习率动态设置的 AdamW 优化器及其学习率衰减函数
ema.pyExponentialMovingAverage指数滑动平均(EMA)
lr.pyInverseSquareRootSchedule反平方根学习率调度
__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_ratefloat / LRScheduler0.001学习率,可为浮点数或学习率调度器
beta1float / Tensor0.9一阶矩估计指数衰减率(shape 为 [1] 的 float32 Tensor 亦可)
beta2float / Tensor0.999二阶矩估计指数衰减率
epsilonfloat / Tensor1e-08数值稳定小量
parameterslist / tupleNone待更新的参数(动转静模式下必填;静态模式下为 None 时更新全部参数)
weight_decayfloat / Tensor0.01权重衰减系数(解耦形式)
apply_decay_param_funfunction / NoneNone回调函数,仅对apply_decay_param_fun(Tensor.name) == True的参数施加权重衰减
grad_clipGradientClipBaseNone梯度裁剪策略(如GradientClipByGlobalNormGradientClipByNormGradientClipByValue),None 表示不裁剪
lazy_modeboolFalse惰性模式:仅更新当前 mini-batch 中有梯度的元素,可显著加速超大参数更新,但语义与标准 Adam 略有差异
multi_precisionboolFalse是否在权重更新时使用多精度(如 fp16 训练时维护 fp32 master weight)
layerwise_decayfloat1.0分层学习率衰减比率(1.0 表示不衰减)
n_layersint12Transformer 编码器总层数
set_param_lr_funfunction / Nonelayerwise_lr_decay在 Adam 算子执行前设置参数学习率的回调函数
name_dictdictNone键为模型动态名(model.named_parameters()所得)、值为静态名(结构名)的映射
namestrNone优化器名称,一般无需设置

其中layerwise_decayn_layersset_param_lr_funname_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_decayname_dictn_layers三个参数,后续调用时只需传入param即可。随后super().__init__(...)将剩余参数透传给父类paddle.optimizer.AdamW,因此AdamWDL天然继承 Paddle 官方 AdamW 的全部能力(lazy_modemulti_precisiongrad_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

其逻辑可拆解为:

  1. 名称映射:通过name_dict将参数的动态名(param.name)映射为静态结构名(如encoder.layers.0.self_attn.q_proj.weight)。
  2. 编码器层:若静态名包含encoder.layers.,解析出层号layer,计算ratio = decay_rate ** (n_layers - layer)。即第 0 层学习率为基准学习率的α^n_layers,最后一层(layer = n_layers - 1)为α^1,顶层结构(如 pooler、head)保持ratio = 1.0
  3. embedding 层:若静态名包含embedding,学习率缩放到α^(n_layers+1),是所有层中最小的,符合"embedding 学习最保守"的实践经验。
  4. 其他结构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

执行顺序非常清晰:

  1. set_param_lr_fun为 None,退化为父类标准 AdamW 逻辑;
  2. 先执行解耦权重衰减(_append_decoupled_weight_decay);
  3. 保存当前学习率prev_lr,调用set_param_lr_fun(param)得到该参数的缩放系数ratio,将optimize_attr["learning_rate"]临时放大;
  4. 调用父类逻辑执行 Adam 算子(此时算子使用缩放后的学习率);
  5. 立即恢复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_layersname_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_orderrename_auto_parallel_state_dict等方法的注释,其中明确提到adamw_optimizer_param_suffix_name_mappingadamw_optimizer_status_name_suffix_mappings两套命名规则)。这意味着:若你使用AdamWDL(或任一paddle.optimizer.AdamW系优化器)训练,其优化器状态与 PaddleNLP 的 checkpoint 转换/续训链路是天然兼容的,无需额外适配。

八、注意事项与最佳实践

  1. n_layers必须与模型实际层数一致layerwise_lr_decaydecay_rate ** (n_layers - layer)计算每层缩放,若n_layers传错,衰减幅度会整体偏移。建议从模型 config 的num_hidden_layers字段动态获取。
  2. name_dict依赖静态名规则:默认衰减函数要求静态名包含encoder.layers.embedding关键字。若模型结构命名不同(例如静态名中不含encoder),请自定义set_param_lr_fun,否则所有参数 ratio 恒为 1.0。
  3. layerwise_decay的类型约束:仅接受float或 PaddleVariable,传入其他类型(如numpy标量、int)会抛出TypeError("coeff should be float or Tensor.")
  4. 权重衰减是解耦的weight_decay作用于参数本身(param -= param * coeff * lr),而非梯度;配合apply_decay_param_fun可精确控制哪些参数参与衰减(例如常见的"bias 与 LayerNorm 参数不做权重衰减"策略)。
  5. 多精度训练建议:fp16 训练时建议开启multi_precision=True,此时衰减与更新都在 fp32 master weight 上进行,数值稳定性更好;同时配合lazy_mode可在超大参数下加速更新。
  6. 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.

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

相关推荐

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

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

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

立即咨询