视觉研究者的强化学习指南:PPO 与 GRPO
这篇博客写了什么,写给谁看
这是一篇关于 近端策略优化(PPO) 的深度解读——它是 LLM 的 RLHF 中最流行的算法之一。同时也会介绍 DeepSeek 提出的 组相对策略优化(GRPO),最后还有 DeepSeek R1 技术报告 中一些令人印象深刻的技巧总结。
这些内容出自一个主要做视觉方向、对 RL 了解不多的人之手。如果你也是这样的情况,希望这篇文章对你有帮助。
LLM 的预训练与后训练
LLM 的训练可以分为预训练和后训练两个阶段:
-
预训练:经典的"往模型里灌数据"阶段,模型在大规模网络数据上训练做下一个 token 预测;
-
后训练:这个阶段我们尝试提升模型的推理能力。后训练通常有两个阶段,即:
- ✨ 监督微调(SFT):在高质量示例上微调模型
- ✨ RLHF:使用强化学习进一步对齐模型
DeepSeek 的超高效后训练
值得注意的是,DeepSeek R1 技术报告中最令人惊讶的事情之一是:他们的 R1-zero 模型完全跳过了 SFT 部分,直接在基座模型(DeepSeek V3)上应用 RL。这样做有几个好处:
-
计算效率:跳过后训练的一个阶段带来了计算效率的提升;
-
开放式学习:允许模型通过探索来"自我进化"推理能力;
-
对齐:避免了人工策划的 SFT 数据引入的偏差。
注意:虽然看到有人通过跳过整个后训练阶段来节省算力似乎是"那不是显而易见嘛"的事情,但我怀疑没有一个非常好的基座模型是做不到这一点的。
但他们并没有止步于此!DeepSeek 还通过引入 GRPO 替代 PPO,使 RLHF 部分更加高效,这消除了对单独 critic 模型的需求(critic 模型通常和策略模型一样大),将内存和计算开销减少了约 50%。要理解他们为什么以及如何做到这一点,也为了满足我们自己的求知欲,现在让我们来看看 RLHF 究竟是怎么做的,以及这些算法在其中扮演什么角色。
RLHF
让我们把 RLHF 的工作流程分解为几个步骤:
-
步骤 1:对于每个提示(prompt),从模型中采样多个回复;
-
步骤 2:人类按质量对这些输出进行排序;
-
步骤 3:训练一个奖励模型来预测人类的偏好/排序,给定任意模型回复;
-
步骤 4:使用 RL(如 PPO、GRPO)来微调模型,以最大化奖励模型的分数。
如我们所见,这个过程相对简单,有两个可学习的组件,即奖励模型和"RL 部分"。现在让我们更详细地深入每个组件。
奖励模型
奖励模型确实处于自动化工作的最前线:现实中,我们不可能让人类对模型的所有输出进行排序。一个节省成本的方法是让标注员对 LLM 输出的一小部分进行评分,然后训练一个模型来预测这些标注员的偏好——这就是奖励模型的由来。说完这些,现在来看一些数学:
将我们的可学习奖励模型记为 $R_\phi$。给定一个提示 $p$,LLM 生成 $N$ 个回复 ${r_1, r_2, \ldots, r_N}$。然后,假设根据人类评分者的判断,回复 $r_i$ 优于 $r_j$,奖励模型被训练来最小化以下目标函数:
$$\mathcal{L}(\phi) = -\log \sigma(R_\phi(p, r_i) - R_\phi(p, r_j))$$
其中 $\sigma$ 表示 sigmoid 函数。
旁注:该目标函数源自 Bradley-Terry 模型,它将评分者偏好 $r_i$ 优于 $r_j$ 的概率定义为:
$$P(r_i \succ r_j) = \frac{\exp\big(R_\phi(p, r_i)\big)}{\exp\big(R_\phi(p, r_i)\big) + \exp\big(R_\phi(p, r_j)\big)}$$
对这个概率取负对数似然就得到上面的损失 $\mathcal{L}(\phi)$。sigmoid $\sigma$ 自然地从重新整理 Bradley-Terry 比率中产生。
注意,对于部分回复,奖励始终为 0;只有对于 LLM 的完整回复,奖励模型才会返回一个非零的标量分数。这个重要事实在后面会变得很关键。
“RL 部分”:PPO
这部分仅供对 PPO 感兴趣的读者阅读,如果你打开这篇博客的目的是理解 GRPO,其实不需要理解这些。我只能说,终于理解 PPO 的工作原理给我带来了巨大的快乐,然后当我意识到 GRPO 比 PPO 简单得多时,又有了一种巨大的"果然如此"的满足感。所以如果你准备好了坐一趟情感过山车——让我们开始吧。
首先,一个高层概述。PPO 全称近端策略优化(Proximal Policy Optimization),它需要以下组件:
-
策略($\pi_\theta$):经过预训练/SFT 的 LLM;
-
奖励模型($R_\phi$):一个已训练且冻结的网络,给定对提示的完整回复,提供标量奖励;
-
评论家($V_\gamma$):也称为价值函数,是一个可学习的网络,接受对提示的部分回复并预测标量奖励。
🎉 恭喜——把 LLM 叫做"策略",你现在就是一个 RL 人了!每个组件的用途在我们了解工作流程后会变得更加清晰,工作流程包含五个阶段:
-
生成回复:LLM 为给定提示生成多个回复;
-
评分回复:奖励模型为每个回复分配奖励;
-
计算优势:使用 GAE 计算优势(后面会详细说,用于训练 LLM);
-
优化策略:通过优化总目标来更新 LLM;
-
更新评论家:训练价值函数使其更好地预测给定部分回复的奖励。
现在让我们更详细地看看其中一些阶段/组件,然后看看它们是如何组合在一起的。
术语:状态和动作
在继续之前再介绍一些 RL 术语。在本节的讨论中,我们将使用状态(记为 $s_t$)和动作(记为 $a_t$)这两个术语。注意,这里下标 $t$ 用于在 token 级别表示状态和动作;相比之下,之前我们定义提示 $p$ 和回复 $r_i$ 时,下标 $i$ 用于在实例级别表示回复。
为了更清楚,假设我们给 LLM 一个提示 $p$。LLM 然后开始逐 token 生成长度为 $T$ 的回复 $r_i$:
-
$t=0$:我们的状态就是提示,即 $s_0 = {p}$,第一个动作 $a_0$ 就是 LLM 生成的第一个词 token;
-
$t=1$:状态变为 $s_1 = {p, a_0}$,LLM 在以状态为条件生成下一个动作 $a_1$;……
-
$t=T-1$:状态为 $s_{T-1} = {p, a_{0:T-2}}$,LLM 生成最后一个动作 $a_{T-1}$。
将其与之前的记号联系起来,所有动作串在一起构成一个回复,即 $r_i = {a_0, a_1, \ldots, a_{T-1}}$。
广义优势估计(GAE)
我们的策略被更新以优化优势——直觉上,它定义了一个特定动作 $a_t$(即词)比策略在状态 $s_t$(即提示 + 已生成的词)中采取的平均动作好多少。形式化地:
$$A_t = Q(s_t, a_t) - V(s_t)$$
其中 $Q(s_t, a_t)$ 是在状态 $s_t$ 中采取特定动作 $a_t$ 的期望累积奖励,$V(s_t)$ 是策略在状态 $s_t$ 中采取平均动作的期望累积奖励。
估计这个优势有两种主要方法,各有利弊:
-
蒙特卡洛(MC):使用完整轨迹(即完整回复)的奖励。由于稀疏奖励,这种方法方差很高——从 LLM 中采样足够多的样本来用 MC 优化代价很高,但它偏差低,因为我们可以准确地建模奖励;
-
时间差分(TD):使用单步轨迹奖励(即衡量给定提示后刚生成的词有多好)。这样我们可以在 token 级别计算奖励,显著降低了方差,但同时偏差上升了,因为我们无法从部分生成的回复中准确预测最终奖励。
这就是 GAE 的用武之地——它通过多步 TD 来平衡偏差和方差。然而,回想一下我们之前提到的,如果回复不完整,奖励模型会返回 0:我们如何在不知道生成一个词前后奖励如何变化的情况下计算 TD?因此我们引入了一个正好做这件事的模型,我们称之为"评论家"。
评论家(价值函数)
🕵️ 评论家被训练来在只给定部分状态的情况下预测最终奖励,这样我们就可以计算 TD。训练评论家 $V_\gamma$ 相当直接:
给定部分状态 $s_t$,我们想预测奖励模型在给定完整状态 $s_T = {p, r}$ 时的输出。评论家的目标函数可以写为:
$$L(\gamma) = \mathbb{E}t \left[(V\gamma(s_t) - \text{sg}(R_\phi(s_T)))^2\right]$$
其中 $\text{sg}$ 表示停止梯度操作。如我们所见,评论家用简单的 L2 损失来拟合奖励模型的分数。
你可能注意到,虽然奖励模型 $R_\phi$ 在 PPO 之前训练并冻结,但评论家是和 LLM 一起训练的,尽管它的工作也只是预测奖励。这是因为价值函数必须在当前策略下估计部分回复的奖励;因此,它必须与 LLM 一起更新,以避免其预测过时和不对齐。而这,就是 RL 中所说的 Actor-Critic(麦克风放下)。
回到 GAE
有了评论家 $V_\gamma$,我们现在有了一种从部分状态预测奖励的方法。现在让我们继续 GAE,如前所述它计算多步 TD 目标:
$$A^{\text{GAE}}K = \delta_0 + \lambda \delta_1 + \lambda^2 \delta_2 + \cdots + \lambda^{K-1} \delta{K-1} = \sum^{K-1}_{t=0} \lambda^t \delta_t$$
其中 $K$ 表示 TD 步数,$K < T$(因为显然你不能计算超出轨迹长度的 TD)。$\delta_t$ 表示第 $t$ 步的 TD 误差,计算为:
$$\delta_t = V_\gamma(s_{t+1}) - V_\gamma(s_t)$$
简单来说,TD 误差计算一个时间步的期望总奖励差异,而 $A_K^{\text{GAE}}$ 通过聚合 $K$ 步的单步 TD 误差来估计优势。GAE 方程中的 $\lambda$ 控制方差和偏差之间的权衡:当 $\lambda = 0$ 时,GAE 退化为单步 TD;当 $\lambda = 1$ 时,GAE 变为 MC。
在 RLHF 中,我们想最大化这个优势项,从而最大化 LLM 生成的每个 token 的奖励。
旁注:好吧,为了简洁我这里偷了一些工。原始的 GAE 中还有一个折扣因子 $\eta$:
$$A^{\text{GAE}}K = \sum^{K-1}{t=0} (\lambda\eta)^t \delta_t$$
它也用在 TD 误差 $\delta_t$ 中,还有一个额外的奖励项:
$$\delta_t = R_\phi(s_t) + \eta V_\gamma(s_{t+1}) - V_\gamma(s_t)$$
但由于我们几乎总是有 $\eta = 1$,且对于 $t < T$ 总是有 $R_\phi(s_t) = 0$,我走了捷径简化并省略了这些项。
组合在一起——PPO 目标函数
PPO 目标函数有几个组成部分,即:1)裁剪代理目标,2)熵奖励,3)KL 惩罚。
1. 裁剪代理目标
这是我们最大化 $A_K^{\text{GAE}}$ 的地方,使得 LLM 预测的每个 token 都能最大化奖励(或者,按照之前优势的定义,LLM 预测的每个 token 应该比其平均预测好得多)。裁剪代理目标通过概率比 $c_t(\pi_\theta)$ 来约束策略更新:
$$L^{\text{clip}}(\theta) = \mathbb{E}t \left[ \min(c_t(\pi\theta)A^{\text{GAE}}t, \text{clip}(c_t(\pi\theta), 1-\epsilon, 1+\epsilon)A^{\text{GAE}}_t)\right]$$
其中 $\epsilon$ 控制裁剪范围,$c_t(\pi_\theta)$ 是在给定累积状态 $s_t$ 下预测特定 token $a_t$ 的概率比,更新前后对比:
$$c_t(\pi_\theta) = \frac{\pi_\theta (a_t | s_t)}{\pi_{\theta_{\text{old}}} (a_t | s_t)}$$
具体例子:
-
假设 LLM 给词 “unlimited” 分配以下概率:
- 更新前:0.1
- 更新后:0.3
- 那么概率比 $c_t = 0.3 / 0.1 = 3$
-
如果我们取 $\epsilon = 0.2$,$c_t$ 被裁剪到 1.2;
-
最终裁剪代理损失为 $L^{\text{clip}}(\pi_\theta) = 1.2 A_K^{\text{GAE}}$。
你可以把裁剪理解为一种防止过度自信的方法——没有裁剪的话,一个大的 $A_K^{\text{GAE}}$ 可能导致策略过度偏向某个动作。
2. KL 散度惩罚
此外,我们还有 KL 散度惩罚,它防止当前策略 $\theta$ 偏离我们微调的原始模型 $\theta_{\text{orig}}$ 太远:
$$\text{KL}(\theta) = \mathbb{E}{s_t} \left[ \mathbb{D}{\text{KL}}(\pi_{\theta_{\text{orig}}}(\cdot | s_t) | \pi_{\theta}(\cdot | s_t)) \right]$$
KL 简单地通过在序列和批次上取平均来估计。
伪代码:
# 计算原始策略和当前策略/模型之间的 KL 散度
logits_orig = original_model(states) # 原始模型的 logits
logits_current = current_model(states) # 当前模型的 logits
probs_orig = F.softmax(logits_orig, dim=-1)
log_probs_orig = F.log_softmax(logits_orig, dim=-1)
log_probs_current = F.log_softmax(logits_current, dim=-1)
kl_div = (probs_orig * (log_probs_orig - log_probs_current)).sum(dim=-1)
kl_penalty = kl_div.mean() # 在序列和批次上取平均
3. 熵奖励
熵奖励通过惩罚低熵来鼓励 LLM 生成的探索性:
$$H(\theta) = - \mathbb{E}{a_t} [\log \pi\theta (a_t | s_t)]$$
伪代码:
# 计算当前策略的熵
probs_current = F.softmax(logits_current, dim=-1)
log_probs_current = F.log_softmax(logits_current, dim=-1)
entropy = -(probs_current * log_probs_current).sum(dim=-1)
entropy_bonus = entropy.mean() # 在序列和批次上取平均
最终的 PPO 目标函数
给定上述三个项,加上价值函数 MSE 损失(回忆一下它是和 LLM 一起优化的),PPO 目标函数定义如下:
$$\mathcal{L}{\text{PPO}}(\theta, \gamma) = \underbrace{\mathcal{L}{\text{clip}}(\theta)}{\text{最大化奖励}} + \underbrace{w_1 H(\theta)}{\text{最大化熵}} - \underbrace{w_2 \text{KL}(\theta)}{\text{惩罚 KL 散度}} - \underbrace{w_3 \mathcal{L}(\gamma)}{\text{评论家 L2}}$$
各项总结如下:
| 项 | 目的 |
|---|---|
| $\mathcal{L}_{\text{clip}}(\theta)$ | 最大化高优势动作的奖励(通过裁剪避免不稳定) |
| $H(\theta)$ | 最大化熵以鼓励探索 |
| $\text{KL}(\theta)$ | 惩罚偏离参考策略(稳定性) |
| $\mathcal{L}(\gamma)$ | 最小化价值预测误差(评论家 L2 损失) |
“RL 部分”:GRPO
现在我们对 PPO 有了很好的理解,GRPO 就非常容易理解了。关键差异在于两个算法如何估计优势 $A$:与 PPO 通过评论家估计优势不同,GRPO 通过使用相同提示从 LLM 采样多个样本来实现。
🌈 工作流程:
-
对于每个提示 $p$,从 LLM 策略 $\pi_\theta$ 中采样一组 $N$ 个回复 $\mathcal{G} = {r_1, r_2, \ldots, r_N}$;
-
使用奖励模型 $R_\phi$ 为每个回复计算奖励 ${R_\phi(r_1), R_\phi(r_2), \ldots, R_\phi(r_N)}$;
-
计算每个回复的组归一化优势:
$$A_i = \frac{R_\phi(r_i) - \text{mean}(\mathcal{G})}{\text{std}(\mathcal{G})}$$
其中 $\text{mean}(\mathcal{G})$ 和 $\text{std}(\mathcal{G})$ 分别表示组内的均值和标准差。
简单多了,对吧?在 GRPO 中,优势被近似为每个回复在其回复组内的归一化奖励。这消除了对 critic 网络逐步计算奖励的需求,更不用说数学上的简洁和优雅了。这确实让人不禁要问——为什么我们不早点这么做?
我没有一个好的答案,因为缺乏实践经验:我猜测这与硬件能力有关,因为我们如今能使用的现代 GPU/TPU 使得采样变得更快更高效。再说一次我不是专家,非常欢迎大家对此提供见解!
更新:来自 @him_sahni 的一些见解,他"前世是做 RL 的":“为什么之前没人尝试过 GRPO"的原因是——我们尝试过。在 REINFORCE 中,你通过减去一个基线(通常是多个轨迹的平均奖励)来更新策略,以减少变异性。事实上,理论表明理想的基线是从某个状态开始的总期望未来奖励,通常称为"价值”。使用价值函数作为基线就是所谓的 actor-critic 方法,PPO 是它的一个稳定版本。现在,在传统 REINFORCE 中,基线可以是当前状态的任意函数,传统上只是单个批次中轨迹的奖励;而在 GRPO 中,这个基线是对每个提示生成 1000 个样本来计算的,这才是新颖之处。
GRPO 目标函数
与 PPO 类似,GRPO 仍然使用裁剪代理损失和 KL 惩罚。这里不使用熵奖励项,因为基于组的采样本身就鼓励了探索。裁剪代理损失与 PPO 中使用的相同,但为了完整性,这里给出:
$$\mathcal{L}{\text{clip}}(\theta) = \frac{1}{N} \sum{i=1}^N \left( \min\left( \frac{\pi_\theta(r_i|p)}{\pi_{\theta_{\text{old}}}(r_i|p)} A_i, \ \text{clip}\left( \frac{\pi_\theta(r_i|p)}{\pi_{\theta_{\text{old}}}(r_i|p)}, 1-\epsilon, 1+\epsilon \right) A_i \right) \right)$$
然后加上 KL 惩罚项,最终的 GRPO 目标函数可以写为:
$$\mathcal{L}{\text{GRPO}}(\theta) = \underbrace{\mathcal{L}{\text{clip}}(\theta)}{\text{最大化奖励}} - \underbrace{w_1 \mathbb{D}{\text{KL}}(\pi_\theta | \pi_{\text{orig}})}_{\text{惩罚 KL 散度}}$$
关于 R1 的更多思考:极致简约
最后,说几句关于 R1 的话。
不管是否被过度炒作,从阅读论文来看,R1 最突出的一点是它采用了一种精简的、务实的 LLM 训练方法,优先考虑极致简约而非精巧复杂。GRPO 只是冰山一角。以下是更多极致简约的例子:
1. 基于规则的确定性奖励
-
是什么:放弃神经过程奖励模型(PRM)或结果奖励模型(ORM)。使用二元检查,包括:
- 答案正确性:最终答案与真实答案匹配(如数学解答、代码编译)
- 格式化:强制答案使用
<think>...</think><answer>...</answer>模板 - 语言一致性:惩罚混合语言输出(如对中文查询用英文推理)
-
为什么:确定性规则避免了奖励黑客(如模型用看似合理但错误的步骤欺骗神经奖励模型),并消除了奖励模型训练成本。
2. 冷启动数据:最少人工干预
-
是什么:不再策划大规模 SFT 数据集,而是通过以下方式收集几千个高质量 CoT 示例:
- 用少样本示例提示基座模型
- 轻量人工后处理(如添加 markdown 格式化)
-
为什么:避免了昂贵的 SFT 阶段,同时用"足够好"的起点引导 RL。
3. 拒绝采样:严格过滤,猛烈训练
-
是什么:在 RL 训练后,生成 60 万条推理轨迹,然后丢弃所有不正确的回复。只保留"赢家"(正确答案)用于监督微调(SFT)。没有花哨的重新排序,没有偏好对。只有适者生存式的过滤。
-
为什么:管用,为什么不呢!
4. 蒸馏:复制粘贴推理
-
是什么:为了训练更小的模型,直接在 DeepSeek-R1 生成的 80 万条回复上微调它们。没有 RL,没有迭代对齐——只有模仿。
-
为什么:更小的模型继承了更大模型通过暴力 RL 发现的推理模式,为小规模部署绕过了昂贵的 RL。
DeepSeek-R1 的设计反映了 AI 领域一个更广泛的趋势:规模和简约往往胜过精巧的工程。通过毫不留情地走捷径——用规则替代学习组件、利用大规模并行采样、锚定预训练基线——R1 以更少的失败模式实现了 SOTA 结果。它不优雅,但很有效。
谁会想到激励好的思考的最佳方式是停止过度思考呢 🌈