Chapter 11

调试与常见错误

📌 commit 4f9953be335e 从启动失败、OOM、NaN、NCCL hang 到 checkpoint 加载错的清单

分布式训练大半时间都是在调环境和处理诡异问题。本章汇总在 ColossalAI 上踩过的高频坑、定位思路和处理方法。

11.1启动类问题

现象原因处理
RuntimeError: CUDA error: invalid device ordinal CUDA_VISIBLE_DEVICES--nproc_per_node 不一致 对齐:8 个 GPU 就两者都设 8
colossalai run 卡 init 不动 master_port 被占;或 NCCL 网络无法选出 换端口;export NCCL_SOCKET_IFNAME=eth0
BUILD_EXT 编译失败 CUDA / gcc / torch 版本不匹配 op_builder/builder.py 报错;优先用 nightly wheel
ImportError: libcudart.so.12 CUDA toolkit 与 torch 编译期版本不一致 conda install cudatoolkit=12.1 或换 torch 版
多机时 worker 0 启动后立刻退出 MASTER_ADDR 写成了主机自身的 hostname 用 worker 0 的实际 IP;防火墙开端口

11.2OOM

看到 torch.OutOfMemoryError: CUDA out of memory,先看是哪个阶段:

快速降显存清单

  1. 开 bf16;
  2. 开 gradient_checkpointing_ratio=0.5;
  3. 降 micro_batch_size,开梯度累积;
  4. 开 ZeRO-2 / ZeRO-1;
  5. 开 Gemini + offload_optim_frac=1.0;
  6. 加 TP。

11.3NaN / loss spike

出现位置常见原因处理
第 0–1 步就 NaN fp16 的 initial_scale 太大;或数据中有特殊 token 换 bf16;或 initial_scale 降到 2^10
训到中途突然 NaN lr 过高;或某 batch 数据异常 降 lr 一半重试;保存复现 batch;开 anomaly detection
反向后 weight 包含 NaN grad clip 没生效,或 reduce 中数值越界 torch.nn.utils.clip_grad_norm_(params, 1.0)
load checkpoint 后 NaN ckpt 是用不同 TP/PP shard 保存的;加载时切法不一致 booster.load_model 而不是 model.load_state_dict

定位 NaN 来源的小工具:

torch.autograd.set_detect_anomaly(True)   # backward 时自动定位
# 或自己挂 hook
for name, p in model.named_parameters():
    p.register_hook(lambda g, n=name: print(n, g.isnan().any().item()))

11.4NCCL hang / 死锁

多卡训练里最难调的就是 NCCL hang——程序不报错,就是不动了。常见原因:

现象原因处理
部分 rank stuck 在 backward 不同 rank 走了不同 branch(if rank == 0 ...) 确保所有 rank 走相同 graph;通信前同步
每隔 N step hang 一次 data loader 给不同 rank 不同长度的 batch(导致 micro_batch 数不一致) 用 packing;或 attention_mask 对齐
多机时启动后无任何输出 hang IB 网络段路由错;NCCL 走 socket fallback 但端口不通 NCCL_DEBUG=INFO 看选了哪条网卡
save_checkpoint 时 hang 某些 rank 没参与 save 但其他 rank 在等 用 booster API(自带 barrier)

调试 NCCL:

export NCCL_DEBUG=INFO            # 详细日志
export NCCL_DEBUG_SUBSYS=ALL
export NCCL_ASYNC_ERROR_HANDLING=1   # hang 一定时间后抛错而不是死等
export TORCH_NCCL_BLOCKING_WAIT=1   # PyTorch 端等待时也抛错
export TORCH_DISTRIBUTED_DEBUG=DETAIL

11.5plugin 特定的坑

Gemini

HybridParallel

LowLevelZero

11.6性能类问题

现象定位处理
GPU 利用率(nvidia-smi)只有 40-50% data loader 喂不上 加 num_workers;用 packing;用 streaming dataset
第一个 step 慢,后续正常 JIT / fused kernel 第一次 compile warmup 一两步丢掉
某些 step 偶尔慢 5× data loader prefetch 用完;或 save_ckpt 触发 调 prefetch_factor;async save
多机比单机慢得多 跨机带宽 << NVLink 减 tp_size、多用 PP;或 IB 网络
开了 gradient_checkpoint 反而变慢 显存已经够,重算白白浪费 把 ratio 降到 0 或关掉

11.7checkpoint 加载问题

现象处理
"state_dict has missing keys" 说明 ckpt 是 PEFT/LoRA 增量;先 load base 再 load lora
切 TP 后加载 1-TP 的 ckpt 报 shape mismatch booster.load_model(model, ckpt_path),框架会自动 re-shard
HF 转 Colossal 后 loss 异常 q_proj/k_proj/v_proj 的 fused 顺序错;用官方转换脚本
ZeRO-3 切的 ckpt 在单卡 inference 加载失败 booster.save_model(shard=False) 合并成单文件

11.8定位思路(通用)

  1. 先单卡跑通:去掉 colossalai run,用 python 直接跑(plugin 选 TorchDDP),看是否能跑;
  2. 再两卡跑通:先 TP=2,再加 PP;
  3. 对比已知 baseline:跑官方 example 验证 ColossalAI 装对了;
  4. 开 verboseCOLOSSALAI_LOG_LEVEL=DEBUG NCCL_DEBUG=INFO
  5. 压力测试隔离:去掉 fused / flash / ckpt 等开关,最朴素 plugin 跑,逐个加回;
  6. 读源码报错位置:ColossalAI 注释还算友善,多半能在 Plugin/__init__ 里找到原因。

11.9这章你需要带走的