AcceleratedOptimizer.eval() does not reach through DeepSpeed wrapper, silently saving train-view schedule-free weights

当 Accelerate 配合 DeepSpeed Zero 与 schedule-free 优化器(如 ProdigyPlusScheduleFree)时, AcceleratedOptimizer.eval() 不会像 train() 那样穿透 DeepSpeed 额外包装的优化器层,导致调用

快速结论:当 Accelerate 配合 DeepSpeed Zero 与 schedule-free 优化器(如 ProdigyPlusScheduleFree)时,AcceleratedOptimizer.eval() 不会像 train() 那样穿透 DeepSpeed 额外包装的优化器层,导致调用 eval() 静默变成空操作,保存 checkpoint 时仍然写入训练视图的权重。

适用环境:Accelerate 1.14.0、Linux 5.4.0 x86_64(glibc 2.31)、Python 3.13.11、NumPy 2.4.6、PyTorch 2.12.0+cu130、CUDA、DeepSpeed 0.19.1、bf16 混合精度;问题在 DeepSpeed ZeRO-1 加 schedule-free 优化器场景下出现。该 bug 已在 Accelerate 1.15.0 确认修复。

最快修复方案:升级到 Accelerate 1.15.0 或更高版本,该版本补齐了 eval() 对 DeepSpeed 优化器包装层的穿透逻辑。若无条件升级,可临时对底层优化器手动调用 eval(),但需自行确认包装层级。

注意事项:该问题不会抛异常,属于静默错误,容易在保存 checkpoint 后被忽略。升级后仍建议核对优化器元数据中的 train_mode 与 use_schedulefree。此外,Issue 中提到一个独立的 ZeRO-1 自定义优化器兼容问题(见 #4392),修复 eval() 不代表该问题也随之消失。

问题场景

用户在使用 Accelerate 配合 DeepSpeed ZeRO-1 进行分布式 diffusion-transformer 训练,优化器为 schedule-free 类型(ProdigyPlusScheduleFree),混合精度为 bf16。在 accelerator.save_state() 之前调用 optimizer.eval(),期望保存评估视图的 fp32 master 权重,但实际保存的仍是训练视图权重。该行为影响后续推理 checkpoint 转换,会静默地恢复出错误的参数。

报错原文

AcceleratedOptimizer.eval() does not reach through DeepSpeed wrapper, silently saving train-view schedule-free weights

实际表现不是异常抛出,而是在保存的优化器元数据中保留了训练视图状态:

use_schedulefree=True
train_mode=True

原因分析

最可能的原因是 Accelerate 的 AcceleratedOptimizer.train() 中存在针对 DeepSpeed 额外优化器包装层的专用回退分支:当直接包装对象没有 train() 时,会检查 self.optimizer.optimizer 并调用其 train()。而对应的 eval() 没有实现同样的穿透逻辑,只检查了直接包装对象。如果 DeepSpeed 包装层没有暴露 eval(),调用就会变成空操作,不报错也不生效。这被认为是 #3266 只给 train() 增加 DeepSpeed wrapper 遍历后缺失的对称另一半。

环境排查

  • 确认 Accelerate 版本,Accelerate 1.14.0 存在此问题,1.15.0 已修复。
  • 确认 DeepSpeed 版本(Issue 中为 0.19.1)及是否使用 ZeRO-1。
  • 确认 PyTorch 版本(Issue 中为 2.12.0+cu130)与 CUDA 加速器。
  • 确认 Python 版本(Issue 中为 3.13.11)与 NumPy 版本(2.4.6)。
  • 确认优化器是否为 schedule-free 类型,例如 ProdigyPlusScheduleFree,并检查其是否实现 train() / eval() 模式切换。
  • 检查保存的优化器元数据中 use_schedulefree 与 train_mode 的实际取值。

解决步骤

  1. 将 Accelerate 升级到 1.15.0 或更高版本,该版本已补齐 eval() 对 DeepSpeed 优化器包装层的穿透逻辑。
  2. 升级后,在真实两卡 DeepSpeed ZeRO-1 训练中验证:调用 optimizer.eval() 后,底层 Schedule-Free 优化器在两个 rank 上都应报告 train_mode=False。
  3. 确认 optimizer.train() 仍能恢复 train_mode=True 及训练视图权重。
  4. 在调用 accelerator.save_state() 之前执行 optimizer.eval(),保存后检查优化器元数据不再残留 train_mode=True。
  5. 若暂时无法升级,可优先尝试手动访问底层优化器并调用其 eval(),但此方式未经 Issue 验证,需自行确认包装层级与调用时机。

验证方法

在升级到 Accelerate 1.15.0 后,于两卡 DeepSpeed ZeRO-1 环境中运行训练脚本:在 accelerator.save_state() 之前调用 optimizer.eval(),确认底层 Schedule-Free 优化器在两个 rank 上 train_mode=False;随后调用 optimizer.train(),确认 train_mode=True 且训练视图权重被正确恢复。也可检查保存的优化器元数据中 use_schedulefree=True 时 train_mode 是否为期望值。

参考来源

huggingface/accelerate #4159

关联 Issue:huggingface/accelerate #4392(ZeRO-1 自定义优化器兼容问题,与本修复独立)

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29004

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注