PyTorch推理结果不一致,大概率因未调用model.eval();训练后模型默认train模式,Dropout随机丢弃、BatchNorm用当前batch统计量,导致相同输入输出波动,必须显式调用model.eval()并配合torch.no_grad()、固定随机种子及cuDNN确定性设置才能确保稳定。

model.eval() 没调用,BatchNorm 和 Dropout 仍在“动”
PyTorch 模型在训练和推理时行为不同:BatchNorm 用运行统计量(running_mean / running_var),Dropout 随机丢弃神经元。但这些只在 model.train() 下生效;预测时必须显式切换到评估模式,否则每次 forward() 都可能因 dropout 掩码不同或 BN 统计量未冻结而输出漂移。
常见错误是只在训练后调了一次 model.eval(),但部署时加载模型后又忘了再调——尤其用 Flask/FastAPI 封装时,模型实例化后没统一设状态。
实操建议:
立即学习“Python免费学习笔记(深入)”;
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 加载模型后立刻执行
model.eval(),且确保所有预测入口都走这个路径 - 配合
torch.no_grad()使用,避免梯度缓存干扰(虽然不影响输出值,但能防意外) - 检查是否混用了原生
torch.nn.functional.dropout—— 它不响应training参数,必须手动传p=0.0
随机种子没全设,np / random / torch 各自为政
只设 torch.manual_seed(42) 不够。PyTorch 的 seed 不影响 NumPy 或 Python 内置 random,而数据加载、采样、特征工程里常混用三者。比如用 sklearn.utils.resample 做测试集重采样,它依赖 random;用 np.random.choice 做索引打乱,它依赖 np.random。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在预测脚本最开头一次性设齐:
np.random.seed(42)、random.seed(42)、torch.manual_seed(42) - GPU 环境加
torch.cuda.manual_seed_all(42) - 启用确定性模式:
torch.backends.cudnn.deterministic = True(会略降速,但必要) - 避免多线程加载数据(如
DataLoader(num_workers > 0)),worker 间 seed 不同步
预处理流程不一致,归一化参数没固化
本地训练时用 StandardScaler().fit(X_train) 得到 mean/std,预测时却用新数据重新 fit —— 这等于把测试数据信息泄露进推理流程。更隐蔽的是:如果 scaler 是 pipeline 一部分但没被持久化,线上加载的只是空壳对象,调用 transform() 时实际用的是默认初始化参数。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 训练完立刻保存 scaler:
joblib.dump(scaler, 'scaler.pkl'),线上加载后直接transform() - 避免在全局 scope 对原始数据做
scaler.fit_transform(X)—— 这会让测试集参与拟合 - 检查图像预处理是否用了不同 backend(如 PIL vs OpenCV 读图通道顺序不同)、不同插值方式(PIL.Image.BILINEAR vs .LANCZOS)
模型保存/加载方式引入隐式状态差异
用 torch.save(model.state_dict(), ...) 只存权重,不存 BN 的 running_mean 和 running_var;而用 torch.save(model, ...) 虽存了全部状态,但反序列化时若环境版本不一致(如 PyTorch 2.0 → 2.1),某些层内部逻辑可能微变。ONNX 导出虽跨平台,但部分自定义算子不支持,回退到 PyTorch 时又绕回原问题。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 优先用
state_dict+ 架构代码方式部署,确保线上重建模型结构完全一致 - 若必须用完整模型保存,验证加载后 BN 层的
running_mean是否与训练结束时一致(打印对比) - ONNX 导出后,用
onnxruntime.InferenceSession加载并跑相同输入,比对输出误差(应
model.eval() 后还得喂一次 dummy input 让其稳定),以及预处理对象没随模型一起打包——这两处问题在本地单测时几乎不暴露,一上生产就浮出水面。

















