前言:GRPO 宏观视角

1. 为什么我们需要 GRPO?(Motivation)

在DeepSeek-Math和DeepSeek-R1等前沿工作中,GRPO被证明是一种极其高效的强化学习算法。要理解它,我们必须先看一眼它的前辈——PPO (Proximal Policy Optimization)

在标准的RLHF(Reinforcement Learning from Human Feedback)流程中,PPO是绝对的王者,但它有一个巨大的痛点:显存占用极大,计算资源昂贵

请看下面这个内存占用对比图:

+-------------------------------------------------------+
|              传统 PPO (Standard RLHF)                 |
| ----------------------------------------------------- |
|  1. Actor Model (策略模型,即我们要训练的LLM)           | <--- 梯度更新
|  2. Critic Model (价值模型,估算状态价值 V(s))          | <--- 梯度更新
|  3. Reference Model (参考模型,用于计算 KL 散度)        | <--- 冻结参数
|  4. Reward Model (奖励模型,用于给 Answer 打分)         | <--- 冻结参数
+-------------------------------------------------------+
        
        ↓↓↓ (演变为) ↓↓↓

+-------------------------------------------------------+
|                     GRPO                              |
| ----------------------------------------------------- |
|  1. Actor Model (策略模型)                            | <--- 梯度更新
|  2. Reference Model (参考模型)                        | <--- 冻结参数
|  [Critic Model 被移除了!]                             |
|  [Reward Model 依然存在,但通常是轻量级或规则基]        |
+-------------------------------------------------------+

核心痛点: PPO 需要维护一个和 Actor 一样大的 Critic 模型(通常是同等规模的 Transformer)。如果你在训练一个 70B 的模型,PPO 意味着你需要加载 70B (Actor) + 70B (Critic) + 70B (Ref) + RM,这对显存是灾难性的。

GRPO 的核心洞察: 我们真的需要一个单独的神经网络(Critic)来告诉 Actor “你做得好不好”吗?能不能直接通过“这一组生成结果的内部比较”来判断优劣?

2. GRPO 的核心机制:自顶向下的直觉

GRPO 全称 Group Relative Policy Optimization。顾名思义,它由两个关键词组成:

  1. Group(组)

  2. Relative(相对)

想象一下考试评分:

  • PPO (Value-based): 学生 A 回答了问题。老师(Reward Model)打分 80 分。但是,这个 80 分好不好?我们需要一个“预言家”(Critic Model)来预测:“这道题通常大家能拿 75 分”。

    • 优势(Advantage)= 80 - 75 = +5。

    • 结论: Critic 必不可少,用来提供 Baseline(基线)。

  • GRPO (Group-based): 我就不请“预言家”了。我让学生 A 针对同一个问题,生成 8 个不同的答案(Group)。 这 8 个答案的得分分别是:[80, 70, 90, 60, 85, 75, 95, 65]。 算出这组的平均分:77.5 分。

    • 答案1 (80分) 的优势 = 80 - 77.5 = +2.5

    • 答案3 (90分) 的优势 = 90 - 77.5 = +12.5

    • 答案4 (60分) 的优势 = 60 - 77.5 = -17.5

结论: GRPO 通过采样一组输出,计算组内的平均值作为 Baseline。不需要额外的 Critic 模型参数

3. GRPO 的系统脉络图 (Architecture Flow)

为了让你建立最直观的印象,我绘制了 GRPO 的数据流转图。请仔细观察“Group Sampling”这一步。

User Prompt (Question)
       │
       ▼
+---------------------+
|    Actor Model      |  <-- 也就是我们要训练的 LLM
+---------------------+
       │
       │ (生成 N 个不同的回答)
       ▼
+---------------------------------------------------------------+
|                      Group Sampling (N=4 示例)                |
|                                                               |
|  Output 1: "The answer is A..."  ----> Reward Model --> r1    |
|  Output 2: "Calculated as B..."  ----> Reward Model --> r2    |
|  Output 3: "Let x = y, so A..."  ----> Reward Model --> r3    |
|  Output 4: "Based on logic C..." ----> Reward Model --> r4    |
+---------------------------------------------------------------+
       │
       ▼
+---------------------------------------------------------------+
|                  Advantage Calculation (核心)                 |
|                                                               |
| 1. 计算均值 (Mean): μ = (r1+r2+r3+r4) / 4                     |
| 2. 计算标准差 (Std): σ                                        |
| 3. 计算相对优势 (Advantage): A_i = (r_i - μ) / σ              |
+---------------------------------------------------------------+
       │
       ▼
+---------------------+
|  Policy Optimization|  <-- 使用 PPO 的 Clip Loss 公式
|      (Update)       |      但在 Advantage 项上使用了上述 A_i
+---------------------+

本讲核心 takeaway:

  1. 省资源: GRPO 去掉了 Critic 模型,极大地节省了显存和计算量,使得训练超大模型变得更加可行。

  2. 基线(Baseline)的转变: 从“神经网络预测的基线”(Value Function)转变为“当前Batch内的统计基线”(Group Mean)。

  3. 原理: 通过对同一个问题采样多条路径,用组内归一化(Group Normalization)的方式来确定哪个答案是好的,哪个是差的。

GRPO 深度剖析:PPO 昂贵且脆弱的 Critic

在经典的强化学习(RL)理论中,Actor-Critic 架构几乎被奉为圭臬。但在大语言模型(LLM)的场景下,这个架构显得愈发笨重且低效。我们要解决的问题是:为什么训练一个好的 Value Function (Critic) 如此困难且昂贵?

1. 回顾 PPO 的核心数学依赖

要理解痛点,先看公式。PPO 的核心在于最大化以下目标函数(简化版):

$L^{CLIP}(\theta) = \mathbb{E} \left[ \min(r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t) \right]$

在标准 PPO 中,我们通常使用 GAE (Generalized Advantage Estimation) 来计算它,而 GAE 高度依赖于 TD Error ($\delta_t$):

$\delta_t = r_t + \gamma V_\phi(s_{t+1}) - V_\phi(s_t)$

请注意这里出现的 $V_\phi(s)$。这就是 Critic 网络,它的任务是预测状态$s$ 的预期回报。

致命逻辑链条:

  1. Actor 的更新方向取决于 $\hat{A}_t$

  2. $\hat{A}_t$ 的准确性取决于 $V_\phi(s)$ 的预测精度。

  3. 如果 Critic $V_\phi$ 也是一个刚开始训练的神经网络,它的预测是充满了噪声和偏差的

2. 痛点一:计算与显存的“双倍惩罚” (The Computational Tax)

在传统的 RL(如玩 Atari 游戏或机器人控制)中,Actor 和 Critic 通常只是几层 MLP 或小型 CNN,参数量很少。

但在 LLM 中,情况发生了质变:

  • 状态空间(State Space)极度复杂: 输入是 token 序列,语义极其丰富。

  • 理解“价值”需要“智能”: 要判断一句话“好不好”,Critic 网络必须具备和 Actor 相当的理解能力。

这意味着,如果你的 Actor 是一个 7B 的 Llama-3,你的 Critic 通常也必须是一个 7B 的 Transformer(或者至少共享大部分主干参数)。

显存账单(Training a 7B model):

  • Actor (7B): 权重 + 梯度 + 优化器状态 很大。

  • Critic (7B): 权重 + 梯度 + 优化器状态  同样大

这就是为什么在 RLHF 阶段,显存占用往往是 SFT 阶段的 2-4 倍。Critic 不仅占据了显存,还占据了大量的 FLOPs(前向传播和反向传播),使得训练吞吐量(Tokens/sec)直接减半。

3. 痛点二:价值函数的“训练困境” (The Optimization Difficulty)

这才是更深层的学术痛点。在 LLM 生成任务中,训练 Critic 往往比训练 Actor 更难。

3.1 奖励的稀疏性与主观性

在围棋中,输赢是客观的。但在对话中,Reward Model 给出的分数(比如 0.85 或 0.92)往往带有很强的主观噪声。Critic 试图去拟合这个充满噪声的 Reward Model,极易过拟合或欠拟合。

3.2 价值评估的难度

比如以下场景:

Prompt: "请证明黎曼猜想。"

Token 1: "黎"

Token 2: "曼"

Token 3: "猜"

...

Critic 需要在看到 "黎曼猜" 这几个字的时候,就预测出这句话最终能得多少分(Value)。这几乎是不可能的任务,因为后续生成的质量完全未定。

结果: 在 LLM 训练初期,Critic 的 Loss 通常非常高,且收敛极慢。

后果: 如果 Critic 预测不准(V(s) 瞎猜),那么计算出的优势 $\hat{A}_t$ 就是错误的。Actor 会根据错误的信号进行更新,导致模型性能震荡甚至崩塌(Collapse)。

4. GRPO 的降维打击:用“统计”替代“预测”

既然训练一个神经网络(Critic)去预测 Baseline(V(s))既贵又难,GRPO 选择了一个统计学的方法。

我们不再问 Critic:“这一单大概能得多少分?”

而是直接做实验:“既然我不知道基线,那我就多跑几次,取平均值当基线。”

让我们对比一下 Baseline 的来源:

特性 PPO (Standard) GRPO
Baseline 来源 参数化模型 $V_\phi(s)$ (Critic) 蒙特卡洛采样的组均值 (Group Mean)
计算成本 高 (需前向/反向传播整个 Critic 网络) 低 (仅需对 Reward 标量求平均)
准确性依赖 依赖 Critic 训练得好不好 依赖 Group Size (N) 是否足够大
无偏性 Critic 可能有 Bias 组内均值是当前策略的无偏估计
【PPO 的视角:预言家模式】
       
       学生(Actor): "我写完了,看我这篇作文!"
            │
            ▼
       预言家(Critic): (甚至还没看别人的) 
       "基于我对你过往表现和题目难度的深奥计算,
        我觉得这种题目的平均分应该是 75 分。"
            │
            ▼
       老师(Reward): "这篇实际得分 80 分。"
            │
            ▼
       结果: 优势 = 80 - 75 = +5 (你比预言的要好)

----------------------------------------------------

       【GRPO 的视角:赛马模式】
       
       学生(Actor): 分身成 4 个人,写了 4 篇作文。
            │
            ▼
       作文A (80分) | 作文B (60分) | 作文C (85分) | 作文D (75分)
            │
            ▼
       统计员(Math): "大家停一下,算个平均分。"
       平均分 = (80+60+85+75)/4 = 75 分。
            │
            ▼
       结果(A): 优势 = 80 - 75 = +5 (你比这一组的平均水平好)

本质总结: GRPO 利用大数定律(虽然 N 通常只有 8-64,但在统计上已足够有效)来实时的、动态的构建 Baseline,从而彻底删除了那个笨拙的 Critic 网络。

本讲核心 Takeaway:

  1. PPO 的 Critic 在 LLM 时代不仅仅是增加了显存,更重要的是它引入了优化难题

  2. Value Function 在长文本生成任务中极难收敛,不准确的 Value 会误导 Actor。

  3. GRPO 通过Group Relative(组相对)的方式,用“采样均值”替代了“参数预测”,在数学上依然构成了有效的 Advantage 估计,同时将计算成本砍半。

GRPO 深度剖析:数学形式化—目标函数的重构

在强化学习中,所有都蕴含在那个我们需要最大化的$J(\theta)$ 之中。GRPO 的美妙之处在于,它在 PPO 的基础上做了一次手术,切除了 $V(s)$,植入了一个基于组(Group)的归一化算子。

1. 符号定义 (Notation Setup)

为了保证严谨性,我们先定义这一讲的数学符合:

  • $\pi_\theta$: 我们要训练的策略模型(Actor),即当前的 LLM。

  • $\pi_{ref}$: 参考模型(Reference Model),通常是 SFT 后的初始模型,用于防止模型跑偏。

  • $q$: 用户的问题(Prompt),从数据集 P(Q) 中采样。

  • $\{o_1, o_2, ..., o_G\}$ 一组输出(Outputs)。这里 G是组的大小(Group Size),比如 8 或 16。

  • $r_i$: 第 $i$个输出对应的奖励值(Reward),由 Reward Model 给出。

2. 核心变革:组相对优势

在 PPO 中,优势 $\hat{A}_t$依赖于 Critic。而在 GRPO 中,优势 $\hat{A}_i$则依赖于同行衬托

对于同一个问题 $q$,模型生成了一组输出 $\{o_1, ..., o_G\}$,对应的奖励为 $\{r_1, ..., r_G\}$

$i$个输出的优势函数定义为:

$\hat{A}_i = \frac{r_i - \text{mean}(\{r_1, ..., r_G\})}{\text{std}(\{r_1, ..., r_G\}) + \epsilon}$

深度解读:

  • Mean ($\mu$): 这一组输出的平均水平。如果 $r_i > \mu$,说明这个回答比你自己生成的平均水平好,应当鼓励($\hat{A}_i > 0$)。

  • Std ($\sigma$): 标准差用于缩放。这非常关键!

    • 如果某次生成大家的差别很大(比如 [10, 90, 50]),标准差大,优势会被缩小,梯度的更新幅度会变稳。

    • 如果差别很小(比如 [80, 81, 79]),说明模型对这个问题的表现很一致,标准差小,微小的分数差异会被放大,模型能学到细微的区别。

  • $\epsilon$: 一个极小的数(如 $1e-8$),防止分母为 0。

[原始奖励空间 Reward Space]
       
       Group A (简单题): [0.9, 0.92, 0.88, 0.95]  <-- 分数都很高
       Group B (困难题): [0.1, 0.05, 0.20, 0.15]  <-- 分数都很低

       如果不做归一化,模型会拼命学习 Group A (Reward大,梯度大),
       而忽略 Group B (Reward小,梯度小)。这显然不合理。

       -------------------------------------------
       
       [GRPO 归一化空间 Normalized Advantage]
       
       Group A: [-0.4, +0.3, -1.1, +1.2] 
       Group B: [-0.4, -1.2, +1.2, +0.4]

       ↑↑↑
       你看!无论是简单题还是困难题,
       都被拉到了同一个“相对起跑线” (均值为0,方差为1)。
       模型现在只关注:在这个场景下,哪个答案相对更好?

3. 终极公式:GRPO 目标函数

有了上面的 $\hat{A}_i$,我们可以写出 GRPO 需要最大化的完整目标函数:

$J_{GRPO}(\theta) = \mathbb{E}_{q \sim P(Q), \{o_i\}_{i=1}^G \sim \pi_{\theta_{old}}(O|q)} \left[ \frac{1}{G} \sum_{i=1}^G \left( \mathcal{L}^{CLIP}_i - \beta \mathbb{D}_{KL}(\pi_\theta || \pi_{ref}) \right) \right]$

3.1 期望与采样 (Expectation & Sampling)

$\mathbb{E}_{q \sim P(Q), \{o_i\}_{i=1}^G \sim \pi_{\theta_{old}}(O|q)}$

这告诉我们训练的循环逻辑:

  1. 采样一个 Prompt $q$

  2. 用旧策略 $\pi_{old}$ 采样 $G$ 个输出 $\{o_i\}$

  3. 接下来的计算都是基于这$G$ 个样本的平均值。

3.2 代理损失 (Surrogate Loss with Clipping)

这是直接继承自 PPO 的部分,但在 $A_i$ 上用了 GRPO 的定义:

$\mathcal{L}^{CLIP}_i = \min \left( \frac{\pi_\theta(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} \hat{A}_i, \text{clip}\left(\frac{\pi_\theta(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1-\epsilon, 1+\epsilon\right) \hat{A}_i \right)$

  • 比率 (Ratio) $\frac{\pi_\theta}{\pi_{\theta_{old}}}$:衡量当前策略和采样时的策略差了多少。

  • Clip: 限制更新幅度,防止一次更新步子迈太大把模型扯坏了。这是 PPO 家族稳定性的基石。

3.3 KL 散度惩罚 (KL Divergence Penalty)

$- \beta \mathbb{D}_{KL}(\pi_\theta || \pi_{ref})$

为什么要减去它?

  • 这是一个正则化项 (Regularization)

  • $\pi_{ref}$ 是我们的 SFT 模型(它是懂人话的)。

  • $\pi_\theta$ 是我们在训练的模型。

  • 如果 $\pi_\theta$ 为了拿高分(Reward Hacking),开始输出乱码或者极端的欺骗性文本,它和 $\pi_{ref}$ 的分布差异会变大,KL 散度飙升。

  • 因为公式前面是负号,最大化目标函数 $J$ 就意味着要最小化 KL 散度。

4. Token 级 vs 样本级 KL

在实际实现中(如 DeepSeekMath 的论文),KL 散度通常使用如下近似计算方式:

$\mathbb{D}_{KL} \approx \frac{\pi_{ref}(o_i|q)}{\pi_{\theta}(o_i|q)} - \log \frac{\pi_{ref}(o_i|q)}{\pi_{\theta}(o_i|q)} - 1$

(注:具体实现有多种近似公式,Schulman 的近似最为常见)

关键点在于:GRPO 通常将 KL 散度作为一个 Loss 项直接加在目标函数里,而不是像某些 PPO 实现那样作为 Reward 的一部分(PPO-reward-shaping)。 这使得训练更加直观,我们明确地知道模型在优化什么:“相对优势”减去“偏离代价”

本讲核心 Takeaway:

  1. 公式的核心: $J_{GRPO}$ 是 PPO Clip Loss 的变体,唯一的区别在于 Advantage 的来源。

  2. 归一化的魔力:$\frac{r - \mu}{\sigma}$ 使得模型在面对不同难度的 Prompt 时,梯度尺度保持稳定,这是 GRPO 训练稳定的关键。

  3. 正则化: KL 惩罚项是防止模型“走火入魔”的必选组件。

现在,公式已经摆在桌上了。但在实际把代码跑起来之前,我们面临一个极其具体且棘手的超参数选择问题:$G$(Group Size) 到底取多少?

取 4?取 16?还是 64?

如果 $G$太小,统计均值不准确怎么办?如果 $G$ 太大,显存爆了怎么办?

在下一讲,我们将深入探讨 Group 的奥秘,从统计学角度分析 $G$ 的大小如何决定了 GRPO 的成败,以及如何根据你的显存大小来权衡这个参数。

GRPO 深度剖析:组(Group)——统计方差与采样策略

1. 为什么 $G$ 不能太小?——基线估计的方差问题

我们知道,GRPO 用组内均值 $\mu$ 来近似真实的 State Value $V(s)$。从蒙特卡洛(Monte Carlo)的角度看,这是一个无偏估计,但方差(Variance)可能极大。

1.1 统计学直觉

假设真实的价值 $V(s) = 75$

如果 $G=2$(极端的例子),我们只采样两个样本:

  • Case 1: 运气好,采到 [74, 76]。均值 75,估计完美。

  • Case 2: 运气差,采到 [50, 60](模型发挥失常)。均值 55。

    • 在这个 Case 2 中,60 分的样本会被认为具有“巨大优势”(+5分 relative to 55),而在真实基准(75分)下,它其实是很差的。

结论:$G$太小时,Baseline ($\mu$) 的波动极其剧烈。这会导致梯度的方差变大,训练过程像是在“走醉鬼步”(Random Walk),难以收敛。

1.2 文献中的经验值

DeepSeek-Math 和 DeepSeek-R1 的论文中,通常倾向于较大的 $G$

  • DeepSeek-Math: 推荐 $G \in [16, 64]$

  • 经验法则: 对于复杂的逻辑推理任务(如数学题),解空间的方差本身就大(对就是1,错就是0),需要更大的 $G$ 来稳定均值。

2. 为什么 $G$ 不能无限大?——边际递减与显存墙

你可能会问:“既然 G 越大估计越准,为什么不设成 1024?”

这里有两个制约因素:

  1. 显存墙(The VRAM Wall):

    GRPO 需要在一次 Forward Pass 中生成 G 个完整的长序列。

    • 如果你训练 70B 模型,Context Length 4096。

    • G=64 意味着你需要同时显存驻留 64 条 4096 长度的 KV Cache 和中间激活值。这对显存是毁灭性的打击。

  2. 边际效益递减(Diminishing Returns):

    根据中心极限定理,标准误(Standard Error)随 $\frac{1}{\sqrt{G}}$下降。

    • $G$从 4 增加到 16,误差减半,收益巨大。

    • $G$ 从 64 增加到 256,误差只减半,但计算成本翻了 4 倍。

3. 致命陷阱:模式崩塌(Mode Collapse)与标准差

在 GRPO 公式中,分母是 $\sigma$(标准差)。这引入了一个在 PPO 中不存在的风险。

$\hat{A}_i = \frac{r_i - \mu}{\sigma + \epsilon}$

如果 $\sigma \approx 0$ 会发生什么?

这意味着模型生成的 $G$个回答完全一样,或者得分完全一样。

  • 此时,分子$r_i - \mu$ 也是 0。

  • 结果:$\hat{A}_i = 0$

  • 梯度消失: 模型学不到任何东西。对于这个 Batch,训练是无效的。

3.1 采样策略的重要性 (Temperature is Key)

在 SFT 中,我们有时会用 Greedy Search (Temp=0) 来追求最稳妥的回答。

但在 GRPO 中,Temperature 绝对不能为 0。

你需要强制引入多样性(Diversity)

  • Temperature: 通常设为 0.6 - 1.0。

  • Top-P / Top-K: 适当放宽。

我们必须迫使模型在一个Prompt下探索不同的路径。只有产生了差异(Contrast),GRPO 才能通过比较(Comparison)来学习。

【无效的组 (Low Variance)】 -> 训练停滞
Temperature = 0.1
Prompt: 1+1=?
Output 1: 2 (Reward: 1.0)
Output 2: 2 (Reward: 1.0)
Output 3: 2 (Reward: 1.0)
Output 4: 2 (Reward: 1.0)
Stats: μ=1.0, σ=0.0
Advantage: [0, 0, 0, 0]  <-- 没有任何信号告诉模型“保持这样”或“改变”

---------------------------------------------

【有效的组 (High Variance)】 -> 高效学习
Temperature = 1.0
Prompt: 1+1=?
Output 1: 2       (Reward: 1.0) -> Adv: +0.8
Output 2: 3       (Reward: 0.0) -> Adv: -1.2
Output 3: 11      (Reward: 0.0) -> Adv: -1.2
Output 4: Two     (Reward: 1.0) -> Adv: +0.8
Stats: μ=0.5, σ=0.5
Advantage: 产生了强烈的正负反馈信号!

4. 进阶技巧:Iterative GRPO (在线生成 vs 离线生成)

处理 $G$ 带来的显存压力,学术界和工业界有两种做法:

4.1 Online Generation (标准 GRPO)

在训练步中实时生成 $G$个样本。

  • 优点: 数据是完全 On-Policy 的,完全符合数学推导。

  • 缺点: 慢,显存占用大。

4.2 Offline / Hybrid Generation (变体)

先用当前模型跑 inference,把数据存到 Buffer 里(比如存 64 个),然后训练时从 Buffer 里拿数据计算 Loss。

  • 风险: 变成了 Off-Policy。如果模型更新了,Buffer 里的数据就“过时”了(Stale)。

  • 修正: 需要引入 Importance Sampling (IS) 或者限制 Buffer 的刷新频率。DeepSeek 的实现通常倾向于高效的 Online 生成,或者极短周期的 Buffer。

本讲核心 Takeaway:

  1. Group Size ($G$) 是权衡“估计方差”与“显存成本”的杠杆。一般推荐 $G \in [16, 64]$

  2. 多样性是生命线: GRPO 依赖组内差异来提取信号。必须使用非零的 Temperature 采样。如果模型坍缩到单一输出,GRPO 将失效。

  3. 标准差的意义: $\sigma$ 不仅仅是分母,它是一个“自适应的各种系数”。当大家都很烂时,微小的差异会被放大;当大家都很好时,差异会被缩小。

明白了 $G$的设定,我们其实只解决了一半的问题。公式里的$\hat{A}_i$ 算出来了,但不要忘了,我们并没有 Critic。这就引出了一个深刻的问题:没有 Value Function 的情况下,我们如何确保 Advantage 的计算真的是“优势”,而不是“噪声”? 尤其是当 Reward 本身很稀疏的时候(比如做数学题,全错就是0,很难拿到1)。

下一讲,我们将深入 优势函数(Advantage Function)的重构,探讨在 Reward 稀疏或密集场景下,GRPO 具体的数值表现和潜在的改进技巧(如 Outcome Reward vs Process Reward)。

在传统的 PPO 中,优势函数 $\hat{A}_t$ 是极其复杂的,它通常利用 GAE(Generalized Advantage Estimation)进行时间步(Token)级别的细粒度指派。而在 GRPO 中,由于移除了 Critic,我们的评价体系发生了根本性的范式转移:从“基于价值预测的绝对评价”转向了“基于群体竞争的相对评价”。

GRPO 深度剖析:优势函数(Advantage Function)的重构——零和博弈与稀疏奖励

1. 零和博弈:归一化的深层含义

让我们再次审视那个看起来人畜无害的公式:

$\hat{A}_i = \frac{r_i - \mu}{\sigma + \epsilon}$

如果你对这一组 $\hat{A}_i$ 求和,你会发现一个惊人的性质:

$\sum_{i=1}^G \hat{A}_i \approx 0$

(注:在 $\epsilon$极小的情况下,加权后的均值严格为 0,标准差严格为 1)

1.1 物理意义:组内“内卷”

这意味着 GRPO 构建了一个局部的零和博弈(Zero-Sum Game)环境。

  • 在一个 Batch 中,无论大家的回答质量整体有多高(比如都是 99 分),或者整体有多烂(比如都是 1 分),总有一半的回答会被“惩罚”(Adv < 0),另一半会被“奖励”(Adv > 0)。

  • 绝对分数的“消逝”: 模型不再关心“我是否考了 60 分及格”,模型只关心“我是否考得比隔壁的同学高”。

1.2 这种设计的利弊

  • 利(鲁棒性): Reward Model 有时候会发生 Drift(整体分数虚高或虚低),组内归一化直接消除了这种整体性的 Bias。

  • 弊(错误信号): 如果一组回答全是垃圾(Reward 都是 0.0),但有一个稍微沾点边(Reward 0.01),那个 0.01 的回答会被赋予巨大的正优势,导致模型过度强化这个“矮子里拔将军”的行为。(这也是为什么我们需要上一讲提到的 KL 散度约束,防止模型跑偏)

2. 信号粒度的问题:轨迹级 vs Token 级

这是一个学术界非常关注的细节。

  • PPO (Critc-based): 每一句话的每一个 Token,Critic 都会给出一个价值评估 $V(s_t)$。因此 PPO 可以精确地说:“虽然你最后答错了,但你中间推理的第三步是很棒的。”(Temporal Credit Assignment)。

  • GRPO (Outcome-based): 通常情况下,我们只有一个最终的 Reward $r_i$(比如答案对不对)。这意味着,我们将把这个 $r_i$产生的优势 $\hat{A}_i$广播(Broadcast) 给这条回答中的每一个 Token。

$\text{Token Loss}_t = -\log \pi(a_t|s_t) \cdot \hat{A}_{\text{trajectory}}$

这听起来很粗糙,对吧? 如果我写了 1000 字的推理,只有最后一步错了,导致 $r=0$,难道这 1000 字都要被惩罚吗?

DeepSeek 的解决方案:过程奖励与格式奖励

为了缓解这个“一刀切”的问题,GRPO 极其依赖 Reward Engineering。在 DeepSeek-R1 中,Reward $r_i$ 不再是一个单一的标量,而是多个部分的组合:

$r_{total} = \alpha \cdot r_{accuracy} + \beta \cdot r_{format}$

  1. 结果奖励 ($r_{accuracy}$): 最终答案对不对?(LeedCode 通过率,数学答案匹配)。

  2. 格式奖励 ($r_{format}$): 这就是“过程”的一种体现。比如强制模型必须输出 <think>...</think> 标签。如果模型写了标签,即便答案错,它也能拿到一部分格式分,避免全盘否定。

通过这种方式,GRPO 在没有 Critic 进行逐词评估的情况下,依然引导模型学习到了结构化的推理能力,还是挺逆天的。

【场景:做一道数学题,过程完美,但最后算错了】
Prompt: 2 * 3 + 4 = ?
Model: "2*3得6, 6+4得... 9!" (错误答案)

---------------------------------------------------

[PPO 的视角 - 精细化打击]
Token序列:  [2*3得6] -> [6+4得] -> [9!]
Critic V(s):   ↑高       ↑高      ↓骤降
Advantage:    (+0.5)    (+0.6)   (-2.0)
更新结果: 
"2*3得6" 被鼓励 (保留正确逻辑)
"9!" 被狠狠惩罚 (修正错误结果)

---------------------------------------------------

[GRPO 的视角 - 连坐制度]
Token序列:  [2*3得6] -> [6+4得] -> [9!]
Group Mean: 0.5 (假设别人做对了)
此样本 Reward: 0.0 (因为答案错了)
Advantage: -1.0 (全序列统一惩罚)
更新结果:
"2*3得6" 被惩罚 (无辜躺枪)
"6+4得" 被惩罚 (无辜躺枪)
"9!" 被惩罚 (罪有应得)

[思考]: 为什么 GRPO 依然能工作?
因为在海量的采样中 (Big Data + Large Steps),
"2*3得6" 这一步出现在"正确样本"中的概率远高于"错误样本"。
虽然单次更新有噪声,但期望值 (Expectation) 最终会收敛到正确方向。
这就是大数定律的暴力美学。

4 处理稀疏奖励 (The Sparse Reward Problem)

在数学或代码任务中,Reward 往往是二值的(0 或 1)。这在 GRPO 中会制造麻烦。

极端情况:$G$个样本全错(全是 0)或全对(全是 1)。

此时 $\sigma = 0$,公式退化。虽然有 $\epsilon$保护不报错,但 $\hat{A}_i$ 会变成 0。梯度消失,训练空转。

工程上的应对策略:

  1. 冷启动(Cold Start): 在使用 GRPO 纯 RL 之前,必须有一个足够强的 SFT 模型,保证它至少有 10%-20% 的概率能答对题。如果模型一开始什么都不会,GRPO 是推不动的。

  2. 混合奖励: 如前所述,加入 dense reward(如格式检查、长度惩罚、语言流畅度分),确保存活下来的样本之间即使答案都错了,分数也有高低之分(比如写了步骤的 0 分比啥都没写的 0 分要稍微好一点点)。

本讲核心 Takeaway:

  1. 相对评价: GRPO 构建了一个组内竞争机制,Advantage 均值为 0。这消除了绝对 Reward 的波动影响。

  2. 粗粒度信号: 相比 PPO 的 Token 级评价,GRPO 默认是 Trajectory 级评价。它依赖海量数据的统计规律来区分哪些 Token 是真正导致错误的。

  3. 对初始能力的依赖: 因为没有 Critic 指导“这一步走得好”,GRPO 要求模型自身必须具备一定的成功率,否则无法从全 0 的反馈中学习。

现在,我们有了目标函数,有了采样策略,也有了奖励计算方法。 但是,强化学习最怕的就是模型为了拿高分而“走火入魔”(Reward Hacking)。比如,为了让回答变长(如果Reward和长度正相关),模型开始疯狂重复废话。

虽然我们在公式里写了 KL 散度项,但在实际代码和训练中,如何精确计算和控制 KL 散度是一门极深的学问。

在 RLHF 的世界里,有一个著名的诅咒叫 Goodhart's Law(古德哈特定律):“当一个指标变成目标,它就不再是一个好的指标。”

如果我们完全放任 GRPO 去最大化我们定义的 Reward(比如数学题做对得 1 分),而没有任何约束,模型很快就会发现一些人类意想不到的“捷径”(Reward Hacking)。为了防止模型变成一个“为了得分不择手段的疯子”,我们需要一个锚点。这个锚点就是 KL 散度(Kullback-Leibler Divergence)

GRPO 深度剖析:KL 散度的处理

1. 为什么要加 KL?

想象我们在训练模型写诗。 Reward Model 倾向于给押韵的句子高分。

如果没有 KL 散度约束,GRPO 训练出来的模型可能会变成这样:

Prompt: 写一首关于春天的诗。 Model: "猫 猫 猫 猫 / 喵 喵 喵 喵" (押韵满分,语义崩坏)

这就是 Reward Hacking。模型利用了 Reward Model 的漏洞(比如对某些特定 Token 的偏好),牺牲了语言的可读性和逻辑性来换取高分。

$\pi_{ref}$ (Reference Model) 的作用就是那个拿着教鞭的老师。它通常是 SFT 阶段得到的模型。它的潜台词是: “你可以去尝试拿高分,但你的说话方式(概率分布)不能离我太远。你首先得像个正常人说话,其次才是答对题。”

2. KL 散度的数学计算:Token 级别的微操

在 LLM 中,KL 散度不是算整个句子的,而是算每一个 Token 的。

对于第 $t$ 个 Token,模型 $\pi_\theta$ 预测的概率分布是 $P$,参考模型 $\pi_{ref}$ 预测的概率分布是 $Q$。 理论上的 KL 公式是 $\sum P(x) \log \frac{P(x)}{Q(x)}$

但在 GRPO 的实际工程实现中(参考 DeepSeekMath 和常见的 PPO 实现),我们通常使用 Schulman 近似 或者简单的 Log-Ratio

最常用的近似公式:

对于生成的某个特定 token $x_t$

$D_{KL}(x_t) \approx \log \frac{\pi_\theta(x_t | s_t)}{\pi_{ref}(x_t | s_t)} = \log \pi_\theta(x_t) - \log \pi_{ref}(x_t)$

注意:

  • 如果 $\pi_\theta(x_t)$$\pi_{ref}(x_t)$ 高很多(模型过度自信地输出了这个词),差值为正,KL Loss 变大,惩罚模型。

  • 如果 $\pi_\theta(x_t)$ 变小了(模型不敢说 SFT 原本会说的词),差值为负,但这通常由期望项平衡。

汇总到 Loss 中

在 GRPO 的目标函数中,我们将这个 KL 值作为一个惩罚项(Penalty)减去:

$J = \dots - \beta \sum_{t=1}^T \left( \log \pi_\theta(x_t|s_t) - \log \pi_{ref}(x_t|s_t) \right)$

这里 T 是生成的序列长度。这意味着:每一个偏离 Reference 的 Token 都要付出代价。

3. 两种流派:KL in Reward vs KL in Loss

在代码实现层面,你会看到两种处理 KL 的方式,这点非常容易混淆,你需要特别注意:

流派 A:PPO 经典做法 (Reward Shaping)

直接把 KL 惩罚扣在 Reward 里。

$R_{final} = R_{outcome} - \beta \cdot D_{KL}$

  • 逻辑: 这里的 Reward 变成了“综合得分”。

  • 在 GRPO 中的问题: 如果我们把 KL 加到 $R$ 里,再进行 Group Normalization ($\frac{R-\mu}{\sigma}$),KL 的物理意义会被方差缩放扭曲。这通常不是 GRPO 的最佳实践。

流派 B:DeepSeek-Math 做法 (Direct Loss Term)

保持 Reward 纯净,把 KL 放在目标函数里作为正则项。

$Loss = Loss_{surrogate}(\hat{A}) + \beta \cdot D_{KL}$

  • 逻辑:$\hat{A}$ 负责指引方向(哪个答案好),$D_{KL}$负责拉住缰绳(别走太远)。

  • 优势: 这种分离让优化更可控。$\beta$ 可以独立调节,不受 Reward 数值范围的影响。

我们可以把 $\pi_{ref}$ 想象成地球,$\pi_\theta$ 是风筝。

[ 崩坏区 / Collapse Zone ]
         (模型输出乱码,但可能偶然骗过 Reward Model)
                    WARNING: KL >>> 1.0
                          /
                         /  <-- 巨大的 KL 惩罚 (强引力)
                        /
          +-----------------------+
          |      训练中的模型       |  ---> 试图往高 Reward 方向飞
          |      Policy (Actor)   |
          +-----------------------+
                        \
                         \  <-- Beta (β) 决定了引力绳的粗细
                          \
                   [ 安全区 / Trust Region ]
            (语言通顺,符合人类语法,由 SFT 模型定义)
                    Reference Model
  • 如果 $\beta$ 太大 (Beta=1.0): 引力太强,风筝飞不起来。模型会死死守着 SFT 的参数,不敢做任何探索。训练 Loss 下降,但 Reward 不升。

  • 如果$\beta$ 太小 (Beta=0.0): 绳子断了。风筝飞入太空(崩坏区)。模型为了优化 Reward 开始胡言乱语。

  • 黄金甜点 (Beta $\approx$ 0.01 - 0.05): 既允许模型探索新的解题路径,又强迫它用“人话”把答案写出来。

动态 Beta (Adaptive KL) —— 进阶技巧

在长时间的训练(如几万步)中,固定的 $\beta$往往不好用。

  • 训练初期,模型很容易偏离,需要强 KL 约束。

  • 训练后期,模型已经学乖了,可以适当放松 $\beta$ 让它冲击更高分。

或者反过来(PPO 中常用):设定一个目标 KL 值(比如 $d_{targ} = 0.01$)。

  • 如果当前 KL > $d_{targ}$:说明跑太远了,调大 $\beta$

  • 如果当前 KL < $d_{targ}$:说明太保守了,调小 $\beta$

虽然 GRPO 论文中较少强调这一点,但在工程实践中,监控 KL 曲线是判断训练是否健康的唯一标准。

  • 健康的曲线: KL 缓慢上升,然后稳定在一个小数值(如 0.01-0.05)。

  • 暴雷的曲线: KL 指数级爆炸,或者突然变成 0。

本讲核心 Takeaway:

  1. 安全锚点: KL 散度约束是 RLHF 区别于普通 Fine-tuning 的关键,它保证了生成的语言质量。

  2. 公式选择: 在 GRPO 中,建议将 KL 作为 Loss 的独立正则项,而不是混入 Reward,以避免被 Normalization 干扰。

  3. 计算粒度: KL 是 Token-level 的。每一个“标新立异”的词都会累积罚分。

理论闭环已经完成。现在,我们需要把这些积木拼成一个完整的、会动的机器。 下一讲,我们将进入系统级的整合,我们将像拆解引擎一样,一步步拆解 GRPO 的完整训练循环 (Training Loop)。我们将看到数据是如何在 GPU 之间流转,以及梯度是如何反向传播的。

GRPO 深度剖析·:训练循环详解

GRPO 的训练过程可以被切割为两个截然不同的阶段:

  1. Rollout Phase (采样阶段):只做推理,不传梯度,极度消耗显存带宽。

  2. Training Phase (学习阶段):反向传播,更新权重,极度消耗算力。

1. Step 1: 采样 (Rollout / Generation) —— 最昂贵的开销

一切始于一批 Prompts。假设我们的 Batch Size (of Prompts) = $B$,Group Size = $G$

我们需要生成 $B \times G$ 条完整的回答。

  • 输入: $B$ 个问题(例如:["1+1=?", "解释量子力学", ...])。

  • 动作: 模型 $\pi_{\theta_{old}}$(当前的 Actor)进入 eval() 模式。

  • 操作:

    1. 复制每个 Prompt $G$ 次。

    2. 并行生成$B \times G$个回答。

    3. 关键数据留存: 在生成的过程中,我们要顺手把每个 Token 的Log Probability (LogProbs) 存下来!

      • 为什么要存?因为计算 Loss 里的比率 $\frac{\pi_\theta}{\pi_{old}}$ 时,分母就是这个时候算出来的。如果不存,训练时就要多做一次前向传播,太亏了。

注意: 这是 GRPO 训练中最慢的一步。对于 70B 模型,生成长文本(COT)可能占据整个训练时间的 70%-80%。所以这里通常会用到 vLLM 等加速推理框架。

2. Step 2: 评分与归一化 (Evaluation & Advantage)

生成结束后,GPU 显存里躺着 $B \times G$ 条轨迹(Trajectories)。现在的任务是给它们打标签。

  • 动作:

    1. 打分: 调用 Reward Function(可能是代码解释器,也可能是另一个 Reward Model),得到 $B \times G$个标量奖励 $\{r_{ij}\}$

    2. 分组归一化(核心):

      • 将数据切分为 $B$组,每组 $G$个轨迹。

      • 对每组计算均值 $\mu_j$和标准差 $\sigma_j$

      • 计算优势 $\hat{A}_{ij} = \frac{r_{ij} - \mu_j}{\sigma_j + \epsilon}$

    3. 释放显存: 此时,原始的 KV Cache 可以扔掉了(除非你用 PPO 的特殊实现),我们只需要 Input IDs, Masks, Old LogProbs 和 Advantages。

3. Step 3: 学习 (Optimization) —— 内层循环

数据准备好了,现在进入真正的“训练”环节。这通常是一个内层循环(Inner Epochs)。

也就是说,我们辛辛苦苦生成的这批数据,不会只用一次,通常会反复用它更新模型 1-4 次(Update Epochs)。

对于每一次更新迭代:

  1. Actor Forward (新策略前向传播):

    • 把刚才那 $B \times G$条数据喂给当前正在更新的 Actor 模型。

    • 计算 New LogProbs

    • 注意: 这一步需要梯度 requires_grad=True

  2. Reference Forward (参考模型前向传播):

    • 把同样的数据喂给 Reference Model(冻结参数)。

    • 计算 Ref LogProbs

    • 用途: 用于计算 KL 散度。

    • 优化技巧: 如果显存不够,这一步可以预先算好存起来,牺牲磁盘/内存换显存。

  3. Loss Calculation (损失计算):

    • Ratio: $\exp(\text{New LogProbs} - \text{Old LogProbs})$

    • Surrogate Loss: 用 Clip 公式结合 Advantage 计算策略梯度。

    • KL Loss: $\beta \times (\text{New LogProbs} - \text{Ref LogProbs})$

    • Total Loss: Surrogate Loss + KL Loss。

  4. Backward & Step (反向传播与更新):

    • loss.backward()

    • optimizer.step()

实例

Prompt (输入): “2 + 2 = ?”

Group Size ($G$): 4 (我们让模型生成 4 个回答)

模型生成了以下 4 条数据(为了方便理解,我把 Token 拆开了):

  • 回答 A: ["是", "4", "<EOS>"] (正确,简洁)

  • 回答 B: ["等", "于", "5", "<EOS>"] (错误)

  • 回答 C: ["答", "案", "是", "4", "<EOS>"] (正确,啰嗦)

  • 回答 D: ["不", "知", "道", "<EOS>"] (错误,拒绝回答)

第一步:打分 (Scoring) —— 此时与 Token 长短无关

GRPO 并不是看一个字打一次分,而是看完整个句子打一次分。

假设 Reward Model 只看结果对不对(对=1.0,错=0.0):

  • 回答 A 得分 ($r_A$): 1.0

  • 回答 B 得分 ($r_B$): 0.0

  • 回答 C 得分 ($r_C$): 1.0

  • 回答 D 得分 ($r_D$): 0.0

注意:此时我们手里的 Reward 只是 4 个简单的数字:[1.0, 0.0, 1.0, 0.0]完全不涉及 Token 的切分。

第二步:计算均值与标准差 (Group Statistics)

现在我们在组内(Group)计算统计量:

  1. 均值 ($\mu$): $(1.0 + 0.0 + 1.0 + 0.0) / 4 = \mathbf{0.5}$

  2. 标准差 ($\sigma$): 计算可得 $\mathbf{0.5}$

  3. 计算优势 ($\hat{A}_i$): 公式 $\frac{r_i - \mu}{\sigma}$

    • 回答 A (Adv): $(1.0 - 0.5) / 0.5 = \mathbf{+1.0}$

    • 回答 B (Adv): $(0.0 - 0.5) / 0.5 = \mathbf{-1.0}$

    • 回答 C (Adv): $(1.0 - 0.5) / 0.5 = \mathbf{+1.0}$

    • 回答 D (Adv): $(0.0 - 0.5) / 0.5 = \mathbf{-1.0}$

关键点: 到目前为止,我们只得到了 4 个标量值

第三步:广播 (Broadcasting) 

现在的难题是:回答 A 有 3 个 Token,回答 C 有 5 个 Token,长度不一样,怎么把这一个标量 $+1.0$ 用到计算里?

这就叫广播。我们将这个优势值,像“淋雨”一样,均匀地淋在每一个 Token 头上。

看看【回答 C】的内部视角:

Token 序列:["答", "案", "是", "4", "<EOS>"]

优势值 $\hat{A}_C$:+1.0

在计算 Loss 时,我们会展开成这样:

时间步 (t) Token (xt​) 模型预测概率 (π) 这一步的优势 (A^t​) 解释
t=1 0.2 +1.0 既然结果对了,鼓励你说“答”
t=2 0.9 +1.0 既然结果对了,鼓励你说“案”
t=3 0.8 +1.0 既然结果对了,鼓励你说“是”
t=4 4 0.9 +1.0 既然结果对了,鼓励你说“4”
t=5 EOS 0.99 +1.0 既然结果对了,鼓励你结束

哪怕 "答" 和 "案" 这两个字本身和数学没关系,它们也会被同样地奖励。

再看看【回答 B】的内部视角:

Token 序列:["等", "于", "5", "<EOS>"]

优势值 $\hat{A}_B$:-1.0

时间步 (t) Token (xt​) 模型预测概率 (π) 这一步的优势 (A^t​) 解释
t=1 0.6 -1.0 既然结果错了,惩罚你说“等”
t=2 0.8 -1.0 既然结果错了,惩罚你说“于”
t=3 5 0.3 -1.0 既然结果错了,惩罚你说“5”
t=4 EOS 0.9 -1.0 既然结果错了,惩罚你结束

第四步:Masking (对齐与遮盖) —— 工程实现的最后一步

在 GPU 里,因为 tensor 必须是方方正正的,所以短的句子后面会补 0 (Padding)。同时,我们不能计算 Prompt 的 Loss

假设 Prompt 长度为 5。 回答 A 长度为 3。 回答 C 长度为 5。 最大长度为 5。

优势矩阵 (Advantage Tensor) 的形状是 [Batch, Group, Max_Seq_Len]

Prompt 部分 (Mask掉,不算分)     |     Response 部分 (参与计算)
------------------------------------------------------------
[ 0, 0, 0, 0, 0,               +1.0, +1.0, +1.0,  0,    0   ]  <- 回答 A (补了2个0)
[ 0, 0, 0, 0, 0,               -1.0, -1.0, -1.0, -1.0,  0   ]  <- 回答 B (补了1个0)
[ 0, 0, 0, 0, 0,               +1.0, +1.0, +1.0, +1.0, +1.0 ]  <- 回答 C (填满)
[ 0, 0, 0, 0, 0,               -1.0, -1.0, -1.0, -1.0,  0   ]  <- 回答 D (补了1个0)

[ Prompts (Batch=B) ]
            │
            ▼
+-----------------------------+
|    Generation Phase         | <--- 🕰️ 耗时最长
| (No Grad, Temp > 0)         |
| Outputs: B * G trajectories |
| Save: Old LogProbs          |
+-----------------------------+
            │
            ▼
+-----------------------------+
|    Scoring Phase            |
| 1. Calculate Rewards (r)    |
| 2. Group Normalization      | <--- GRPO 魔法发生地
| 3. Compute Advantage (A)    |      A = (r - μ) / σ
+-----------------------------+
            │
            ▼
    [ Training Buffer ]
 (包含: InputIDs, Old_LogProbs, Advantages)
            │
            ▼
+-----------------------------+  <--循环 N 次 (Inner Epochs)
|    Optimization Phase       |
|                             |
|  Current Policy (Actor) --> New LogProbs (with Grad)
|  Reference Model        --> Ref LogProbs (No Grad)
|                             |
|  Loss = PPO_Clip(A) + β*KL  |
|          Backprop           |
+-----------------------------+
            │
            ▼
    [ Updated Actor Model ] --> 准备下一轮采样

 工程细节:显存优化的关键点

GRPO 虽然去掉了 Critic,但 $G$倍的数据量依然庞大。

  1. Gradient Checkpointing (梯度检查点): 必须开。用 30% 的额外计算时间换取 4-5 倍的显存空间。否则 $B \times G$ 的长序列直接 OOM。

  2. Micro-Batching (微批次):

    • 虽然我们采样了 $B \times G$ 个样本(比如 $4 \times 16 = 64$),在 Optimization 阶段,我们不需要一次性把 64 个样本全塞进 GPU。

    • 我们可以把这 64 个样本拆成 Chunk(例如每次 2 个),累积梯度(Gradient Accumulation),最后更新一次。

    • 注意: Group Normalization 必须在切分 Micro-Batch 之前 完成!优势的计算是基于全组的,不能切碎了算。

本讲核心 Takeaway:

  1. 两段式结构: GRPO 严格区分“造数据(Rollout)”和“吃数据(Train)”。

  2. 数据复用: 为了摊薄昂贵的采样成本,通常会对同一批数据进行多次梯度更新(Inner Epochs)。

  3. 计算瓶颈: 与 SFT 不同,SFT 的瓶颈在反向传播,GRPO 的瓶颈通常在生成阶段(因为是自回归的逐词生成)。

现在,我们已经彻底搞懂了 GRPO 内部是如何运转的。 但是,没有对比就没有伤害。学术界现在百家争鸣,DPO (Direct Preference Optimization) 也是红极一时,号称“不需要强化学习的强化学习”。

GRPO 深度剖析:横向对比——GRPO vs DPO vs PPO,谁才是终局?

1. 三国鼎立:流派概览

首先,让我们用最简练的语言定义这三位选手:

1.PPO (Proximal Policy Optimization):

  • 哲学: “我要请个老师(Critic)时刻盯着你,你每走一步(Token),我就告诉你这步走得好不好。”

  • 状态: 极其强大,但重得像坦克,显存杀手,调参火葬场。

2.DPO (Direct Preference Optimization):

  • 哲学: “我不需要老师。我只要看两份作业,一份是满分(Winner),一份是零分(Loser)。我只学满分的,远离零分的。”

  • 状态: 轻量级,本质上是对比学习(Contrastive Learning),不是强化学习。训练极快,但容易过拟合。

3.GRPO (Group Relative Policy Optimization):

  • 哲学: “我不需要老师,但我让你们全班(Group)考试。谁考得比平均分高,谁就是好学生。”

  • 状态: 结合了 PPO 的在线探索能力和 DPO 的轻量化优势。

2. DPO 的死穴:为什么推理模型(O1/R1)不爱用 DPO?

DPO 在聊天、写小说、排比句等主观偏好(Alignment)任务上是非常牛。但在数学、代码等客观推理(Reasoning)任务上,它有致命缺陷。

2.1 缺陷一:缺乏探索 (No Exploration)

  • DPO (Off-policy): 数据集是静态的,通常由 GPT-4 蒸馏生成好和坏回答。

  • 训练时: 模型看着 GPT-4 的答案说:“哦,我要模仿这个”。

  • 问题: 如果 GPT-4 的解法不是最优的呢?或者模型自己能想出一种更“刁钻”的解法呢?DPO 不允许模型尝试新路径。它只是在做Behavior Cloning (行为克隆) 的变体。

2.2 缺陷二:分布偏移 (Distribution Shift)

DPO 的数据往往是离线的。

  • 如果你的模型(Student)很弱,而数据(Teacher)太强,模型根本理解不了 Teacher 为什么这么做,只能死记硬背。

  • GRPO (On-policy): 数据是模型自己刚刚生成的。

    • “这是我自己写出来的答案,虽然烂,但我知道我是怎么想的。”

    • 基于自己的能力边界进行改进,这种**自举(Bootstrapping)对于逻辑能力的提升至关重要。

【DPO 的路径:照猫画虎】
       
       标准答案(y_w):  A -> B -> C -> D (完美路径)
       模型能力:       只能走到 B,后面看不懂。
       结果:           强行背诵 C -> D。遇到新题直接懵逼。

       -------------------------------------------

       【GRPO 的路径:自我进化】
       
       尝试1: A -> X -> Y (错) -> Reward: 0
       尝试2: A -> B -> E (错) -> Reward: 0
       尝试3: A -> B -> C (对!) -> Reward: 1  <-- "Aha! 我找到路了!"
       
       结果: 模型通过无数次试错,真的"理解"了从 B 到 C 的逻辑。

3. PPO vs GRPO:同门师兄弟的权衡

既然确定了要用 RL(强化学习),为什么不坚持用 PPO?

3.1 显存与计算效率

  • PPO: 需要 Critic 模型。对于 70B 模型,这基本上意味着显存翻倍。

  • GRPO: 只要 Actor。通过多次采样(Group Sampling)来代替 Critic。

    • 代价: GRPO 的采样阶段(Inference)很慢。

    • 收益: 显存占用低,能训练更大的模型。

    • 结论: 在推理算力(vLLM)越来越便宜,而训练显存(H100)依然昂贵的今天,GRPO 是更经济的选择。

3.2 信号的细腻程度

  • PPO: Token-level 信号。Critic 能告诉你:“第 5 步推导错了”。

  • GRPO: Trajectory-level 信号。只能告诉你:“这一整题做错了”。

    • 这看起来是 GRPO 的劣势。但在 DeepSeek-R1 等工作中,通过思维链(CoT)过程奖励(Process Reward)的结合,或者仅仅依靠大规模数据的统计规律,GRPO 证明了即使信号粗糙,只要样本量够大,依然能收敛出 SOTA 的效果。

维度 DPO PPO GRPO
本质 监督学习 (Contrastive) 强化学习 (Actor-Critic) 强化学习 (Policy Gradient)
核心机制 拟合偏好数据 (Offline) 价值函数指引 (Online) 组内相对优势 (Online)
显存占用 ⭐ (极低) ⭐⭐⭐ (极高) ⭐⭐ (中等,含采样)
训练稳定性 ⭐⭐⭐ (需调参少) ⭐ (极难调) ⭐⭐ (较稳,看Group大小)
探索能力  无 (模仿)  强 (探索)  强 (探索)
适用场景 聊天、风格对齐、安全 机器人控制、复杂博弈 逻辑推理、数学、代码
Critic网络 不需要 需要 不需要
Baseline Value Model Group Mean

为什么 GRPO 是“推理”的未来?

DeepSeek-Math 和 DeepSeek-R1 的成功揭示了一个趋势:

对于推理(Reasoning)任务,验证(Verification)比生成(Generation)容易。

  • 判断一道数学题做没做对(验证),只需要一个简单的 Python 脚本或 Rule。

  • 让 DPO 去模仿解题过程,很容易学到皮毛(格式)学不到神髓(逻辑)。

  • 让 GRPO 去猜答案,然后通过客观的验证器(Verifier/Compiler)给反馈,模型就能在不断的“猜-验证-修正”中涌现出真正的智能。

结论: 如果你的目标是让模型像人一样说话,用 DPO。如果你的目标是让模型解出人类未解的数学题,必须用 RL (GRPO/PPO)。而 GRPO 是目前 RL 路线中性价比最高的工程解法。

本讲核心 Takeaway:

  1. DPO 缺乏探索: 它是离线的,适合对齐人类偏好,但不适合探索未知解法(推理)。

  2. GRPO 是 PPO 的瘦身版: 它用计算(采样)换显存(去 Critic),在算力成本结构变化的今天,这笔交易极其划算。

  3. 推理需要 On-policy: 想要复现 O1/R1 的效果,必须让模型在环境中实时试错,GRPO 提供了这种环境。

现在,理论、公式、对比都讲完了。

你已经具备了手搓 GRPO 的所有知识储备。

在下一讲,我们将进入代码实战环节。我将为你构建一个PyTorch 风格的 GRPO 最小实现(Minimal Implementation)。我们将不再谈论抽象的 $\pi_\theta$,而是谈论具体的 torch.gather, logits, 和 loss.backward()

GRPO 深度剖析·:用 PyTorch 伪代码还原 GRPO

假设我们已经完成采样(Rollout),数据已经躺在显存里了。 现在的任务是:计算 Loss 并反向传播。

1. 核心输入数据结构 (The Inputs)

在进入计算之前,你必须极其清楚你的 Tensor 形状(Shape)。 假设:

  • Batch Size (B): 2 (有多少个 Prompt)

  • Group Size (G): 4 (每个 Prompt 生成了 4 个回答)

  • Sequence Length (L): 1024 (Token 长度)

import torch
import torch.nn.functional as F

# 1. input_ids: 包含了 Prompt + Response 的完整 token 序列
# Shape: [B, G, L] -> [2, 4, 1024]
# 在实际通过模型前,通常会 flatten 成 [B*G, L] -> [8, 1024]
input_ids = ... 

# 2. attention_mask: 标记哪些是 Padding (0), 哪些是真实数据 (1)
# Shape: [B*G, L]
attention_mask = ...

# 3. old_log_probs: 在采样阶段(Rollout)记录下来的 log 概率
# 这是为了计算 PPO 的 Ratio。
# Shape: [B*G, L]
old_log_probs = ...

# 4. rewards: 外部 Reward Model 给出的分数,每个回答一个分
# Shape: [B, G] -> [2, 4]
# 注意:这是标量,还没广播到 Token 级别
rewards = torch.tensor([
    [1.0, 0.0, 1.0, 0.5],  # Group 1
    [0.2, 0.2, 0.9, 0.1]   # Group 2
])

2. 核心函数一:计算优势 (Compute Advantages)

这是 GRPO 区别于 PPO 的灵魂代码。我们需要在 Group 维度上做归一化。

def compute_grpo_advantages(rewards, epsilon=1e-8):
    """
    输入 rewards: [B, G]
    输出 advantages: [B, G]
    """
    # 1. 计算组内均值 (Mean)
    # dim=1 表示沿着 Group 维度求平均
    mean = rewards.mean(dim=1, keepdim=True)  # Shape: [B, 1]
    
    # 2. 计算组内标准差 (Std)
    std = rewards.std(dim=1, keepdim=True)    # Shape: [B, 1]
    
    # 3. 归一化 (Normalization)
    # 广播机制会自动把 [B, 1] 扩展成 [B, G]
    advantages = (rewards - mean) / (std + epsilon)
    
    # 返回 Shape: [B, G]
    return advantages

3. 核心函数二:获取 Token 概率 (Get LogProbs)

我们需要让模型再跑一次前向传播(Forward),拿到当前的 LogProbs。 注意:这里有一个经典的 gather 操作,一定要看懂。

def get_per_token_logps(model, input_ids, attention_mask):
    """
    计算给定序列中每个 Token 的 Log Probability
    """
    # 1. 模型前向传播
    # outputs.logits Shape: [B*G, L, Vocab_Size]
    outputs = model(input_ids, attention_mask=attention_mask)
    logits = outputs.logits
    
    # 2. 对 Logits 取 LogSoftmax (转成 Log 概率)
    # Shape: [B*G, L, Vocab_Size]
    all_logprobs = F.log_softmax(logits, dim=-1)
    
    # 3. 提取特定 Token 的概率 (Gather)
    # input_ids 是我们需要模型预测的目标词。
    # 我们通常把 input_ids 向左移一位作为 label (next token prediction),这里简化处理。
    # 这一步是为了只把“模型实际生成的那个词”的概率拿出来。
    # Shape: [B*G, L]
    token_logprobs = torch.gather(
        all_logprobs, 
        dim=2, 
        index=input_ids.unsqueeze(-1)
    ).squeeze(-1)
    
    return token_logprobs

4. 核心函数三:GRPO Loss 计算 (The Grand Assembly)

现在把所有东西拼起来。

def grpo_loss_step(model, ref_model, input_ids, mask, old_log_probs, rewards, beta=0.04):
    """
    单步训练逻辑
    """
    # --- Step 1: 准备优势 (Advantage) ---
    # Shape: [B, G]
    advantages = compute_grpo_advantages(rewards)
    
    # [关键] 广播机制 (Broadcasting)
    # 我们要把 [B, G] 的优势扩展到每个 Token 上 [B*G, L]
    # 先 flatten 成 [B*G]
    advantages = advantages.view(-1) 
    # 再扩展到序列长度 (这里只是逻辑示意,实际代码通常在计算 loss 时利用 broadcasting)
    # 假设我们让 advantage 变成 [B*G, 1],后面乘以 [B*G, L] 的 loss 矩阵
    advantages = advantages.unsqueeze(1) 
    
    # --- Step 2: 当前模型前向传播 ---
    # Shape: [B*G, L]
    new_log_probs = get_per_token_logps(model, input_ids, mask)
    
    # --- Step 3: 参考模型前向传播 (用于 KL) ---
    with torch.no_grad():
        ref_log_probs = get_per_token_logps(ref_model, input_ids, mask)

    # --- Step 4: 计算比率 (Ratio) ---
    # ratio = P_new / P_old = exp(log_P_new - log_P_old)
    # Shape: [B*G, L]
    ratio = torch.exp(new_log_probs - old_log_probs)
    
    # --- Step 5: PPO Clip Loss (Surrogate) ---
    # loss1 = ratio * A
    surr1 = ratio * advantages
    # loss2 = clip(ratio) * A
    surr2 = torch.clamp(ratio, 1 - 0.2, 1 + 0.2) * advantages
    
    # 取最小值 (PPO 核心),注意是负号因为要 maximize advantage
    policy_loss = -torch.min(surr1, surr2)
    
    # --- Step 6: KL Penalty ---
    # approx_kl = log_P_new - log_P_ref
    kl_div = new_log_probs - ref_log_probs
    # 这种写法是把 KL 当作 Loss 的一项
    kl_loss = beta * kl_div
    
    # --- Step 7: 综合 Loss ---
    # Shape: [B*G, L]
    total_loss = policy_loss + kl_loss
    
    # --- Step 8: Masking (只计算 Response 部分) ---
    # 我们不能计算 Prompt 部分的 Loss,也不能计算 Padding 的 Loss。
    # 假设我们要 mask 掉 Prompt 和 Padding,只保留 Response。
    # completion_mask 是一个 0/1 矩阵,标记哪些是回答的 token。
    completion_mask = ... (根据 prompt 长度生成)
    
    # 应用 Mask 并求平均
    masked_loss = total_loss * completion_mask
    final_loss = masked_loss.sum() / completion_mask.sum()
    
    return final_loss

本讲核心 Takeaway:

  1. 代码即公式: GRPO 的代码实现就是对 PPO Loss 的一次简单修改,改动不超过 50 行。

  2. Group 处理: 核心在于把 [B, G] 的 Reward 变成 [B, G] 的 Advantage,然后 Flatten 成 [B*G] 喂给 Loss 计算。

  3. 计算图: ref_model 不需要梯度,old_log_probs 是常数(detach),只有 new_log_probs 所在的计算图需要反向传播。

进阶与展望——R1 的变体与推理的未来

1. DeepSeek-R1 的实战变体:规则即奖励 (Rule-based Rewards)

DeepSeek-R1 最让人震撼的一点是:它在 R1-Zero 阶段,完全没有使用神经网络作为 Reward Model

这是一个极其反直觉的“退步”。我们花了几年时间研究怎么训练 Reward Model,结果 R1 告诉我们:对于推理任务,只要写个 Python 脚本就够了。

GRPO + Compiler/Verifier

在 R1 的 GRPO 训练中,Reward 的构成极其朴素:

  1. 准确性奖励 (Accuracy Reward): 把模型生成的答案(Answer)塞进编译器运行,或者与标准答案字符串匹配。通过 = 1.0,失败 = 0.0。

  2. 格式奖励 (Format Reward): 强制模型把思考过程写在 <think></think> 之间。这其实是一种“强约束引导”,防止模型在探索过程中把格式跑丢了。

洞察: 这意味着 GRPO 将强化学习从“拟合人类偏好”(这是玄学)拉回了“通过客观检验”(这是科学)。只要你能写出一个验证脚本(Checker),你就能用 GRPO 训练出一个超越人类的模型。

2. 解决冷启动问题 (The Cold Start Problem)

在第五讲我们提到,如果模型一道题都做不对,GRPO 是无效的(全 0 奖励)。DeepSeek-R1 是如何解决这个问题的?

这里有两个阶段的 GRPO:

  1. R1-Zero (纯 RL): 让模型盲搜。这需要极大的耐心和算力。初期模型会输出无数的乱码和错误,直到偶然间(Serendipity)它撞对了一次。一旦撞对,GRPO 的优势函数就会瞬间捕捉到这个信号,指数级地放大这条路径的概率。这是“顿悟时刻”(Aha Moment)的数学本质。

  2. R1 (SFT + RL): 先用少量高质量的“思维链数据”做 SFT,把模型教会“怎么像人一样思考”。这相当于给 GRPO 一个很高的初始起点(Initial Policy)。这种做法收敛更快,效果更稳。

未来趋势: 如何更高效地获取“第一口奶”(SFT Data)?使用小模型(Student)去蒸馏大模型(Teacher)的 GRPO 轨迹,可能是一条捷径。

3. 迈向多模态与 Agent (Multimodal & Agents)

GRPO 的逻辑可以完美迁移到 Text 以外的领域。

3.1 Visual GRPO

想象一个视觉推理任务:“图中这只猫在干什么?”

  • Prompt: 图片 + 问题。

  • Group: 生成 16 个不同的解释。

  • Verifier: 这里很难用规则判断。但如果是“数图中有几个苹果”,这就是完美的 GRPO 场景。模型多数几次,如果答案是 5,且检测框对准了,Reward = 1。

3.2 Agent Tool Use

Agent 调用工具(如搜索、计算器)本质上也是一种推理路径。

  • Action: 调用 API。

  • Outcome: API 返回结果是否解决了用户问题?

  • GRPO 的机会: 我们可以采样一组 API 调用序列。有的序列调用了 10 次才解决,有的调用了 2 次就解决了。GRPO 会自然地奖励那些既快又准的工具使用策略。

4. 终极猜想:Process GRPO (Dense Reward)

目前的 GRPO 还是基于结果(Outcome)的。就像老师只看卷子最后的答案给分。

我个人猜想未来的 GRPO 可能会进化为 Dense GRPO。

我们不需要训练一个巨大的 Critic,但我们可以引入轻量级的 Step-level Verifier

  • 每生成一行代码,就跑一次语法检查。

  • 每推导一步数学公式,就用符号计算引擎(SymPy)验算一步。

这样,Reward $r$ 不再是一个标量,而是一个向量$r = [r_1, r_2, ..., r_T]$

优势函数 $\hat{A}_t$ 将利用蒙特卡洛树搜索(MCTS)的思想,更精准地评估每一步的价值。这将是 System 2 思维的完全体。

【GRPO 知识图谱】

1. 核心动机 (Motivation)
   └── 抛弃 Critic -> 省显存 -> 赋能大模型训练 (Lecture 1-2)

2. 数学原理 (Math Foundation)
   ├── 目标函数: Clip Loss + KL Penalty (Lecture 3, 6)
   ├── 优势函数: Group Normalization (r - μ) / σ (Lecture 5)
   └── 采样策略: Group Size & Temperature (Lecture 4)

3. 工程实现 (Implementation)
   ├── 训练循环: Rollout -> Score -> Train (Lecture 7)
   ├── 代码实战: Gather, Broadcasting, Masking (Lecture 9)
   └── 算法对比: PPO vs DPO vs GRPO (Lecture 8)

4. 未来展望 (Future)
   └── Rule-based Reward, Self-Verification, Agents (Lecture 10)

你现在已经掌握了目前 LLM Post-training 领域最前沿的武器。

  • 当你看到显存不够时,你会想到 GRPO。

  • 当你看到模型推理能力弱时,你会想到 GRPO。

  • 当你看到 DPO 遇到瓶颈时,你会想到 GRPO。

能看到这里,说明你有及其惊人的毅力和学习渴望,希望本人小小的一点分享,能够帮助大家完全搞懂GRPO,在AGI的道路上出一份力,加油!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐