Chapter 12

常见错误与解决:cpu_adam、stage 3 hang、bf16 NaN

📌 本章基于 GitHub issues 高频问题与本基线 commit 510ebe58e4e4 。 调试 DeepSpeed 的第一句口诀: 先跑 ds_report ,把输出贴 issue。

12.1 调试链路总览

第一步要看的看什么
1ds_report op 是否编译、CUDA / PyTorch / NCCL 版本
2nvidia-smi 显存余量、GPU 利用率
3NCCL_DEBUG=INFO ./train.sh 2>&1 | tee log多机 hang 时尤其重要
4wall_clock_breakdown: true 看 fwd/bwd/step 谁慢
5ds_config 的 pydantic 校验报错 JSON 字段错

12.2 安装期错误(参见第 2 章 2.6 节)

现象修复
nvcc fatal: unsupported gpu architectureexport TORCH_CUDA_ARCH_LIST="8.0;9.0"
FusedAdam: [NO] DS_BUILD_FUSED_ADAM=1 pip install -U --force-reinstall deepspeed --no-build-isolation
CPUAdam: [NO] DS_BUILD_CPU_ADAM=1 pip ...
aio not built apt install libaio-devDS_BUILD_AIO=1

12.3 启动期错误

报错原因修复
pydantic.ValidationError: ... train_batch_size三个 batch 字段不一致留两个 + 一个 "auto"
bf16 and fp16 cannot both be enabled 同时开 bf16/fp16二选一
RuntimeError: Address already in use master_port 占用换端口 / kill 残留进程
NCCL Error: unhandled cuda error 多卡 driver 版本不一致 / NVLink 故障升 / 一致化 driver;查 nvidia-smi topo -m
FileNotFoundError: ds_config.json 路径问题绝对路径 / 在脚本所在目录运行
RuntimeError: missing required env: LOCAL_RANK用了 python 直接跑,没走 launcherdeepspeedtorchrun

12.4 ZeRO-3 hang(最难调)

典型表现:训练启动后 GPU 利用率 0,stdout 无新输出,几分钟后 NCCL timeout。可能原因:

原因诊断方法修复
某 rank forward 没碰到的层,
别人在 all-gather
NCCL_DEBUG=INFO,看 rank 间通信不对称检查模型是否有 if rank==0 等分支;统一控制流
一个 rank 早 return 了 同上 模型 forward 中所有 rank 必须执行同样数量的 module
数据 sampler 不一致 各 rank 打印 batch shape DistributedSampler;不要自己 shuffle
参数初始化没分布式 broadcast 各 rank 打印 model 参数 hash 初始化在 rank 0 → broadcast;或用 DS context manager
用 PyTorch 调度 + DS 同时存在 看你是不是手写 backward() 必须用 engine.backward()

调 hang 的杀手锏:

# 1. 打开 NCCL 详细日志
export NCCL_DEBUG=INFO
export TORCH_DISTRIBUTED_DEBUG=DETAIL

# 2. 把 timeout 调大,让 hang 暴露出来而不是直接 fail
export NCCL_TIMEOUT=3600   # 1 小时

# 3. 打开 DS 自己的 wall clock breakdown,看哪一步卡住
# ds_config.json: "wall_clock_breakdown": true

12.5 bf16 / fp16 NaN

现象多半原因修复
训了几十步 loss 突变 NaN fp16 数值溢出 改 bf16;降 LR;改 gradient_clipping
loss 一开始就 NaN 数据预处理坏了 查 dataloader 给的 input 是否有 NaN / overflow token
bf16 训得稳但 eval 时 NaN eval 没切 bf16 HF Trainer 同时设 bf16=True, bf16_full_eval=True
loss scale 频繁缩水 fp16 边界 initial_scale_power;或切 bf16
grad norm 爆涨 样本 outlier / LR 过大 gradient_clipping: 1.0;剔脏样本

12.6 内存不足

现象修复路线
第 1 步就 CUDA OOM activation_checkpointing、bf16;降 batch;改 ZeRO-2 → ZeRO-3
训几百步后 OOM 碰到长样本;开 packing 或卡 cutoff_len;样本长度 sort
save_checkpoint 时 OOM stage3_gather_16bit_weights_on_model_save 时要小心:保存的是 rank 0 上 gather 后完整模型;CPU offload
多卡某张 OOM 别张没事 用了 DataParallel?换 DDP / DS 模式;检查 --num_gpus 是否一致
NVMe offload 时反复 OOM 检查 offload_param.max_in_cpu;nvme_path 路径是否真 NVMe

12.7 checkpoint / 保存恢复问题

现象修复
ZeRO-3 保存后 pytorch_model.bin 不存在config 必须有 stage3_gather_16bit_weights_on_model_save: true
ZeRO-3 保存的是 shard,怎么合? 用仓库自带 zero_to_fp32.pypython deepspeed/utils/zero_to_fp32.py CHECKPOINT_DIR OUTPUT_DIR [--max_shard_size 5GB] [--safe_serialization] [--tag global_step100] [--exclude_frozen_parameters]
resume 训练 step 不连续 load_checkpoint 返回 (path, client_state),要从 client_state 取 step / epoch 接着跑
多卡数 / 配置改了之后 resume 失败 DS checkpoint 强绑定 ZeRO stage + DP 数;改了要重新预训
checkpoint 文件极大(占盘几百 GB) 保存频率太高 / 没限制;设 save_total_limit;ZeRO-3 + offload 时只保存 16bit

12.8 推理 / merge 错误

现象修复
init_inference: tensor parallel not divisiblehidden_size / num_heads / vocab 不是 tp_size 倍数
推理输出乱码 kernel_inject 替换错;试 replace_with_kernel_inject=False
训完想 merge LoRA 但模型是 ZeRO-3 切的 zero_to_fp32.py 合一份完整 HF 权重再 merge

12.9 多机训练特有问题

现象修复
ssh 跨机连不上 配 ssh 免密;~/.ssh/config 写好 Host;hostfile 用主机名 / 别名
所有进程读 ds_config.json 时 race config 放共享文件系统,所有节点路径一致
NCCL 卡在 init 查防火墙是否开放 NCCL_PORT_RANGE;查 NCCL_SOCKET_IFNAME 是不是选对网卡
训练慢 5 倍 没启用 InfiniBand;NCCL_IB_DISABLE=0,确认 nvidia-smi topo 显示 NVLink/IB
跨机 hostfile 写法不熟 worker-1 slots=8 每行一台;slots 即 GPU 数
有的机器编译过 op,有的没编 提前 DS_BUILD_OPS=1 统一编;或共享 wheel 缓存

12.10 提交 issue 的最小信息清单

遇到 DS 自身 bug 时(不是配置 / 数据问题),给 GitHub 提 issue 前准备这些:

  1. ds_report 完整输出;
  2. pip list | grep -i 'torch\|deepspeed\|transformers\|accelerate\|peft\|bitsandbytes\|flash'
  3. OS 信息:uname -a,CUDA 版本:nvcc --version,driver 版本:nvidia-smi
  4. 能复现的最小脚本(≤ 50 行);
  5. 完整 ds_config.json;
  6. 完整报错堆栈(NCCL_DEBUG=INFO + TORCH_DISTRIBUTED_DEBUG=DETAIL 时的);
  7. deepspeed --num_gpus N script.py 时的 N、是否多机;
  8. 如果是 HF Trainer + DS,提供 TrainingArguments 的 dict 化输出。

12.11 Stage 3 "悄悄变慢":trace cache 进入 INVALID

ch05.11 提过 PartitionedParameterCoordinator 的三态 trace 机制。INVALID 状态不会报错,只是 Coordinator 退化为同步 all-gather,整个训练时间会几倍恶化。常见触发原因:

触发修复
模型 forward 里有 if rank == 0: ... 这类 rank 分支把所有 rank 的 module 访问序列对齐;rank 特定逻辑挪到训练循环里做
动态控制流(不同样本走不同 module) if/else 但确保所有 rank 走同一分支;否则不要 stage 3
非全 rank 访问的外部参数没注册 deepspeed.zero.register_external_parameter(consumer_module, param)
动态构图(torch.compile / dynamic shape)频繁触发 recompile关闭 dynamic shape;用 packing 让 seq 长度齐整

诊断方法:

# Coordinator 内部有这个 flag,但默认不输出 warning
# 临时改源码:partitioned_param_coordinator.py 找 __log_trace_cache_warnings = True

# 或简单地:跑 5 步,断点看 coordinator.__trace_mode 的值
# COMPLETE = 健康;INVALID = 已退化

12.12 ZenFlow 启用条件错误

现象原因修复
ZenFlow requires PyTorch >= 2.1 老 PyTorch 升 PyTorch 或换 SuperOffload
ZenFlow requires cpu offload enabled没开 offload_optimizer.device=cpu补上 offload 字段,ZenFlow 只在 offload 路径下有意义
开了 ZenFlow + fused optimizer 第一步报错 FusedAdamZenFlowSelectiveAdamW 冲突config 不要写 "optimizer": {"type": "AdamW"},让 DS 自动选 ZenFlow 版
ZenFlow 训练 loss 很慢才下降 full_warm_up_rounds 太长调小 warmup;或 topk_ratio 调到 0.2+

12.13 Muon 优化器:fallback 与首步慢

现象原因修复
用 Muon 训练但发现 loss 第一步就 NaN embedding / LM head 用了 Muon(Muon 对小张量不稳定)给这些 param_group 写 "use_muon": false,走 AuxAdam 兜底
Muon 第一个 step 卡 30 秒 Newton-Schulz 矩阵正交化迭代要 warm-up GPU kernel正常,不是 bug;后续步会快
多 GPU 时 Muon 训练吞吐反而比 AdamW 低 Muon 自己的分布式版本需要 DS 包装 (MuonWithAuxAdam)确认 optimizer.type = "muon" 写在 ds_config 里,不要自己 import torch_muon
Muon checkpoint 加载到非 Muon 训练时报错 Muon optimizer state 有自定义字段resume 必须保持优化器一致;不能 Muon 训一半切 AdamW
心法 DS 这种"训练框架 + 系统软件"边界模糊的库,错误信息往往离根因很远。 调试时永远多问一句"我所看到的,真的是真相吗"; 打开 NCCL/Distributed/DS 三层 verbose 日志,比猜半天有用 100 倍。