深度学习中优化器的Adafactor算法原理与自适应参数缩放机制
题目描述
Adafactor是一种高效的自适应优化算法,旨在减少Adam系列优化器(如Adam、AdamW)的内存占用,尤其适用于训练大型模型(如Transformer)。其核心思想是移除Adam中的动量变量,并通过对二阶矩估计进行低秩分解和参数特定缩放来逼近原始更新规则。本题将深入解析Adafactor的数学原理、实现细节及其如何通过自适应参数缩放平衡计算效率与性能。
解题过程
步骤1:回顾Adam优化器的标准形式
为理解Adafactor的改进,先回顾Adam的更新规则。对于参数θ,Adam维护两个动量变量:
- 一阶矩(均值)\(m_t\)
- 二阶矩(未中心化的方差)\(v_t\)
更新步骤为:
\[m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \]
\[ v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \]
\[ \hat{m}_t = m_t / (1-\beta_1^t), \quad \hat{v}_t = v_t / (1-\beta_2^t) \]
\[ \theta_{t+1} = \theta_t - \eta \cdot \hat{m}_t / (\sqrt{\hat{v}_t} + \epsilon) \]
其中\(g_t\)是梯度,\(\eta\)是学习率,\(\epsilon\)是小常数防止除零。
问题:对于参数量为\(n\)的模型,Adam需要存储两个与参数同形的动量张量,内存占用为\(O(2n)\)。在大型模型中(如十亿参数),这可能导致内存瓶颈。
步骤2:Adafactor的核心设计思想
Adafactor从两方面压缩内存:
- 移除一阶矩:直接使用梯度代替动量,即\(m_t = g_t\)。这牺牲了平滑性,但实验表明在训练后期影响较小。
- 对二阶矩进行低秩分解:将全尺寸的\(v_t\)分解为两个低秩矩阵的乘积,显著减少存储。
具体来说,考虑参数张量形状为\((r, c)\)的矩阵(高维张量可重塑)。标准Adam的\(v_t\)形状为\((r, c)\),而Adafactor将其近似为:
\[v_t \approx R_t \cdot C_t / \text{mean}(C_t) \]
其中\(R_t\)是形状\((r, 1)\)的行因子,\(C_t\)是形状\((1, c)\)的列因子。存储开销从\(O(rc)\)降至\(O(r+c)\)。
步骤3:二阶矩的低秩分解实现
Adafactor直接计算行均值与列均值作为低秩近似:
- 行均值:\(R_t(i) = \frac{1}{c} \sum_{j} g_t(i, j)^2\)(形状\((r, 1)\))
- 列均值:\(C_t(j) = \frac{1}{r} \sum_{i} g_t(i, j)^2\)(形状\((1, c)\))
则近似的二阶矩为:
\[\hat{v}_t(i, j) = R_t(i) \cdot C_t(j) \]
但直接乘积可能导致数值不稳定,因此进行归一化(使用均值):
\[\hat{v}_t(i, j) = R_t(i) \cdot C_t(j) / \left( \frac{1}{c} \sum_{k} C_t(k) \right) \]
这样既保持了尺度,又减少了存储。
更新规则:二阶矩通过指数移动平均(EMA)更新:
\[R_t = \beta_2 R_{t-1} + (1-\beta_2) [\text{row\_mean}(g_t^2)] \]
\[ C_t = \beta_2 C_{t-1} + (1-\beta_2) [\text{col\_mean}(g_t^2)] \]
注意:这里行/列均值计算的是当前梯度平方的均值,而非累积。
步骤4:自适应参数缩放(参数特定学习率)
Adafactor引入参数特定的缩放因子来补偿移除动量后的性能损失。更新公式为:
\[\theta_{t+1} = \theta_t - \eta \cdot g_t / (\sqrt{\hat{v}_t} + \epsilon) \]
其中\(\eta\)是全局学习率,\(\hat{v}_t\)为前述低秩近似。但为稳定训练,通常还添加:
- 相对步长限制:限制更新幅度不超过参数幅度的某个比例。
- 参数缩放系数:根据参数的历史幅度动态调整学习率,公式为:
\[\text{scale} = \min(\eta, \alpha \cdot \text{RMS}(\theta_t)) \]
其中\(\text{RMS}(\theta_t)\)是参数θ的均方根,\(\alpha\)是超参数(如0.01)。最终更新为:
\[\theta_{t+1} = \theta_t - \text{scale} \cdot g_t / (\sqrt{\hat{v}_t} + \epsilon) \]
这样,参数幅度较大时更新相对保守,较小则更新相对激进。
步骤5:Adafactor的完整算法流程
以矩阵参数为例,完整步骤为:
- 输入:参数矩阵\(\theta \in \mathbb{R}^{r \times c}\),梯度\(g_t\),全局学习率\(\eta\),衰减率\(\beta_2\),常数\(\epsilon_1, \epsilon_2\)。
- 计算梯度平方:\(g_t^2\)。
- 更新二阶矩分解:
- 计算行均值:\(r_t = \text{mean}(g_t^2, \text{dim}=1)\)(形状\((r, 1)\))
- 计算列均值:\(c_t = \text{mean}(g_t^2, \text{dim}=0)\)(形状\((1, c)\))
- 更新EMA:\(R_t = \beta_2 R_{t-1} + (1-\beta_2) r_t\),\(C_t = \beta_2 C_{t-1} + (1-\beta_2) c_t\)。
- 重构二阶矩估计:
- \(\hat{v}_t = R_t \cdot C_t / \text{mean}(C_t)\)(广播到\((r, c)\))。
- 计算参数特定缩放:
- \(\text{param\_scale} = \max(\epsilon_2, \text{RMS}(\theta_t))\)。
- \(\text{step\_size} = \eta \cdot \min(1, \alpha \cdot \text{param\_scale})\)。
- 执行更新:
- \(\theta_{t+1} = \theta_t - \text{step\_size} \cdot g_t / (\sqrt{\hat{v}_t} + \epsilon_1)\)。
对于高维张量,可重塑为矩阵处理(如将通道维度合并为行,空间维度合并为列)。
步骤6:与Adam的对比与优势
- 内存效率:Adafactor存储开销为\(O(n + r + c)\),远小于Adam的\(O(2n)\)。
- 计算开销:低秩分解引入额外均值计算,但整体仍近似线性复杂度。
- 性能表现:在机器翻译等任务中,Adafactor在达到相似性能的同时显著减少内存,尤其适合资源受限环境或超大模型。
- 超参数简化:通常固定\(\beta_2 = 0.999\),无需一阶矩的\(\beta_1\),简化调参。
步骤7:实际应用注意事项
- 学习率调度:Adafactor常与平方根衰减调度结合,如\(\eta_t = \eta_0 / \sqrt{t}\)。
- 数值稳定性:分母中的\(\epsilon_1\)防止除零(如1e-30),\(\epsilon_2\)防止参数缩放过小(如1e-3)。
- 一维参数处理:对于向量参数,可直接使用梯度平方的移动平均作为\(v_t\)(无需分解)。
总结
Adafactor通过移除一阶矩和对二阶矩进行低秩分解,实现了内存高效的自适应优化。其自适应参数缩放机制平衡了更新幅度,使训练稳定。该算法在Transformer等大型模型训练中广泛应用,是资源受限场景下的重要工具。理解其低秩近似与缩放原理有助于在实际任务中有效调优。