共计 1725 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
在传统的 BP 神经网络训练中,我们通常会使用随机梯度下降(SGD)来更新网络参数。然而,这种方法存在两个主要问题:

- 振荡问题:在损失函数的陡峭区域,梯度值较大,导致参数更新幅度过大,容易在最优解附近来回震荡。
- 收敛慢和局部最优:在平坦区域,梯度值很小,导致参数更新缓慢。同时,模型容易陷入局部最优而无法到达全局最优。
技术解析
动量因子(Momentum)的引入就是为了解决这些问题。其核心思想是为梯度下降增加 ” 惯性 ”,使参数更新方向不仅取决于当前梯度,还考虑历史梯度的加权平均。
数学表达式为:
$$v_t = \beta v_{t-1} + (1-\beta)\nabla_\theta J(\theta)$$
$$\theta = \theta – \alpha v_t$$
其中:
– $v_t$ 是当前时刻的动量项
– $\beta$ 是动量系数,通常取 0.5-0.99
– $\alpha$ 是学习率
与普通 SGD 相比,带动量的 SGD 有以下优势:
- 在梯度方向一致的维度上加速收敛
- 在梯度方向变化的维度上减少震荡
- 有助于跳出局部最优
代码实现
下面是用 PyTorch 实现带动量的全连接神经网络示例:
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, 512)
self.fc2 = nn.Linear(512, 10)
def forward(self, x):
x = x.view(-1, 784)
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
model = Net()
criterion = nn.CrossEntropyLoss()
# 关键:这里设置动量参数
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # β=0.9
# 训练循环
for epoch in range(10):
for batch_idx, (data, target) in enumerate(train_loader):
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
实验对比
我们在 MNIST 数据集上对比了不同动量系数下的训练效果:
- β=0(普通 SGD):收敛缓慢,容易出现震荡
- β=0.5:收敛速度明显加快,但仍有一定震荡
- β=0.9:收敛最快且稳定,参数更新轨迹平滑
生产建议
在实际应用中,建议遵循以下调参策略:
- 学习率与动量的协同:通常动量越大,学习率可以设置得越小
- 与批归一化配合:BatchNorm 可以减少内部协变量偏移,使动量法更有效
- 动态调整策略:可以采用 Cosine 退火等方法来动态调整动量系数
延伸思考
动量法还有更高级的变体,如 Nesterov 加速梯度(NAG),它在计算梯度时考虑了未来的参数位置。现代优化器如 Adam 实际上结合了动量法和自适应学习率的优点。读者可以尝试在 CIFAR-10 等更复杂的数据集上复现这些实验,观察不同优化器的表现差异。
通过合理使用动量因子,我们能够显著提升神经网络的训练效率和稳定性,这是每个机器学习工程师都应该掌握的基本技能。
正文完
