ZeRO-2切分梯度和优化器状态,参数全量保留在各GPU;ZeRO-3进一步分片参数,节省显存但引入频繁all-gather通信,易拖慢训练,尤其在层数多、激活大或batch小时。

DeepSpeed ZeRO-2 和 ZeRO-3 切分逻辑差异直接影响显存占用和通信开销
ZeRO-2 对梯度和优化器状态做切分,模型参数仍完整保留在每个 GPU 上;ZeRO-3 则进一步切分模型参数本身(即“参数分片”),显存节省更激进,但引入跨 GPU 的参数按需拉取(all-gather)——这在 forward/backward 中频繁触发时会明显拖慢训练速度,尤其当模型层数多、激活量大或 batch size 小时。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 先用
deepspeed --zero-stage 2测 baseline 显存和吞吐,确认梯度/优化器状态是瓶颈;只有显存仍超限时再升到zero-stage 3 - 启用
stage3_gather_16bit_weights_on_model_save true避免保存时 OOM,但注意这会让save_checkpoint()变慢 - 若使用
torch.compile(),必须关闭stage3_prefetch_bucket_size或设为 0,否则可能触发 CUDA graph 冲突
PyTorch DDP 与 DeepSpeed 混用会导致 ZeRO 失效且报错 RuntimeError: Expected all tensors to be on the same device
DeepSpeed 自身已封装了分布式通信和参数同步逻辑,若在代码中手动调用 torch.nn.parallel.DistributedDataParallel 或误调 model.cuda() / model.to(device),会破坏 DeepSpeed 的设备管理,导致张量分散在不同 device 上,进而触发该错误。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 初始化模型后直接传给
deepspeed.initialize(),不要额外 wrap DDP - 所有
.to()、.cuda()调用必须删除;设备由 DeepSpeed 在engine.module中统一管理 - 检查
forward()中是否手动移动输入数据——应统一用engine.device,例如:input = input.to(engine.device)
ZeRO-3 下 torch.no_grad() 推理仍可能 OOM,因未自动禁用参数 gather
即使进入 torch.no_grad(),DeepSpeed 默认仍会在每次 forward 前执行参数 all-gather(为兼容某些 hook 行为),这对大模型推理是冗余开销,且容易爆显存。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 推理前显式调用
engine.zero_grad()(清空 optimizer 状态) +engine._zero3_consolidated_fp16_states = False(可选) - 更可靠的做法:用
engine.set_gradient_accumulation_steps(1)并在 eval loop 中加engine.disable_untested_features()(慎用) - 推荐方案:改用
engine.eval()+ 手动控制engine.module.eval(),并在 forward 前插入engine._allgather_params(reload=False)的绕过逻辑(需 patchdeepspeed.runtime.zero.stage3.py中相关判断)
DeepSpeed config 中 offload_optimizer 和 offload_param 开启后反而变慢的常见原因
CPU offload 不是万能加速项。当 NVMe 带宽不足、PCIe 通道被占满,或 CPU 内存延迟高时,offload 会引入严重 IO 瓶颈,训练 step time 可能翻倍。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 仅在单卡显存 2x 模型 fp16 参数大小时考虑开启
offload_param -
offload_optimizer必须搭配pin_memory: true和buffer_count: 4(默认 5),否则 pinned memory 不足会导致频繁 host-to-device memcpy - 用
nvidia-smi dmon -s u -d 1观察rx/tx值,若持续 >8 GB/s,说明 PCIe 已饱和,应停用 offload
stage3_max_live_parameters 反而比默认值更稳。


















