深度学习中的优化器之SGD with Lookahead算法原理与实现细节
题目描述
在深度学习优化算法中,SGD with Lookahead 是一种新颖的优化器框架,它通过引入“前瞻”机制,在常规优化器(例如SGD、Adam等)的基础上,维护两组参数:一组是“快速权重”,用于执行常规优化更新;另一组是“慢速权重”,用于周期性地对快速权重进行平滑平均。这种方法旨在提高训练的稳定性、收敛速度和泛化性能,尤其适用于存在较大梯度噪声或损失函数地形复杂的场景。
解题过程
1. 核心思想
Lookahead 不依赖于特定的基础优化器,而是作为一个包装器,在基础优化器每次更新后,周期性地对基础优化器更新的参数(快速权重)和一组“慢速权重”进行线性插值,从而得到一个更平滑、更稳定的参数更新轨迹。其核心动机是:基础优化器的更新可能会在损失表面上快速波动,而 Lookahead 通过对快速权重进行滑动平均,能够抑制振荡,从而更稳定地朝着更优区域前进。
2. 算法步骤详解
步骤 1: 符号定义
- 基础优化器(inner optimizer):例如 SGD、Adam 等,负责执行每一步的参数更新。
- 快速权重(fast weights):基础优化器直接维护和更新的参数,记为 \(\theta\)。
- 慢速权重(slow weights):Lookahead 维护的另一组参数,记为 \(\phi\)。
- 前瞻步数(lookahead steps):在每次慢速权重更新之前,基础优化器执行的步数,记为 \(k\)。
- 慢速权重更新系数(slow weights update coefficient):控制慢速权重更新幅度的超参数,记为 \(\alpha\)(通常取 0.5)。
步骤 2: 算法流程
- 初始化:将快速权重 \(\theta\) 和慢速权重 \(\phi\) 初始化为相同的值,通常为基础优化器所需的初始参数。
- 内循环(快速权重更新):对于每个训练步 \(t\):
- 基础优化器基于当前快速权重 \(\theta_t\) 计算梯度,并更新快速权重:
\[ \theta_{t+1} = \theta_t - \eta \nabla L(\theta_t) \]
其中 $ \eta $ 是基础优化器的学习率,$ L $ 是损失函数。
- 如果当前步数 \(t\) 是 \(k\) 的倍数,则执行步骤 3;否则继续内循环。
- 外循环(慢速权重更新):每经过 \(k\) 步快速权重更新,执行一次慢速权重更新:
- 将慢速权重 \(\phi\) 向当前的快速权重 \(\theta_{t+1}\) 进行线性插值:
\[ \phi_{\text{new}} = \phi + \alpha (\theta_{t+1} - \phi) \]
也可以写成:
\[ \phi_{\text{new}} = (1 - \alpha) \phi + \alpha \theta_{t+1} \]
- 将快速权重 \(\theta_{t+1}\) 同步为新的慢速权重 \(\phi_{\text{new}}\):
\[ \theta_{t+1} \leftarrow \phi_{\text{new}} \]
这一步确保了快速权重从平滑后的位置继续更新,而不是从可能振荡的位置重新开始。
步骤 3: 重复迭代
- 重复步骤 2 和步骤 3 直到训练结束。最终使用的模型参数是慢速权重 \(\phi\),因为它代表了更稳定、更平滑的优化路径。
3. 数学直观解释
- 快速权重 \(\theta\) 类似于常规优化过程,可能会在损失表面上快速移动,甚至产生振荡。
- 慢速权重 \(\phi\) 通过对快速权重的滑动平均,平滑了更新轨迹。这类似于Polyak平均,但 Lookahead 是在线进行的,不需要保存历史参数。
- 更新公式 \(\phi_{\text{new}} = (1 - \alpha) \phi + \alpha \theta\) 可以看作是将慢速权重向快速权重方向“拉动”,但只移动一小步(由 \(\alpha\) 控制)。当 \(\alpha\) 较小时,慢速权重更新缓慢,稳定性高;当 \(\alpha\) 较大时,慢速权重更紧跟快速权重,但可能引入更多波动。
4. 实现细节
伪代码实现:
# 超参数
k = 5 # 每k步更新一次慢速权重
alpha = 0.5 # 慢速权重更新系数
# 初始化
theta = initialize_parameters() # 快速权重
phi = theta.copy() # 慢速权重
inner_optimizer = SGD(theta, lr=0.01) # 基础优化器,可以是SGD、Adam等
for t in range(total_steps):
# 1. 基础优化器更新快速权重
loss = compute_loss(theta)
loss.backward()
inner_optimizer.step()
inner_optimizer.zero_grad()
# 2. 每隔k步更新慢速权重
if t % k == 0:
# 慢速权重更新
phi = phi + alpha * (theta - phi)
# 快速权重同步为慢速权重
theta.data.copy_(phi)
注意点:
- 基础优化器(如 SGD、Adam)通常只需要维护快速权重 \(\theta\) 的梯度状态(例如动量、二阶矩估计)。在慢速权重更新后,这些状态通常不重置,以保持优化器的历史信息。
- 在实现时,需要确保基础优化器的内部状态(如动量缓冲区)与快速权重 \(\theta\) 同步更新,避免状态不一致。
5. 算法优势与适用场景
- 稳定性提升:通过滑动平均抑制了基础优化器的更新振荡,损失下降更平滑。
- 泛化能力增强:慢速权重倾向于收敛到更平坦的极小值,这在理论上与更好的泛化性能相关。
- 超参数鲁棒性:对基础优化器的学习率、动量等超参数不那么敏感,因为 Lookahead 的平滑作用可以补偿不完美的超参数选择。
- 兼容性:可以与任何基础优化器结合,如 SGD、Adam、RMSprop 等,无需修改基础优化器的内部逻辑。
典型应用场景:
- 训练深度神经网络时,当损失函数曲面复杂、梯度噪声大时。
- 需要快速收敛且避免陷入尖锐极小值的任务。
- 与大型 batch size 训练结合,以保持稳定性。
总结
SGD with Lookahead 通过在基础优化器之上增加一个“前瞻”的外循环,周期性地对快速权重进行滑动平均,产生一组更平滑的慢速权重。这种方法不依赖于特定优化器,实现简单,能有效提升训练稳定性和泛化性能。理解其核心在于把握“快速权重探索、慢速权重平滑”的双重更新机制,以及超参数 \(k\) 和 \(\alpha\) 对更新频率和幅度的影响。