
DeepSpeed 的 DeepSpeedEngine.eval() 方法不返回模型对象,而是直接修改内部状态;若错误地执行 self.model = self.model.eval(),会导致模型变量被赋值为 None,引发后续运行错误。
deepspeed 的 `deepspeedengine.eval()` 方法不返回模型对象,而是直接修改内部状态;若错误地执行 `self.model = self.model.eval()`,会导致模型变量被赋值为 `none`,引发后续运行错误。
在使用 DeepSpeed 加速训练时,一个常见但易被忽视的陷阱是误将 model.eval() 的调用当作链式操作来使用——即对返回值进行重新赋值。实际上,DeepSpeedEngine.eval() 是一个就地(in-place)方法,其设计与标准 PyTorch nn.Module.eval() 行为不兼容:它不返回 self,而是隐式调用 self.module.train(False) 并返回 None。
查看 DeepSpeed 源码(v0.16.4 engine.py#L1900)可确认:
def eval(self):
self.warn_unscaled_loss = True
self.module.train(False) # ← 无 return 语句,Python 默认返回 None而标准 PyTorch 的 nn.Module.eval() 定义为:
def eval(self):
self.training = False
for module in self.children():
module.eval()
return self # ← 显式返回 self,支持链式调用因此,以下写法在原生 PyTorch 中安全,但在 DeepSpeed 下会导致严重错误:
# ❌ 错误:self.model 被赋值为 None self.model = self.model.eval() # 返回 None → self.model = None
正确做法是仅调用 .eval(),不进行赋值:
# ✅ 正确:直接触发评估模式切换 self.model.eval() # 修改 self.model.module 的 training 状态
此时 self.model 仍为 DeepSpeedEngine 实例,其内部封装的原始模型(self.model.module)已进入 eval 模式,所有 Dropout、BatchNorm 等层行为均符合预期。
? 验证方式:可在
eval()后检查self.model.module.training,应为False;同时确保在训练循环中恢复训练模式:self.model.train()。
此外,为提升代码健壮性与可读性,建议统一采用显式状态管理风格:
def evaluate(self, epoch_num=None, keep_all=True):
print("Before eval:", self.model)
# 进入评估模式(无返回值)
self.model.eval()
# 执行验证逻辑(注意:禁用梯度、不更新参数)
with torch.no_grad():
for batch in self.val_dataloader:
outputs = self.model(batch['input'])
# ... 计算指标
# ✅ 可选:显式切回训练模式(尤其在 train/eval 交替频繁时)
self.model.train()
print("After eval (still valid object):", self.model)⚠️ 注意事项:
- 不要依赖
model.eval()的返回值做任何赋值操作,无论是否使用 DeepSpeed; - DeepSpeed 的
train()/eval()方法只影响self.module的training属性,不影响DeepSpeedEngine自身的其他功能(如 ZeRO 状态、通信逻辑等); - 若需访问底层原始模型(例如用于
torch.jit.trace或自定义 hook),请通过self.model.module获取,而非self.model; - 在混合精度(FP16)或 ZeRO 优化下,
eval()不会自动禁用 FP16 推理——如需纯 FP32 推理,请额外控制self.model.fp16_enabled()或手动转换输入张量。
总结:DeepSpeed 的 API 设计更强调“命令式状态变更”,而非函数式链式调用。理解这一差异,是避免 NoneType 异常、保障训练/验证流程稳定的关键。

















