强化学习

策略梯度算法
强化学习中策略梯度优化是指,通过优化策略函数的参数来寻求最优策略的一类方法。
我们先对优化目标
自然地,定性为最大化给定状态分布以及当前策略
实际中,令
在将词元作为最小生成来作MDP推演时,一般将折扣因子设为1,因为我们关注的目标是生成整体。
策略梯度的推导
令
有
使用
简化中间过程,大致是
,稍加推广得到:
其中,
轨迹总回报
整条轨迹中的折扣奖励之和。
从时刻
开始的回报表示执行动作
后、从当前时刻起获得的未来折扣回报。带基线的回报
在回报中减去仅依赖状态的基线
,不会改变梯度估计的期望,但能降低方差。状态—动作价值
表示在状态
采取动作 后,后续回报的期望。优势函数
衡量动作
相对于该状态下策略平均表现的增益;若能准确估计,通常具有更低的梯度方差。时序差分(TD)残差
它比较价值函数的当前预测与一步 bootstrap 目标,常用于近似优势函数。
其中,
是状态价值,表示在
是状态—动作价值。于是,优势函数可写为
即一步 TD 残差。这是因为在确定性环境中,上式不再需要对状态转移取期望。
朴素策略梯度
一个关于时刻t的简单版本为:
简单地用当前轨迹的生成token来进行计算,即
一个众所周知的问题,由于缺少基线,朴素策略梯度的方差极大,缓解的方式有优势函数(PPO),组平均值(GRPO)等。
REINFORCE
核心思想
REINFORCE 是最基本的策略梯度方法之一,其核心思想是:
提高获得高于预期回报的动作的概率,降低获得低于预期回报的动作的概率。
早期 REINFORCE 的参数更新可以概括为:
其中:
:学习率; :基线(baseline),用于降低梯度估计的方差; :奖励相对于基线的增量; :资格迹(eligibility),表示奖励应归因于哪些参数。
现代策略梯度形式
对于策略
其中:
:动作 之后获得的累计回报; :状态相关的 baseline; :调整该动作概率的方向。
使用优势函数表示
定义优势:
则 REINFORCE 可以简写为:
因此:
REINFORCE 的核心可以概括为:
即用优势决定动作应该被鼓励还是抑制以及更新强度,再通过对数策略梯度更新模型参数。
REINFORCE留一法(RLOO)
优势
留一法的前提是:对一个提示生成了多条轨迹。
显然,即利用其余轨迹的均值作为当前轨迹return的基线。
优势通过整个生成的奖励来计算,具体分不分配到token上是单纯的处理。一般会将并入KL惩罚后得到的优势广播到每个token
值得注意的是,RLOO以及传统的策略梯度将KL惩罚加到奖励上,而GRPO则在损失层面施加KL惩罚。
PPO近端策略优化

PPO 的裁剪代理目标为:
其中
:采集当前这批数据时的旧策略; :正在更新的当前策略; :优势函数; :新旧策略对同一动作概率的比值。
但我们希望优化当前策略
旧数据的采样分布与当前策略不一致。因此使用重要性采样比率
重新调整旧样本的权重,使其可以用于估计当前策略的目标。
需要注意,
而
PPO 通常会对同一批轨迹进行多次梯度更新,主要目的是提高样本利用率,因为强化学习中的环境采样通常比对已有数据进行梯度计算更加昂贵。
一批数据由
每次都计算
第一次更新前:
随着多次梯度更新,
虽然重要性采样可以修正这种分布差异,但当新旧策略相差过大时,
限制旧数据推动策略继续变化的程度。

- 正优势
:该动作比平均水平好,因此希望提高其概率,即推动 增大。但当 时,说明该动作的概率已经提高得足够多,目标函数变平、梯度变为 ,不再继续强化。 - 负优势
:该动作比平均水平差,因此希望降低其概率,即推动 减小。但当 时,说明该动作的概率已经降低得足够多,目标函数同样变平、梯度变为 ,不再继续抑制。 因此图中倾斜部分表示梯度正常传播,推动策略朝改善方向更新;水平部分表示裁剪生效,该样本不再提供进一步推动策略远离旧策略的梯度。 需要注意,PPO 并不是强制要求
而是当策略已经沿着有利方向变化过大时停止该样本的进一步激励。因此,
价值函数/critic network
PPO 中的价值函数(Critic)用于预测从当前状态
对于语言模型,
价值函数主要作为策略梯度的基线(baseline)。实际回报相对于价值预测的差异形成优势:
其中
Critic 的训练
最简单的方法是使用实际累计回报(Monte Carlo Return)作为训练目标:
并通过回归训练:
但 Monte Carlo Return 方差较大,因此主流 PPO 通常结合 GAE 构造更加稳定的训练目标。
首先根据 rollout 时保存的旧价值预测计算 TD 误差:
GAE 将多个未来 TD 误差加权:
随后利用优势构造 Critic 的训练目标:
并训练当前 Critic:
因此,同一个 GAE 结果有两个主要用途:
其中
:接近一步 TD,方差较小但更依赖 Critic 的估计; :更加接近 Monte Carlo Return,使用更多真实的未来奖励信息。
一次 PPO 迭代中,可以概括为:
通常在一批 rollout 上先计算并固定
GRPO:Group Relative Policy Optimization

GRPO(Group Relative Policy Optimization,组相对策略优化)的核心思想可以概括为:
对于同一个 prompt 一次采样多个回答,让这些回答在组内相互比较,用“相对于组内其他回答好多少”来构造优势,而不再像 PPO 那样额外训练一个 Critic / Value Model 来估计价值函数。
对于一个提示
奖励模型或可验证奖励函数分别给这些回答打分。GRPO 使用组内奖励构造优势:高于组内平均水平的回答得到正优势,低于平均水平的回答得到负优势,然后使用类似 PPO 的 clipped objective 更新策略。
与 PPO 相比,最关键的变化是:
因此 GRPO 不需要单独训练一个与策略模型规模相近的价值模型。
GRPO 的目标函数
对于第
GRPO 的目标函数为:
其中前半部分基本沿用了 PPO 的 clipping 机制:
限制策略一次更新不能偏离旧策略太远;KL 项
则约束当前策略不要过度偏离参考模型。
因此 GRPO 和 PPO 的一个核心区别并不在 clipping,而在于
GRPO 的优势计算
最基本的 GRPO 优势来自组内相对奖励。
假设同一个 prompt 生成
首先计算组内均值和标准差:
然后将第
它表达的就是:
因此 GRPO 不需要学习
结果监督与过程监督
GRPO 可以使用两种不同粒度的奖励:结果监督(Outcome Supervision)和过程监督(Process Supervision)。两者的根本区别在于奖励落在什么位置。
结果监督只评价整个回答最终是否好,例如数学题只判断最终答案是否正确:
因此一个回答只有一个最终奖励。将组内奖励标准化:
然后把这个值直接赋给该回答的所有 token:
也就是说,同一个回答中的所有 token 共用相同的优势。例如:
若最终标准化奖励为
它的优点是简单,而且最终正确性通常容易验证;缺点是无法判断一个长推理过程中究竟哪一步做得好、哪一步出现了错误。
过程监督则进一步评价推理过程中的各个步骤。例如:
过程奖励模型分别在每一步结束时产生奖励:
这些过程奖励同样先进行标准化:
对于回答
因此不同位置的 token 可以具有不同的 advantage。例如三个步骤的标准化奖励为
那么位于不同步骤中的 token 大致会获得:
所以两种监督方式最直观的区别就是:
过程监督的 credit assignment 更细,可以告诉模型“哪部分推理值得强化”;结果监督则只告诉模型“整个答案最终好不好”。
与 RLOO 的关系
如果进一步去掉 GRPO 中的标准差归一化,就得到常见的 Dr. GRPO 形式:
而 RLOO(REINFORCE Leave-One-Out)使用除自己以外其他
两者满足简单的缩放关系:
因此在忽略这个常数缩放(实践中通常可被学习率等吸收)后,Dr. GRPO 的优势估计与 RLOO 的 leave-one-out 优势估计本质上非常接近。
组序列策略优化(GSPO)
动机
在 PPO、GRPO 等策略梯度方法中,训练样本通常由旧策略
其中
GRPO 在 token 级别计算重要性比率:
然后分别对每个 token 的重要性比率进行裁剪。
但 GRPO 的奖励和优势通常是在完整回答级别计算的,即同一个回答
对于长序列、大模型或 MoE 模型,个别 token 的重要性比率还可能发生较大波动,使一次回答内部不同 token 的更新强度不一致。
GSPO(Group Sequence Policy Optimization)的核心思想就是将重要性采样从 token 级提升到序列级:
使重要性采样的粒度与回答级奖励、优势的粒度保持一致。
序列概率
对于输入
由于语言模型采用自回归生成,整个回答的概率可以分解为:
因此,当前策略与旧策略之间的序列级重要性比率可以写为:
直接使用这一乘积会使重要性比率强烈依赖序列长度:即使每个 token 的概率只发生很小变化,长序列累乘后也可能得到非常大或非常小的比率。
长度归一化的序列级重要性比率
为消除序列长度带来的尺度问题,GSPO 对完整序列的重要性比率进行长度归一化:
将自回归概率分解代入:
为了避免长序列概率连乘导致的数值不稳定,实际计算通常转换到对数空间:
因此,
经过长度归一化后,不同长度回答的重要性比率具有更加可比的尺度,同时一个回答中的所有 token 共享同一个序列级重要性权重
GSPO 的优势计算
GSPO 的优势计算与 GRPO 基本相同。对于同一个输入
并获得对应奖励:
通过组内奖励的均值和标准差进行标准化,可以得到:
其中
:该回答优于组内平均水平,应提高其生成概率; :该回答劣于组内平均水平,应降低其生成概率。
因此 GSPO 中,重要性比率和优势都处于序列级:
GSPO 的优化目标
GSPO 保留了与 GRPO 类似的 PPO-style clipped objective,但将 token 级重要性比率替换为序列级重要性比率(此处省略KL散度):
其中:
与 GRPO 最核心的区别可以概括为:
即 GRPO 对每个 token 分别计算和裁剪重要性比率,而 GSPO 首先将整个回答的策略变化聚合为一个长度归一化的序列级比率,再对整个回答统一进行重要性采样校正和裁剪。
因此,GSPO 可以概括为:
其核心目的在于使
处于相同的优化粒度,从而提高长序列以及大规模模型训练时策略更新的稳定性。
裁剪重要性采样策略优化(CISPO)
动机
PPO / GRPO 的做法是对代理目标函数进行裁剪:
这种方式在重要性比率超出裁剪范围后,可能使对应 token 的梯度直接变为 0,即出现“丢弃 token 梯度”的现象。
CISPO(Clipped Importance Sampling Policy Optimization)的核心思想是:
这样即使某个 token 的重要性比率过大或过小,它仍然可以产生策略梯度,只是其梯度的权重受到限制。
裁剪重要性采样权重
首先计算 token 级重要性比率:
然后直接对重要性比率进行裁剪:
其中可以使用非对称裁剪:
例如增大
停止梯度
CISPO 对裁剪后的重要性权重使用停止梯度:
因此
CISPO 目标函数
CISPO 采用类似 REINFORCE 的目标:
因此单个 token 对参数产生的梯度近似为:
这里
与 PPO / GRPO 的核心区别
PPO / GRPO:
当重要性比率超出一定范围时,部分 token 可能进入目标函数的平坦区域,从而不再提供策略梯度。
CISPO:
即:
因此 CISPO 的核心可以概括为:
这种方法允许重要性采样裁剪引入一定偏差,以换取更低的梯度方差和更稳定的训练。
算法实现
1. EOS trick 为什么“没有生成 EOS”不能直接拿截断文本去评分?
RLHF 中比较常见的 token 级奖励形式是
也就是说,大部分 token 只有 KL penalty,真正的 RM reward 通常只在完整回答末尾加入。InstructGPT 明确采用了这种“最终 RM reward + 每 token KL penalty”的结构。
问题在于,如果规定 max_new_tokens=1024,模型可能一直生成到 1024 token 都没有输出 EOS。此时这段文本本质上是被系统强行截断的未完成回答。如果仍然把它当完整答案交给 RM,模型可能学会利用这种分布偏差。
因此早期 TL;DR 实现以及后续复现会使用所谓 EOS trick:
Huang 等人的复现讨论了 -1 惩罚;Ivison 等人的 NeurIPS 2024 实现使用更强的 -10 截断惩罚。
所以这里的核心不是“RM 必须在 EOS token 上运行”,更准确地说是:
只有一个完整终止的 response 才应该得到正常的终局 reward;被长度上限截断的 response 通常需要额外惩罚。
当然,这种方法的问题是很明显的,在接近设定的最大长度时,会造成termination bias,对于需要长思考和长推理的大语言模型并不适合。合适的做法应当是对truncated的回答施加更加温和且自适应的reward。
2. 价值网络初始化:为什么从 Reward Model 初始化 Critic
这一点和前面讨论的 Critic 很有关系。
PPO 中 Critic 最终要学习的是
而 Reward Model 学到的是类似
的“这个回答最终有多好”。
两者任务并不相同,但 RM 已经学习到了大量与回答质量有关的语义表示,因此用 RM 初始化 Critic,比“语言模型 + 随机 value head”更接近 Critic 最终要解决的问题。
InstructGPT 就是这么做的,而且有一个容易忽略的细节:它对 1.3B、6B、175B policy 全部使用同一个 6B RM 和 6B value model,然后 value model 从该 RM 初始化。也就是说,Critic 并不一定要和 Actor 一样大。
初始化以后,Critic 会继续训练,所以:
训练开始后,RM 仍然主要评价完整回答,而 Critic 会逐渐变成一种 per-token return predictor:
预测“从当前 prefix 开始,未来最终能拿多少 reward”。
Huang 等人也特别指出,从 RM warm start 可以明显改善训练初期的 value estimate。
更有说服力的是 Tülu 3 的 RLVR 实验。它的实际 reward 已经不是 RM,而是数学答案等产生的可验证二值奖励,但他们仍比较了:
和
结果 general RM 初始化取得了更高的 GSM8K 测试性能和更好的整体平均性能。也就是说,即使最终 reward 是 verifier,RM 学到的“质量表征”仍然可以作为 Critic 很好的先验。
这也解释了一个看起来有些反直觉的现象:
Tülu 3 甚至发现,在 verifier reward 上再叠加 RM score 反而会增加噪声。
3. Reward normalization、reward whitening 和 advantage whitening 其实是三件事
这里原文说“归一化到
OpenAI 早期 Ziegler et al. 的实现,对 RM 输出做的是 affine normalization:
使参考数据分布上的 reward 满足近似
对应
而 InstructGPT 又稍有不同:由于 pairwise RM loss
对整体平移不敏感,因此只通过 bias 把 labeler demonstrations 的平均 reward 调到 0。
所以“reward normalization = 映射到
PPO 更新阶段又存在另一层 reward whitening。典型形式为
不过 OpenAI 早期代码对 reward 使用的实现实际上会重新加回原来的均值,因此更接近
即主要标准化 reward 的尺度而不改变平均 reward。
而 advantage whitening 通常直接做
使 batch 内 advantage 均值约为 0、标准差约为 1。
它主要解决的是梯度尺度问题:
如果某个 batch 中
值得注意的是,reward whitening 并不是一定要做。Huang 等人的 TL;DR 实验发现,reward whitening 会导致生成明显变短,这是因为whining会引入和序列长度有关的偏置项,降低未控制长度情况下的 preference rate;而 Ivison et al. 的实现干脆不做 reward whitening,只做 advantage whitening,训练依然稳定。
因此现代实践更接近:
4. “不同 KL 估计器”具体指什么
首先要把两个很容易混淆的 ratio 分开:
和
完全是两件事。
其中:
:产生 rollout 的旧 policy; :通常是冻结的 SFT/DPO 初始模型。
对于某个状态
但实际 rollout 已经采样出了
所以最简单的 Monte-Carlo estimator 是
因为
它是无偏的,但一个具体 token 上完全可能出现
因此方差比较大。
Schulman 讨论了另外两种常见估计:
令
则
具有较低方差,但只是局部二阶近似,因此有 bias。
还有
也就是
它利用 control variate,在采样来自
保证单样本 KL estimator 非负,通常方差也比
经典 PPO-RLHF 最常见的 reward shaping 仍然是
也就是直接使用
5. KL controller:动态调的到底是什么
RLHF 的总体目标可以写成
其中真正控制“模型能偏离 reference 多远”的是
而不是 PPO 的 clip 参数
早期 OpenAI 实现使用 adaptive KL controller。其核心更新为
然后
其中 horizon。
直观上:
反之:
但后来的许多实现改为直接固定
并通过实验发现
Ivison et al. 的 NeurIPS 2024 PPO 实现同样采用固定 KL coefficient,默认
使用更大的 70B RM 时则采用约
需要区分:PPO clip 限制的是“一批 rollout 被重复优化时,相对
的单次更新幅度”;KL penalty 限制的是整个训练过>程中,相对固定 的长期漂移。 这两个机制虽然都在“限制更新”,限制的对象并不相同。
6. 损失聚合的权衡
在语言模型的策略梯度训练中,每个样本包含不同数量的有效 completion token。设第
按序列归一化
即先对每个序列内部的 token loss 求平均,再对序列求平均。
其特点是:
- 每条序列对 batch loss 的总贡献相同;
- 序列长度不会改变该序列的整体权重;
- 但单个 token 的梯度权重为
因此短序列中的每个 token 会获得更大的梯度。
例如长度分别为
因此这种方式虽然实现了“序列公平”,却可能隐式偏向短序列。
按 token 归一化
即把整个 batch 中所有有效 token 放在一起求平均。
此时每个 token 获得相同的梯度权重:
因此:
- 所有 token 地位相同;
- 长序列因为包含更多 token,会产生更大的总梯度;
- 不会出现短序列单 token 梯度更大的问题。
这种聚合方式常见于 DAPO。
定长归一化
设生成长度上限为固定常数
所有有效 token 都除以相同的
与此同时,长序列拥有更多有效 token,因此仍然会贡献更大的总梯度。
可以将其看成:
Dr. GRPO 使用了这一类思想。
其中 padding / prompt token 通常通过 completion_mask 去除:
实际求和的是
三种聚合方式的核心区别
| 聚合方式 | 单 token 权重 | 单序列总权重 | 长度偏置 |
|---|---|---|---|
| 按序列归一化 | (\propto 1/ | a_i | ) |
| 按 token 归一化 | 全部相同 | 长序列更大 | 长序列总权重更大 |
| 定长归一化 | 全部相同 | 长序列更大 | 类似按 token,但尺度由固定 |
因此不存在绝对最优的聚合方式,本质上是在决定:
这在长推理训练中特别重要,因为序列长度本身可能与任务难度、推理深度和奖励相关。
MDP 与 Bandit:为什么这与 loss 聚合有关
RLHF 中还存在两种不同的建模视角。
Token-level MDP
将每个 token 看作一个动作:
Critic 为每个状态估计
并利用逐 token reward、TD error 和 GAE 得到不同位置的优势:
因此同一序列中通常有
这是传统 PPO 的典型建模方式。
Sequence-level Bandit
另一种方式把整个 completion
视为一次完整动作,只得到一个序列级奖励:
由此得到一个序列级优势:
随后广播到所有 token:
因此同一回答中的所有 token 使用相同的优势。
RLOO、GRPO 等方法常采用这种序列级思想,例如:
然后
此时 loss 如何在 token 和 sequence 之间聚合就更加关键,因为优势本身已经是序列级的。
为什么 RLHF 中通常取
在一般的多步 MDP 中:
通常取
来降低遥远未来奖励的权重。
但 RLHF 有一个特殊性质:主要任务奖励通常只在整条回答完成之后得到,例如:
或 verifier 给出的最终正确性奖励。
如果采用
则这个最终奖励传播到较早 token 时会变成
序列越长,前面的 token 获得的任务奖励就越小。
例如:
时:
这样一个最终奖励几乎无法影响早期推理 token。
但对于长推理而言,前面的 token 并不一定比后面的 token “不重要”;它们可能正是完成后续推理所必需的步骤。因此经典 RLHF 通常采用
使最终奖励可以完整传播到整个 response:
这与一般机器人控制中的 MDP 不同:机器人任务中,未来状态可能对应真实的未来时间和独立决策,因此折扣未来奖励通常具有明确意义;而 RLHF 的一条 completion 更接近一次完整的有限时域决策过程,其最终评分是对整个回答的评价。
随着智能体式(agentic)RL 场景日渐成熟——模型在其中执行真正的多步动作,例如工具调用、代码执行和网页浏览——折扣可能会重新变得重要,因为这些场景涉及真正彼此独立的序贯决策,其长期后果各不相同。
总结
这一部分实际上涉及两个相互关联的问题:
对于 token-level PPO:
loss 聚合主要决定不同 token 和不同长度序列的梯度尺度。
对于 GRPO / RLOO 一类 sequence-level 方法:
整个序列共享同一个优势,因此长度归一化方式会直接决定:
这也是按序列归一化、按 token 归一化和定长归一化之间最本质的区别。
7. 异步 RL 系统总结
偏向infra的处理。
传统策略梯度通常采用严格的 on-policy 同步训练:
即必须先用当前策略
图 23 展示了三种典型组织方式:
生成与训练合并、严格同步
所有 GPU 先共同生成,再共同训练。能够保持严格 on-policy,但生成和训练串行,吞吐率较低。生成与训练分离,但仍保持 on-policy
一组 GPU 负责生成(actor),另一组负责训练(learner)。虽然两部分被拆开,但为了保证训练使用最新策略生成的数据,二者仍需要频繁等待和同步,因此会出现明显的 GPU 空闲时间。异步 / off-policy 训练
actor 持续生成,learner 同时持续更新参数,使两条流水线重叠:
这样 GPU 利用率和整体吞吐量显著提高,但 learner 使用的数据可能来自旧策略,因此不再严格 on-policy,需要处理策略滞后带来的 off-policy 问题。
工程上通常采用 Actor–Learner 架构:
- Actor:负责使用语言模型进行 rollout / response generation;
- Learner:负责计算奖励、优势和梯度并更新策略;
- Prompt Queue:向多个 actor 分发生成任务;
- Results Queue:收集 actor 返回的生成轨迹,供 learner 消费;
- 常利用 Ray 等分布式框架组织进程,并使用 vLLM 等推理引擎提高生成吞吐量。
其核心思想可以概括为:
从而把 RL 训练由传统的:
变成近似的双流水线:
不过异步化会引入两个主要问题:
样本长度不均衡。 推理模型的回答可能从几百 token 到数万甚至十万 token。同步 batch 中只要有一个样本特别长,其余 GPU 就必须等待它生成完成,造成大量算力空闲。常见缓解方法是 sequence-level packing,即动态地把较短样本组合到同一批次中,使不同 GPU 的 token 工作量更加均衡。
策略滞后(policy staleness)。 在完全异步系统中,actor 可能仍在使用旧参数
因此,异步 RL 的关键矛盾是:
所以异步 RL 不只是一个系统优化问题,还要求训练算法能够容忍甚至校正这种 off-policy / policy lag。其主要目标是在GPU 利用率、生成吞吐量和训练稳定性之间取得平衡。
7. 截断重要性采样(TIS)总结
截断重要性采样(Truncated Importance Sampling, TIS)主要用于异步 RL 中修正采样策略与当前学习策略之间的分布偏差。
普通重要性采样使用比率
将由旧策略采集的数据重新加权,使其近似服从当前策略。但当两个策略差异较大时,
TIS 对这个比率施加单侧上限截断:
其中
需要注意,TIS 与 PPO 的 clip 不完全相同。PPO 通常限制策略更新比例在
时通常仍允许其自然下降,而只有
时才截断为
在异步 LLM RL 中,需要区分两类不同的策略偏差。
第一类是训练阶段内部的策略漂移。例如 PPO/GRPO 对同一批 rollout 做多次梯度更新时,当前策略
这就是 PPO/GRPO 中原本已经存在的 policy ratio。
第二类是系统层面的 learner–sampler 偏差。异步训练时,actor / sampler 可能仍用较旧参数生成样本,而 learner 已经更新到新的参数,因此:
并使用
这两个 ratio 的来源不同:
二者可以同时存在。
对于只有单次梯度更新的 REINFORCE,不存在 PPO 式的 batch 内策略漂移,此时主要只需要修正 learner 和 sampler 之间的偏差:
而对于 PPO / GRPO,由于既存在异步采样偏差,又存在多步更新导致的策略漂移,因此目标大致可以写成:
也就是说:
实际实现通常在 logprob 空间计算:
再计算
工程上通常还会先把 logratio 限制在如
因此可以把整个 TIS 的作用浓缩为:
它本质上是在off-policy 校正能力与梯度稳定性之间做折中。
辅助方法
广义优势估计GAE
1. 步优势估计
一步优势:
两步优势:
一般的
其中
2. 用 TD 误差表示 步优势
定义一步 TD 误差:
例如两步:
中间的
3. GAE 的第一种表示:不同 步优势的加权平均
GAE 不选择固定的
即:
4. GAE 的第二种表示:TD 误差的加权和
将
代入上式并整理,可以得到:
也就是:
因此实际计算时还能写成反向递推:
5. 两个折扣参数的含义
:奖励折扣因子。控制未来奖励本身的重要程度:
:GAE 衰减参数。控制 Advantage 估计时使用多远的 TD 信息,也控制偏差—方差折中:
即退化为一步 TD,通常低方差、高偏差;而
时会纳入更多远期 TD 误差,更接近长步数/Monte Carlo 的优势估计,通常低偏差、高方差。
所以可以简单记成:
6. GAE 的 Batch 并行计算示例
实际 PPO / RLHF 中通常不会只计算一条轨迹,而是把多条轨迹组成 Batch 并行计算。假设 Batch Size = 2,两条轨迹的有效长度分别为 3 和 2:
为了组成相同长度的 Tensor,需要将第二条轨迹 padding 到长度 3:
定义 valid_mask 表示当前位置是否为有效 token:
因此数据形状都是:
这里最重要的是:Batch 维度可以完全并行,但时间维度仍然需要从后向前递推。
GAE 的递推公式为:
对于 Batch 中的两条轨迹,可以同时计算:
也就是说,每次循环处理的不是一个标量,而是一个长度为 B=2 的向量。
对应代码:
import torch
# shape = (B=2, L=3)
rewards = torch.tensor([
[1.0, 2.0, 3.0],
[2.0, 1.0, 0.0], # 最后一个位置是 padding
])
values = torch.tensor([
[3.0, 4.0, 2.0],
[2.0, 2.0, 0.0],
])
# 1 = 有效位置,0 = padding
valid_mask = torch.tensor([
[1.0, 1.0, 1.0],
[1.0, 1.0, 0.0],
])
gamma = 0.9
lam = 0.95
B, L = rewards.shape
advantages = torch.zeros_like(rewards)
# shape = (B,)
next_v = torch.zeros(B)
gae = torch.zeros(B)
for t in reversed(range(L)):
valid = valid_mask[:, t]
# 当前时间步之后是否还能继续传播
if t == L - 1:
next_valid = torch.zeros(B)
else:
next_valid = valid_mask[:, t + 1]
# 下面所有运算都是 B 条轨迹并行进行
delta = (
rewards[:, t]
+ gamma * next_valid * next_v
- values[:, t]
)
gae = (
delta
+ gamma * lam * next_valid * gae
)
# padding 位置直接清零
gae = gae * valid
advantages[:, t] = gae
next_v = values[:, t]
print(advantages)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
核心在于:
rewards[:, t]
values[:, t]
gae2
3
它们都不是标量,而是:
例如在
rewards[:, 1]得到:
因此 GPU 会同时计算两条轨迹的:
而不是先完整计算轨迹 1,再计算轨迹 2。
整个计算结构可以理解为:
Batch 维:并行
↓ ↓
轨迹 1 轨迹 2
t=2 [计算] [PAD] ← 同时计算
↑ ↑
t=1 [计算] [计算] ← 同时计算
↑ ↑
t=0 [计算] [计算] ← 同时计算
时间维:反向递推2
3
4
5
6
7
8
9
10
11
所以 GAE 的并行设计实际上是:
mask 则负责阻止 GAE 穿过轨迹终点或 padding 继续传播。例如第二条轨迹的最后一个位置是 PAD,因此该位置不会产生 advantage,也不会把信息传播回有效轨迹。