深度学习中优化器的Adafactor算法原理与自适应参数缩放机制
字数 2477 2025-12-14 21:16:30
深度学习中优化器的Adafactor算法原理与自适应参数缩放机制
题目描述
Adafactor是一种内存高效的优化算法,专门为训练超大规模模型(如具有数十亿参数的Transformer)设计。它通过自适应参数缩放和替代动量机制,在几乎不损失性能的前提下,显著减少了优化器状态的内存占用。题目要求深入讲解Adafactor的核心思想、数学原理、实现细节及其如何通过分解二阶矩估计来实现内存优化。
解题过程
一、问题背景与动机
- 内存瓶颈:传统自适应优化器(如Adam)需要为每个参数存储两个状态变量:一阶矩(动量)和二阶矩(方差估计)。对于参数量为 \(P\) 的模型,这需要 \(2P\) 个额外状态,在超大模型中成为内存瓶颈。
- Adafactor目标:设计一种优化器,在保持自适应学习率优点的同时,将状态内存从 \(O(P)\) 减少到 \(O(P^{2/3})\) 或更低,适用于内存受限的场景(如训练百亿参数模型)。
二、Adafactor的核心思想
- 分解二阶矩:将全尺寸的逐参数二阶矩矩阵分解为两个低秩因子矩阵的乘积,从而用更少的内存近似原矩阵。
- 移除动量:直接使用梯度的缩放版本进行更新,避免存储一阶矩,进一步减少内存。
- 自适应学习率:基于梯度的RMS(均方根)动态调整每个参数的学习率,保持对稀疏梯度的适应性。
三、算法原理与推导
步骤1:二阶矩的因式分解
- 在Adam中,二阶矩 \(v_t \in \mathbb{R}^P\) 为逐参数向量。
- Adafactor假设参数可排列为矩阵形式 \(W \in \mathbb{R}^{m \times n}\)(例如,全连接层权重)。对于该矩阵,维护两个因子:
- 行因子 \(R \in \mathbb{R}^{m \times 1}\)(每行的RMS)
- 列因子 \(C \in \mathbb{R}^{1 \times n}\)(每列的RMS)
- 近似二阶矩矩阵:\(V \approx R \cdot C\)(外积),内存从 \(m \times n\) 降至 \(m + n\)。
- 对于一般参数张量,可沿多个维度分解,实现 \(O(P^{2/3})\) 内存。
步骤2:更新规则推导
- 定义梯度矩阵 \(G_t \in \mathbb{R}^{m \times n}\) 在时间步 \(t\)。
- 计算缩放的二阶矩估计:
\[ \hat{v}_t(i,j) = \text{RMS}(R_t(i)) \cdot \text{RMS}(C_t(j)) \cdot \text{clip}(\text{其他衰减项}) \]
其中RMS操作计算历史梯度的均方根。
- 学习率调整:参数 \((i,j)\) 的学习率为 \(\eta / \sqrt{\hat{v}_t(i,j) + \epsilon}\),\(\eta\) 为全局学习率。
- 更新公式:
\[ W_{t+1} = W_t - \eta \cdot \frac{G_t}{\sqrt{\hat{v}_t} + \epsilon} \]
注意:这里直接使用当前梯度 \(G_t\),而非动量累积的梯度。
步骤3:可选动量机制
- 为加速训练,可引入简单的动量项:
\[ m_t = \beta_1 m_{t-1} + (1-\beta_1) G_t \]
但为节省内存,\(m_t\) 也可用低秩近似或较小 \(\beta_1\)。
四、关键组件详解
-
RMS计算:
- 对于向量 \(x\),RMS定义为 \(\sqrt{\frac{1}{n} \sum_i x_i^2}\)。
- 在Adafactor中,沿行和列分别计算历史梯度的RMS,模拟二阶矩的衰减平均。
-
学习率裁剪与预热:
- 为避免更新步长过大,对学习率进行裁剪:\(\text{lr} = \min(\text{lr}_{\text{calc}}, \text{lr}_{\text{max}})\)。
- 训练初期使用学习率预热(例如,线性增加)以提高稳定性。
-
相对更新量控制:
- 引入参数更新量的RMS与参数RMS的比值约束,防止个别参数更新幅度异常。
五、算法步骤总结
- 初始化:设置全局学习率 \(\eta\),衰减因子 \(\beta_2\)(用于RMS计算),小常数 \(\epsilon\)。
- 对于每个参数矩阵 \(W \in \mathbb{R}^{m \times n}\):
a. 计算当前梯度 \(G_t\)。
b. 更新行因子 \(R_t\) 和列因子 \(C_t\) 基于 \(G_t\) 的平方的指数移动平均。
c. 估计二阶矩 \(\hat{v}_t = R_t \otimes C_t\)(外积)。
d. 计算自适应学习率矩阵:\(\text{lr}_t = \eta / \sqrt{\hat{v}_t + \epsilon}\)。
e. 执行更新:\(W_{t+1} = W_t - \text{lr}_t \odot G_t\)(\(\odot\) 为逐元素乘)。 - 重复直至收敛。
六、优势与局限性
- 优势:
- 内存效率高:状态内存减少约一个数量级。
- 适用于大规模分布式训练和内存受限设备。
- 在机器翻译、语言建模等任务中表现接近Adam。
- 局限性:
- 无动量可能使收敛速度略慢于Adam。
- 超参数调整(如衰减因子)对性能敏感。
- 分解近似可能在某些任务中引入偏差。
七、实现示例(伪代码)
class Adafactor:
def __init__(self, params, lr=1e-3, beta2=0.999, eps=1e-8):
self.lr = lr
self.beta2 = beta2 # 用于RMS计算的衰减率
self.eps = eps
self.state = {} # 存储行/列因子
def step(self):
for param in params:
grad = param.grad
shape = grad.shape
if len(shape) == 2: # 矩阵参数
m, n = shape
# 初始化状态
if param not in self.state:
self.state[param] = {
'row_rms': torch.zeros(m),
'col_rms': torch.zeros(n)
}
state = self.state[param]
# 更新行/列RMS估计
state['row_rms'] = self.beta2 * state['row_rms'] + (1-self.beta2) * grad.pow(2).mean(dim=1)
state['col_rms'] = self.beta2 * state['col_rms'] + (1-self.beta2) * grad.pow(2).mean(dim=0)
# 计算二阶矩近似
v_hat = torch.outer(state['row_rms'].sqrt(), state['col_rms'].sqrt())
# 自适应学习率
lr_t = self.lr / (v_hat.sqrt() + self.eps)
# 参数更新
param.data -= lr_t * grad
总结
Adafactor通过分解二阶矩估计和移除显式动量,实现了内存高效的自适应优化。其核心在于用行和列因子的外积近似全二阶矩矩阵,大幅降低了存储开销,使其成为训练超大规模深度学习模型的实用选择。理解其数学近似和更新机制,有助于在内存受限场景下有效应用该优化器。