深度学习模型组件之优化器-结合 Adam 与 Nesterov 加速(Nadam)
文章目录
近年来,深度学习模型的训练离不开高效的优化算法。从最基础的随机梯度下降( SGD)到 Momentum、AdaGrad、RMSProp,再到 Adam,优化算法不断演进以适应大规模、高维非凸问题的挑战。 Nadam(Nesterov-accelerated Adaptive Moment Estimation) 正是在这一发展过程中出现的,它在 Adam 的基础上引入了 Nesterov 加速技术,从而融合了自适应学习率和预见性动量的优点。
1. 背景介绍
1.1 Adam 算法
Adam
由 Kingma
和 Ba
提出,结合了一阶矩(均值)和二阶矩(梯度平方的移动平均)来动态调整各参数的学习率,其更新公式如下:
公式解释参考:深度学习模型组件之优化器–自适应学习率优化方法(Adadelta、Adam、AdamW)
1.2 Nesterov 加速梯度(NAG)
Nesterov
动量(NAG
)与传统动量法不同,在更新时先沿着累积动量“预走”一步,再计算梯度,其更新过程为:
公式解释参考:深度学习模型组件之优化器–动量优化方法(带动量的 SGD 与 Nesterov 加速梯度)
2. Nadam 算法的原理与推导
Nadam
结合了 Adam 的自适应更新 和 Nesterov 动量加速,在参数优化时具有更好的收敛特性。其核心步骤包括梯度计算、动量更新以及最终的参数更新。下面对公式中的参数进行详细解释,并推导其更新过程。
2.1 梯度及动量更新
Nadam
采用 Adam
的一阶矩(动量项)和二阶矩(梯度平方的移动平均)进行更新:
-
- mt:当前时间步 ttt 的梯度一阶矩估计,即梯度的指数加权移动平均**,可以看作是梯度的平滑版本。
- β1:控制一阶矩的衰减率,通常设置为 0.9,使得历史梯度仍能影响当前更新。
- gt:当前时间步 ttt 的梯度。
-
- vt-1:梯度的二阶矩估计,即梯度平方的指数加权移动平均,用于调整学习率。
- β2:控制二阶矩的衰减率,通常设为 0.999,以减少梯度变化带来的影响。
-
- m^t 和 v^t:分别是对 mt和 vt 进行偏差校正后的值,避免初始阶段梯度估计值偏小的问题。
2.2 引入 Nesterov 修正
Nadam
的关键在于结合 Nesterov 预更新策略,即在计算梯度时,先沿动量方向前进一步,再计算梯度。为了实现这一点,Nadam
对 Adam
的一阶矩做了调整:
-
- m~t:结合了历史动量项和当前梯度的修正值,使得更新方向更具前瞻性。
这个调整可以理解为:相比于 Adam,Nadam 在计算动量项时提前考虑了梯度变化,从而加速收敛。
2.3 参数更新
最终,模型参数按照以下公式进行更新:
-
- θt:当前时间步 ttt 的参数值。
- η:全局学习率,决定每次更新的步长。
- v^t+ϵ:用于自适应调整学习率,防止分母为零(ϵ 一般设为10−8方)。
Nadam
通过引入 Nesterov 预更新策略,在 Adam 的基础上改进了动量更新方式,使优化过程更稳定,并在梯度变化较大的情况下表现更好。相比于 Adam
,它的收敛速度更快,泛化能力更强,但同时超参数调节更加复杂。
3. Nadam 的优缺点
优点
- 自适应性与动量结合:
Nadam
同时利用 Adam 的自适应学习率 和 Nesterov 预更新的优势,使梯度更新更为灵敏,从而加快收敛速度。 - 平滑更新:
Nadam
减少了参数更新过程中的震荡,在鞍点区域具有更好的稳定性。
缺点
- 超参数调节较复杂:
Nadam
需要设置学习率、β1 及 β2等超参数,不同任务下可能需要反复调试。 - 计算略有增加:与
Adam
相比,Nadam
额外引入了 Nesterov 预更新,虽然计算量变化不大,但实现上稍显复杂。
4. Nadam 代码示例
在深度学习任务中,Nadam 可以作为优化器来加速神经网络训练。以下是使用 Nadam
进行优化的 Python
代码示例:
PyTorch 使用 Nadam训练神经网络
import torch
import torch.nn as nn
import torch.optim as optim
# 定义简单的神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 50)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(50, 1)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
# 创建模型
model = SimpleNet()
# 定义损失函数
criterion = nn.MSELoss()
# 使用 Nadam 作为优化器
optimizer = optim.NAdam(model.parameters(), lr=0.001)
# 生成随机数据
x_train = torch.randn(100, 10)
y_train = torch.randn(100, 1)
# 训练模型
for epoch in range(100):
optimizer.zero_grad() # 清空梯度
outputs = model(x_train) # 前向传播
loss = criterion(outputs, y_train) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
if (epoch + 1) % 10 == 0:
print(f"Epoch [{epoch+1}/100], Loss: {loss.item():.4f}")
5. 总结
优化方法 | 优点 | 缺点 |
---|---|---|
梯度下降(GD) | 理论简单,更新方向稳定 | 计算量大,收敛速度慢,易陷入局部最优 |
随机梯度下降(SGD) | 更新速度快,适合大数据集,易跳出局部最优 | 更新过程有噪声,收敛路径不平滑 |
带动量的 SGD | 引入动量加速收敛,减少更新震荡 | 动量因子设置敏感,需精细调参 |
Nesterov 加速梯度 (NAG) | 采用预更新策略,加速收敛 | 计算复杂度增加 |
Adam | 结合动量与自适应学习率,收敛快,适用性广 | 在某些情况下可能收敛不稳定 |
Nadam | 结合 Adam 的自适应性 + Nesterov 的预更新,加速收敛 | 超参数较多,调参复杂 |
下可能收敛不稳定 | ||
Nadam | 结合 Adam 的自适应性 + Nesterov 的预更新,加速收敛 | 超参数较多,调参复杂 |