随着 DeepSeek 的 R1-Zero 等工作展示出大规模强化学习在提升语言模型复杂推理能力上的潜力,研究社区对于其背后所使用的强化学习算法——组相对策略优化(Group Relative Policy Optimization, GRPO)的兴趣日益浓厚。GRPO 是对近端策略优化(Proximal Policy Optimization, PPO)框架的改进,其引入了两项核心创新:

  • 其一,GRPO 摒弃了传统的 Critic 模型,转而利用组内样本的平均得分来估计价值基线(Value Baseline)。
  • 其二,GRPO 未将 KL(Kullback-Leibler)散度作为惩罚项添加到奖励函数中,而是直接将其作为正则化项加入到损失函数里,以此来约束学习策略与参考策略之间的差异。

然而,一个关键且往往被忽视的实现细节在于,将奖励函数中的 KL 惩罚项直接迁移为损失函数中的正则化项,并非一个简单的等价转换,尤其是在 off-policy 设定下。这一转换要求 KL 散度的计算必须在整个词汇表(Vocabulary)的维度上进行,而不是仅仅依赖于采样轨迹(Sampled Trajectory)中的词元。

本文旨在深入剖析这一关键细节,阐明为何在 GRPO 中 KL 散度的计算方式至关重要,并探讨其对算法实现和性能可能带来的影响。

理论回顾:从 PPO 到 GRPO

为了清晰地揭示这一实现细节的重要性,我们首先需要回顾 PPO 和 GRPO 这两种算法的理论基础,并辨析它们之间的差异。

PPO 与 GRPO 的损失函数

PPO 的目标函数通常定义为:

其中, 是当前需要优化的策略, 是用于采样数据的旧策略, 是优势函数(Advantage Function)的估计。

而根据相关论文,GRPO 的损失函数则表示为:

与 PPO 的形式类似,核心区别在于 GRPO 明确地在损失函数中加入了一个 KL 散度正则化项 ,其中 通常是一个固定的参考策略(例如,初始的 SFT 模型)。

一个值得注意的共同点是,无论是 PPO 还是 GRPO,其训练样本(即 query-response 对)都是从一个固定的旧策略 中采样的。这意味着两种算法本质上都属于off-policy学习的范畴,因为用于生成数据的策略()与需要被优化的策略()是不同的。这个 off-policy 的特性是理解后续讨论的关键。

KL 散度项在两种算法中的不同角色

现在,我们来仔细审视 KL 散度在 PPO 和 GRPO 中扮演的不同角色。

在 PPO 的典型实现中,为了防止学习策略 偏离旧策略 过远,通常会将一个与 KL 散度相关的惩罚项加入到奖励函数中。修改后的奖励函数可以表示为:

需要注意的是,这里的 KL 散度是计算旧策略 与参考策略 之间的距离。在计算 PPO 的损失函数梯度时,这个奖励项 是一个常数,因为它不依赖于我们正在优化的参数 。在代码实现中,这相当于对奖励值执行了 detach() 操作,使其不参与梯度反向传播。

而在 GRPO 中,KL 散度被直接作为正则化项加入到损失函数中,其形式为 。这里的 KL 散度计算的是当前策略 与参考策略 之间的距离。由于 是与优化参数 直接相关的,因此这一项在计算梯度时不能被忽略,它会直接对参数更新产生影响。

梯度的计算:REINFORCE 算法的启示

为了更深入地理解为何 的存在会带来本质区别,我们需要回顾一下强化学习中最基础的策略梯度算法之一:REINFORCE。

REINFORCE 算法的目标函数非常简单,即最大化期望奖励:

其梯度可以表示为:

由于期望的计算涉及到对 的积分或求和,直接对期望求导是困难的,尤其是当奖励函数 是不可微的或者是一个黑盒时。为了解决这个问题,REINFORCE 算法使用了所谓的“对数-导数技巧”(Log-derivative Trick),也称为得分函数估计器(Score Function Estimator):

利用这个技巧,REINFORCE 损失函数的梯度可以重写为:

这个形式将梯度的计算转化为了一个可以通过从 采样来估计的期望值。梯度计算的核心落在了对数概率项 上。

GRPO 中 KL 散度梯度的关键

现在我们将这个思路应用到 GRPO 的 KL 散度项上。在 GRPO 的上下文中,KL 散度以期望的形式表达为:

当我们需要计算 GRPO 损失函数关于 的梯度时,除了类似 PPO 的优势加权策略比率项之外,还需要对这个 KL 散度项求导。由于 KL 散度项中包含了与 显式相关的 ,其梯度计算就变得至关重要:

  • 分布的微分:由于 是一个覆盖所有可能动作(在语言模型中即整个词汇表)的概率分布,参数 的任何微小变化都会引起整个概率分布的重新分配。因此,在求导时必须考虑到这种全局性的变化。
  • 梯度的传播:KL 散度项中的 会直接参与梯度计算,并将梯度反向传播到策略网络的参数上。这与 PPO 中奖励项被 detach 的情况完全不同,在 PPO 中,奖励信号本身不直接提供关于如何调整策略参数的梯度信息。

回到实现:为何不能简单复用 PPO 的 KL 计算方式?

在许多开源的 PPO 实现中(例如 OpenRLHF 的代码库),用于计算奖励惩罚的 KL 散度通常采用一种近似计算方法。例如,compute_approx_kl 函数的输入 log_probslog_probs_base 的形状通常是 [batch_size, response_length]

def compute_approx_kl(
    log_probs: torch.Tensor,
    log_probs_base: torch.Tensor,
    kl_estimator: str = "k1",
)
 -> torch.Tensor:

    """
    Compute the approximate KL divergence between two distributions.
    Schulman blog: http://joschu.net/blog/kl-approx.html

    Args:
        log_probs: Log probabilities of the new distribution.
        log_probs_base: Log probabilities of the base distribution.
    """


    if kl_estimator == "k1":
        log_ratio = log_probs.float() - log_probs_base.float()

    # The k2 estimator is the non negative kl approximation in
    # http://joschu.net/blog/kl-approx.html
    # The k2_loss is approximately equivalent to the
    # one-step KL divergence penalty with the k1 estimator
    # used in https://arxiv.org/pdf/2310.10505.
    if kl_estimator == "k2":
        log_ratio = log_probs.float() - log_probs_base.float()
        log_ratio = log_ratio**2 / 2.0

    # The k3 estimator is the non negative kl approximation in
    # http://joschu.net/blog/kl-approx.html
    if kl_estimator == "k3":
        log_ratio = log_probs.float() - log_probs_base.float()
        log_ratio = -log_ratio
        log_ratio = log_ratio.exp() - 1 - log_ratio

    log_ratio = log_ratio.clamp(min=-10, max=10)
    return log_ratio

这种计算方式仅仅利用了采样轨迹中出现的词元的对数概率,通过计算它们之间的差值来近似 KL 散度。之所以在 PPO 中可以这样做,是因为我们回顾 PPO 的奖励函数 ,以及简化后的损失项 。由于期望是基于固定的旧策略 来计算的,并且该期望的计算与我们需要优化的参数 无关,因此我们可以将 KL 散度的期望展开:

在这种情况下,我们只需要计算在 采样出的轨迹上, 的对数概率之差的期望即可。这解释了为什么在 PPO 的实现中可以省略 vocab_size 维度。

然而,在 GRPO 中,情况发生了根本性的变化。当 KL 散度项 作为损失函数的一部分时,它的期望是基于当前策略 计算的:

由于我们是 off-policy 学习,我们的采样是基于 进行的,而不是 。如果我们错误地复用 PPO 中的 compute_approx_kl 函数,并代入当前策略的对数概率 log_probs(来自 )和参考策略的对数概率 log_probs_ref(来自 ),然后用从 采样的轨迹来估计这个期望,我们实际上计算的是:

这个表达式丢失了关于 分布本身的信息,从而导致梯度计算错误,因为我们忽略了对 求期望这一环节。正确的梯度推导应该是:

这个梯度的正确估计,要求我们或者在当前策略 上进行在线采样,或者使用重要性采样等技术进行修正。简单地在从 采样的轨迹上计算对数概率差,是无法得到正确梯度的。

因此,在 GRPO 中,为了正确计算 KL 散度及其梯度,必须在整个词汇表的维度上进行操作,即需要计算完整的概率分布 ,而不能仅仅局限于采样到的词元。

解决之道

三种可能的解决方案:

  1. 遵循传统的 KL 散度计算方式
    这意味着需要显式地计算完整的 KL 散度。这通常需要模型为每个词元输出一个覆盖整个词汇表的 logits 向量,然后计算两个概率分布之间的 KL 散度。这种方法的计算开销巨大,尤其是在词汇表非常大的情况下,可能导致其在实践中不可行。
    一种可能的近似方法是采用知识蒸馏中常用的技巧,例如只对 top-K 的 logits 计算 KL 散度,但这是否为强化学习场景下的最优选择,仍有待验证。

  2. 仅使用 on-policy (On-policy)版本的 GRPO(DeepSeek-Math 的做法)
    如果我们将 GRPO 严格限制在 on-policy 的设定下,即每次更新参数后,都使用新的策略 来重新采样数据,那么 就等于 。在这种情况下,损失函数的期望和采样都来自于同一个分布 ,之前提到的近似计算就变得合理了。
    当 GRPO 是 on-policy 时,损失函数可以被重写为:

    其梯度为:

    这与之前不正确的近似形式在结构上是匹配的。然而,纯粹的o n学习通常意味着较低的样本效率,因为每次迭代都需要抛弃旧数据并重新采样。

  3. 将 KL 散度项移回奖励函数
    这或许是最具实践意义的方案。在 off-policy 的设定下,我们可以将 KL 散度项重新放回到奖励函数中,使其退化为 PPO 的形式。这样,KL 散度计算的是 之间的距离,从而避免了对 求导的复杂性。
    这种做法在计算上更为高效,因为它允许我们复用 PPO 中成熟且计算开销较小的近似 KL 散度计算方法。从本质上看,这种 off-policy 的 GRPO 实现,在核心的损失函数梯度计算上,与带有重要性采样的 PPO 算法是等价的。

    尽管如此,损失函数的形式变得相似,但 GRPO 的一个关键特性仍然与 PPO 不同:GRPO 要求每个词元级别的奖励是轨迹级别的总奖励减去该词元级别的 KL 散度。这与 PPO 中通常使用的词元级别奖励加上(可能是)轨迹级别的 KL 惩罚项有所区别。

GRPO 的核心要点与潜在影响

通过上述分析,我们可以总结出 GRPO 的几个核心要点:

  • 组优势归一化:GRPO 使用组内样本的统计特性(均值和标准差)来归一化优势函数,这在某些基于规则的奖励设定下可能比 PPO 在整个回放缓冲区(Replay Buffer)上进行归一化表现更好。
  • on-policy vs. off-policy 的实现差异:对于 on-policy 版本的 GRPO,直接使用近似的 KL 损失实现是可行的。但对于更具挑战性也更具样本效率的 off-policy 版本,将 KL 项放回奖励函数是更为实际和高效的选择,尽管这使其在形式上接近 PPO。
  • 词元奖励的构成:GRPO 的 off-policy 版本在奖励构成上与 PPO 存在差异,这可能是两者性能表现不同的一个潜在来源。

虽然在当前的实践中,许多强化学习算法(尤其是在训练推理能力 LLM 时)都倾向于接近 on-policy 的设定,因此这个问题的影响可能不显著。但是,随着 GRPO 被应用到更广泛的、更需要 off-policy 学习的场景中,这个 KL 散度的计算差异可能会变得愈发重要。

目前,由于在开源社区中,一个完全遵循其原始定义的、真正的 off-policy GRPO 实现尚未出现,并且三种不同的实现方式也未经过充分的实验对比,因此,不同 GRPO 实现方式之间的区别,以及 GRPO 与 PPO 之间的性能差异,仍然是一个值得持续讨论和研究的课题。

结语

本文深入剖析了在 GRPO 算法中 KL 散度实现的一个关键但易被忽视的细节。通过对比其与 PPO 的异同,并借助 REINFORCE 算法的理论视角,我们阐明了为何在 off-policy 设定下,不能简单地将 PPO 中用于奖励计算的 KL 散度近似方法直接迁移到 GRPO 的损失函数中。正确的处理方式要求对整个词汇表进行计算,但这在计算上是昂贵的。

文章探讨了三种可行的解决方案:遵循传统但昂贵的计算方式、退回到样本效率较低的 on-policy 版本,或者将其改写回类似 PPO 的形式。对于希望在大模型上应用或研究 GRPO 的研究人员来说,理解这一细节对于正确实现算法、分析实验结果以及探索未来改进方向都具有重要的指导意义。在强化学习算法的工程实践中,正是这些看似细微的理论和实现差异,最终决定了模型的训练稳定性和最终性能。

参考文献

[1] Guo D, Yang D, Zhang H, et al. Deepseek-r1: Incentivizing reasoning capability in llms via reinforcement learning[J]. arXiv preprint arXiv:2501.12948, 2025.

[2] Shao Z, Wang P, Zhu Q, et al. Deepseekmath: Pushing the limits of mathematical reasoning in open language models[J]. arXiv preprint arXiv:2402.03300, 2024.

[3] R. J. Williams. Simple statistical gradient-following algorithms for connectionist reinforcement learning. Machine Learning, 8:5–32, 1992.

[4] Mohamed S, Rosca M, Figurnov M, et al. Monte carlo gradient estimation in machine learning[J]. Journal of Machine Learning Research, 2020, 21(132): 1-62.

[5] https://github.com/OpenRLHF/OpenRLHF/tree/main

[6] http://joschu.net/blog/kl-approx.html

[7] https://hongyuzang.notion.site/The-critical-implementation-detail-of-KL-Loss-in-GRPO-1ae3fe2c1ff9809a9307c5402e190373

[8] https://newfacade.github.io/notes-on-reinforcement-learning/15-ppo.html

[9] https://github.com/volcengine/verl

[10] https://github.com/huggingface/trl/tree/main


往期文章: