PyTorch训练时为何最优模型保存异常,加载后始终为首轮训练精度?
这类问题90%以上是逻辑判断或者数值计算环节出了问题,按出现概率从高到低依次排查即可:
1. 准确率计算未做归一化,比较逻辑失效
这是新手最容易踩的坑:在eval_model中计算验证准确率时,直接把累计的预测正确样本数作为准确率返回,没有除以验证集总样本数。
举个例子:验证集共1000个样本,第一轮模型预测正确820个,如果代码没有做除法,返回的val_acc=820,此时best_accuracy被更新为820。后续训练中只要单轮验证的正确样本数低于820,就永远不会触发保存逻辑,最终磁盘上的模型文件始终是首轮保存的版本。
*排查方法:在每轮打印信息里加上准确率的实际类型和原始数值:
print(f'验证准确率原始值: {val_acc}, 类型: {type(val_acc)}')
如果输出的数值远大于1,就说明缺失了归一化步骤,把准确率计算改为“累计正确样本数 / 验证集总样本数”即可。
2. 准确率数值未脱离计算图/设备,比较时出现隐式异常
如果val_acc是没有经过detach()、仍保留在CUDA设备上的张量,部分PyTorch版本会在跨设备、跨类型比较时出现隐式转换错误,导致判断逻辑失效。同时带计算图的张量会持续占用显存。
*修复方式:在进入保存逻辑前,把val_acc转成普通Python浮点数:
# 脱离计算图、转到CPU、转成标量值 val_acc_scalar = val_acc.detach().cpu().item() if val_acc_scalar > best_accuracy: torch.save(model.state_dict(), 'best_model_state.bin') best_accuracy = val_acc_scalar
3. 验证流程未关闭训练模式,导致模型权重被篡改
如果eval_model内部没有先执行model.eval(),也没有用torch.no_grad()包裹验证逻辑,模型在验证阶段会受dropout、BatchNorm层的影响产生计算偏移,甚至因为保留梯度图导致权重被意外修改,最终算出来的验证准确率持续低于第一轮的数值,无法触发保存。
*排查检查:确认eval_model的开头部分有对应的模式切换和梯度关闭逻辑:
def eval_model(model, data_loader, loss_fn, device, n_examples): # 关闭训练模式 model.eval() losses = [] correct_predictions = 0 # 关闭梯度计算 with torch.no_grad(): for batch in data_loader: # 原有验证逻辑
如果缺失这两部分,补上即可解决问题。
4. 保存操作未实际生效
如果保存路径存在只读权限、路径指向的位置和加载位置不一致,也会导致磁盘上始终保留首轮保存的旧模型。
*排查方法:在torch.save后加一行打印,确认保存逻辑真的执行:
if val_acc_scalar > best_accuracy: torch.save(model.state_dict(), 'best_model_state.bin') best_accuracy = val_acc_scalar print(f'已更新最优模型,当前最优验证准确率: {best_accuracy:.4f}')
如果整个训练过程中只有第一轮出现了这条打印,说明后续轮次根本没达到保存条件;如果多轮都有打印,但加载后的精度不对,需要检查加载路径和保存路径是否完全一致,避免加载到旧的缓存文件。
内容的提问来源于stack exchange,提问作者Mohamed Amine

