基于人类反馈的强化学习

面向语言模型的后训练RLHF简明指南

作者:Nathan Lambert 译者:Junwei He

本章目录

策略梯度算法

让RLHF在语言模型领域流行起来的,正是基于策略梯度(policy gradient)的强化学习算法。 这些算法(如PPO、GRPO、REINFORCE)利用最近生成的样本直接更新模型,而不是像传统强化学习那样依赖经验回放缓冲区(replay buffer)。 本节将介绍策略梯度算法的基本原理,以及它们在现代RLHF框架中的应用。

从机器学习角度看,策略梯度是RLHF流程中最复杂的部分之一。 不过,和大多数现代AI模型一样,其最终效果很大程度上仍取决于输入的数据质量。

RLHF常用的算法体系也在不断演变。 ChatGPT时代,大家公认其核心算法是PPO,许多早期工作也以此为基础。 后来,越来越多研究显示REINFORCE风格的算法也很有潜力 [1] [2],其优点是可以不训练独立的价值网络,从而节省显存,也不必使用 GAE;它仍需要奖励信号,奖励可来自奖励模型或可验证的评分函数。 此外,还有如Group Relative Policy Optimization(GRPO)等新算法,特别适用于推理类任务,但总体上这些算法都可针对具体任务进行调整和优化。 本章将重点介绍策略梯度的基本框架,以及PPO、GRPO和REINFORCE三大算法,因为它们构成了RLHF文献的核心。

相关符号定义请参见“问题设定”章节。

策略梯度算法基础

强化学习算法的目标是最大化状态\(s \in \mathcal{S}\)和动作\(a \in \mathcal{A}\)轨迹上的未来折扣奖励(见第3章定义)。 智能体的目标(return)是某一时刻\(t\)下未来折扣奖励的累加和(\(\gamma\in [0,1)\)为折扣因子,平衡即时与远期收益):

\[G_t = R_{t+1} + \gamma R_{t+2} + \cdots = \sum_{k=0}^\infty \gamma^k R_{t+k+1}.\qquad{(1)}\]

递推形式为: \[G_{t} = \gamma G_{t+1} + R_{t+1}.\qquad{(2)}\]

据此可定义值函数\(V(s)\),即在当前状态下未来回报的期望:

\[V(s) = \mathbb{E}\big[G_t | S_t = s \big].\qquad{(3)}\]

所有策略梯度算法的目标,都是优化由特定策略\(\pi(a|s)\)诱导的值函数。

令 \(d_0(s)\) 为与参数无关的初始状态分布,期望回报目标为: \[ J(\theta) \;=\; \sum_s d_0(s)V^{\pi_\theta}(s), \qquad{(4)}\]

策略梯度算法的核心是计算当前策略下有限时间期望回报的梯度。 有了期望回报\(J\),参数更新为: \[\theta \leftarrow \theta + \alpha \nabla_\theta J(\theta)\qquad{(5)}\]

关键在于如何计算该梯度。 Schulman等(2015)对策略梯度的多种计算方式做了综述 [3]。 以下采用有限回合、\(\gamma=1\) 的常见语言模型设定(终止后奖励为 0)。若优化折扣回报,梯度还需包含外层 \(\gamma^t\) 权重。目标是估计 \(g := \nabla_\theta J(\theta)\),常见形式如:

\[ g = \mathbb{E}\Big[\sum_{t=0}^\infty \Psi_t \nabla_\theta \text{log} \pi_\theta(a_t|s_t) \Big], \qquad{(6)}\]

在上述无折扣设定下,\(\Psi_t\) 可以是:

  1. \(\sum_{t=0}^{\infty} r_t\):整个轨迹的总奖励。
  2. \(\sum_{t'=t}^{\infty} r_{t'}\):动作\(a_t\)之后的奖励(即return \(G\))。
  3. \(\sum_{t'=t}^{\infty} r_{t'} - b(s_t)\):带baseline的版本。
  4. \(Q^{\pi}(s_t, a_t)\):状态-动作值函数。
  5. \(A^{\pi}(s_t, a_t)\):优势函数,是常用的降方差选择,但一般不能保证它在所有基线中方差最低。
  6. \(r_t + V^{\pi}(s_{t+1}) - V^{\pi}(s_t)\):TD残差。

Baseline的作用是降低策略更新的方差(后文详述)。

对于语言模型,上述部分概念需适当解释。 对确定性策略 \(\mu\),有 \(V^\mu(s)=Q^\mu(s,\mu(s))\);只有取最优贪心动作时才涉及 \(\max_a Q\)。对随机策略,有 \(V^\pi(s)=\mathbb{E}_{a\sim\pi(\cdot|s)}Q^\pi(s,a)\)。若 token 拼接产生确定性后继状态 \(s+a\),且即时奖励为 \(r(s,a)\),则:

\[A^\pi(s,a)=r(s,a)+\gamma V^\pi(s+a)-V^\pi(s)\qquad{(7)}\]

这包含即时奖励和后继状态价值。一般随机转移下,后继价值需对转移分布取期望;使用近似价值网络时,单个 TD 残差只是优势的估计。

Vanilla Policy Gradient

最基础的策略梯度实现是对\(J(\theta)\)关于策略参数求导:

\[\nabla_\theta J(\pi_\theta) = \mathbb{E}_\tau \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) G_t \right]\qquad{(8)}\]

普通策略梯度算法的常见问题是梯度方差大,可通过多种方式缓解。 常用方法是引入baseline对值估计进行归一化。 baseline的本质是按状态对下游动作的价值进行归一化(如Advantage即Q值与V值之差)。 最简单的baseline是奖励的批均值或滑动平均。 与当前采样动作无关的状态基线不改变梯度期望,因为 \(\mathbb{E}_{a\sim\pi(\cdot|s)}[\nabla_\theta\log\pi_\theta(a|s)]=0\);它的作用是降低方差,而不是“去偏”。若批均值包含样本自身,其动作依赖性会引入缩放或偏差,不能直接套用这一结论。

许多策略梯度算法都基于优势函数的形式:

\[\nabla_\theta J(\pi_\theta) = \mathbb{E}_\tau \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) A^{\pi_\theta}(s_t, a_t) \right]\qquad{(9)}\]

策略梯度实现的核心是对概率策略求导:

\[\nabla_\theta \log \pi_\theta(a|s) = \frac{\nabla_\theta \pi_\theta(a|s)}{\pi_\theta(a|s)}\qquad{(10)}\]

由链式法则推导:

\[\nabla_\theta \log x = \frac{1}{x} \nabla_\theta x\qquad{(11)}\]

本章后续会用到这些推导。

REINFORCE

REINFORCE算法名称可能是后加缩写,但其核心思想对现代RL算法极为重要。 其定义见经典论文Simple statistical gradient-following algorithms for connectionist reinforcement learning [4]:

名称意为”REward Increment = Nonnegative Factor X Offset Reinforcement X Characteristic Eligibility.”

即更新规则有三个部分:

  1. 非负因子:学习率(如\(\alpha\))。
  2. Offset Reinforcement:baseline \(b\)或其他奖励归一化因子,提升稳定性。
  3. Characteristic Eligibility:每个token的归属度,现代公式中常为policy的log概率。

因此,更新形式如下:

\[ \Delta_\theta = \alpha(r - b)e \qquad{(12)}\]

用现代符号和广义return \(G\),REINFORCE公式为:

\[ \nabla_{\theta}\,J(\theta) \;=\; \mathbb{E}_{\tau \sim \pi_{\theta}}\!\Big[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t \mid s_t)\,(G_t - b) \Big], \qquad{(13)}\]

其中 \(G_t-b(s_t)\) 是带基线的回报估计;当基线等于 \(V^\pi(s_t)\) 时,它的条件期望为优势。记该估计为 \(A_t\),可进一步写为:

\[ \nabla_{\theta}\,J(\theta) \;=\; \mathbb{E}_{\tau \sim \pi_{\theta}}\!\Big[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t \mid s_t)\,A_t \Big], \qquad{(14)}\]

REINFORCE是vanilla策略梯度的具体实现,使用蒙特卡洛方法估算梯度。

REINFORCE可不使用值网络(value network);值网络仅用于baseline。 常见的 PPO actor-critic 实现使用价值网络与 GAE 估计优势;PPO 的裁剪目标本身并不强制使用价值网络,也不能保证优势估计精确。

REINFORCE Leave One Out(RLOO)

RLOO与标准REINFORCE的核心区别在于:RLOO用同一 prompt 下其他独立采样回答的平均奖励做baseline,而不是全batch均值 [5], [1], [6]。

这要求每个prompt生成多个回复,这在RL微调语言模型中很常见。

具体地,RLOO baseline如下(每个prompt采样\(K\)条轨迹或动作\(a_1, ..., a_K\)):

\[ b(s, a_k) = \frac{1}{K-1}\sum_{i=1, i\neq k}^{K} R(s, a_i), \qquad{(15)}\]

对应的优势为:

\[ A(s, a_k) = R(s, a_k) - b(s, a_k). \qquad{(16)}\]

也可写为:

\[ A(s, a_k) = \frac{K}{K - 1}\left(R(s, a_k) - \frac{1}{K}\sum_{i=1}^{K} R(s, a_i)\right). \qquad{(17)}\]

这是一种简单低方差的优势更新,与GRPO非常相似(GRPO后文介绍),只是KL惩罚和步长裁剪的位置不同。 RLOO优势也可与PPO裁剪结合,说明这些算法本质上非常接近。

RLOO等无需值网络的算法,会将序列的优势(或奖励)分配给序列中每个token。 而PPO等用值网络的算法,则为每个token单独分配值,并对最终奖励进行折扣。 例如,带KL惩罚时,RLOO对整个补全求和,PPO等则对每个token单独计算并从奖励中扣除(GRPO则从优势中扣除)。 这些细节和权衡将在后文详述。

Proximal Policy Optimization(PPO)

PPO [7] 是深度RL成功的基石算法之一(如OpenAI的DOTA 5 [8]等)。 其每个样本的待最大化裁剪代理目标为(最小化损失时取负号):

\[ \begin{aligned} \rho_t(\theta)&=\frac{\pi_\theta(a_t|s_t)}{\pi_{\mathrm{old}}(a_t|s_t)},\\ \ell(\rho,A)&=\min\{\rho A,\operatorname{clip}(\rho,1-\varepsilon,1+\varepsilon)A\},\\ J_t(\theta)&=\ell(\rho_t(\theta),A_t). \end{aligned} \qquad{(18)}\]

语言模型中常按 token 计算条件概率比。序列概率虽然等于条件概率的乘积,但逐 token 裁剪并不等价于对整条序列的概率比做一次裁剪。 实际实现中通常用log概率,计算更高效。

\[ J(\theta)=\frac{1}{|a|}\sum_{t=1}^{|a|}\ell(\rho_t(\theta),A_t). \qquad{(19)}\]

这就是PPO的逐token版本,也适用于其他策略梯度方法,后文实现部分会详细介绍。 其中\(\frac{1}{|a|}\)为常见实现习惯,正式推导中未必出现(见[9])。

PPO的核心在于“策略比率”:

\[R(\theta) = \frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)}\qquad{(20)}\]

策略比率控制参数更新,直观易懂。 每个batch的第一步梯度,策略比率为1(通常每batch做1-4步梯度更新)。 如果策略比率大于1且优势为正,说明新策略更倾向于该动作,反之则相反。

不同情况下,损失函数的表达式如下:

裁剪限制的是代理目标在部分方向上的收益,并不对参数更新或 KL 距离施加硬性边界。当裁剪未激活时,该项与未裁剪的重要性加权代理目标一致。

Group Relative Policy Optimization(GRPO)

GRPO最早出现在DeepSeekMath [10],也被DeepSeek-V3 [11]、DeepSeek-R1 [12]等采用。 GRPO 是 PPO 风格的变体:用同一问题下多个回答的奖励构造组内基线,替代独立训练的价值网络。它仍可保留旧策略概率和用于 KL 正则化的参考策略。 这样有两个好处:

  1. 避免用LM主干学习值函数的难题(最佳实践尚未确定)。
  2. 节省显存,无需保留另一套模型权重。

在结果监督下,GRPO 对同一 prompt 采样多个回答。每个回答内部的 token 共享该回答的优势,不同回答的优势通常不同。

GRPO目标函数与PPO类似。对同一问题\(s\)的\(G\)个回复\(\{a_1, ..., a_G\}\),待最大化目标为:

\[ J(\theta)=\frac{1}{G}\sum_{i=1}^G J_i(\theta), \qquad{(21)}\]

可展开为逐token损失:

\[ \begin{aligned} \rho_{i,t}(\theta)&=\frac{\pi_\theta(a_{i,t}|s_{i,t})}{\pi_{\mathrm{old}}(a_{i,t}|s_{i,t})},\\ J_i(\theta)&=\frac{1}{|a_i|}\sum_{t=1}^{|a_i|}\Big[\ell(\rho_{i,t}(\theta),A_{i,t})\\ &\qquad-\beta D_{\mathrm{KL}}\big(\pi_\theta(\cdot|s_{i,t})\|\pi_{\mathrm{ref}}(\cdot|s_{i,t})\big)\Big]. \end{aligned} \qquad{(22)}\]

GRPO与PPO的主要区别在于,GRPO的标准实现将KL距离直接加到损失项中。 优势计算为(\(\epsilon_{\mathrm{num}}>0\) 是数值稳定项,与 PPO 的裁剪阈值不同):

\[ A_i=\frac{r_i-\operatorname{mean}(r_1,\ldots,r_G)}{\operatorname{std}(r_1,\ldots,r_G)+\epsilon_{\mathrm{num}}}. \qquad{(23)}\]

直观上,GRPO是在同一问题内比较多个答案,模型学会更像正确答案、远离错误答案。 这种优势计算简单直接,适合RLHF大批量采样场景。 与PPO、REINFORCE等基于奖励模型打分的RLHF相比,GRPO常用更多每prompt采样数,优势估计有更丰富上下文。

组内标准差归一化会改变不同问题的相对权重 [9]。当一组奖励完全相同(例如二元奖励下全对或全错)时,分子为 0;分母加稳定项后,所有优势均为 0,不会产生更大的优势。标准差很小但非零时,归一化可能放大奖励差异。Dr. GRPO 去掉了这一标准差缩放。

式 23适用于结果监督(如标准奖励模型或可验证奖励),过程监督则需不同实现。 此时,GRPO优势为后续推理步骤归一化奖励之和。

GRPO的优势估计也可不带PPO裁剪,直接用于vanilla策略梯度(如REINFORCE),但这不是标准形式。 实际上,GRPO的Dr. GRPO变体 [9]的优势估计与RLOO仅差一个缩放因子:

\[ \tilde{A}_i = r_i - \text{mean}({r_1, r_2, \cdots, r_G}) = r_i - \frac{1}{G}\sum_{j=1}^G r_j \qquad{(24)}\]

而RLOO优势为:

\[ A_i^\text{RLOO} = r_i - \frac{1}{G-1}\sum_{j=1, i\neq j}^G r_j \qquad{(25)}\]

两者相差\(\frac{G}{G-1}\)的缩放(通常可通过归一化消除实际影响):

\[ \begin{aligned} \frac{G}{G-1} \tilde{A}_i &= \frac{G}{G-1} \left( r_i - \frac{1}{G}\sum_{j=1}^G r_j \right) \\ &= r_i - \frac{1}{G-1} \sum_{j=1, j\neq i}^G r_j \\ &= A_i^{\text{RLOO}} \end{aligned} \qquad{(26)}\]

实现细节

与早期深度RL文献相比,面向语言模型等大模型的RLHF实现有许多细节需注意。 以下是一些关键点:

更多实现细节可参考 [14],算法基础见 [15]。

策略梯度基础实现

一个简单的策略梯度实现如下,利用优势函数估计梯度,为后续PPO/GRPO等算法做准备:

pg_loss = -advantages * ratio

ratio为新旧策略概率的比值(log概率差的指数)。

理解该公式时,需考虑不同batch中的情况。 我们希望模型越好,损失越小。

损失聚合

实际实现时,关键问题是:如何对KL距离和损失进行求和,设计不同的值归属方式。

本节主要采用 token 级 MDP 表示。若将完整回复作为 bandit 动作,可为同一回答共享一个优势;但各 token 的 log 概率、概率比与梯度仍可能不同。MDP 与 bandit 的区别在于动作和状态转移的建模,不能仅归结为损失如何聚合。

假设有如下变量,batch大小为B,序列长度为L:

advantages # [B, 1]
per_token_probability_ratios # [B, L]

用pg_loss = -advantages * ratio即可批量计算损失。这样每个补全的所有token共享同一优势(outcome reward场景),乘以每token的概率比。

若用值网络,loss行为差异更大。 outcome reward时,优势每token相同,token概率差异主导学习动力。

GRPO和PPO等算法中,loss通常对补全token求和:

sequence_loss = ((per_token_loss * completion_mask).sum(dim=1) / \
             completion_mask.sum(dim=1)).mean()

这类似于masked_mean操作。 也可对每token单独平均:

token_loss = ((per_token_loss * completion_mask).sum() / \
            completion_mask.sum())

直观上,按序列平均似乎更好,因为我们关注的是结果,而不是具体token。 但这会引入微妙偏差。 举例,两个长度不同的序列,优势分别为a_1和a_2:

seq_1_advs = [a_1, a_1, a_1, a_1, a_1] # 5 tokens
seq_2_advs = [a_2, a_2, a_2, a_2, a_2, a_2, a_2, a_2, a_2, a_2] # 10 tokens

若最后一个token决定优势为正,多步梯度后,loss可能为:

seq_1_losses = [1, 1, 1, 1, 10] # 5 tokens
seq_2_losses = [1, 1, 1, 1, 1, 1, 1, 1, 1, 10] # 10 tokens

序列平均后:

seq_1_loss = 2.8
seq_2_loss = 1.9

若对所有 token 平均,则为 \((14+19)/(5+10)=2.2\);若先对每条序列平均再对两条序列平均,则为 \((2.8+1.9)/2=2.35\)。 若序列差异更大,loss差距会更明显。

更完整的例子见下方代码,演示了两条样本(长短不同)下三种loss聚合方式。

from typing import Optional
import torch

def masked_mean(values: torch.Tensor, mask: torch.Tensor, axis: Optional[int] = None) -> torch.Tensor:
    if axis is not None:
        return (values * mask).sum(axis=axis) / mask.sum(axis=axis)
    else:
        return (values * mask).sum() / mask.sum()

def masked_sum(
        values: torch.Tensor,
        mask: torch.Tensor,
        axis: Optional[int] = None,
        constant_normalizer: float = 1.0,
    ) -> torch.Tensor:
    if axis is not None:
        return (values * mask).sum(axis=axis) / constant_normalizer
    else:
        return (values * mask).sum() / constant_normalizer

ratio = torch.tensor([
    [1., 1, 1, 1, 1, 1, 1,],
    [1, 1, 1, 1, 1, 1, 1,],
], requires_grad=True)


advs = torch.tensor([
    [2, 2, 2, 2, 2, 2, 2,],
    [2, 2, 2, 2, 2, 2, 2,],
])

masks = torch.tensor([
    [1, 1, 1, 1, 0, 0, 0,],
    [1, 1, 1, 1, 1, 1, 1,],
])

max_gen_len = 7

masked_mean_result = masked_mean(ratio * advs, masks, axis=1)
masked_mean_token_level = masked_mean(ratio * advs, masks, axis=None)
masked_sum_result = masked_sum(ratio * advs, masks, axis=1, constant_normalizer=max_gen_len)

print("masked_mean", masked_mean_result)
print("masked_sum", masked_sum_result)
print("masked_mean_token_level", masked_mean_token_level)

masked_mean_result.mean().backward()
print("ratio.grad", ratio.grad)
ratio.grad.zero_()

masked_sum_result.mean().backward()
print("ratio.grad", ratio.grad)
ratio.grad.zero_()

masked_mean_token_level.mean().backward()
print("ratio.grad", ratio.grad)

可以看到,GRPO默认实现(masked_mean)下,短序列每token梯度更大,Dr. GRPO和DAPO则更均衡。 如果用梯度累积,短长序列的平衡还会变化。

另一种聚合方式(见[9])是每token损失用最大序列长度归一化,这会影响不同batch间的损失对比。

实际应用中,最优方案取决于具体任务和在线学习设置。 RLHF实践中,数值稳定性和损失方差最小的方案往往更受青睐。

PPO实现示例

PPO有多种实现,核心损失计算如下。 值函数的计算也很关键,且有多种实现方式。

注意,这里的参考策略(或旧log概率)指采样时的参数,不一定是reference policy。reference policy仅用于KL惩罚。

下面仅展示损失层,假设已在采样阶段用旧价值预测和包含 KL 塑形的奖励计算了 GAE 与回报目标。所有 token 张量均为 [B*G, L],每条回答至少包含一个有效 token;old_logps、gae_advantages、returns 在更新阶段保持固定。

import torch

mask = completion_mask.to(new_per_token_logps.dtype)
advantages = gae_advantages.detach()  # [B*G, L],不要再 unsqueeze
returns = returns.detach()  # GAE + old_values 或选定的回报目标
old_logps = per_token_logps.detach()

# 仅用有效回答 token 计算优势统计量
valid = advantages[completion_mask.bool()]
advantages = (advantages - valid.mean()) / (
    valid.std(correction=0) + 1e-8
)
ratio = torch.exp(new_per_token_logps - old_logps)
eps = self.cliprange
pg_losses1 = -advantages * ratio
pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - eps, 1.0 + eps)
pg_loss_max = torch.maximum(pg_losses1, pg_losses2)

# values 是当前价值网络的预测;目标是回报,不是单步即时奖励
vf_loss = 0.5 * (returns - values).square()
per_token_loss = pg_loss_max + self.vf_coef * vf_loss
loss = ((per_token_loss * mask).sum(1) / mask.sum(1)).mean()

with torch.no_grad():
    clip_frac = ((pg_losses2 > pg_losses1) * mask).sum() / mask.sum()
    # 与采样旧策略的近似 KL;不同于对参考策略的 KL 正则项
    log_ratio = new_per_token_logps - old_logps
    approx_kl = (0.5 * log_ratio.square() * mask).sum() / mask.sum()
    value_loss = (vf_loss * mask).sum() / mask.sum()

PPO的核心在于如何更新策略梯度损失。 关注这三行:

pg_losses1 = -advantages * ratio  # Shape: (B*G, L)
pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - eps, 1.0 + eps)  # Shape: (B*G, L)
pg_loss_max = torch.max(pg_losses1, pg_losses2)  # Shape: (B*G, L)

pg_losses1即vanilla优势策略梯度,PPO在此基础上引入裁剪,使更新步长不至于过大。

代码裁剪的是概率比 ratio,不是 logratio。它通过代理损失抑制部分过大的策略改变,但不保证参数步长或 KL 距离满足硬性上限。

最后取二者最大值,确保更保守的损失更新。

PPO在学习值函数的同时进行上述操作,虽然更复杂,但这是参数更新的核心逻辑。

PPO/GRPO单步梯度(无裁剪)简化

若PPO(或GRPO)每样本只做一次梯度更新,主方程可大幅简化。 此时\(\pi_\theta = \pi_{\theta_{old}}\),更新规则变为(\([]_\nabla\)表示停止梯度):

\[J(\theta) = \frac{1}{G}\sum_{i=1}^G \left(\frac{\pi_\theta(a_i|s)}{\left[\pi_{\theta}(a_i|s)\right]_\nabla}A_i - \beta D_{KL}(\pi_\theta||\pi_{ref})\right). \qquad{(27)}\]

此时可省略第二次策略梯度和裁剪逻辑,优化器更接近标准策略梯度。

GRPO实现示例

DeepSeekMath论文详细介绍了GRPO与PPO的实现差异 [10]。 如,GRPO将KL惩罚直接加到损失项,而PPO多在奖励中加KL。 通常KL距离对每个token单独计算,推理训练时每个prompt采样多个补全,每batch有多个prompt,这里展平后的逐 token KL 形状为 [B*G, L],其中 G 是每个 prompt 的回答数。

综合起来,伪代码如下:

# B: Batch Size, L: Sequence Length, G: Number of Generations
# rewards: (B*G,), prompt-major 排列,同一 prompt 的 G 个回答连续
# 要求 G > 1;下面使用总体标准差
mean_grouped_rewards = rewards.view(-1, self.num_generations).mean(dim=1)
std_grouped_rewards = rewards.view(-1, self.num_generations).std(dim=1, correction=0)

# Normalize the rewards to compute the advantages
mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(self.num_generations, dim=0)
std_grouped_rewards = std_grouped_rewards.repeat_interleave(self.num_generations, dim=0)
# Shape: (B*G,)

# Compute advantages
advantages = (rewards - mean_grouped_rewards) / (std_grouped_rewards + 1e-4)
advantages = advantages.unsqueeze(1)
# Shape: (B*G, 1)

# Compute probability ratio between new and old policies
ratio = torch.exp(new_per_token_logps - per_token_logps.detach())  # Shape: (B*G, L)

# PPO clipping objective
eps = self.cliprange  # e.g. 0.2
pg_losses1 = -advantages * ratio  # Shape: (B*G, L)
pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - eps, 1.0 + eps)  # Shape: (B*G, L)
pg_loss_max = torch.max(pg_losses1, pg_losses2)  # Shape: (B*G, L)

# important to GRPO -- PPO applies this in reward traditionally
# Combine with KL penalty
per_token_loss = pg_loss_max + self.beta * per_token_kl  # Shape: (B*G, L)

# Apply completion mask and compute final loss
loss = ((per_token_loss * completion_mask).sum(dim=1) / completion_mask.sum(dim=1)).mean()
 # Scalar

# Compute core metric for logging (KL, reward, etc. also logged)
with torch.no_grad():
    # Compute clipping fraction
    clip_frac = ((pg_losses2 > pg_losses1).float() * completion_mask).sum() / completion_mask.sum()

    # Compute approximate KL
    log_ratio = new_per_token_logps - per_token_logps.detach()
    approx_kl = (0.5 * log_ratio.square() * completion_mask).sum() / completion_mask.sum()

更多解释请见上文PPO部分。

RLOO vs. GRPO

RLOO的优势更新与GRPO极为接近,突出两者在本质上的相似性。 RLOO的优势是相对于同一问题下其他补全的奖励。 简明代码(参考TRL实现):

 # 此处是 generation-major 排列:每轮依次采样 N 个 prompt
# 若输入是 prompt-major,需要先 reshape(N, k).T,不能直接套用
# rloo_k --> 每prompt补全数,必须 > 1
# rlhf_reward --> 所有补全的奖励,长度B = N x k
rlhf_reward = rlhf_reward.reshape(rloo_k, -1) #
# 形状: (k, N),每列j为prompt j的k个奖励

baseline = (rlhf_reward.sum(0) - rlhf_reward) / (rloo_k - 1)
# baseline --> leave-one-out基线奖励,形状: (k, N)
# baseline[i, j]是prompt j除i外样本的平均奖励

advantages = rlhf_reward - baseline
# advantages --> 同形状

advantages = advantages.flatten() # 恢复原始tensor形状

其余实现细节与其他策略梯度方法类似。

补充话题

要精通策略梯度算法在RLHF中的应用,还有许多细节值得关注。

广义优势估计(GAE)

广义优势估计(Generalized Advantage Estimation, GAE)是一种平衡偏差-方差权衡的优势计算方法[3]。较短的自举估计通常方差较低,但更依赖价值函数近似的准确性;完整回报的蒙特卡洛估计通常方差较高,却不依赖末端自举值(真正终止时)。GAE通过结合多步预测和指数加权滑动平均来优化这一权衡。

\(n\)步优势估计的数学表达式如下(类似于时序差分残差):

\[ \hat{A}_t^{(n)} = \begin{cases} r_t + \gamma V(s_{t+1}) - V(s_t), & n = 1 \\ r_t + \gamma r_{t+1} + \gamma^2 V(s_{t+2}) - V(s_t), & n = 2 \\ \vdots \\ \sum_{k=0}^{n-1} \gamma^k r_{t+k} + \gamma^n V(s_{t+n}) - V(s_t), & \text{一般情况} \end{cases} \]

其中: - \(\gamma\) 为折扣因子 - \(V(s)\) 是状态价值函数;\(\delta_t=r_t+\gamma V(s_{t+1})-V(s_t)\) 为 TD 残差 - 有限回合在实际终止处截断求和,终止状态价值为 0;仅因长度限制被截断时,是否自举需按任务定义处理 - 最终表达式通过引入衰减因子\(\lambda\)实现滑动平均:

\[ \hat{A}_t^{\text{GAE}(\gamma,\lambda)} = \sum_{l=0}^{\infty} (\gamma\lambda)^l \delta_{t+l} \]

参考文献

[1]
A. Ahmadian 等, 《Back to basics: Revisiting reinforce style optimization for learning from human feedback in llms》, arXiv preprint arXiv:2402.14740, 2024.
[2]
Z. Wang 等, 《HelpSteer2-Preference: Complementing Ratings with Preferences》, arXiv preprint arXiv:2410.01257, 2024.
[3]
J. Schulman, P. Moritz, S. Levine, M. Jordan, 和 P. Abbeel, 《High-dimensional continuous control using generalized advantage estimation》, 收入 Proceedings of the International Conference on Learning Representations (ICLR), 2016.
[4]
R. J. Williams, 《Simple statistical gradient-following algorithms for connectionist reinforcement learning》, Machine learning, 卷 8, 页 229~256, 1992.
[5]
S. C. Huang, A. Ahmadian, 和 C. F. AI, 《Putting RL back in RLHF》. https://huggingface.co/blog/putting_rl_back_in_rlhf_with_rloo, 2024年.
[6]
W. Kool, H. van Hoof, 和 M. Welling, 《Buy 4 reinforce samples, get a baseline for free!》, 2019.
[7]
J. Schulman, F. Wolski, P. Dhariwal, A. Radford, 和 O. Klimov, 《Proximal policy optimization algorithms》, arXiv preprint arXiv:1707.06347, 2017.
[8]
C. Berner 等, 《Dota 2 with large scale deep reinforcement learning》, arXiv preprint arXiv:1912.06680, 2019.
[9]
Z. Liu 等, 《Understanding R1-Zero-Like Training: A Critical Perspective》, arXiv preprint arXiv:2503.20783, 3月 2025, 载于: https://arxiv.org/abs/2503.20783
[10]
Z. Shao 等, 《Deepseekmath: Pushing the limits of mathematical reasoning in open language models》, arXiv preprint arXiv:2402.03300, 2024.
[11]
A. Liu 等, 《Deepseek-v3 technical report》, arXiv preprint arXiv:2412.19437, 2024.
[12]
D. Guo 等, 《Deepseek-r1: Incentivizing reasoning capability in llms via reinforcement learning》, arXiv preprint arXiv:2501.12948, 2025.
[13]
J. Schulman, 《Approximating KL-divergence》. http://joschu.net/blog/kl-approx.html, 2016年.
[14]
S. Huang, M. Noukhovitch, A. Hosseini, K. Rasul, W. Wang, 和 L. Tunstall, 《The N+ Implementation Details of RLHF with PPO: A Case Study on TL;DR Summarization》, 收入 First Conference on Language Modeling, 2024. 载于: https://openreview.net/forum?id=kHO2ZTa8e3
[15]
L. Weng, 《Policy Gradient Algorithms》, lilianweng.github.io, 2018, 载于: https://lilianweng.github.io/posts/2018-04-08-policy-gradient/
← 上一章: 拒绝采样 下一章: 直接对齐算法 →