【LLM ✖️ 强化学习】— DPO、PPO、 GRPO、GSPO、DAPO、SAPO

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) πθ(atst):当前策略(待更新)对动作的概率
  • π o l d ( a t ∣ s t ) \pi_{\mathrm{old}}(a_t\mid s_t) πold(atst):采样这批数据时的旧策略(更新前冻结)
  • 策略概率比(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(atst)πθ(atst)

  • 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 的核心策略项。

优点

  1. 训练稳定
  2. 支持在线学习,通过与环境交互实时更新策略

缺点

  1. 计算开销大,需要训练Policy和Critic模型,同时加载Reference和Reward模型推理
  2. 依赖奖励模型

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πθ(ywx)logπθ(ylx))(logπref(ywx)logπref(ylx))

你也可以把它写成“相对参考模型的 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πθ(yx)logπref(yx)

Δ θ ( 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) πθ(yx):当前要训练的语言模型策略
  • π r e f ( y ∣ x ) \pi_{\mathrm{ref}}(y|x) πref(yx):参考策略(常用 SFT 模型冻结拷贝)
  • β > 0 \beta>0 β>0:温度系数,控制偏好推动强度,也影响“贴近参考策略”的程度

log ⁡ π ( y ∣ x ) \log \pi(y|x) logπ(yx) 在 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πθ(yx)=t=1Tlogπθ(ytx,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()

优点

  1. 无需奖励模型,直接通过数据偏好优化模型
  2. 显存占用低,可快速微调模型
  3. 适合离线场景,在语言模型等对齐任务表现优异

缺点

  1. 分布偏移敏感
  2. 依赖高质量数据
  3. 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=1Goi1t=1oi(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,tq,oi,<t)πθ(oi,tq,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)rimean(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()

优点

  1. 不训练 value/critic,显著降低显存与计算开销,工程实现更轻量。
  2. 组内相对奖励天然匹配“同题多回答对比”的数据形态,baseline 直接来自组均值。
  3. 仍保留 PPO-Clip 的稳定更新机制,能在一定程度上抑制策略突变。

缺点

  1. 需要对每个 prompt 采样 G G G 个输出,生成成本随 G G G 线性增长,吞吐容易成为瓶颈。
  2. 组内标准化带来偏置风险,比如长度偏置、题目难度偏置等,需要额外处理或改损失。
  3. 当组内奖励差异很小(甚至全都一样)时,优势接近 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(θ)=ExD, {yi}i=1Gπθold(x)[G1i=1Gmin(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}). πθ(yix)=t=1yiπθ(yi,tx,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(yix)πθ(yix))yi1.

等价的 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(θ)=yi1(logπθ(yix)logπθold(yix))=yi1t=1yilogπθold(yi,tx,yi,<t)πθ(yi,tx,yi,<t).

直观解释:

  • s i ( θ ) s_i(\theta) si(θ) 是“新旧策略对整段回答的概率比”,再取 1 ∣ y i ∣ \frac{1}{|y_i|} yi1 次幂,相当于对 token 比率取几何平均。
  • 这么做是为了把不同长度回答的尺度拉齐,降低方差,也让 clipping 的数值范围更可控。

5) GSPO 更新在做什么

对每个回答 y i y_i yi

  1. 用奖励 r ( x , y i ) r(x,y_i) r(x,yi) 得到组内优势 A ^ i \hat A_i A^i
  2. 计算序列级比率 s i ( θ ) s_i(\theta) si(θ)
  3. min ⁡ ( ⋅ ) \min(\cdot) min() 形成“保守”的 PPO-Clip 风格目标
  4. − 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()

优点

  1. 更稳:用序列级 ratio 并让同一回答内 token 等权,有助于减少 token 级重要性权重累积带来的不稳定。
  2. 更匹配奖励粒度:奖励通常是序列级标量,GSPO 直接做序列级 clipping 与优化,reward 粒度与优化粒度一致。
  3. 更易扩展到大模型与 MoE:论文与官方总结强调其在大规模训练、尤其 MoE RL 上更稳定,并有潜力简化 RL 基础设施。

缺点

  1. ε \varepsilon ε 更敏感:由于 s i ( θ ) s_i(\theta) si(θ) 的定义不同,clipping 阈值的数量级往往与 token-level 方法不同,需要重新调参。
  2. 可能更“激进地裁剪样本”:序列级 clipping 的裁剪范围一旦触发,会对整段回答的更新一起按住,训练中被 clip 的比例可能显著高于 token-level。
  3. 组内奖励方差过小会变弱:当同一 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=1Goi1i=1Gt=1oimin(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,tq,oi,<t)πθ(oi,tq,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)Rimean({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=1Goi1i=1Gt=1oi()

直觉:长推理链条里出现的模式不会因为“样本太长”而被稀释。

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(LmaxLcache)y,1,yLmaxLcacheLmaxLcache<yLmaxLmax<y

训练时可把它加到正确性奖励上:
R i ← R i + R length ( o i ) R_i \leftarrow R_i + R_{\text{length}}(o_i) RiRi+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

优点

  1. 更稳定:非对称 clipping 与过长样本奖励整形,针对 long-CoT 的熵塌缩与奖励噪声做了直接修补。
  2. 更高效:动态采样过滤掉零梯度组,提升有效样本比例,减少“算了但没学到”的更新。
  3. 更适配 long-CoT:token 级聚合避免长样本信号被稀释,更容易强化“中间推理步骤”的模式。

缺点

  1. 对任务形态有前提:规则奖励依赖“可验证”或可稳定判等的任务,开放式偏好对齐不一定适用。
  2. 系统复杂度更高:动态采样与过滤会改变数据流与吞吐,工程实现和调参成本更高。
  3. 可能引入分布偏置:过滤“全对/全错”组会改变训练分布,若过滤策略不当,可能导致泛化或校准问题。

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=1Gyi1t=1yifSAPO(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,tq,yi,<t)πθ(yi,tq,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)Rimean({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(r1))

温度参数用正负优势做非对称控制:

τ 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^i0通常取 τ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(1pi,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

优点

  1. 软门控替代硬 clipping,减少“梯度突然归零”,训练更连续。
  2. token 级自适应,序列里只有少数 token 很 off-policy 时,不会把整条序列一刀切丢掉。
  3. 正负优势非对称温度控制,负更新更快衰减,针对大词表下的训练不稳定更直接。

缺点

  1. 多了 τ pos , τ neg \tau_{\text{pos}},\tau_{\text{neg}} τpos,τneg 等超参,调参成本上升。
  2. 若门控过软(温度过小),可能放进更多 off-policy 噪声,稳定性收益变小。
  3. 在奖励极噪或优势估计偏置大时,门控只能“缓冲更新”,无法从根源修正信号质量。
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐