首页
/ PyTorch C++ 前端自适应学习率优化器实战指南:Adam / AdamW / RMSprop / Adagrad

PyTorch C++ 前端自适应学习率优化器实战指南:Adam / AdamW / RMSprop / Adagrad

2026-09-07 19:57:46作者:姚月梅Lane

自适应学习率优化器(Adaptive Learning Rate Optimizers)是深度学习中收敛快、调参成本低的一类核心工具:它们会依据每个参数的历史梯度信息,为每个参数自动维护一个独立的学习率,从而在 Transformer、稀疏特征、RNN 等场景中大幅减少手动调度学习率的工作。本文以当前仓库 docs/cpp/source/api/optim/adaptive.md 为主线,系统讲解 PyTorch C++(libtorch)前端 torch::optim 中 Adam、AdamW、RMSprop、Adagrad 四个自适应优化器的原理、完整参数表、构造示例与源码实现细节,并结合仓库中的头文件与 .cpp 实现给出可直接复制运行的最小实践方案,帮助读者在自定义模型训练循环里正确选用与配置优化器。

自适应优化器概览

与传统 SGD 不同,自适应优化器不直接对所有参数使用同一个全局学习率,而是为每个参数维护一阶/二阶梯度统计量,并用它们对学习率逐参数缩放。当前仓库 C++ API 中对这一族优化器的实现遵循统一的“Options + ParamState + Optimizer”三段式结构(参见 torch/csrc/api/include/torch/optim/ 目录):

  • Options(如 AdamOptions):继承自 OptimizerCloneableOptions,承载全部超参数,采用链式 .xxx(...) 设置风格,并自带默认值;
  • ParamState(如 AdamParamState):保存每个参数在训练中持续更新的状态张量(动量、平方梯度均值等);
  • Optimizer(如 Adam):构造时接收 model->parameters()(一个 std::vector<Tensor>std::vector<OptimizerParamGroup>),并在每次 step() 时遍历全部 param_groups_ 更新参数。

每个优化器的默认值、参数取值范围校验以及状态序列化逻辑都可以从对应头文件中直接确认,下面逐一对四种优化器展开。

Adam(自适应矩估计,Adaptive Moment Estimation)

Adam 同时结合了 RMSprop 的逐参数缩放与动量思想:它维护梯度的一阶矩均值 exp_avg 与二阶矩均值 exp_avg_sq,并利用二者计算自适应的更新步长,因此在绝大多数深度学习任务中是一个收敛快、几乎无需精细调参的默认选择。原文档指出其典型适用场景为:Transformer / 注意力模型、快速原型迭代、缺少充分调参时间的场景以及通用深度学习任务。

关键参数

adam.h 中可以确认全部参数及默认值:

参数 默认值 说明
lr 1e-3 学习率,典型范围 1e-3 ~ 1e-4
betas {0.9, 0.999} 一阶/二阶矩的指数衰减系数(β1, β2
eps 1e-8 数值稳定性常数
weight_decay 0 L2 正则(注意:其耦合方式与 SGD 不同,见下文)
amsgrad false 是否启用 AMSGrad 变体(维护二阶矩的历史最大值)

构造器会通过 TORCH_CHECK 做参数校验:lr >= 0eps >= 00 <= betas < 1.0weight_decay >= 0,传入非法值会直接抛错,避免训练悄悄进入异常状态(见 adam.h)。

示例

// 标准 Adam 配置
auto optimizer = torch::optim::Adam(
    model->parameters(),
    torch::optim::AdamOptions(1e-3)        // learning rate
        .betas({0.9, 0.999})               // momentum terms
        .eps(1e-8)                         // numerical stability
        .weight_decay(0));                 // L2 penalty

// Transformer 场景:更低学习率配合 (β2=0.98) 的配置
auto optimizer = torch::optim::Adam(
    model->parameters(),
    torch::optim::AdamOptions(1e-4)
        .betas({0.9, 0.98}));              // β2=0.98 for transformers

底层更新逻辑(源码视角)

adam.cppAdam::step() 中,每一步的实际流程是:

  1. 跳过没有梯度定义的参数;稀疏梯度会直接报错(Adam does not support sparse gradients);
  2. 首次遇到某个参数时初始化 exp_avgexp_avg_sq(若启用 amsgrad 还需 max_exp_avg_sq),三者形状与参数一致、值全 0;
  3. step += 1,并计算偏差修正项 bias_correction1 = 1 - β1^stepbias_correction2 = 1 - β2^step(解决训练早期矩估计向 0 偏移的问题);
  4. weight_decay != 0,将其耦合进梯度grad = grad + weight_decay * p(这正是 Adam 与 AdamW 的本质区别);
  5. 更新一阶矩 exp_avg = β1·exp_avg + (1-β1)·grad、二阶矩 exp_avg_sq = β2·exp_avg_sq + (1-β2)·grad²
  6. 启用 amsgrad 时,denommax(max_exp_avg_sq, exp_avg_sq) 的均方根;否则直接取 exp_avg_sq 的均方根,并统一加上 eps
  7. step_size = lr / bias_correction1 执行参数更新 p -= step_size * exp_avg / denom

AdamW(解耦权重衰减的 Adam)

AdamW 修正了 Adam 中权重衰减与梯度更新耦合的问题。在 Adam 中,L2 惩罚先被加进梯度再参与一阶矩更新,这会让权重衰减项也被矩估计的缩放影响,导致正则化效果偏离预期;AdamW 则把权重衰减直接作用到权重上,与 SGD 的行为一致。因此原文档的建议是:只要需要 weight decay,就优先用 AdamW 而非 Adam——尤其是 BERT、GPT 等 Transformer 模型的大规模训练。

与 Adam 的数学差异

  • Adam:weight = weight - lr * (grad + weight_decay * weight)
  • AdamW:weight = weight - lr * grad - lr * weight_decay * weight

实现层面,adamw.cpp 在进入 Adam 式矩更新之前先执行 p.mul_(1 - lr * weight_decay),即“先衰减、再按 Adam 规则更新”,权重衰减与梯度完全解耦。其余一阶/二阶矩更新、偏差修正与 AMSGrad 分支与 Adam 一致。

AdamWOptions 的全部参数及默认值定义在 adamw.hlr=1e-3betas={0.9, 0.999}eps=1e-8weight_decay=1e-2(注意:与 AdamOptions 默认 weight_decay=0 不同,AdamW 默认就带正则)、amsgrad=false。其取值校验规则与 Adam 完全相同。

示例

// AdamW with decoupled weight decay - preferred for transformers
auto optimizer = torch::optim::AdamW(
    model->parameters(),
    torch::optim::AdamWOptions(1e-4)
        .betas({0.9, 0.999})
        .weight_decay(0.01));              // Decoupled regularization

RMSprop(均方根传播)

RMSprop 用最近梯度的平方滑动平均来逐参数缩放学习率:梯度大则步长被压缩,梯度小则步长放大。它对非平稳目标(如 RNN/LSTM 训练、在线学习、梯度尺度波动明显的任务)尤其有效。原文档给出的典型场景是 RNN/LSTM、非平稳目标与在线学习。

关键参数

rmsprop.h 中确认全部默认值:

参数 默认值 说明
lr 1e-2 学习率,典型范围 1e-3 ~ 1e-2
alpha 0.99 平方梯度滑动平均的平滑常数
eps 1e-8 数值稳定性常数
weight_decay 0 L2 正则(耦合进梯度)
momentum 0 可选的动量项(>0 时启用动量缓冲)
centered false 是否使用 centered RMSprop(按方差归一化)

参数校验要求 lr / eps / momentum / weight_decay / alpha 均非负。

示例

// RMSprop for RNN training
auto optimizer = torch::optim::RMSprop(
    model->parameters(),
    torch::optim::RMSpropOptions(1e-3)
        .alpha(0.99)                       // smoothing constant
        .momentum(0.9)                     // optional momentum
        .centered(true));                  // normalize by variance

底层更新逻辑(源码视角)

rmsprop.cpp 可以看出核心流程:首先更新平方梯度均值 square_avg = α·square_avg + (1-α)·grad²;当 centered=true 时额外维护梯度均值 grad_avg,并用“方差”作分母——即 avg = sqrt(square_avg - grad_avg²) + eps(等价于按方差归一化),否则 avg = sqrt(square_avg) + eps。若 momentum > 0,则维护动量缓冲并执行 buf = momentum·buf + grad/avgp -= lr·buf;否则直接 p -= lr·grad/avg。与 Adam 一致,RMSprop 同样不支持稀疏梯度(见源码中 RMSprop does not support sparse gradients 的断言)。

Adagrad(自适应梯度)

Adagrad 依据梯度平方的累加和来调整学习率:频繁更新的参数会被分配更小的学习率,更新稀少的参数则获得更大的学习率,因此非常适合稀疏数据场景。原文档建议的使用场景包括:带稀疏特征的 NLP 任务、更新不频繁的 Embedding 层、用户/物品特征稀疏的推荐系统。

关键参数

adagrad.h 中确认全部默认值:

参数 默认值 说明
lr 1e-2 学习率
lr_decay 0 学习率随时间步衰减的系数
weight_decay 0 L2 正则(与稀疏梯度不兼容)
initial_accumulator_value 0 累加器 sum 的初始值(构造时即创建,见下文)
eps 1e-10 数值稳定性常数(注意默认值比 Adam 系列更小)

与其余优化器不同,Adagrad 在构造时就会为每个参数创建 sum 状态张量,并用 initial_accumulator_value 填充(torch::full_like(p, initial_accumulator_value, MemoryFormat::Preserve),见 adagrad.h),因此该参数直接影响首个 batch 的有效步长。

示例

// Adagrad for sparse NLP features
auto optimizer = torch::optim::Adagrad(
    model->parameters(),
    torch::optim::AdagradOptions(0.01)
        .lr_decay(0)                       // learning rate decay
        .weight_decay(0)
        .initial_accumulator_value(0));

局限性

学习率随训练单调递减是 Adagrad 的固有特性:由于 sum 只增不减,分母持续变大,可能导致训练过早停滞。对长程训练,原文档建议改用 Adam 或 RMSprop。

底层更新逻辑(源码视角)

adagrad.cpp 展示了两个值得注意的细节:

  1. 学习率衰减公式clr = lr / (1 + (step - 1) * lr_decay),即 lr_decay 越大、越到后期学习率越小;
  2. 稀疏梯度原生支持:Adagrad 是四个优化器中唯一支持稀疏梯度的——梯度为稀疏张量时先 coalesce(),仅对非零位置累加平方并更新(adagrad.cpp)。但注意 weight_decay 与稀疏梯度不兼容,启用会抛错。

四款优化器选型速查

优化器 默认 lr 默认 weight_decay 稀疏梯度 推荐场景
Adam 1e-3 0 不支持 通用任务默认选择、Transformer、快速实验
AdamW 1e-3 1e-2 不支持 需要正确 L2 正则时优先于 Adam、BERT/GPT 类训练
RMSprop 1e-2 0 不支持 RNN/LSTM、非平稳目标、在线学习
Adagrad 1e-2 0 支持 稀疏特征 NLP、Embedding、稀疏推荐系统

选择建议(原文档观点与源码参数综合):

  • 没有特殊需求时,优先把 Adam 作为默认优化器,配合 1e-3 左右的学习率即可快速收敛;
  • 一旦引入 weight_decay 做正则,切换为 AdamW 以获得解耦、更可控的正则行为;
  • 任务目标非平稳或结构偏序列(RNN/LSTM)时考虑 RMSprop,必要时开启 centeredmomentum
  • 特征高度稀疏、且需要稀疏梯度支持时使用 Adagrad,并接受学习率单调下降的代价。

进阶:多参数组、状态持久化与断点续训

自适应优化器在实际工程中还有两个常见需求,仓库代码均有对应支持:

按参数组差异化配置

四个优化器都提供了“接收 std::vector<OptimizerParamGroup> 的构造重载”(如 adam.h),可对模型的不同子模块设置不同学习率,例如对 Embedding 层使用更大的学习率、对主干网络使用较小的学习率。

训练状态的保存与恢复

每个 Optimizer 类都实现了 save(serialize::OutputArchive&)load(serialize::InputArchive&)(见 adam.h 及各 .cpp 实现)。Options 与 ParamState 均具备 serialize 方法,stepexp_avgexp_avg_sqmax_exp_avg_sq(Adam/AdamW)、square_avg/momentum_buffer/grad_avg(RMSprop)、sum/step(Adagrad)等状态都可整体序列化,从而支持 checkpoint 保存与断点续训。

一个兼容性细节:仓库代码保留了 1.5.0 之前旧格式的反序列化路径(如 adam.cpp)。若加载到旧格式存档,会打印 “Your serialized Adam optimizer is still using the old serialization format…” 的警告并建议重新保存——在新版本中用 save/load 即可自动读写带 pytorch_version 标记的新格式。

延伸阅读

读者可以把本文示例中的 model 替换为自己通过 torch::nn 组装的自定义模块(例如 torch::nn::Sequential 或多层 torch::nn::Linear),构造后在训练循环中依次调用 optimizer.zero_grad()loss.backward()optimizer.step() 即可完成一次自适应优化器的完整接入。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388