最近审阅:2026-10 · 先读:PPO clipping与 GRPO

一段回答整体答对了,其中某个 token 的概率却下降了。训练时,是把整段回答一起推高,还是单独照顾那个 token?GSPO 和 ASPO 从不同方向处理这个问题。先别背缩写,看看它们把权重放在哪里。

先分清 3 个策略

角色 这次用来做什么 为什么要分开
当前策略 $\pi_\theta$ 正在求梯度的模型 随 optimizer step 改变
采样 / 旧策略 $\pi_b$ 生成训练回答;提供分母 同步训练常用冻结的 old policy;异步时应记录实际采样版本
Reference $\pi_{\rm ref}$ KL 或偏好目标的锚点 不是因为“也冻结了”就能拿来替代采样策略

给定已经采出的回答 $y$,token 概率比为:

$$ \rho_t=\exp\left(\log\pi_\theta(y_t\mid x,y_{<t})-\log\pi_b(y_t\mid x,y_{<t})\right). $$

两个模型必须看到相同的 prefix。拿当前模型自由生成的另一个回答来相除,没有这个含义。prompt、工具返回和 padding 也不能不加区分地算成模型动作。

GSPO:先把概率变化汇总到回答级别

GSPO 论文使用长度归一化的序列比值,再做序列级 clipping。一个回答的 token 共享组内 advantage $A$:

$$ s=\exp\left(\frac{1}{T}\sum_{t=1}^{T}\log\rho_t\right),\qquad J=\min\left(sA,\operatorname{clip}(s,1-\epsilon,1+\epsilon)A\right). $$

它是 token 比值的几何平均,不是算术平均,也不是原始序列概率比 $\prod_t\rho_t$。开 $T$ 次方可以减轻长度对数值范围的影响,但也改变了权重;不能据此宣称它是无偏的整条轨迹 importance sampling。

读论文的动机时,也要把“估计很不稳”和“恒等式不成立”分开。满足支持条件时,单个样本的 importance-sampling 估计也可以无偏,只是方差(variance)可能很大;不能仅凭“每个位置只采了一个 token”就判定它无效。逐 token 比值没有修正旧前缀分布,是另一个问题。这个两步例子里,第二步 ratio 全是 1,整段回答的成功率却从 0.35 变成 0.65。GSPO 的实验结果值得看,但不等于证明所有逐 token surrogate 都不成立。

两个 token,变化方向相反

下面是自拟数值,不是模型实测。设 $A=1$,两个 token 的比值为 [2, 0.5]:

算法 / 量 算出来多少 说明
原始序列比值 $2\times0.5=1$ 整段回答的概率没有变
GSPO 比值 $\sqrt{2\times0.5}=1$ 在演示用的 [0.8, 1.2] 内,不触发 clipping
token-level PPO surrogate 的平均 $(1.2+0.5)/2=0.85$ 正 advantage 下,第一个 token 的增益被截住

在 GSPO 未裁剪的分支,固定 advantage 后:

$$ \nabla J=\frac{sA}{T}\sum_t\nabla\log\pi_\theta(y_t\mid x,y_{<t}). $$

这里每个 token 得到相同的外部系数 $sA/T$,不代表参数梯度相同:不同位置的网络 Jacobian 仍然不同。这个例子也暴露了代价:序列平均可能掩盖局部的大幅变化。

这也不是“只拿高分回答来训练”。把同一例子的 $A$ 改成 −1,$s$ 仍是 1,两个位置各自的局部 log-prob 导数都是 −0.5,负向信号没有被删掉。裁剪取决于比值和 advantage 的方向,而不是回答有没有得高分。

MoE 还有一层工程问题:原论文 §5.3报告了路由变化下的训练不稳定。但新旧模型选了不同专家,并不自动使两个输出概率不能相除。仍需检查相同前缀、实际采样分布(sampling distribution)和支持条件,以及 rollout 与训练实现算出的概率是否一致;换成 GSPO 不能代替这些检查。

import math

def clipped_surrogate(ratio, advantage, lower=0.8, upper=1.2):
    if not 0 < lower <= 1 <= upper or not math.isfinite(ratio) or ratio <= 0:
        raise ValueError("invalid ratio or clipping interval")
    return min(ratio * advantage, min(upper, max(lower, ratio)) * advantage)

def sequence_ratio(log_ratios, action_mask):
    if len(log_ratios) != len(action_mask) or any(type(flag) is not bool for flag in action_mask):
        raise ValueError("one Boolean action mask per token is required")
    selected = [value for value, active in zip(log_ratios, action_mask) if active]
    if not selected or not all(math.isfinite(value) for value in selected):
        raise ValueError("at least one finite action log-ratio is required")
    return math.exp(sum(selected) / len(selected))

ratios = [2.0, 0.5]
assert math.isclose(sequence_ratio([math.log(value) for value in ratios], [True, True]), 1)
assert math.isclose(sum(clipped_surrogate(value, 1) for value in ratios) / 2, 0.85)
assert clipped_surrogate(0.5, -1) == -0.8

这是目标值检查,不是完整 trainer。实际 GSPO 的阈值要按序列比值重新选;这里的 0.2 只是方便手算,不能直接照搬为训练配置。组内全同 reward、错误的 action mask、失真的 reward,也不会因为换成 GSPO 就消失。

ASPO:正 advantage 的权重,反过来算

ASPO 论文(2026-05-15 的 v2 改题为 “When Importance Sampling Misallocates Credit: Asymmetric Ratios for Outcome-Supervised RL”,方法在 §6)讨论的是 token-level 更新:保留按 advantage 方向的 mask,对正 advantage 使用倒数权重,对负 advantage 保留原比值,并约束极端权重。它的动机是让已经涨得很多与仍落后的 token 不再沿用同一种放大方式。

最容易写错的是梯度。令 $\ell=\log p_\theta$,采样概率为 $p_b$。正 advantage 分支写成:

$$ \widehat\rho=\frac{p_b p_\theta}{\operatorname{sg}(p_\theta^2)}, \qquad \widehat\rho\text{ 的前向值}=\frac{p_b}{p_\theta}, \qquad \frac{\partial\widehat\rho}{\partial\ell}=+\frac{p_b}{p_\theta}. $$

$\operatorname{sg}$ 是 stop-gradient。若直接写可微的 1 / ratio,导数反而是负数,就把提高好 token 概率的方向写反了。数值一样,不等于训练行为一样。

算一次局部更新

假设采样时概率 0.2,现在是 0.1,$A=1$。普通比值是 0.5;ASPO 正分支的权重是 2。在没有触发 mask 或权重上限的情况下,它们乘在 $\nabla\log p_\theta$ 前的系数分别为 0.5 和 2。

下面只验证 stop-gradient 的局部含义:求导时固定分母,不是每次扰动后都重新计算分母。

import math

behavior_probability = 0.2
current_probability = 0.1
frozen_square = current_probability ** 2
current_logp = math.log(current_probability)
step = 1e-6

def detached_denominator_value(log_probability):
    return behavior_probability * math.exp(log_probability) / frozen_square

derivative = (
    detached_denominator_value(current_logp + step)
    - detached_denominator_value(current_logp - step)
) / (2 * step)
assert math.isclose(derivative, 2.0, rel_tol=1e-6)

没有 mask、dual clipping 和 loss reduction 的这几行,不能叫完整 ASPO 实现。作者代码适合继续对照。论文 v1 的 token-masking 条件把正 advantage 的上界印成了 $1-\epsilon_{high}$;v2 §6 已改为与 PPO 一致的 $1+\epsilon_{high}$。接入训练前仍应核对具体实现,而不是把论文里的句子直接复制成条件判断。

怎么判断哪种改法值得试

先记录同一批 rollout 的 reward、长度、比值分布和有效 token 数,再换 loss。尽量保持数据、生成预算、初始化与评估相同。

观察到的现象 值得检查 不能仅凭什么下结论
少数 token 比值极端 序列聚合是否改善稳定性;是否掩盖局部异常 不能只看平均 ratio 接近 1
advantage 为正和为负时,权重分布很不一样 按 advantage 符号画分布,核对 detach 与 clipping 不能把所有熵下降都叫训练崩溃
有效梯度越来越少 按 token / sequence 分别统计 mask 比例 两种粒度的 clip fraction 不能直接横比
分数涨了、回答也长了很多 固定推理预算,再比较正确率与失败类型 更长不自动等于更会推理

这些方法修改的是更新规则,不会把错误的评分标准(criterion)变成正确标准。接着读 DAPO 的采样与归一化,再看异步训练中的旧数据。

本页术语 3
方差 variance
估计量的波动幅度;重要性加权最容易在这里爆炸
采样分布 sampling distribution
训练候选被抽中的概率分布,它决定模型经常看到哪类边界
评分标准 criterion
这次评估究竟要判断哪一项质量