共计 1377 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点
MNIST 手写数字识别是深度学习的经典入门项目。使用 5 层卷积神经网络 (CNN) 的典型结构如下:

- 第一层:卷积层(Conv2d) + ReLU 激活 + 最大池化(MaxPool2d)
- 第二层:卷积层 + ReLU + 最大池化
- 第三层:全连接层(Linear) + Softmax 输出
新手常遇到的最大困扰就是学习率 (learning rate) 设置不当:
- 学习率过大:损失函数震荡不收敛,准确率波动剧烈
- 学习率过小:训练进度缓慢,可能需要数百 epoch 才能达到较好效果
技术方案对比
固定学习率 vs 动态调整
- 固定学习率:
- 最简单直接,但需要反复试错
-
推荐初始尝试值:0.01(SGD 优化器)或 0.001(Adam)
-
阶梯衰减(StepLR):
- 每 N 个 epoch 将学习率乘以衰减系数
-
例如:初始 0.1,每 30epoch 衰减为 0.1 倍
-
余弦退火(CosineAnnealingLR):
- 学习率呈余弦曲线变化
- 适合跳出局部最优
优化器选择
- SGD:对学习率敏感,需要精细调整
- Adam:自适应调整学习率,新手友好
核心实现
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR
# 模型定义
class CNN5(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.fc = nn.Linear(1600, 10) # MNIST 最终输出 10 类
def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.max_pool2d(x, 2)
x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, 2)
x = torch.flatten(x, 1)
return self.fc(x)
# 初始化
model = CNN5()
optimizer = optim.Adam(model.parameters(), lr=0.001) # 初始学习率
scheduler = StepLR(optimizer, step_size=30, gamma=0.1) # 每 30epoch 衰减
# 训练循环
for epoch in range(100):
# ... 训练代码 ...
scheduler.step() # 更新学习率
避坑指南
- 学习率与 batch size:
- 增大 batch size 时,应同比增大学习率
-
例如 batch size 从 128→256,学习率从 0.01→0.02
-
早停机制:
- 当验证集准确率连续 N 次不提升时终止训练
-
避免过拟合
-
梯度检查:
- 健康训练时梯度值应在 1e- 3 到 1e- 1 之间
- 过大可能爆炸,过小可能消失
延伸思考
如果测试集准确率突然下降,可能原因包括:
- 学习率衰减过快,模型无法继续优化
- 后期学习率过大导致震荡
- 优化器动量 (momentum) 设置不当
建议尝试将本方案迁移到 CIFAR-10 数据集,注意:
- 调整输入尺寸(32×32 RGB 图像)
- 可能需要更深的网络结构
- 学习率通常需要设置得更小
通过合理设置学习率,你的模型应该能在 MNIST 上达到 99%+ 的测试准确率。记住:调参是一门需要实践的艺术,多尝试不同组合才能找到最佳配置。
正文完
