【LLM ✖️ 强化学习】— DPO、PPO、 GRPO、GSPO、DAPO、SAPO
本文系统梳理了语言模型强化学习的几类主流优化方法,并聚焦于其核心目标、算法思路与优缺点。PPO 通过 clipping 新旧策略概率比,稳定在线策略梯度更新,但需训练价值网络,计算开销大。DPO 利用偏好对直接优化策略,无需奖励模型,但严重依赖离线数据质量,且分布偏移敏感。GRPO 在 PPO 基础上省去价值网络,通过组内相对奖励构造优势,大幅降低了计算需求,但依赖成组采样,推理成本增加。G
【LLM ✖️ 强化学习】— DPO、PPO、 GRPO、GSPO、DAPO、SAPO
-
- PPO(Proximal Policy Optimization)
- DPO(Direct Preference Optimization)
- GRPO(Group Relative Policy Optimization)
- GSPO(Group Sequence Policy Optimization)
- DAPO(Decoupled Clip and Dynamic Sampling Policy Optimization)
- 1. 目标函数(训练时通常最小化其相反数)
- 2. 符号与每一项在做什么
- 3. DAPO 的 4 个关键改动点(相对朴素 PPO/GRPO)
- 4. 伪代码(核心实现)
- SAPO(Soft Adaptive Policy Optimization)
PPO(Proximal Policy Optimization)
PPO 通过限制新旧策略差异,让策略梯度更新更稳定,常用做法是对“概率比”做 clipping。
核心目标函数(PPO-Clip)
PPO-Clip 的策略目标(最大化)为:
L C L I P ( θ ) = E t [ min ( r t ( θ ) A ^ t , c l i p ( r t ( θ ) , 1 − ϵ , 1 + ϵ ) A ^ t ) ] L^{\mathrm{CLIP}}(\theta) =\mathbb{E}_t\Big[ \min\big( r_t(\theta)\,\hat A_t,\; \mathrm{clip}(r_t(\theta),1-\epsilon,1+\epsilon)\,\hat A_t \big) \Big] LCLIP(θ)=Et[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)]
符号解释
- s t s_t st:时刻 t t t 的状态
- a t a_t at:在 s t s_t st 下执行的动作
- π θ ( a t ∣ s t ) \pi_\theta(a_t\mid s_t) πθ(at∣st):当前策略(待更新)对动作的概率
- π o l d ( a t ∣ s t ) \pi_{\mathrm{old}}(a_t\mid s_t) πold(at∣st):采样这批数据时的旧策略(更新前冻结)
- 策略概率比(policy ratio):
r t ( θ ) = π θ ( a t ∣ s t ) π o l d ( a t ∣ s t ) r_t(\theta)=\frac{\pi_\theta(a_t\mid s_t)}{\pi_{\mathrm{old}}(a_t\mid s_t)} rt(θ)=πold(at∣st)πθ(at∣st)
- A ^ t \hat A_t A^t:优势函数(例如 GAE 估计),表示该动作相对基线“好多少/差多少”
- ϵ \epsilon ϵ:clipping 阈值,限制 r t r_t rt 的有效范围为 [ 1 − ϵ , 1 + ϵ ] [1-\epsilon,\,1+\epsilon] [1−ϵ,1+ϵ]
直觉怎么理解 min 和 clip
- 当 A ^ t > 0 \hat A_t>0 A^t>0:希望提高该动作概率(让 r t r_t rt 变大),但当 r t > 1 + ϵ r_t>1+\epsilon rt>1+ϵ 后,clip 会把增益按住,min 会选择更保守项,避免更新过猛。
- 当 A ^ t < 0 \hat A_t<0 A^t<0:希望降低该动作概率(让 r t r_t rt 变小),但当 r t < 1 − ϵ r_t<1-\epsilon rt<1−ϵ 后,同样会被 clip 限制,避免一次压得太狠。
代码示例(对应上面的 L C L I P L^{\mathrm{CLIP}} LCLIP)
# new_log_prob = log pi_theta(a_t|s_t)
# old_log_prob = log pi_old(a_t|s_t)
# adv = A_hat_t
# epsilon = clip threshold
ratio = torch.exp(new_log_prob - old_log_prob) # r_t(theta)
surr1 = ratio * adv # r_t * A_hat
surr2 = torch.clamp(ratio, 1 - epsilon, 1 + epsilon) * adv # clip(r_t) * A_hat
# maximize L^CLIP <=> minimize negative
loss_policy = -torch.min(surr1, surr2).mean()
备注:完整 PPO 训练里通常还会加 value loss(拟合 V θ ( s ) V_\theta(s) Vθ(s))和 entropy bonus(鼓励探索),但上面这段就是 PPO-Clip 的核心策略项。
优点
- 训练稳定
- 支持在线学习,通过与环境交互实时更新策略
缺点
- 计算开销大,需要训练Policy和Critic模型,同时加载Reference和Reward模型推理
- 依赖奖励模型
DPO(Direct Preference Optimization)
一句话:DPO 直接用“偏好对”训练策略模型,用一个二分类式的对比损失让 chosen 的相对概率高于 rejected,同时用参考模型约束策略漂移。
核心损失函数(DPO Loss)
给定偏好对数据 ( x , y w , y l ) (x, y_w, y_l) (x,yw,yl),DPO 的常用损失为:
L D P O ( θ ) = − E ( x , y w , y l ) ∼ D [ log σ ( β ⋅ Δ θ ( x , y w , y l ) ) ] \mathcal{L}_{\mathrm{DPO}}(\theta) =-\mathbb{E}_{(x,y_w,y_l)\sim \mathcal{D}} \left[ \log \sigma\!\left( \beta \cdot \Delta_\theta(x,y_w,y_l) \right) \right] LDPO(θ)=−E(x,yw,yl)∼D[logσ(β⋅Δθ(x,yw,yl))]
其中
Δ θ ( x , y w , y l ) = ( log π θ ( y w ∣ x ) − log π θ ( y l ∣ x ) ) − ( log π r e f ( y w ∣ x ) − log π r e f ( y l ∣ x ) ) \Delta_\theta(x,y_w,y_l) =\Big(\log \pi_\theta(y_w|x)-\log \pi_\theta(y_l|x)\Big) -\Big(\log \pi_{\mathrm{ref}}(y_w|x)-\log \pi_{\mathrm{ref}}(y_l|x)\Big) Δθ(x,yw,yl)=(logπθ(yw∣x)−logπθ(yl∣x))−(logπref(yw∣x)−logπref(yl∣x))
你也可以把它写成“相对参考模型的 log-ratio 差”更直观。定义
r θ ( x , y ) = log π θ ( y ∣ x ) − log π r e f ( y ∣ x ) r_\theta(x,y)=\log \pi_\theta(y|x)-\log \pi_{\mathrm{ref}}(y|x) rθ(x,y)=logπθ(y∣x)−logπref(y∣x)
则
Δ θ ( x , y w , y l ) = r θ ( x , y w ) − r θ ( x , y l ) \Delta_\theta(x,y_w,y_l)=r_\theta(x,y_w)-r_\theta(x,y_l) Δθ(x,yw,yl)=rθ(x,yw)−rθ(x,yl)
符号解释
- x x x:prompt / 指令
- y w y_w yw:更偏好的回答(chosen / winner)
- y l y_l yl:更不偏好的回答(rejected / loser)
- π θ ( y ∣ x ) \pi_\theta(y|x) πθ(y∣x):当前要训练的语言模型策略
- π r e f ( y ∣ x ) \pi_{\mathrm{ref}}(y|x) πref(y∣x):参考策略(常用 SFT 模型冻结拷贝)
- β > 0 \beta>0 β>0:温度系数,控制偏好推动强度,也影响“贴近参考策略”的程度
log π ( y ∣ x ) \log \pi(y|x) logπ(y∣x) 在 LLM 里怎么计算
对自回归语言模型,完整回答 y = ( y 1 , … , y T ) y=(y_1,\dots,y_T) y=(y1,…,yT) 的对数概率通常按 token 求和:
log π θ ( y ∣ x ) = ∑ t = 1 T log π θ ( y t ∣ x , y < t ) \log \pi_\theta(y|x)=\sum_{t=1}^{T}\log \pi_\theta(y_t\mid x,y_{<t}) logπθ(y∣x)=t=1∑Tlogπθ(yt∣x,y<t)
工程上一般只对 completion(回答部分)计分,用 mask 排除 prompt 与 padding。
直觉怎么理解这个损失
- Δ θ > 0 \Delta_\theta>0 Δθ>0 表示:相对参考模型,当前策略更偏向 y w y_w yw 而不是 y l y_l yl
- 损失里的 log σ ( β Δ θ ) \log\sigma(\beta\Delta_\theta) logσ(βΔθ) 是一个“偏好二分类”的对数似然
- 训练会推动 Δ θ \Delta_\theta Δθ 变大,也就是把概率质量从 rejected 挪向 chosen,但参考模型项会抑制无界漂移
代码示例(关键部分,对应上面的 DPO Loss)
# inputs: batch of (x, y_w, y_l)
# pi: policy model (trainable), ref: reference model (frozen)
# beta: temperature
# 1) compute sequence log-prob for chosen / rejected under policy and reference
logp_w = seq_logprob(pi, x, y_w) # = sum_t log pi(y_t | x, y_<t) on completion tokens
logp_l = seq_logprob(pi, x, y_l)
ref_logp_w = seq_logprob(ref, x, y_w)
ref_logp_l = seq_logprob(ref, x, y_l)
# 2) DPO logit: beta * Delta_theta
delta = (logp_w - logp_l) - (ref_logp_w - ref_logp_l)
logits = beta * delta
# 3) DPO loss (maximize log sigma => minimize negative)
loss = -torch.logsigmoid(logits).mean()
# optional diagnostics often used in practice
reward_w = (beta * (logp_w - ref_logp_w)).detach()
reward_l = (beta * (logp_l - ref_logp_l)).detach()
acc = (reward_w > reward_l).float().mean()
优点
- 无需奖励模型,直接通过数据偏好优化模型
- 显存占用低,可快速微调模型
- 适合离线场景,在语言模型等对齐任务表现优异
缺点
- 分布偏移敏感
- 依赖高质量数据
- KL散度的约束会被弱化
GRPO(Group Relative Policy Optimization)
GRPO 是一种“去掉 critic 的 PPO 变体”,对同一条 prompt 一次采样一组回答,用组内相对奖励当作 baseline 来构造优势函数,再用 PPO-Clip 方式更新策略。
核心目标函数(先给形式)
对每个问题 q q q,从旧策略 π θ o l d \pi_{\theta_{\mathrm{old}}} πθold 采样 G G G 个输出 { o i } i = 1 G \{o_i\}_{i=1}^G {oi}i=1G,GRPO 要最大化的目标可写为:
J G R P O ( θ ) = E [ 1 G ∑ i = 1 G 1 ∣ o i ∣ ∑ t = 1 ∣ o i ∣ ( min ( r i , t ( θ ) A ^ i , t , c l i p ( r i , t ( θ ) , 1 − ϵ , 1 + ϵ ) A ^ i , t ) − β D K L ( π θ ∥ π r e f ) ) ] J_{\mathrm{GRPO}}(\theta) =\mathbb{E}\left[ \frac{1}{G}\sum_{i=1}^{G}\frac{1}{|o_i|}\sum_{t=1}^{|o_i|} \left( \min\Big( r_{i,t}(\theta)\,\hat A_{i,t},\; \mathrm{clip}(r_{i,t}(\theta),1-\epsilon,1+\epsilon)\,\hat A_{i,t} \Big) -\beta\, D_{\mathrm{KL}}(\pi_\theta\|\pi_{\mathrm{ref}}) \right) \right] JGRPO(θ)=E G1i=1∑G∣oi∣1t=1∑∣oi∣(min(ri,t(θ)A^i,t,clip(ri,t(θ),1−ϵ,1+ϵ)A^i,t)−βDKL(πθ∥πref))
其中 token 级策略比为:
r i , t ( θ ) = π θ ( o i , t ∣ q , o i , < t ) π θ o l d ( o i , t ∣ q , o i , < t ) r_{i,t}(\theta)= \frac{\pi_\theta(o_{i,t}\mid q,o_{i,<t})}{\pi_{\theta_{\mathrm{old}}}(o_{i,t}\mid q,o_{i,<t})} ri,t(θ)=πθold(oi,t∣q,oi,<t)πθ(oi,t∣q,oi,<t)
符号解释
- q q q:prompt / 问题
- o i o_i oi:对同一 q q q 采样得到的第 i i i 个输出序列,长度为 ∣ o i ∣ |o_i| ∣oi∣,第 t t t 个 token 为 o i , t o_{i,t} oi,t
- π θ \pi_\theta πθ:当前待更新策略(policy 模型)
- π θ o l d \pi_{\theta_{\mathrm{old}}} πθold:采样这批输出时冻结的旧策略
- π r e f \pi_{\mathrm{ref}} πref:参考策略(通常是 SFT 模型冻结拷贝),用于 KL 约束
- ϵ \epsilon ϵ:clipping 阈值,限制每次更新的相对幅度
- β \beta β:KL 系数,越大越“贴近参考策略”
- A ^ i , t \hat A_{i,t} A^i,t:优势函数(GRPO 的关键在于它来自“组内相对奖励”,不需要 critic)
优势函数怎么来(最常用的 outcome supervision)
先用奖励模型或可验证规则给每个输出打一个标量奖励 r i r_i ri,对组内做标准化:
r ~ i = r i − m e a n ( r ) s t d ( r ) \tilde r_i=\frac{r_i-\mathrm{mean}(r)}{\mathrm{std}(r)} r~i=std(r)ri−mean(r)
然后把整条序列的每个 token 都用同一个优势:
A ^ i , t = r ~ i \hat A_{i,t}=\tilde r_i A^i,t=r~i
直观上:同一题里,回答比组内平均更好就得到正优势,反之为负优势,组内均值就成了 baseline。
代码示例
# For each prompt q in a batch:
# 1) sample G completions from old policy pi_old
# 2) score each completion with reward_model -> r_i
# 3) normalize rewards within the group -> advantage A_i
# 4) PPO-clip style update using token-level ratios; optional KL to reference
# hyperparams: epsilon, beta
# models: pi_theta (trainable), pi_old (frozen snapshot), pi_ref (frozen reference)
losses = []
for q in batch_prompts:
o_list = sample(pi_old, q, num_samples=G) # {o_i}
r = [reward_model(q, o_i) for o_i in o_list] # {r_i}
r_mean, r_std = mean(r), std(r) + 1e-8
A = [(ri - r_mean) / r_std for ri in r] # \tilde r_i
for i, o_i in enumerate(o_list):
# token-level logprobs under current / old / ref
logp_new = token_logprob(pi_theta, q, o_i) # shape [T]
logp_old = token_logprob(pi_old, q, o_i) # shape [T]
logp_ref = token_logprob(pi_ref, q, o_i) # shape [T]
ratio = exp(logp_new - logp_old) # r_{i,t}(theta)
surr1 = ratio * A[i] # broadcast over tokens
surr2 = clip(ratio, 1-epsilon, 1+epsilon) * A[i]
# KL term can be token-level estimate; keep it as kl_t here
kl_t = approx_kl(logp_new, logp_ref) # shape [T]
# maximize objective <=> minimize negative
per_token_obj = min(surr1, surr2) - beta * kl_t
loss_i = -mean(per_token_obj) # average over tokens
losses.append(loss_i)
loss = mean(losses) # average over group and batch
loss.backward()
optimizer.step()
优点
- 不训练 value/critic,显著降低显存与计算开销,工程实现更轻量。
- 组内相对奖励天然匹配“同题多回答对比”的数据形态,baseline 直接来自组均值。
- 仍保留 PPO-Clip 的稳定更新机制,能在一定程度上抑制策略突变。
缺点
- 需要对每个 prompt 采样 G G G 个输出,生成成本随 G G G 线性增长,吞吐容易成为瓶颈。
- 组内标准化带来偏置风险,比如长度偏置、题目难度偏置等,需要额外处理或改损失。
- 当组内奖励差异很小(甚至全都一样)时,优势接近 0,更新会变弱,训练可能停滞。
GSPO(Group Sequence Policy Optimization)
GSPO 用序列级的重要性比率 s i ( θ ) s_i(\theta) si(θ) 做序列级 clipping,再配合“同一 query 的组内相对优势”更新策略,目的是让大模型 RL 训练更稳、更高效。
1) 核心目标函数(先给形式)
对每个 query x x x,从旧策略 π θ o l d \pi_{\theta_{\mathrm{old}}} πθold 采样一组回答 { y i } i = 1 G \{y_i\}_{i=1}^{G} {yi}i=1G,GSPO 最大化:
J G S P O ( θ ) = E x ∼ D , { y i } i = 1 G ∼ π θ o l d ( ⋅ ∣ x ) [ 1 G ∑ i = 1 G min ( s i ( θ ) A ^ i , c l i p ( s i ( θ ) , 1 − ϵ , 1 + ϵ ) A ^ i ) ] . J_{\mathrm{GSPO}}(\theta) =\mathbb{E}_{x\sim D,\ \{y_i\}_{i=1}^{G}\sim \pi_{\theta_{\mathrm{old}}}(\cdot|x)} \left[ \frac{1}{G}\sum_{i=1}^{G} \min\Big( s_i(\theta)\,\hat A_i,\; \mathrm{clip}(s_i(\theta),1-\epsilon,1+\epsilon)\,\hat A_i \Big) \right]. JGSPO(θ)=Ex∼D, {yi}i=1G∼πθold(⋅∣x)[G1i=1∑Gmin(si(θ)A^i,clip(si(θ),1−ϵ,1+ϵ)A^i)].
- ϵ \epsilon ϵ:clipping 范围
- 论文推导里通常先把 KL 正则项省略(不是重点);工程实现里常额外加到目标里作为惩罚项。
2) 符号与基本设定
- x x x:query / prompt
- D D D:query 集合
- y i = ( y i , 1 , … , y i , ∣ y i ∣ ) y_i=(y_{i,1},\ldots,y_{i,|y_i|}) yi=(yi,1,…,yi,∣yi∣):对同一 x x x 采样得到的第 i i i 个完整回答序列
- G G G:每个 x x x 采样的回答数(group size)
- π θ \pi_\theta πθ:当前待训练的策略模型
- π θ o l d \pi_{\theta_{\mathrm{old}}} πθold:采样这批数据时冻结的旧策略
自回归语言模型的序列似然:
π θ ( y i ∣ x ) = ∏ t = 1 ∣ y i ∣ π θ ( y i , t ∣ x , y i , < t ) . \pi_\theta(y_i|x)=\prod_{t=1}^{|y_i|}\pi_\theta(y_{i,t}\mid x,y_{i,<t}). πθ(yi∣x)=t=1∏∣yi∣πθ(yi,t∣x,yi,<t).
3) 组内优势 A ^ i \hat A_i A^i(不用 critic)
GSPO 采用组内标准化的优势(所有 token 共享同一个序列级优势):
A ^ i = r ( x , y i ) − m e a n ( { r ( x , y j ) } j = 1 G ) s t d ( { r ( x , y j ) } j = 1 G ) . \hat A_i =\frac{ r(x,y_i)-\mathrm{mean}\big(\{r(x,y_j)\}_{j=1}^{G}\big) }{ \mathrm{std}\big(\{r(x,y_j)\}_{j=1}^{G}\big) }. A^i=std({r(x,yj)}j=1G)r(x,yi)−mean({r(x,yj)}j=1G).
- r ( x , y i ) r(x,y_i) r(x,yi):verifier / reward model 对整段回答的打分(标量)
- 这是 GRPO 的“组内相对优势”思路,但 GSPO 会把优化粒度也提升到序列级。
4) 序列级策略比 s i ( θ ) s_i(\theta) si(θ)
GSPO 的关键是:重要性比率不再是 token 级 w i , t ( θ ) w_{i,t}(\theta) wi,t(θ),而是序列级 s i ( θ ) s_i(\theta) si(θ),并且做了长度归一化:
s i ( θ ) = ( π θ ( y i ∣ x ) π θ o l d ( y i ∣ x ) ) 1 ∣ y i ∣ . s_i(\theta) =\left( \frac{\pi_\theta(y_i|x)}{\pi_{\theta_{\mathrm{old}}}(y_i|x)} \right)^{\frac{1}{|y_i|}}. si(θ)=(πθold(yi∣x)πθ(yi∣x))∣yi∣1.
等价的 log 形式(实现更稳):
log s i ( θ ) = 1 ∣ y i ∣ ( log π θ ( y i ∣ x ) − log π θ o l d ( y i ∣ x ) ) = 1 ∣ y i ∣ ∑ t = 1 ∣ y i ∣ log π θ ( y i , t ∣ x , y i , < t ) π θ o l d ( y i , t ∣ x , y i , < t ) . \log s_i(\theta) =\frac{1}{|y_i|} \left( \log \pi_\theta(y_i|x)-\log \pi_{\theta_{\mathrm{old}}}(y_i|x) \right) =\frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta(y_{i,t}\mid x,y_{i,<t})}{\pi_{\theta_{\mathrm{old}}}(y_{i,t}\mid x,y_{i,<t})}. logsi(θ)=∣yi∣1(logπθ(yi∣x)−logπθold(yi∣x))=∣yi∣1t=1∑∣yi∣logπθold(yi,t∣x,yi,<t)πθ(yi,t∣x,yi,<t).
直观解释:
- s i ( θ ) s_i(\theta) si(θ) 是“新旧策略对整段回答的概率比”,再取 1 ∣ y i ∣ \frac{1}{|y_i|} ∣yi∣1 次幂,相当于对 token 比率取几何平均。
- 这么做是为了把不同长度回答的尺度拉齐,降低方差,也让 clipping 的数值范围更可控。
5) GSPO 更新在做什么
对每个回答 y i y_i yi:
- 用奖励 r ( x , y i ) r(x,y_i) r(x,yi) 得到组内优势 A ^ i \hat A_i A^i
- 计算序列级比率 s i ( θ ) s_i(\theta) si(θ)
- 用 min ( ⋅ ) \min(\cdot) min(⋅) 形成“保守”的 PPO-Clip 风格目标
- 对 − J G S P O ( θ ) -J_{\mathrm{GSPO}}(\theta) −JGSPO(θ) 做梯度下降更新参数 θ \theta θ
关键区别:
- GRPO:token 级比率 w i , t w_{i,t} wi,t + token 级 clipping
- GSPO:序列级比率 s i s_i si + 序列级 clipping(对整个回答一起裁剪)
6) 伪代码(贴合上面的目标函数)
# GSPO pseudocode (objective-aligned)
# Inputs:
# pi_theta: trainable policy
# pi_old: frozen snapshot used to sample rollouts
# reward_fn: scalar reward r(x, y)
# Hyperparams:
# G: group size, epsilon: clip range
losses = []
for x in batch_queries:
# 1) sample a group of sequences from old policy
ys = sample(pi_old, x, num_samples=G) # y_1 ... y_G
# 2) compute scalar rewards
r = [reward_fn(x, y) for y in ys] # r_i
# 3) group-based advantage (z-score within the group)
r_mean = mean(r)
r_std = std(r) + 1e-8
A = [(ri - r_mean) / r_std for ri in r] # A_i
# 4) sequence-level ratio + PPO-style clipping
for i, y in enumerate(ys):
# sequence logprob on completion tokens
logp_new = seq_logprob(pi_theta, x, y) # log pi_theta(y|x)
logp_old = seq_logprob(pi_old, x, y) # log pi_old(y|x)
L = len(y) + 1e-8
s = exp((logp_new - logp_old) / L) # s_i(theta)
obj1 = s * A[i]
obj2 = clamp(s, 1 - epsilon, 1 + epsilon) * A[i]
# maximize min(obj1, obj2) <=> minimize negative
loss_i = -min(obj1, obj2)
losses.append(loss_i)
loss = mean(losses)
loss.backward()
optimizer.step()
优点
- 更稳:用序列级 ratio 并让同一回答内 token 等权,有助于减少 token 级重要性权重累积带来的不稳定。
- 更匹配奖励粒度:奖励通常是序列级标量,GSPO 直接做序列级 clipping 与优化,reward 粒度与优化粒度一致。
- 更易扩展到大模型与 MoE:论文与官方总结强调其在大规模训练、尤其 MoE RL 上更稳定,并有潜力简化 RL 基础设施。
缺点
- 对 ε \varepsilon ε 更敏感:由于 s i ( θ ) s_i(\theta) si(θ) 的定义不同,clipping 阈值的数量级往往与 token-level 方法不同,需要重新调参。
- 可能更“激进地裁剪样本”:序列级 clipping 的裁剪范围一旦触发,会对整段回答的更新一起按住,训练中被 clip 的比例可能显著高于 token-level。
- 组内奖励方差过小会变弱:当同一 query 的 G G G 个回答打分接近时, A ^ i \widehat{A}_i A i 接近 0,梯度会变小,收敛速度容易受影响。
DAPO(Decoupled Clip and Dynamic Sampling Policy Optimization)
DAPO 是一种面向可验证任务(如数学题)的 LLM 强化学习训练算法,在 GRPO 的框架上进一步通过“非对称 clipping + 动态采样 + token 级损失聚合 + 过长样本奖励整形”提升稳定性与效率。
说明:这里的 DAPO 指的是 Yu et al. 2025 提出的 “Decoupled Clip and Dynamic Sampling Policy Optimization”,不是 Meta 那篇 “Dual Approximation Policy Optimization”。
1. 目标函数(训练时通常最小化其相反数)
DAPO 的优化目标写成“最大化”形式为:
J DAPO ( θ ) = E ( q , a ) ∼ D , { o i } i = 1 G ∼ π θ old ( ⋅ ∣ q ) [ 1 ∑ i = 1 G ∣ o i ∣ ∑ i = 1 G ∑ t = 1 ∣ o i ∣ min ( r i , t ( θ ) A ^ i , t , clip ( r i , t ( θ ) , 1 − ε low , 1 + ε high ) A ^ i , t ) ] \mathcal{J}_{\text{DAPO}}(\theta) =\mathbb{E}_{(q,a)\sim \mathcal{D},\,\{o_i\}_{i=1}^{G}\sim \pi_{\theta_{\text{old}}}(\cdot|q)} \left[ \frac{1}{\sum_{i=1}^{G}|o_i|} \sum_{i=1}^{G}\sum_{t=1}^{|o_i|} \min\Big( r_{i,t}(\theta)\,\hat A_{i,t}, \operatorname{clip}(r_{i,t}(\theta),\,1-\varepsilon_{\text{low}},\,1+\varepsilon_{\text{high}})\,\hat A_{i,t} \Big) \right] JDAPO(θ)=E(q,a)∼D,{oi}i=1G∼πθold(⋅∣q) ∑i=1G∣oi∣1i=1∑Gt=1∑∣oi∣min(ri,t(θ)A^i,t,clip(ri,t(θ),1−εlow,1+εhigh)A^i,t)
并带有一个“组内必须有对有错”的约束(用于动态采样过滤):
0 < ∣ { o i : is_equivalent ( a , o i ) } ∣ < G 0 < \left|\{o_i:\text{is\_equivalent}(a,o_i)\}\right| < G 0< {oi:is_equivalent(a,oi)} <G
训练实现里通常最小化损失:
L DAPO ( θ ) = − J DAPO ( θ ) \mathcal{L}_{\text{DAPO}}(\theta) = -\mathcal{J}_{\text{DAPO}}(\theta) LDAPO(θ)=−JDAPO(θ)
2. 符号与每一项在做什么
2.1 采样对象与组(group)
- q q q:prompt(题目)
- a a a:标准答案(ground truth)
- D \mathcal{D} D:训练数据分布
- 对每个 q q q,用旧策略(行为策略) π θ old \pi_{\theta_{\text{old}}} πθold 采样 G G G 个输出序列:
- { o i } i = 1 G \{o_i\}_{i=1}^{G} {oi}i=1G:第 i i i 个完整输出(一段长文本)
- ∣ o i ∣ |o_i| ∣oi∣:第 i i i 个输出的 token 数
- o i , t o_{i,t} oi,t:第 i i i 个输出的第 t t t 个 token
- o i , < t o_{i,<t} oi,<t:其前缀
2.2 “策略概率比” r i , t ( θ ) r_{i,t}(\theta) ri,t(θ)(importance sampling ratio)
它衡量“新策略相对旧策略”在该 token 上的概率变化:
r i , t ( θ ) = π θ ( o i , t ∣ q , o i , < t ) π θ old ( o i , t ∣ q , o i , < t ) r_{i,t}(\theta) =\frac{\pi_{\theta}(o_{i,t}\mid q,o_{i,<t})}{\pi_{\theta_{\text{old}}}(o_{i,t}\mid q,o_{i,<t})} ri,t(θ)=πθold(oi,t∣q,oi,<t)πθ(oi,t∣q,oi,<t)
直觉上:
- r i , t > 1 r_{i,t}>1 ri,t>1 表示新策略更倾向生成这个 token
- r i , t < 1 r_{i,t}<1 ri,t<1 表示新策略在压低这个 token 的概率
2.3 奖励 R i R_i Ri 与优势 A ^ i , t \hat A_{i,t} A^i,t
DAPO 在可验证任务里常用规则奖励(对就 +1,不对就 -1):
R i = R ( y ^ i , a ) = { 1 , is_equivalent ( y ^ i , a ) − 1 , otherwise R_i = R(\hat y_i, a)= \begin{cases} 1, & \text{is\_equivalent}(\hat y_i,a)\\ -1, & \text{otherwise} \end{cases} Ri=R(y^i,a)={1,−1,is_equivalent(y^i,a)otherwise
其中 y ^ i \hat y_i y^i 是从 o i o_i oi 解析出来的最终答案。
然后把组内奖励标准化成“组相对优势”,并把它广播到该样本的每个 token(所以写成 A ^ i , t \hat A_{i,t} A^i,t):
A ^ i , t = R i − mean ( { R j } j = 1 G ) std ( { R j } j = 1 G ) \hat A_{i,t} =\frac{R_i-\operatorname{mean}(\{R_j\}_{j=1}^{G})}{\operatorname{std}(\{R_j\}_{j=1}^{G})} A^i,t=std({Rj}j=1G)Ri−mean({Rj}j=1G)
组相对优势的作用是把更新方向变成:
- 比组内平均更好的样本:整体提升其 token 概率
- 比组内平均更差的样本:整体压低其 token 概率
3. DAPO 的 4 个关键改动点(相对朴素 PPO/GRPO)
3.1 Clip-Higher:非对称 clipping(“抬高上限”)
标准 PPO/GRPO 往往用同一个 ε \varepsilon ε 做对称裁剪。DAPO 把裁剪区间拆成两侧不同的参数:
- 下裁剪: 1 − ε low 1-\varepsilon_{\text{low}} 1−εlow
- 上裁剪: 1 + ε high 1+\varepsilon_{\text{high}} 1+εhigh,并让 ε high > ε low \varepsilon_{\text{high}}>\varepsilon_{\text{low}} εhigh>εlow
对应的 clipped ratio:
clip ( r , 1 − ε low , 1 + ε high ) \operatorname{clip}(r,1-\varepsilon_{\text{low}},1+\varepsilon_{\text{high}}) clip(r,1−εlow,1+εhigh)
动机:上裁剪太小会让“低概率 token 的概率提升”很难发生,训练中容易熵塌缩,生成变得过早确定化。
3.2 Dynamic Sampling:动态采样与组过滤
当某个 prompt 的 G G G 个采样要么全对要么全错时,组内奖励几乎相同,优势会接近 0,梯度也接近 0,等价于“有效 batch 变小”。
所以 DAPO 的做法是过采样,然后过滤掉“全对”或“全错”的组,只保留满足:
0 < ∣ { o i : is_equivalent ( a , o i ) } ∣ < G 0 < \left|\{o_i:\text{is\_equivalent}(a,o_i)\}\right| < G 0< {oi:is_equivalent(a,oi)} <G
这样每个训练 batch 里保留更多“有区分度”的学习信号。
3.3 Token-level Policy Gradient Loss:token 级聚合
朴素 GRPO 常见的聚合方式是“先对每条样本做 token 平均,再对样本平均”,这会让长样本里的每个 token 权重偏小,不利于 long-CoT 学习。
DAPO 用 token 级聚合,直接对所有 token 做平均:
1 ∑ i = 1 G ∣ o i ∣ ∑ i = 1 G ∑ t = 1 ∣ o i ∣ ( ⋅ ) \frac{1}{\sum_{i=1}^{G}|o_i|}\sum_{i=1}^{G}\sum_{t=1}^{|o_i|}(\cdot) ∑i=1G∣oi∣1i=1∑Gt=1∑∣oi∣(⋅)
直觉:长推理链条里出现的模式不会因为“样本太长”而被稀释。
3.4 Overlong Reward Shaping:过长样本奖励整形
生成超过最大长度时,直接打一个强惩罚会引入噪声,因为“推理过程可能是对的,只是没写完”。
DAPO 里给出一个长度惩罚函数(soft punishment),在接近上限时逐渐变负:
设
- ∣ y ∣ |y| ∣y∣:输出长度
- L max L_{\max} Lmax:最大长度
- L cache L_{\text{cache}} Lcache:软惩罚缓冲区长度
R length ( y ) = { 0 , ∣ y ∣ ≤ L max − L cache ( L max − L cache ) − ∣ y ∣ L cache , L max − L cache < ∣ y ∣ ≤ L max − 1 , L max < ∣ y ∣ R_{\text{length}}(y)= \begin{cases} 0, & |y|\le L_{\max}-L_{\text{cache}}\\ \frac{(L_{\max}-L_{\text{cache}})-|y|}{L_{\text{cache}}}, & L_{\max}-L_{\text{cache}}<|y|\le L_{\max}\\ -1, & L_{\max}<|y| \end{cases} Rlength(y)=⎩ ⎨ ⎧0,Lcache(Lmax−Lcache)−∣y∣,−1,∣y∣≤Lmax−LcacheLmax−Lcache<∣y∣≤LmaxLmax<∣y∣
训练时可把它加到正确性奖励上:
R i ← R i + R length ( o i ) R_i \leftarrow R_i + R_{\text{length}}(o_i) Ri←Ri+Rlength(oi)
4. 伪代码(核心实现)
# inputs:
# pi : current policy π_θ
# pi_old : behavior policy π_{θ_old} (frozen snapshot for rollout)
# q : prompt
# outs : G sampled responses [o_1, ..., o_G] from pi_old
# R : scalar rewards per response [R_1, ..., R_G]
# hyper:
# eps_low, eps_high
# (optional) dynamic sampling filter: require not-all-correct and not-all-wrong
# if sum(correct_flags) in {0, G}: resample outs
# group-relative advantage (broadcast to tokens)
A = (R - mean(R)) / (std(R) + 1e-8) # A_i
total = 0.0
ntok = 0
for i, o in enumerate(outs):
logp_new = token_logprob(pi, q, o) # [|o|]
logp_old = token_logprob(pi_old, q, o) # [|o|]
ratio = exp(logp_new - logp_old) # r_{i,t}(θ)
ratio_c = clamp(ratio, 1-eps_low, 1+eps_high) # Clip-Higher (asymmetric)
surr1 = ratio * A[i]
surr2 = ratio_c * A[i]
total += sum(minimum(surr1, surr2)) # sum over tokens
ntok += len(o)
loss = -(total / ntok) # token-level mean loss
优点
- 更稳定:非对称 clipping 与过长样本奖励整形,针对 long-CoT 的熵塌缩与奖励噪声做了直接修补。
- 更高效:动态采样过滤掉零梯度组,提升有效样本比例,减少“算了但没学到”的更新。
- 更适配 long-CoT:token 级聚合避免长样本信号被稀释,更容易强化“中间推理步骤”的模式。
缺点
- 对任务形态有前提:规则奖励依赖“可验证”或可稳定判等的任务,开放式偏好对齐不一定适用。
- 系统复杂度更高:动态采样与过滤会改变数据流与吞吐,工程实现和调参成本更高。
- 可能引入分布偏置:过滤“全对/全错”组会改变训练分布,若过滤策略不当,可能导致泛化或校准问题。
SAPO(Soft Adaptive Policy Optimization)
这里的 SAPO 指 Soft Adaptive Policy Optimization(Qwen 团队提出的软门控替代硬 clipping 的 group-based policy optimization),不是 “Swarm sAmpling Policy Optimization”等同名缩写。
重要性比率通常表现出高方差 —— 这种现象在专家混合模型中更为严重 —— 导致不稳定的更新。现有的基于组的策略优化方法,如 GSPO 和 GRPO,通过硬裁剪来缓解这个问题,但这使得同时保持稳定性和有效学习变得困难。我们提出了软自适应策略优化(SAPO),它用一个平滑的、温度控制的门限取代硬裁剪,在保留有用学习信号的同时自适应地减弱离策略更新。与 GSPO 和 GRPO 相比,SAPO 既具有序列一致性又具有令牌适应性。与 GSPO 一样,SAPO 保持序列级一致性,但其软门限形成一个连续的信任区域,避免了 GSPO 中使用的脆弱的硬裁剪边界。当一个序列包含一些高度离策略的令牌时,GSPO 会抑制该序列的所有梯度,而 SAPO 则有选择地仅降低违规令牌的权重,并保留来自接近策略令牌的学习信号,从而提高样本效率。相对于 GRPO,SAPO 用平滑的、温度控制的缩放取代硬令牌级裁剪,实现更具信息性和稳定性的更新。在数学推理基准测试上的实证结果表明,在可比的训练预算下,SAPO 表现出更好的训练稳定性和更高的一次通过率(Pass@1)性能。此外,我们使用 SAPO 来训练 Qwen3 - VL 模型系列,证明 SAPO 在各种任务和不同模型规模上都能带来一致的性能提升。总体而言,SAPO 为大语言模型(LLMs)的强化学习训练提供了一种更可靠、可扩展且有效的优化策略。
1. 损失函数(最小化形式)
给定每个 prompt q q q 采样得到一组响应 { y i } i = 1 G \{y_i\}_{i=1}^{G} {yi}i=1G(由旧策略 π θ old \pi_{\theta_{\text{old}}} πθold 生成),SAPO 的 token 级 surrogate 可以写成:
L SAPO ( θ ) = − 1 G ∑ i = 1 G 1 ∣ y i ∣ ∑ t = 1 ∣ y i ∣ f SAPO ( r i , t ( θ ) ) A ^ i \mathcal{L}_{\text{SAPO}}(\theta) =-\frac{1}{G}\sum_{i=1}^{G}\frac{1}{|y_i|}\sum_{t=1}^{|y_i|} f_{\text{SAPO}}\!\left(r_{i,t}(\theta)\right)\,\hat A_i LSAPO(θ)=−G1i=1∑G∣yi∣1t=1∑∣yi∣fSAPO(ri,t(θ))A^i
其中
r i , t ( θ ) = π θ ( y i , t ∣ q , y i , < t ) π θ old ( y i , t ∣ q , y i , < t ) r_{i,t}(\theta)=\frac{\pi_\theta(y_{i,t}\mid q,y_{i,<t})}{\pi_{\theta_{\text{old}}}(y_{i,t}\mid q,y_{i,<t})} ri,t(θ)=πθold(yi,t∣q,yi,<t)πθ(yi,t∣q,yi,<t)
A ^ i = R i − mean ( { R j } j = 1 G ) std ( { R j } j = 1 G ) \hat A_i=\frac{R_i-\operatorname{mean}(\{R_j\}_{j=1}^{G})}{\operatorname{std}(\{R_j\}_{j=1}^{G})} A^i=std({Rj}j=1G)Ri−mean({Rj}j=1G)
并且 SAPO 用“软门控函数”替代 GRPO/GSPO 的硬 clipping:
f SAPO ( r ) = 4 τ i σ ( τ i ( r − 1 ) ) f_{\text{SAPO}}(r)=\frac{4}{\tau_i}\,\sigma\big(\tau_i(r-1)\big) fSAPO(r)=τi4σ(τi(r−1))
温度参数用正负优势做非对称控制:
τ i = { τ pos , A ^ i > 0 τ neg , A ^ i ≤ 0 通常取 τ neg > τ pos \tau_i= \begin{cases} \tau_{\text{pos}}, & \hat A_i>0\\ \tau_{\text{neg}}, & \hat A_i\le 0 \end{cases} \quad\text{通常取 }\tau_{\text{neg}}>\tau_{\text{pos}} τi={τpos,τneg,A^i>0A^i≤0通常取 τneg>τpos
2. 为什么它能更稳(“软信任域”直觉)
对 f SAPO ( r ) f_{\text{SAPO}}(r) fSAPO(r) 求导得到门控权重(梯度衰减因子):
w i , t ( θ ) = d d r f SAPO ( r ) ∣ r = r i , t ( θ ) = 4 p i , t ( 1 − p i , t ) , p i , t = σ ( τ i ( r i , t ( θ ) − 1 ) ) w_{i,t}(\theta)=\frac{d}{dr}f_{\text{SAPO}}(r)\Big|_{r=r_{i,t}(\theta)} =4p_{i,t}(1-p_{i,t}),\quad p_{i,t}=\sigma\big(\tau_i(r_{i,t}(\theta)-1)\big) wi,t(θ)=drdfSAPO(r) r=ri,t(θ)=4pi,t(1−pi,t),pi,t=σ(τi(ri,t(θ)−1))
w i , t w_{i,t} wi,t 在 r i , t = 1 r_{i,t}=1 ri,t=1 处取最大值 1, r i , t r_{i,t} ri,t 偏离 1 时会平滑衰减,所以不会像硬 clipping 那样“直接截断梯度”,但会把明显 off-policy 的 token 更新压下去。
3. 伪代码(核心损失计算)
# given: token_logprob(pi, q, y) -> [|y|] log probs
# given: rewards R_i per sequence, advantage A_i computed per sequence
loss_sum, tok_sum = 0.0, 0
for i, y in enumerate(group_outputs): # i=1..G
A = A_i[i] # scalar, broadcast to tokens
tau = tau_pos if A > 0 else tau_neg
logp_new = token_logprob(pi, q, y) # [T]
logp_old = token_logprob(pi_old, q, y) # [T]
r = exp(logp_new - logp_old) # [T]
f = (4.0 / tau) * sigmoid(tau * (r - 1.0)) # [T]
loss_sum += sum(-f * A) # sum over tokens
tok_sum += len(y)
loss = loss_sum / tok_sum
优点
- 软门控替代硬 clipping,减少“梯度突然归零”,训练更连续。
- token 级自适应,序列里只有少数 token 很 off-policy 时,不会把整条序列一刀切丢掉。
- 正负优势非对称温度控制,负更新更快衰减,针对大词表下的训练不稳定更直接。
缺点
- 多了 τ pos , τ neg \tau_{\text{pos}},\tau_{\text{neg}} τpos,τneg 等超参,调参成本上升。
- 若门控过软(温度过小),可能放进更多 off-policy 噪声,稳定性收益变小。
- 在奖励极噪或优势估计偏置大时,门控只能“缓冲更新”,无法从根源修正信号质量。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)