从PPO到SAPO:大语言模型策略优化算法推导与实战

发布时间:2026/10/24 4:27:07
从PPO到SAPO:大语言模型策略优化算法推导与实战 1. 从PPO开始旧路线的能力边界PPO、GRPO、DAPO、GSPO、SAPO这条推导链过去两年几乎成了策略优化算法升级的完整缩影。表面上它们只是换了一个目标函数实际上每一次切换都涉及“优势估计”这件事的根本变化。这篇我会沿着这条线把每一步的推导逻辑、改动动机和训练中真正影响收敛的地方拆开讲。适合正在做LLM对齐、奖励模型微调或打算把RL引入生成任务的人。1.1 为什么策略优化必须引入重要性采样强化学习的最终目标是最大化累计奖励的期望也就是$$J(\theta)\mathbb{E}{\tau \sim \pi\theta}\left[\sum_{t0}^{T} r_t\right]$$如果直接对这个目标求梯度会得到经典的策略梯度$$\nabla_\theta J(\theta)\mathbb{E}{\tau \sim \pi\theta}\left[\sum_{t0}^{T} \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot R_t\right]$$问题在于这个期望是从当前策略 $\pi_\theta$ 采样的。训练时每更新一步策略样本就“过期”了想再采一批新样本又要重新和环境交互一轮。这在大模型场景下是不可接受的因为一次 rollout 的成本可能是一整批生成序列再加上奖励模型打分时间和显存都吃不消。于是有了重要性采样。我们把期望换成从旧策略 $\pi_{\theta_{old}}$ 采样得到的形式$$\mathbb{E}{\tau \sim \pi{\theta}}\left[\cdot\right]\mathbb{E}{\tau \sim \pi{\theta_{old}}}\left[\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)} \cdot \cdot\right]$$记策略比例$$\rho_t\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}$$这样我们就能用一批旧样本反复更新好几轮。但“反复使用”也带来问题如果 $\rho_t$ 太大说明新旧策略差异已经很大这时再用旧样本估计新策略的梯度偏差会非常离谱。TRPO 的做法是显式约束 KL 散度PPO 则把这个约束变成了一个更廉价的裁剪目标。1.2 PPO的裁剪目标函数是怎么推出来的PPO 最终形式是$$J^{\text{CLIP}}(\theta)\mathbb{E}\left[\min\left(\rho_t A_t,;\operatorname{clip}(\rho_t, 1-\varepsilon, 1\varepsilon) A_t\right)\right]$$其中 $\operatorname{clip}(\rho_t, 1-\varepsilon, 1\varepsilon)$ 把 $\rho_t$ 限制在 $[1-\varepsilon, 1\varepsilon]$ 区间内$A_t$ 是优势函数。如果只是用 $\rho_t A_t$ 作为目标策略可以一步迈得非常大。加入 $\min$ 之后这个目标会自动选择“更保守的一侧”。当 $A_t0$ 时如果 $\rho_t$ 超过 $1\varepsilon$$\min$ 会选择被裁剪后的常数值梯度变为 0防止策略继续激进放大这个动作的概率当 $A_t0$ 时如果 $\rho_t$ 低于 $1-\varepsilon$同样会被截住防止策略继续疯狂压低这个动作的概率。在传统 RL 场景里$A_t$ 通常用 GAE 来估计$$A_t\sum_{l0}^{T-t}(\gamma\lambda)^l \delta_{tl}$$$$\delta_t r_t \gamma V(s_{t1}) - V(s_t)$$这里出现了一个关键角色$V(s)$也就是 Critic 价值网络。PPO 通常采用 Actor-Critic 架构Actor 负责输出策略Critic 负责估计状态价值然后用两者的差值计算优势。这个设计在小规模环境里没什么问题但放到 LLM 上三个隐患会逐步放大。1.3 LLM场景下PPO的三个隐痛第一个是显存。Critic 网络如果和策略网络同规模等于让训练显存直接翻一倍。哪怕只把 Critic 做成比 Actor 小几倍整个优化器状态、梯度、中间激活也都得翻倍。第二个是价值估计的噪声。LLM 的奖励往往是稀疏的一条长回答只有最后打分中间几千个 token 的“价值”全靠 Critic 去 bootstrap。Critic 预测不准GAE 的 $\delta_t$ 就会带着噪声传播最终优势估计的方差被放大。这个问题在短 horizon 的 gym 环境里不致命但在动辄 1k token 的生成任务里很致命。第三个是超参敏感性。PPO 对 reward scale、GAE 的 $\lambda$、KL 惩罚系数都非常敏感。同样的参数在小规模任务上调通了换一个数据集或者换一个奖励模型很可能直接崩。正是这三个痛点把推导链推向了去掉 Critic 的方向。GRPO 就是这条路上最关键的转折点。2. GRPO把Critic请出去用“组内相对优势”代替价值估计GRPO 的核心思想很直白既然 Critic 又贵又不准那就不用它改为在一个 prompt 下采样多条候选回答用“组内相对比较”来估计优势。2.1 从单动作价值到组内相对优势给定一个 query $q$从当前旧策略中采样 $G$ 条回答记作 ${a_1, a_2, ..., a_G}$。奖励模型给每条回答打分得到 ${r_1, r_2, ..., r_G}$。GRPO 不再计算绝对价值而是计算这条回答在组内有多“突出”$$\mu \frac{1}{G}\sum_{i1}^{G} r_i$$$$\sigma \sqrt{\frac{1}{G}\sum_{i1}^{G}(r_i - \mu)^2 \epsilon}$$$$A_i \frac{r_i - \mu}{\sigma}$$这个 $A_i$ 就是组内相对优势。它天然是近似零均值、单位方差的。如果某条回答比组内平均分高$A_i0$策略会倾向提高它的概率如果比平均分低$A_i0$策略会降低它的概率。这个设计的巧妙之处在于它把“绝对分数”降级成了“组内相对位置”。奖励模型如果整体打分手松手紧只要组内相对关系不变优势估计就不会受太大影响。这在奖励尺度不稳定时非常有用。2.2 GRPO的损失函数推导GRPO 在 PPO 上的改动就是把 $A_t$ 换成 $A_i$并去掉 Critic。损失函数写成$$L^{\text{GRPO}}(\theta)-\mathbb{E}\left[\frac{1}{G}\sum_{i1}^{G}\min\left(\rho_i A_i,;\operatorname{clip}(\rho_i, 1-\varepsilon, 1\varepsilon)A_i\right)\right]\beta, D_{\mathrm{KL}}\left(\pi_\theta | \pi_{\mathrm{ref}}\right)$$这里的 $\rho_i$ 是当前策略和采样策略之间的概率比$$\rho_i\frac{\pi_\theta(a_i|q)}{\pi_{\theta_{\mathrm{old}}}(a_i|q)}$$KL 项通常加的是与参考策略 $\pi_{\mathrm{ref}}$ 的散度用来防止模型在 RL 过程中把语言能力带偏这也是 LLM 场景下和传统 PPO 不太一样的地方。从梯度形式上看忽略裁剪后GRPO 相当于把每个样本的梯度加权为$$\nabla_\theta L \propto -\frac{1}{G}\sum_{i1}^{G} A_i ,\nabla_\theta \log \pi_\theta(a_i|q)$$因为 $A_i$ 在组内做了标准化所有样本的梯度幅度被拉到了同一量级。这样即使某条回答的奖励绝对值特别大也不会像 PPO 那样把单条样本的梯度放大到破坏整个策略的程度。2.3 相比PPO的实际收益第一显存直接少了一大块。不需要维护 Critic 和它的优化器状态同样的显存可以塞更大的策略模型或者更大的 batch。第二优势估计更稳定。组内归一化用的是一个 batch 内几十条真实采样结果而不是一个可能没训练好的神经网络估值。规则奖励场景下GRPO 的稳定性比 PPO 好非常多这也是它很快成为 LLM 微调主流的原因。第三实现简单。不需要处理 GAE 的时序依赖不需要训练价值网络只需要一次 forward 拿到所有序列的 logprob配合奖励分数做归一化就行。但 GRPO 并不是终点。组内归一化依然有自身的毛病一是 clip 机制会把策略比例和优势耦合在一起二是组内统计量在小 batch 下噪声很大。DAPO 和后面的 GSPO就是在分别处理这些问题。3. DAPO给GRPO打补丁DAPO 我习惯把它理解成“把 GRPO 的动手脚空间拆得更干净”。它最核心的改动是 Decoupled Clip也就是解耦裁剪。3.1 Decoupled Clip先把“比例”和“优势”拆开GRPO 沿用了 PPO 的 $\min$ 目标$$L_i\min\left(\rho_i A_i,;\operatorname{clip}(\rho_i, 1-\varepsilon, 1\varepsilon)A_i\right)$$这个形式虽然简洁但存在一个隐蔽的问题$\min$ 操作会让 clip 边界和优势的符号耦合在一起。当 $A_i0$ 时激活的是上限裁剪当 $A_i0$ 时激活的是下限裁剪。也就是说同一个 $\rho_i$只要优势符号翻转一下策略的更新方向和政策比例的有效范围都会跟着变。问题在于优势估计本身是有噪声的。一个本来应该为正的 advantage可能因为奖励噪声变成负的这时候策略更新就被迫走了另一个方向。这在组内优势近零均值的情况下尤其常见一个样本今天在组内排正数第二明天稍微波动就变负数第三梯度方向来回抖。DAPO 的做法是把“策略该不该动”和“往哪里动”拆开。一个典型表达是$$L_i\operatorname{clip}(\rho_i,;1-\varepsilon_{\mathrm{low}},;1\varepsilon_{\mathrm{high}}) \cdot A_i$$这里的 clip 只负责限制策略比例$A_i$ 只负责决定方向和幅度。更严格一点说实现里常常把 $A_i$ 当成权重项不让它去参与 $\min$ 的选择。这样即使某一时刻 advantage 符号翻转也不会导致 clip 边界在上下限之间跳跃。你可以这么理解PPO/GRPO 的 clip 像是一个“既看速度又看方向的刹车”而 DAPO 的 clip 是“只看速度的限速器”方向交给 advantage 自己判断。这个解耦让策略更新在边界处更平滑尤其是当 $A_i$ 在 0 附近抖动时目标函数不会因为 $\min$ 切换而出现明显的不连续。3.2 Token级Loss与动态采样训练信号粒度之争另一个重要改动是把 loss 从 sequence 级拆到 token 级。GRPO 里 $\rho_i$ 是整个序列的概率比等于所有 token 概率比的乘积。假设一条回答有 500 个 token其中 480 个 token 是正常表达只有最后 20 个 token 决定了答案正确性。Sequence 级概率比会把 500 个 token 的联合变化混在一起奖励信号很难回传到真正起作用的 token 上。Token 级 loss 的做法是把概率比逐 token 拆开$$\rho_{i,t}\frac{\pi_\theta(a_{i,t}|q,a_{i,t})}{\pi_{\theta_{\mathrm{old}}}(a_{i,t}|q,a_{i,t})}$$然后对序列内所有 token 的 loss 取平均$$L_i\frac{1}{T_i}\sum_{t1}^{T_i} L(\rho_{i,t}, A_i)$$这里的 $A_i$ 依然是整条序列的组内优势但策略更新力度分摊到了每个 token 上。好处是长回答里真正导致成败的关键 token 更容易被识别坏处是如果不对 padding 和太长序列做 mask训练会被凑数的 token 稀释。所以实际实现里必须做长度归一化和 padding mask。DAPO 还强调动态采样。简单说就是在一个 global batch 里不要把某个 prompt 的 $G$ 条回答全放进去而是让每个 batch 尽量覆盖不同 prompt并且根据 advantage 的分布动态调整正负样本占比。否则可能出现一个 batch 里 90% 样本都是负 advantage梯度整体偏向压抑某个 token最后模型很快退化。4. GSPO把“分组”这件事做得更平滑GRPO 用组内均值和方差来归一化听起来很合理但有一个工程细节容易被忽略组有多大如果每个 prompt 只采样 4 到 8 条回答那么这组样本的均值和方差本身波动很大。特别是奖励分数只有 0 和 1 时一组样本可能全是 0标准差为 0下一组又全是 1标准差还是 0。这种不连续的统计量会让 advantage 出现奇怪的跳变。GSPO 在我这里的理解是 Group-wise Smoothed Policy Optimization也就是把组内统计量换成平滑版本。它不是对 GRPO 的否定而是对组内归一化的“降噪处理”。4.1 从点估计到分布估计把组内统计量换成平滑版本GRPO 用的是当前这个 group 的局部均值和局部方差。GSPO 则引入跨 batch 的指数滑动平均用全局平滑量代替局部统计量$$\mu_s \leftarrow \beta \mu_s (1-\beta)\mu_{\text{batch}}$$$$\sigma_s^2 \leftarrow \beta \sigma_s^2 (1-\beta)\sigma_{\text{batch}}^2$$然后计算优势$$A_i\frac{r_i - \mu_s}{\sqrt{\sigma_s^2 \epsilon}}$$这里的 $\beta$ 是平滑系数通常取 0.9 到 0.99。$\beta$ 越大统计量更新越慢越能反映整个训练阶段的大盘水平$\beta$ 越小统计量越跟随当前 batch越接近原始 GRPO。这么做的动机很朴素如果组大小只有 4那么一个组里的均值可能差出 0.5 以上优势估计的置信区间非常大。用 EMA 统计量代替局部统计量后baseline 更稳定advantage 的波动更接近“奖励模型真实分布”而不是“这一组采样运气”。你可以把 GRPO 组内归一化想象成拿一屋子人的平均身高来判断一个人高不高但这屋子只有 4 个人GSPO 是把过去很多屋子的平均身高也记下来再做判断。后者的参照系更稳。4.2 一个统一的梯度方差视角从梯度方差的角度看策略梯度可以拆成两部分$$\mathrm{Var}\left[\nabla L\right]\mathbb{E}\left[\mathrm{Var}\left(\nabla L \mid \text{group}\right)\right]\mathrm{Var}\left(\mathbb{E}\left[\nabla L \mid \text{group}\right]\right)$$前半部分是组内采样导致的随机梯度噪声后半部分是组间 baseline 波动带来的额外方差。GRPO 主要降低了前半部分因为它用了组内归一化让每个样本的梯度幅度相近。但组间的均值/方差波动依然在组越小越明显。GSPO 的 EMA 统计量直接压缩后半部分。它可以让同一批训练数据在不同 step 里获得更一致的 advantage而不是每次因为组内运气不同而剧烈变化。实际训练中的一个表现是 loss 曲线更干净early stopping 和 reward 波动更容易被分辨出来。这个“平滑”思想到了 SAPO 会被进一步放大不只是统计量平滑连 clip 边界和采样策略都开始随训练状态自适应。5. SAPO自适应边界最后的“长尾修正”SAPO 我按 Self-Adaptive Policy Optimization 来理解。它要解决的问题是所有前面算法都用固定的 $\varepsilon$但训练不同阶段对 clip 的需求完全不同。训练早期策略探索空间大模型应该允许更大的更新幅度训练后期策略已经收敛到一个不错的区域更新幅度必须快速收紧否则会出现震荡。5.1 从固定clip到自适应clipPPO、GRPO、DAPO 的 $\varepsilon$ 是一个人工设定的常数比如 0.2。SAPO 的基本思路是让 $\varepsilon$ 根据当前 advantage 的分布动态变化。一个可用的自适公式是$$\varepsilon_t \operatorname{clip}\left(\varepsilon_{\min} \left(\varepsilon_{\max}-\varepsilon_{\min}\right)\cdot \frac{\mathrm{IQR}(A_t)}{\mathrm{IQR}{\text{ref}}},;\varepsilon{\min},;\varepsilon_{\max}\right)$$其中 $\mathrm{IQR}(A_t)$ 是当前 batch 优势的四分位距$\mathrm{IQR}_{\text{ref}}$ 是训练开始阶段的参考四分位距。如果当前优势分布发散说明样本间差距大策略需要更大自由度来区分好坏$\varepsilon_t$ 会变大如果优势分布集中说明策略已经比较稳定$\varepsilon_t$ 会变小防止过度更新。这个自适应过程不需要额外的神经网络只需要在线维护 advantage 的分位数统计计算代价非常低。它带来的直接好处是少调一个关键超参而且能让算法在训练过程中自然地从“激进探索”过渡到“保守利用”。5.2 自适应采样把运气成分从组构造中降下来SAPO 还会动态调整采样策略。GRPO 的 $G$ 如果固定不变遇到奖励分布剧烈变化时就很被动。比如训练进入瓶颈期所有 candidate 的 advantage 全都在 0 附近这个 group 几乎没有提供任何有效梯度过一会儿模型突然学到某个模式优势分布又变得特别宽固定 $G$ 又可能采样不够。更实用的做法是基于策略熵或者 advantage 信号的方差来调整每个 prompt 的采样数量。策略熵高的时候说明模型输出还很随机可以多采样几条来稳定 baseline策略熵低的时候说明模型已经比较确定可以少采样几条省下算力去做更多 prompt 覆盖。这种采样调整同样不需要改动损失函数直接作用在数据 pipeline 上在长文本 RL 训练里收益非常明显。到 SAPO 这一步这条推导链的核心脉络已经清楚PPO 用 Critic 估计价值GRPO 用组内相对比较替代 CriticDAPO 把 clip 和 advantage 解耦GSPO 把组内统计量平滑化SAPO 再把 clip 和采样变成自适应。每一步都不是另起炉灶而是在前一步的短板上做定向修补。6. 实操对照从理论到训练配置前面讲了大量公式推导这里落回到实际训练配置。很多人在 PPO 到 GRPO 之间来回切换时感觉“好像差异不大”其实是因为没有理解优势估计的源头变了。下面这张表可以帮你快速对照。6.1 关键差异速查表算法优势来源是否依赖Critic核心机制典型适用场景PPOGAE依赖价值网络是裁剪 GAE传统控制任务、小规模RLGRPO组内归一化否组内相对优势LLM微调、规则奖励DAPO组内归一化 解耦否Decoupled Clip、Token级Loss长文本推理、稀疏奖励GSPO平滑组内归一化否EMA统计量降噪小组采样、噪声奖励SAPO自适应组内归一化否自适应Clip和采样训练后期、奖励分布偏移明显我实际跑 LLM 微调时最常用的组合是 GRPO 起步如果出现 loss 震荡或者单条回答的 reward 极端值太强就切 DAPO 风格的目标函数如果 reward 噪声大则把组内统计量换成 EMA也就是 GSPO 的做法到了训练后半段再打开自适应 clip。你完全不需要把它们当成五个互斥的算法它们更像五个可以叠加的 patch。训练循环的伪代码大概是这样的for step in range(max_steps): queries, responses, old_logprobs, rewards sample_group(prompts, policy, group_size8) # GRPO / GSPO: 组内优势归一化 if use_smoothed_stats: mu, sigma update_ema(mu, sigma, rewards) advantages (rewards - mu) / (sigma eps) else: advantages group_normalize(rewards) # 当前策略下重新计算 logprobs logprobs policy.get_logprobs(queries, responses) ratio (logprobs - old_logprobs).exp() if use_decoupled_clip: policy_loss -decoupled_clip_loss(ratio, advantages, eps) else: policy_loss -ppo_clip_loss(ratio, advantages, eps) kl_loss beta * kl_divergence(policy, ref_policy) loss policy_loss kl_loss optimizer.backward(loss)6.2 训练中经常踩的坑第一个坑是组内标准差为 0。规则奖励只有 0 和 1 时如果一个组里所有样本都是同一个分数GRPO 的 $\sigma$ 会直接变成 0。处理办法是加一个很小的 $\epsilon$但这只是兜底。更推荐的做法是采样组里至少保留一条正样本和一条负样本或者用 GSPO 的 EMA 统计量作为分母这样即使当前组没有差异也能参考历史分布给出稳定优势。第二个坑是 clip 范围设得和 PPO 一样。PPO 的 0.2 在价值网络环境下很好用但在 GRPO 这类组内相对优势里优势的绝对量级已经完全变了0.2 有时太紧有时太松。我一般从 0.25 到 0.3 起步然后根据训练 batch 中更新超过 clip 边界的样本比例做调整。如果超过 10% 的样本都到了 clip 边界说明范围太紧如果几乎碰不到边界说明范围太松。第三个坑是正负样本比例失衡。组内归一化后正优势样本和负优势样本数量应该是大致均衡的但因为采样方差可能出现一个 batch 里大部分样本都是正优势。这时候策略会整体提高一堆候选的概率等于在奖励噪声上过拟合。动态采样不只是 DAPO 的专利我在 GRPO 里也会强制每个 batch 保证一定的正负比例。第四个坑是 token 级 loss 的长度归一化。长回答的 token 数天然更多如果不对每个序列做长度归一化长回答会主导梯度导致模型偏向生成更长但不一定更正确的输出。我在实现里会给每个 token loss 除以序列长度并且对 padding 位置做 mask这已经是标配了。第五个坑是 KL 项的系数 $\beta$。在 LLM 强化学习里$\beta$ 太小会让模型很快在格式上跑偏生成一堆高分但语言质量糟糕的内容$\beta$ 太大会让策略学不动更新幅度被 KL 死死压住。一个相对实用的经验是先把 $\beta$ 设到和奖励尺度同量级比如 0.01然后看 KL 曲线的斜率。如果 KL 上涨过快就调大如果策略奖励不涨就调小。这些经验不是我拍脑袋想出来的而是在多次调参过程中反复撞出来的。尤其是“组大小”这个超参很多人喜欢设成 4因为省显存。但在长文本任务里组大小为 4 的组内均值和方差噪声非常大训练曲线经常像锯齿一样。我后来改成 8 或 16稳定性明显提升。如果你的显存不够优先减少组大小而不是减少 batch然后用 GSPO 的 EMA 统计量补救这样保住多样性的同时也能控制 advantage 的噪声。这条推导链走到 SAPO本质上是在回答一个问题优势估计到底应该相信多少、信任到什么程度、如何随着训练阶段变化。PPO 相信价值网络GRPO 相信群体比较DAPO 相信解耦后的独立信号GSPO 相信长期平滑的统计量SAPO 则开始根据当前证据动态调整信任半径。每一步的推导都能落脚到训练时的具体现象下次如果你的 loss 曲线又开始剧烈震荡不妨沿着这条链往前找一找是优势估计噪声太大还是 clip 边界不合时宜又或者是组内统计量太小。想清楚这一点改代码会比盲目调超参有用得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询