最近审阅:2026-10 · 前置:RL 与语言模型、PPO clipping
先跟着一条回答走一遍
给模型一道乘法题,它调用计算器,读到结果,再写出答案。到这里,我们有了一次运行记录。但要用它训练,还得知道:哪些 token 是模型选的?生成时用的哪版权重?它是真的完成了,还是被长度限制叫停了?
这些信息不是事后可有可无的日志。丢了它们,可能连 loss 应该算在哪些位置都说不清。下面先讲 rollout 记录什么,再看吞吐、log-prob 和上下文怎样影响训练。算例均为教学假设,不是某个框架的实测性能。
Rollout 不等于一条完整答案
Rollout 是让策略实际运行,收集动作、观察和结果的过程。 可以一次跑一个任务,也可以批量跑;可以收集完整任务,也可以只收集一段。它本身不规定一定使用最新策略,也不规定一定有 Reward Model。
| 名称 | 在计算器任务里指什么 | 容易混淆的地方 |
|---|---|---|
| Completion / 一段生成 | 一次模型调用生成的 token 序列 | 工具调用也可能是一段生成,不一定是最终答案 |
| Assistant turn / 助手回合 | 助手的一次发言或工具请求 | 具体边界取决于对话模板与运行协议 |
| Episode / 完整任务 | 从题目到成功、失败或定义好的终点 | 可能包含多次模型调用和环境返回 |
| Fragment / 轨迹片段 | 收集器暂时截下的一段过程 | 不一定走到了任务终点 |
模型生成了结束标记,可能只是这一轮发言结束;工具还没返回,整个任务就还没有完成。反过来,若任务本来只要求一条回答,一次 completion 也可以就是整个 episode。
- 模型生成请求计算 → 写出答案两段 assistant 输出都可以含策略动作。保留采样 token、对应 log-prob 和生成版本。
- 环境返回计算器返回 12结果会进入后续上下文,但不是策略采到的动作。不能直接给它套 policy loss。
一条能用于训练的记录
想象记录中有这些片段,长度只是虚构数字,不代表实际 tokenizer 的切分:
| 片段 | token 数 | 是否作为策略动作计算 loss | 原因 |
|---|---|---|---|
| 用户题目 | 6 | 否 | 是输入条件 |
| assistant 工具请求 | 4 | 是,按该任务约定 | 是模型实际生成的选择 |
| 计算器结果 | 3 | 否 | 来自环境,不是策略采样 |
| assistant 最终回答 | 5 | 是 | 是模型生成的答案 |
| padding | 2 | 否 | 只是对齐批次 |
输入共有 20 个位置,但有效动作只有 9 个。policy loss 的分母不能随手写成 20。工具结果虽然不直接产生 policy loss,仍会影响后续动作;“不算它的 loss”不等于“从上下文里删掉”。角色 / 控制标记是否为采样动作,要按真实生成协议决定,不能仅凭文本长相。
记录至少包含下列几类信息,字段名随实现而定:
| 信息 | 为什么留它 |
|---|---|
| prompt / episode ID、token IDs、角色边界、有效动作 mask | 对齐 logits、动作和后续回放(replay) |
| 行为策略(behavior policy)版本、采样参数、动作 log-prob | 知道这条数据怎样被采出来 |
| 停止原因、最终 observation、工具状态与错误 | 区分任务终止、收集截断和运行失败 |
| reward 来源 / 版本、评分状态 | 未评分不是零分;工具超时也不自动等于模型答错 |
| 需要时的 old / reference log-prob、value、return / advantage | 明确由哪个模型、在哪一步算出 |
reward 可以来自测试、环境、人工或学习出的评分模型。Reference、value、advantage 不一定都在生成时计算,甚至不一定都需要;不要为了凑一张大表而混淆算法角色。PPO 的 rollout buffer 通常只服务一轮有限的多次更新,不等于历史经验可以无限重放。PPO 原论文讨论的是采样与多轮 minibatch 更新交替进行。
小检查:20 个位置,为什么只平均 9 个?
下面已经按“被预测的 token”对齐,不再演示 logits shift。数值是任意的逐位置教学 loss。真实实现还要核对下一 token 的移位与采样协议。
def action_mean(values, mask):
if len(values) != len(mask) or any(flag not in (0, 1) for flag in mask):
raise ValueError("aligned values and binary mask required")
count = sum(mask)
if count == 0:
raise ValueError("no policy actions")
return sum(value for value, flag in zip(values, mask) if flag) / count
mask = [0] * 6 + [1] * 4 + [0] * 3 + [1] * 5 + [0] * 2
values = [99.0] * 6 + [2.0] * 4 + [99.0] * 3 + [4.0] * 5 + [99.0] * 2
assert len(mask) == 20 and sum(mask) == 9
assert abs(action_mean(values, mask) - 28 / 9) < 1e-12
到了终点,还是只是采样时间用完了?
假设当前一步 reward 为 0.2,下一状态估值为 0.8,折扣 $\gamma=0.9$。做一步 TD target 时:
- 任务真的终止:没有后续回报,target = 0.2。
- 只是外部收集时间到了,任务本可继续:保留 bootstrap,target =
0.2 + 0.9 × 0.8 = 0.92。
这不是让你一律给长度截断补 value。若任务定义为“最多 10 步,没完成就失败”,预算耗尽本身就是终点;如果只是收集器每 10 步暂停一次,就不是同一种情况。Gymnasium 的时间限制说明明确区分 termination 与 truncation;有限时域任务还需要让观察包含剩余时间等必要信息。
bootstrap 要使用被截断轨迹的最后状态,不能误用环境 reset 后新任务的第一帧。若做 GAE,暂停收集的边界通常还要截断 advantage 递推,不把下一个 episode 的误差串过来;bootstrap mask 和递推边界 mask 不一定相同。没有 Critic 的方法,则按自己的截断和奖励(reward)约定处理,不必硬加一个 value 字段。
采样慢,先分清时间花在哪里
一批数据可能依次经历生成、工具等待、评分、训练、权重同步(refit)。只盯 GPU utilization,很容易把“GPU 在等工具”误判成“模型太小”。按 NVML 的定义,nvidia-smi 里的利用率只是采样周期里有 kernel 在跑的时间比例,不说明算力用了多少,见性能:roofline、算术强度与 MFU。
用一组假设时间算账:采样 30 秒,评分 10 秒,训练 20 秒,同步 5 秒。完全串行需要 65 秒。如果前三段有独立资源、能在不同批次间理想重叠,而每批同步的 5 秒不能重叠,稳态间隔下界才是 max(30, 10, 20) + 5 = 35 秒,约 1.86×。这不是“异步必然快一倍”:首批仍要经过完整流程,资源争用、长尾(long tail)任务和网络都会增加时间。
| 方式 | 省心在哪里 | 需要付出的代价 |
|---|---|---|
| 同步采样,再更新 | 版本和批次边界清楚,容易排查;生成和训练可以轮流使用同一批 GPU | 各阶段可能互相等待 |
| 限制队列长度的异步 | 能重叠部分生成与训练 | 需要处理策略延迟、丢弃规则和回压 |
| 深队列、持续采样 | 容纳突发数据和慢任务 | 可能更旧、更占内存,不保证有效更新更快 |
要同时记录采样 tokens/s、真正参与 loss 的 tokens/s、队列等待分位数、版本落后程度、超时 / 丢弃比例,以及等 wall time 的任务质量。更高吞吐若主要来自收集无效回答,就没有省到学习成本。异步方法的目标差异见 异步策略学习。
同一份权重,log-prob 也可能对不上
先把三件事拆开:模型原始分布、温度 / top-p 等处理后的采样分布(sampling distribution)、训练框架重算出的分布。它们可能相同,也可能不同。引擎返回的字段叫 logprob,不代表已经告诉你是哪一种。
例如原始概率为 [0.8, 0.2]。用温度 2 采样,概率变成 [2/3, 1/3]。若新模型给第二项概率 0.25,相对实际行为分布的比率是 0.25 / (1/3) = 0.75;若错用原始 0.2 作分母,就变成 1.25,连增减方向都反了。top-p 截掉某些动作后,还会引入支持集问题,不能假装普通 importance sampling 能恢复从来没采过的部分。
若选择以实际 behavior policy $b$ 为基准,一个动作的比率为:
$$ r_t(\theta)=\frac{\pi_\theta(a_t\mid s_t)}{b(a_t\mid s_t)}. $$
也有实现用训练端重算的 old policy 作为 PPO anchor,再另外校正 rollout 与 old 的差异。分母不是永远来自推理引擎(inference engine),也不是永远能由训练端替换。 要按目标和实现看这几个分布怎样对应。verl 的 rollout correction 文档区分了这两类设计;此处核对的是 2026-09-23 更新的文档,不保证未来配置名称不变。
这份文档背后有几篇一手资料,值得直接读。博客 “Your Efficient RL Framework Secretly Brings You Off-Policy RL Training” 指出,推理引擎(如 vLLM)和训练框架给同一个 token 算出的概率并不相同,因此,即使在线收集新样本,原本希望保持 on-policy 的训练也可能出现采样策略与训练策略不一致的偏差;它的修正是把训练端与推理端的概率比截断到一个区间内,再乘到梯度上,叫截断重要性采样(truncated importance sampling, TIS)。“When Speed Kills Stability”(Liu 等,2025)把这种不一致和 RL 训练崩溃联系起来,并比较 token 级与序列级的修正。Ring-1T 报告(Ling Team,2025)里的 IcePop 则对差异过大的 token 做 mask 和裁剪。框架也在跟进:截至 2026-10,TRL v1.16.0 用 vLLM 生成时默认打开这类修正(vllm_importance_sampling_correction=True;配置签名里的默认模式是按整条序列 mask 的 sequence_mask,上限 vllm_importance_sampling_clip_max=3.0)。默认打开不等于问题已经解决:先查清差异从哪来,再决定截断还是丢弃。
统一采样语义后,再查 kernel、精度、模板、位置、mask 等差异。同一批 token、同一权重在两侧评分,按有效动作统计差值的分位数、极端值和长度切片(slice),不要求所有硬件都 bitwise 一致。比率偏离大也不一定全因数据旧了:对齐错误和数值差异同样可能造成。
为什么只看平均差值不够?
两个动作的 log-prob 差分别是 +0.7 和 −0.7,平均恰好为 0。取指数后,比率却约为 2.01 和 0.50,平均也不再是 1。这是指数非线性的简单例子,不是实际误差分布。
还要分清监控对象:原始 ratio 超出 clipping 区间,不等于梯度一定被裁掉。PPO 的 active branch 与 advantage 正负有关。详情见 PPO clipping;不要只凭一个 clip fraction 就断言训练已经失效。
上下文删掉什么,训练就看不到什么
继续用工具任务。用户最早说“不要改动原文件”,几轮之后,为了腾出窗口,系统把这句话删了。最终模型改动了文件,问题就不只是 reward 给得不好:它做决定时根本没看到约束。多轮 agent 训练里,截断和压缩怎样影响记分与 loss,见前沿草稿里的回合变长以后一节。
| 处理方式 | 能省下什么 | 要验证什么 |
|---|---|---|
| 保留最近若干轮 | 实现简单、长度可控 | 目标与早期约束是否被挤掉 |
| 固定保留目标,裁剪重复工具输出 | 少一次摘要模型调用 | 裁剪规则是否误删证据 |
| 摘要旧历史,必要时检索(retrieval)原记录 | 可保留更长任务的主要信息 | 摘要错误、遗漏和检索失败 |
这些做法同时影响任务完成率、答案质量和延迟,不只是“让上下文装得下”。更换压缩策略改变了策略看到的观察,不一定改变底层环境的转移规则;丢掉必要历史还可能让观察不再满足 Markov 条件。把模板、摘要器 / 检索器版本、原始工具记录和外部状态一起管理,才能比较实验并恢复任务。
先跑通一小批,再扩大规模
不必一开始就搭最复杂的异步系统。先固定一小批任务,追踪它们从采样到 loss 的每一步:动作数能对上,终止原因能解释,reward 能复核,再去优化等待时间。这里没有“算法一定比工程次要”的排序;需要排除的,是代码实际上没有执行你以为的那个目标。
这些改动也不是都不改变目标函数(objective function)。更换截断奖励、数据筛选或采样处理,可能改变训练分布、估计量甚至任务定义。把变动记录下来,比笼统地叫它“提速优化”更有用。
放大训练前,我会检查这些
- 从一条原始记录,能重新对齐 token、角色、policy mask 和下一 token 的 logits 吗?
- 未评分、工具失败、任务失败、长度截断,是否被悄悄合成了同一个零分?
- old、current、reference 与实际 behavior distribution 分别是什么?采样参数是否留档?
- 同步改成异步后,采样吞吐、数据年龄和等时间质量是否一起报告?
- 恢复时除了模型权重,队列、采样器、工具与上下文状态是否也有明确处理?
继续阅读
- 实验与调试:从小实验检查训练循环。
- Checkpoint 与恢复:验证恢复后的下一步,而不只是能加载文件。
- 蒸馏:学生 rollout 怎样接入 teacher 监督。
- 同一个基座能走多远:具体报告里的算法与系统变化。
如果只记住一件事
Rollout 不只是“生成一点训练数据”。它要留下足够的信息,让训练知道模型在什么条件下作了哪些选择。先把这条记录讲清楚,再看怎样跑得更快。
本页术语 10
- 回放 replay
- 用固定输入重新执行一段数据或模型链路,检查结果是否一致
- 奖励 reward
- RL 里指导策略更新的标量信号
- 权重同步 refit
- 训练器更新完权重后推回推理引擎,让下一轮 rollout 用上新策略
- 长尾 long tail
- 出现频率很低但总量可观的那部分情况
- 采样分布 sampling distribution
- 训练候选被抽中的概率分布,它决定模型经常看到哪类边界
- 推理引擎 inference engine
- 负责高吞吐生成或模型服务的执行系统
- 重要性采样 importance sampling
- 从容易采样的 proposal 抽样,再用权重估计目标分布下的期望
- 切片 slice
- 按内容或用户属性划分的子群体,用来检查平均值掩盖了什么
- 检索 retrieval
- 从外部语料里找出相关内容
- 目标函数 objective function
- 训练直接优化的数学量;它不一定等于最终产品目标