前言: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。顾名思义,它由两个关键词组成:
Group(组)
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:
省资源:GRPO 去掉了 Critic 模型,极大地节省了显存和计算量,使得训练超大模型变得更加可行。
基线(Baseline)的转变:从“神经网络预测的基线”(Value Function)转变为“当前Batch内的统计基线”(Group Mean)。
原理:通过对同一个问题采样多条路径,用组内归一化(Group Normalization)的方式来确定哪个答案是好的,哪个是差的。
GRPO 深度剖析:PPO 昂贵且脆弱的 Critic
在经典的强化学习(RL)理论中,Actor-Critic 架构几乎被奉为圭臬。但在大语言模型(LLM)的场景下,这个架构显得愈发笨重且低效。我们要解决的问题是:为什么训练一个好的 Value Function (Critic) 如此困难且昂贵?
1. 回顾 PPO 的核心数学依赖
要理解痛点,先看公式。PPO 的核心在于最大化以下目标函数(简化版):
在标准 PPO 中,我们通常使用GAE (Generalized Advantage Estimation)来计算它,而 GAE 高度依赖于 TD Error ():
请注意这里出现的。这就是 Critic 网络,它的任务是预测状态
的预期回报。
致命逻辑链条:
Actor 的更新方向取决于
。
的准确性取决于
的预测精度。
如果 Critic
也是一个刚开始训练的神经网络,它的预测是充满了噪声和偏差的。
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) 瞎猜),那么计算出的优势就是错误的。Actor 会根据错误的信号进行更新,导致模型性能震荡甚至崩塌(Collapse)。
4. GRPO 的降维打击:用“统计”替代“预测”
既然训练一个神经网络(Critic)去预测 Baseline(V(s))既贵又难,GRPO 选择了一个统计学的方法。
我们不再问 Critic:“这一单大概能得多少分?”
而是直接做实验:“既然我不知道基线,那我就多跑几次,取平均值当基线。”
让我们对比一下Baseline的来源:
| 特性 | PPO (Standard) | GRPO |
| Baseline 来源 | 参数化模型 | 蒙特卡洛采样的组均值 (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:
PPO 的 Critic 在 LLM 时代不仅仅是增加了显存,更重要的是它引入了优化难题。
Value Function 在长文本生成任务中极难收敛,不准确的 Value 会误导 Actor。
GRPO 通过Group Relative(组相对)的方式,用“采样均值”替代了“参数预测”,在数学上依然构成了有效的 Advantage 估计,同时将计算成本砍半。
GRPO 深度剖析:数学形式化—目标函数的重构
在强化学习中,所有都蕴含在那个我们需要最大化的之中。GRPO 的美妙之处在于,它在 PPO 的基础上做了一次手术,切除了
,植入了一个基于组(Group)的归一化算子。
1. 符号定义 (Notation Setup)
为了保证严谨性,我们先定义这一讲的数学符合:
: 我们要训练的策略模型(Actor),即当前的 LLM。
: 参考模型(Reference Model),通常是 SFT 后的初始模型,用于防止模型跑偏。
: 用户的问题(Prompt),从数据集 P(Q) 中采样。
一组输出(Outputs)。这里 G是组的大小(Group Size),比如 8 或 16。
: 第
个输出对应的奖励值(Reward),由 Reward Model 给出。
2. 核心变革:组相对优势
在 PPO 中,优势依赖于 Critic。而在 GRPO 中,优势
则依赖于同行衬托。
对于同一个问题,模型生成了一组输出
,对应的奖励为
。
第个输出的优势函数定义为:
深度解读:
Mean (
): 这一组输出的平均水平。如果
,说明这个回答比你自己生成的平均水平好,应当鼓励(
)。
Std (
): 标准差用于缩放。这非常关键!
如果某次生成大家的差别很大(比如 [10, 90, 50]),标准差大,优势会被缩小,梯度的更新幅度会变稳。
如果差别很小(比如 [80, 81, 79]),说明模型对这个问题的表现很一致,标准差小,微小的分数差异会被放大,模型能学到细微的区别。
: 一个极小的数(如
),防止分母为 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 目标函数
有了上面的,我们可以写出 GRPO 需要最大化的完整目标函数:
3.1 期望与采样 (Expectation & Sampling)
这告诉我们训练的循环逻辑:
采样一个 Prompt
。
用旧策略
采样
个输出
。
接下来的计算都是基于这
个样本的平均值。
3.2 代理损失 (Surrogate Loss with Clipping)
这是直接继承自 PPO 的部分,但在 $A_i$ 上用了 GRPO 的定义:
比率 (Ratio)
:衡量当前策略和采样时的策略差了多少。
Clip: 限制更新幅度,防止一次更新步子迈太大把模型扯坏了。这是 PPO 家族稳定性的基石。
3.3 KL 散度惩罚 (KL Divergence Penalty)
为什么要减去它?
这是一个正则化项 (Regularization)。
是我们的 SFT 模型(它是懂人话的)。
是我们在训练的模型。
如果
为了拿高分(Reward Hacking),开始输出乱码或者极端的欺骗性文本,它和
的分布差异会变大,KL 散度飙升。
因为公式前面是负号,最大化目标函数
就意味着要最小化KL 散度。
4. Token 级 vs 样本级 KL
在实际实现中(如 DeepSeekMath 的论文),KL 散度通常使用如下近似计算方式:
(注:具体实现有多种近似公式,Schulman 的近似最为常见)
关键点在于:GRPO 通常将 KL 散度作为一个 Loss 项直接加在目标函数里,而不是像某些 PPO 实现那样作为 Reward 的一部分(PPO-reward-shaping)。这使得训练更加直观,我们明确地知道模型在优化什么:“相对优势”减去“偏离代价”。
本讲核心 Takeaway:
公式的核心:
是 PPO Clip Loss 的变体,唯一的区别在于 Advantage 的来源。
归一化的魔力:
使得模型在面对不同难度的 Prompt 时,梯度尺度保持稳定,这是 GRPO 训练稳定的关键。
正则化:KL 惩罚项是防止模型“走火入魔”的必选组件。
现在,公式已经摆在桌上了。但在实际把代码跑起来之前,我们面临一个极其具体且棘手的超参数选择问题:(Group Size) 到底取多少?
取 4?取 16?还是 64?
如果太小,统计均值不准确怎么办?如果
太大,显存爆了怎么办?
在下一讲,我们将深入探讨Group 的奥秘,从统计学角度分析的大小如何决定了 GRPO 的成败,以及如何根据你的显存大小来权衡这个参数。
GRPO 深度剖析:组(Group)——统计方差与采样策略
1. 为什么
不能太小?——基线估计的方差问题
我们知道,GRPO 用组内均值来近似真实的 State Value
。从蒙特卡洛(Monte Carlo)的角度看,这是一个无偏估计,但方差(Variance)可能极大。
1.1 统计学直觉
假设真实的价值。
如果(极端的例子),我们只采样两个样本:
Case 1:运气好,采到 [74, 76]。均值 75,估计完美。
Case 2:运气差,采到 [50, 60](模型发挥失常)。均值 55。
在这个 Case 2 中,60 分的样本会被认为具有“巨大优势”(+5分 relative to 55),而在真实基准(75分)下,它其实是很差的。
结论:当太小时,Baseline (
) 的波动极其剧烈。这会导致梯度的方差变大,训练过程像是在“走醉鬼步”(Random Walk),难以收敛。
1.2 文献中的经验值
DeepSeek-Math 和 DeepSeek-R1 的论文中,通常倾向于较大的。
DeepSeek-Math:推荐
。
经验法则:对于复杂的逻辑推理任务(如数学题),解空间的方差本身就大(对就是1,错就是0),需要更大的
来稳定均值。
2. 为什么 $G$ 不能无限大?——边际递减与显存墙
你可能会问:“既然 G 越大估计越准,为什么不设成 1024?”
这里有两个制约因素:
显存墙(The VRAM Wall):
GRPO 需要在一次 Forward Pass 中生成 G 个完整的长序列。
如果你训练 70B 模型,Context Length 4096。
G=64 意味着你需要同时显存驻留 64 条 4096 长度的 KV Cache 和中间激活值。这对显存是毁灭性的打击。
边际效益递减(Diminishing Returns):
根据中心极限定理,标准误(Standard Error)随
下降。
从 4 增加到 16,误差减半,收益巨大。
从 64 增加到 256,误差只减半,但计算成本翻了 4 倍。
3. 致命陷阱:模式崩塌(Mode Collapse)与标准差
在 GRPO 公式中,分母是(标准差)。这引入了一个在 PPO 中不存在的风险。
如果会发生什么?
这意味着模型生成的个回答完全一样,或者得分完全一样。
此时,分子
也是 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 离线生成)
处理带来的显存压力,学术界和工业界有两种做法:
4.1 Online Generation (标准 GRPO)
在训练步中实时生成个样本。
优点:数据是完全 On-Policy 的,完全符合数学推导。
缺点:慢,显存占用大。
4.2 Offline / Hybrid Generation (变体)
先用当前模型跑 inference,把数据存到 Buffer 里(比如存 64 个),然后训练时从 Buffer 里拿数据计算 Loss。
风险:变成了 Off-Policy。如果模型更新了,Buffer 里的数据就“过时”了(Stale)。
修正:需要引入 Importance Sampling (IS) 或者限制 Buffer 的刷新频率。DeepSeek 的实现通常倾向于高效的 Online 生成,或者极短周期的 Buffer。
本讲核心 Takeaway:
Group Size (
)是权衡“估计方差”与“显存成本”的杠杆。一般推荐
。
多样性是生命线:GRPO 依赖组内差异来提取信号。必须使用非零的 Temperature 采样。如果模型坍缩到单一输出,GRPO 将失效。
标准差的意义:
不仅仅是分母,它是一个“自适应的各种系数”。当大家都很烂时,微小的差异会被放大;当大家都很好时,差异会被缩小。
明白了的设定,我们其实只解决了一半的问题。公式里的
算出来了,但不要忘了,我们并没有 Critic。这就引出了一个深刻的问题:没有 Value Function 的情况下,我们如何确保 Advantage 的计算真的是“优势”,而不是“噪声”?尤其是当 Reward 本身很稀疏的时候(比如做数学题,全错就是0,很难拿到1)。
下一讲,我们将深入优势函数(Advantage Function)的重构,探讨在 Reward 稀疏或密集场景下,GRPO 具体的数值表现和潜在的改进技巧(如 Outcome Reward vs Process Reward)。
在传统的 PPO 中,优势函数是极其复杂的,它通常利用 GAE(Generalized Advantage Estimation)进行时间步(Token)级别的细粒度指派。而在 GRPO 中,由于移除了 Critic,我们的评价体系发生了根本性的范式转移:从“基于价值预测的绝对评价”转向了“基于群体竞争的相对评价”。
GRPO 深度剖析:优势函数(Advantage Function)的重构——零和博弈与稀疏奖励
1. 零和博弈:归一化的深层含义
让我们再次审视那个看起来人畜无害的公式:
如果你对这一组求和,你会发现一个惊人的性质:
(注:在极小的情况下,加权后的均值严格为 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 都会给出一个价值评估
。因此 PPO 可以精确地说:“虽然你最后答错了,但你中间推理的第三步是很棒的。”(Temporal Credit Assignment)。
GRPO (Outcome-based):通常情况下,我们只有一个最终的 Reward
(比如答案对不对)。这意味着,我们将把这个
产生的优势
,广播(Broadcast)给这条回答中的每一个 Token。
这听起来很粗糙,对吧?如果我写了 1000 字的推理,只有最后一步错了,导致,难道这 1000 字都要被惩罚吗?
DeepSeek 的解决方案:过程奖励与格式奖励
为了缓解这个“一刀切”的问题,GRPO 极其依赖Reward Engineering。在 DeepSeek-R1 中,Reward不再是一个单一的标量,而是多个部分的组合:
结果奖励 (
):最终答案对不对?(LeedCode 通过率,数学答案匹配)。
格式奖励 (
):这就是“过程”的一种体现。比如强制模型必须输出
<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 中会制造麻烦。
极端情况:个样本全错(全是 0)或全对(全是 1)。
此时,公式退化。虽然有
保护不报错,但
会变成 0。梯度消失,训练空转。
工程上的应对策略:
冷启动(Cold Start):在使用 GRPO 纯 RL 之前,必须有一个足够强的 SFT 模型,保证它至少有 10%-20% 的概率能答对题。如果模型一开始什么都不会,GRPO 是推不动的。
混合奖励:如前所述,加入 dense reward(如格式检查、长度惩罚、语言流畅度分),确保存活下来的样本之间即使答案都错了,分数也有高低之分(比如写了步骤的 0 分比啥都没写的 0 分要稍微好一点点)。
本讲核心 Takeaway:
相对评价:GRPO 构建了一个组内竞争机制,Advantage 均值为 0。这消除了绝对 Reward 的波动影响。
粗粒度信号:相比 PPO 的 Token 级评价,GRPO 默认是 Trajectory 级评价。它依赖海量数据的统计规律来区分哪些 Token 是真正导致错误的。
对初始能力的依赖:因为没有 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 的偏好),牺牲了语言的可读性和逻辑性来换取高分。
(Reference Model)的作用就是那个拿着教鞭的老师。它通常是 SFT 阶段得到的模型。它的潜台词是:“你可以去尝试拿高分,但你的说话方式(概率分布)不能离我太远。你首先得像个正常人说话,其次才是答对题。”
2. KL 散度的数学计算:Token 级别的微操
在 LLM 中,KL 散度不是算整个句子的,而是算每一个 Token的。
对于第个 Token,模型
预测的概率分布是
,参考模型
预测的概率分布是
。 理论上的 KL 公式是
。
但在 GRPO 的实际工程实现中(参考 DeepSeekMath 和常见的 PPO 实现),我们通常使用Schulman 近似或者简单的Log-Ratio。
最常用的近似公式:
对于生成的某个特定 token:
注意:
如果
比
高很多(模型过度自信地输出了这个词),差值为正,KL Loss 变大,惩罚模型。
如果
变小了(模型不敢说 SFT 原本会说的词),差值为负,但这通常由期望项平衡。
汇总到 Loss 中
在 GRPO 的目标函数中,我们将这个 KL 值作为一个惩罚项(Penalty)减去:
这里 T 是生成的序列长度。这意味着:每一个偏离 Reference 的 Token 都要付出代价。
3. 两种流派:KL in Reward vs KL in Loss
在代码实现层面,你会看到两种处理 KL 的方式,这点非常容易混淆,你需要特别注意:
流派 A:PPO 经典做法 (Reward Shaping)
直接把 KL 惩罚扣在 Reward 里。
逻辑:这里的 Reward 变成了“综合得分”。
在 GRPO 中的问题:如果我们把 KL 加到
里,再进行 Group Normalization (
),KL 的物理意义会被方差缩放扭曲。这通常不是 GRPO 的最佳实践。
流派 B:DeepSeek-Math 做法 (Direct Loss Term)
保持 Reward 纯净,把 KL 放在目标函数里作为正则项。
逻辑:
负责指引方向(哪个答案好),
负责拉住缰绳(别走太远)。
优势:这种分离让优化更可控。
可以独立调节,不受 Reward 数值范围的影响。
我们可以把想象成地球,
是风筝。
[ 崩坏区 / Collapse Zone ] (模型输出乱码,但可能偶然骗过 Reward Model) WARNING: KL >>> 1.0 / / <-- 巨大的 KL 惩罚 (强引力) / +-----------------------+ | 训练中的模型 | ---> 试图往高 Reward 方向飞 | Policy (Actor) | +-----------------------+ \ \ <-- Beta (β) 决定了引力绳的粗细 \ [ 安全区 / Trust Region ] (语言通顺,符合人类语法,由 SFT 模型定义) Reference Model如果
太大 (Beta=1.0):引力太强,风筝飞不起来。模型会死死守着 SFT 的参数,不敢做任何探索。训练 Loss 下降,但 Reward 不升。
如果
太小 (Beta=0.0):绳子断了。风筝飞入太空(崩坏区)。模型为了优化 Reward 开始胡言乱语。
黄金甜点 (Beta
0.01 - 0.05):既允许模型探索新的解题路径,又强迫它用“人话”把答案写出来。
动态 Beta (Adaptive KL) —— 进阶技巧
在长时间的训练(如几万步)中,固定的往往不好用。
训练初期,模型很容易偏离,需要强 KL 约束。
训练后期,模型已经学乖了,可以适当放松
让它冲击更高分。
或者反过来(PPO 中常用):设定一个目标 KL 值(比如)。
如果当前 KL >
:说明跑太远了,调大
。
如果当前 KL <
:说明太保守了,调小
。
虽然 GRPO 论文中较少强调这一点,但在工程实践中,监控 KL 曲线是判断训练是否健康的唯一标准。
健康的曲线:KL 缓慢上升,然后稳定在一个小数值(如 0.01-0.05)。
暴雷的曲线:KL 指数级爆炸,或者突然变成 0。
本讲核心 Takeaway:
安全锚点:KL 散度约束是 RLHF 区别于普通 Fine-tuning 的关键,它保证了生成的语言质量。
公式选择:在 GRPO 中,建议将 KL 作为 Loss 的独立正则项,而不是混入 Reward,以避免被 Normalization 干扰。
计算粒度:KL 是 Token-level 的。每一个“标新立异”的词都会累积罚分。
理论闭环已经完成。现在,我们需要把这些积木拼成一个完整的、会动的机器。 下一讲,我们将进入系统级的整合,我们将像拆解引擎一样,一步步拆解GRPO 的完整训练循环 (Training Loop)。我们将看到数据是如何在 GPU 之间流转,以及梯度是如何反向传播的。
GRPO 深度剖析·:训练循环详解
GRPO 的训练过程可以被切割为两个截然不同的阶段:
Rollout Phase (采样阶段):只做推理,不传梯度,极度消耗显存带宽。
Training Phase (学习阶段):反向传播,更新权重,极度消耗算力。
1. Step 1: 采样 (Rollout / Generation) —— 最昂贵的开销
一切始于一批 Prompts。假设我们的 Batch Size (of Prompts) =,Group Size =
。
我们需要生成条完整的回答。
输入:
个问题(例如:["1+1=?", "解释量子力学", ...])。
动作:模型
(当前的 Actor)进入
eval()模式。操作:
复制每个 Prompt
次。
并行生成
个回答。
关键数据留存:在生成的过程中,我们要顺手把每个 Token 的Log Probability (LogProbs)存下来!
为什么要存?因为计算 Loss 里的比率
时,分母就是这个时候算出来的。如果不存,训练时就要多做一次前向传播,太亏了。
注意:这是 GRPO 训练中最慢的一步。对于 70B 模型,生成长文本(COT)可能占据整个训练时间的 70%-80%。所以这里通常会用到 vLLM 等加速推理框架。
2. Step 2: 评分与归一化 (Evaluation & Advantage)
生成结束后,GPU 显存里躺着条轨迹(Trajectories)。现在的任务是给它们打标签。
动作:
打分:调用 Reward Function(可能是代码解释器,也可能是另一个 Reward Model),得到
个标量奖励
。
分组归一化(核心):
将数据切分为
组,每组
个轨迹。
对每组计算均值
和标准差
。
计算优势
。
释放显存:此时,原始的 KV Cache 可以扔掉了(除非你用 PPO 的特殊实现),我们只需要 Input IDs, Masks, Old LogProbs 和 Advantages。
3. Step 3: 学习 (Optimization) —— 内层循环
数据准备好了,现在进入真正的“训练”环节。这通常是一个内层循环(Inner Epochs)。
也就是说,我们辛辛苦苦生成的这批数据,不会只用一次,通常会反复用它更新模型 1-4 次(Update Epochs)。
对于每一次更新迭代:
Actor Forward (新策略前向传播):
把刚才那
条数据喂给当前正在更新的 Actor 模型。
计算 New LogProbs。
注意:这一步需要梯度
requires_grad=True。
Reference Forward (参考模型前向传播):
把同样的数据喂给 Reference Model(冻结参数)。
计算 Ref LogProbs。
用途:用于计算 KL 散度。
优化技巧:如果显存不够,这一步可以预先算好存起来,牺牲磁盘/内存换显存。
Loss Calculation (损失计算):
Ratio:
。
Surrogate Loss:用 Clip 公式结合 Advantage 计算策略梯度。
KL Loss:
。
Total Loss:Surrogate Loss + KL Loss。
Backward & Step (反向传播与更新):
loss.backward()optimizer.step()
实例
Prompt (输入): “2 + 2 = ?”
Group Size (): 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 得分 (
):1.0
回答 B 得分 (
):0.0
回答 C 得分 (
):1.0
回答 D 得分 (
):0.0
注意:此时我们手里的 Reward 只是 4 个简单的数字:[1.0, 0.0, 1.0, 0.0]。完全不涉及 Token 的切分。
第二步:计算均值与标准差 (Group Statistics)
现在我们在组内(Group)计算统计量:
均值 (
):
标准差 (
):计算可得
计算优势 (
):公式
回答 A (Adv):
回答 B (Adv):
回答 C (Adv):
回答 D (Adv):
关键点:到目前为止,我们只得到了4 个标量值。
第三步:广播 (Broadcasting)
现在的难题是:回答 A 有 3 个 Token,回答 C 有 5 个 Token,长度不一样,怎么把这一个标量用到计算里?
这就叫广播。我们将这个优势值,像“淋雨”一样,均匀地淋在每一个 Token 头上。
看看【回答 C】的内部视角:
Token 序列:["答", "案", "是", "4", "<EOS>"]
优势值:+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>"]
优势值:-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,但倍的数据量依然庞大。
Gradient Checkpointing (梯度检查点):必须开。用 30% 的额外计算时间换取 4-5 倍的显存空间。否则
的长序列直接 OOM。
Micro-Batching (微批次):
虽然我们采样了
个样本(比如
),在 Optimization 阶段,我们不需要一次性把 64 个样本全塞进 GPU。
我们可以把这 64 个样本拆成 Chunk(例如每次 2 个),累积梯度(Gradient Accumulation),最后更新一次。
注意:Group Normalization 必须在切分 Micro-Batch之前完成!优势的计算是基于全组的,不能切碎了算。
本讲核心 Takeaway:
两段式结构:GRPO 严格区分“造数据(Rollout)”和“吃数据(Train)”。
数据复用:为了摊薄昂贵的采样成本,通常会对同一批数据进行多次梯度更新(Inner Epochs)。
计算瓶颈:与 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:
DPO 缺乏探索:它是离线的,适合对齐人类偏好,但不适合探索未知解法(推理)。
GRPO 是 PPO 的瘦身版:它用计算(采样)换显存(去 Critic),在算力成本结构变化的今天,这笔交易极其划算。
推理需要 On-policy:想要复现 O1/R1 的效果,必须让模型在环境中实时试错,GRPO 提供了这种环境。
现在,理论、公式、对比都讲完了。
你已经具备了手搓 GRPO 的所有知识储备。
在下一讲,我们将进入代码实战环节。我将为你构建一个PyTorch 风格的 GRPO 最小实现(Minimal Implementation)。我们将不再谈论抽象的,而是谈论具体的
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 advantages3. 核心函数二:获取 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_logprobs4. 核心函数三: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:
代码即公式:GRPO 的代码实现就是对 PPO Loss 的一次简单修改,改动不超过 50 行。
Group 处理:核心在于把
[B, G]的 Reward 变成[B, G]的 Advantage,然后 Flatten 成[B*G]喂给 Loss 计算。计算图:
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 的构成极其朴素:
准确性奖励 (Accuracy Reward):把模型生成的答案(Answer)塞进编译器运行,或者与标准答案字符串匹配。通过 = 1.0,失败 = 0.0。
格式奖励 (Format Reward):强制模型把思考过程写在
<think>和</think>之间。这其实是一种“强约束引导”,防止模型在探索过程中把格式跑丢了。
洞察:这意味着 GRPO 将强化学习从“拟合人类偏好”(这是玄学)拉回了“通过客观检验”(这是科学)。只要你能写出一个验证脚本(Checker),你就能用 GRPO 训练出一个超越人类的模型。
2. 解决冷启动问题 (The Cold Start Problem)
在第五讲我们提到,如果模型一道题都做不对,GRPO 是无效的(全 0 奖励)。DeepSeek-R1 是如何解决这个问题的?
这里有两个阶段的 GRPO:
R1-Zero (纯 RL):让模型盲搜。这需要极大的耐心和算力。初期模型会输出无数的乱码和错误,直到偶然间(Serendipity)它撞对了一次。一旦撞对,GRPO 的优势函数就会瞬间捕捉到这个信号,指数级地放大这条路径的概率。这是“顿悟时刻”(Aha Moment)的数学本质。
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不再是一个标量,而是一个向量
。
优势函数将利用蒙特卡洛树搜索(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的道路上出一份力,加油!