GRPO学习

https://blog.csdn.net/qq_38961840/article/details/145384852
https://arxiv.org/abs/2402.03300

强化学习中的基本概念

智能体、环境与交互

在传统的强化学习框架中,我们通常有一个智能体(Agent) 和一个环境(Environment)。智能体每一步会基于自身策略 \(\pi(s)\) 去决定一个动作 \(a\),然后环境会根据这个动作给出新的状态和一个奖励 \(r\),智能体收集这个奖励并继续下一步。这种循环往复构成了一个时间序列过程,直到到达终止条件(如达成目标或超时等)。

在语言模型(尤其是大型语言模型,LLM)当中:

  • 可以把一个“问题”(如一段文本提示 prompt)当作环境给的状态
  • 模型(智能体)产出下一 token(动作),不断重复,直到生成完整回答
  • 人类或奖励模型再给予整段回答的质量分,或每个 token 局部奖励
  • 虽然与传统强化学习中的“马尔可夫决策过程(MDP)”有差别,但本质上也可抽象为状态—动作—奖励—状态—动作的机制

状态、动作、奖励、策略

  • 状态 \(s\)
    • 语言模型:已生成的 token 序列(及当前问题)视为状态
    • 传统 RL:环境观测到的向量或特征
  • 动作 \(a\)
    • 语言模型:在词表 vocabulary 里选下一个 token
    • 机器人/游戏:移动、旋转、跳跃等
  • 奖励 \(r\)
    • 衡量好坏的指标。语言模型对齐常用奖励模型打分,或规则判断
  • 策略 \(\pi\)
    • 智能体在状态 \(s\) 下如何选动作 \(a\) 的概率分布 \(\pi(a|s)\)
    • 语言模型即产生每个 token 的条件分布

价值函数与优势函数:为什么需要它们

PPO 等典型策略梯度方法中,通常引入价值函数(Value Function),表示当前状态下未来期望奖励;进一步有优势函数(Advantage Function),衡量“这个动作比平均水平好多少”。

为什么要引入价值/优势函数?

  • 只有奖励直接指引时,样本方差大,收敛慢
  • 价值函数可降低训练方差,提升效率

从传统方法到近端策略优化(PPO)的发展脉络

策略梯度与Actor-Critic范式

  • 策略梯度方法(Policy Gradient):直接对策略函数 \(\pi_\theta(a|s)\) 建模,最大化期望回报
    • 不用枚举所有状态-动作组合
    • 适应高维连续动作空间,策略表示灵活
  • REINFORCE 等方法方差大、不稳定
  • Actor-Critic 框架
    • “策略”叫 Actor,“价值函数”叫 Critic
    • Critic 估计价值,降低方差

PPO 的核心思路:clip 与优势函数

  • PPO 在 Actor-Critic 基础上,引入剪切(clip)技巧,防止策略更新过猛
  • 需要每步有价值网络估计优势函数
  • 广义优势估计(GAE)常用以降低方差

PPO 局限性:

  • 大模型时代,价值网络需与策略网络同规模,内存与计算消耗巨大
  • RLHF 等场景需奖励模型、价值网络、策略模型,算力负担重

img

GRPO(分组相对策略优化)是什么?

GRPO 提出的动机:为何需要它

  • PPO 痛点:大模型时代,价值网络开销大
  • GRPO(Group Relative Policy Optimization):用“分组输出相互比较”估计基线,免去价值网络

核心动机:

  • 许多实际应用奖励只在序列末端给分(Outcome Supervision),或每步给局部分数(Process Supervision)
  • 奖励离散稀疏,价值网络不划算
  • 同一问题 \(q\) 采样多份输出 \(o_1, o_2, ..., o_G\),奖励对比,推断优劣
  • 对每个输出所有 token 做相对评分,无需显式价值函数

GRPO 的关键点一:分组采样与相对奖励

  • 分组采样:每个问题 \(q\),采样 \(G\) 份输出 \(o_1, ..., o_G\)
  • 奖励归一化

    \[\mathbf{r} = \{r_1, r_2, ..., r_G\} \]

    做归一化(减均值除标准差),得分组内相对奖励 \(\tilde{r}_i\)
  • 相对奖励赋值:每个输出所有 token 共享 \(\tilde{r}_i\),作为优势函数

GRPO 的关键点二:无需价值网络的高效策略优化

  • 不需拟合每个 token 的价值函数,节省内存与计算
  • 代价:每个问题需采样多份输出,推理开销增加
  • 与“自洽性采样(Self-consistency)”思路类似

PPO 的核心目标函数回顾

PPO 简化目标函数:

\[\mathcal{J}^{\mathrm{PPO}}(\theta) = \mathbb{E}_{q \sim P(Q),\, o \sim \pi_{\theta_{\mathrm{old}}}(O \mid q)} \left[ \frac{1}{\|o\|} \sum_{t=1}^{\|o\|} \frac{\pi_{\theta}(o_t \mid q, o_{<t})}{\pi_{\theta_{\mathrm{old}}}(o_t \mid q, o_{<t})} \, A_t \right] \]

  • \(q\):训练集问题分布采样
  • \(o\):旧策略生成的输出序列
  • \(\|o\|\):输出序列长度
  • \(A_t\):优势函数,需单独价值网络估计

GRPO 的目标函数

  • 同样从问题分布采样 \(q\),但对同一 \(q\) 采样一组输出 \(\{o_1, ..., o_G\}\)
  • 每个输出 \(o_i\) 打分 \(r_i\),相对化后作为所有 token 的优势
  • 目标函数类似 PPO,但“价值函数”被分组相对奖励替代

GRPO 目标函数:

\[\mathcal{J}^{\mathrm{GRPO}}(\theta) = \mathbb{E} \left[ \frac{1}{G} \sum_{i=1}^{G} \frac{1}{\|o_i\|} \sum_{t=1}^{\|o_i\|} \min\left( r_{\mathrm{ratio}},\, \operatorname{clip}(r_{\mathrm{ratio}},\, 1-\varepsilon,\, 1+\varepsilon) \right) \cdot \hat{A}_{i,t} \right] - \text{(KL 正则项)} \]

  • \(r_{\mathrm{ratio}} = \frac{\pi_{\theta}(o_{i,t}\mid q, o_{i,<t})}{\pi_{\theta_{\mathrm{old}}}(o_{i,t}\mid q, o_{i,<t})}\)
  • \(\hat{A}_{i,t}\):分组相对优势
  • KL 正则:限制策略与参考策略差异

分组得分与基线估计

\(\hat{A}_{i,t}\) 到底怎么来?就是分组相对奖励:我们先把每个 \(o_i\) 的奖励 \(r_i\) 做如下归一化:

\[\tilde{r}_i = \frac{r_i - \mathrm{mean}(\mathbf{r})}{\mathrm{std}(\mathbf{r})} \]

然后令

\[\hat{A}_{i,t} = \tilde{r}_i \]

也就是说,输出 \(o_i\) 的所有 token 共享同一个分数 \(\tilde{r}_i\)。它们的好坏相对于该分组内的平均水平来衡量,而不依赖外部价值网络去“拆分”或“插值”。这样我们就得到了一个无价值网络的优势函数,核心思路就是基于相互间的比较与排序。

过程监督(process supervision):推理过程每步打分,每步有局部奖励,可累加或折算成与 token 对应的优势。


一步步理解损失函数

PPO/GRPO 都可视为一种“Actor 优化”过程,每个 token 的梯度大致如下:

\[\nabla_{\theta} \mathcal{J}(\theta) = \mathbb{E}\left[ (\text{gradient coefficient}) \cdot \nabla_{\theta} \log \pi_{\theta}(o_t \mid q, o_{<t}) \right] \]

  • PPO:gradient coefficient 含优势 \(A_t\) 及 ratio
  • GRPO:gradient coefficient 变为分组奖励为基础的值
  • GRPO 是 PPO 的变体,同样维持 ratio 范式,只是优势函数来自分组内相对奖励,而非价值网络

惩罚项与 KL 正则

PPO 中常见的 KL 惩罚手段或者 clipping 手段,在 GRPO 中都可以保留,以避免训练过程中的策略分布出现暴走。当然,也有一些更精细的做法,比如把 per-token KL 正则直接加到损失中,而不是只在奖励函数 \(r\) 里扣一个 \(\beta \cdot \log \frac{\pi_\theta}{\pi_{\mathrm{ref}}}\)。这在各家实现时略有不同,但思路都类似。


实例讲解:如何用 GRPO 来解决一个简单问题

有了上文的理论基础后,下面通过一个简化实例,帮助你把 GRPO 的实施逻辑走一遍。从最基本的样本生成到分组打分再到反向传播,都捋顺顺。

实验场景与环境:示例说明

假设有一个文本对话场景:系统给定一个问题 \(q\),模型需要给出回答 \(o\)。我们有一个奖励模型来判断回答的好坏(比如回答是否准确、是否违反某些安全规范等),返回一个数值分 \(r\)。为简单起见,先考虑结果监督(Outcome Supervision)的情境。

在这个设定下,每个问题 \(q\) 提供的“回合”只有一次——即输出一段文本 \(o\),即可拿到一个终端奖励 \(r\)。要做 GRPO,我们至少要对同一个 \(q\) 生成 \(G\) 条回复 \(o_1, o_2, ..., o_G\)


过程监督 VS 结果监督

  • 结果监督(Outcome Supervision):只有输出序列结束才打一个奖励,如回答对/错、得分多少。GRPO 则把这个 \(r\) 同样分配给序列里每个 token。
  • 过程监督(Process Supervision):对中间推理步骤也有打分(比如计算正确一步就+1,错误一步就-1)。那就得收集多个时刻的奖励,然后累加到每个 token 或步骤上,再做分组相对化。

在绝大多数简单场景下,初学者往往更容易先实现结果监督的版本,这也正好方便讲解 GRPO 的主干思路。


分组采样的实现:batch 内如何分组?

在实际操作中,我们往往会在一个 batch 中包含若干个问题 \(q\),对每个问题生成 \(G\) 个答案。也就是说 batch 大小为 \(B\),每个问题生成 \(G\) 个候选,那么一次前向推理要生成 \(B \times G\) 条候选。然后,每个候选都送奖励模型 \(\mathrm{RM}\) 得到分数 \(r_i\)。注意这样做推理开销不小,如果 \(G\) 较大,会显著增加生成次数,但换来的好处是,我们不再需要价值网络了。


实际伪代码示例

以结果监督为例,给出一个简化版伪代码,帮助理解 GRPO 的操作流程。假设 \(\pi_\theta\) 是当前策略模型,\(\pi_{\text{ref}}\) 是参考模型(一般初始可设为和 \(\pi_\theta\) 同一个拷贝,用于算 KL 正则),\(\mathrm{RM}\) 是奖励模型。

# 请注意这只是简化的示例,忽略了各种超参数细节
# GPRO 伪代码 (结果监督)

for iteration in range(N_iterations):
    # 1) 设置参考模型 pi_ref <- pi_theta
    pi_ref = clone(pi_theta)

    for step in range(M_steps_per_iter):
        # 2) 从训练集中取一批问题 D_b
        D_b = sample_batch(train_dataset, batch_size=B)

        # 3) 让旧策略 pi_theta 生成 G 个输出
        #    o_i 表示第 i 个候选答案
        batch_outs = []
        for q in D_b:
            outs_for_q = []
            for i in range(G):
                o_i = sample(pi_theta, q)
                outs_for_q.append(o_i)
            batch_outs.append(outs_for_q)

        # 4) 对每个输出用奖励模型 RM 打分
        #    r_i = RM(q, o_i)
        #    同时做分组归一化
        #    r_i_tilde = (r_i - mean(r)) / std(r)
        #    赋值给 A_i (整条序列的优势)

        # 这里只是一种写法:对 batch 内每个 q 都做
        for outs_for_q in batch_outs:
            # outs_for_q 大小是 G
            r_list = [RM(q, o_i) for o_i in outs_for_q]
            mean_r = mean(r_list)
            std_r = std(r_list)
            if std_r == 0: std_r = 1e-8  # 避免除0

            for i, o_i in enumerate(outs_for_q):
                r_tilde = (r_list[i] - mean_r) / std_r
                # 把这个 r_tilde 记为 A(o_i) 用于后续计算
                # 也可以存在某个 data structure 里

        # 5) 根据 GPRO 目标函数做梯度更新
        #    关键是每个 token 的优势都用 A(o_i)
        #    并加上 KL 正则
        loss = compute_gpro_loss(pi_theta, pi_ref, batch_outs, r_tilde_values)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

在这个伪代码中,最关键的部分是:对每个问题采样 \(G\) 个输出,分别用奖励模型打分,然后在该分组内做归一化。每个输出 \(o_i\) 的所有 token 共享同一个优势值 \(\hat{A}_{i,t} = \tilde{r}_i\),即

\[\hat{A}_{i,t} = \tilde{r}_i = \frac{r_i - \mathrm{mean}(\mathbf{r})}{\mathrm{std}(\mathbf{r})} \]

然后像 PPO 一样,使用 ratio + clip 的方式进行梯度更新。

这样就完成了结果监督版本的 GRPO 训练循环。与 PPO 相比,主要区别在于:不再需要大型的价值网络来估计优势,而是通过分组对比直接获得相对优势。


GRPO的高级实践

迭代式强化学习:奖励模型的更新与回放机制

在实际用 GRPO 的时候,如果你的奖励模型 RM 也是学习得来的,那么你就会发现:当策略模型变强时,RM 所得到的训练样本分布会越来越“难”,这时 RM 自身也需要更新。这样就会出现迭代强化学习流程:先用当前 RM 来指导一轮策略更新,然后再用新策略生成的数据来更新 RM。为了避免灾难性遗忘,可以保留一部分旧数据(回放机制 replay buffer),让 RM 每次都在新旧数据上共同训练,这样 RM 不会完全忘记之前的问题特征。


与RFT、DPO、PPO的比较与兼容

如果你关注过 RFT(Rejection Sampling Fine-tuning)或 DPO(Direct Preference Optimization)等方法,可能会好奇,GRPO 跟它们的差别在哪?

  • RFT:基于同一个模型生成多条输出,再筛选掉错误或低质量的,用剩下的做微调。它不显式区分好答案与更好答案的幅度差异,也不怎么更新策略分布对错的概率比例;可以算是比 GRPO 简单但也少了细粒度的奖励。
  • DPO:需要成对比较 o+、o-,然后做一个 pairwise 的对比损失,和 GRPO 的思路也有差异;DPO 主要是无需单独的 RL 优化器,但它也需要 pairwise preference 的训练集。
  • PPO:最常见的 RL 算法,需要维护价值网络 Critic;而 GRPO 则尝试通过分组相对奖励来免去价值网络的需求。

在实际应用中,你或许会发现 GRPO 和 PPO 并不是对立关系。相反,如果你想在某些场景继续用价值网络的估计,或者想把“分组相对奖励”与“价值函数估计”结合起来,也能做成一个“混合式”的算法。


如何在大模型中使用GRPO:大模型的内存与计算优化

  • 内存节省:因为不用价值网络了,一下子省下了你那数十亿参数的 Critic;即使你要训练奖励模型 RM,也通常比 Critic 规模小得多,因为后者常常要和 Actor 同规模。
  • 计算优化:采样 GRPO 条输出会增加推理开销,但假如你本来就打算做多样性生成或自洽采样,那么这点代价在许多场景可以接受。并且在实践中,你可以对 GRPO 做折中:不一定要搞到 64、128 这样大的值,有时候 8、16 就能提供足够稳定的信号了。

GRPO在数学推理中的应用

聊到这里,你或许会问,“为什么在数学推理上特别推荐 GRPO?” 答案是:数学推理往往需要对问题进行多次思考和尝试,然后选出最优或最正确的答案;在强化学习调优模型时,如果采用 PPO,需要把每一步的中间推理步骤都拟合一个价值,这个操作很昂贵且可能噪声很大。而 GRPO 只需在每个问题末端对最终答案进行打分,并做分组对比即可。


DeepSeekMath的背景:数学预训练与指令微调

以DeepSeekMath为例,这是一个在大规模数学数据(包括 Common Crawl 中挖掘到的 1200 亿数学相关 tokens)上持续训练的 7B 参数级大模型,然后再用数学指令微调(CoT、PoT 等)进行强化。最终它在竞赛级别的 MATH 数据集上超过了 50% 的准确率,且优于众多同类开源模型。DeepSeekMath 在最后一步引入了强化学习,其中就包含了类似 GRPO 的思路:使用分组对比来为每个采样解答分配一个相对奖励。


为什么GRPO能帮助数学推理的性能提升

  • 多候选比较:数学题经常一题多解,或者一题多错。对同一个题目生成多条解答,然后让奖励模型或规则判断优劣,就能充分地分辨出优质解答,策略更新也更“有的放矢”。
  • 减少价值网络负担:数学推理往往需要对中间推理步骤给出价值估计,训练一个庞大的 Critic 不但开销大,还不一定效果好;分组相对法则让我们可以只在最终结论处打分就足够了。
  • 更快收敛:当你一次就能对同一个题目生成多条回答,批量比较之后再更新策略,训练效率比采样单条回答要高一些。

领域内与领域外的表现:GSM8K与MATH只是起点

在 DeepSeekMath 或其他类似模型的实验中,常见的领域内任务就是GSM8K(小学奥数题库)与MATH(高难度竞赛级别数学题),这二者在训练集中已经比较常见,或在微调数据中直接出现。当我们用 GRPO 对这些任务做强化学习时,能大幅提升Top1 准确率,还能间接提升Maj@K(使用多候选投票后的准确率)。更有趣的是,某些领域外(Out-of-domain)任务也常能收益于这种训练,因为分组对比的过程让模型的输出分布“更稳健”,不再容易随机输出无关或浅层错误。


常见问题答疑 (FAQ)

GRPO和PPO的训练时长、资源对比?

在传统 PPO 里,你需要并行维护一个与 Actor 模型同规模的价值网络,并且对每条生成序列都要算价值函数,还要做 backprop。在 GRPO 里,取而代之的是多生成的开销(分组 GRPO 条)。至于哪种方式更省资源,要看你把 GRPO 设多大,还要看你的价值网络是否可以小规模近似。整体上,如果 GRPO 取一个适中的值,GRPO 通常能省掉 Critic 带来的大规模网络开销,显著降低内存占用,所以对大模型场景往往非常划算。

如果奖励模型精度不高会怎样?

如果奖励模型 RM 本身不靠谱,GRPO 就会有问题,因为它基本完全依赖 RM 的评分来区分哪个输出好。可以把不靠谱的奖励模型理解成 PPO 里一个“瞎子价值网络”,一样会引导错误。所以说在 RLHF 场景下,如何拿到足够精确的奖励模型是关键。如果 RM 噪声很大,你至少要做大量分组采样(让噪声平均化),或引入一些更新机制来持续标注数据纠偏。

能不能只做离线版本的GRPO?

可以——如果你已经有一个固定的语料,其中包含每个问题的多个候选回答,以及相应的奖励分,甚至比较关系,那就可以做一个离线版的 GRPO:直接把这些现成分组拿来计算目标函数更新策略。不过,这和在线的强化学习还是有区别,在线能不断地探索新的输出,离线则只能在已有数据里学习。如果你的数据够丰富,离线也能取得不错的效果,称之为Offline RL思路。但若你想持续提升模型性能,在线还是更灵活。

posted @ 2025-05-27 14:49  一介布衣、  阅读(546)  评论(0)    收藏  举报