从 SFT 到 RLVR 的平滑过渡:GRPO 的直觉解释
Published:
这篇文章主要是从直觉角度来解释大模型训练中 SFT 和 RL 的关系。我们可以看到区别于SFT时“老师说的就是对”的学习方式,RL 是为了能够高效的利用“没那么正确”的样本,从而增加模型“正确”的可能性。这篇文章是我在阅读整理 Understanding Reinforcement Learning for Model Training, and future directions with GRAPE 这篇报告的思考和笔记,也强烈建议大家去读原文。
1. SFT:本质是“背答案”,和老师越相似越好
我们在 SFT(Supervised Fine-Tuning)阶段,训练的 Loss Function 本质上是负对数似然(NLL, Negative Log Likelihood)。
隐含的前提是:SFT 假设数据集中给出的就是“标准答案”(例如从教师模型蒸馏出的正确解题步骤和最终答案)。
对于第 $t$ 个 token,现有模型 $\theta$ 在当前上下文 \(\vec{s}_t\) 下生成正确 token $a_t$ 的概率是 $\pi_\theta(a_t \mid \vec{s}_t)$。我们当然希望这个概率越大越好。 对于一整句话,假设每个 token 相对独立,那么生成这句“标准答案”的概率就是所有 token 概率的乘积:(这里我们假设t从1开始,但实际上t应该从prompt后的第一个token开始) \(P(\text{Whole Sequence}) = \prod_{t=1}^{T} \pi_\theta(a_t \mid \vec{s}_t)\)
我们的理论目标是让这个概率接近 1。但由于每个 token 的概率通常很小(比如 0.01),连乘之后数值会无限接近于 0,导致计算机浮点数溢出。所以我们**取对数(Log)把乘法变加法,再取负号变成最小化问题,这就构成了梯度下降的 Loss: \(Loss = - \sum_{t=1}^{T} \log(\pi_\theta(a_t \mid \vec{s}_t))\)
如果把一堆样本 Batch 在一起(假设有 $S$ 个样本),平均 Loss 就是: \(\mathcal{L}_{SFT} = \frac{1}{S} \sum_{i=1}^{S} NLL(\vec{text}_i, \theta)\)
2. REINFORCE:给好学生“加鸡腿”
SFT 的问题在于:它平等地对待了每一个样本。 但在实际生成中,有的回答是完美的,有的是一般的,甚至我们希望模型能从“负样本”中吸取教训。 于是,我们很自然地想到给 Loss 加一个权重(Reward): \(\mathcal{L}_{RL} = \frac{1}{S} \sum_{i=1}^{S} R(\vec{text}_i) \cdot NLL(\vec{text}_i, \theta)\)
这就是大名鼎鼎的 REINFORCE 算法。
那为什么实际训练中很少直接用它? 如果 $R$ 的绝对值过大,会带来严重的梯度高方差(High Variance)。
- 直觉解释:假设好样本得 1001 分,坏样本得 999 分。虽然分差只有 2 分,但模型在更新时,会受到一股“1001 的推力”和一股“999 的推力”。模型参数会被推得左一下、右一下,剧烈震荡,导致难以收敛。
3. Advantage:关键在于“超出预期”
为了解决方差问题,我们需要对 $R$ 进行标准化处理:减去一个基线(Baseline, $V$)。 比如把 (1001, 999) 减去基线 1000,变成 (+1, -1)。这样梯度就稳定多了。
改进后的 Loss 公式(引入 Advantage): \(Loss = -\frac{1}{S} \sum_{i=1}^{S} \sum_{t=1}^{T} \underbrace{\left( R(\vec{text}_i) - V_M(\vec{s}_{it}) \right)}_{\text{Advantage (A)}} \cdot \log\left( \pi_\theta(a_{it} \mid \vec{s}_{it}) \right)\)
这里的 $R(\vec{text}_i)$ 是整段文本的最终得分,但基线 \(V_M(\vec{s}_{it})\) 依赖于当前时刻 $t$ 的状态(即 prompt + 已经生成的 token)。
如何从直觉上理解 $R - V$(Advantage)? 我们可以把它看作“惊喜度”:
- 惊喜(R > V): 假设这条样本最终得分 $R$ 很高。但在第 $t$ 个 token 之前,生成的序列平平无奇,模型觉得“这把也就拿个及格分吧”($V$ 较低)。突然,第 $t$ 个 token 出现(比如一道数学题的关键辅助线,或者代码里的神来之笔),最终导致了高分。
- 此时 $R - V$ 很大。模型会意识到:“原来是这一步让我逆天改命的!” 于是大幅增加该 token 的概率。
- 失望(R < V): 假设 $V$ 很高(模型觉得稳了),结果第 $t$ 个 token 瞎写,导致最终 $R$ 很低。
- 此时 $R - V$ 是负数。模型会反思:“原来是这一步毁了所有!” 于是抑制该 token。
- 符合预期(R ≈ V): 如果不管好坏,模型的预期 $V$ 和实际结果 $R$ 都差不多,说明没有惊喜。梯度接近 0,模型参数保持稳定,不进行无效更新。
4. 从 PPO 到 GRPO:去掉了“教练”
搞清楚了原理,剩下的就是工程实现:$R$ 和 $V$ 怎么来?
- 获取 R(奖励):
- RLHF: 需要训练一个 Reward Model 模仿人类偏好打分。
- RLVR(如数学/代码): 这个简单多了!答案对了给 1 分,格式对了给 0.2 分。规则就是 Reward。
- 获取 V(基线):
- PPO 的做法: 雇一个“教练”——训练一个独立的 Critic Model(Value Model)来实时预测当前的 $V$。这很贵,也很慢,而且 Critic 自己也需要训练。
- GRPO 的做法: 既然我们生成了一组(Group)回答,为什么不拿“同伴的平均分”当基线? GRPO 不需要 Critic Model。它通过采样一组输出(比如 8 个),计算这 8 个输出的平均 Reward。
- 比平均分高的,Advantage 为正(好学生)。
- 比平均分低的,Advantage 为负(差学生)。
看到这里,聪明的你肯定会问,为什么以前我们不在一个 Batch 里直接拿所有生成结果的均值来做为 V 呢?这好像是很容易想到的呀 如果是从 PPO 过来的同学,肯定意识到,在 PPO 中,我们是把不同的 Prompt 放在一个 Batch 里,这会带来什么问题呢?不同的 Prompt 难度不一样,它们的 reward 如果放在一起,结果不会很理想。
假设我有一个问题的难度近似于 1+1 = 2, 而模型答对了,获得了 Reward 1. 还有一个问题是一道非常难的竞赛类题目,模型打错了,但是中间的思考过程大部分都是正确的,获得reward 0. 在这种情况下,V=0.5, 而模型朝难的竞赛题思考的过程被负向激励了(0-0.5=-0.5)久而久之,很有可能会发生的情况是,模型对难题一概不理,反正都拿到的是0的奖励。那么模型也就学不到新东西了。
GRPO 的巧妙之处: 首先一个组内的prompt是一致的,不存在难度上的波动。它用组内平均值替代了 Critic Model 的预测值。正因如此,GRPO 计算出的 Advantage 通常是针对整条样本的(因为平均分是整条样本的平均),而不再像 PPO 那样拥有逐个 Token 的精细 $V(s)$。但对于数学推理这类“结果导向”的任务,这种简化不仅效果好,还极大地节省了显存和计算资源。
本质上,PPO 和 GRPO 都是 REINFORCE 的变种。下一篇我们将详细介绍从 REINFORCE $\to$ TRPO $\to$ PPO $\to$ GRPO 的演进之路。Advantage 的计算当然是 GRPO 的核心,但我们还有其他的部分(如重要性采样, Clip)才能完整理解 GRPO 以及它的一系列变种如 DAPO, DR.GRPO, GSPO 等
