Skip to content

Qwen3-8B terminal-rl run (45h, ~1100 steps): peak 0.45 acc → mode collapse #2

@HansBug

Description

@HansBug

TL;DR

在和 issue #1 同一台 8× H200(143 GB/卡)上跑了一次 terminal-rl GRPO outcome-only(dense pass-rate reward,无 PRM、无 process reward),把模型换成 Qwen3-8B,启动脚本与上游官方 terminal-rl/terminal-rl_qwen3-8b.sh 在 args/算法配置上完全一致——只在 wrapper 层加了 4 处与本机环境相关的兼容补丁(conda activate / .env / LD_LIBRARY_PATH / --no-gradient-accumulation-fusion),并复用了 issue #1 列出的 7 处源码补丁(无新增)。


1 期望训练结果

terminal-rl 至今没有官方公开的 expected accuracy / reward 曲线,issue #1 已经把这一点穷举调研过(arxiv/2603.10165 Table 4 terminal 行为空、blog openclawrl1 / openclawrl2 的表格也只有 GUI/tool-call、伴生 paper arxiv/2602.02488 不含 terminal、main repo gh issue list -S "terminal" 至今没有数字回复、HF Papers 讨论区也没有),不重复展开。

唯一仍有指导性的参考是 Tech Report Table 4 / Table 3:

来源 setting outcome-only
Table 4 tool-call (250 step) 0.17
Table 4 GUI (120 step) 0.31
Table 4 terminal
Table 3 personal-agent Binary RL (8/16 step) 0.25 / 0.23

把本次 8B 全程 terminal/accuracy 均值 0.349 / 峰值桶 0.45+ 对照这两条只能说:量级合理,介于 GUI outcome-only(0.31)和 GUI integrated(0.33)之间,超出 tool-call outcome-only 基线(0.17)。仍然不能判断"达标"或"不达标"——这只是没有对照组的自我度量。


2 启动顺序和命令

完整可运行脚本:run_qwen3_8b_experiment.sh (gist)。该脚本对 上游官方 8B 脚本 的修改全部为 wrapper 层环境兼容,结构(cleanup_prev → start_router → check_router → check_gpus → detect_nvlink → start_ray_head → submit_job)和算法 args 完全保持。

2.1 4 处 wrapper 层 compat add

# 修改 为什么需要
1 conda activate tbench-rlset +u 包围) activate 脚本访问 unset 变量,set -u 会炸
2 source $REPO_ROOT/../.env 注入 WANDB_API_KEY/WANDB_PROJECT/WANDB_GROUP/WANDB_ENTITY
3 LD_LIBRARY_PATH=$CONDA_PREFIX/lib/python3.12/site-packages/nvidia/{cudnn,nvtx,cusparse,nccl,...}/lib TE 2.13 运行时找不到 libcudnn_graph.so.9 / NVTX / NCCL
4 MISC_ARGS+=( --no-gradient-accumulation-fusion ) 防御性保留:尽管本机最终装上了 Apex(pip install -v "git+...NVIDIA/apex" --no-build-isolation --build-option="--cpp_ext" --build-option="--cuda_ext"),保留该 flag 可在 Apex 更新失败时仍能跑(性能损失约 2–3 %)

diff 的算法 args 部分0 行变更——其它差异都是 wrapper 默认值(HF_CKPT=/nfs/models/Qwen3-8B / REF_LOAD=... / SAVE_CKPT=... / RAY_TMPDIR=/tmp/ray_tbench_8b)。

2.2 前置依赖

# /nfs/terminal-rl-workspace/  (NFS 工作区)
conda env 'tbench-rl'  (Python 3.12)
  torch 2.9.1+cu128
  sglang 0.5.10.post1
  ray 2.55.1
  mbridge 0.15.1
  transformer-engine 2.13.0     (prebuilt cu12 wheel)
  flash-attn 2.8.3              (prebuilt cu12torch2.9 wheel)
  apex (cpp_ext + cuda_ext)     (本次相比 issue #1 4B 跑新增)
  + slime -e .                   (editable install)

.env:
  WANDB_API_KEY=<key>
  WANDB_PROJECT=openclaw-terminal-rl
  WANDB_GROUP=qwen3-8b-terminal-rl
  WANDB_ENTITY=hansbug
  WANDB_MODE=online

ckpt:

HF_HUB_DISABLE_XET=1 hf download Qwen/Qwen3-8B --local-dir /nfs/models/Qwen3-8B

cd slime && source scripts/models/qwen3-8B.sh
python tools/convert_hf_to_torch_dist.py "${MODEL_ARGS[@]}" \
    --hf-checkpoint /nfs/models/Qwen3-8B --rotary-base 1000000 \
    --no-gradient-accumulation-fusion \
    --save /nfs/models/Qwen3-8B_torch_dist

2.3 Pool server(与 issue #1 共用,不重启)

docker run -d --name openclaw_pool_server --restart unless-stopped \
    -p 18081:18081 \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /nfs/terminal-rl-workspace/OpenClaw-RL:/nfs/terminal-rl-workspace/OpenClaw-RL \
    -e DATASET_DIR=/nfs/terminal-rl-workspace/OpenClaw-RL/terminal-rl/dataset \
    -e TBENCH_OUTPUT_ROOT=/nfs/terminal-rl-workspace/OpenClaw-RL/terminal-rl/build_outputs \
    -e TBENCH_DOCKER_IMAGE_SOURCE=build \
    -e ENV_SERVER_PORT=18081 \
    openclaw_pool_server:v1 \
    bash -c "cd /nfs/terminal-rl-workspace/OpenClaw-RL && \
        python -m terminal-rl.remote.pool_server --host 0.0.0.0 --port 18081 \
            --max-tasks 8 --max-runs-per-task 4 \
            --output-root /nfs/terminal-rl-workspace/OpenClaw-RL/terminal-rl/build_outputs"

2.4 启动训练

cd /nfs/terminal-rl-workspace/OpenClaw-RL
source /home/ubuntu/miniconda3/etc/profile.d/conda.sh && conda activate tbench-rl
: > terminal-rl/logs/training_8b.log
nohup bash terminal-rl/run_qwen3_8b_experiment.sh > terminal-rl/logs/training_8b.log 2>&1 &

# ckpt 保留守护(保留最新 KEEP_N=2 个 iter_*)
SAVE_CKPT=/nfs/terminal-rl-workspace/OpenClaw-RL/terminal-rl/ckpt/qwen3-8b-terminal-rl \
KEEP_N=2 nohup bash terminal-rl/ckpt_retention.sh \
    > terminal-rl/logs/ckpt_retention_8b.log 2>&1 &

2.5 和官方 terminal-rl_qwen3-8b.sh 在算法 args 上的差异

0 项——MISC_ARGS / OPTIMIZER_ARGS / GRPO_ARGS / ROLLOUT_ARGS / DISTRIBUTED_ARGS / WANDB_ARGS / CKPT_ARGS / EVAL_ARGS / PERF_ARGS 与上游 terminal-rl/terminal-rl_qwen3-8b.sh 完全相同(除去 --no-gradient-accumulation-fusion 这一条防御性附加)。


3 源码修改清单

与 issue #1 完全一致的 7 处仓库内 + 1 处 site-packages 补丁(继承自上次 4B 跑,未删未改)。完整 diff 内容、必要性、不打的后果、风险都在 issue #1 §4 详细列过,这里只列汇总并在右侧标注对本次 8B + TE + 8-GPU + 装 Apex 跑法是否仍严格必需

 Megatron-LM/megatron/core/utils.py                                        |  7 +++++--
 slime/slime/backends/megatron_utils/initialize.py                         |  4 +++-
 slime/slime/backends/megatron_utils/megatron_to_hf/qwen2.py               | 11 +++++++++++
 slime/slime/backends/megatron_utils/update_weight/update_weight_from_distributed.py | 22 ++++++++++++++++++----
 slime/slime/backends/sglang_utils/sglang_engine.py                        |  6 ++++++
 terminal-rl/remote/docker_compose_utils.py                                |  5 ++++-
 terminal-rl/remote/terminal_env.py                                        |  5 ++++-
 7 files changed, 51 insertions(+), 9 deletions(-)
# 修改 本次跑法严格必需 备注
4.1 Megatron-LM/.../utils.py:TE 未装时 is_te_min_version 安全回退 本次装了 TE 2.13,永远不进入 None 分支;保留无副作用
4.2 slime/.../initialize.py:numpy 2.x 硬 assert → warning sglang ≥ 0.5 把 numpy 拉到 2.3.5;不打补丁 0 步即崩
4.3 slime/.../megatron_to_hf/qwen2.py:补 local-impl 命名 本次走 TE 路径,不会撞到 local-impl 名字
4.4 slime/.../update_weight_from_distributed.py:NCCL Duplicate-GPU catch 本次 8 GPU 不会 dup;保留对 1-GPU debug 模式有用,对生产有风险(吞掉真实 NCCL 错)
4.5 slime/.../sglang_engine.py:HTTP 端的 NCCL Duplicate-GPU catch 同 4.4
4.6 terminal-rl/remote/docker_compose_utils.pybuild(timeout=) try/except terminal-bench 0.2.18 砍掉了 timeout kwarg;不打补丁 pool_server /reset 全 500
4.7 terminal-rl/remote/terminal_env.pyTerminal.start(timeout=) try/except 同 4.6
4.8 mbridge/models/qwen2.py(site-packages,非 git 追踪):补 local-impl mapping 走 TE 路径不撞;对 local 路径是 bug fix

本次 8-GPU + TE + Apex 跑法严格必需的仍是 4.2 / 4.6 / 4.7 三条。 其它五处沿用,对当前路径 0 副作用,但 4.4 / 4.5 在生产环境应改为 env-var gate 或彻底删除。


4 关键指标曲线

00_dashboard.png

单指标图(浅蓝原始 + 橙 EMA(α=0.1)):

terminal/accuracy(pytest 通过率)
01_accuracy.png

terminal/reward_mean(= 2·accuracy − 1)
02_reward_mean.png

rollout/raw_reward(训练 batch 均值)
03_raw_reward.png

train/grad_norm
04_grad_norm.png

train/kl_loss
05_kl_loss.png

train/entropy_loss
06_entropy_loss.png

terminal/non_trainable_ratio(无学习信号 group 比例)
07_non_trainable_ratio.png

rollout/response_len/mean(agent 输出 token 数)
08_response_len.png


5 全程指标统计(first20 / mean_all / last20)

指标 n min max first20 mean_all last20
terminal/accuracy 1093 0.000 1.000 0.221 0.349 0.276
terminal/reward_mean 1093 -1.000 1.000 -0.559 -0.302 -0.447
rollout/raw_reward 1096 -1.000 1.000 -0.357 -0.261 -0.492
train/grad_norm 1107 0.000 3.804 0.847 0.326 0.008
train/kl_loss 1107 0.000 2.491 0.012 0.151 0.073
train/entropy_loss 1107 0.000 0.539 0.064 0.103 0.066
terminal/non_trainable_ratio 1096 0.235 1.000 0.346 0.731 0.920
rollout/response_len/mean 1096 1.154 339.3 97.07 57.00 7.01

完整 summary_stats.json

{
  "wandb_run_id": "dvu9eexe",
  "wandb_url": "https://wandb.ai/hansbug/openclaw-terminal-rl/runs/dvu9eexe",
  "runtime_sec": 161973,
  "runtime_min": 2699,
  "runtime_h": 44.99,
  "lastStep": 0,
  "state": "crashed",
  "terminal/accuracy":         {"n":1093,"min":0.0,"max":1.0,    "mean_all":0.3487,"mean_first20":0.2207,"mean_last20":0.2764},
  "terminal/reward_mean":      {"n":1093,"min":-1.0,"max":1.0,   "mean_all":-0.3025,"mean_first20":-0.5587,"mean_last20":-0.4472},
  "rollout/raw_reward":        {"n":1096,"min":-1.0,"max":1.0,   "mean_all":-0.2611,"mean_first20":-0.3565,"mean_last20":-0.4917},
  "train/grad_norm":           {"n":1107,"min":0.0,"max":3.8038, "mean_all":0.3256,"mean_first20":0.8465,"mean_last20":0.0083},
  "train/kl_loss":             {"n":1107,"min":0.0,"max":2.4909, "mean_all":0.1511,"mean_first20":0.0119,"mean_last20":0.0726},
  "train/entropy_loss":        {"n":1107,"min":0.0,"max":0.5387, "mean_all":0.1034,"mean_first20":0.0638,"mean_last20":0.0659},
  "terminal/non_trainable_ratio":{"n":1096,"min":0.2353,"max":1.0,"mean_all":0.7306,"mean_first20":0.346,"mean_last20":0.9195},
  "rollout/response_len/mean": {"n":1096,"min":1.1538,"max":339.27,"mean_all":57.0029,"mean_first20":97.068,"mean_last20":7.0105}
}

6 训练阶段叙事(按时间序)

Phase A · iter 0 – 30 / 0–1.5h(冷启动)

Phase B · iter 30 – 200 / 1.5–8h(爬升 + 第一段平台)

  • accuracy 25 步桶在 0.30–0.49 摆动,三次跨过 0.45
  • raw_reward 维持 -0.20 ~ +0.10 区间
  • KL 短暂飙到 0.39(25 min 内回落到 0.27),同期 reward 反而上升 → 是有效大步策略更新,不是发散
  • non_trainable_ratio 0.33–0.47,仍有充足学习信号

Phase C · iter 200 – 600 / 8–25h(plateau)

  • accuracy 桶均值长期 0.30–0.45 区间,没有持续突破峰值
  • raw_reward 开始系统性下滑:mean_all 从早期的 +0.05 缓慢退到 -0.20
  • non_trainable_ratio 慢慢从 0.4 涨到 0.7
  • KL 持续低位(0.10–0.20),未越过 0.3 警戒,没有发散

Phase D · iter 600 – 1000 / 25–40h(学习信号枯竭)

  • non_trainable_ratio 锁死 0.85–0.95:每个 batch 8 个 sample 几乎全同结果(要么全过要么全失败),advantage = 0,没梯度可回传
  • grad_norm 跌到 0.01 数量级,模型实际上停止更新
  • response_len 从 ~100 token 单调下滑到 ~30 token

Phase E · iter 1000 – 1095 / 40–45h(mode collapse + 主动停)

  • response_len 进一步坍缩到 ~7 token——agent 几乎只输出空字符串 / 单个 token 的退化策略
  • accuracy 跌回 0.20–0.28
  • raw_reward 跌到 -0.50 区间
  • 26 日 09:42 主动 kill(pkill -SIGTERM parent + ray stop --force),ckpt 守护保留 iter_0001087 + iter_0001095

最佳 ckpt 出现在 Phase A → B 转折期(约 iter_0000127iter_0000183,单步 accuracy 跨 0.45+ / raw_reward 翻正首例)。该段已被 ckpt retention 守护淘汰(守护只保留最新 2 个)。这是和 issue #1 4B 跑同样的教训:outcome-only GRPO 的最佳 ckpt 出现在中前段,retention 策略需要换成"保留 best-by-eval-acc"而不是"保留最新"。

Phase C → E 的根因分析见本 issue 后续 comment——把 wandb / 训练日志 / 源码 / dataset / rollout trace 串起来定位。


7 与 issue #1(4B / 270 step)的对比

维度 issue #1(4B) 本次(8B)
runtime 10 h 45 h(×4.5)
训练量 ~270 wandb step / 261 rollout 1107 wandb step / 1093 rollout
accuracy 全程均值 0.345 0.349(基本持平)
accuracy peak(25 步桶) 0.385(step 60–79) 0.45+(iter 120–180 段,单步 1.0)
raw_reward 全程均值 -0.177 -0.261(更差)
raw_reward peak +0.572 +1.000(单 batch)
KL 漂移峰 0.54(279 步桶,主动停) 0.39(瞬时,自行回落)
grad_norm 末段 4.4(仍在更新) 0.008(事实停止更新)
non_trainable 末段 n/a(无该指标) 0.92
response_len 末段 ~150 token ~7 token
收尾原因 KL 漂移 0.54 主动停 non_trainable 锁死 + response 坍缩,主动停
最佳 ckpt 是否保留 (retention=2 淘汰) (同前)

核心结论:把 4B 跑了 270 步看到的 plateau,在 8B 上以 4× 时长走得更深,最终走到了 mode collapse——response 从 100 token 塌缩到 7 token,accuracy 从早期峰值 0.45 跌回 0.27。这不是发散(KL 一直在合理区间,grad 单调降到 0),而是 GRPO 在稀疏 outcome 信号 + 87% non-trainable 数据下的退化吸引子:既然几乎 ⅞ 的 prompt 拿不到学习信号,剩下 ⅛ 内出现的极端短输出(终端命令 1–2 token 就能 pytest pass 的简单题)会被反复加权,最后压垮其它行为。

issue #1 末尾对上游的建议(pin terminal-bench 版本 / --save-max-to-keep / 加大 --kl-loss-coef 或建议早停)继续成立;本次新增建议见 §9。


8 异常现象与诊断

8.1 SETA env /allocate 429 / /reset 500 风暴

训练中段(约 1h 左右)出现密集的 429 Too Many Requests500 Internal Server Error 在 RolloutManager 日志里刷屏。诊断后是 SETA env 的 docker compose pool 短时拥塞,rollout 内部已重试,并未阻塞训练步。把诊断 Monitor 的 grep 收紧(去掉 Error|429|500 这些关键字,只保留 Timer train end | saving checkpoint | Killed | OOM | RayActorError | Traceback 等致命签名)即可避免噪音洗版。不是真故障,但需要在文档里说一句(issue #1 没遇到,因为 4B 跑得短)。

8.2 grad_norm 单调下降到 0.01

4B 跑 270 步 grad_norm 仍在 4 量级,本 8B 1000+ 步后跌到 0.01 量级。直接原因是 advantage = 0 的 batch 占比 92%——GRPO 对 advantage 用组内 z-score normalize,组内全成功或全失败时 advantage 全 0,不向上回传梯度。这不是数值错误,是数据信号问题。

8.3 response_len 从 97 → 7 token

最 surprise 的现象。个别简单 SETA task 的 pytest pass 标准非常宽松("打印一行特定字符串"或"创建一个文件"),agent 在这部分用极短输出就能拿到 +1 reward。GRPO 在 ⅞ batch 没有信号、只有 ⅛ batch 有信号时,反复加权这些极短输出策略,长期累积压垮了其它行为模式。这是 outcome-only RL + 难度极不均匀数据集 + GRPO 的 well-known failure mode——issue #1 4B 跑没有撞到只是因为步数不够。详见后续 comment 的实证分析。


9 给上游的建议(在 issue #1 § 8 基础上新增)

  1. --save-max-to-keep N--save-best-by <metric>:本次同样因为只保留最新 2 个 ckpt 而丢失 phase A→B 的最佳权重。slime 至今没这两个 flag。
  2. non_trainable_ratio 早停 hook:当前 GRPO 实现下,如果 non_trainable_ratio 长期 > 0.7,训练实际上停止学习;建议默认加一个 patience-based 早停(比如连续 N 个 step 越过阈值就 abort)。
  3. 数据集难度自适应 / curriculum:SETA 1376 个任务难度极不均匀(最简单的是 echo / touch,最难的需要多步 shell + python 脚本),outcome-only 信号下天然让简单题主导梯度。建议给 terminal-rl/dataset/seta_env_convert/train.jsonl 配套一个难度分级或 reweight。
  4. 响应长度下界保护:在 reward 上叠加一个对极短输出的轻惩罚(或 length-normalized advantage),防止 mode collapse。
  5. README 加一条"已知失败模式"段落,把 §8.3 写进去——下游用户撞到 response 坍缩时无处对照。
  6. issue 实验记录:terminal-rl Qwen3-4B GRPO outcome-only 单节点 8×H200(10h,wandb lpurziy1) #1 §8 的 5 条建议(pin terminal-bench 版本 / inspect.signature 自适应 / --save-max-to-keep / KL coef 保护 / single-node 跳过 router_server)继续成立。

10 Attachments

  • 8 张单指标图 + 1 张 dashboard:见 §4,全部走 GitHub user-attachments CDN(无 commit / branch)
  • summary_stats.json:内联于 §5
  • 完整启动脚本:run_qwen3_8b_experiment.sh (gist)(375 行,含 4 处 wrapper compat add 注释)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions