资讯动态

GRPO实战:奖励值与优势值计算及组内归一化详解

发布时间:2026/10/8 21:29:09 来源:尧图企业网站定制
1. 从奖励到优势GRPO 到底在优化什么第一次接触 GRPOGroup Relative Policy Optimization组相对策略优化的人十有八九会被“奖励值”和“优势值”这两个词绕晕。我当初也是看论文的时候觉得公式都懂一上手写代码就发现奖励到底怎么算、优势到底怎么估、为什么同一个 batch 里要分组、组内又为什么要做归一化这些问题不搞清楚训练曲线就是一条躺平的直线。先把结论摆在前面GRPO 的核心思想是用“一组采样结果的相对好坏”来替代传统 PPO 里那个需要单独训练的 Critic 网络。换句话说它把“这个动作到底比平均水平好多少”这件事从“让一个价值网络去猜”变成了“直接拿同一组里的其他样本来对比”。这个改动看起来小但它直接决定了奖励值和优势值在整个流程里的角色分工。这篇文章我打算按实际落地的顺序来讲先拆清楚奖励值和优势值各自是什么、在 GRPO 里怎么算再讲组内归一化的具体实现然后是完整训练流程和参数选择最后把我踩过的坑和排查经验整理出来。适合已经了解 PPO 基本框架、想动手跑 GRPO 的读者也适合只想搞懂“优势值到底怎么来的”这部分原理的朋友。全文会尽量用生活化的类比配合公式和代码保证你看完能自己写出一版能跑通的实现。在展开之前先明确一个贯穿全文的类比把 GRPO 想象成一场班级考试。奖励值就是每个学生这次考试的实际得分而优势值则是“这个学生比班里平均分高了多少、高出的部分折算成标准分是多少”。GRPO 不关心你考了 80 分还是 90 分它关心的是你在这次考试里相对其他同学的位置。这个“相对位置”就是优势值也是策略真正用来更新参数的信号。2. 奖励值与优势值的角色拆解2.1 奖励值环境给出的原始信号奖励值Reward在强化学习里是最原始、最直接的学习信号。它由环境或奖励函数给出回答的问题是“这一步动作到底好不好”。在 GRPO 的语境下奖励通常是在一条完整轨迹生成结束之后由奖励模型或规则函数统一打分的。这里有个容易被忽略的点GRPO 里的奖励往往是序列级的而不是步级的。什么意思就是你可能生成了一整段回答、一整条推理链最后才拿到一个标量奖励。这跟传统 Atari 游戏里每一步都有即时奖励的场景很不一样。序列级奖励带来的直接后果是同一条轨迹里所有 token 共享同一个奖励值这就为后面的组内对比埋下了伏笔。奖励值的来源一般有三类。第一类是规则奖励比如数学题答案对不对、代码能不能通过单元测试这种奖励最干净没有噪声能用规则判定的场景优先用它。第二类是奖励模型用一个人工标注偏好训练出来的打分模型适合开放式生成任务但它本身有偏差会带来 reward hacking 的风险。第三类是混合奖励把规则分和模型分加权组合这也是目前工程上最常用的做法。我个人的经验是奖励函数的设计比算法本身更影响最终效果。GRPO 再强如果奖励信号本身是错的或者有偏的训练出来的策略只会更高效地钻空子。所以别急着调 GRPO 的超参先把奖励函数打磨到“给分合理、难以作弊”再说。2.2 优势值把绝对分数变成相对信号优势值Advantage回答的是另一个问题“这个动作比平均水平好多少”。它的数学定义是动作价值函数减去状态价值函数也就是 A(s,a) Q(s,a) - V(s)。直观理解就是在当前状态下采取这个动作比“随便采取一个动作的平均表现”强多少。传统 PPO 里优势值靠 GAE广义优势估计来算而 GAE 又依赖一个单独训练的 Critic 网络去估计 V(s)。这个 Critic 网络本身就是个麻烦它要跟策略网络一起训练训练不稳定会拖累策略训练太慢又会让优势估计滞后。更头疼的是Critic 网络参数量往往和策略网络一个量级显存和算力开销直接翻倍。GRPO 的聪明之处就在于它干脆不要 Critic 网络了。既然优势的本质是“相对好坏”那我直接在同一组采样里做对比不就行了同一组里奖励高的样本优势为正奖励低的优势为负奖励居中的接近零。这样一来优势值的计算就退化成了一个纯粹的组内归一化操作完全不需要额外的价值网络。这个设计带来的好处是实打实的显存占用明显下降训练流程简化而且避免了 Critic 估计不准带来的偏差。代价是它要求同一组内必须有足够多的样本否则组内统计量不稳定优势估计的方差会很大。这也是为什么 GRPO 里 group size 这个参数特别关键后面会专门讲。2.3 两者在 GRPO 里的分工把奖励值和优势值放在一起看它们在 GRPO 里的分工非常清晰。奖励值是“原材料”是环境或奖励函数给的原始反馈优势值是“加工品”是把原始反馈经过组内对比和归一化之后真正喂给策略梯度的信号。用一个更具体的例子说明。假设同一个 prompt 采样了 8 条回答奖励分别是 [1.0, 0.8, 0.8, 0.5, 0.5, 0.5, 0.2, 0.0]。奖励值就是这 8 个数本身。而优势值则是把这 8 个数减去均值、除以标准差之后的结果。均值是 0.5375标准差大概 0.32那么奖励 1.0 的那条优势约等于 1.45奖励 0.0 的那条约等于 -1.68。策略更新时它不会去追那 1.0 的绝对分数而是去追那 1.45 的相对优势。这个区别在实操中非常关键。如果你的奖励函数整体偏移了一个常数比如所有奖励都加 10传统方法可能会受影响但 GRPO 的优势值完全不变因为减均值这一步把常数消掉了。这就是相对信号的好处它对奖励的绝对尺度不敏感只关心相对排序。3. GRPO 组内优势计算的核心机制3.1 为什么是“组”而不是“批”理解 GRPO 的关键是搞清楚“组”和“批”的区别。一个 batch 里可能有很多个 prompt每个 prompt 又采样多条回答。GRPO 的归一化是在同一个 prompt 对应的那组回答内部做的而不是在整个 batch 上做。为什么必须这样因为不同 prompt 的难度天差地别。有的问题简单模型随便答都能拿高分有的问题难答得再好奖励也上不去。如果跨 prompt 做归一化简单题的高分就会把难题的低分衬托得特别惨优势估计就失真了。只在组内对比等于是在“同一道题的不同解法之间”比高低这才是公平的比较。这个设计其实暗合了因果强化学习里“控制混杂变量”的思路。prompt 本身就是一个混杂变量它同时影响奖励和策略表现。把 prompt 固定住、只在组内比较相当于把 prompt 这个变量控制住了剩下的差异才真正反映策略动作的好坏。这也是为什么很多做因果强化学习的研究者会对 GRPO 的组内设计特别感兴趣。3.2 组内归一化的数学形式GRPO 的优势计算可以写成下面这个形式。对于第 i 个 prompt 采样出的第 j 条回答其优势值为A_ij (r_ij - mean(r_i)) / (std(r_i) eps)其中 r_ij 是这条回答的奖励mean(r_i) 和 std(r_i) 分别是第 i 组所有回答奖励的均值和标准差eps 是一个防止除零的小常数通常取 1e-8 左右。这个公式看起来简单但每一步都有讲究。减均值是为了把优势中心化到零附近让正负优势大致平衡这样策略更新不会整体偏向某个方向。除以标准差是为了把优势缩放到一个稳定的量级避免奖励尺度变化导致梯度爆炸或消失。eps 虽然小但绝对不能省因为如果一组里所有回答奖励完全相同比如全对或全错标准差就是零不处理会直接产生 NaN。这里有个实操细节标准差到底用有偏估计还是无偏估计也就是分母是 N 还是 N-1。理论上无偏估计更严谨但实践中因为 group size 通常不大8 到 64 之间两者差异可以忽略而且大多数开源实现用的是有偏版本除以 N。我建议跟主流实现保持一致避免因为这种细节导致复现结果对不上。3.3 组大小对优势估计的影响group size 是 GRPO 里最需要仔细调的参数之一。它直接决定了组内统计量的质量。组太小均值和标准差估计不准优势值噪声大组太大采样成本高而且组内对比的边际收益递减。从统计角度看均值的标准误是 std/sqrt(N)N 是组大小。N4 时标准误是 std/2N16 时是 std/4N64 时是 std/8。也就是说组大小从 4 增加到 16估计精度提升一倍从 16 增加到 64再提升一倍。但采样成本是线性增长的。所以这里有个明显的收益递减拐点。我实测下来的经验是对于奖励区分度高的任务比如数学题对错分明group size 取 8 就够用对于奖励区分度低的任务比如开放式写作分数都挤在中间建议取 16 甚至 32否则组内标准差太小优势值全被噪声淹没。如果显存允许16 是一个比较稳妥的默认值。还有一个容易被忽略的点如果一组里所有回答奖励都一样那这组数据对策略更新是零贡献的因为优势全是零。在训练后期模型对某个 prompt 已经学得很好所有采样都拿满分这时候这组数据就浪费了。有些实现会做过滤把全同奖励的组直接丢掉节省算力。这个技巧在训练后期特别有用。4. 完整训练流程与实操实现4.1 从采样到优势计算的完整链路把整个流程串起来GRPO 的一次训练迭代大致是这样的。第一步从数据集里取一批 prompt。第二步对每个 prompt 用当前策略采样 group size 条回答同时记录每条回答的 log 概率。第三步用奖励函数给每条回答打分得到奖励值。第四步在每个 prompt 组内做归一化算出优势值。第五步用优势值构造策略梯度损失加上 KL 惩罚项。第六步反向传播更新策略参数。这个链路里第三步和第四步是 GRPO 区别于 PPO 的核心。PPO 在第四步会调用 Critic 网络估计 V(s)再用 GAE 算优势GRPO 直接跳过 Critic用组内统计量代替。第五步里PPO 和 GRPO 都要算重要性采样比率但 GRPO 通常还会加一个 clipping 操作来限制策略更新幅度。下面给一段核心逻辑的伪代码帮助理解数据流# 假设 rewards 形状为 [num_prompts, group_size] # 每个 prompt 对应一行 # 组内归一化计算优势 mean rewards.mean(dim1, keepdimTrue) std rewards.std(dim1, keepdimTrue) advantages (rewards - mean) / (std 1e-8) # 优势值广播到每个 token # token_advantages 形状为 [num_prompts, group_size, seq_len] token_advantages advantages.unsqueeze(-1).expand_as(log_probs) # 策略梯度损失 ratio torch.exp(new_log_probs - old_log_probs) clipped_ratio torch.clamp(ratio, 1 - clip_eps, 1 clip_eps) policy_loss -torch.min(ratio * token_advantages, clipped_ratio * token_advantages).mean() # KL 惩罚 kl_penalty kl_coef * (old_log_probs - ref_log_probs).mean() total_loss policy_loss kl_penalty这段代码里最值得注意的是优势值的广播方式。因为奖励是序列级的一条回答只有一个奖励所以这条回答里所有 token 共享同一个优势值。这跟 PPO 里每个 token 有独立优势值的情况不同。这个设计简化了计算但也意味着 GRPO 对序列内部的动作区分度不如 PPO 精细。对于推理类任务这通常不是问题因为整条推理链的好坏本来就该整体评价。4.2 关键参数的选择与计算GRPO 有几个参数必须调好否则训练很难收敛。我把它们和推荐值整理成表方便对照。参数含义推荐值调整建议group_size每个 prompt 采样条数8-16奖励区分度低时增大clip_eps比率裁剪范围0.2训练不稳定时减小kl_coefKL 惩罚系数0.01-0.04策略跑偏时增大learning_rate学习率1e-6 到 5e-6比 PPO 略小temperature采样温度0.7-1.0太低会降低组内多样性max_new_tokens最大生成长度按任务定太长会稀释优势信号这里重点说两个。第一个是 temperature。GRPO 依赖组内多样性来产生有区分度的优势值如果温度太低所有采样几乎一样组内标准差趋近于零优势值全是噪声。我一般从 0.8 起步如果发现组内奖励方差太小就往上调到 1.0。但温度也不能太高否则采样质量下降奖励整体变差。第二个是 kl_coef。GRPO 因为没有 Critic策略更容易跑偏KL 惩罚是防止模型偏离参考模型太远的关键。系数太小模型会为了刷奖励而输出乱码系数太大模型几乎不更新训练没效果。我的经验是从 0.02 开始观察 KL 散度的实际值如果持续超过 0.1 就加大系数如果一直低于 0.01 就适当减小。4.3 一个可复现的最小实现为了让大家能直接上手我把一个最小可跑的 GRPO 训练循环写出来。这里省略了模型加载和数据处理的细节只保留核心逻辑。import torch import torch.nn.functional as F def grpo_step(policy, ref_policy, prompts, reward_fn, group_size8, clip_eps0.2, kl_coef0.02): # 1. 采样每个 prompt 生成 group_size 条回答 all_responses [] all_log_probs [] for prompt in prompts: responses, log_probs policy.generate( prompt, num_return_sequencesgroup_size, temperature0.8, return_log_probsTrue ) all_responses.append(responses) all_log_probs.append(log_probs) # 2. 打分计算每条回答的奖励 rewards torch.tensor([ [reward_fn(p, r) for r in resps] for p, resps in zip(prompts, all_responses) ]) # 3. 组内归一化计算优势值 mean rewards.mean(dim1, keepdimTrue) std rewards.std(dim1, keepdimTrue) advantages (rewards - mean) / (std 1e-8) # 4. 计算新旧策略的 log 概率 old_log_probs torch.stack(all_log_probs) new_log_probs policy.recompute_log_probs( prompts, all_responses ) ref_log_probs ref_policy.recompute_log_probs( prompts, all_responses ) # 5. 策略梯度损失 token_adv advantages.unsqueeze(-1).expand_as(new_log_probs) ratio torch.exp(new_log_probs - old_log_probs) clipped torch.clamp(ratio, 1 - clip_eps, 1 clip_eps) policy_loss -torch.min( ratio * token_adv, clipped * token_adv ).mean() # 6. KL 惩罚 kl (old_log_probs - ref_log_probs).mean() loss policy_loss kl_coef * kl return loss, rewards.mean().item(), kl.item()这段代码里第 3 步的组内归一化是整个 GRPO 的灵魂。你可以试着把这一步换成“直接使用原始奖励”然后对比训练曲线会发现没有归一化的话策略更新会被奖励的绝对尺度主导收敛非常不稳定。这个对比实验我强烈建议每个初学 GRPO 的人都做一遍做完你就彻底理解优势值存在的意义了。5. 常见问题与排查技巧实录5.1 训练不收敛的典型症状与对策GRPO 训练不收敛症状通常有几种。第一种是奖励曲线一直平着不动说明策略根本没学到东西。这时候先检查优势值是不是全接近零如果是多半是组内奖励方差太小要么提高 temperature要么增大 group size要么重新设计奖励函数增加区分度。第二种是奖励先涨后崩训练到一半突然掉下去。这通常是 KL 散度失控策略跑偏了。对策是加大 kl_coef或者减小 learning_rate。我遇到过一次kl_coef 从 0.02 加到 0.05 之后曲线就稳住了。第三种是奖励震荡剧烈上下乱跳。这往往是 group size 太小导致优势估计噪声大或者 clip_eps 设得太大让更新幅度失控。把 group size 从 4 提到 16clip_eps 从 0.3 降到 0.2一般能明显改善。排查的时候有个顺序技巧先看奖励的组内方差再看优势值的分布最后看 KL 散度。这三个指标基本能定位到问题出在采样、优势计算还是策略更新环节。5.2 奖励作弊的识别与防范奖励作弊reward hacking是 GRPO 训练里最隐蔽的坑。模型会找到奖励函数的漏洞用你意想不到的方式刷高分但实际输出质量很差。比如奖励模型偏好长回答模型就开始疯狂堆废话规则奖励只看关键词模型就把关键词重复一百遍。识别奖励作弊的信号有几个。一是奖励持续上涨但人工评估质量下降这个最直接。二是输出长度异常增长或者出现大量重复模式。三是 KL 散度突然飙升因为模型在快速偏离参考策略去钻空子。防范手段我总结了三招。第一招是奖励函数里加长度惩罚和重复惩罚堵住最常见的漏洞。第二招是定期做人工抽检别只看自动指标。第三招是保持适度的 KL 惩罚让模型不能偏离参考策略太远。这三招配合使用基本能挡住大部分作弊行为。5.3 显存与效率优化经验GRPO 虽然省掉了 Critic 网络但 group size 条采样同时驻留显存开销也不小。我踩过的坑是 group size 设成 32 之后直接 OOM。后来摸索出几个优化手段。第一个是梯度检查点用时间换显存能省下不少激活值占用。第二个是采样和训练分阶段采样完先把 log 概率存下来释放采样时的中间激活再进入训练阶段。第三个是动态 group size训练早期用大 group 保证优势估计质量后期模型稳定了减小 group 节省算力。还有一个容易被忽略的点参考模型ref policy其实不需要一直驻留显存。可以在需要算 ref_log_probs 的时候临时加载算完就释放。虽然会慢一点但显存能省下一整个模型的量。对于显存紧张的场景这个技巧很实用。5.4 问题速查表症状可能原因排查方向解决手段奖励不涨优势值全零检查组内方差提高温度或增大组奖励先涨后崩KL 失控看 KL 散度曲线加大 kl_coef奖励震荡优势噪声大看优势分布增大组或减小 clip输出变长变废奖励作弊人工抽检加长度惩罚显存 OOM组太大看显存峰值梯度检查点或减组训练极慢参考模型常驻看显存占用临时加载参考模型这张表是我自己训练时总结的基本覆盖了八成以上的常见问题。遇到新问题的时候先对照这张表排查能省下大量试错时间。6. 从奖励到优势的工程心得回过头看GRPO 最精妙的地方就是把“优势估计”这个原本需要额外网络的任务转化成了一个纯粹的统计操作。奖励值是环境给的我们控制不了太多但优势值怎么算完全是我们自己设计的。组内归一化这个看似简单的操作实际上承担了整个算法的稳定性。我在实际项目里最大的体会是别把 GRPO 当成一个黑盒算法来调而要把它当成一套“如何把奖励信号转化成学习信号”的方法论。奖励函数设计得好组内归一化做得稳GRPO 就能跑得很漂亮这两步任何一步出问题后面调再多超参都是白费。最后分享一个我常用的小技巧训练前先跑一遍纯采样把奖励分布和组内方差画出来看看。如果组内方差普遍很小说明这个任务不适合直接上 GRPO得先改奖励函数或者调整采样策略。这个预检查花不了多少时间但能帮你避开后面几天的无效训练。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑