# Debug 指南 ## 对齐精度 在开发 slime 的过程中,经常会需要检查模型的精度是否正确,可以通过以下方式检查: 1. 训练第一步 1. rollout 的生成是否是人话,如果不是,有以下 2 种可能: - 参数没有正常加载。需要查看是否有 megatron 成功加载 ckpt 的日志; - 更新参数有误。可以查看是不是所有的参数都做了转换和参数对应,或者参数名是不是根据并行做了转换(例如 pp_size > 1 时,第二个 stage 提供的参数的 layer id 是不是正确的)。一个比较彻底的方法是在对应模型的 sglang 实现的 `load_weights` 中保存所有的参数,查看和加载的 ckpt 中是否一致; - 如果所有参数更新都正确,还出现问题,有可能是 sglang 里有一些特殊的 buffer 在 release 的时候被释放了; - 如果是用 pretrain 模型进行的测试,可以换成同结构模型的 instruct 版本,查看这种乱码是不是 pretrain 模型特有的。 2. 查看打印的 rollout stats 的 `log_probs` 和 `ref_log_probs` 是否完全相等(即第一步 kl=0),且值较小 - 如果不是完全相等的,一般是 transformer engine 中的某些 non-deterministic kernel 导致的,例如: - 在某些版本的 te 里,megatron 需要 `--attention-backend flash`,来强制使用 flash attention,从而避免 CP 下 fused attention 的数值不稳定; - 如果数值较大(例如 >1),一般有 2 种可能: - 如果值非常大,应该是训练配置有问题; - 如果值只是比 sft loss 的状态略大,例如 instruct 模型的 logprob 到了 0.8,有可能是数据不符合训练的 chat template,或者不符合冷启动的分布。 3. 查看在推一训一(`num_steps_per_rollout == 1`),kl 是否为 0,grad_norm 是否较小 - 基本上就是一些 megatron / te 相关的 bug,例如: - moe 需要开启 `--moe-permute-fusion`。 2. 训练第二步 1. 对于训推一体,查看是否能正确加载第二步,是否会 OOM; ## 训练推理单独 debug slime 支持将训练部分和推理部分分开进行调试,从而实现: - 在调优/debug 推理部分时,只用少量卡就可以启动任务; - 在调优/debug 训练部分时,可以保证模型输入固定,去除 rollout 的随机性。 具体来说,目前 slime 提供了如下的参数来进行分离调试: 1. `--debug-rollout-only` 开启后,slime 将不会加载 megatron,只初始化 sglang ,可以用这个方法来进行推理部分的调试。 1. `--debug-train-only` 开启后,slime 将不会加载 sglang,只初始化 megatron ,可以用这个方法来进行训练部分的调试。 2. `--save-debug-rollout-data /your/saved/debug/data_{rollout_id}.pt` 开启后,会保存每次 rollout 的结果,可以和 `--debug-rollout-only` 配合使用。注意保存的方式为 `args.save_debug_rollout_data.format(rollout_id=rollout_id)`。 3. `--load-debug-rollout-data /your/saved/debug/data_{rollout_id}.pt` 开启后,会从 `args.load_debug_rollout_data.format(rollout_id=rollout_id)` 来加载数据,并且不会初始化 sglang(自动设置 `debug_train_only=True`)。可以以这种方式来固定训练部分的输入,对训练部分进行调优,例如切换各种并行。 ## INT4 / Compressed-Tensors 量化 Checkpoint 问题 使用 INT4 量化模型(如 `compressed-tensors` 的 `W4A16`)时,checkpoint 的 `config.json` 中有一个 `quantization_config.ignore` 列表,指定哪些参数**不**做量化。在线权重更新(Megatron → SGLang)时,slime 也会读取这个 ignore list 来决定哪些参数需要 INT4 量化。ignore list 不正确会导致静默错误: 1. **MoE 路由权重(`mlp.gate.weight`)变成全零** MoE 的路由权重(`mlp.gate.weight`,shape `[num_experts, hidden_size]`)是一个普通的 2D weight tensor,但它**不是** Linear 层的权重。如果它不在 ignore list 中,在线量化器会把它 INT4 量化为 `weight_packed`、`weight_scale`、`weight_zero_point` 等。然而 SGLang 不会以量化名称来加载路由权重,因此这些参数在 `load_weights` 时被静默跳过,导致 gate 权重全零。 **修复方法**:确保 `config.json` 的 ignore list 中包含 `"re:.*mlp\\.gate\\..*"`。 2. **其他非 Linear 的 2D 权重** 类似问题可能出现在任何不是真正 Linear 层的 2D `.weight` tensor 上,例如 `model.embed_tokens.weight`。务必检查 ignore list 覆盖了所有非 Linear 权重。 **推荐的 ignore 配置**(以 GLM 系 MoE 模型为例): ```json "ignore": [ "lm_head", "model.embed_tokens.weight", "re:.*self_attn.*", "re:.*mlp\\.shared_experts.*", "re:.*mlp\\.gate_up_proj.*", "re:.*mlp\\.gate_proj.*", "re:.*mlp\\.up_proj.*", "re:.*mlp\\.down_proj.*", "re:.*eh_proj.*", "re:.*mlp\\.gate\\..*" ] ``` 3. **safetensors 分片缺失** 转换工具偶尔可能产出不完整的 checkpoint(例如缺少 `model-00010-of-00093.safetensors`)。转换完成后,务必检查: - `.safetensors` 文件数量是否与预期一致。 - `model.safetensors.index.json` 中是否包含所有 layer 的条目。 - 抽查关键 layer(如第一个 MoE layer)的 key 数量是否正确。 4. **如何排查** - 使用 `--check-weight-update-equal` 验证 Megatron → SGLang 权重同步后的值是否正确。如果某个参数在 SGLang 侧全为零,说明它可能被错误量化或在 checkpoint 中缺失。 - 使用 `--debug-rollout-only` 配合少量 GPU,快速测试 SGLang 能否从量化 checkpoint 正常生成文本。 ## Debug sglang illegal memory access (IMA) 在进行大规模 RL 时,不时会遇到 SGLang IMA 的问题,以下是我们的一些 debug 建议: 1. 开启 `CUDA_LAUNCH_BLOCKING=1` 2. 开启关闭投机采样、 cuda graph 来查看问题是否消除 IMA 经常出现在 cuda graph replay 的 padding 中,或者是投机采样与主模型的区别。经常可以通过两者的各种开关组合来缩小问题。 3. 关闭 deepep 如果训练和推理中开启了 deepep,可以关闭查看有没有差别 4. 尝试 CUDA Core Dump 确定报错 kernel 这里推荐 vllm 团队的这一文档:[CUDA Core Dump: An Effective Tool to Debug Memory Access Issues and Beyond](https://blog.vllm.ai/2025/08/11/cuda-debugging.html) ## 使用 Ray Distributed Debugger 单步调试 Ray 提供了基于 debugpy 的[分布式调试器](https://docs.ray.io/en/latest/ray-observability/ray-distributed-debugger.html),可以在 driver 进程中设置断点并单步执行代码。 1. 安装 debugpy: ```bash pip install debugpy==1.8.0 ``` 2. 在启动脚本中启用 `RAY_DEBUG_POSTMORTEM`: ```bash export RAY_DEBUG_POSTMORTEM=1 RUNTIME_ENV_JSON="{ \"env_vars\": { ... \"RAY_DEBUG_POSTMORTEM\": \"${RAY_DEBUG_POSTMORTEM:-0}\" } }" ray job submit --address="http://127.0.0.1:8265" \ --runtime-env-json="${RUNTIME_ENV_JSON}" \ -- python3 train.py [args...] ``` 3. 在 `train.py` 中的 `breakpoint()` 前添加 `ray.init()`: ```python if __name__ == "__main__": ray.init() breakpoint() args = parse_args() train(args) ``` 必须先调用 `ray.init()`,因为分布式调试器依赖 `core_worker`,只有 Ray 初始化后才可用。否则 `breakpoint()` 会报错 `AttributeError: 'Worker' object has no attribute 'core_worker'`。 4. 通过 VS Code 连接调试器: 在 VS Code 中安装 [Ray Distributed Debugger](https://marketplace.visualstudio.com/items?itemName=ray-project.ray-distributed-debugger) 扩展。运行启动脚本提交 job 后,当 job 执行到 `breakpoint()` 暂停后,在 VS Code 的 Ray Dashboard 面板中点击活跃的断点即可 attach 调试器,之后可以直接在编辑器中单步执行、查看变量、设置新断点。 > **注意**:调试完成后务必移除 `ray.init()` 和 `breakpoint()`。不带参数的 `ray.init()` 在多节点训练中可能导致问题,因为 `ray job submit` 会注入特定的 namespace 和 runtime environment 配置。