model.eval()没生效的主因是调用位置错误:在DataParallel/DistributedDataParallel模型上需用model.module.eval(),且验证中不可混入model.train();BatchNorm统计量异常则源于小batch_size或异常样本导致训练时统计失真。

model.eval()没生效:常见调用位置错误
最常被忽略的是调用时机和作用对象。比如在 DataParallel 或 DistributedDataParallel 包装后的模型上,直接调用 model.eval() 只会影响包装器本身,而不会透传到底层模型。此时必须用 model.module.eval()。
另一个典型错误是:在验证循环中只在开头调用一次 model.eval(),但中间穿插了训练代码(比如误把验证逻辑写进训练 epoch),导致后续 batch 又回到了 training=True 状态。
- 检查模型是否被
nn.DataParallel或torch.nn.parallel.DistributedDataParallel包装 —— 是则必须用model.module.eval() - 确认
model.eval()调用后没有再执行model.train()或其它触发状态重置的操作 - 打印
model.training值验证:应在验证前为False,且全程保持不变
BatchNorm 统计量异常:训练阶段就已污染
即使正确调用了 model.eval(),如果训练时 BatchNorm 层的移动平均统计量(running_mean / running_var)本身就不稳定或偏差大,推理时固定使用这些错误值,结果自然飘忽。
典型诱因是训练 batch_size 过小(如 batch_size=1 或 2),导致每个 batch 的均值/方差估计严重失真;或者训练数据中存在大量异常样本(如全黑图像、空 tensor),拖垮全局统计量。
立即学习“Python免费学习笔记(深入)”;
- 训练时避免
batch_size (尤其对 BN 敏感的网络如 ResNet、MobileNet) - 用
model.modules()遍历所有nn.BatchNorm*层,检查layer.running_mean是否存在明显 NaN 或极端离群值 - 若发现统计量异常,可尝试重训,或加载权重后手动重置:
layer.reset_running_stats()(慎用,仅限调试)
Dropout 层未被 model.eval() 覆盖
绝大多数 Dropout 层(nn.Dropout, nn.Dropout2d)确实会响应 model.eval(),但有例外:
- 自定义模块中手动写了
if self.training:但漏掉了else分支,导致 eval 模式下无输出或返回默认值 - 用了第三方库中非标准 Dropout 实现(如某些 vision transformer 中的 stochastic depth),它们可能不依赖
self.training标志 - 模型里混用了
torch.nn.functional.dropout—— 这个函数**不自动读取模型状态**,必须显式传入training=False
例如:F.dropout(x, p=0.5) 在 eval 模式下仍会随机丢弃;正确写法是 F.dropout(x, p=0.5, training=False)。
推理时仍启用梯度或随机种子未冻结
model.eval() 不影响随机性源头。PyTorch 推理结果波动,有时根本不是模型层的问题,而是输入或环境层面的随机干扰:
- 没禁用 CUDA 的非确定性行为:
torch.backends.cudnn.enabled = False和torch.backends.cudnn.benchmark = False必须设置 - 没固定 Python / NumPy / PyTorch 的随机种子,尤其当预处理含随机裁剪(即使已关增强)或 DataLoader 启用了
shuffle=True - 用了
torch.no_grad()外层但内部仍有torch.enable_grad()或 autograd.function 自定义逻辑
真正稳定的推理流程必须同时满足三件事:model.eval() + torch.no_grad() + 全局 seed 固定 + cuDNN 确定性开关关闭。


















