共计 2261 个字符,预计需要花费 6 分钟才能阅读完成。
核心概念
随机梯度下降(Stochastic Gradient Descent, SGD)是深度学习中最基础的优化算法之一。它的核心思想是通过迭代更新模型参数,最小化损失函数。与批量梯度下降(Batch Gradient Descent)不同,SGD 每次只使用一个样本(或一小批样本)来计算梯度,从而大大提高了计算效率。

数学上,SGD 的权重更新过程可以表示为:
$$w_{t+1} = w_t – \eta \nabla J(w_t; x_i, y_i)$$
其中:
– $w_t$ 是第 $t$ 次迭代的权重
– $\eta$ 是学习率
– $\nabla J(w_t; x_i, y_i)$ 是损失函数 $J$ 关于权重 $w_t$ 在样本 $(x_i, y_i)$ 上的梯度
与批量梯度下降相比,SGD 的计算量更小,但梯度估计的方差较大,可能导致收敛过程不稳定。
痛点分析
虽然 SGD 简单高效,但在实际应用中常面临以下问题:
- 学习率敏感:学习率过大可能导致震荡甚至发散,过小则收敛速度慢。
- 震荡收敛:由于每次更新基于单个或少量样本,梯度估计噪声大,导致收敛路径曲折。
- 局部最优:在高维非凸优化问题中,容易陷入局部最优或鞍点。
- 梯度消失 / 爆炸:在深层网络中,梯度可能指数级衰减或增长,导致训练困难。
技术方案
为了解决上述问题,研究者提出了多种 SGD 变种算法:
- 动量法(Momentum):引入动量项来加速收敛并减少震荡。更新公式为:
$$v_{t+1} = \gamma v_t + \eta \nabla J(w_t)$$
$$w_{t+1} = w_t – v_{t+1}$$
其中 $\gamma$ 是动量系数,通常设为 0.9。
-
Adagrad:自适应调整学习率,对频繁更新的参数使用较小学习率,反之亦然。
-
Adam:结合动量和自适应学习率,是目前最常用的优化器之一。
代码示例
以下是用 PyTorch 实现带学习率衰减的 SGD 的完整代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 数据加载
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
# 模型定义
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, 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 = SimpleNet()
# 优化器设置
optimizer = optim.SGD(model.parameters(),
lr=0.01,
momentum=0.9, # 动量系数
weight_decay=1e-4) # L2 正则化
# 学习率调度器
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)
# 训练循环
for epoch in range(10):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
optimizer.zero_grad()
output = model(data)
loss = nn.functional.cross_entropy(output, target)
loss.backward()
optimizer.step()
scheduler.step() # 更新学习率
print(f'Epoch {epoch}, LR: {scheduler.get_last_lr()[0]:.6f}')
性能测试
在 MNIST 数据集上,我们比较了不同优化器的表现:
- Vanilla SGD:收敛速度较慢,准确率提升平缓
- SGD with Momentum:收敛速度明显加快,震荡减小
- Adam:初期收敛最快,但最终准确率与带动量的 SGD 相当
避坑指南
- 学习率初始值选择:
- 常见初始值为 0.1、0.01 或 0.001
-
可以通过学习率扫描(learning rate sweep)来确定最佳值
-
梯度问题诊断:
- 梯度爆炸:损失值变为 NaN
- 梯度消失:模型参数更新量极小
-
解决方案:梯度裁剪、批归一化、调整初始化方法
-
小批量大小选择:
- 太小:噪声大,收敛不稳定
- 太大:内存需求高,每次更新计算量大
- 经验值:32-256 之间
思考题
如何设计实验验证 SGD 在不同神经网络深度下的表现差异?可以考虑以下方面:
- 构建不同深度的网络(如 3 层、5 层、10 层)
- 固定其他超参数,仅改变网络深度
- 记录训练过程中的损失曲线和验证准确率
- 比较收敛速度和最终性能
- 分析梯度传播情况(如梯度范数随时间变化)
通过这样的实验,可以深入理解 SGD 在不同网络结构中的行为特点。
