You need to enable JavaScript to run this app. MNIST奇偶二分类任务中VGG模型损失与准确率停滞问题排查
优惠活动
大模型
产品
解决方案
定价
更多

MNIST奇偶二分类任务中VGG模型损失与准确率停滞问题排查

问题根因定位(按影响程度从高到低排序)

1. 准确率计算存在严重逻辑错误,日志显示的数值完全无效

  • 你做二分类时模型最后一层输出形状为 [batch_size, 1],每个样本仅输出一个0-1范围的概率值,但算准确率时使用了_,pred_labels = pred.max(1)——因为输出通道只有1个,max取出来的类别索引永远是0,和模型实际预测的概率值没有任何关系。
  • 更隐蔽的问题是,你在计算损失前给labels做了unsqueeze(1)变成形状[batch_size, 1],后续计算准确率时,形状为[batch_size]的pred_labels和形状为[batch_size, 1]的labels比较时,会触发PyTorch广播机制,被自动扩展成[batch_size, batch_size]的二维矩阵再做相等判断。
  • 这种错误计算出来的“准确率”本质是个虚值,刚好等于训练集中偶数类的总样本数除以batch数量,和模型实际的预测效果完全无关。你日志里固定在31%左右的准确率,就是这个错误逻辑算出来的结果。
  • 正确的二分类准确率计算应该是:直接把Sigmoid输出的概率和0.5阈值比较,得到预测类别后和原始形状的labels做匹配,不要用多分类的max取索引逻辑,也不要在算准确率时保留labels的多余维度。

2. 初始化逻辑与损失函数不匹配,导致模型陷入梯度饱和区完全无法更新

  • 你现在用nn.BCELoss搭配最后一层的Sigmoid激活,但卷积/全连接层的初始化用了Kaiming均匀分布,且非线性项指定为leaky_relu。这种初始化方式会让最后一层全连接层输出的logit值过大,直接把Sigmoid推到饱和区(输出无限接近0或1)。
  • Sigmoid在饱和区的梯度几乎为0,再加上你使用的是无动量的SGD优化器、学习率0.01,根本无法跳出饱和区,所以从第一轮训练开始,模型参数就几乎不更新,损失值自然一直固定在初始值不下降。你看到的稳定在49左右的损失值,就是因为初始输出直接触发Sigmoid饱和,BCELoss算出的高饱和损失值,因为梯度为0一直没有变化。
  • 更稳妥的修复方式是直接用nn.BCEWithLogitsLoss替换现有损失函数,同时把网络最后一层的Sigmoid去掉——这个损失内部自带Sigmoid计算,数值稳定性远好于手动加Sigmoid+BCELoss的组合,能从根源上避免饱和问题。

3. 全连接层维度匹配存在潜在隐患

  • 你代码中硬编码了全连接层输入维度为2048,这个数值是针对32x32分辨率输入计算的:32x32的图像经过4次2x2步长的MaxPool后,特征图尺寸为2x2x512,展平后刚好2048维。
  • 而MNIST原始输入是28x28,经过4次MaxPool后特征图尺寸仅为1x1x512,展平后只有512维。如果没有给MNIST图像做2像素padding将尺寸补到32x32,代码会直接报维度不匹配错误;就算你做了padding,也不建议硬编码维度值,最好在展平后打印张量形状确认和全连接层输入匹配。

4. 超参数设置不利于收敛

  • 你设置的Dropout概率为0.65,对于MNIST这类简单数据集来说过高,会随机丢弃65%的特征,导致有效信号丢失太多,进一步拖慢收敛。建议将Dropout概率降到0.2-0.3即可。

核心修复调整清单
  • 移除网络最后一层的nn.Sigmoid(),将损失函数替换为nn.BCEWithLogitsLoss(),提升训练稳定性。
  • 修正准确率计算逻辑:不再使用max取索引的多分类逻辑,直接将logit输出与0阈值比较得到预测结果,和原始形状的labels做匹配计算准确率。
  • 确认全连接层输入维度和实际展平后的特征维度一致,避免硬编码带来的匹配错误。
  • 将Dropout概率从0.65调整到0.2-0.3,减少不必要的特征丢失。

注:你写的奇偶分类标签转换逻辑没有问题,不需要修改。

内容的提问来源于stack exchange,提问作者CasellaJr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 21:36:00