共计 2272 个字符,预计需要花费 6 分钟才能阅读完成。
技术背景
深度学习的优化算法经历了多次迭代演进,从最基础的批量梯度下降(Batch Gradient Descent)到随机梯度下降(SGD),再到引入动量的 Momentum 和自适应学习率的 AdaGrad。这些算法的演进都是为了解决非凸优化中的核心挑战:学习率的调整。

- SGD:最基本的优化算法,每次更新参数时使用当前 batch 的梯度。优点是实现简单,但缺点是容易陷入局部最优,且学习率需要手动调整。
- Momentum:在 SGD 的基础上引入动量项,加速收敛并减少震荡。公式为:
$$v_t = \beta v_{t-1} + (1-\beta)\nabla_\theta J(\theta)$$
$$\theta_{t+1} = \theta_t – \eta v_t$$ - AdaGrad:自适应调整学习率,适合稀疏数据。公式为:
$$G_t = G_{t-1} + (\nabla_\theta J(\theta))^2$$
$$\theta_{t+1} = \theta_t – \frac{\eta}{\sqrt{G_t + \epsilon}} \nabla_\theta J(\theta)$$
核心对比维度
数学原理
Adam 结合了动量和自适应学习率的优点,其更新公式为:
- 计算动量:
$$m_t = \beta_1 m_{t-1} + (1-\beta_1)\nabla_\theta J(\theta)$$ - 计算自适应学习率:
$$v_t = \beta_2 v_{t-1} + (1-\beta_2)(\nabla_\theta J(\theta))^2$$ - 偏差修正:
$$\hat{m}_t = \frac{m_t}{1-\beta_1^t}$$
$$\hat{v}_t = \frac{v_t}{1-\beta_2^t}$$ - 参数更新:
$$\theta_{t+1} = \theta_t – \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t$$
计算效率
- 内存占用 :Adam 需要存储动量和自适应学习率的中间变量,内存占用略高于 SGD。
- GPU 并行化 :Adam 的计算复杂度与 SGD 相当,适合 GPU 并行化。
超参数敏感性
- 学习率 :Adam 对学习率的敏感性较低,通常使用默认值 0.001 即可。
- β 参数 :β1 和 β2 通常设置为 0.9 和 0.999,调整这些参数对收敛速度有显著影响。
实战代码
以下是一个在 PyTorch 中实现 MNIST 分类任务的 AB 测试代码示例:
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
# 数据加载
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
# 模型定义
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = x.view(-1, 784)
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
model = Net()
# 优化器定义
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01)
optimizer_adam = optim.Adam(model.parameters(), lr=0.001)
# 训练循环
for epoch in range(10):
for batch_idx, (data, target) in enumerate(train_loader):
optimizer_sgd.zero_grad()
output = model(data)
loss = nn.CrossEntropyLoss()(output, target)
loss.backward()
optimizer_sgd.step()
optimizer_adam.zero_grad()
output = model(data)
loss = nn.CrossEntropyLoss()(output, target)
loss.backward()
optimizer_adam.step()
生产建议
- 小数据场景 :使用梯度累积技巧,即多次小 batch 的梯度累加后再更新参数。
- 梯度爆炸 :使用梯度裁剪(gradient clipping)策略,限制梯度的最大值。
- 分布式训练 :选择支持同步更新的优化器,如 DistributedAdam。
对比表格
| 算法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| SGD | 数据量小、简单模型 | 实现简单、内存占用低 | 收敛慢、需手动调学习率 |
| Adam | 大数据、复杂模型 | 自适应学习率、收敛快 | 内存占用高、超参数敏感 |
结尾思考题
当 Adam 遇到鞍点:自适应优化的局限性分析。Adam 虽然在大多数情况下表现优异,但在某些非凸优化问题中可能会陷入鞍点。如何结合其他优化算法(如 SGD)来避免这一问题?
正文完
