Chapter 11
调试与常见错误
分布式训练大半时间都是在调环境和处理诡异问题。本章汇总在 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,先看是哪个阶段:
- 第 0 步就 OOM:模型本身没装下 → 加 TP / 加 Gemini offload;
- 第 1 步 OOM:activation 太大 → 开 gradient_checkpoint;
- 第 N 步突然 OOM:内存碎片或泄漏 →
torch.cuda.empty_cache()看是否复发; - Generate 时 OOM(PPO 场景):kv cache 太大 → 降 max_new_tokens 或开 PagedAttention。
快速降显存清单
- 开 bf16;
- 开 gradient_checkpointing_ratio=0.5;
- 降 micro_batch_size,开梯度累积;
- 开 ZeRO-2 / ZeRO-1;
- 开 Gemini + offload_optim_frac=1.0;
- 加 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
- 第 0 步 OOM:chunk 太大;调小
search_range_m到 64; - save 后再 load 报 chunk size mismatch:必须用同样的
chunk_config_dict加载; - CPU OOM:
offload_optim_frac=1占满 RAM;降到 0.5。
HybridParallel
- tp_size 不整除 num_heads:报 assertion;调整 tp_size 或换模型;
- num_microbatches 太少导致 PP 气泡占满:micro_batch 数应 ≥ 4 × pp_size;
- save ckpt 后用 transformers 加载失败:用
sharded_strategy='hf'。
LowLevelZero
- cpu_offload 后训练巨慢:Optimizer 在 CPU 上 step;考虑用 Gemini 的 HybridAdam;
- stage=2 时显存仍很高:activation 没切;考虑 grad ckpt 或加 TP。
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定位思路(通用)
- 先单卡跑通:去掉 colossalai run,用 python 直接跑(plugin 选 TorchDDP),看是否能跑;
- 再两卡跑通:先 TP=2,再加 PP;
- 对比已知 baseline:跑官方 example 验证 ColossalAI 装对了;
- 开 verbose:
COLOSSALAI_LOG_LEVEL=DEBUG NCCL_DEBUG=INFO; - 压力测试隔离:去掉 fused / flash / ckpt 等开关,最朴素 plugin 跑,逐个加回;
- 读源码报错位置:ColossalAI 注释还算友善,多半能在 Plugin/__init__ 里找到原因。
11.9这章你需要带走的
- OOM 优先按 6 步降显存清单走;
- NaN 优先换 bf16 + clip_grad_norm;
- NCCL hang 必看
NCCL_DEBUG=INFO+ 检查 if-else 分支是否所有 rank 一致; - ckpt 加载尽量用
booster.load_model而不是 PyTorch 原生 API; - 跑不通先回退到 TorchDDP plugin 验证 ColossalAI 安装;
- 开
TORCH_DISTRIBUTED_DEBUG=DETAIL是分布式调试的标配。