--- library_name: transformers license: apache-2.0 license_link: https://huggingface.co/Palind/Qwen3-4B-SC-CodeAgent-200Steps/blob/main/LICENSE pipeline_tag: text-generation base_model: Qwen/Qwen3-4B base_model_relation: finetune datasets: - BAAI/TACO tags: - reinforcement-learning - score-centering - code-generation - agentic-rl - experimental - qwen3 --- # Qwen3-4B-SC-CodeAgent-200Steps 这是基于 **[Qwen/Qwen3-4B](https://huggingface.co/Qwen/Qwen3-4B)** 原始版本训练的探索性检查点,使用 Score Centering(SC)和自然异步 rollout,在一个经过筛选的 TACO 子集上完成了 200 个训练步。它不是 Qwen3-4B 的 2507 版本,也不是经过验证的通用代码 agent。 最终在固定的 256 题 TACO 开发集上,**130 题通过该实验保存的全部测试,准确率为 50.78%**。但工具使用非常少,且大量轨迹达到长度上限后没有可判题的完整提交。本次发布主要用于记录实验、分析训练行为和开展后续对照,**不据此宣称 SC 优于其他算法,或 agent 能力得到提升**。 ## 实验结果 ![200 步训练与开发集验证指标](./figures/training-summary.png) 每 25 步在同一固定开发集上验证一次,每题采样一个回答。下表列出全部 8 次结果,未省略回落点。 | 训练步 | 全测试通过题数 / 256 | 题目准确率 | 逐题测试通过率均值 | 有可提取代码的比例 | 实际使用工具的题数 | |---:|---:|---:|---:|---:|---:| | 25 | 94 | 36.72% | 37.08% | 38.28% | 3 | | 50 | 93 | 36.33% | 38.10% | 39.84% | 2 | | 75 | 104 | 40.63% | 43.27% | 45.70% | 0 | | 100 | 114 | 44.53% | 47.56% | 50.78% | 0 | | 125 | 118 | 46.09% | 51.60% | 55.08% | 0 | | 150 | 117 | 45.70% | 51.54% | 56.64% | 0 | | 175 | 123 | 48.05% | 53.50% | 56.64% | 0 | | 200 | 130 | 50.78% | 56.11% | 61.33% | 0 | 指标含义: - **题目准确率 `acc`**:最终提交通过该题全部保存测试时记为 1,否则为 0。 - **逐题测试通过率均值 `pass_rate`**:先计算每题通过测试的比例,再对题目取平均;不是把所有题的测试混在一起计算通过率。没有可判题提交的题目记为 0。 - **有可提取代码 `format_ok`**:判题器能够从末次助手回答中提取闭合代码块。这不保证代码正确,也不强制要求 `` 标签。 - **实际使用工具**:隔离执行器确实启动了 Python 进程;只生成调用描述或无效参数不计为实际执行;程序启动后报错则仍算使用过工具。 第 200 步的开发集结果可进一步分为:157/256 题有可提取代码,99/256 题没有可判题提交;有提交的 157 题中,130 题全通过。后一个条件比例不能替代完整开发集准确率。另有 4 题出现程序执行错误或超时,共涉及 123 个测试用例;这不是“123 道题出错”。未出现判题累计执行预算耗尽。 ![SC、梯度和实际滞后诊断](./figures/sc-diagnostics.png) 上图显示 SC 修正项、梯度范数、top-128 未覆盖的概率质量,以及实际采样版本滞后。概率覆盖率高不等于算法优于其他方法。 逐步数值见[原始指标文件](./metrics/metrics.jsonl)、[结果摘要](./metrics/summary.json);实验设置见[训练配置](./experiment/training_config.json)。 ## 工具行为与截断 训练日志共记录 **12,800 条轨迹**,其中只有 **40 条(0.3125%)**实际执行过工具。最后 50 个训练步的工具使用率为 0;最终开发集的 256 条轨迹也均未调用工具。 长度上限仍是明显限制。最后 50 步的训练轨迹达到响应长度上限的比例平均为 **40.81%**;最后一批 64 条中,34 条达到 8,192 tokens,32 条没有可判题提交,且这 32 条全部达到长度上限。抽查可见尚未结束的推理和未写完的代码。这些是训练轨迹的统计,不能直接当作最终开发集的截断比例。 因此,这个检查点的已观测行为主要是直接生成代码,尚未展示稳定的“调用工具—阅读反馈—修改方案”能力。工具调用稀少也不能单独证明 SC 无效:本次没有匹配的算法对照,亦未隔离任务、提示、长度预算和优化目标各自的影响。 ## 训练方法与配置 训练采用 verl 原生 `ToolAgentLoop` 加自定义 `run_python` 工具,rollout 与训练使用独立 GPU 池并行进行。**本实验没有使用 Harbor、Terminal-Bench 或 SWE 环境**;它是带可选 Python 自测工具的标准输入/输出编程任务,工具调用之间没有持久文件系统状态。 SC 通过中心化策略的 score 来抑制训练与采样分布不一致时的漂移。本实验使用采样端每个生成 token 的 top-128 概率构造近似中心化项,并扩展了多轮工具轨迹的元数据对齐。工具观察和其他非模型生成内容不参与策略损失。 | 项目 | 配置 | |---|---| | 初始化 | 原始 `Qwen/Qwen3-4B`,thinking 模式开启 | | 训练框架 | verl,基于 upstream `8718ca30a3f002f93b7c4fd99b9b2506718681bc`,加本实验修改 | | 硬件 | 8 × NVIDIA A100 80GB;4 张训练,4 张独立 rollout | | 训练 / 推理后端 | FSDP2 / vLLM,rollout TP=1 | | 数值精度 | BF16 计算,FP32 主参数 | | 本仓库发布权重 | 从训练检查点导出为 BF16,供推理使用 | | 更新目标 | REINFORCE 形式的策略损失 + SC;`bypass_mode=True` | | 优势估计 | 每题 4 条回答,减去组内平均奖励,不按组内标准差归一化 | | 其他修正 | 不使用 TIS、RS 或 PPO clipping;不使用 critic、KL 惩罚或熵奖励 | | SC 概率记录 | top-128;这是概率记录宽度,不是生成时的 top-k 截断 | | 优化器 | AdamW,学习率 `1e-6`,恒定学习率,无 warmup,weight decay=0 | | 梯度裁剪 | 全局范数上限 1.0 | | 批次 | 每训练步 16 题 × 4 回答,共 64 条轨迹;`ppo_epochs=1` | | 训练长度 | 200 个 trainer steps;每 25 步验证、每 50 步保存检查点 | | 异步设置 | `separate_async`,每个训练步同步权重,预填充 2 批 | | 滞后控制 | `max_off_policy_threshold=4`,超出限制使用 `drop` 策略 | | 上下文预算 | prompt 最长 4,096;响应最长 8,192;总长度上限 12,288 tokens | | 多轮预算 | 最多 6 个助手轮次,每轮最多 1 个并行工具调用 | | 训练采样 | temperature=1.0,top-p=1.0,不启用生成 top-k 截断 | | 开发集采样 | 每题 1 条,temperature=0.6,top-p=1.0,不启用生成 top-k 截断 | | 随机性 | 单次运行,数据种子 42;没有多种子重复实验 | 这里的滞后来自真实的生成、队列和训练并行,未人为给每条轨迹固定增加 4 步延迟。每步同步也不会使已经生成的数据重新变成当前策略样本。日志中记录到的最大轨迹版本跨度为 4。 奖励使用最终代码的测试通过比例,范围为 `[0, 1]`,没有工具使用奖励。模型可以用题面样例或自己构造的 stdin 调用工具,但不能通过工具查询隐藏测试;隐藏测试仅由最终判题器使用。 ## 数据与划分 数据来自 [BAAI/TACO](https://huggingface.co/datasets/BAAI/TACO),固定 revision 为 `d593ed0a2becbbc952230bb89be09189bf1056dc`。为控制首次实验的数据准备成本,使用 `ALL` 配置中的训练分片 02、03、04,再进行筛选。**这不是对整个 TACO 的随机抽样,也不是官方 TACO 测试集结果。** 筛选保留标准输入/输出、Python 标准库可执行任务,检查题目与测试结构、包含工具 schema 后的 prompt 长度,排除图像依赖及疑似 special-judge 任务,并按规范化文本、URL 和平台题号去重。每道入选题至少有一份上游参考解在相同隔离判题器下通过全部保存测试。通过参考解校验不等于证明题目或标签绝对正确,special-judge 排除亦是保守启发式。 最终按固定规则划分 **2,000 题训练集 + 256 题开发集**,两者在上述身份键下无重叠。参考解和隐藏测试不进入模型提示。开发集被反复用于训练期间验证,因此本卡将其称为 **dev**,而非未触碰的最终测试集。 另已准备官方 LiveCodeBench `release_v6` 的 public/private tests,并用于已知题目身份的交叉排除;**本检查点尚未在 LiveCodeBench 上评测,没有可报告的 LCB 分数**。去重不能证明基础模型预训练数据中不存在污染。 ## 执行与判题边界 `run_python` 每次运行一个独立 Python 3.12 进程,使用 Linux Landlock 文件访问限制与 seccomp 系统调用白名单。网络、GPU、用户目录读取、子进程和文件写入被禁止,只提供解释器、标准库、必要系统库以及当前程序。它是访问隔离,不是完整虚拟机;部分路径元数据仍可能可见。 单次执行上限为 3 秒、1 GiB 地址空间,每个输出流最多 1 MiB。判题累计执行预算为 90 秒,排队耗时单独记录,不计入该预算。启动失败通过用户程序无法继承的专用状态管道报告,不把基础设施故障当成普通错误答案。 判题只使用最后一个助手生成段中的代码。超长整数输出采用精确十进制字符串比较,避免 Python 整数转换位数限制;浮点输出采用容差比较。这里使用的是实验自定义判题器,**不声称与官方 LiveCodeBench checker 完全一致**。 ## 发布文件与导出验证 本仓库发布最终第 200 步的 BF16 推理权重、tokenizer、chat template 和 generation config,并附带训练指标与配置摘要。它不是带 optimizer 状态的训练恢复包,也没有打包完整训练环境或工具 harness。 官方 verl 合并器将四个 FP32 训练分片转换为 BF16。已检查导出 tensor 的名称、形状、有限性以及与源分片 BF16 转换结果的一致性,并完成本地 tokenizer 与短前向加载检查;导出后没有重新运行完整开发集,卡片分数来自训练任务中的对应 checkpoint。导出细节见[转换清单](./conversion_manifest.json)。 ## 使用 下面展示普通文本生成,采用基础模型默认的 temperature=0.6、top-p=0.95、top-k=20。这与上表开发集评测设置不完全相同。模型生成的 Python 代码不会由此示例自动执行。 ```python import torch from transformers import AutoTokenizer, AutoModelForCausalLM model_id = "Palind/Qwen3-4B-SC-CodeAgent-200Steps" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForCausalLM.from_pretrained( model_id, dtype=torch.bfloat16, device_map="auto", ) messages = [ {"role": "system", "content": "Solve the programming task in Python 3. Finish with a complete solution in a Python code block."}, {"role": "user", "content": "Read two integers from standard input and print their sum."}, ] prompt = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, enable_thinking=True, ) inputs = tokenizer(prompt, return_tensors="pt").to(model.device) with torch.inference_mode(): output = model.generate( **inputs, do_sample=True, temperature=0.6, top_p=0.95, top_k=20, max_new_tokens=8192, ) print(tokenizer.decode(output[0, inputs.input_ids.shape[1]:], skip_special_tokens=False)) ``` 模型权重只生成文本;本仓库不是可独立运行的 agent 产品。要复现带工具的设置,还需要给 chat template 提供 `run_python(code, stdin)` 的函数 schema,并由外部 harness 注册工具、解析调用、在隔离执行器中运行、把工具响应交回模型。单独调用 `generate()` 不会自动建立工具执行环境,也不等价于本实验的评测协议。 ## 目前能支持的结论 这次运行验证了 4B 模型、8 张 A100 上 SC 与自然异步代码任务链路可以完成 200 步训练,并留下了逐步指标。已记录开发集准确率从第 25 步的 36.72% 上升至第 200 步的 50.78%。**第 25 步不是初始化基线**:没有 step-0 评测,因而不能据此声称相对基础模型提升了 14.06 个百分点。 此外,本次没有匹配计算预算的无 SC 对照、没有多种子重复、没有外部 benchmark 成绩。工具交互稀少且后期消失,响应截断比例仍高。该检查点适合作为后续对照和诊断的起点,尚不足以支持算法优势、通用 agent 能力或长程软件工程能力的结论。 ## 相关来源 - [Qwen3 原始模型](https://huggingface.co/Qwen/Qwen3-4B) - [Score Centering Stabilizes Off-policy Reinforcement Learning](https://arxiv.org/html/2609.20807v1) - [verl](https://github.com/verl-project/verl) - [TACO 数据集](https://huggingface.co/datasets/BAAI/TACO)