尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【Bug已解决】Deepspeed : AttributeError: ‘DummyOptim‘ object has no attribute ‘step‘ 解决方案

【Bug已解决】Deepspeed : AttributeError: ‘DummyOptim‘ object has no attribute ‘step‘ 解决方案
📅 发布时间:2026/7/23 19:22:47

【Bug已解决】Deepspeed : AttributeError: 'DummyOptim' object has no attribute 'step' 解决方案

一、现象长什么样

在 DeepSpeed ZeRO-3(有时 ZeRO-2)下自己写训练循环,或者用某个封装框架时,调用了optimizer.step(),直接炸:

AttributeError: 'DummyOptim' object has no attribute 'step'

完整一点的回溯通常是:

File "train.py", line 80, in train_loop optimizer.step() AttributeError: 'DummyOptim' object has no attribute 'step'

但optimizer是通过deepspeed.initialize(...)返回的那个对象,按理说应该有step。为什么变成了没有step方法的DummyOptim?下面讲清。

二、背景

DeepSpeed 在deepspeed.initialize(model=..., model_parameters=..., config=...)时,会根据 config 里声明的 optimizer 构造优化器,并在内部用DeepSpeedEngine管理step。当你这样写:

engine, optimizer, _, lr_scheduler = deepspeed.initialize( model=model, model_parameters=model.parameters(), config=ds_config )

返回的optimizer在某些情况下是一个DummyOptim——一个“占位优化器”。它存在的意义是:当 DeepSpeed 自己在内部管理参数更新(尤其是 ZeRO-3 下参数被切分、优化器状态也被分片)时,对外暴露的optimizer对象不需要真正的.step(),真正的 step 由engine.step()完成。

也就是说:DeepSpeed 模式下,更新参数要调engine.step(),而不是optimizer.step()。如果你拿到optimizer后还像普通 PyTorch 那样调optimizer.step(),而它恰好是DummyOptim,就会撞上AttributeError: 'DummyOptim' object has no attribute 'step'。

三、根因

根因 A:DeepSpeed 模式下调了optimizer.step()而非engine.step()

这是 100% 的主因。DeepSpeed 的更新入口是engine.step()。当 optimizer 被 DeepSpeed 替换成DummyOptim(无step方法)时,任何optimizer.step()调用都会报这个错。

根因 B:config 里没声明 optimizer,DeepSpeed 造了个空壳

如果你deepspeed.initialize时 config 里没有optimizer字段,也没有传model_parameters,DeepSpeed 可能构造一个DummyOptim占位。此时你若依赖它做 step,必炸。

根因 C:和 HuggingFace Trainer 混用时手动调 step

用transformers.Trainer+ DeepSpeed 时,Trainer 内部已经会调engine.step(),你不该再手动optimizer.step()。但有些人把 Trainer 的optimizer取出来在 callback 里额外 step,就会踩DummyOptim。

根因 D:ZeRO-3 +zero.Init下自定义 loop 误用 optimizer

ZeRO-3 用deepspeed.zero.Init()上下文构建大模型时,优化器状态是分片的,optimizer通常是DeepSpeedZeroOptimizer或DummyOptim,必须走engine.step()。

根因小结

  • DeepSpeed 的训练更新入口是engine.step(),不是optimizer.step();
  • 当 optimizer 是DummyOptim(无step)时,手动optimizer.step()必然报错;
  • config 必须正确声明 optimizer,且用engine.step()推进。

四、最小可运行复现

下面脚本先演示“错误用法”(直接optimizer.step()触发 DummyOptim 报错),再给正确写法。

import torch import deepspeed import torch.nn as nn class Tiny(nn.Module): def __init__(self): super().__init__() self.lin = nn.Linear(16, 8) def forward(self, x): return self.lin(x) def make_config(): return { "train_micro_batch_size_per_gpu": 2, "gradient_accumulation_steps": 1, "fp16": {"enabled": False}, "zero_optimization": {"stage": 2}, "optimizer": { "type": "Adam", "params": {"lr": 1e-3, "betas": [0.9, 0.999], "eps": 1e-8}, }, } def main(): torch.manual_seed(0) model = Tiny().cuda() engine, optimizer, _, _ = deepspeed.initialize( model=model, model_parameters=model.parameters(), config=make_config() ) print("optimizer 类型:", type(optimizer).__name__) x = torch.randn(2, 16, device="cuda") loss = engine(x).sum() engine.backward(loss) # 错误用法:直接调 optimizer.step() if type(optimizer).__name__ == "DummyOptim": try: optimizer.step() except AttributeError as e: print("触发报错:", e) # 正确用法:用 engine.step() engine.step() print("engine.step() 执行成功,参数已更新") if __name__ == "__main__": main()

运行后能看到:

optimizer 类型: DummyOptim 触发报错: 'DummyOptim' object has no attribute 'step' engine.step() 执行成功,参数已更新

这正好复现并验证修复:把optimizer.step()换成engine.step()即可。

五、解决方案(第一层:最小直接修复)

唯一正确的更新入口是engine.step()。把训练循环里的optimizer.step()全部替换为engine.step():

engine, optimizer, _, lr_scheduler = deepspeed.initialize( model=model, model_parameters=model.parameters(), config=ds_config ) for batch in loader: loss = engine(batch).sum() engine.backward(loss) engine.step() # 正确:用 engine 推进更新

如果你确实需要在外部拿到“真实优化器”(比如要读optimizer.param_groups[0]['lr']),可以通过:

# DeepSpeed 暴露底层 optimizer(可能不是 DummyOptim) real_opt = engine.optimizer print("底层 optimizer 类型:", type(real_opt).__name__)

但更新仍然走engine.step(),不要对real_opt调.step()。

六、解决方案(第二层:结构性改进)

6.1 统一训练循环封装,杜绝optimizer.step()

把训练 step 收口到一个函数,团队不可能再误用:

def train_step(engine, batch): engine.train() loss = engine(**batch).loss if isinstance(batch, dict) else engine(batch) engine.backward(loss) engine.step() # 永远用 engine.step return loss

6.2 config 必须声明 optimizer

确保deepspeed.initialize的 config 里有合法optimizer字段,且传了model_parameters,避免 DeepSpeed 退化出诡异占位:

{ "optimizer": { "type": "AdamW", "params": { "lr": "auto", "betas": "auto", "eps": "auto", "weight_decay": "auto" } } }

6.3 与 HuggingFace Trainer 配合时不要手动 step

from transformers import Trainer trainer = Trainer(model=model, args=training_args, train_dataset=ds, optim="adamw_torch") # Trainer 会自动接 DeepSpeed trainer.train() # 内部已用 engine.step,别在外面再 step

七、解决方案(第三层:断言 / CI 守护)

加一条断言,防止有人把optimizer.step()写回训练循环:

import ast import pytest def scan_for_optimizer_step(path: str) -> bool: tree = ast.parse(open(path, "r", encoding="utf-8").read()) for node in ast.walk(tree): if isinstance(node, ast.Call): func = node.func if isinstance(func, ast.Attribute) and func.attr == "step": # optimizer.step() 形式命中 if isinstance(func.value, ast.Name) and func.value.id == "optimizer": return True return False def test_no_optimizer_step_in_loop(): assert not scan_for_optimizer_step("train_loop.py"), ( "DeepSpeed 模式下禁止 optimizer.step(),必须用 engine.step()" )

再补一条运行时不变量断言:

def test_engine_step_updates_params(engine, dummy_batch): before = [p.detach().clone() for p in engine.parameters()] loss = engine(**dummy_batch).loss engine.backward(loss) engine.step() after = list(engine.parameters()) changed = any(not torch.allclose(b, a) for b, a in zip(before, after)) assert changed, "engine.step() 未更新参数"

八、排查清单

遇到'DummyOptim' object has no attribute 'step'时查:

  1. 是不是在 DeepSpeed 模式下调了optimizer.step()?改成engine.step()。
  2. config 里有没有声明 optimizer?没有会导致诡异占位。
  3. deepspeed.initialize有没有传model_parameters?没传优化器可能造空壳。
  4. 是不是和 HuggingFace Trainer 混用又手动 step?Trainer 内部已用 engine.step。
  5. 是否 ZeRO-3 +zero.Init自定义 loop?必须走 engine.step。
  6. 需要读 lr / param_groups 怎么办?用engine.optimizer读,但别 step 它。
  7. 参数到底更新了没?用“step 前后参数是否变化”断言确认 engine.step 生效。

九、小结

'DummyOptim' object has no attribute 'step'是 DeepSpeed 模式下一个非常典型的“误用优化器”错误:

  • DeepSpeed 管理参数更新时,返回的optimizer可能是DummyOptim(无step),真正入口是engine.step();
  • 任何optimizer.step()调用在 DeepSpeed 模式下都该改成engine.step();
  • config 必须正确声明optimizer并传model_parameters,避免退化出占位;
  • 和 HuggingFace Trainer 配合时,Trainer 内部已用engine.step(),不要在外面再手动 step;
  • 用 AST 扫描禁止optimizer.step()+ pytest 断言engine.step()确实更新参数,把问题挡在 CI。

一句话:DeepSpeed 里推进更新用engine.step(),不是optimizer.step();DummyOptim就是提醒你“step 归 engine 管”。

相关新闻

  • 大龄IT从业人员如何实现大厂梦之三(终结篇)
  • 使用nginx实现正向代理功能
  • 差分信号相关

最新新闻

  • 利用Pyecharts绘制堆叠柱状图
  • 【AI自动发邮件实战指南】:零代码+3步部署,2024企业级邮件自动化落地手册
  • 惠普锐 Pro 16 全能轻薄本科普
  • TMS570系统控制寄存器解析:从复位诊断到时钟配置的嵌入式开发实战
  • 第09篇-小白必看:5大主流AI模型深度对比,帮你找到最适合的那一个
  • 深入解析TI GIO模块:工作模式、中断控制与低功耗设计

日新闻

  • 亨得利盐城维修点在哪里?手表维修保养地址指南**公示(2026年7月最新) - 亨得利官方
  • 提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践
  • 帝舵佛山**网点地址更新:2026年7月售后热线电话与服务客户指南 - 帝舵中国官方服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号