学习率设置不当会导致loss停滞,过高引发震荡或发散,过低导致更新缓慢;Adam默认1e-3仅是起点,建议前10–20步观察loss曲线,爆炸则降lr至1e-4或5e-5,水平不动则适当调高。

学习率设太高或太低都会让 loss 卡住
训练不动,第一反应不是模型烂,而是学习率没调对。太高会跳过最优解,在损失曲面上来回震荡甚至发散;太低则更新太慢,看起来像“不动”。尤其用 Adam 时,默认 lr=1e-3 在多数 CV/NLP 任务上只是起点,不是万能解。
实操建议:
- 先跑 10–20 个 step,画出
loss曲线(别等一个 epoch 结束),看前几十步是震荡、缓慢下降,还是直接 NaN - 如果前几步
loss就爆炸(比如从 2.3 突然跳到 100+),大概率lr太高,试试1e-4或5e-5 - 如果
loss几乎水平不动(变化 SGD +momentum=0.9更容易暴露问题 - 别盲目套用别人 lr:ResNet50 在 ImageNet 上常用
0.1,但小数据集上用0.01都可能过猛
输入数据没标准化,BatchNorm 就成摆设
BatchNorm 层依赖输入在 batch 内近似零均值、单位方差。如果原始图像像素是 [0, 255],而你没除以 255 或减去均值,它内部的 running_mean / running_var 会严重偏移,导致前向输出失真,梯度传不下去。
常见错误现象:
- 训练初期
loss下降极慢,但验证集准确率也不涨 - 用
torchvision.transforms.Normalize时填错mean和std(比如把 ImageNet 的[0.485, 0.456, 0.406]用在自定义灰度图上) - 训练和推理时 normalization 不一致:训练用了 Normalize,但
val/testdataloader 忘了加
实操建议:
- 检查你的
Dataset.__getitem__或transforms链,确认最终输入模型的 tensor 是float32且值域在[0, 1]或[-1, 1],不是[0, 255] - 如果是自己算
mean/std,务必在训练集上计算,别用测试集或随便估;小数据集建议用[0.5, 0.5, 0.5]和[0.5, 0.5, 0.5]先兜底 - 打印一个 batch 的
input.mean()和input.std(),确认数量级合理(例如 RGB 图归一后 mean 应在 0.4–0.5 区间)
loss 不降但 grad.norm() 接近零?查梯度是否被截断或消失
损失卡住,但模型参数根本没怎么动——这时候不是数据或学习率的问题,而是梯度本身出事了。常见于 deep CNN 或 RNN 中的梯度消失,或误用了 torch.no_grad()、detach()、requires_grad=False。
实操建议:
- 在
optimizer.step()前加一句:print([p.grad.norm().item() for p in model.parameters() if p.grad is not None]),看是不是全接近 0 - 检查有没有在 loss 计算里不小心用了
.detach()(比如做 pseudo-labeling 时忘了取消 detach) - 如果用了
nn.DataParallel或DistributedDataParallel,确保 loss 是标量(.item()会断梯度,.mean()才对) - 激活函数别乱换:
ReLU安全,但Sigmoid或Tanh在深层网络中容易饱和,导致梯度为 0
验证集 loss 不降,但训练集降得快?小心数据泄露和标签错误
训练 loss 持续掉,验证 loss 卡住甚至上升,说明模型学到了训练集里的噪声或错误模式。最常被忽略的是标签本身——比如文件名和 label.txt 对不上、多分类中类别索引从 1 开始却没减 1、或者某类样本全被误标成另一类。
实操建议:
- 随机抽 10–20 个训练样本,用
plt.imshow()+print(label)肉眼核对,别信路径名或自动标注逻辑 - 统计每个 epoch 的各类样本数,看是否某类突然消失(常见于
WeightedRandomSampler配置错权重) - 关掉所有 augmentation(只留 ToTensor + Normalize),跑 1–2 个 epoch,看验证 loss 是否开始下降——如果依然不动,基本锁定数据或标签问题
- 如果用的是自定义
Dataset,重点检查__len__和__getitem__索引是否越界或重复
真正卡住的时候,人容易盯着模型结构调,但实际八成问题出在 loader 输出的第一步:那个 tensor 到底长什么样,值是多少,label 对不对——这些没法靠 print(model) 看出来,得亲手 inspect。


















