强化学习这条线我断断续续跟了几年,从最早手撸REINFORCE被方差折磨到怀疑人生,到后来用PPO把训练曲线压稳,再到最近折腾GRPO这类去掉Critic的变体,踩过的坑基本都集中在同一个地方——方差。很多人第一次跑策略梯度的时候都会遇到这种场景:代码逻辑明明没问题,reward也在涨,但loss曲线像心电图一样上下乱跳,同一个实验换个随机种子结果能差出一倍。这不是玄学,本质上是策略梯度估计的方差在作祟。这篇内容我打算把REINFORCE、PPO、GRPO这三代方法串起来讲,重点不是复述公式,而是说清楚每一代到底在解决什么方差问题、为什么这么设计、实际写代码时哪些细节会决定成败。适合已经跑过至少一个策略梯度demo、想搞明白背后逻辑的人,也适合正在选算法做项目、纠结该用PPO还是GRPO的从业者。
1. 策略梯度的方差到底从哪来
1.1 从REINFORCE的目标函数说起
REINFORCE的核心思想非常朴素:让带来高回报的动作概率变大,带来低回报的动作概率变小。它的梯度估计形式是回报乘以对数策略的梯度。问题就出在这个"回报"上。在一整条轨迹里,我们用一个标量回报去加权每一个时间步的动作,这意味着前面某个动作的好坏,被后面所有动作的结果一起"背锅"了。
举个具体的例子。假设一个episode有50步,第3步做了一个无关紧要的动作,但第40步因为运气好拿到了大奖励。REINFORCE会把这份功劳平摊到包括第3步在内的所有动作上,第3步的策略梯度就被这个和它几乎无关的大回报推了一把。单条轨迹这样估计,多条轨迹一平均,方差自然就上去了。
更麻烦的是回报的尺度。如果环境奖励是0到100的范围,那回报可能累积到几千,梯度更新的幅度就会非常大,学习率稍微大一点直接发散。我早期做的一个gridworld实验,奖励设成每步+10,结果训练十次有八次崩掉,后来把奖励归一化到0到1才稳下来。这就是方差在尺度上的体现。
1.2 蒙特卡洛估计的固有缺陷
REINFORCE用的是蒙特卡洛方法估计回报,也就是必须等一整条轨迹跑完才能更新一次。这带来两个直接后果。第一,单条轨迹的回报是一个高方差的无偏估计,样本量不够的时候噪声极大。第二,不同轨迹长度不同,回报的累积方式也不一样,长轨迹的方差天然比短轨迹大。
你可以把这件事类比成抽样调查。如果我想估计全国人的平均身高,只抽一个人问,那结果完全看运气;抽一万个人,结果就稳了。REINFORCE每次更新相当于只抽了很少的"样本"(一条或几条轨迹),估计自然不稳。要降方差,最直接的办法就是增加样本量,但采样在强化学习里是最贵的操作,很多时候根本采不起。
1.3 方差与偏差的权衡关系
这里必须点明一个贯穿全文的主线:降方差往往要引入偏差。最典型的就是后面要讲的Actor-Critic,用Critic估计的价值函数来代替真实回报,Critic估得准就降方差,估不准就引入偏差。PPO、GRPO的各种设计,本质上都是在"方差-偏差-样本效率"这个三角里找平衡点。
理解了这个权衡,你再看那些算法细节就不会觉得是拍脑袋想出来的。比如为什么PPO要限制策略更新幅度?因为策略变化太大会让旧数据估计的价值失效,偏差急剧上升。为什么GRPO敢去掉Critic?因为它用组内相对比较的方式,用一种更轻量的手段把方差压下去,代价是牺牲了一部分理论上的最优性。
2. REINFORCE的降方差改造:从基线到因果性
2.1 减去基线为什么有效
REINFORCE最经典的改进就是减去一个基线。公式上,把回报减去一个只和状态有关的基线函数,梯度期望不变,但方差会下降。为什么期望不变?因为基线不依赖动作,对动作求期望的时候它那一项会归零。为什么方差下降?因为减去基线相当于把回报"中心化"了,让正负回报更均衡,梯度更新的方向更集中。
实操里最常用的基线就是状态价值函数。你可以用一个单独的神经网络去拟合它,也可以用简单的滑动平均。我早期偷懒用过整条轨迹回报的均值当基线,效果居然还不错,尤其是在奖励分布比较均匀的任务里。这个技巧成本极低,但降方差效果立竿见影,强烈建议所有手写REINFORCE的人都加上。
2.2 因果性技巧:只让未来影响现在
比基线更狠的一招是因果性处理。既然第3步的动作不可能影响第40步之前发生的事,那第3步的梯度就不应该被第40步的奖励影响。具体做法是把回报改成"从当前时刻到结束的累积回报",也就是只累加未来的奖励。
这个改动看起来只是把求和范围变了一下,但对降方差的效果非常明显。因为它切断了大量无关的噪声来源。在长轨迹任务里,因果性技巧带来的方差下降往往比基线还大。我在一个路径规划的小项目里对比过,加上因果性之后,同样的训练轮数下策略的稳定性提升了一大截,收敛需要的episode数大概少了三成。
2.3 广义优势估计GAE的引入
把基线思想和因果性思想结合起来,就得到了优势函数的概念:某个动作相对于平均水平好多少。优势函数的估计方法里,GAE是最常用的一个。它用一个参数lambda在偏差和方差之间做连续调节。lambda等于0时只用一步的TD误差,方差最低但偏差最大;lambda等于1时退化成蒙特卡洛回报,无偏但方差最高。
实际调参时,lambda一般取0.95到0.99之间。这个区间是大量实验总结出来的经验值,兼顾了稳定性和准确性。我自己的习惯是先固定0.95,如果训练不稳再往上调一点。要注意GAE依赖一个价值网络来算TD误差,所以它天然属于Actor-Critic框架,纯REINFORCE用不了。
3. PPO凭什么成为默认选择
3.1 重要性采样与信任域思想
PPO要解决的核心问题是:能不能用一批旧策略采的数据,多更新几次策略?直接重复使用旧数据会出问题,因为旧数据是按旧策略分布采的,新策略变了之后这个分布就对不上了。重要性采样通过一个比值来修正这个分布差异,让旧数据还能用。
但重要性采样的比值如果偏离1太远,估计的方差会爆炸。所以PPO加了一个裁剪操作,把比值限制在一个区间里,比如0.8到1.2。这就是所谓的信任域思想:不让新策略离旧策略太远。这个裁剪是PPO的灵魂,也是它比原始策略梯度稳的根本原因。
3.2 裁剪机制背后的直觉
裁剪的直觉其实很好理解。假设某个动作在旧策略下概率是0.5,新策略想把它提到0.9,比值就是1.8,超出了1.2的上限,于是梯度被"截断",不再鼓励继续提高这个动作的概率。反过来如果新策略想把它压到0.1,比值0.2低于下限,同样被截断。
这样做的好处是防止某一次更新把策略带偏太远,导致后面用旧数据估计全都失效。代价是当策略确实需要大幅调整时,PPO会显得"保守",收敛变慢。我在做连续控制任务时深有体会,PPO前期往往比一些激进算法慢,但后期曲线明显更平滑,不容易崩。
3.3 实操中PPO最容易翻车的几个点
第一个坑是优势归一化。PPO对优势的尺度很敏感,如果不做归一化,不同batch之间的梯度幅度差异会很大。标准做法是在每个batch内对优势做零均值一方差的归一化。这个操作看起来不起眼,但不做的话训练稳定性会差很多。
第二个坑是裁剪系数的选择。默认0.2是个不错的起点,但任务不同要调。奖励稀疏的任务可以适当放宽到0.3,让策略敢动;奖励密集且容易过拟合的任务可以收紧到0.1。
第三个坑是价值函数的更新。很多人只关注策略网络,忽略了价值网络的拟合质量。价值网络如果跟不上,GAE算出来的优势就是错的,整个训练都会歪。我的经验是价值网络的更新频率可以比策略网络高一点,或者给价值损失一个稍大的权重。
| 参数 | 常用取值 | 调整方向 | 影响 |
|---|---|---|---|
| 裁剪系数 | 0.2 | 稀疏奖励调大,密集奖励调小 | 越大越激进,越小越保守 |
| GAE lambda | 0.95 | 不稳时调大 | 越大偏差小方差大 |
| 学习率 | 3e-4 | 发散时减半 | 直接决定更新幅度 |
| 优势归一化 | 开启 | 基本都开 | 显著提升稳定性 |
4. GRPO的破局思路:去掉Critic行不行
4.1 组内相对比较的核心机制
GRPO最吸引人的地方就是它不需要Critic网络。传统Actor-Critic要额外维护一个价值网络,参数量大、训练不稳定、还容易和价值估计的偏差纠缠。GRPO换了个思路:对同一个问题采样一组回答,用组内的相对好坏来代替绝对的价值估计。
具体来说,对每个输入采样G个输出,算出每个输出的奖励,然后用组内奖励的均值和标准差做归一化,得到每个输出的相对优势。这个相对优势就替代了GAE里的优势函数。因为比较是在同一组内进行的,很多共享的噪声被抵消掉了,方差自然下降。
这个思路在语言模型的对齐任务里特别吃香,因为语言任务的奖励往往来自一个奖励模型,绝对尺度不好把握,但相对好坏很容易判断。GRPO在数学推理、代码生成这类有明确对错的任务上表现尤其好。
4.2 为什么去掉Critic反而更稳
Critic的问题在于它本身也是个需要学习的网络,它的估计误差会直接传导到策略更新里。在训练早期,Critic基本是瞎猜的,这时候用它算出来的优势去更新策略,等于在噪声上做优化。GRPO绕开了这个环节,用采样组内的统计量代替学习出来的价值,少了一个误差来源。
当然这不是没有代价。组内比较要求每个输入采样多个输出,采样成本上升。而且组内比较只在同一输入内部有意义,跨输入的绝对水平它管不了。所以GRPO更适合那种"每个问题独立、组内可比"的场景,不太适合需要跨状态精细价值估计的连续控制任务。
4.3 GRPO与PPO的适用边界
选PPO还是GRPO,我的判断标准是看任务形态。如果是连续控制、状态空间连续、需要精细的价值估计,PPO这类带Critic的方法更合适。如果是语言生成、每个样本独立、奖励可以组内比较,GRPO更省事也更稳。
还有一个现实因素是工程成本。PPO要维护两个网络,调参维度多;GRPO结构简单,但采样开销大。如果你的采样预算充足而算力紧张,GRPO是划算的;如果采样很贵,那还是老老实实用PPO。
5. 方差治理的通用工程手段
5.1 奖励设计与归一化
不管用哪个算法,奖励的尺度都会直接影响方差。我的习惯是在环境层面就把奖励控制在合理范围,比如统一归一化到0到1,或者做running mean标准化。这一步做在算法之前,收益比调任何超参都大。
奖励稀疏的任务要格外小心,因为大部分轨迹回报都是零,方差反而可能看起来很小,但一旦有非零回报就是大值,梯度会突然跳。这时候可以考虑奖励塑形,或者用课程学习逐步增加难度。
5.2 并行采样与批量大小
降方差最朴素的办法就是多采样。并行多个环境同时跑,把batch size做大,梯度估计的方差会按样本量的平方根下降。这是最可靠的手段,没有之一。代价是算力和内存。
实操里我会先确定一个能接受的训练时间,然后在这个约束下把batch size尽量做大。如果显存不够,可以用梯度累积模拟大batch。要注意的是batch size变大之后学习率通常也要相应调整,不然更新会显得太慢。
5.3 梯度裁剪与学习率调度
梯度裁剪是防发散的最后一道防线。当梯度范数超过阈值时按比例缩放,避免单次更新过猛。阈值一般设0.5到1.0之间。这个操作对PPO和GRPO都适用。
学习率调度也很关键。前期可以用大一点的学习率快速探索,后期衰减下来做精细调整。线性衰减或者余弦衰减都是常见选择。我个人的经验是,如果训练曲线在中后期开始震荡,八成是学习率没降下来。
6. 我踩过的几个真实坑
6.1 优势没归一化导致训练随机性极大
有一次做多智能体路径规划,用PPO训练,同样的代码换个种子结果天差地别。排查了半天以为是环境随机性,最后发现是优势没做归一化。加上归一化之后,不同种子的曲线立刻收敛到差不多的水平。这个坑让我记到现在,任何策略梯度方法我都会先检查优势的尺度。
6.2 价值网络学习率设错拖垮整个训练
还有一次价值网络的学习率设得和策略网络一样,结果价值估计一直不准,GAE算出来的优势全是噪声,策略怎么都学不好。后来把价值网络的学习率调大了一倍,训练立刻顺畅了。这件事说明Actor-Critic里两个网络的平衡很重要,不能想当然地设成一样。
6.3 GRPO组大小设置不当的后果
用GRPO的时候我一开始把组大小设成4,结果组内统计量噪声太大,相对优势估计不稳。后来加到16,效果明显改善。组大小太小,均值和标准差的估计就不靠谱;太大又浪费采样。经验值是8到16之间比较合适,具体看任务。
6.4 裁剪系数和KL惩罚的取舍
PPO里除了裁剪,有时还会加KL惩罚项来约束策略变化。这两个机制有重叠,同时用容易互相干扰。我的做法是优先用裁剪,只有在裁剪压不住的时候才加KL惩罚,而且惩罚系数要从小往大调,避免一上来就把策略锁死。
7. 从方差视角看算法选型
把REINFORCE、PPO、GRPO放在方差这条主线上看,脉络就很清楚了。REINFORCE是起点,方差最大但实现最简单;PPO通过裁剪和重要性采样,在样本效率和稳定性之间找到了很好的平衡,成了通用默认选择;GRPO则针对特定场景(尤其是语言模型对齐)用组内比较的方式绕开了Critic,进一步简化了结构。
选型的时候不要迷信哪个算法"更强",要看你的任务形态、采样预算、算力条件。连续控制优先PPO,语言生成且采样便宜优先GRPO,教学或者极简场景可以用REINFORCE加基线。理解了方差从哪来、每种方法怎么治它,你就能根据自己的约束做出合理判断,而不是盲目跟风。
最后分享一个我自己的习惯:不管用哪个算法,训练前先跑一个极小的任务,把reward曲线、优势分布、梯度范数都打印出来看一眼。很多方差问题在早期就能暴露,等到大规模训练崩了再回头查,成本高得多。这个习惯帮我省下的时间,比任何调参技巧都多。