尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
GRPO参考实现详解:抛弃价值网络的组内优势强化学习
GRPO 这个名字最近在强化学习圈子里被反复提起尤其是做模型对齐和后训练的那批人几乎人手一份参考实现。原因其实不复杂PPO 一直是强化学习微调语言模型的主力但它需要额外训练一个价值网络来估计 token 级优势训练一个 7B 甚至更大的模型时这个 critic 会吃掉大量显存和算力还会带来价值网络不收敛、优势估计偏差这类问题。GRPO 的思路很直接把价值网络整个扔掉用同一个 prompt 采样出来的一组回答的奖励均值作为基线直接做组内相对优势估计。这样既省了显存也少了一个训练中最大的不稳定来源。这篇文章不会抄论文公式而是以“参考实现”为主线把 GRPO 从采样、奖励计算、组内优势归一化、损失函数、KL 惩罚到训练循环每一步拆开讲并给出一份可以直接改着用的 Python 训练骨架。适合正在做 RLHF 对齐、手头有 SFT 之后的模型、想绕开 PPO 复杂管线去验证奖励假设的团队或个人。如果你只是听过 GRPO 这个名字想搞清楚它和 PPO 到底差在哪从第一节开始看也完全跟得上。1. 理解 GRPO 为什么存在PPO 对齐管线里的那个瓶颈1.1 PPO 为什么需要价值网络先回想一下经典的强化学习对齐流程SFT 模型作为初始策略然后训练一个奖励模型来模拟人类偏好最后用强化学习在这个奖励信号上继续优化策略。PPO 在这个阶段要同时维护四个角色策略模型、价值网络、奖励模型和参考模型。其中价值网络负责预测当前状态也就是已经生成的 token 序列未来能拿到的总奖励是优势函数估计的核心。问题在于语言模型场景下的“状态”是一个变长 token 序列价值网络要精准预测“还没生成的文本到底值多少分”这本身就是一件非常难的事。实践中价值网络经常出现两个毛病一是训练不稳定loss 震荡导致优势估计反复横跳二是它的预测尺度跟真实奖励尺度对不齐比如奖励模型打 2.5 分价值网络却预测未来奖励是 8 分整个训练信号就被带偏了。再加上完整 PPO 管线要配置 GAE 的 lambda、价值 loss 系数、价值剪裁范围等一堆超参调参成本非常高。更现实的一个痛点是资源占用。7B 的 actor 已经不小再放一个 7B 的 critic 进去显存翻倍还不止推理和反向传播的时间也明显拉长。我在早期尝试 PPO 对齐时光是让 critic 稳定收敛就花掉了大量预算真正留给策略学习的精力反而变少了。1.2 GRPO 的核心替换思想GRPO 做的事情是把“用价值网络估计基线”换成“用同一 prompt 的一组采样结果做基线”。具体来说对每个 prompt先从当前策略采样出 G 个回答分别打分得到 G 个奖励然后用这组奖励的均值和标准差做归一化得到每个回答的优势。直观理解这就是“班级内部排名”替代“绝对分数”绝对分数的尺度会随着训练推进不断漂移但组内相对好坏在任意阶段都稳定能告诉模型哪些回答比平均水平好、哪些比平均水平差。这个替代并不只是省一个模型那么简单。价值网络本质上是在学一个回归任务回归模型天然会有误差误差又会被带入策略梯度而 GRPO 的基线是直接从当前采样中算出来的统计量不引入额外的学习误差。虽然组内均值这个估计量的方差比理论上完美的价值函数要大一些但工程收益极其显著没有 critic 的额外前向和反向显存占用降低一截训练流程也简化成了“采样-打分-归一化-更新”四步。1.3 参考实现的整体架构一个最小可用的 GRPO 参考实现通常由这几个模块组成模块作用备注策略模型被训练的对象负责生成回答可全量微调也可只训练 LoRA 层参考模型冻结的策略副本用于 KL 惩罚通常与策略模型同架构奖励计算器给定 prompt 和回答返回标量奖励可以是规则脚本、奖励模型或评测接口采样引擎为每个 prompt 生成 G 个回答关键点是按组组织和排序数据训练循环计算组内优势、构造损失、更新参数GRPO 的核心逻辑全在这里整个数据流是这样的一批 prompt 进入采样引擎每个 prompt 生成一个长度为 G 的回答组奖励计算器给每个回答打分训练循环按组做优势归一化再结合 KL 惩罚算损失更新策略。需要注意的是参考模型在整个过程中不更新所以理论上可以把它的参数冻结后放 CPU 或者显存偏低的设备上只在算 KL 时做一次前向。2. 核心细节拆解组内基线、KL 控制与损失函数2.1 组内优势估计的计算方式与边界情况组内优势的计算公式是所有实现都会遇到的第一个关键点。假设一个 prompt 采样了 G 个回答奖励分别为 r1 到 rG那么第 i 个回答的优势就是A_i (r_i - mean(r)) / (std(r) eps)mean 就是这组奖励的平均值std 是标准差eps 是一个很小的数比如 1e-8防止组内奖励全部相等时除零出 NaN。这一步做完后每个回答的优势被拉回近似零均值、单位方差的范围含义非常清楚大于零说明优于本组平均小于零说明劣于本组平均。这里我踩过的第一个坑是归一化的范围。有人会图省事把整个 batch 的所有回答放在一起归一化而不是按 prompt 分组。这在小规模测试上可能看不出太大问题但一旦不同 prompt 的奖励尺度差异很大强势 prompt 的回答会压制弱势 prompt 的回答梯度方向直接歪掉。正确做法是严格按组归一化组和组之间互不干扰。另一个边界情况是组内奖励方差过低。如果 G 个回答的奖励几乎一样std 趋于零优势趋近于零策略就得不到有效梯度。这个问题会在第 4 节详细讨论排查方法但至少在实现层面要保证 std 加 eps、而且要把组的奖励统计量打印出来实时观察。2.2 奖励分配给 token 的方式GRPO 原始做法里一个回答只有一个奖励这个奖励被分配给生成的所有 token。也就是说整段文本的所有 token 共享同一个优势值。这个设计在训练速度和实现简洁性上都讨喜因为优势只需要按照代数做一次不需要像 PPO 那样逐 token 计算。但实践中有些场景希望用“稠密奖励”比如能判断每一步推理对不对的验证器可以给回答的不同片段打不同的分。参考实现要支持这种需求可以把奖励从标量改成 token 级数组然后在计算损失时按下标取对应位置的奖励。不过要提醒一句稠密奖励的归一化仍然建议以组为单位整体归一化绝不能用“每条回答只归一化自己的 token”这种方式否则不同 token 的优势尺度会打架。还有一个必须注意的实现细节计算 logprob 时要准确屏蔽 prompt 部分的 token。生成时模型看到的是完整的 prompt 回答但损失只应该计算在回答部分。用 position 索引或者 attention mask 把 prompt 区域置零否则 prompt 的 logprob 也会被算进 ratio 和 KL 里整个优化目标就偏了。2.3 KL 惩罚的两种落地方式GRPO 和 PPO 一样都不能完全放开策略自由发挥否则模型会利用奖励函数的漏洞输出乱码或者重复文本去刷分。参考实现里最常见的控制手段是加一个 KL 惩罚项把策略拉向参考模型。第一种方式是损失层面加惩罚项也就是在原始的策略损失后面加上loss policy_loss beta * kl_estimate其中 kl_estimate 用当前策略和参考模型的 logprob 差来近似比如取生成 token 上log π_θ - log π_ref的平均。第二种方式是把 KL 惩罚包装成奖励信号在奖励归一化之前先做r_i r_i - beta * kl_i相当于每条回答的分数先被“离线惩罚”一遍。两种方式都能跑区别在于对优势分布的影响。损失层面加惩罚项KL 惩罚不经过归一化尺度相对稳定奖励层面做惩罚KL 会先被归一化控制力会随组内差异变化。我的建议是参考实现用损失层面的方式超参 beta 的语义更直观排查时也更容易定位问题。2.4 超参数速查表这里给出一张常见取值范围表基于 7B 左右模型的实践经验不同模型规模和任务需要微调。参数常见取值说明组数 G4 ~ 16越大基线越稳生成成本越高采样温度0.8 ~ 1.2温度过低组内多样性不够clip epsilon0.2常规 PPO 剪裁范围KL 系数 beta0.01 ~ 0.04过小容易奖励黑客学习率5e-7 ~ 2e-67B 全参微调的建议区间回答最大长度512 ~ 2048依任务而定每批 prompt 数64 ~ 512兼顾吞吐与多样性组数 G 是最需要敏感对待的超参。组数太小比如 G2均值估计噪声大优势容易出现虚高虚低组数太大每个 prompt 的生成成本线性上涨。实践里我一般从 G4 起步小规模试跑确认奖励信号没问题再视显存余量往 8 或 16 加。3. 从零写一个 GRPO 训练脚本3.1 环境准备与数据组织写参考实现时我建议先跑通一个“最小闭环”再做扩展。依赖只需要常见的深度学习框架、开源模型库、数据加载工具不需要额外引入复杂的强化学习框架。数据组织是整个环节最容易被低估的部分每个 prompt 要采样 G 次这意味着数据要从 “B 个 prompt” 变成 “B 组、每组 G 条” 的结构。我会用一个字典列表来组织每组数据比如每条记录包含prompt、response、group_index、reward、old_logprob、ref_logprob这些字段。group_index 用来把同组数据分到一起归一化优势时直接按这个索引分组操作。数据加载阶段就提前把 prompt 编码成 input id生成阶段只需要传入 token id避免反复调 tokenizer。3.2 采样与奖励计算采样是 GRPO 训练中最耗时的环节。对每个 prompt用当前策略模型跑 G 次生成每次使用随机采样温度落在 0.8 到 1.2 之间。生成的同时要记录旧策略的 logprob这个值也可以事后重新前向算但如果在生成时顺手缓存能省一次前向开销。奖励计算器要保持接口简单输入 prompt 和 response 字符串输出一个标量。规则型奖励比如判断回答是否包含正确答案最容易实现也是验证 GRPO 流程的首选。训练初期我会故意用一些带明显区分度的规则比如“回答里出现特定关键词加一分、越长越扣分”确保奖励信号有起伏方便观察优势归一化是否生效。等流程跑通再替换成奖励模型或评测接口。import torch def compute_logprobs_at_tokens(model, input_ids, attention_mask, response_mask): logits model(input_idsinput_ids, attention_maskattention_mask).logits log_probs torch.log_softmax(logits, dim-1) token_log_probs torch.gather(log_probs, -1, input_ids.unsqueeze(-1)).squeeze(-1) masked token_log_probs * response_mask return masked.sum(-1), response_mask.sum(-1)这个函数返回两个值整段回答的累计 logprob 和有效 token 数。ratio 计算用累计值KL 计算最好除以 token 数避免长回答和短回答在损失里权重不一致。3.3 组内优势归一化与损失构造拿到所有回答的奖励后按组做归一化。这里把组数据 reshape 成[batch, group_size]对最后一维做 mean 和 std然后广播回去既清晰又不容易出错。def grpo_advantages(rewards, group_size, eps1e-8): rewards rewards.view(-1, group_size) mu rewards.mean(dim-1, keepdimTrue) std rewards.std(dim-1, keepdimTrue) eps adv (rewards - mu) / std return adv.view(-1)损失构造是参考实现的核心也是新手最容易写错的地方。首要任务是计算当前策略下回答的新 logprob再和缓存下来的旧 logprob 一起得到重要性采样比例ratio exp(new_logprob - old_logprob)。接着是关键步骤clip 操作必须带着比例和优势一起做计算min(ratio * A, clip(ratio, 1-eps, 1eps) * A)最后取负号当作要最小化的损失。KL 惩罚用当前策略和参考模型的 logprob 差近似乘以 beta 加到总损失上。def grpo_loss(new_logprob, old_logprob, ref_logprob, advantage, beta0.04, clip_eps0.2): ratio torch.exp(new_logprob - old_logprob) clipped_ratio torch.clamp(ratio, 1.0 - clip_eps, 1.0 clip_eps) surr1 ratio * advantage surr2 clipped_ratio * advantage policy_loss -torch.min(surr1, surr2).mean() kl_estimate (new_logprob - ref_logprob).mean() return policy_loss beta * kl_estimate注意 new_logprob 和 old_logprob 都应该是同一批回答的累计 logprob区别只是计算时机不同old 来自采样时的旧策略参数new 来自当前参数重新前向。KL 和损失需要时参考模型的 ref_logprob 是冻结模型算出来的全程不变。这种参考实现的写法有一个好处它天然适合每个采样批只更新一次的策略不用像 PPO 那样维护一个大 rollouts 缓冲池。更新完参数后旧 logprob 就失效了下一轮重新采样就好。3.4 训练循环与收敛监控训练循环本身不复杂但监控指标比代码更关键。我通常在日志里同时记录组内奖励的均值、标准差、优势的标准差、KL 估计值、策略损失、梯度范数和回答平均长度。这些指标能快速定位大多数训练异常。一个实用的启动方法是先做烟囱测试用一个极小模型构造一个特征明显的规则奖励跑 20 到 50 步观察奖励是否上升。如果烟囱测试里奖励都不动问题几乎一定出在数据结构或者损失构造上这时候排查成本最低。烟囱测试通过后再换成目标模型和真实奖励函数能省下大量调试时间。4. 参考现实中的常见问题与排查实录4.1 训练不稳定KL 爆炸与 loss 震荡我见过最多的问题是 KL 惩罚失效具体表现是 KL 估计值从零点几瞬间飙到几十生成文本明显偏离参考模型甚至出现大量重复 token。根本原因是奖励信号足够强策略发现偏离参考模型能拿到更高分而 beta 太小压不住。排查方向有两类一是检查奖励计算器是否被“钻漏子”比如只按格式给分、内容完全乱写也能拿高分这时需要削弱奖励与内容的脱钩二是提高 beta或者对 KL 估计做 clip 上限比如强制单条 KL 不超过 2.0阻止极端偏离。另外一个容易被忽略的点是学习率7B 模型用 5e-6 以上的学习率跑 GRPOloss 几乎必然震荡。这个任务的学习率应该比 SFT 小一个量级。4.2 组内奖励全部相等导致梯度消失当同一个 prompt 的所有回答得分完全一致时std 是 0优势是 0这条数据的策略梯度直接归零。这未必是代码 bug可能是因为 prompt 难度太低所有回答都能答对也可能是采样温度太低G 个回答几乎一字不差。实际操作中的排查方法是把奖励的组内 std 打到日志里如果大量组的 std 都在 0.1 以下就需要行动。优先提高温度到 1.0 以上让采样更有多样性其次是过滤掉太简单的 prompt保证每组至少存在分差最后可以考虑增大 G通过更多采样带来差异。4.3 显存吃紧时的策略显存优化是参考实现绕不开的实操问题。最见效的招是给策略模型开梯度检查点用一点计算换显存参考模型可以冻结后放到 CPU 或使用设备 offload 机制只在算 KL 时载入暂存结果。如果显存仍然紧张把 LoRA 作为默认训练方式会大幅减少优化器状态占用的显存效果和全参微调在中小规模数据上差距不大。还有一个经常被忽视的优化点避免重复前向。旧 logprob 在生成时缓存参考模型的 logprob 也缓存整个更新循环里只需要一次新策略前向能省下可观的时间和显存。生成时限制最大 token 长度、关闭不需要的缓存也是常规操作。4.4 on-policy 数据与多轮更新的关系GRPO 对 on-policy 性质很敏感。采样完成后策略一旦更新旧回答的分布就跟当前策略不一致了。有些参考实现为了省生成次数会尝试对同一批数据做多个梯度步这在 GRPO 里代价很高。因为重要性采样比例只能修正权重无法修正分布偏差多步更新会让旧数据的负面影响迅速累积。我的选择是严格“采样一次用一次”保证每个采样批只对应一次参数更新。宁可在数据采集效率和更新频率之间牺牲一点吞吐也不要让策略在陈旧数据上反复打转。这个问题在大 batch 训练时尤其明显值得在实现文档里明确写清楚。5. 一点个人经验与建议我在落地 GRPO 参考实现时最大的感受是这个算法对工程实现细节的宽容度比 PPO 高不少但容不下“差不多就行”的数据处理。特别是组内归一化和 KL 惩罚这两块一旦标准化后续换奖励、换模型、换规模都会很顺。一个很实用的技巧是把组内奖励的 mean 和 std 作为最核心的监控指标每次训练迭代都看一眼。如果 std 长期偏低说明样本多样性有问题这时候调任何算法超参都治标不治本。另一个建议是永远先从最简陋的规则奖励起步先证明训练闭环能推着奖励曲线往上走再谈真实奖励函数的接入。GRPO 这个方向后续还可以向广义优势估计、动态 beta 调节和步级稠密奖励扩展但基础的训练循环只要搭得干净这些扩展都只是往骨架上挂肉而已。
RELATED

相关推荐

Origin双图组合实战:堆积面积图与点线图科研绘图技巧

Origin双图组合实战:堆积面积图与点线图科研绘图技巧

1. 科研绘图利器Origin的双图组合实战在科研数据处理与可视化领域,OriginLab公司开发的Origin软件始终保持着不可替代的地位。作为一名长期使用Origin进行科学绘图的材料学研究者,我发现堆积面积图与点线图的组合呈现方式,在展示成分演变趋势…

📅 2026/10/26 4:19:18
2024春节AI技术全景:多模态模型、开源跃进与工具链革新

2024春节AI技术全景:多模态模型、开源跃进与工具链革新

1. 春节AI领域关键进展全景回顾2024年春节期间,当大多数人沉浸在节日氛围中时,全球AI领域正悄然发生着多项重大技术突破与行业变革。作为从业者,我梳理了这段时间最具价值的七个技术方向进展,这些变化将直接影响未来6-12个月的产品…

📅 2026/10/26 4:19:18
AI主要应用领域全景解析:从感知到生成的技术落地地图

AI主要应用领域全景解析:从感知到生成的技术落地地图

1. 从“能做什么”到“正在做什么”:AI落地版图的真实切面聊到人工智能的应用领域,很多人脑子里蹦出来的第一反应往往是科幻电影里的场景——机器人管家、全自动工厂、无人驾驶满街跑。但如果你真正在行业里摸爬滚打过几年,就会发现一个很有意…

📅 2026/10/26 4:14:18
MORE NEWS

更多资讯

📰

skyRL-Agent:基于强化学习的智能体系统开发实践

1. skyRL-Agent项目概述skyRL-Agent是一个基于强化学习(Reinforcement Learning)框架开发的智能体系统,从名称中的"RL"可以明确看出其技术基础。这类系统通常用于构建能够自主学习和决策的AI代理,在游戏AI、自动化控制、…

📰

Mhvtl虚拟带库搭建与TSM 6.3备份恢复实战指南

简介:面向Linux运维、备份管理员与系统架构师的TSM 6.3 Mhvtl Oracle DB2备份恢复测试文档,基于CentOS 6.5环境,完整覆盖虚拟带库搭建到数据库异机恢复的测试链路。资源为单个docx文档,共53页,压缩包大小1.83MB&…

📰

Apollo配置中心包管理安装与生产调优实践

Apollo 这套配置中心在不少团队里都是"上线前必须过的一道坎",而真正让人卡住的地方往往不是代码怎么接,而是第一步——怎么把它稳稳当当装到机器上。我前后装过源码版、容器版、包管理版,折腾下来发现生产环境最省心的还是包管理安…

📰

Go内存逃逸分析:原理、优化与实践指南

1. Go 内存逃逸分析核心概念解析内存逃逸分析是 Go 编译器在编译阶段执行的关键优化技术,它决定了变量应该分配在栈(stack)还是堆(heap)上。当变量在函数执行结束后仍然可能被访问时,就会发生"逃逸"现象,此时变量必须分配在堆上。G…

📰

Golang即时通信系统架构设计与可靠性实践

做后端这一行,基于Golang的即时通信系统这个题目我前后被问过不下十次,问的人里有刚把语法啃完想找个项目练手的,也有手上业务真到了要自己搭一套消息通道的。这两拨人的诉求完全不一样:前者想看到一个能跑起来的骨架,…

📰

Miniforge环境管理:Python开发高效工具指南

1. Miniforge开发环境概述Miniforge作为轻量级的conda替代方案,已经成为Python开发者的新宠。它继承了conda强大的环境管理能力,同时避免了Anaconda的商业许可问题。我在多个机器学习项目中切换使用Miniforge后,发现其启动速度和包解析效率确…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬