面试问PPO和GRPO:省掉Critic后,优势从哪来?
这道题我在技术面里见过不止一次,它其实是一个很好的“照妖镜”:能答上来的人,说明对PPO的理解不只是在背公式;答不上来的人,哪怕能默写clip loss,也大概率没真正跑过RLHF。PPO的优势估计几乎离不开Critic,因为价值网络是baseline的来源;但GRPO把Critic整个去掉了,照样能在语言模型对齐里稳定训练,而且效果还不输PPO。省掉Critic之后,优势到底从哪来?这是理解GRPO唯一重要的问题。
这篇文章不聊论文复述,我从“优势怎么算”这个根上把PPO和GRPO掰开讲清楚,再把实操时最容易踩的坑一起列出来。适合正在准备RL岗位面试的人、做LLM对齐训练但没细看过两者的工程师,以及那些刚看完公式想知道“所以呢”的读者。
1. 先回答一个问题:PPO到底为什么需要Critic
要理解GRPO为什么能省掉Critic,第一步是先搞清楚Critic在PPO里承担了什么角色。很多人只知道Critic是“神经网络”,它的作用是估价值,但估价值这一步到底在解决什么问题,才是这道题的核心。
1.1 没有基线,优势就是一个没有参照系的差值
强化学习的目标不是让所有动作的概率都变大,而是让“好动作的概率变大、差动作的概率变小”。但问题是,什么是“好”,什么是“差”?如果只看奖励的绝对数值,会遇到一个很尴尬的情况:假设某个状态下所有动作的奖励都是正的,那模型会倾向于把所有动作的概率都推高,因为每个动作看起来都在带来收益。但最优策略显然只需要保留其中一部分。
所以我们需要一个“当前平均水平”作为参照:某个动作的奖励减去平均水平,得到的差值才是真正的优劣信号。这个差值就是优势函数(Advantage)——A(s,a) = Q(s,a) − V(s),Q是动作的价值,V是状态的价值。这里的V就是Critic要去拟合的。
“优势”这个名字已经很直白了:它不是“这个动作能拿多少分”,而是“这个动作比当前策略的均值表现好多少”。好多少是相对的,相对就需要一个基线。类比现实生活,问“这次考试考了90分好不好”,没人能回答;但如果知道全班平均分是70,那90分就是明显的好。Critic本质上就是那个“全班平均分”,只不过它要实时动态地算出来。
1.2 GAE的直觉和Critic训练之间的矛盾
PPO实际算优势时,并不会直接拿完整回报去减价值,而是用GAE(Generalized Advantage Estimation,广义优势估计)。GAE的公式长这样:
δ_t = r_t + γV(s_{t+1}) − V(s_t)
A_t = Σ_{l=0}^{∞} (γλ)^l δ_{t+l}
翻译成人话:先用当前奖励r_t加上下一步状态的价值,减去当前状态的预测价值,得到一个“单步优势残差”δ_t;然后把未来若干个残差按照γλ的指数衰减累加起来。λ决定了你多信任远期的信息,γ决定了未来的奖励在当下值多少钱。
这里有个非常反直觉的点:GAE里的每一步都要用V(s_{t+1}),也就是说Critic的预测质量直接决定了优势的准确性。如果Critic给错了基线,GAE算出来的东西就不只是噪声问题,而是系统性偏差。我在早期跑PPO做RLHF时遇到过最典型的情况是:reward曲线的波动没多大,但actor的loss突然崩了,查了一圈发现是Critic的value loss发散,导致GAE输出错乱。这算是PPO实战中最常见、也最让人头疼的问题之一。
而且在PPO里,Critic自身也在训练,每轮采样后要用MSE去更新价值网络,需要单独调value loss的权重、学习率,甚至要考虑value loss和policy loss的量纲差异。量纲问题听上去很小,真调的时候很磨人:政策梯度是概率比的带权更新,value loss是均方误差,两者数值范围经常差一个数量级,一起回传时梯度容易被大数值牵着鼻子走。
所以Critic在PPO里不只是一个“辅助分支”,它是优势计算的基石。如果Critic离线,整个PPO的更新立刻变成没有参照系的瞎猜。
2. GRPO的思路转变:把绝对价值预测换成组内相对比较
说完了Critic为什么重要,才能理解GRPO的“抠掉Critic”为什么不是胡来。它没有去换一个更精确的价值网络,而是从根本上换了一套信号来源:不再预测绝对价值,而是利用同一个输入下多个输出的相对排序来做优势估计。
2.1 GRPO的前提条件:语言模型对齐里的采样自由度
GRPO能这样做,依赖一个语言模型场景的特殊性:同一个prompt,你可以非常便宜地采样出多个不同的response来。传统RL里的智能体是在环境里一步步做决策的,每一步的状态都依赖前一步的动作,想在一个状态下采样多次很费劲;但LLM对齐不一样,一个prompt对应的完整输出就是一次采样,你可以轻松生成G个不同的回复,每个回复都是独立且完整的episode,奖励在最后统一给出。
这个差异很关键。传统RL里你必须从当前状态出发估计一个价值函数,是因为你没法在同一状态下廉价地试很多次;而LLM对齐里“试很多次”是生成时顺手的事。既然能直接拿到一堆输出,那就不需要学一个“平均分预测器”了——可以直接用这一堆输出里的实际平均分。
GRPO的做法是这样:对一个prompt q,采样出G个输出,用reward model分别打分,然后在组内计算优势:
A_i = (r_i − μ_group) / (σ_group + ε)
其中μ_group是这G个分数的均值,σ_group是标准差,ε是为了防止除零的小常数。这就是标准的z-score归一化。score高于组内平均,优势为正;低于平均,优势为负。训练时,这组输出中的“更优者”概率会被推高,“更差者”概率会被压低。
2.2 组内归一化的本质:把回归问题换成排序问题
很多人第一次看到GRPO公式时,会觉得这不过就是个标准化,没什么了不起。但这个标准化实际上完成了一次非常深刻的转换。
Reward model输出的绝对分数本身包含大量噪声,而且不同prompt之间的分数分布差异很大。有的prompt下所有输出都能拿4分,有的prompt下拿2分就算不错了。如果直接用绝对分数作为优势,梯度就会在遇到“宽松”的prompt时整体偏向正向,遇到“严格”的prompt时整体偏向负向,信号很不稳定。组内z-score抹平了这种组间尺度的差异,让每个prompt内部独立比较,优势均值为0,方差为1,梯度更新量级天然可控。
更重要的是,组内归一化把“回归”变成了“排序”。做reward model的时候,训练数据通常就是两个或多个response的相对偏好排序,模型学到的本质是“哪个更好”而非“好多少”。GRPO的组内优势正是沿用了这个逻辑:它不问你绝对能得几分,只问你在这组输出里排第几、比平均值好多少。这恰恰和reward model的表达能力对齐了,信号在传递过程中损耗更小。
用一个生活化的类比:PPO的Critic像高考,你考多少分有一个绝对分数线,但这个分数线本身年年变化、各省不同;GRPO像小组排名,不用关心全世界的评分标准,只要对比同一组里的人谁是第一、谁是垫底,这个相对顺序在大多数情况下已经足够指导你往哪个方向改。
所以说,GRPO不是“没有优势了”,而是优势的来源从“对绝对价值的估计”变成了“对组内相对质量的直接测量”。后者在LLM对齐场景里更便宜、更直接,也就更稳。
3. 实操迁移:从PPO到GRPO的更新逻辑与代码骨架
讲清楚原理,下一步就是落地。GRPO的更新逻辑并不复杂,但如果你是从PPO的代码改过来的,有几个地方容易写错:组内归一化的作用域、KL项的加入位置、以及优势的平均性质对clip造成的影响。
3.1 简化版GRPO更新伪代码
我给一个适合在面试里演示的简化版本,或者作为从零搭建训练流程的骨架。核心逻辑只有六步。
import torch def grpo_loss( logprobs, # 新策略: [batch, seq_len] ref_logprobs, # 参考模型: [batch, seq_len] ratio_logprobs, # 旧策略: [batch, seq_len],用于算ratio的旧概率 rewards, # [batch],每个样本的reward打分 group_ids, # [batch],标记每个样本属于哪个prompt组 g, # 每组样本数 clip_eps=0.2, beta=0.01 ): # 1. 每个token的累积概率差,用于后续KL项 per_token_kl = torch.exp(ref_logprobs - logprobs) - (ref_logprobs - logprobs) - 1 # 2. 组内优势归一化,注意是按组做z-score,不是整batch做 advantages = torch.zeros_like(rewards) for group_id in torch.unique(group_ids): mask = group_ids == group_id group_rewards = rewards[mask] mean = group_rewards.mean() std = group_rewards.std() + 1e-5 advantages[mask] = (group_rewards - mean) / std # 3. 计算概率比:ratio = exp(新logprob - 旧logprob) log_ratio = (logprobs - ratio_logprobs).sum(dim=-1) ratio = torch.exp(log_ratio) # 4. PPO风格的clip loss,但优势来自组内比较 clipped_ratio = torch.clamp(ratio, 1.0 - clip_eps, 1.0 + clip_eps) pg_loss = -torch.min(ratio * advantages, clipped_ratio * advantages).mean() # 5. 序列级KL惩罚(也可以用逐token均摊) kl_loss = per_token_kl.sum(dim=-1).mean() return pg_loss + beta * kl_loss我实际见过不少人刚开始上手时,把组内归一化误写成整个batch的一次z-score,这是最严重的错误。GRPO里的“group”是同一个prompt采出的G条输出,只有同组之间的对比才有意义。跨prompt做归一化,等于把小组排名和高考试卷混在一起排序,理论依据就崩了。
3.2 参数选择与实验心得
组大小G怎么选。论文和实践中G一般取4到16,我建议起步用8。G越大,优势估计的方差越低,训练越稳,但采集一次经验需要的生成次数、reward model打分次数都线性增长,小团队容易卡在算力上。G取4时训练会明显更吵,loss曲线波动大,但不是不能跑,适合快速验证。
KL项的β取值。GRPO里其实还保留了KL散度惩罚,它的作用是阻止优化后的策略偏离参考模型太远。β一般在0.001到0.02之间,具体取决于你的reward scale。我个人习惯先固定β=0.01跑200步,看训练后输出文本的重复率和长度变化,再决定要不要调大。KL惩罚千万别省,如果完全去掉,模型会在几轮更新内就collapse成非常极端的重复文本,因为组内优势会不断强化那些当前reward model偏爱的格式,得不到约束。
Clip策略要不要改。GRPO的clip和PPO外观完全一样,但含义有些微区别:PPO里优势可能有偏,clip是在限制“偶尔冒出来的极端优势”对策略的冲击;GRPO里优势严格零均值,极端值也少,clip更像是一种保险丝,正常情况下踩不到clip边界,但真踩到了说明策略变化太快,应该检查学习率。
3.3 PPO迁移到GRPO时,training loop的典型变化
PPO的常规流程是采样一批经验、算GAE优势、再用小批量多epoch去更新actor和critic。GRPO去掉了“多epoch交替更新”的复杂度,流程更短:生成G组输出,打分,组内归一化,一次更新完。
这不是一个细节差别,而是训练稳定性的来源。PPO里actor在更新时,critic还在用旧价值算优势,价值在更新前后本身不一致;你来我往,两边都动,调试时要分开盯两个loss。GRPO不需要维护这套双网络状态,训练循环里少了一整类“耦合故障”。我在一次项目里从PPO切换到GRPO之后,原先每隔几十步就可能出现的value loss爆炸直接消失了,省下了大量调参时间。
4. 面试官追问:GRPO常见的五个细节问题
把主流程聊完之后,面试官一般不会停在表面,而是会从各种角度去挖你对GRPO的理解是“背过”还是“真会”。这里我列出自己在面试中遇到过和给候选人出过的五类高频追问,每一条都值得快速答出实质内容。
4.1 没有Critic,KL惩罚里的“参考模型”算不算新的价值网络?
这是概念区分题。参考模型(Reference Model)不是Critic。Critic是训练中动态更新的价值预测器,它的作用是提供优势基线;Reference Model是训练前冻结的旧策略,它只提供一个KL距离约束,防止新策略跑偏,不参与优势计算。两者的角色完全不同,一句话讲明白就行。
4.2 优势归一化丢掉绝对分数信息,不会导致训练出问题吗?
会丢掉绝对信息,但在对齐场景,相对排序才是核心信号。reward model预测的绝对值本来就没有物理意义,还受prompt难度影响。组内归一化等价于说:我们只关心“这组里哪个好”,不关心“这组整体能拿多少分”。如果场景要求绝对质量,比如安全合规评判,那就需要另加约束;普通RLHF任务里,相对排序足够了。
4.3 组大小G越大越好吗?有没有上限?
从方差角度看,G越大优势越可靠,但收益是递减的。G从4提到8收益明显,从8提到16收益减弱,从16再往上提升,增加的采样成本远超稳定性的改善。而且G增大后,每组里可能会出现“矮子里面拔将军”的问题:G越多,蒸馏中随机生成的bad case也越多,排名信号里真正的质量差距反而被稀释。我在实际训练中G=8最常见,偶尔用12,16以上只在评测需要时才跑。
4.4 GRPO和DPO有什么本质区别?
这是一个非常值得准备的对比题。DPO直接在偏好对数据上训练,连reward model都不需要,它假设存在一个隐式奖励,通过偏好对的概率比较来做优化;GRPO需要显式reward model打分,但打分结果只作为组内排序来源,不需要训练单独的奖励头。
可以理解为:DPO省掉了“奖励模型”这个显式组件,GRPO省掉了“价值模型”这个显式组件。两者的共同思路都是避开传统RL中的高方差采样,选择更直接的相对偏好信号。如果面试官顺着这个方向问,说明他在考察你对RLHF整个技术谱系的理解,而不是单个公式。
4.5 如果reward model分数全是同一个值,GRPO会怎样?
这是一个压力测试问题。如果同组所有输出的分数完全相同,组内标准差是0,归一化后的优势会很难看。ε的存在只能防除零,但无法制造出有效差异。这种情况下,GRPO退化成无信号状态——loss不变,梯度无效,模型不更新。
实践中这种问题比想象中常见,特别在早期训练一些“保守型”reward model时,输出分数可能集中在很窄的区间。我的排查习惯是:训练前先看一组采样的reward分布,如果std小于某个阈值,就会考虑在两个地方调整:一是给reward model打分加一个温度系数来拉开区分度,二是调大G看能否采样到更高分样本拉大组内差距。如果两招都无效,问题大概率出在reward model本身,不是GRPO。
5. 避坑清单:我在训练中踩过的实际坑位
这部分不是理论推演,是我切切实实遇到过、最后通过修改训练策略解决的问题。如果你正准备把GRPO跑起来,下面这几条可以提前帮你省掉几天的debug时间。
5.1 组内归一化的优势会影响学习率,别忽视这个联动
因为GRPO的优势已经被标准化到均值为0、方差为1,梯度量级相对稳定,很多人会直接把学习率调得比PPO更大。确实有一定空间,但不是越大越好。我试过把学习率从PPO时代的5e-6提到2e-5后,前200步loss下降很爽,接着就出现了回复模板逐渐固化、长度变短的现象。这本质是KL惩罚来不及约束,策略被组内优势推得太猛。在GRPO里面,学习率和β是一对联动参数,单个调一定出问题。
5.2 reward model打分噪声对GRPO的影响方式和PPO不同
PPO里reward噪声会被GAE的时间累积部分平滑掉,GRPO里没有这种机制,噪声直接进入组内排序,劣质的reward model打分会让“优势”变成随机方向。所以GRPO对reward质量的要求反而比PPO要高。如果打分里明显出现同类输出分数忽高忽低,我建议先做reward model的校准,或者在分数上做一点平滑,哪怕是简单的指数移动平均也有效果。
5.3 长度偏好偏差会被组内比较进一步放大
我在跑GRPO时发现一个很隐蔽的问题:reward model对长输出的偏爱,会在组内比较中被额外放大。原因在于,组内采样到的不同长度输出原本就比分混在一起,而长回答往往看起来更完整,reward model打分偏高。GRPO不关心绝对分,但“长回答在该组中排更高”这件事会被充分强化。几轮之后,平均回复长度肉眼可见地涨。
应对办法有两种。一种是在reward打分环节输入之前对长度做归一化或减分惩罚,另一种是在采样时做长度分层——把相似长度的输出分到同一组再比较。前者改动简单,后者更干净但工程成本高,我实际用了前者。
5.4 组内排序让小模型更容易掉进“重复胜利”
组内比较意味着模型永远在朝“当前这组里更好的方向”移动。对已经收敛的Base模型来说,这一步方向通常没错;但如果模型偏小或初始化不良,它最容易被reward model偏爱的几个表面特征带偏,比如插入一堆“总结: ...”、“重要性: ...”这种格式化的废话。这类问题在PPO里也是存在的,但GRPO的组内排序机制会更快发现这个捷径并不断强化它。
所以跑GRPO时要格外盯住输出文本的多样性和质量指标,不能只看reward curve。我在训练流程里加了一个规则:每500步随机抽50个prompt,人工观察生成结果和Base model输出做对比。如果发现某个共性的格式痕迹出现频率异常,就立即回退检查reward model是否过度偏好了该特征。
5.5 小batch和大G,优先加大G
最后一条经验,算是我给团队定的一个默认策略:训练不稳定时,优先增大G而不是增大batch size。因为GRPO的优势估计方差主要由组内样本数决定,batch size影响的只是优化步数的统计稳定性,远不如组内样本数直接。G翻倍带来的稳定性提升,体感上比batch翻倍明显得多,算力开销反而更小——因为同样的数据量,大G意味着每轮更新时梯度更干净。这个策略在我自己的实验里反复验证过,先调G,再调其他训练超参,排查效率高很多。
这篇文章从面试题出发,把PPO里Critic存在的意义、GRPO用组内归一化替代价值估计的原理、实操中的训练配置和排查经验都完整过了一遍。如果看完能用自己的话回答“省掉Critic后,优势从哪来”,那这道题就算真正想通了。