☰
DeepSpeed 0.18.4升级实战:Python 3.12与AMD ROCm稳定性优化全解析
2026/10/20 8:49:12 网站建设 项目流程

DeepSpeed v0.18.4 这个版本我没有第一时间追,是等到手头一个跑了大半月的训练任务稳定结束后才抽空升级的。这次发布最吸引人的标签是“稳定性优化”和“兼容性完善”,其中 Python 3.12 正式支持、AMD ROCm 全面适配,都是很多训练环境一直等的点。这篇文章从实际使用角度聊一聊 v0.18.4 到底改了什么、升级时需要注意哪些配置文件、以及我在 NVIDIA 和 AMD 两类环境里的测试结果。无论你是老用户还是第一次装 DeepSpeed,下文基本都能作为一份可参照的升级清单。

1. 版本定位与升级动机

1.1 一次“缝缝补补”的版本,为什么重要

从版本号看,0.18.4 只是 0.18.x 系列里的一次补丁级更新,没有引入类似 ZeRO-4、Pipeline Parallel 这种大功能,但这恰恰是它值得关注的原因。分布式训练框架最怕的不是缺新功能,而是放大训练规模后暴露出来的偶发卡顿、断点失败、显存泄漏,这类问题在模型跑几百上千步时才会暴露,排查成本极高。

我翻了一下 release notes 和社区反馈,0.18.4 的重点集中在几块:长稳训练下的通信边界处理、CPU offload 与 checkpoint 保存的配合、ZeRO-3 在权重合并时的内存占用、以及不同 PyTorch 版本下的算子构建兼容。在我自己的 8 卡机器上,旧版本跑同一个 7B 规模的 LoRA 微调时,偶尔会出现梯度 all-reduce 后某个 rank 不继续的情况,升级到 0.18.4 之后连续跑了三天没有复现。单凭这一点,就值得把升级列上日程。

另外,补丁版本往往不会给你“必须改代码”的压力,更多是帮你排除环境里的隐性风险。如果你正在使用 0.17.x 或更早版本,建议先看一遍 0.18.x 系列对配置文件字段的校验逻辑,再做升级。

1.2 Python 3.12 支持背后的兼容性压力

Python 3.12 正式铺开已经有一段时间,新的深度学习项目大多默认用 3.12 建虚拟环境。但 DeepSpeed 这种包含大量 C++/CUDA/HIP 扩展的框架,对解释器版本的适配并不像纯 Python 库那么简单。Python 3.12 移除了长期使用的 distutils,setup.py里的不少构建逻辑都依赖它,这会导致在老版本 DeepSpeed 上新环境编译时直接报ModuleNotFoundError: No module named 'distutils'之类的问题。

v0.18.4 对构建系统做了比较彻底的清理,改用更现代的 setuptools 声明方式,同时补上了 Python 3.12 对应的 wheel/ABI 标记。从我实际安装的体验来说,在干净的 Python 3.12 环境里pip install deepspeed==0.18.4不需要再手动补 distutils 补丁,也没有出现 cryptic 的 C 扩展编译错误。你要是之前被这个劝退过,这次可以重新试一试。

Python 3.12 本身对训练代码的加速效果很小,但统一环境版本的收益很大。团队里有人用 3.10、有人用 3.12,reproduce 问题的时候经常因为 ABI 差异得到完全不同的报错。升级到 0.18.4 之后,至少 DeepSpeed 这台“发动机”不再挑剔解释器版本。

1.3 AMD ROCm 全平台适配的实际价值

AMD ROCm 生态最近两年进步明显,但很多训练框架对它的支持停留在“能编译、能跑单卡”的程度,一旦开启 ZeRO offload 或者多卡通信,就会遇到各种诡异的hang。v0.18.4 的标题里专门把 AMD ROCm 拿出来说,说明这次不是简单修两个宏定义,而是对算子构建、通信后端、显存管理路径都做了适配。

我理解的实际价值有两点。第一,单卡显存不够的用户可以把 ZeRO-Stage 3 和 CPU offload 真正用在 AMD 卡上,而不只是把模型加载起来看个 loss;第二,多卡环境下 RCCL 路径的稳定性提升,意味着 AMD 机房里的用户可以不用再忍受 rank 间进度不一致。DeepSpeed 底层对 AMD 卡并不是另起炉灶,而是让原有 CUDA 代码通过 HIP 转换层跑在 ROCm 上,因此同样的训练脚本和 config 文件基本可以复用。

如果你是 AMD 平台的用户,以前梯度 offload 或者 optimizer state 磁盘缓存这类功能只能看别人用,这次可以实测一下。需要注意的是,ROCm 下 PyTorch 的安装源和 NVIDIA 版本不同,必须确保 DeepSpeed、PyTorch、ROCm 三者的驱动匹配,具体方法我在第三节里写了。

2. 升级 DeepSpeed 0.18.4 前的环境与配置检查

2.1 依赖版本和编译要求

升级前先检查底层的 PyTorch 版本。DeepSpeed 0.18.4 要求 PyTorch 有比较好的 C++ ABI 兼容性,官方推荐使用较新的稳定版本。结合我测试过的组合,PyTorch 2.1 到 2.3 都工作正常,Python 3.8 以上到 3.12 均可安装。如果是 Python 3.12,建议先保证setuptools在 68 以上,wheel和ninja也提前升到最新版。

很多人安装 DeepSpeed 习惯直接pip install deepspeed,但在 GPU 机器更推荐显式编译一次算子:

pip install --upgrade pip setuptools wheel ninja DS_BUILD_OPS=1 pip install deepspeed==0.18.4

DS_BUILD_OPS=1会触发 JIT 编译,把 Adam、Transformer 等自定义算子针对当前 GPU 架构做本地优化。编译时如果出现ninja: build stopped: subcommand failed,绝大多数情况是缺少系统依赖,比如python3.12-dev、libaio-dev或者编译器版本过旧。装完系统包后先pip uninstall -y deepspeed,再重装一次。

AMD ROCm 环境下编译要求更严格。除了 ROCm 的驱动和 HIP 环境,还需要确认torch.version.hip和系统里的 ROCm 版本能对得上。我测试时用的是 ROCm 6.0 对应的 PyTorch,直接编译 DeepSpeed ops 没有遇到 HIP 头文件缺失的问题,但如果你是翻新旧项目,一定要检查原来那份 PyTorch 是哪个 ROCm 版本编译的,混用很容易出现MIOpen或hipLaunchKernel相关的诡异错误。

2.2 旧配置文件的逐项核对

DeepSpeed 的配置是一份 JSON 或 YAML 文件,字段很多,0.18.x 系列对配置的校验比旧版本严。升级后第一步不是急着启动,而是把旧配置里的过时字段清一遍。

首当其冲的是zero_optimization这一节。如果原来用 ZeRO-2,推荐加两个字段:

{ "zero_optimization": { "stage": 2, "contiguous_gradients": true, "overlap_comm": true, "reduce_scatter": true } }

reduce_scatter在旧版本里默认行为不稳定,0.18.4 对它做了大量边界修复,显式开启后 ZeRO-2 的通信量更可控。另一个容易踩坑的字段是offload_optimizer,如果你在 ZeRO-3 里开了pin_memory,升级后如果机器内存不足,反而可能触发更频繁的换页。

建议在升级前把配置里的 optimizer 类型写完整。DeepSpeed 支持Adam、AdamW、Lion等,但 0.18.4 对 optimizer 参数校验更严格,少一个默认字段会直接抛 Configuration Error。我的习惯是先把optimizer.params里的lr、weight_decay、betas全部写显式。

2.3 从 checkpoint 恢复的正确姿态

老项目最怕升级后 checkpoint 不兼容。DeepSpeed 的 checkpoint 会保存模型权重、优化器状态、学习率调度器状态以及 ZeRO 分片信息,跨小版本恢复一般没问题,但如果你是从 0.15 或 0.16 跳到 0.18.4,强烈不建议直接用旧 checkpoint 接着训。比较好的做法是先用旧版本把训练跑完,或者至少用一个稳定中间点导出一次全量权重。

zero_to_fp32.py这个脚本在 0.18.4 里也做了更新,主要是修复了 ZeRO-3 下多个 rank 的权重文件合并时的内存占用问题。如果你用 PyTorch,在恢复 checkpoint 时注意不要跨版本做这种合并,宁可多花几分钟重新保存一次全量权重,也不要在一个刚升级的环境里去解析旧的分片文件。

3. 从零安装到跑通最小训练脚本

3.1 Python 3.12 虚拟环境安装流程

我先在一台带 NVIDIA 卡的机器上演示完整流程。第一步创建虚拟环境:

python3.12 -m venv ds-312 source ds-312/bin/activate

激活后先升级基础工具链:

pip install --upgrade pip setuptools wheel ninja

然后在同一环境里安装对应的 PyTorch。这里要注意,DeepSpeed 编译时会探测 PyTorch 的头文件和库路径,所以顺序必须是先 PyTorch 后 DeepSpeed。如果你直接pip install deepspeed再装 PyTorch,很可能出现算子编译成功但运行时找不到符号的错误。

接着安装 DeepSpeed:

DS_BUILD_OPS=1 pip install deepspeed==0.18.4

如果你只想要纯 Python 版本不动算子,也可以不加DS_BUILD_OPS=1。但对于实际训练,还是建议完整编译一次。安装完成后用pip check验证依赖树,缺少pydantic、jsonargparse或者pyyaml时要注意补上,这些是配置解析的基础依赖。

3.2 用 ds_report 验证安装结果

DeepSpeed 自带环境诊断工具ds_report,命令直接敲:

ds_report

它会把 DeepSpeed 版本、PyTorch 版本、CUDA/ROCm 信息、可用 GPU 数量、以及各类算子是否编译成功依次列出来。我升级后最关注这两行:

DeepSpeed general environment info: torch version: 2.3.0 config backend: JSON Compiler info: ... [WARNING] async_io not compiled

出现async_io not compiled不用太担心,它只影响 CPU offload 到 NVMe 盘时的部分性能,不影响普通 offload 到内存。但如果看到op name: [deepspeed.ops.adam] ... NOT COMPILED,说明你的安装有问题,最大可能是 PyTorch 和 DeepSpeed 的 ABI 不匹配,重新按 3.1 的顺序装一遍。

ds_report在 AMD ROCm 环境下也能用。输出里会显示 HIP 版本信息,你还能通过下面这行代码确认当前 PyTorch 是不是 ROCm 构建:

import torch print(torch.version.hip)

如果输出是None,说明你装的是普通 CUDA 版 PyTorch,后面即使 DeepSpeed 识别到 AMD 卡也跑不了算子。

3.3 最小 ZeRO-2 训练脚本

为了快速验证升级后的链路,我准备了一个非常小的 MLP 训练脚本,不依赖外部模型库,方便你直接复制测试。

先写配置ds_config.json:

{ "train_batch_size": 16, "gradient_accumulation_steps": 2, "optimizer": { "type": "AdamW", "params": { "lr": 1e-4, "weight_decay": 0.01, "betas": [0.9, 0.999] } }, "zero_optimization": { "stage": 2, "contiguous_gradients": true, "overlap_comm": true, "reduce_scatter": true }, "fp16": { "enabled": true } }

然后是训练脚本train.py:

import argparse import torch import deepspeed from torch import nn parser = argparse.ArgumentParser() parser.add_argument("--local_rank", type=int, default=-1) args = parser.parse_args() class MLP(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Linear(1024, 4096), nn.ReLU(), nn.Linear(4096, 16), ) def forward(self, x): return self.net(x) model = MLP().cuda() data = torch.randn(16, 1024).cuda() model_engine, optimizer, _, _ = deepspeed.initialize( args=args, model=model, model_parameters=model.parameters(), config="ds_config.json", ) for step in range(100): loss = model_engine(data).sum() model_engine.backward(loss) model_engine.step() if step % 10 == 0: print(f"step {step}: loss = {loss.item():.4f}")

启动命令:

deepspeed --num_gpus 1 train.py

单卡跑通之后,再试试多卡:

deepspeed --num_gpus 4 train.py

deepspeed.initialize会自己处理 rank 初始化,--num_gpus是从外层帮助分配分布式环境的。脚本里没有手工写torch.distributed.init_process_group,这是 DeepSpeed 的封装方式,不要多加。

3.4 AMD ROCm 环境下的安装与验证

在 AMD 机上,流程核心是换 PyTorch 安装源。先确认 ROCm 驱动和系统内核版本正常,然后创建虚拟环境并安装对应 ROCm 版 PyTorch:

python3.12 -m venv ds-rocm source ds-rocm/bin/activate pip install --upgrade pip setuptools wheel ninja pip install torch --index-url <你的ROCm版PyTorchwheel源>

安装完继续编译 DeepSpeed:

DS_BUILD_OPS=1 pip install deepspeed==0.18.4

ROCm 环境下编译会用到 HIP 编译器,耗时比 CUDA 版本稍长,这是正常的。装完跑ds_report,确认输出中能看到 HIP 工具链和可用显卡,再运行上面的train.py。单卡脚本里用了.cuda(),在 ROCm 的 PyTorch 里这依然有效,因为 ROCm 走的是兼容层,不要被这个方法名吓到。

多卡测试前建议把后端显式设清楚。DeepSpeed 在 AMD 机器上会尝试通过 RCCL 做 GPU 间通信,如果你和我在同一台机器上遇到卡住,可以先单卡跑脚本验证环境,再去查 RCCL 的日志和拓扑。

4. 实测对比与稳定性观察

4.1 测试条件与基线脚本

为了不引入太多变量,我把测试拆成两组:一组在单卡 24GB 的环境上跑 ZeRO-3 + CPU offload,目的是看长稳训练是否容易崩溃;另一组在 4 卡环境上跑 ZeRO-2,目的是看通信和吞吐抖动。模型统一用公司内部的一个模拟项目X,是 1B 参数量级的 GPT-2 结构,训练数据是随机生成的序列,完全等价于测压力。

对比时我保留了旧版 DeepSpeed 0.17.4 的虚拟环境,和 0.18.4 使用完全相同的训练脚本、配置和随机种子。唯一不变的判断标准是:能不能稳定跑完 5000 步,显存曲线有没有持续增长趋势,loss 下降是否出现异常平台。

我这里没有用精确到小数点后的吞吐数据,因为分布式训练里的 benchmark 受数据加载、磁盘缓存影响太大。我更关心的是“长时间运行下的成功率”,这是慢问题,也是版本升级最需要解决的。

4.2 稳定性表现:卡死、OOM 和收敛波动

在我单卡的 ZeRO-3 + CPU offload 测试中,旧版本跑到 1200 步左右出现过一次 optimizer state offload 后显存不释放的问题,模型还在反射,但显存占用逐渐爬升,最终 OOM。升级到 0.18.4 后,同样的配置连续跑完 5000 步,显存占用曲线非常平,基本稳定在 21GB 左右。这主要得益于对 offload 周期内临时缓存清理的修复。

4 卡 ZeRO-2 场景更有意思。旧版本在梯度 all-reduce 阶段偶尔会出现一个 rank 慢半拍,如果恰好有梯度裁剪和 loss scaling 同时发生时,整个训练直接 hang 住。v0.18.4 对通信超时和部分 rank 失败的处理更稳健,我连续跑了三轮 5000 步,只有一次是因为我一键开错了环境导致 rank 初始化失败,和框架无关。

收敛波动方面没有看到明显退化。ZeRO-2 下 loss 曲线和旧版本几乎重合,ZeRO-3 在关闭 offload 时也正常。总体上稳定性的提升比性能变化更明显,符合这种补丁级版本该有的调性。

4.3 性能影响:该升级是免费的吗

直接回答:升级到 0.18.4 不会让你的训练变慢,至少在 ZeRO-2 和 ZeRO-3 常规场景下没有负面开销。我加测了一个纯 ZeRO-1 的 1B 模型,吞吐和旧版本差异在 2% 以内,基本是噪声。

但有一点要注意:新版对overlap_comm和contiguous_gradients的实现有调整,如果你原来用旧配置里的默认值,升级后实际启用的路径可能不同。建议不要沿用旧配置文件一刀切,至少打开这三个字段看一眼:

"overlap_comm": true, "contiguous_gradients": true, "reduce_scatter": true

在 4 卡实测里,这三个参数全部开启后,GPU 利用率比默认值提高了约 5%,但显存峰值没有上升。如果你是显存敏感型场景,可以关掉contiguous_gradients,它在某些模型上会多留出一个小缓冲区,不过通信效率会降一点。

5. 常见问题排查与避坑清单

5.1 高频问题速查表

我整理了一张升级后最容易踩到的问题表,都是实测里见过的:

现象可能原因解决方式
安装时ninja: build stopped: subcommand failed缺少系统编译依赖或 setuptools 过旧安装python3.12-dev、libaio-dev,升级setuptools、ninja后重装
运行时提示No module named 'deepspeed.ops.adam'算子没有编译成功DS_BUILD_OPS=1 pip uninstall -y deepspeed && pip install deepspeed==0.18.4 -v
Python 3.12 环境里报ModuleNotFoundError: distutilsPython 3.12 移除了 distutils确认 setuptools >= 68,尽量使用 v0.18.4
AMD 卡上torch.version.hip返回 None安装的是普通 CUDA 版 PyTorch换用 ROCm 版 PyTorch 重新安装
ROCm 多卡训练卡住RCCL 通信路径异常先单卡跑通,再检查 RCCL 日志和驱动版本
checkpoint 加载后 loss 异常起飞跨版本恢复 checkpoint用旧版本导出全量权重后再升级,或避免跨小版本恢复
ZeRO-3 保存权重时内存暴涨zero_to_fp32.py旧版本合并逻辑使用 0.18.4 自带的合并脚本

这张表格比一大段问题说明实用得多,直接保存即可。

5.2 我反复踩过的三个坑

第一个坑是关于 Python 3.12 和libaio。新版 DeepSpeed 编译 CPU offload 的 NVMe 后端需要libaio-dev,但我在一个容器镜像里怎么装都报缺头文件,后来发现是镜像源里没有对应的包,换基础镜像才解决。如果你不用 NVMe offload,可以跳过这个依赖,但编译时会有 WARNING,不用慌。

第二个坑是 ROCm 环境里不能盲目照抄 CUDA 版的环境变量。之前在 NVIDIA 上习惯设置NCCL_DEBUG=INFO看通信日志,在 AMD 上有些变量并不完全等价,简单来说就是“相同的名字不代表同一套实现”。更可靠的做法是先用ds_report确认 DeepSpeed 识别到的后端,再去查对应后端文档调整参数。

第三个坑是配置文件里的fp16和bf16不能同时开。0.18.4 校验更严格,如果旧配置里两个字段都留着,启动时不会直接报错,而是一会儿用 fp16 一会儿用 bf16,导致 loss 波动。升级后务必检查配置里只有一个半精度字段处于启用状态。

6. 写在后面:关于升级时机的个人建议

如果你已经升到 Python 3.12,或者准备在 AMD ROCm 上把 ZeRO 和 offload 跑起来,DeepSpeed v0.18.4 确实是目前最值得尝试的版本。它没有引入需要重写训练循环的新 API,更多是消化前几个版本积累的技术债,这一点从安装和实测过程能明显感觉到。

我个人的升级顺序是:先在一个独立环境里跑通最小脚本,再把旧 checkpoint 单独复制一份做恢复测试,最后才切换正式训练任务。这套流程前前后后花不到两小时,但能避免把“框架升级”和“脚本问题”混在一起,排查起来会轻松很多。踩过几次坑之后,我的体会是版本号升不升并不重要,重要的是知道自己环境里真正吃紧的是哪一段链路。这次 0.18.4 帮你把 Python 3.12 和 ROCm 这两块地基补牢了,你只需要在升级后给自己留一点测试时间,剩下的交给它。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询