统一记号:

  • 目标函数(loss):L(θ)L(θ),参数向量:θθ
  • tt 次迭代(iteration)参数:θtθ_t
  • 梯度:gt=θL(θt)g_t = ∇_{θ}\,L(θ_t)
  • 学习率:ηη(或 αα
  • mini-batch:Bt\mathcal{B}_t,其梯度估计记为 gt=θLBt(θt)g_t=∇_{θ}\,L_{\mathcal{B}_t}(θ_t)

P1:优化器

优化器是在反向传播给出梯度之后,决定“参数往哪里走、走多远”的更新规则,一个最常见的形式是损失最小化:

minθ  L(θ)=1Ni=1N(f(xi;θ),yi)\min_{θ}\; L(θ)=\dfrac{1}{N}\sum_{i=1}^{N} \ell\big(f(x_i;θ),y_i\big)

反向传播(backprop)负责高效地算出梯度 θL\nabla_\theta\mathcal{L};优化器负责把梯度变成一次次参数更新。

P2:Vanilla GD(批量梯度下降,Batch Gradient Descent)

最朴素的梯度下降每次用全量数据计算精确梯度,然后沿负梯度更新:

θt+1=θtηθL(θt)θ_{t+1}=θ_t-η\,∇_{θ}L(θ_t)

优点:

  • 梯度精确、更新方向稳定;对凸问题有清晰理论。

缺点:

  • 每步都要扫全数据集,代价高;大模型/大数据下几乎不可用。
  • 对非凸深网也不具备“逃离坏点”的随机性。

P3:SGD(随机梯度下降,Stochastic Gradient Descent)

SGD 每次只用一个样本(或极小 batch)来计算梯度:

θt+1=θtηθ(f(xit;θt),yit)θ_{t+1}=θ_t-η\,∇_{θ}\ell\big(f(x_{i_t};θ_t),y_{i_t}\big)

优点:

  • 单步开销小、更新频繁;在大数据下非常高效。
  • 梯度噪声有时反而有益:更容易“抖”出鞍点或糟糕的局部区域。

缺点(直觉上也最常见):

  • 更新噪声大,loss 曲线抖动明显。
  • 在狭长沟壑(某些维度很陡、某些维度很平)中容易来回震荡。
  • 在鞍点附近(一个方向上凸、另一个方向上凹)可能进展很慢。

图 a) 中,起点的位置会影响梯度下降的结果:在点1,3处,参数梯度下降到了全局最小值,而在2处,错误的起点使其下降到了局部最小值。在图b)中,SGD的更新方向由于噪声的存在而变得不稳定,从而使得参数能够跳出鞍点区域,继续向下优化。

P4:Mini-batch SGD

实践中最常见的是 mini-batch SGD:用一个小批量 Bt\mathcal{B}_t 估计梯度:

gt=θLBt(θt),θt+1=θtηgtg_t=∇_{θ}L_{\mathcal{B}_t}(θ_t),\qquad θ_{t+1}=θ_t-η\,g_t

它介于 GD 和 SGD 之间:

  • batch 变大 → 梯度方差更小、更新更稳,但单步更贵。
  • batch 变小 → 更新更“吵”,探索更强,但可能更难调、吞吐不一定更高。

工程里通常按硬件吞吐与收敛稳定性折中选 batch size;并配合学习率缩放(例如线性缩放)与 warmup。

P5:SGD with Momentum(动量法)

动量法的核心是引入“速度”变量 vv(一阶动量),对梯度做指数滑动平均,从而减少震荡并加速沿一致方向的前进:

vt+1=μvtηgtv_{t+1}=μv_t-η\,g_t

θt+1=θt+vt+1θ_{t+1}=θ_t+v_{t+1}

其中 μ[0,1)\mu\in[0,1) 是动量系数,常见经验值是 0.9。

优点:

  • 在“长沟壑”场景明显加速;抑制来回震荡。
  • 比 vanilla SGD 更稳、更好调。

缺点:

  • 仍可能在某些非凸区域走得过头(overshoot)。
  • 额外超参数(μ\mu)与更复杂的调度策略。

直观上看无动量的随机梯度下降以非直接的路径接近最小值,而有动量的随机梯度下降则由前一段优化方向和当前batch的梯度加权组合产生,可以平滑轨迹加快收敛。

P6:Nesterov Momentum(NAG,Nesterov 加速梯度)

Nesterov 的直觉是“先按动量往前看一步,再在那个位置上评估梯度”,因此更像是在用更前瞻的梯度修正动量方向。

一种常见写法是:

gt=θL(θt+μvt)g_t = ∇_{θ}L(θ_t+μv_t)

vt+1=μvtηgt,θt+1=θt+vt+1v_{t+1}=μv_t-η\,g_t,\qquad θ_{t+1}=θ_t+v_{t+1}

优点:

  • 在很多任务上比普通 Momentum 稍稳,收敛更“聪明”。

缺点:

  • 仍需调学习率与动量。

Second-order methods(二阶方法)

深度学习优化里还有一类很常见的思路是基于 Newton’s method(二阶方法),其核心迭代为:

xx[Hf(x)]1f(x)x \leftarrow x - [H_f(x)]^{-1} \nabla f(x)

其中 Hf(x)H_f(x) 是 Hessian 矩阵(由二阶偏导数组成的方阵),f(x)\nabla f(x) 是梯度向量。直觉上,Hessian 描述了损失函数的局部曲率:乘上 Hessian 的逆,会让优化在“曲率较小(更平坦)”的方向迈更大步,在“曲率较大(更陡)”的方向迈更小步。注意这个更新式里没有显式学习率超参数,这也是二阶方法常被拿来对比一阶方法的一个优点。

但在深度学习里直接计算并求逆 Hessian 往往不可行:时间和空间开销都极其昂贵。比如一个有 100 万参数的网络,Hessian 是 [1,000,000×1,000,000][1{,}000{,}000\times 1{,}000{,}000] 的矩阵,显式存储就需要约 3725 GB 内存。

因此出现了大量 quasi-Newton(拟牛顿)方法来近似 [Hf(x)]1[H_f(x)]^{-1}。其中最常见的是 L-BFGS:它利用一段时间内的梯度信息来隐式构造近似(不显式存整张矩阵)。

但即使缓解了内存问题,L-BFGS 的一个朴素应用仍然通常需要在全量训练集上工作;而把它可靠地用在 mini-batch 上更困难,至今仍是研究/工程上的活跃方向。

所以在大规模深度学习与 CNN 训练中,目前更常见的仍是基于(Nesterov)Momentum 的 SGD 变体:实现更简单、扩展性更好。

P7:AdaGrad(自适应学习率:累积平方梯度)

AdaGrad 的动机是:不同参数维度的更新频率差异很大(例如稀疏特征/embedding),希望“常更新的参数走慢点,少更新的参数走快点”。

做法:对每个参数维护平方梯度累积 ss

st+1=st+gt2s_{t+1}=s_t+g_t^2

θt+1=θtηgtst+1+εθ_{t+1}=θ_t-η\,\dfrac{g_t}{\sqrt{s_{t+1}}+ε}

其中 gt2g_t^2\sqrt{\cdot}、除法都是逐元素(element-wise),ϵ\epsilon 防止除 0。

优点:

  • 在稀疏特征场景非常强;对每个参数都有自适应步长。

缺点:

  • sts_t 单调增大,导致有效学习率单调衰减,训练后期可能“走不动”。

P8:RMSProp(自适应学习率:滑动平均平方梯度)

RMSProp 可以看作对 AdaGrad 的“降温”:不再把所有历史平方梯度都累积进去,而是用指数滑动平均跟踪一个有限时间尺度内的平方梯度。远离当前时间点的梯度信息会被逐渐丢弃,从而避免学习率过快衰减的问题。

st+1=ρst+(1ρ)gt+12s_{t+1}=ρs_t+(1-ρ)g_{t+1}^2

θt+1=θtηgt+1st+1+εθ_{t+1}=θ_t-η\,\dfrac{g_{t+1}}{\sqrt{s_{t+1}}+ε}

其中 ρ\rho 常取 0.9 或 0.99。

优点:

  • 避免 AdaGrad 学习率过快衰减;在很多深网任务上表现稳定。

缺点:

  • 仍需要调学习率;不同任务对 ρ\rho 也可能敏感。

P9:AdaDelta(自适应学习率:进一步减少对初始学习率依赖)

AdaDelta 也是“滑动窗口”的思想:它在 RMSProp 的基础上,进一步把更新步长做得更自洽,尽量减少对手工设定初始学习率的依赖。

一种常见的表达是同时跟踪:平方梯度的滑动平均 ss 与平方更新量的滑动平均 Δ\Delta

st+1=ρst+(1ρ)gt+12s_{t+1}=ρs_t+(1-ρ)g_{t+1}^2

Δθt=Δt+εst+1+εgt+1Δθ_t=-\dfrac{\sqrt{Δ_t+ε}}{\sqrt{s_{t+1}+ε}}\,g_{t+1}

Δt+1=ρΔt+(1ρ)(Δθt+1)2Δ_{t+1}=ρΔ_t+(1-ρ)(Δθ_{t+1})^2

θt+1=θt+Δθtθ_{t+1}=θ_t+Δθ_t

优点:

  • 经验上更不容易出现“有效学习率衰减到 0”的尴尬。

缺点:

  • 现代深度学习中常被 Adam/AdamW 取代;生态与默认配置更少。

P10:Adam(Adaptive Moment Estimation)

Adam 可以理解为:

  • Momentum:对梯度做一阶动量(均值)的滑动平均
  • RMSProp:对平方梯度做二阶动量(方差规模)的滑动平均

更新(逐元素)为:

mt+1=β1mt+(1β1)gt+1m_{t+1}=β_1 m_t+(1-β_1)g_{t+1}

vt+1=β2vt+(1β2)gt+12v_{t+1}=β_2 v_t+(1-β_2)g_{t+1}^2

由于 m0=v0=0m_0=v_0=0 会导致早期偏置(bias),使得初始动量过大,Adam 通常使用偏置修正:

m^t=mt1β1t,v^t=vt1β2t\hat{m}_{t}=\dfrac{m_{t}}{1-β_1^{t}},\qquad \hat{v}_{t}=\dfrac{v_{t}}{1-β_2^{t}}

初始时将 m^t\hat{m}_tv^t\hat{v}_t 放大,抵消 mtm_tvtv_t 靠近 0 的影响。随着时间推移,修正项趋近于1,对原始动量的影响减小。
最终更新:

θt+1=θtηm^t+1v^t+1+εθ_{t+1}=θ_t-η\,\dfrac{\hat{m}_{t+1}}{\sqrt{\hat{v}_{t+1}}+ε}

经验默认值常见为:β1=0.9,β2=0.999,ϵ=108\beta_1=0.9,\beta_2=0.999,\epsilon=10^{-8}

优点:

  • 收敛快、对学习率不那么敏感;新任务上很适合快速跑通 baseline。

潜在缺点(常见讨论点):

  • 在部分设置下可能出现训练后期震荡或泛化不如精调的 SGD 系列。

还有note上的动图:

P11:Nadam(Nesterov + Adam)

Nadam 把 Nesterov 的“lookahead”思想融入 Adam 的一阶动量中,本质上是用更前瞻的方式来计算动量项,从而在一些任务上获得更快或更稳的收敛。

一个常用、也比较容易和代码对齐的 Nadam(带 bias correction)可以写成:

先算 Adam 的动量与偏置修正:

mt+1=β1mt+(1β1)gtm_{t+1}=β_1 m_t+(1-β_1)g_t

vt+1=β2vt+(1β2)gt2v_{t+1}=β_2 v_t+(1-β_2)g_t^2

m^t+1=mt+11β1t+1,v^t+1=vt+11β2t+1\hat{m}_{t+1}=\dfrac{m_{t+1}}{1-β_1^{t+1}},\qquad \hat{v}_{t+1}=\dfrac{v_{t+1}}{1-β_2^{t+1}}

然后把 Nesterov 的“lookahead”体现在一阶动量上(把它看作一个更前瞻的一阶动量项):

mt+1(N)=β1m^t+(1β1)gt+11β1t+1m_{t+1}^{(N)}=β_1\hat{m}_{t}+\dfrac{(1-β_1)g_{t+1}}{1-β_1^{t+1}}

最终参数更新:

θt+1=θtηmt+1(N)v^t+1+εθ_{t+1}=θ_t-η\,\dfrac{m_{t+1}^{(N)}}{\sqrt{\hat{v}_{t+1}}+ε}

实现上常见做法是对 mm 的使用方式做 Nesterov 风格的修正(不同实现细节略有差异),但核心思想可以记作一句话:Adam 负责“自适应步长 + 动量”,Nadam 负责“在动量上再更前瞻一点”。

优点:

  • 有时比 Adam 更快、更稳。

缺点:

  • 并非总是优于 Adam;生态不如 AdamW 常用。

P12:AdamW(Decoupled Weight Decay)

在很多代码里你会看到把 L2 正则写进 loss:

L(θ)=L(θ)+λ2θ22L'(θ)=L(θ)+\dfrac{λ}{2}\lVert θ\rVert_2^2

这会让梯度多出一项 λθλθ

但对 Adam 这类自适应方法来说,“把 L2 正则并入梯度”与“做权重衰减(weight decay)”并不完全等价。
AdamW 的核心就是把权重衰减从梯度里解耦出来,直接在参数更新时做衰减:

θt+1=θtηm^t+1v^t+1+εηλθtθ_{t+1}=θ_t-η\,\dfrac{\hat{m}_{t+1}}{\sqrt{\hat{v}_{t+1}}+ε}-η\,λθ_t

优点:

  • 在大量任务里比 Adam + L2 更稳,泛化更好;现代训练中非常常用。

P13:学习率调度(Learning Rate Scheduler)

很多时候,“用什么优化器”不如“学习率怎么随时间变化”重要。一个常用经验是:

  • 前期需要更大的学习率来探索与快速下降
  • 后期需要更小的学习率来稳定收敛到更好点

下面列几类常见调度方式(它们可与上面任何优化器组合):

1)Step decay(阶梯衰减)

每隔若干 epoch 把学习率乘一个系数 γ\gamma

ηγηη\leftarrow γη

优点是简单、超参数好解释;缺点是学习率跳变可能带来波动。

2)Exponential decay(指数衰减)

ηt=η0ektη_t=η_0 e^{-kt}

更平滑,但需要合适的 kk

3)1/t1/t decay

ηt=η01+ktη_t=\dfrac{η_0}{1+kt}

衰减更温和,经典但不一定是最佳。

4)Cosine decay(余弦退火)

在现代训练(尤其是视觉任务)中非常常见:

ηt=ηmin+12(ηmaxηmin)(1+cosπtT)η_t=η_{\min}+\dfrac{1}{2}(η_{\max}-η_{\min})\left(1+\cos\dfrac{\pi t}{T}\right)

5)Warmup

无论用哪种 decay,大 batch 或大模型训练常常会在前几百/几千 step 用 warmup:

  • 从很小的学习率线性/非线性升到目标学习率
  • 减少初期不稳定与梯度爆炸风险