模型 / 数据集
PRIME-RL/TTRL avatar
PRIME-RL/TTRL

TTRL:用多数投票当奖励,在无标注测试集上做强化学习

[NeurIPS 2025] TTRL: Test-Time Reinforcement Learning

1,123 个 Star81 个 ForkPythonMIT

秒懂

它是什么?
TTRL 把测试时扩展里的多数投票改造成奖励函数,让模型在没有标准答案的测试数据上继续做 RL。仓库基于 verl 实现,MIT 许可,但复现门槛是 8 张 A100 80GB。
适合谁用?
TTRL 适合手里有一批无标注推理题、想验证测试时训练是否有效的团队:模型规模在 7B 级别、能凑出 8 张 A100 80GB、并且愿意接受多数投票奖励本身带来的噪声。它不适合标注数据充足、或者算力只有单卡或消费级显卡的团队,因为仓库给出的实验环境是 8 x NVIDIA A100 80GB,示例脚本按这个规模配置。
能商用吗?
可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
还在维护吗?
在维护。仓库最近一次提交在 154 天前。
用什么语言写的?
主要是 Python(依据 GitHub 的语言统计)。

以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。

开源项目深度解析

没有标准答案时,奖励从哪来

推理类任务的强化学习通常依赖可验证答案:数学题对最终数字,代码题对单元测试。这套流程的前提是你能拿到 ground truth。TTRL 要处理的是另一类场景,手上只有测试集本身,没有标签,却希望模型在这批数据上继续训练。仓库把这个设定称为「在无显式标签的数据上做 RL」,并指出核心难点在于推理阶段的奖励估计。它的答案来自测试时扩展里的常规做法:对同一道题采样多次,用多数投票的结果当作参考答案。README 里那张 reward 伪代码图说明,实现 TTRL 只需要改奖励函数。读者需要清楚这意味着什么:奖励信号不是外部给定的,而是模型自己采样出来的共识。共识正确时训练有效,共识错误时训练会把错误固化下来。仓库没有回避这一点,它报告的结果显示 TTRL 能超过初始模型的 maj@n 上限,并接近用真实标签训练的模型,但这是在特定模型和基准上观察到的现象,不是对任意数据集的保证。

多数投票奖励的数据流

从仓库结构看,TTRL 的代码放在 verl 目录下,训练流程沿用 verl 的 rollout 与更新循环。一次迭代大致是:对一批无标注题目,模型按采样参数生成 n 个回答;这些回答被解析出最终答案,然后按答案分组统计,出现次数最多的那个答案成为该题的伪标签;每个回答根据是否与伪标签一致获得奖励;奖励再交给 verl 的策略更新部分。README 提到 TTRL 只被 maj@n 这一个指标监督,同时又说它能超过这个指标的上限,这一点值得留意,因为多数投票奖励的粒度很粗,它只区分「和多数一致」与「不一致」,不区分回答质量的高低。n 的取值直接决定伪标签的可靠性:n 太小,多数投票接近随机;n 太大,单步 rollout 的成本线性上升。仓库的示例脚本里给出了具体配置,但 README 正文没有把 n 的取值单独拎出来讨论,这一块需要读 examples/ttrl 下的脚本才能确认。

安装依赖与数据格式转换

README 给出的环境搭建步骤如下。先克隆仓库并进入 verl 子目录,然后建一个 Python 3.10 的 conda 环境,执行安装脚本,最后以可编辑模式安装当前包:

git clone https://github.com/PRIME-RL/TTRL.git cd TTRL/verl conda create -n ttrl python==3.10 conda activate ttrl bash scripts/install_ttrl_deps.sh pip install -e .

数据方面,README 明确说明可以用 verl/data/preprocess.py 把 JSON 格式的数据转成 Parquet,供 verl 训练使用。这一步容易被忽略,但它是必须的:verl 的数据加载器读的是 Parquet,直接喂 JSON 不会工作。仓库在 verl/examples/ttrl 目录下提供了多个模型和多个基准的脚本,README 建议代码细节参考 verl 官方文档,也就是说 TTRL 本身没有独立的配置文档,参数的语义要回到 verl 那边查。

复现 AIME 2024 的实际门槛

复现入口是一条命令:bash examples/ttrl/Qwen2.5/aime.sh。README 在 Getting Started 末尾注明,所有实验都在 8 x NVIDIA A100 80GB 上完成。这句话的信息量比看上去大:7B 模型的 RL 训练,rollout 阶段要为每道题生成 n 个回答,显存占用与 n 和序列长度同时相关,8 卡 80GB 是作者验证过的配置,单卡或 24GB 显卡大概率跑不动示例脚本的默认设置。仓库还提到,他们用预览版代码做了三次独立运行,两次 pass@1(贪心)为 43.3,一次为 46.7。作者把这些数字放在 README 里,本身就是在提示复现存在波动,同一份代码同一份数据,结果会有几个点的差异。评估 TTRL 时不应该拿单次运行的峰值当作基线。

verl v0.4.1 之后的一行开关

README 的 News 里有一条 2025-08-17 的记录:TTRL 被合入 verl v0.4.1,此后只需设置 +ttrl.enable=True 就能启用。这个改动改变了使用方式。在此之前,采用 TTRL 意味着维护一份 fork,跟随上游 verl 的更新会比较麻烦;在此之后,TTRL 变成上游的一个开关,升级 verl 时会一并带上。代价是版本绑定:这个开关只存在于 v0.4.1 及之后的版本,用更早的 verl 就得回到本仓库的代码。另外,仓库的 releases 列表里出现的两个标签是 verl (v2.0.0) 和 OpenRLHF (v1.0.0),这两个名字对应的是上游框架的版本标记,不是 TTRL 自己的版本号。把 release 标签当成 TTRL 的发布节奏会误判它的维护状态,判断这个项目是否活跃,应该看 main 分支的提交和 News 段落。

和带标签的 RLVR 差在哪

TTRL 的替代方案是标准的 RLVR 流程,也就是用真实答案做奖励,例如数学题比对最终数值、代码题跑测试用例。两者的差别不在算法实现,而在奖励的来源和可靠性。RLVR 的奖励是确定性的,对就是对,错就是错;TTRL 的奖励来自模型自身的采样分布,是估计值。这带来两个直接后果。第一,TTRL 的收益上限受初始模型的 maj@n 水平约束,README 承认这一点,同时报告 TTRL 能超过这个上限,但超过多少取决于任务和模型,不是通用常数。第二,如果初始模型在某类题目上系统性犯错,多数投票会一致地给出错误答案,训练会强化这个错误,而且因为没有标签,你无法从训练信号里察觉。RLVR 不存在这个问题,代价是你必须先把标注做出来。

什么时候不该用 TTRL

有几类情况应该直接排除 TTRL。标注数据已经充足时,用带标签的 RLVR 更直接,奖励信号更干净,也不需要把 n 设得很大来保证投票质量。算力受限时同样不适合,README 的实验环境是 8 x A100 80GB,示例脚本按这个规模写,缩到单卡需要自己重写并行配置和 batch 设置,而这部分 README 没有给出指导。任务本身不适合多数投票时也要谨慎:投票奖励要求答案可以被解析成离散的最终形式,数学题的数值、选择题的选项都满足,开放式生成、长文写作、代码重构这类没有唯一答案的任务,多数投票没有意义。最后,如果无法承受训练不稳定,TTRL 也不是稳妥选择,作者自己报告的三次运行里 pass@1 有 43.3 和 46.7 两个档位。

许可、维护与升级成本

仓库采用 MIT 许可,这意味着可以商用、可以修改、可以再分发,义务主要是保留版权与许可声明。需要注意的一点是,TTRL 的代码基于 verl 构建,verl 自身也是独立项目,其许可条款需要单独确认,不能因为 TTRL 是 MIT 就默认整条依赖链都是 MIT。升级成本方面,TTRL 合入 verl 上游之后,跟随 verl 版本升级是主要路径,但这也意味着上游对训练循环、配置项、数据加载器的改动会直接影响到 TTRL 的运行方式,README 已经把代码细节的查询指向 verl 文档,实际上是把这部分维护责任交给了上游。仓库最近一次 push 在 2026-04-15,News 里还有一条 2026-03-10 关于 URLVR 的记录,指向 urlvr-dev 分支上的另一份代码,说明主线之外还有并行的研究方向。分支上的代码和 main 分支的稳定性不是一回事,采用前要确认自己需要的是哪一份。

编辑结论

TTRL 适合手里有一批无标注推理题、想验证测试时训练是否有效的团队:模型规模在 7B 级别、能凑出 8 张 A100 80GB、并且愿意接受多数投票奖励本身带来的噪声。它不适合标注数据充足、或者算力只有单卡或消费级显卡的团队,因为仓库给出的实验环境是 8 x NVIDIA A100 80GB,示例脚本按这个规模配置。动手前先确认三件事:一是 verl 版本是否满足 README 提到的 v0.4.1 及以上,只有在这个版本上才能用 +ttrl.enable=True 开关;二是用 verl/data/preprocess.py 把 JSON 数据转成 Parquet 后,再检查采样参数 n 是否足够大,因为奖励完全来自 maj@n;三是先跑 examples/ttrl/Qwen2.5/aime.sh 观察训练曲线是否稳定,再决定要不要把 TTRL 用到自己的数据集上。

官方来源

  1. License: MIT
  2. PRIME-RL/TTRL on GitHub
  3. Project website
  4. README
  5. Releases
社区笔记

社区笔记