arl. 调用前面的章节,因此这些导入属于一个保存块。
6.1 得分函数估计量
目标是从策略采样的轨迹的期望回报(return): 回报 由环境计算,这里由测试计算,它对 没有导数。对 的依赖完全位于分布 中。在求和号内求导,并利用 (附录 A.5): 右侧的期望可以从样本中估计。剩下的是整条轨迹的得分 。2.2 节把轨迹概率分解为智能体能控制的部分和不能控制的部分: 初始分布 和转移 都不依赖于 ,因此它们的梯度为零,于是 这就是式 (6.1),即针对轨迹的 REINFORCE 估计量(Williams, 1992)。推导中用到了三个假设,每一个都是真实系统可能打破它的地方。回报不能直接依赖于 ,如果学习得到的奖励模型(reward model)与策略共享权重,这一点就不成立。轨迹必须从 本身采样,一旦在更新后复用数据,或者推理引擎计算出的概率略有不同,这一点就不成立(第 8 章、第 13 章)。对轨迹的求和必须有限或性质良好,轮数预算保证了这一点。 估计量只需要用 来采样。它从不计算 的值,也从不对它求导。我们在第 2 章的修复 MDP 上检验这一点,那里可以得到精确梯度。下面的采样器并行运行许多回合,并为每个回合记录每个状态-动作对被访问的次数以及总回报。[S, A] 的 logits 表 做 softmax,随机初始化,使它既不均匀也不确定。第 2 章的 policy_evaluation 精确给出 ,自动微分给出精确梯度。蒙特卡洛一侧则把每个回合的 计算为 visits * log_softmax(theta),乘以回报,取平均,再求导。
returns、visits 和 log_pi。只有最后一个依赖于 ,而 根本没有出现在被求导的表达式中。编码环境的处境与 相同:我们可以运行它,但不能对它求导。
代理损失
我们求导的表达式 (回报保持固定)是一个代理目标(surrogate objective)。它在当前参数处的梯度等于式 (6.1) 的估计量。它的值不是 ,也不估计任何有用的量。回报作为常数进入,因此把每个回报平移同一个量,会任意改变代理目标的值,而它的期望梯度仍为 ,因为 。6.2 可供学习的基座策略
式 (6.1) 用每条轨迹的回报为其得分加权。如果每条采样轨迹的回报都相同,估计量就是该常数乘以 ,这个量的期望为零,而加上基线后它恰好为零。第 5 章测量了未训练的 TinyAgent:384 次调用中 384 次格式错误,每个回合奖励都是 0。强化学习无法从那里起步。 实际的大模型 RL 通常也不会从这种随机策略起步。预训练模型已经能写出流畅的文本,经过指令微调的模型大多数时候已经能输出格式正确的工具调用。强化学习随后改进的是它做出哪些调用。我们用行为克隆(behavior cloning)给 TinyAgent 同样的起点:在示范数据上做监督训练,交叉熵损失只作用于策略 token。示范来自第 4 章的random 和 blind 脚本化策略在训练任务上的运行。它们格式正确但毫无技巧:random 在九个合法调用中均匀选择,blind 不读代码就猜一个修复操作。模仿它们的模型学会了调用格式和回合的节奏,但学不会如何选择正确的操作。我们把得到的结果称为基座智能体。它是本书中预训练基座模型的对应物,后面每一章都从它开始做强化学习。第 15 章会系统研究监督式热启动(warm start),包括它对探索(exploration)的影响。
行为克隆损失是在策略 token 上的平均,我们经常用到它,值得保存为一个函数。
make_base_agent 从每个脚本化策略为每个训练任务收集 episodes_per_task 条示范,用 scripted_rollout(5.6 节)编码,然后在随机小批次(minibatch)上执行 steps 次 Adam 更新。所有内容都设定了随机种子,因此相同的调用返回相同的权重。
rollout_stats 报告平均奖励、通过率(pass rate,已提交且所有隐藏测试都通过)、格式正确且合法的调用比例、提交率,以及每个回合的平均轮数和策略 token 数。后面的章节记录同样的数字。
random(4.6 节中训练集上为 0.10)和 blind(0.21)之间,远低于 oracle 的 1.00。它在留出任务上的数字与训练任务上的数字在采样噪声范围内一致,因为它所模仿的东西都不依赖于正在修复的是哪个函数。本章其他样本中它的通过率在 0.12 到 0.19 之间;对几百个回合而言,这个范围就是采样误差。撰写本章时,在检查主机上 make_base_agent 约用时五秒。构建过程不检查计时。
一个采样回合展示了强化学习要面对的行为。
clamp 上,一个回合提交了未修改的文件,一个猜了 add_zero_guard 后提交,还有一个在编辑和测试之间徘徊直到预算耗尽。基座智能体确实读取文件时(只在少数回合中如此),它接下来的操作与读到的内容无关。成功主要来自碰巧猜中修复操作。然而,每一次成功都是一条回报与其他轨迹不同的轨迹,因此策略梯度不再为零。
6.3 token 上的估计量
在 2.8 节的 token 层面,一条轨迹是一个 token 序列 ,其前缀为 。每个 token 要么由策略抽取,要么由环境和模板抽取。当 token 从策略采样时记 ,这恰好就是 5.6 节的动作掩码。于是 其中 包括提示、模板和每一个工具响应。第二个求和不依赖于 ,因此 这就是式 (6.2)。工具 token 不携带直接梯度:模型从不被推动去预测它们。但它们携带间接梯度,因为之后的每个策略 token 都通过注意力以它们为条件,梯度经由它们的隐藏表示流入共享权重。一批 条轨迹对应的损失为 其中 被视为常数。我们在一批 64 个基座智能体回合上检验这两个论断。token 嵌入上的一个 hook 保存相对于每个位置输入表示的梯度,retain_grad 保存相对于 logits 的梯度。
6.4 基线
6.1 节的平移实验提示我们:从回报中减去某个量,可以在不引入偏差的情况下降低估计量的噪声。一般的表述如下。令 为时刻 状态的任意函数,或者是任何在 被采样之前就已确定的量的函数。那么 对 取外层期望(塔性质,tower property),对每个 都得到 。因此,对任何这样的 , 这就是式 (6.3)。证明只用到 在抽取 时已经确定这一点。基线可以依赖于任务、提示和更早的 token。它不能依赖于 、任何在 之后采样的东西,也不能依赖于该轨迹自身的回报。用包含该轨迹自身奖励的批次均值计算的基线会轻微违反最后一条,第 7 章会量化其后果。 基线不能改变均值,因此它只能改变方差。对常数基线 和逐轨迹得分 ,总方差 是 的二次函数,在下式处取最小值: 这是一个加权平均奖励,权重正比于得分范数的平方。在实践中,普通的平均奖励与它很接近,而 ,即从任务初始状态出发的期望回报,是自然的依赖于状态的选择(附录 A.3 把基线作为控制变量(control variate)来讨论)。 我们先在精确梯度已知的地方确认无偏性。我们采样 400 个独立批次,每批 64 个修复 MDP 回合,对四种基线分别在各批次上平均估计量:无基线、精确的 、批次自身的平均回报,以及一个故意选得很差的常数 1。6.5 因果性与 reward-to-go
基线论证还有第二个推论。假设奖励在回合进行中到达,。对 ,奖励 在 被采样之前就已确定,因此它可以作为 的基线,且 。去掉这些项得到 即式 (6.4)。内层求和就是 reward-to-go(剩余回报)。因果性是说:一个动作的学习权重只需包含从它发生时起的奖励。在 token 层面, 遍历 token 位置,。 对于放在最后一个策略 token 上的纯结果奖励(outcome reward),每个策略 token 上都有 ,因果性不改变任何东西。当奖励包含中间项时它才重要:每轮的成本、对格式错误调用的格式惩罚,或者每轮的塑形奖励(第 11 章、第 12 章)。我们给探测回合加上每次调用 0.1 的成本。成本放在每次调用的最后一个 token 上,隐藏测试奖励放在最后一次调用的最后一个 token 上。returns_to_go,并用优势的估计替换剩下的 reward-to-go。
6.6 损失聚合
式 (6.2) 是对 token 求和、对轨迹取平均。实现中很少恰好这样计算。它们在一个填充后的[B, T] 张量上计算逐 token 项 ,再把它归约为一个标量,而所选的归约方式给每个 token 设定了一个权重 :。令 为轨迹 的策略 token 数。常用的四种归约是
只有
seq_mean_token_sum 是推导所得的式 (6.2)。其他模式改变归一化方式;其中有的只是整体缩放,有的还会改变不同长度轨迹之间的相对权重。token_mean 除以批次中的策略 token 数,因此当批次中的轨迹变长时,有效学习率(learning rate)会缩小。seq_mean_token_mean 给每条轨迹相同的总权重而不论其长度,因此长轨迹中单个 token 的权重比短轨迹中更小。constant 除以一个固定的数 ,例如批大小乘以策略 token 的最大数目,这使逐 token 权重在不同批次间保持固定。哪些公开方案使用哪种模式,以及 Dr. GRPO 对按序列归一化的批评(Liu et al., 2025),是第 9 章的主题。这里我们构建这些归约并测量它们的作用。
reinforce_loss 对优势做了 detach。在推导中它们是常数,6.8 节展示了不这样做时出现的偏差。表中的权重就是损失相对于每个逐 token 项的梯度,因此可以用自动微分直接读出。取三条轨迹,分别有 4、8 和 24 个策略 token:
token_mean、seq_mean_token_sum 和 constant 下,三条轨迹的逐 token 权重相同,因此 24 个 token 的轨迹的总权重是 4 个 token 轨迹的六倍。这三种模式只差一个整体缩放,且只有 token_mean 的缩放依赖于批次(这里是 )。在 seq_mean_token_mean 下,每条轨迹的总权重为 ,短轨迹中一个 token 的权重是长轨迹中一个 token 的六倍。
在智能体任务中,长度不是随机的。在基座智能体的探测批次中,成功回合比失败回合短,因为侥幸猜中会以较早的提交结束,而失败则会四处徘徊。使用 作为优势时,成功被向上推,失败被向下推,而聚合方式决定推的力度。
seq_mean_token_mean 精确地平衡了总量,因为每条轨迹的总权重为 ,而优势之和为零。然而按 token 计,每个长失败被向下推的力度只有每个短失败的三分之一。如果策略能让一个失败回合变长,例如在放弃之前多加几次调用,seq_mean_token_mean 就会降低这些调用中每一次的逐 token 惩罚。这就是 Dr. GRPO 归因于按序列归一化的长度增长背后的机制(第 9 章)。
seq_mean_token_mean 是平的,因此批次中最短和最长轨迹的总权重相同。右图是批次中其余部分的长度:只有 token_mean 会在其他轨迹变长时改变一条轨迹的逐 token 权重,当其他轨迹的平均长度从 10 增长到 40 时,该权重降到四分之一。因此,随着训练中回合变长,token_mean 会降低每个 token 的有效学习率,而其他三种模式不会。本书默认使用 token_mean,因为大多数公开方案使用它,而且依赖于批次的缩放容易监控。附录 B 问题 1 会详细论证这一选择。
6.7 用 REINFORCE 训练 TinyAgent
第一次强化学习运行所需的一切都已就绪。一次更新从当前策略采样一批回合,计算优势,并在代理损失上走一步梯度。policy_gradient_step 在一个整理好的批次上执行更新。它裁剪(clip)全局梯度范数,这是一项标准的保护措施,我们同时记录其值;它返回损失以及裁剪之前的梯度范数。第 8 章和第 9 章会替换损失,但保持这个形状。
no change)或做出无害的改动,因此像 fix_comparison -> add_zero_guard -> fix_comparison 这样的序列同时修复了 compare 和 zero 两种缺陷类型。最常见的留出回合将 swap_minmax 应用了五次,按奇偶性,min 和 max 最终仍处于交换后的状态。在这个奖励下,叠加编辑是一种合法的修复,但没有哪个工程师会接受这种策略,而它之所以能迁移到留出任务,只是因为编辑菜单很小。读取,这种本可以干净迁移的行为,从未出现。基座智能体只在少数回合中读取,而且读完后选择的操作与读到的内容无关,因此在它自己的样本中,读取与奖励不相关,而 REINFORCE 只能强化它采样到的东西。
证据。 本书测得,基于一个 158k 参数模型、20 个训练任务、单个随机种子。这次运行演示的是估计量的机制,并不是关于大模型会学到哪些策略的论断。定性的教训是估计量的一般性质,而非这个模型的性质:强化学习会放大在策略自身样本中已经得到奖励的行为,而记住训练任务或利用一个很小的动作菜单,都是低成本的得分方式。第 4 章为“学到的不只是格式”设定了 0.2 的留出门槛,而这次运行表明,越过这条门槛本身并不能证明智能体学会了读取。第 7、9 和 12 章会回到这次运行的两种失败模式:为无用步骤分配学习信号,以及缺乏信息收集。
6.8 实践中的设计问题
问题:稀疏二值奖励下的高方差。 现象: 每步奖励曲线噪声很大,不同随机种子的学习速度差异很大,简单任务饱和的同时困难任务上的进展停滞。 原因: 在二值奖励和低通过率下,大多数回合以一个小的负权重贡献得分,少数回合贡献大的正权重。估计量的方差相对于其均值很大(图 6.1),而一个所有采样回合都失败的任务没有任何贡献。 可选方案: (a) 每步更多回合,rollout 计算量线性增长,换来 的噪声降低。(b) 更好的基线,尤其是由组内样本得到的逐任务基线(第 7 章)。(c) 更稠密的奖励,但有被钻空子的风险(第 11 章)。(d) 让任务保持在接近 50% 通过率的课程(第 14 章)。 建议做法: 在调整任何其他东西之前,先直接测量梯度噪声。下面的代码块把 240 个探测回合分成互不相交的批次,计算同样大小的独立批次所得梯度估计之间的余弦相似度。
问题:聚合引入的长度偏差。 现象: 平均回合长度在训练中增长,而奖励没有相应上升,增长往往集中在失败回合中。 原因:seq_mean_token_mean把每条轨迹的推力除以其自身长度(6.6 节),因此更长的失败在每个 token 上受到的惩罚更少。token_mean把步长与批次的总长度耦合在一起。 可选方案: (a)seq_mean_token_sum或constant,它们保持逐 token 权重固定,但需要针对更大的梯度重新调整学习率。(b) 广泛使用的token_mean,同时记录批次的策略 token 数。(c) 显式的长度成本,这会改变任务。 建议做法:token_mean,同时分别记录成功和失败回合的平均长度。如果失败回合变长,就切换到constant。
问题:detach 了错误的张量。 现象: 训练能运行,损失也在下降,但学习更慢或朝奇怪的方向漂移,梯度与参考实现不一致。 原因: 某个在代理损失中应当是常数的量,例如由模型自身输出计算的基线或优势,被留在了计算图上。于是它的梯度被加到估计量上,而推导中没有任何依据支持这一项。 可选方案: (a) 在损失函数内部对优势做 detach,如reinforce_loss所做。(b) 在torch.no_grad()下计算优势。 建议做法: 两者都做,再加一个在固定批次上把梯度与参考实现比较的单元测试。下面的代码块在一个五动作老虎机(bandit)上复现这种偏差,基线直接取自策略自身的概率。
detach() 就进入策略损失时,会出现同样的 bug(第 7 章)。
问题:优势全为零的批次。
现象: 在某些步中每个回合的奖励都相同,记录的损失和梯度恰好为零,但策略仍在变化,有时还会退化。
原因: 所有奖励相等且使用均值基线时,每个优势都为零,因此梯度为零。Adam 仍会按照之前各步的一阶矩估计移动参数。权重衰减也会移动参数。优势为零的一步并不是空操作。在 token_mean 下,优势为零的轨迹还会往归一化分母中加入 token,从而稀释其他每条轨迹的权重。
可选方案: (a) 当批次没有信号时跳过优化器步骤。(b) 在组成批次之前过滤掉优势为零的组并重新采样,即动态采样(第 9 章)。(c) 接受这种漂移。
建议做法: 使用组时选 (b),否则选 (a)。下面的代码块先走一个普通的步,再在全零优势上走一步。
小结
- REINFORCE 估计量 只需要用环境来采样。环境动态从得分中消失;在 token 层面,所有提示、模板和工具 token 也同样消失,这就是动作掩码。工具 token 仍通过注意力影响梯度。
- 代理损失的梯度就是估计量,而它的值没有意义。应记录奖励和通过率,而不是损失。
- 任何在动作被采样之前就已确定的基线都使估计量保持无偏。在基座智能体上,平均奖励接近最优常数,基线为 1 会使方差扩大七倍以上,而由 12 个回合估计的逐任务基线并不比全局均值更好。reward-to-go 从每个 token 的权重中去掉过去的奖励,在每次调用有成本的设定下使方差降低了 43%。
- 损失聚合设定每个 token 的权重。三种常用模式按长度成比例地为轨迹加权,
seq_mean_token_mean为它们等权加权,而token_mean把步长与批次的长度耦合在一起。 - 基座智能体,即在
random和blind的运行数据上做行为克隆的 TinyAgent,98% 的时候写出合法调用,回合通过率在 12% 到 19% 之间。八十步 REINFORCE 把它在训练任务上的通过率从 0.14 提高到 0.78,在留出任务上从 0.13 提高到 0.46,靠的是部分记住函数名和叠加编辑,而从未靠读取。
练习
- 用来自
policy_evaluation的依赖于状态的基线 ,在每一步应用,重复 6.4 节的无偏性检验。与精确常数 相比,每批方差下降了多少?然后改用 ,并说明此时均值是错的。证明中的哪一行不再成立? - 证明最优常数基线为 ,并解释它为什么不同于平均奖励。在 TinyAgent 中,哪些类型的回合的 较大?
- 改变一个假设:分别用
seq_mean_token_mean和seq_mean_token_sum做 REINFORCE 训练(为后者重新调整学习率,并说明为什么需要重新调整)。记录失败回合中策略 token 的平均数目。失败回合的长度是否如 6.6 节预测的那样变化? - 只用
blind的运行数据构建基座智能体,再只用random的运行数据构建一次。测量通过率、读取率,然后测量 80 步 REINFORCE 后的结果。哪种初始分布带来更多的留出提升?为什么? - 让读取对 REINFORCE 变得有用:用一个 50% 的时候会出错的 oracle(它先读取,再以 0.5 的概率选择正确的操作)的示范来训练基座智能体。REINFORCE 现在会学会读取吗?测量留出通过率。
- token 级估计量把每个 token 视为一个动作。推导一个把整次调用视为一个动作的轮级估计量。证明它的得分是该调用中各 token 得分之和,因此两个估计量完全相同。这两种建模方式在哪里不再等价?
- 设计评审:一位同事提议用代理损失的滑动平均绝对值对其做归一化来稳定训练,“让损失保持在 1 左右”。这对梯度有什么影响?在什么条件下它等价于一个学习率调度?什么时候它会改变更新的方向?