PyTorch训练Chinese MNIST损失收敛固定值、准确率低如何排查
问题排查方向与解决方案
你观察到的收敛值2.708050刚好是15分类任务下随机猜测的交叉熵基准值(计算式为ln(15)≈2.708),说明模型训练完全没有收敛,预测结果等效于随机输出,问题集中在以下三个核心点:
1. 损失函数与输出、标签格式完全不匹配
这是导致模型无法学习的最核心原因:
- PyTorch内置的
CrossEntropyLoss有严格的输入要求:- 模型传入的预测值必须是未经过任何激活函数的原始logits,损失内部会自动完成Softmax计算
- 标签默认要求传入形状为
[batch_size]的类别索引张量(dtype为torch.long),不需要手动转成one-hot格式
- 现有代码的两处错误:
- 模型最后一层错误添加了
Sigmoid()激活,把输出压缩到0-1区间,直接破坏了交叉熵的计算逻辑,反向传播的梯度完全异常 - 数据集里手动把标签转成了形状为
[batch_size, 15]的one-hot编码,和损失要求的标签格式不匹配
- 模型最后一层错误添加了
- 修正方案:
- 删除模型最后一层的
Sigmoid(),最后一层全连接直接输出15维原始logits - 数据集类不需要生成one-hot标签,直接存储类别索引即可,
__getitem__返回一维的类别标签
- 删除模型最后一层的
2. 测试循环准确率计算逻辑错误
现有测试循环用pred.argmax(1) == y计算正确数,但传入的y是one-hot格式的二维张量,和argmax(1)输出的一维预测索引形状不匹配,比较结果永远为False,所以显示的准确率始终低于10%,这个问题会在标签格式修正后同步解决。
3. 训练逻辑错误导致参数无法累积更新
你在每个epoch的循环内部都重新初始化了损失函数和Adam优化器:
for i in tqdm(range(NUM_EPOCHS)): train_loop(train_dl,model_mk_47, CrossEntropyLoss(), Adam(model_mk_47.parameters()) )
Adam优化器会存储每个参数的历史动量状态,每次重新初始化都会清空之前所有的更新记录,等于每个epoch都从零开始更新参数,模型自然无法持续收敛。
- 修正方案:把损失函数、优化器的初始化移到epoch循环外,全局只初始化一次,同时记得在每个epoch训练完成后调用测试循环:
model_mk_47 = NeuralNetwork().to(device) # 优先用float32训练,比double速度快2-4倍,精度完全足够 loss_fn = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model_mk_47.parameters(), lr=1e-3) NUM_EPOCHS=10 for i in tqdm(range(NUM_EPOCHS)): train_loop(train_dl,model_mk_47, loss_fn, optimizer) test_loop(test_dl, model_mk_47, loss_fn)
修正后的可复用核心代码
数据集类
class Custom_dataset(Dataset): def __init__(self,filepath,value): data = [cv2.imread(fp,-1) for fp in filepath] data = [i/np.max(i) for i in data] data = torch.tensor(data, dtype=torch.float32) data = data.view(-1,1,64,64) self.data = data self.size = len(value) assert len(filepath)==len(value), 'length mismatch' # 直接存储类别索引,无需one-hot self.targets = torch.tensor([value_idx[i] for i in value], dtype=torch.long) print('data shape ',self.data.shape) def __getitem__(self,idx): return (self.data[idx,:], self.targets[idx]) def __len__(self): return self.size
模型结构
class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(64*64, 64*32), nn.ReLU(), nn.Linear(64*32, 32*32), nn.ReLU(), nn.Linear(32*32, 16*32), nn.ReLU(), nn.Linear(32*16, 16*16), nn.ReLU(), nn.Linear(16*16, 15) ) def forward(self, x): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits
按以上点修改后,训练损失会快速下降,该数据集上简单MLP的准确率可以轻松达到90%以上。
内容的提问来源于stack exchange,提问作者Akash
相关产品推荐
相关产品推荐

