Optimizer
首先注意:优化和优化器是两个概念。
优化器主要分为一阶优化器和二阶优化器。
一阶优化器(First-Order Optimizers) 按照发展的先后顺序,主要有以下内容:
- 普通梯度下降:Gradient Descent (1847), SGD (1951)
- 结合动量法:Polyak Momentum (1964), Nesterov Momentum (1983)
- 结合Adaptive LR: AdaGrad (2011), RMSProp (2012), AdaDelta (2012)
- 同时结合动量和自适应梯度: Adam (2014), AdamW (2017)
二阶优化器(Second-Order Optimizers) 按照发展的先后顺序,主要有以下内容:
- 纯粹二阶:牛顿法 (17世纪),准牛顿法L-BFGS (1989)
- 近似二阶:KFAC (2015),AdaHessian (2020)
SGD
在优化理论的笔记中我们已经详细介绍了梯度下降和相关内容。由于SGD是后续优化器内容的基础,因此我们在这里再次回顾一下主要知识。
一维梯度下降
一维中的梯度下降给我们很好的启发。考虑一类连续可微实值函数 \(f : \mathbb{R} \to \mathbb{R}\),利用泰勒展开,我们可以得到
即在一阶近似中,\(f(x+\epsilon)\) 可通过 \(x\) 处的函数值 \(f(x)\) 和一阶导数 \(f'(x)\) 得出。
我们可以假设在负梯度方向上移动的 \(\epsilon\) 会减少 \(f\)。为了简单起见,我们选择固定步长 \(\eta>0\),然后取
将其代入泰勒展开式我们可以得到
如果其导数 \(f'(x)\neq 0\) 没有消失,我们就能继续展开,这是因为
此外,我们总是可以令 \(\eta\) 小到足以使高阶项变得不相关。因此,
这意味着,如果我们使用
来迭代 \(x\),函数 \(f(x)\) 的值可能会下降。
因此,在梯度下降中,我们首先选择初始值 \(x\) 和常数 \(\eta>0\),然后使用它们连续迭代 \(x\),直到停止条件达成。例如,当梯度 \(|f'(x)|\) 的幅度足够小或迭代次数达到某个值时。
下面我们来展示如何实现梯度下降。为了简单起见,我们选用目标函数
尽管我们知道 \(x=0\) 时 \(f(x)\) 能取得最小值,但我们仍然使用这个简单的函数来观察 \(x\) 的变化。
随机梯度更新
在深度学习中,目标函数通常是训练数据集中每个样本的损失函数的平均值。给定 \(n\) 个样本的训练数据集,我们假设 \(f_i(\mathbf{x})\) 是关于索引 \(i\) 的训练样本的损失函数,其中 \(\mathbf{x}\) 是参数向量。然后我们得到目标函数
\(\mathbf{x}\) 的目标函数的梯度计算为
如果使用梯度下降法,则每个自变量迭代的计算代价为 \(O(n)\),它随 \(n\) 线性增长。因此,当训练数据集较大时,每次迭代的梯度下降计算代价将较高。
随机梯度下降(SGD)可降低每次迭代时的计算代价。在随机梯度下降的每次迭代中,我们对数据样本随机均匀采样一个索引 \(i\),其中 \(i \in \{1, \dots, n\}\),并计算梯度 \(\nabla f_i(\mathbf{x})\) 以更新 \(\mathbf{x}\):
其中 \(\eta\) 是学习率。我们可以看到,每次迭代的计算代价从梯度下降的 \(O(n)\) 降至常数 \(O(1)\)。
此外,我们要强调,随机梯度 \(\nabla f_i(\mathbf{x})\) 是对完整梯度 \(\nabla f(\mathbf{x})\) 的无偏估计,因为
这意味着,平均而言,随机梯度是对梯度的良好估计。
SGD收敛条件
SGD 的收敛性对学习率的选择非常敏感。根据 Robbins-Monro 条件,SGD 的收敛需要学习率 \(\epsilon_k\) 满足以下两个条件:
第一个条件保证学习率之和发散,确保算法有足够的"能量"到达任意远的最优点;第二个条件保证学习率的平方和收敛,确保噪声的累积效应有界,算法最终能收敛。
注意: 常数学习率 \(\epsilon_k = \epsilon\) 不满足第二个条件(\(\sum \epsilon^2 = \infty\)),因此理论上常数学习率的 SGD 不保证收敛。这就是为什么实践中通常需要学习率衰减策略。常见的做法是在训练损失出现平台期(plateau)时将学习率减半或缩小为 \(\frac{1}{10}\)。
SGD的问题
SGD 的梯度方向并不总是指向最优解的方向。回忆二次"碗"(bowl)的例子,梯度方向通常并不直接指向最小值点 \((0,0)\),而是偏离的。偏离程度取决于问题的结构,具体来说取决于 Hessian 矩阵的条件数(condition number)。条件数越大,梯度方向与最优方向的偏差越大,收敛越慢。
动态学习率
用与时间相关的学习率 \(\eta(t)\) 取代 \(\eta\) 增加了控制优化算法收敛的复杂性。特别是,我们需要弄清 \(\eta\) 的衰减速度。如果太快,我们将过早停止优化。如果减少得太慢,我们会在优化上浪费太多时间。以下是随着时间推移调整 \(\eta\) 时使用的一些基本策略(稍后我们将讨论更高级的策略):
在第一个分段常数(piecewise constant)场景中,我们会降低学习率,例如,每当优化进度停顿时。这是训练深度网络的常见策略。或者,我们可以通过指数衰减(exponential decay)来更积极地降低它。不幸的是,这往往会导致算法在收敛之前过早停止。
一个受欢迎的选择是 \(\alpha = 0.5\) 的多项式衰减(polynomial decay)。在凸优化的情况下,有许多证据表明这种速率表现良好。
无放回抽样
在理论中,随机梯度下降假设我们从一个连续分布 \(p(x,y)\) 中抽取样本。对于有限的数据集,理论上我们应该从 \(n\) 个样本中"有放回"地随机抽取。然而,在工程实践中,我们通常是将 \(n\) 个样本打乱顺序(Shuffle),然后从头到尾走一遍。这叫"无放回抽样"。
更具体的说,我们通常把全局打乱(Shuffle)然后分批次(Batching)进行训练,我们把这个过程叫做"分批次"(Batching)。
实现 Batching 的标准工业流程通常包含以下四步:
- 打乱 (Shuffle) :在每个 Epoch 开始时,随机重新排列所有数据的索引。
- 分块 (Partition) :根据
batch_size\(B\),将索引序列切割成大小为 \(B\) 的连续小段。 - 打包 (Collate) :将读取到的 \(B\) 个样本(可能是图片、文本)堆叠(Stack)成一个 Tensor(张量)。例如:\(B\) 张 \((3, 224, 224)\) 的图片打包成一个 \((B, 3, 224, 224)\) 的矩阵。
- 计算 (Update) :对这一个 Batch 计算平均梯度,并更新模型参数。
如果Batch Size = 数据集总量n,那么就变成了全梯度下降;如果Batch Size = 1,那么就是最理论化的SGD。当然,在实际中我们一般选取Batch Size = 32, 64, 128... 这种取法也叫做"小批量梯度下降MB-SGD"。
下面简单论述一下为什么在实践中无放回抽样是可行的。
我们假设从分布 \(p(x,y)\) 中采样得到样本 \(x_i\)(通常带有标签 \(y_i\)),并且用它来以某种方式更新模型参数。
特别是,对于有限的样本数量,我们仅仅讨论了由某些允许我们在其上执行随机梯度下降的函数 \(\delta_{x_i}\) 和 \(\delta_{y_i}\) 组成的离散分布
但是,这不是我们真正做的。在本节的简单示例中,我们只是将噪声添加到其他非随机梯度上,也就是说,我们假装有成对的 \((x_i, y_i)\)。事实证明,这种做法在这里是合理的(有关详细讨论,请参阅练习)。
更麻烦的是,在以前的所有讨论中,我们显然没有这样做。相反,我们遍历了所有实例恰好一次。要了解为什么这更可取,可以反向考虑一下,即我们有替换地从离散分布中采样 \(n\) 个观测值。随机选择一个元素 \(i\) 的概率是 \(1/n\)。因此选择它至少一次的概率为
类似的推理表明,挑选一些样本(即训练示例)恰好一次的概率是
这导致与无替换采样相比,方差增加并且数据效率降低。因此,在实践中我们执行后者(这是D2L教材中的默认选择)。最后一点注意,重复采用训练数据集的时候,会以不同的随机顺序遍历它。
Batch Size
上面我们提到了,在实际应用中,我们使用小批量梯度下降,而非纯理论上的SGD。一般来说,在硬件条件允许的情况下,我们尽可能选取更大的Batch Size。限制Batch Size的硬件主要是显存,我们优先选 2 的幂次 (32, 64, 128...),因为 GPU 的硬件对齐优化(CUDA Core)在这些数值下效率最高。
这里要注意的是,当你把 batch_size 扩大 \(k\) 倍时,通常也要把学习率(Learning Rate)同步扩大 \(k\) 倍。 这是因为 Batch 越大,单次更新的梯度越"准"(噪音小),你可以步子迈得更大。
在实际应用中,我们一般选取128,这是大多数CNN和NLP任务的黄金起始点。然后我们可以观察Loss曲线:
- 如果Loss震荡的太厉害,完全不收敛,那么我们就尝试增大Batch或者减少学习率。
- 如果Loss下降的太慢,我们就尝试增大学习率。
此外,我们还可以用梯度累加(Gradient Accumulation) 技术:比如说,我们的硬件不够跑32的批量,我们就跑 4 次 batch=8,累计 4 次梯度后再更新一次,逻辑上等同于 batch=32。
Momentum
k-step方法统一框架
在介绍动量方法之前,我们先建立一个统一的理论框架:k-step 方法(Polyak, 1962)。
我们的目标是求解方程 \(\nabla J(\theta) = 0\)。这通常需要一个迭代数值算法,产生一个点的序列:
使之收敛到最优解 \(\theta^*\)。在这样的算法中,每个新的 \(\theta^{(t+1)}\) 是基于过去的迭代点 \(\theta^{(i)}\) 来更新的。
定义: 一个 k-step 方法是指 \(\theta^{(t+1)}\) 的更新依赖于前 \(k\) 个迭代点 \(\theta^{(t)}, \theta^{(t-1)}, \dots, \theta^{(t-k+1)}\)。
到目前为止我们讨论的 GD、SGD、AdaGrad 都是 1-step 方法。通过考虑 k-step 方法,算法的收敛可以随着 \(k\) 的增大而加速。但在实践中,加速方法主要使用 \(k=2\)。对于 \(k>2\),计算复杂度显著增加,超参数的选择变得更加困难,而收敛速度的提升却很小。
\(k=2\) 的方法被称为加速梯度下降(accelerated gradient descent)或动量法(momentum)。
一般 k-step 方法的更新公式(Polyak, 1962):
其中 \(\sum_{i=0}^{k-1} \beta_i = 1\)(保证稳定性)。
2-step 方法的更新公式为:
Polyak动量
动量(Momentum),有时候也称为Heavy Ball Method,其核心概念在1964年由苏联数学家Boris Polyak提出。最初这种方法并非用在SGD上,而是用在标准GD上。Polyak介绍了一种"heavy ball"概念,也就是数学上的动量,可以加速收敛,特别是在沟壑或者病态场景(ill-conditioned scenarios)下。
在狭长的病态峡谷中,SGD会来回震荡,前进缓慢。动量(Momentum)就是为了解决这个问题而生的。动量在更新参数的时候,不仅考虑当前计算出的梯度,还参考上一步移动的方向和速度。其效果就是,在峡谷两侧来回震荡的时候,一左一右的梯度会因为惯性的存在而相互抵消,从而抑制了震荡。而在平缓的谷底,梯度方向很稳定,惯性会不断积累,从而加速冲刺。
一句话记住就是,Polyak动量=梯度+惯性。
Polyak 动量的推导: Polyak 的方法是第一个引入动量的方法,它是一个 2-step 方法,更新公式为:
这与上面一般 2-step 方法的形式类似,只是只使用了一个梯度项。将其改写为:
定义 动量项 \(m_t = \theta^{t+1} - \theta^t\),则得到 Polyak 动量的标准形式:
其中 \(g_t = \nabla J(\theta^t)\),\(\beta\) 为动量系数(momentum factor),\(\alpha\) 为学习率。注意 1-step 方法(即普通 GD)只有 \(\theta^{t+1} = \theta^t - \alpha g_t\),没有动量项。
理解动量的直觉: 考虑梯度向量 \(g_t = [g_{t1}, g_{t2}, \dots, g_{tn}]^T\):
- 如果沿某个维度 \(i\),梯度值 \(g_{ti}\) 总是同号(方向一致),那么 \(m_{ti}\) 会因为不断累积同方向的变化而稳步增大,加速该方向的更新。
- 如果沿另一个维度 \(j\),梯度值 \(g_{tj}\) 在迭代中频繁变号(震荡),那么 \(m_{tj}\) 会因为正负相消而保持较小,抑制该方向的震荡。
SGD with Polyak Momentum 完整算法
给定: 学习率 \(\eta\),动量系数 \(\mu\),初始点 \(\theta_0, \theta_1\)
重复以下步骤:
- 采样一个小批量 \(\{x^{(1)}, x^{(2)}, \dots, x^{(m)}\}\)
- 计算梯度:\(\hat{g}_t \leftarrow \frac{1}{m} \sum_{i=1}^{m} \nabla \mathcal{L}(f(x^{(i)}; \theta); y^{(i)})\)
- 更新: - \(m_t \leftarrow \mu \cdot m_{t-1} - \eta \cdot \hat{g}_t\) (动量) - \(\theta_{t+1} \leftarrow \theta_t + m_t\) (参数)
- \(t \leftarrow t + 1\)
直到停止准则满足。 通常学习率和动量系数通过经验设定。
最优Polyak参数与条件数分析
回忆训练深度神经网络的目标是 \(\min_\theta J(\theta)\),其中 \(g = \nabla J(\theta)\) 是梯度向量,\(H = \nabla^2 J(\theta)\) 是 Hessian 矩阵。
设 \(m\) 和 \(M\) 分别是 Hessian 矩阵 \(H\) 的最小和最大特征值(或其界),即:
Polyak 选择的最优超参数为:
此时到最优点的距离满足:
条件数分析: 定义条件数 \(\rho = \frac{M}{m}\),它衡量了优化问题的"病态程度"。\(\rho\) 越大,\(J(\theta)\) 在某些方向上变化很剧烈而在其他方向上变化很缓慢(想象抛物面的等高线被拉长)。\(\rho\) 小则问题"良好"("圆"的等高线)。
SGD 与 Polyak 动量的收敛对比:
| SGD(1-step) | Polyak 动量(2-step) | |
|---|---|---|
| 更新规则 | \(\theta^{t+1} = \theta^t - \alpha g_t\) | \(m_t = \beta m_{t-1} - \alpha g_t\), \(\theta^{t+1} = \theta^t + m_t\) |
| 收敛界 | \(\|\theta^t - \theta^*\| \le c_1 \|\theta^0 - \theta^*\| (q_1 + \varepsilon)^t\) | \(\|\theta^t - \theta^*\| \le c_2 (\|\theta^0 - \theta^*\|^2 + \|\theta^1 - \theta^*\|)^{1/2} (q_2 + \varepsilon)^t\) |
| 最优收敛率 | \(q_1 = \frac{M-m}{M+m}\),\(\alpha = \frac{2}{M+m}\) | \(q_2 = \frac{\sqrt{M}-\sqrt{m}}{\sqrt{M}+\sqrt{m}}\) |
对于大条件数 \(\rho\):
因此要使距离缩小 \(e^k\) 倍,所需的迭代次数为:
- SGD:\(t = \frac{k}{\ln q_1} \approx \frac{k\rho}{2}\),即 \(O(\kappa)\)
- Polyak 动量:\(t = \frac{k}{\ln q_2} \approx \frac{k\sqrt{\rho}}{2}\),即 \(O(\sqrt{\kappa})\)
结论: 动量方法将收敛速度从 \(O(\kappa)\) 提升到了 \(O(\sqrt{\kappa})\),这在条件数很大的病态问题中是巨大的加速。
Nesterov动量
Nesterov Momentum(Nesterov's Accelerated Gradient, NAG)是在1983年由另一位苏联数学家Yurii Nesterov提出的(后由Sutskever于2013年将其引入深度学习)。
这是对Polyak动量的一个巧妙改进。其核心思想是,不急着在当前位置计算梯度,而是先假装按照现在的惯性往前走一步,在新的位置探探路,看看那里的梯度是什么样的,然后再结合惯性决定最终该怎么走。
一句话记住就是,Nesterov动量=预判梯度+惯性,比标准动量更聪明。
Polyak(经典)动量 vs Nesterov 动量的数学对比:
Polyak 动量在当前位置 \(\theta_t\) 计算梯度:
Nesterov 动量在前瞻位置 \(\theta_t + \mu m_{t-1}\) 计算梯度(look-ahead):
两者的关键区别在于梯度计算的位置不同。Polyak 在当前点 \(\theta_t\) 计算梯度,而 Nesterov 先沿动量方向"预看"一步到 \(\theta_t + \mu m_{t-1}\),再在该位置计算梯度。
注意: 在一些论文和教材中,向量 \(m_t\) 被称为 \(v_t\)(速度/velocity),但更新规则是相同的。
使用速度记号的等价形式:
| Polyak | Nesterov |
|---|---|
| \(v_t = \mu v_{t-1} - \eta \nabla J(\theta_t)\) | \(v_t = \mu v_{t-1} - \eta \nabla J(\theta_t + \mu v_{t-1})\) |
| \(\theta_{t+1} = \theta_t + v_t\) | \(\theta_{t+1} = \theta_t + v_t\) |
其中 \(\mu\) 为动量更新因子(momentum update factor),\(\eta\) 为学习率(learning rate)。
Polyak 与 Nesterov 的比较
- 当学习率 \(\eta\) 较小时,两种方法几乎等价,行为接近。
- 差异在 \(\eta\) 较大时才显现。设 \(\lambda_1 = \lambda_{\max}(\nabla^2 J)\) 为 Hessian 的最大特征值,当 \(\eta \cdot \lambda_1 < 1\) 时,Nesterov 比 Polyak 更"保守",在梯度变化大的方向上步长更小,因此更稳定。
- 但如果 \(\eta \cdot \lambda_1 > 1\),Nesterov 可能过于激进,导致发散。
等价性示例(Sutskever, 2013): 对于一维二次目标函数 \(J(\theta) = \frac{\lambda}{2}\theta^2 + c\theta\),梯度为 \(g(\theta) = \lambda\theta + c\)。Polyak 和 Nesterov 的更新在此情况下可以等价,当且仅当 Nesterov 的动量系数 \(\mu_N\) 与 Polyak 的动量系数 \(\mu_P\) 满足:
其中 \(\lambda\) 是来自目标函数 \(J(\theta)\) 的特征值。
Nesterov 收敛保证
如果 \(J(\theta)\) 是凸函数,Nesterov 方法(Nesterov, 1983)保证:
其中 \(C = 4L\|x_{-1} - x^*\|^2\)(\(x_{-1}\) 为初始点)。这是最优衰减速率。
这一收敛性通过选择特定的动量系数和学习率调度来实现:
Nesterov 证明了在固定 \(N = \lfloor 4\sqrt{M/m} \rfloor - 1\) 步之后,目标值至少减半:
这是与 Polyak 方法类似的线性收敛率(可能具有不同的常数斜率)。
Adaptive Gradient
动量是从方向上解决问题,而自适应梯度(Adaptive Gradient)是从步长上解决问题。
AdaGrad
AdaGrad(Adaptive Gradient Algorithm)由 Duchi 等人于2011年提出,是第一个为每个参数自适应调整学习率的优化算法。
核心直觉: 在训练过程中,不同参数的重要性和更新频率差异很大。对于稀疏特征(如 NLP 中的低频词),其对应的参数梯度很少出现非零值,一旦出现就应该给予较大的更新;而对于频繁更新的参数,应该逐渐减小其步长。AdaGrad 通过累积历史梯度的平方和来实现这一点:更新越频繁的参数,累积值越大,学习率就越小。
高层思想: AdaGrad 的设计思想是在保持接近 GD 方向的同时,通过最小化一个"近端函数"(proximal function),使得新的更新方向比原始 GD 更有效地最小化 \(J(\theta)\)。
AdaGrad 的近端方法推导
回忆 GD 的更新:\(x_{t+1} = x_t + (-\eta g_t)\),其中 \(v_t = -\eta g_t\) 是 GD 的更新向量。AdaGrad 想要找到一个"新"的更新方向:
其中 \(u_t\) 是新的更新向量,要求 \(u_t\) 在接近 \(v_t\) 的同时最小化一个近端函数:
这里 \(f(u)\) 是选定的凸函数,\(\frac{1}{2t}\) 控制 \(u_t\) 与 \(v_t\) 的"接近程度"。
对于 AdaGrad,选择 \(f(u_t) = \frac{1}{t}u_t^T H_t u_t\)(其中 \(H_t\) 是一个已知矩阵),则新的更新通过求解以下优化问题得到:
对 \(u_t\) 求导令其为零:\(\frac{2}{t}H_t u_t - 2v_t = 0\),得到闭式解:
即新的更新是通过将 GD 方向投影/旋转到由 \(H_t\) 定义的矩阵空间来得到的。
投影矩阵 \(H_t\) 的选择:
- 对角 \(H_t\)(常用):\(H_t = \delta I + \text{diag}(G_t)^{1/2}\)
- 完整 \(H_t\)(不常用):\(H_t = \delta I + G_t^{1/2}\)
其中 \(G_t = \sum_{k=1}^{t} g_k g_k^T\) 是梯度外积矩阵(gradient outer product matrix),\(\delta > 0\) 确保 \(H_t^{-1}\) 存在。
算法公式
AdaGrad 维护一个累积变量 \(G_t\),记录每个参数从训练开始到当前时刻的梯度平方和:
其中 \(g_t = \nabla f(\theta_t)\) 是第 \(t\) 步的梯度。参数更新规则为:
其中 \(\eta\) 是全局初始学习率,\(\epsilon\) 是一个很小的常数(如 \(10^{-8}\))用于防止除以零。注意这里的除法和平方根都是逐元素(element-wise)操作,即每个参数都有自己独立的 \(G_t\)。
写成逐维度的形式:
其中 \(G_{t,ii}\) 是矩阵 \(G_t\) 的对角元素(即第 \(i\) 维梯度的累积平方和),\(g_{t,i}\) 是第 \(i\) 维的梯度。
直觉理解: 可以把 \(\sqrt{G_t}\) 看作对每个参数的"历史活跃度"的度量。活跃度高的参数(梯度经常很大)会得到更小的有效学习率 \(\frac{\eta}{\sqrt{G_t}}\),而活跃度低的参数则保留较大的有效学习率。这种机制对处理稀疏数据(如推荐系统、NLP)特别有效。
AdaGrad 完整算法
给定: 学习率 \(\eta\),初始参数 \(\theta\)
重复以下步骤:
- 采样一个小批量 \(X_t = \{x^{(1)}, \dots, x^{(m)}\}\)
- 计算梯度估计:\(\hat{g}_t \leftarrow \frac{1}{m}\sum_{i=1}^{m} \nabla \mathcal{L}(f(x^{(i)}; \theta); y^{(i)})\)
- 计算梯度平方累积和:\(\hat{v}_t \leftarrow \hat{v}_{t-1} + \hat{g}_t^2\)(逐元素平方)
- 更新参数:\(\theta_t \leftarrow \theta_{t-1} - \eta \cdot \frac{\hat{g}_t}{\sqrt{\hat{v}_t + \delta}}\)(逐元素除法)
- \(t \leftarrow t + 1\)
直到停止准则/收敛。
AdaGrad 的性质
有效学习率的单调递减: 在 AdaGrad 中,有效步长或学习率在每一步都递减:
由于分母是梯度范数从第1步开始的累积,所以 \(\eta_{t+1} < \eta_t\),有效学习率严格递减。
主要缺陷: AdaGrad 的致命弱点在于学习率单调递减。由于 \(G_t\) 是梯度平方的累加和,它只增不减,导致有效学习率 \(\frac{\eta}{\sqrt{G_t}}\) 会随着训练不断缩小,最终趋近于零。在训练后期,学习率可能变得过小,使得模型几乎无法继续学习。这在非凸优化(如深度学习)中尤其致命,因为模型可能还远未到达一个好的局部最优点就已经"停滞"了。此外,过去较大的梯度值会持续强烈影响当前的更新。
收敛性: AdaGrad 对在线学习有很好的保证收敛界。收敛结果比 SGD 更强,因为其界 \(\epsilon_T\) 不依赖于步长、网络大小等许多因素(不同于 SGD 的收敛界)。但是,这些界依赖于梯度的方差——更大的方差导致更大的收敛界。
AdaGrad-norm 变体
AdaGrad-norm 是 AdaGrad 的一个变体。与逐元素除法不同,它只除以梯度向量的累积平方范数(一个标量值):
AdaGrad-norm 算法: 初始化学习率 \(\eta\),参数 \(\theta\),\(b_0 > 0\)
新增步骤:
- 计算累积平方范数:\(b_t^2 \leftarrow b_{t-1}^2 + \|\hat{g}_t\|_2^2\)(标量值)
- 更新参数:\(\theta_t \leftarrow \theta_{t-1} - \eta \cdot \frac{\hat{g}_t}{b_t}\)(除以累积范数标量)
性质: AdaGrad-norm 已被证明即使对于非凸目标函数也能收敛,即:
其中 \(\epsilon_T\) 以 \(\frac{1}{\sqrt{T}}\) 的速率趋向于零。收敛性在随机设定(概率收敛)和确定性设定(\(T \ge N\) 后)中都得到了证明。(唯一要求是 \(J(\theta)\) 有下界。)

RMSProp
RMSProp(Root Mean Square Propagation)由 Geoffrey Hinton 在2012年的 Coursera 课程中提出(未正式发表论文),是对 AdaGrad 学习率单调递减问题的直接修复。
核心思想: RMSProp 用梯度平方的指数移动平均(Exponential Moving Average, EMA)替代了 AdaGrad 中的梯度平方累加和。这意味着算法会"遗忘"久远的历史梯度,只关注近期的梯度信息,从而避免学习率不断缩小到零。
指数移动平均(EMA)的推导
注意 AdaGrad 如何通过累加所有历史梯度来激进地减小有效步长。一种缓解方法是逐渐"遗忘"较旧的梯度值,这引出了指数移动平均(EMA) 技术:
AdaGrad 的累积:\(\hat{v}_t \leftarrow \hat{v}_{t-1} + \hat{g}_t^2\)
EMA(即 RMSProp):\(\hat{v}_t \leftarrow \beta_t \hat{v}_{t-1} + (1 - \beta_t)\hat{g}_t^2\)
其中 \(\beta_t\) 是"遗忘因子"(forgetting factor)或权重因子。
展开 EMA 的递推式(假设 \(\beta_t = \beta\) 为常数):
由于 \(0 < \beta < 1\),\(\beta^t \to 0\)(当 \(t\) 增大时),所以旧的梯度值在求和中被逐渐削弱。
算法公式:
其中 \(\rho\) 是衰减率(decay rate),通常取 \(0.9\) 或 \(0.99\)。\(v_t\) 是梯度平方的指数移动平均值。
与 AdaGrad 的对比: AdaGrad 中 \(G_t = \sum_{\tau=1}^{t} g_\tau^2\) 是所有历史梯度平方的简单累加,只增不减;而 RMSProp 中 \(v_t\) 是加权平均,旧梯度的影响会以指数速度衰减(窗口大小约为 \(\frac{1}{1-\rho}\))。这使得 RMSProp 的有效学习率不会单调递减,能够在训练后期依然保持合理的学习速率。
为什么有效: RMSProp 的名字来源于 "Root Mean Square",即对近期梯度平方取均值再开根号。这个值可以看作近期梯度幅度的一个估计。用它来归一化当前梯度,相当于让每个参数的更新步长与其近期梯度的"典型大小"成反比,从而实现自适应学习率。
原始 RMSProp(Hinton)建议选择 \(\beta_t = 0.9\)(常数)。一些近期变体建议自适应调整 \(\beta_t\),例如 \(\beta_t = 1 - \frac{0.9}{t}\),同时也调整学习率为 \(\eta_t = \frac{\eta}{\sqrt{t}}\)。这些变化能获得更好的收敛性,但性能取决于具体的深度学习模型和数据集的复杂度。

RMSProp-norm 变体
类似于 AdaGrad-norm,RMSProp 也有一个 norm 变体:
RMSProp(向量形式):\(\hat{v}_t \leftarrow \beta\hat{v}_{t-1} + (1-\beta)\hat{g}_t^2\)(逐元素)
RMSProp-norm(标量形式):\(\hat{v}_t \leftarrow \beta\hat{v}_{t-1} + (1-\beta)\frac{\|g_t\|^2}{d}\)(标量,\(d\) 为 \(g_t\) 的维度)
两者的更新规则相同:\(\theta_t \leftarrow \theta_{t-1} - \eta \cdot \frac{g_t}{\sqrt{v_t + \delta}}\)
RMSProp-norm 在固定步长下对非凸目标函数也具有保证收敛性(在温和条件——有界下方——下)。

AdaDelta
AdaDelta 由 Zeiler 在2012年提出,是对 AdaGrad 衰减学习率问题的另一种修复方案,与 RMSProp 几乎同时独立提出。
核心特点: AdaDelta 不仅使用梯度平方的指数移动平均(与 RMSProp 类似),还进一步消除了对初始学习率 \(\eta\) 的需求。它通过额外维护一个参数更新量平方的指数移动平均来自动确定更新幅度。
算法公式:
首先计算梯度平方的 EMA(与 RMSProp 相同):
然后计算参数更新量:
最后更新参数更新量平方的 EMA:
注意分子中的 \(\sqrt{s_{t-1} + \epsilon}\) 替代了传统的学习率 \(\eta\),它由历史更新量的 RMS 自动决定。这使得 AdaDelta 完全不需要手动设置初始学习率,减少了一个超参数。
实际使用情况: 虽然 AdaDelta 在理论上很优雅,但在实践中 Adam 优化器的表现通常更好,因此 AdaDelta 的使用频率远低于 Adam 和 RMSProp。
各方法总结回顾
在进入 Adam 之前,让我们回顾一下目前讨论过的所有方法:
| 方法 | 需要的超参数 | 更新规则 | 关键特性 |
|---|---|---|---|
| GD / SGD | 学习率 \(\eta\),初始点 \(\theta^{(0)}\) | \(\theta^{t+1} = \theta^t - \eta g_t\) | 基准方法 |
| AdaGrad | 学习率 \(\eta\),初始点 \(\theta^{(0)}\) | \(v_t = v_{t-1} + g_t^2\); \(\theta^{t+1} = \theta^t - \eta\frac{g_t}{\sqrt{v_t+\epsilon}}\) | 累积梯度平方来自适应搜索方向 |
| RMSProp (EMA) | 学习率 \(\eta\),初始点 \(\theta^{(0)}\),遗忘因子 \(\beta\) | \(v_t = \beta v_{t-1} + (1-\beta)g_t^2\); \(\theta^{t+1} = \theta^t - \eta\frac{g_t}{\sqrt{v_t+\epsilon}}\) | 逐渐遗忘旧梯度值 |
| Polyak 动量 | 学习率 \(\eta\),动量系数 \(\mu\),2个初始点 \(\theta^{(0)}, \theta^{(1)}\) | \(m_t = \mu m_{t-1} - \eta g_t\); \(\theta^{t+1} = \theta^t + m_t\) | 引入动量项(一阶矩) |
| Nesterov (NAG) | 学习率 \(\eta\),动量系数 \(\mu\),2个初始点 \(\theta^{(0)}, \theta^{(1)}\) | \(m_t = \mu m_{t-1} - \eta g(\theta_t + \mu m_{t-1})\); \(\theta^{t+1} = \theta^t + m_t\) | 前瞻梯度 |
Adam
Adam = Momentum + Adaptive Gradient
Adam优化器
Adam(Adaptive Moment Estimation)由 Kingma 和 Ba 于2015年提出(arXiv:1412.6980, 2014)。它将之前所有方法的核心思想结合在一起:
- AdaGrad:累积梯度平方(也称为"二阶矩")
- RMSProp:指数移动平均
- Polyak:引入动量(也称为"一阶矩"),通过累积梯度
Adam 是将以上所有思想整合在一起的方法。
Adam 算法: 需要学习率 \(\eta\),动量系数 \(\mu\),两个遗忘因子 \(\beta_1, \beta_2\),两个初始点 \(\theta^{(0)}, \theta^{(1)}\)
其中 \(\beta_1\) 是动量因子(一阶矩),\(\beta_2\) 是"自适应"因子(二阶矩)。典型推荐值为 \(\beta_1 = 0.9\),\(\beta_2 = 0.999\),是非常大的(接近1的)动量系数。
偏差修正的数学推导
偏差修正是 Adam 相对于简单地结合 Momentum 和 RMSProp 的关键创新。让我们以二阶矩 \(v_t\) 为例推导偏差的来源。
从递推式开始,假设 \(v_0 = 0\):
一般地:
现在考虑在训练 DNN 时,\(g_t\) 是随机的,因此 \(v_t\) 也是随机的。对其取期望:
假设 \(\mathbb{E}[g_i^2] = \mathbb{E}[g_t^2]\)(所有步的梯度二阶矩相同),则:
因此:
这个 \(\frac{1}{1 - \beta_2^t}\) 就是偏差修正因子。偏差来源于 EMA 步骤——当 \(v_0 = 0\) 初始化时,早期的 \(v_t\) 会系统性地偏小。修正后的 \(\hat{v}_t = \frac{v_t}{1-\beta_2^t}\) 是对真实二阶矩 \(\mathbb{E}[g_t^2]\) 的无偏估计。同理,\(\hat{m}_t = \frac{m_t}{1-\beta_1^t}\) 是对真实一阶矩的无偏估计。
Adam 超参数的影响
实验表明(参见下图),在 Adam 的三个关键超参数中:
- 学习率 \(\alpha\)(即 \(\eta\))对性能影响最大,需要仔细调整
- \(1 - \beta_1\) 的影响次之
- \(1 - \beta_2\) 的影响相对较小,对其值不太敏感

Adam 与其他方法的比较
下图展示了 Adam 与 AdaGrad、SGDNesterov、RMSProp、AdaDelta 在不同任务上的训练代价对比(Kingma, 2014)。Adam 通常能够更快地降低训练代价。

Adam 的收敛性质
Adam 和 AdaGrad 的收敛性质对比(Defossez, 2022):
| 方法 | 设置 | 收敛率 |
|---|---|---|
| 原始 AdaGrad | \(\beta_1 = 0\),固定学习率 \(\eta\) | \(\mathbb{E}[\|g(\theta_N)\|^2] \sim O\left(\frac{1}{\sqrt{N}}\log N\right)\) |
| AdaGrad + 自适应学习率 | \(\eta_t = \frac{\eta}{t}\sqrt{\frac{1-\beta_2^t}{1-\beta_2}}\) | \(\mathbb{E}[\|g(\theta_N)\|^2] \sim O\left(\frac{1}{N}\right)\) |
| Adam + 固定学习率 | \(\beta_1 \ne 0\),\(\eta\) 固定 | \(\mathbb{E}[\|g(\theta_N)\|^2] \sim O\left(\frac{1}{\sqrt{N}}\log N\right)\) |
| Adam + 自适应学习率 | \(\beta_1 \ne 0\),\(\eta_t = \eta(1-\beta_1)\sqrt{\frac{1-\beta_2^t}{1-\beta_2}}\) | \(\mathbb{E}[\|g(\theta_N)\|^2] \sim O\left(\frac{1}{N}\right)\) |
使用自适应学习率调度可以将收敛率从 \(O(\frac{1}{\sqrt{N}}\log N)\) 提升到 \(O(\frac{1}{N})\)。
Adam 的不收敛问题与 AMSGrad
Adam 可能不收敛(Reddi et al., 2019):即使对于凸目标函数,Adam 也可能导致不收敛。问题的根源在于 EMA 步骤。
在 SGD 和 AdaGrad 等非 EMA 算法中,有效学习率是单调不增的:
- 对于 SGD(自适应学习率 \(\eta_t = \frac{\eta}{\sqrt{t}}\)):\(\gamma_t = \frac{1}{\eta_{t+1}} - \frac{1}{\eta_t} = \frac{1}{\eta}(\sqrt{t+1} - \sqrt{t}) > 0\)
- 对于 AdaGrad(\(v_t = \sum_{i=1}^{t} g_i^2\)):\(\gamma_{t+1} = \frac{1}{\eta}\left(\sqrt{\sum_{i=1}^{t+1}g_i^2} - \sqrt{\sum_{i=1}^{t}g_i^2}\right) > 0\)
但对于 EMA 方法(RMSProp、Adam),有效学习率可能会增加,这可能导致不收敛。
AMSGrad(修正版 Adam): 核心思想是保留迄今为止 \(v_t\) 的最大值:
AMSGrad 已被证明能保证收敛。

Adam 超参数注意事项
通常收敛性证明要求:
- \(\beta_1 < \beta_2\),且 \(\beta_1 < \sqrt{\beta_2}\)(希望二阶矩上有更强的动量,因为它涉及 \(g_t^2\))
- 学习率调度:\(\alpha_t = \frac{\alpha}{\sqrt{t}}\) 或 \(\eta_t = \frac{\eta}{\sqrt{t}}\)
对于凸和非凸目标函数的不同超参数区域,存在多种收敛证明(参见 Reddi 2018, Defossez 2022, Bock 2018, Zou 2019)。
Nadam
Nadam(Nesterov-accelerated Adaptive Moment Estimation)由 Dozat 于2016年提出,是将 Adam 与 Nesterov 动量结合的方法。
动机: 回忆 Polyak 动量的更新:
将两步合并:\(\theta^{t+1} = \theta^t + (\mu m_{t-1} - \eta g_t)\)
在 Polyak 中,\(g_t\) 和 \(m_{t-1}\) 是不相关的。而在 Nesterov 中,使 \(g_t\) 依赖于 \(m_{t-1}\):
Nesterov vs Nadam 的区别:
| Nesterov | Nadam |
|---|---|
| \(g_t \leftarrow \nabla J(\theta_t + \mu m_{t-1})\) | \(g_t \leftarrow \nabla J(\theta_t)\) |
| \(m_t \leftarrow \mu m_{t-1} - \eta g_t\) | \(m_t \leftarrow \mu m_{t-1} - \eta g_t\) |
| \(\theta^{t+1} \leftarrow \theta^t + m_t\) | \(\theta^{t+1} \leftarrow \theta^t + (\mu m_t - \eta g_t)\) |
注意这两种算法不完全相同。Nesterov 在梯度计算处应用前瞻,而 Nadam 在参数更新处应用新的动量。
在 Adam 框架下,这导致偏差修正后的一阶矩 \(\hat{m}_t\) 的计算发生变化:
Adam:\(\hat{m}_t \leftarrow \frac{m_t}{1 - \beta_1^t}\)
Nadam:\(\hat{m}_t \leftarrow \frac{\beta_1 m_t}{1 - \beta_1^{t+1}} + \frac{(1-\beta_1)g_t}{1 - \beta_1^t}\)

AdamW
二阶优化器
二阶优化器(Second-Order Optimizers) 按照发展的先后顺序,主要有以下内容:
- 纯粹二阶:牛顿法 (17世纪),准牛顿法L-BFGS (1989)
- 近似二阶:KFAC (2015),AdaHessian (2020)
如果说 SGD 是一阶优化(只看当前的"坡度"),那么二阶优化就是既看"坡度"(梯度),又看"坡度的变化率"(曲率)。
牛顿法
牛顿法是二阶优化的基础。它通过泰勒级数展开到二阶来近似目标函数。其核心思想是在当前点寻找一个二次曲面来拟合目标函数,并直接跳到该二次曲面的极小值点。
其中 \(H\) 是 Hessian 矩阵 (二阶导数矩阵),\(\nabla f\) 是梯度(一阶导数)。
AdaHessian
由于标准牛顿法在深度学习中不可行,AdaHessian(An Adaptive Second-order Optimizer) 是近年来提出的一种非常流行的"近似二阶优化器"。它试图结合 Adam 的自适应性和 二阶优化的曲率信息 。在某些复杂任务(如 Transformer 或视觉模型)中,AdaHessian 往往比 Adam 能达到更好的泛化效果,且对超参数不那么敏感。