Score Function Centering 深入浅出:从 Score Function 理解异步 RL 中的 Trainer-Generator Mismatch

发布于
6,604字
en / zh语言

我觉得网上关于 score function centering 的解释或多或少都有些绕,不够直接。我希望写这篇文章给大家提供一种从 intuition 讲起,对 score function centering 最直接的解释。这篇文章是我纯手搓,不是 AI 写的,当然如果你读的是英文版,很抱歉,它是 AI 翻译的。

这篇文章需要你知道 PPO 或者 GRPO 的使用方法(loss 长什么样)和一些简单的概率论知识(就够了)。如果你还没读过,请你花十分钟读一下这一篇文章。

如果你满足以下任意一条,这篇文章应该是适合你的:

  • 只会用 GRPO,但是不知道其内在原理
  • 对 trainer-generator mismatch 想要进一步加深理解
  • 做 async RL 并且 long rollout 训练但是还不确定该如何提升算法稳定性
  • 不知道 score function centering 是个啥(甚至不知道这里的 score 是个啥)但是想了解的

Score Function Centering 并不是一个专有名词,我知道各家 frontier lab 对于这个技术有自己不同的叫法,alternative token、covariance optimization 等等,你想叫什么都行,这篇文章也会解释这些所谓的名字是怎么来的。

太长不看版:

  • 读完第一章你会知道 score function 是计算 policy gradient 的一个重要桥梁,以及我们能从 auto-differentiation 中得到的最直接的产物。
  • 读完第二章,你会知道 score function 的重要性质:它本身是中心化过了的,所以它的期望是 0,我们需要这个期望一直是 0,才能在 policy gradient 中用所谓的 advantage 来代替 reward,以及 trainer-generator mismatch 如何破坏了这个性质。你还会知道这个性质被破坏以后的实际后果:平均 advantage 会额外产生一个把当前 trainer 往旧 generator 拉的 gradient,所以我们需要修复它。
  • 读完第三章,你会知道 score function centering 本质是在干什么,它为什么修复了这个 0 期望属性;以及实际在工程上我们应该如何使用它,以及在这个领域灌水的一个方向。

1. 我们从 score function 讲起,score 是啥?为啥要用它?

1.1 GRPO/PPO 里我们到底在求什么梯度?

下面我们对 policy gradient 做一个快速推导,如果你对这一部分相当熟悉,请跳过这一小节。

假设在某个 state,也就是 LLM 某个 prefix xx 上,policy 是:

pθ(a∣x)p_\theta(a|x)(1)

为了简单,下面暂时把 xx 省略,写成:

pθ(a).p_\theta(a).(2)

我们的目标非常自然:

“让高 reward 的 action 更容易被模型采到。”

因此可以写一个最简单的目标:

J(θ)=Ea∼pθ[R(a)].J(\theta) = \mathbb E_{a\sim p_\theta}[R(a)].(3)

这句话的意思就是:“从当前模型 pθp_\theta 里 sample action,计算 reward,希望它的期望越来越高。”

问题是:∇θEa∼pθ[R(a)]\nabla_\theta \mathbb E_{a\sim p_\theta}[R(a)] 怎么求?这里有一个麻烦:θ\theta 并不直接出现在 R(a)R(a) 里面。它藏在 a∼pθa\sim p_\theta 这个 sampling distribution 里面。也就是说,我们不是在求 ∇θR(a)\nabla_\theta R(a)。事实上,RL 里的 reward 完全可以不可导。比如:数学题答对给 1 分否则给 0 分,或者某个人类专家直接给打分。这都没关系。真正依赖 θ\theta 的,是 pθ(a)p_\theta(a)。

把 expectation 写开:

J(θ)=∑apθ(a)R(a).J(\theta) = \sum_a p_\theta(a)R(a).(4)

求导:

∇θJ(θ)=∑aR(a)∇θpθ(a).\nabla_\theta J(\theta) = \sum_a R(a)\nabla_\theta p_\theta(a).(5)

然后用一个非常简单但极其重要的恒等式:

∇θpθ(a)=pθ(a)∇θlog⁡pθ(a).\nabla_\theta p_\theta(a) = p_\theta(a) \nabla_\theta\log p_\theta(a).(6)

因为:

∇log⁡p=1p∇p.\nabla \log p = \frac{1}{p}\nabla p.(7)

于是:

∇θJ(θ)=∑apθ(a)R(a)∇θlog⁡pθ(a).\nabla_\theta J(\theta) = \sum_a p_\theta(a) R(a) \nabla_\theta\log p_\theta(a).(8)

重新写成 expectation:

∇θJ(θ)=Ea∼pθ[R(a)∇θlog⁡pθ(a)].\nabla_\theta J(\theta) = \mathbb E_{a\sim p_\theta} \left[ R(a) \nabla_\theta\log p_\theta(a) \right].(9)

这里终于出现了 policy gradient 中最关键的东西:

sθ(a)=∇θlog⁡pθ(a).s_\theta(a) = \nabla_\theta\log p_\theta(a).(10)

这个东西叫做 score function。注意这里的 score 不是 reward score。不是“这个回答得了多少分”。它是概率统计中的一个专门术语:

score=∇θlog⁡pθ(a).\text{score} = \nabla_\theta\log p_\theta(a).(11)

也就是 某个被 sample 出来的 action 的 log probability,对模型参数的梯度。

1.2 为什么我们要研究 score?

因为 policy gradient 最核心的结构其实就是:

reward ×\times score

(我知道你想说我们实际用的都是 Advantage×score\text{Advantage}\times\text{score},但你先别急)。

如果换成我们训练 LLM 时更熟悉的写法:

g=R(a)∇θlog⁡pθ(a).g=R(a)\nabla_\theta\log p_\theta(a).(12)

所以可以把两部分理解成:R(a)R(a) 告诉我们 “这个 action 应该被强化还是削弱?强化多少?” 而 ∇θlog⁡pθ(a)\nabla_\theta\log p_\theta(a) 告诉我们:“如果我要提高或者降低这个 action 的 probability,参数应该朝哪个方向走?”

这也是为什么我认为理解 policy gradient 最重要的一句话是:

Score function 是把(你怎么也算不出来的)“概率分布期望的梯度”转化成“可以通过采样计算的梯度估计”的桥梁。

更准确一点:

distribution-level differentiation →\rightarrow sample-level gradient estimation

我们原来想算:

∇θEa∼pθ[R(a)].\nabla_\theta \mathbb E_{a\sim p_\theta}[R(a)].(13)

它看起来需要对整个 action distribution 求导。

但 score-function trick 把它变成:先 sample 一个 action

a∼pθ,a\sim p_\theta,(14)

然后算:

R(a)∇θlog⁡pθ(a).R(a)\nabla_\theta\log p_\theta(a).(15)

多 sample 几次平均:

∇θJ≈1N∑i=1NR(ai)∇θlog⁡pθ(ai).\nabla_\theta J\approx\frac1N\sum_{i=1}^NR(a_i)\nabla_\theta\log p_\theta(a_i).(16)

这就是 REINFORCE / policy gradient 最核心的计算机制。而 GRPO 虽然加入了 group-relative advantage、importance ratio、clipping 等机制,它里面真正产生 policy gradient 的基本对象,仍然绕不开:

∇θlog⁡pθ(a).\nabla_\theta\log p_\theta(a).(17)

同时,你会发现这个东西是你在真正使用 GRPO 的时候,一次 backward 给你的最直接的东西。

2. score function 与 trainer-generator mismatch

2.1 score function 有什么美好的性质?这个性质为什么重要?

我们现在来讨论讨论 advantage。我们都知道,在 GRPO/PPO 里,我们不会直接使用 reward,我们使用的是 advantage(为什么?)。advantage 就是 reward 减去一个常数:

A(a)=R(a)−b,A(a)=R(a)-b,(18)

这里的 bb 可以是 value function,可以是一个 group mean,也可以是任何只和当前 state 有关、但是和具体 action 无关的 baseline。比如在 GRPO 里面,我们最熟悉的事情就是:同一个 prompt 采样很多条 response,然后用某种 group-relative 的 reward 作为 advantage。先不管具体 normalization 怎么做,本质上都是在做一件事情:从 raw reward 里减掉一个“大家共同拥有的部分”,只留下这个 action 相对平均水平好还是差。但是这里有一个非常基础、也非常容易被忽略的问题:

我们凭什么能随便减这个 baseline?

原来的 policy gradient 是:

g=Ea∼pθ[R(a)sθ(a)].g=\mathbb E_{a\sim p_\theta}[R(a)s_\theta(a)].(19)

如果我们把 reward 换成 advantage:

A(a)=R(a)−b,A(a)=R(a)-b,(20)

那么新的 gradient 就是:

g′=Ea∼pθ[(R(a)−b)sθ(a)].g'=\mathbb E_{a\sim p_\theta}[(R(a)-b)s_\theta(a)].(21)

把它展开:

Ea∼pθ[R(a)sθ(a)]−bEa∼pθ[sθ(a)].\mathbb E_{a\sim p_\theta}[R(a)s_\theta(a)] -b\mathbb E_{a\sim p_\theta}[s_\theta(a)].(22)

第一项就是我们原来的 policy gradient。也就是说,如果我们希望减掉 baseline 以后,policy gradient 的期望完全不变,那么第二项必须消失:

bEa∼pθ[sθ(a)]=0.b\mathbb E_{a\sim p_\theta}[s_\theta(a)] = 0.(23)

而 bb 显然不一定等于 0,所以我们真正需要的是:

Ea∼pθ[sθ(a)]=0.\mathbb E_{a\sim p_\theta}[s_\theta(a)] = 0.(24)

这就是 score function 在 policy gradient 里面一个非常重要的性质。你可以先完全不管这个性质为什么成立,只需要记住它的现实意义:

只要 score 的 expectation 是 0,我们就可以给 reward 减一个和 action 无关的 baseline,而不改变 expected policy gradient。

这就是为什么我们可以把 reward 写成 advantage,也是为什么 PPO / GRPO 里面各种 reward centering、value baseline、group-relative baseline 能够成立。有些文章会从 covariance 开始解释这件事情,比如写:

Ea∼pθ[R(a)sθ(a)]=Cov⁡(R(a),s(a))+E[R(a)]E[s(a)],\mathbb E_{a\sim p_\theta}[R(a)s_\theta(a)] = \operatorname{Cov}(R(a),s(a)) + \mathbb E[R(a)]\mathbb E[s(a)],(25)

然后因为 E[s]=0\mathbb E[s]=0,所以最后只剩 covariance。这个写法当然没有问题,但是我个人觉得它反而把一件很简单的事情写绕了。我们真正关心的其实就是一句话:我想把 reward 减掉一个 baseline,但是我又不想让这个 baseline 本身产生 gradient。那自然就要求 E[s]=0\mathbb E[s]=0。就这么简单。

而且这个性质其实非常符合 intuition。假设两个 action 的 reward 是:

R(A)=101,R(B)=99.R(A)=101,\qquad R(B)=99.(26)

和:

R′(A)=2,R′(B)=0.R'(A)=2,\qquad R'(B)=0.(27)

从 policy optimization 的角度,这两组 reward 表达的是完全一样的信息:AA 都比 BB 好 2 分。我们显然不希望仅仅因为给所有 reward 同时加了 99,模型的 expected update direction 就发生变化。所有 reward 同时加减一个常数,都不会改变 expected policy gradient。这也是为什么 baseline 本质上是一个非常自然的操作,而不只是一个数学 trick。

好了,现在问题来了:

为什么 score 的 expectation 恰好会等于 0?

为了回答这个问题,我们终于需要看看 score 的庐山真面目到底是什么。

2.2 Score Function 到底是什么?

接下来,我们对 score function 做一些上过微积分就会做的推导,我们前面定义:

sθ(a)=∇θlog⁡pθ(a).s_\theta(a)=\nabla_\theta\log p_\theta(a).(28)

假设当前模型在某一个 prefix 上输出 vocabulary 中所有 token 的 logits:

zθ(1),zθ(2),…,zθ(V).z_\theta(1),z_\theta(2),\ldots,z_\theta(V).(29)

我们默认输出的分布由 Softmax 给出:

pθ(a)=ezθ(a)∑vezθ(v).p_\theta(a) = \frac{e^{z_\theta(a)}}{\sum_v e^{z_\theta(v)}}.(30)

所以:

sθ(a)=∇θzθ(a)−∇θlog⁡∑vezθ(v).s_\theta(a) = \nabla_\theta z_\theta(a) - \nabla_\theta \log\sum_v e^{z_\theta(v)}.(31)

现在给上式的第一项起一个好听点的名字,叫 logit Jacobian 吧:

Jθ(v)=∇θzθ(v).J_\theta(v)=\nabla_\theta z_\theta(v).(32)

这个 Jθ(v)J_\theta(v) 可以理解成:如果我稍微改变当前 trainer 的模型参数,token vv 的 raw logit 会朝哪个方向变化。

举个直观的例子。假设模型参数从 θ\theta 变成:

θ+Δθ,\theta+\Delta\theta,(33)

那么一阶近似下:

zθ+Δθ(v)≈zθ(v)+Jθ(v)TΔθ.z_{\theta+\Delta\theta}(v) \approx z_\theta(v) + J_\theta(v)^T\Delta\theta.(34)

所以 Jθ(v)J_\theta(v) 本质上就是 token vv 在参数空间里的一个“logit sensitivity direction”。它告诉我们:参数往哪里走,会把这个 token 的 logit 往上推或者往下拉。

接着我们处理上式的第二项:

∇θlog⁡∑vezθ(v)\nabla_\theta \log\sum_v e^{z_\theta(v)}(35)

这里只需要用两次链式法则。先对最外层的 log 求导,得到的分母就是 log 里面的那个求和:

∇θlog⁡∑vezθ(v)=1∑uezθ(u)∇θ∑vezθ(v).\nabla_\theta\log\sum_v e^{z_\theta(v)}=\frac{1}{\sum_u e^{z_\theta(u)}}\nabla_\theta\sum_v e^{z_\theta(v)}.(36)

然后把梯度放进求和里面,再对每一个 ezθ(v)e^{z_\theta(v)} 用一次链式法则:

∇θ∑vezθ(v)=∑v∇θezθ(v)=∑vezθ(v)∇θzθ(v).\nabla_\theta\sum_v e^{z_\theta(v)}=\sum_v\nabla_\theta e^{z_\theta(v)}=\sum_v e^{z_\theta(v)}\nabla_\theta z_\theta(v).(37)

把这两步合起来,就得到:

∑vezθ(v)∇θzθ(v)∑uezθ(u).\frac{ \sum_v \color{orange}{e^{z_\theta(v)}} \color{blue}{\nabla_\theta z_\theta(v)} }{ \color{orange}{\sum_u e^{z_\theta(u)}} }.(38)

而我们恰好有:

pθ(v)=ezθ(v)∑uezθ(u),Jθ(v)=∇θzθ(v).p_\theta(v) = \color{orange}{\frac{e^{z_\theta(v)}}{\sum_u e^{z_\theta(u)}}}, \quad J_\theta(v)= \color{blue}{\nabla_\theta z_\theta(v)}.(39)

所以上式第二项可以写作:

∑vpθ(v)Jθ(v).\sum_v p_\theta(v)J_\theta(v).(40)

也就是:

Ev∼pθ[Jθ(v)].\mathbb E_{v\sim p_\theta}[J_\theta(v)].(41)

因此:

sθ(a)=Jθ(a)−Ev∼pθ[Jθ(v)].s_\theta(a) = J_\theta(a)-\mathbb E_{v\sim p_\theta}[J_\theta(v)].(42)

这个式子特别重要,因为它告诉我们 在 score function 里,Softmax 自己就在给 logit Jacobian 做 centering。Jθ(a)J_\theta(a) 是当前 sample 到的 action 的 logit gradient,而 Ev∼pθ[Jθ(v)]\mathbb E_{v\sim p_\theta}[J_\theta(v)] 是按照当前 policy pθp_\theta 加权以后,所有 action 的平均 logit gradient。

所以 score 其实就是被采样到的 action 的 logit Jacobian,减去当前 policy 下平均的 logit Jacobian。

有了上边这个式子,score 的 expectation 为 0 这件事情几乎都不用证明了。因为 action 本身就是从:

a∼pθa\sim p_\theta(43)

采样出来的,所以:

Ea∼pθ[sθ(a)]=Ea∼pθ[Jθ(a)−Ev∼pθ[Jθ(v)]].\mathbb E_{a\sim p_\theta}[s_\theta(a)]=\mathbb E_{a\sim p_\theta}\left[J_\theta(a)-\mathbb E_{v\sim p_\theta}[J_\theta(v)]\right].(44)

把 expectation 拆开:

=Ea∼pθ[Jθ(a)]−Ev∼pθ[Jθ(v)].=\mathbb E_{a\sim p_\theta}[J_\theta(a)]-\mathbb E_{v\sim p_\theta}[J_\theta(v)].(45)

这里的 aa 和 vv 只是两个名字不同的 dummy variable,它们都来自完全相同的 distribution pθp_\theta,所以这两项就是同一个东西:

Ea∼pθ[sθ(a)]=Epθ[Jθ]−Epθ[Jθ]=0.\mathbb E_{a\sim p_\theta}[s_\theta(a)]=\mathbb E_{p_\theta}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]=0.(46)

所以我们现在可以把整件事情串起来了:Softmax score 本身是 centered 的,因此:

Ea∼pθ[sθ(a)]=0,\mathbb E_{a\sim p_\theta}[s_\theta(a)] = 0,(47)

而这个 zero-mean property 又保证了:我们可以在 policy gradient 中使用 reward 减 baseline 来代替原始的 reward,而不改变 expected policy gradient。

到这里其实一切都非常美好。但是注意,上面整个推导里有一个我们一直在偷偷使用、但是还没有认真强调的假设:

计算 expectation 的 distribution,和生成 action 的 distribution,都是 pθp_\theta。

也就是说,我们默认:

产生 rollout 的 generator,就是现在正在 backward 的 trainer。

一旦这个假设不成立,后面的故事才真正开始。

2.3 trainer-generator mismatch 后到底破坏了什么?

肯定是破坏了 Ea∼pθ[sθ(a)]=0\mathbb E_{a\sim p_\theta}[s_\theta(a)] = 0 这个 score function 的 zero-mean property 啊,不然我们上边讨论那么多不就白费功夫了。

但是这里最重要的问题不是“它被破坏了”,而是:到底是哪一步发生了变化?

我们先把 trainer 和 generator 明确区分开。假设当前正在做 backward、准备更新参数的 trainer policy 是:

pθ(a).p_\theta(a).(48)

而真正生成 rollout 的 generator 是一个稍微旧一点的 policy:

qϕ(a).q_\phi(a).(49)

这里我故意用了不同的参数 θ\theta 和 ϕ\phi。你可以把 θ\theta 理解成 trainer 当前的参数,而 ϕ\phi 是 generator 拿去做 rollout 时的那一版参数。在同步训练里,我们近似有:

qϕ≈pθ,q_\phi \approx p_\theta,(50)

甚至可以直接认为:

qϕ=pθ.q_\phi=p_\theta.(51)

但在 async RL 里,尤其是 rollout 很长、generator 很多、trainer 又一直在更新的时候,这两个分布很自然就会开始产生 mismatch:

qϕ≠pθ.q_\phi\neq p_\theta.(52)

现在回忆一下我们刚才证明 zero-mean property 时用的公式:

Ea∼pθ[sθ(a)]=Ea∼pθ[Jθ(a)]−Ev∼pθ[Jθ(v)]=0.\mathbb E_{a\sim p_\theta}[s_\theta(a)]=\mathbb E_{a\sim p_\theta}[J_\theta(a)]-\mathbb E_{v\sim p_\theta}[J_\theta(v)]=0.(53)

为什么等于 0?本质上就是因为左右两边的 expectation 都是在同一个 pθp_\theta 上算的。第一个 expectation 里,action aa 是从 pθp_\theta sample 出来的;第二个 expectation 里,Softmax 本身又是用 pθp_\theta 对所有 logit Jacobian 做 centering。所以两项正好完全一样。

但是现在 generator 换成了 qϕq_\phi。真正送到 trainer 里的 action 已经不再满足:

a∼pθ,a\sim p_\theta,(54)

而是:

a∼qϕ.a\sim \color{red}{q_\phi}.(55)

注意,trainer 并没有因此改变自己计算 score 的方式。我们 backward 的仍然是当前 trainer 的:

sθ(a)=Jθ(a)−Ev∼pθ[Jθ(v)].s_\theta(a)=J_\theta(a)-\mathbb E_{v\sim p_\theta}[J_\theta(v)].(56)

这里的 JθJ_\theta 仍然来自当前 trainer,Softmax centering 用的也仍然是当前 trainer distribution pθp_\theta。真正变化的只有一件事情:外边这个 action 到底是从哪个 distribution sample 出来的。

所以现在我们真正应该看的,不再是:

Ea∼pθ[sθ(a)],\mathbb E_{a\sim p_\theta}[s_\theta(a)],(57)

而是:

Ea∼qϕ[sθ(a)].\mathbb E_{a\sim \color{red}{q_\phi}}[s_\theta(a)].(58)

把 score 展开:

Ea∼qϕ[sθ(a)]=Ea∼qϕ[Jθ(a)−Ev∼pθ[Jθ(v)]].\mathbb E_{a\sim \color{red}{q_\phi}}[s_\theta(a)]=\mathbb E_{a\sim \color{red}{q_\phi}}\left[J_\theta(a)-\mathbb E_{v\sim p_\theta}[J_\theta(v)]\right].(59)

把 expectation 拆开:

=Ea∼qϕ[Jθ(a)]−Ev∼pθ[Jθ(v)].=\mathbb E_{a\sim \color{red}{q_\phi}}[J_\theta(a)]-\mathbb E_{v\sim p_\theta}[J_\theta(v)].(60)

现在问题就非常明显了。原来我们是:

Ea∼pθ[Jθ(a)]−Ev∼pθ[Jθ(v)],\mathbb E_{a\sim p_\theta}[J_\theta(a)]-\mathbb E_{v\sim p_\theta}[J_\theta(v)],(61)

左右两个 expectation 的 distribution 一样,所以等于 0。现在变成了:

Ea∼qϕ[Jθ(a)]−Ev∼pθ[Jθ(v)].\mathbb E_{a\sim \color{red}{q_\phi}}[J_\theta(a)]-\mathbb E_{v\sim p_\theta}[J_\theta(v)].(62)

左边在 generator distribution qϕq_\phi 下取平均,右边却仍然在 trainer distribution pθp_\theta 下取平均。只要:

qϕ≠pθ,q_\phi\neq p_\theta,(63)

我们一般就不能再指望:

Ea∼qϕ[Jθ(a)]=Ev∼pθ[Jθ(v)].\mathbb E_{a\sim q_\phi}[J_\theta(a)]=\mathbb E_{v\sim p_\theta}[J_\theta(v)].(64)

所以最终:

Ea∼qϕ[sθ(a)]=Eqϕ[Jθ]−Epθ[Jθ]≠0.\mathbb E_{a\sim \color{red}{q_\phi}}[s_\theta(a)]=\mathbb E_{q_\phi}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]\neq 0.(65)

这就是 trainer-generator mismatch 对 score function 最直接的影响。

你会发现,这件事情其实一点都不神秘。Score 自己没有突然变坏,Softmax 的公式也没有变,trainer 计算出来的 JθJ_\theta 也没有变。真正发生变化的,只是 sample action 的 distribution 从 pθp_\theta 换成了 qϕq_\phi。

而 zero-mean property 恰好又依赖于“sample action 的 distribution”和“score 里面做 centering 的 distribution”是同一个 distribution。现在这两个东西不一样了:

a∼qϕ⏟generator sampling distribution,Ev∼pθ[Jθ(v)]⏟trainer centering distribution.\underbrace{a\sim \color{red}{q_\phi}}_{\text{generator sampling distribution}},\qquad \underbrace{\mathbb E_{v\sim p_\theta}[J_\theta(v)]}_{\text{trainer centering distribution}}.(66)

所以原来那个:

Epθ[Jθ]−Epθ[Jθ]=0\mathbb E_{p_\theta}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]=0(67)

就变成了:

Eqϕ[Jθ]−Epθ[Jθ]≠0.\mathbb E_{\color{red}{q_\phi}}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]\neq 0.(68)

到这里,我们其实已经把 trainer-generator mismatch 最关键的一层问题讲清楚了:

在同步 RL 里,generator 和 trainer 是同一个 distribution,所以标准 score 天然是 zero-mean 的;而在 async RL 里,action 来自旧 generator qϕq_\phi,score 却仍然由当前 trainer pθp_\theta 来 center,于是这个 zero-mean property 被破坏了。

2.4 这个 non-zero mean 到底会造成什么实际后果?

到这里你可能会觉得,Eqϕ[sθ]≠0\mathbb E_{q_\phi}[s_\theta]\neq 0 听起来还是有点抽象。我们前面说它会破坏 baseline invariance,但是它到底会给训练带来一个什么样的 gradient?

这件事情如果直接放到 logit space 里看,会非常直观。对于 Softmax,某个 action aa 的 log probability 对整条 logit vector zz 的梯度是:

∇zlog⁡pθ(a)=ea−pθ,\nabla_z\log p_\theta(a)=e_a-p_\theta,(69)

其中 eae_a 是 action aa 对应的 one-hot vector。在同步训练里 a∼pθa\sim p_\theta,所以对它取 expectation:

Ea∼pθ[∇zlog⁡pθ(a)]=pθ−pθ=0.\mathbb E_{a\sim p_\theta}[\nabla_z\log p_\theta(a)]=p_\theta-p_\theta=0.(70)

但是现在 action 是由旧 generator qϕq_\phi sample 出来的:

Ea∼qϕ[∇zlog⁡pθ(a)]=qϕ−pθ.\mathbb E_{a\sim q_\phi}[\nabla_z\log p_\theta(a)]=q_\phi-p_\theta.(71)

这个式子就比 Eq[J]−Ep[J]\mathbb E_q[J]-\mathbb E_p[J] 更容易看出背后的 intuition 了:如果某个 token 在旧 generator 里比在当前 trainer 里更常见,也就是 qϕ(v)>pθ(v)q_\phi(v)>p_\theta(v),这个方向就会把它的 logit 往上推。反过来,如果 qϕ(v)<pθ(v)q_\phi(v)<p_\theta(v),这个方向就会把它往下拉。

现在再回到我们前面一直在讨论的 baseline。假设你给所有 reward 或者 advantage 同时加了一个和 action 无关的常数 cc。在正常的 on-policy setting 里,这个常数不应该产生任何 expected gradient。但是在 trainer-generator mismatch 以后,它会额外产生:

Δgz=c Ea∼qϕ[∇zlog⁡pθ(a)]=c(qϕ−pθ).\Delta g_z=c\,\mathbb E_{a\sim q_\phi}[\nabla_z\log p_\theta(a)]=c(q_\phi-p_\theta).(72)

如果 c>0c>0,这条 gradient 做的事情非常直接:把当前 trainer pθp_\theta 往旧 generator qϕq_\phi 的 distribution 拉。

反过来,如果 c<0c<0,trainer 就会被推向远离 generator 的方向。这是一件非常不好的事情,因为 cc 在这里扮演的其实就是 baseline 的角色,而 baseline 在理论上本来就有正有负。这就相当于我们在 trainer 和 generator 之间的距离上加了一个扰动,而这个扰动的方向和大小完全取决于 baseline 的取值。

这个方向还可以换一个角度来看。假设我们真的拿旧 generator 的 sample 去最大化当前 trainer 的 log likelihood:

Ea∼qϕ[log⁡pθ(a)],\mathbb E_{a\sim q_\phi}[\log p_\theta(a)],(73)

它在 logit space 里的 gradient 恰好就是:

∇zEa∼qϕ[log⁡pθ(a)]=qϕ−pθ.\nabla_z\mathbb E_{a\sim q_\phi}[\log p_\theta(a)]=q_\phi-p_\theta.(74)

换句话说:

qϕ−pθ=−∇zDKL(qϕ∥pθ).q_\phi-p_\theta=-\nabla_zD_{\mathrm{KL}}(q_\phi\|p_\theta).(75)

所以当 mean reward 或者 mean advantage 是正的时候,那个本来应该只是“大家共同拥有的常数部分”,居然会偷偷变成一个方向上和对旧 generator 做 behavior cloning 完全一致的 gradient。这件事情其实很奇怪:一个和 action 之间谁好谁坏都没有关系的平均值,凭什么能决定我们要不要往旧 generator 靠近,以及靠近多少?

用我们前面在参数空间里的写法来看,结论也是一样的。把 advantage 拆成:

A(a)=(A(a)−Aˉq)+Aˉq,Aˉq=Eqϕ[A].A(a)=(A(a)-\bar A_q)+\bar A_q,\qquad \bar A_q=\mathbb E_{q_\phi}[A].(76)

那么:

Eqϕ[A(a)sθ(a)]=Eqϕ[(A(a)−Aˉq)sθ(a)]+Aˉq Eqϕ[sθ(a)].\mathbb E_{q_\phi}[A(a)s_\theta(a)]=\mathbb E_{q_\phi}[(A(a)-\bar A_q)s_\theta(a)]+\bar A_q\,\mathbb E_{q_\phi}[s_\theta(a)].(77)

而第二项就是:

Aˉq(Eqϕ[Jθ]−Epθ[Jθ]).\bar A_q\left(\mathbb E_{q_\phi}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]\right).(78)

这就是 zero-mean 被破坏以后最实际的后果之一:真正描述 token 之间相对好坏的是第一项,但是 mean advantage 又额外乘上了一个 trainer-generator mismatch direction。

现在再看 Score Function Centering,动机就更直接了。我们不只是想让一个数学 expectation 看起来重新等于 0,更是想把这个由“平均 reward 水平”偷偷控制的 old-policy cloning direction 从 reward gradient 里拿掉。至于 trainer 和 generator 到底应该允许差多远,那是另外一个问题,不应该让 Aˉq\bar A_q 在这里顺手替我们做决定。

3. Score Function Centering 到底在做什么?

答案呼之欲出了对不对!Score Function Centering 就是想让原本被破坏的 score function 的 zero-mean property 重新被满足,所以我们就又可以愉快地使用 baseline 啦!

我们刚才已经看到,在 trainer-generator mismatch 之后,真正的问题变成了:

Ea∼qϕ[sθ(a)]=Eqϕ[Jθ]−Epθ[Jθ]≠0.\mathbb E_{a\sim q_\phi}[s_\theta(a)]=\mathbb E_{q_\phi}[J_\theta]-\mathbb E_{p_\theta}[J_\theta]\neq 0.(79)

这里为什么不等于 0?原因非常简单:action 是 generator 的 qϕq_\phi sample 出来的,但是 score 里面减掉的“中心”却还是 trainer 的 pθp_\theta。

想要更正这一点,既然外边的 action 已经来自 a∼qϕa\sim q_\phi,那最直接的办法当然就是:不要再用 pθp_\theta 做 centering,而是改成用真正生成这些 action 的 distribution qϕq_\phi 做 centering。

也就是说,把原来的标准 score:

sθ(a)=Jθ(a)−Ev∼pθ[Jθ(v)]s_\theta(a)=J_\theta(a)-\mathbb E_{v\sim p_\theta}[J_\theta(v)](80)

改成:

s~θ,ϕ(a)=Jθ(a)−Ev∼qϕ[Jθ(v)].\tilde s_{\theta,\phi}(a)=J_\theta(a)-\mathbb E_{v\sim \color{red}{q_\phi}}[J_\theta(v)].(81)

这就是 Score Function Centering 最核心的一步。

注意这里有两个 distribution / model parameter,不要混在一起。Jθ(v)J_\theta(v) 仍然是:

Jθ(v)=∇θzθ(v),J_\theta(v)=\nabla_\theta z_\theta(v),(82)

所以它还是当前 trainer 的 logit Jacobian。我们并不是回到旧 generator 上去算 gradient,也不是要更新 ϕ\phi。真正变化的只有 centering 时使用的 probability distribution:

Ev∼pθ[Jθ(v)]⟶Ev∼qϕ[Jθ(v)].\mathbb E_{v\sim p_\theta}[J_\theta(v)]\quad\longrightarrow\quad\mathbb E_{v\sim \color{red}{q_\phi}}[J_\theta(v)].(83)

你可以把这句话记成:generator 决定“怎么平均”,trainer 决定“往哪个参数方向更新”。

这样一改,zero-mean property 马上就回来了。因为真正的 action 本来就是:

a∼qϕ,a\sim q_\phi,(84)

所以:

Ea∼qϕ[s~θ,ϕ(a)]=Ea∼qϕ[Jθ(a)]−Ev∼qϕ[Jθ(v)].\mathbb E_{a\sim q_\phi}[\tilde s_{\theta,\phi}(a)]=\mathbb E_{a\sim q_\phi}[J_\theta(a)]-\mathbb E_{v\sim q_\phi}[J_\theta(v)].(85)

aa 和 vv 还是两个 dummy variable,而且这一次它们终于又来自同一个 distribution 了:

Ea∼qϕ[s~θ,ϕ(a)]=Eqϕ[Jθ]−Eqϕ[Jθ]=0.\mathbb E_{a\sim q_\phi}[\tilde s_{\theta,\phi}(a)]=\mathbb E_{q_\phi}[J_\theta]-\mathbb E_{q_\phi}[J_\theta]=0.(86)

于是我们重新得到:

Ea∼qϕ[(R(a)−b)s~θ,ϕ(a)]=Ea∼qϕ[R(a)s~θ,ϕ(a)].\mathbb E_{a\sim q_\phi}[(R(a)-b)\tilde s_{\theta,\phi}(a)]=\mathbb E_{a\sim q_\phi}[R(a)\tilde s_{\theta,\phi}(a)].(87)

也就是说,在真正产生 rollout 的 distribution qϕq_\phi 下,baseline 又不会自己产生一个额外的 expected gradient 了。

这就是 Score Function Centering 最本质的 intuition:让 score 的 centering distribution 和真正生成 action 的 distribution 对齐。

3.1 但是现实里有一个很大的问题:我们怎么得到 Eqϕ[Jθ]\mathbb E_{q_\phi}[J_\theta]?

你如果真的在训练 LLM,使用上边的方法,马上就会碰到一个非常现实的问题:

Ev∼qϕ[Jθ(v)]=∑v∈Vqϕ(v)Jθ(v).\mathbb E_{v\sim q_\phi}[J_\theta(v)]=\sum_{v\in\mathcal V}q_\phi(v)J_\theta(v).(88)

假设 vocabulary 有 128K 个 token,那么这个 expectation 看起来要求我们知道 generator 在这个 prefix 上对所有 128K 个 token的 probability:

qϕ(1),qϕ(2),…,qϕ(V).q_\phi(1),q_\phi(2),\ldots,q_\phi(V).(89)

问题是,这些信息是在 generator 生成 token 的那一刻才有的,而真正做 backward 的 trainer 可能在另外一台机器、另外一个 pipeline stage,甚至已经过了几十个 train step。

当然你可以说,那我就把 generator 的整个 vocabulary distribution 都存下来,再发给 trainer 不就完了?

理论上可以,但是工程上不可以。

因为对于 rollout 里的每一个 token position,你都要额外保存一个长度为 vocabulary size 的 distribution。假设 vocab 是 128K,一个 rollout 又有几千甚至几万个 token,再乘上 batch size、generator 数量和并行 rollout 数,这个 communication 和 memory overhead 很快就会变得完全不可接受。

所以现实问题变成了:

我们想知道 qϕq_\phi 下的 expectation,但是又不能把完整的 qϕq_\phi 搬到 trainer。怎么办?

答案还是 Monte Carlo:既然 expectation 本来就是对 qϕq_\phi 求平均,那我们就在 generator 端从 qϕq_\phi 里多 sample 几个 token。

3.2 Alternative Tokens:不要把整个 distribution 给 trainer,给我几个 sample 就够了

假设真正被 generator 选中、进入 rollout 的 token 是:

a∼qϕ.a\sim q_\phi.(90)

除了这个 primary action 以外,我们在完全相同的 prefix上,再额外从 generator distribution 里 sample KK 个 token:

v1,v2,…,vK∼qϕ.v_1,v_2,\ldots,v_K\sim q_\phi.(91)

这些 token 不会真的被 append 到 trajectory 里,也不会改变后续 generation。它们只是在告诉 trainer:

“我当时在这个 prefix 上的 distribution 大概长这样。”

不同地方会给这些 token 起不同的名字,alternative tokens、alter tokens、negative tokens、auxiliary samples……它们本质上都是在做同一件事情:用少量 sample 来描述 generator 当时的 local action distribution。

如果这些 vkv_k 真的是 IID 从 qϕq_\phi 里 sample 出来的,那么:

Ev∼qϕ[Jθ(v)]≈1K∑k=1KJθ(vk).\mathbb E_{v\sim q_\phi}[J_\theta(v)]\approx\frac{1}{K}\sum_{k=1}^KJ_\theta(v_k).(92)

于是 SFC score 就可以近似成:

s^θ,ϕ(a)=Jθ(a)−1K∑k=1KJθ(vk).\hat s_{\theta,\phi}(a)=J_\theta(a)-\frac{1}{K}\sum_{k=1}^KJ_\theta(v_k).(93)

而且这个 Monte Carlo estimator 对我们真正想要的 behavior-centered score 是 unbiased 的:

Ev1,…,vK∼qϕ[1K∑k=1KJθ(vk)]=Ev∼qϕ[Jθ(v)].\mathbb E_{v_1,\ldots,v_K\sim q_\phi}\left[\frac{1}{K}\sum_{k=1}^KJ_\theta(v_k)\right]=\mathbb E_{v\sim q_\phi}[J_\theta(v)].(94)

所以 generator 不需要把整个 qϕq_\phi 传给 trainer。它只需要在每一个 token position 附带一小撮额外的信息,比如:

[a,v1,v2,…,vK].[a,v_1,v_2,\ldots,v_K].(95)

更实际一点,还会把这些 token 在 generator 下的 log probability 一起保存下来:

[log⁡qϕ(a),log⁡qϕ(v1),…,log⁡qϕ(vK)].[\log q_\phi(a),\log q_\phi(v_1),\ldots,\log q_\phi(v_K)].(96)

然后这些信息跟着 rollout 一路从 generator 传到 trainer。所以你可以把一个普通 RL rollout token 想成原来只携带:

token id+reward/advantage+log⁡qϕ(a),\text{token id}+\text{reward/advantage}+\log q_\phi(a),(97)

而 SFC 之后,它还额外携带了一个非常小的 local policy sketch:

{vk,log⁡qϕ(vk)}k=1K.\{v_k,\log q_\phi(v_k)\}_{k=1}^K.(98)

这个 local policy sketch 就是在告诉 trainer:generator 当时除了真正选中的 action 以外,还认为哪些 token 是可能的。

聪明的你发现了,如果想去灌水几篇学术会议,这个 generator distribution 的采样方法,你去换上几种,试一试谁比谁好,又是几篇 paper (LoL)。

3.3 等一下,我们真的要在 trainer 里显式计算 Jθ(v)J_\theta(v) 吗?

看到这里你可能又要有点头大了,因为:

Jθ(v)=∇θzθ(v)J_\theta(v)=\nabla_\theta z_\theta(v)(99)

可是 θ\theta 是整个 LLM 的所有参数。70B 模型的话,Jθ(v)J_\theta(v) 本身就是一个几十 billion 维的 vector。

难道我们真的要给每一个 alternative token 都 materialize 一个巨大的 Jacobian,然后再算:

Jθ(a)−1K∑kJθ(vk)?J_\theta(a)-\frac{1}{K}\sum_kJ_\theta(v_k)?(100)

当然不是。JθJ_\theta 在这里主要是一个帮助我们理解算法的数学对象。真正写代码的时候,我们只需要构造一个 scalar surrogate,然后让 autograd 帮我们把它求导。先回到 SFC 想要的 gradient:

Jθ(a)−Ev∼qϕ[Jθ(v)].J_\theta(a)-\mathbb E_{v\sim q_\phi}[J_\theta(v)].(101)

因为:

Jθ(v)=∇θzθ(v),J_\theta(v)=\nabla_\theta z_\theta(v),(102)

所以:

Jθ(a)−Ev∼qϕ[Jθ(v)]=∇θ(zθ(a)−Ev∼qϕ[zθ(v)]).J_\theta(a)-\mathbb E_{v\sim q_\phi}[J_\theta(v)]=\nabla_\theta\left(z_\theta(a)-\mathbb E_{v\sim q_\phi}[z_\theta(v)]\right).(103)

于是我们真正需要构造的 scalar 非常简单:

cθ,ϕ(a)=zθ(a)−Ev∼qϕ[zθ(v)].c_{\theta,\phi}(a)=z_\theta(a)-\mathbb E_{v\sim q_\phi}[z_\theta(v)].(104)

如果用刚才的 KK 个 alternative samples:

c^θ,ϕ(a)=zθ(a)−1K∑k=1Kzθ(vk).\hat c_{\theta,\phi}(a)=z_\theta(a)-\frac{1}{K}\sum_{k=1}^Kz_\theta(v_k).(105)

然后像普通 policy gradient 一样乘上 advantage:

LSFC=−A(a)c^θ,ϕ(a).L_{\mathrm{SFC}}=-A(a)\hat c_{\theta,\phi}(a).(106)

对它做一次普通 backward:

∇θLSFC=−A(a)(Jθ(a)−1K∑k=1KJθ(vk)).\nabla_\theta L_{\mathrm{SFC}}=-A(a)\left(J_\theta(a)-\frac{1}{K}\sum_{k=1}^KJ_\theta(v_k)\right).(107)

这就是我们想要的 SFC gradient,所以在实际代码里,也是这样,大概逻辑是:

  1. generator 告诉 trainer:primary token 是谁,另外几个 alternative token 是谁;
  2. trainer 正常 forward 当前模型;
  3. 从当前 trainer 的 logits 里 gather 出这几个 token 对应的 logits;
  4. 算一个 primary_logit - mean(alternative_logits);
  5. 乘 advantage;
  6. backward。

3.4 为什么这里甚至连完整的 log-softmax 都不需要?

我们前面明明一直在讲 score:

∇θlog⁡pθ(a),\nabla_\theta\log p_\theta(a),(108)

为什么到了 SFC 实现的时候突然变成直接操作 logit zθ(a)−Eqϕ[zθ(v)]z_\theta(a)-\mathbb E_{q_\phi}[z_\theta(v)]?

原因是 Softmax 的 normalization term 正好会被 cancel。我们可以先从下面这个 quantity 出发:

log⁡pθ(a)−Ev∼qϕ[log⁡pθ(v)].\log p_\theta(a)-\mathbb E_{v\sim q_\phi}[\log p_\theta(v)].(109)

因为:

log⁡pθ(v)=zθ(v)−log⁡Zθ,\log p_\theta(v)=z_\theta(v)-\log Z_\theta,(110)

其中:

Zθ=∑uezθ(u),Z_\theta=\sum_u e^{z_\theta(u)},(111)

所以:

log⁡pθ(a)−Ev∼qϕ[log⁡pθ(v)]=zθ(a)−log⁡Zθ−Ev∼qϕ[zθ(v)−log⁡Zθ].\log p_\theta(a)-\mathbb E_{v\sim q_\phi}[\log p_\theta(v)]=z_\theta(a)-\log Z_\theta-\mathbb E_{v\sim q_\phi}[z_\theta(v)-\log Z_\theta].(112)

对于固定 prefix,log⁡Zθ\log Z_\theta 和 sample 到哪个 vv 没有关系,所以:

Ev∼qϕ[log⁡Zθ]=log⁡Zθ.\mathbb E_{v\sim q_\phi}[\log Z_\theta]=\log Z_\theta.(113)

于是两个 normalization term 直接消掉:

log⁡pθ(a)−Ev∼qϕ[log⁡pθ(v)]=zθ(a)−Ev∼qϕ[zθ(v)].\log p_\theta(a)-\mathbb E_{v\sim q_\phi}[\log p_\theta(v)]=z_\theta(a)-\mathbb E_{v\sim q_\phi}[z_\theta(v)].(114)

这件事情在工程上非常 nice:为了这条 SFC loss path,我们只需要 selected token 的 logits,不需要为了它额外 materialize 整个 vocabulary 的 log-softmax。

这里要稍微严谨一点:这并不代表 LLM forward 从此完全不需要 full vocabulary logits。它只意味着 SFC 这一条 loss / gradient path 本身可以只依赖少量被选中的 token logits。

3.5 纯随机 sample 还不够好:为什么实际会用 Head + Tail?

如果只是最简单地从 qϕq_\phi 里随机 sample:

v1,…,vK∼qϕ,v_1,\ldots,v_K\sim q_\phi,(115)

当然已经可以得到一个 unbiased estimator。但是 LLM 的 next-token distribution 往往非常不均匀:少量 top tokens 可以占掉绝大部分 probability mass,而后面是一个非常长的 tail。

假设某个 prefix 下:

qϕ(A)=0.40,qϕ(B)=0.25,qϕ(C)=0.15,q_\phi(A)=0.40,\qquad q_\phi(B)=0.25,\qquad q_\phi(C)=0.15,(116)

光前三个 token 就已经占了 80% 的 probability mass。

这时候如果我们还把所有 token 都当成普通 Monte Carlo sample 来处理,就有点浪费:明明 A/B/CA/B/C 几乎每次都很重要,我们却还在随机决定“这次有没有抽到它”。

所以一个很自然的做法是:高概率的 head 直接精确计算,只有 long tail 用 Monte Carlo sample。

假设 generator 选出 top-HH token 组成 head set:

H=TopH⁡(qϕ),\mathcal H=\operatorname{TopH}(q_\phi),(117)

它们的总 probability mass 是:

α=∑h∈Hqϕ(h).\alpha=\sum_{h\in\mathcal H}q_\phi(h).(118)

那么任意一个 expectation 都可以拆成:

Ev∼qϕ[f(v)]=∑h∈Hqϕ(h)f(h)+(1−α)Ev∼qϕ(⋅∣v∉H)[f(v)].\mathbb E_{v\sim q_\phi}[f(v)]=\sum_{h\in\mathcal H}q_\phi(h)f(h)+(1-\alpha)\mathbb E_{v\sim q_\phi(\cdot\mid v\notin\mathcal H)}[f(v)].(119)

第一项直接精确计算,第二项从 conditional tail distribution 里 sample MM 个 token:

t1,…,tM∼qϕ(⋅∣v∉H).t_1,\ldots,t_M\sim q_\phi(\cdot\mid v\notin\mathcal H).(120)

于是:

Ev∼qϕ[f(v)]≈∑h∈Hqϕ(h)f(h)+(1−α)1M∑m=1Mf(tm).\mathbb E_{v\sim q_\phi}[f(v)]\approx\sum_{h\in\mathcal H}q_\phi(h)f(h)+(1-\alpha)\frac{1}{M}\sum_{m=1}^Mf(t_m).(121)

在 SFC 里,把:

f(v)=Jθ(v)f(v)=J_\theta(v)(122)

代进去就行。

你可以把这个方法理解成一句朴素的话:

最重要的几个 token 我全都要;剩下概率很小但是数量巨大的 long tail,我再用几个 sample 来代表。

这个 head/tail sampling 本身并不是什么新东西,本质上就是 stratified Monte Carlo。但对于 LLM 这种 vocabulary 很大、概率又高度集中的分布来说,它非常实用。

3.6 所以 SFC 在实际系统里到底需要做什么?

到这里我们可以把整个操作过程用非常具体的方式重新说一遍。

Generator 在某个 prefix xtx_t 上有自己的 behavior policy:

qϕ(⋅∣xt).q_\phi(\cdot|x_t).(123)

它正常 sample 一个真正进入 trajectory 的 token:

at∼qϕ.a_t\sim q_\phi.(124)

与此同时,它额外保留少量能够描述当前 distribution 的 token。最简单可以直接从 qϕq_\phi sample KK 个 alternative tokens;更实际一点,可以保存 top-HH head tokens,再从 conditional tail 里 sample 几个 token。

然后 generator 把这些 token id、对应的 qϕq_\phi probability / log probability,以及自己当时的 model version 一起随着 rollout 传给 trainer。

Trainer 收到这些数据以后,用当前参数 θ\theta 正常 forward,然后只 gather:

zθ(at),zθ(vt,1),…,zθ(vt,K).z_\theta(a_t),\qquad z_\theta(v_{t,1}),\ldots,z_\theta(v_{t,K}).(125)

再用这些 selected logits 构造对:

Eqϕ[zθ(v)]\mathbb E_{q_\phi}[z_\theta(v)](126)

的 sparse estimator,最后形成:

zθ(at)−E^qϕ[zθ(v)].z_\theta(a_t)-\widehat{\mathbb E}_{q_\phi}[z_\theta(v)].(127)

乘上 advantage:

Lt=−At(zθ(at)−E^qϕ[zθ(v)]),L_t=-A_t\left(z_\theta(a_t)-\widehat{\mathbb E}_{q_\phi}[z_\theta(v)]\right),(128)

然后正常 backward。

Autograd 自然就会给出:

−At(Jθ(at)−E^qϕ[Jθ(v)]).-A_t\left(J_\theta(a_t)-\widehat{\mathbb E}_{q_\phi}[J_\theta(v)]\right).(129)

所以如果非要用一句话总结,Score Function Centering 就是:Generator 不再只告诉 trainer“我最后选了哪个 token”,而是顺手带回来一点“我当时的 local distribution 长什么样”的信息;trainer 再利用这些 sparse samples,把原本以 pθp_\theta 为中心的 score,改成以真正生成数据的 qϕq_\phi 为中心。 它解决的问题是:

我们想计算 Eqϕ[Jθ]\mathbb E_{q_\phi}[J_\theta],但是我们不可能把完整的 qϕq_\phi 从 generator 搬到 trainer。

所以我们只能想办法在 generator 端保存一个足够便宜、但又足够有信息量的 sparse approximation。

最后再强调一个很重要的边界:SFC 恢复的是:

Ea∼qϕ[s~θ,ϕ(a)]=0,\mathbb E_{a\sim q_\phi}[\tilde s_{\theta,\phi}(a)]=0,(130)

也就是 behavior distribution 下的 zero-mean / baseline-invariance property。它并不意味着:

s~θ,ϕ(a)=∇θlog⁡pθ(a).\tilde s_{\theta,\phi}(a)=\nabla_\theta\log p_\theta(a).(131)

只要:

qϕ≠pθ,q_\phi\neq p_\theta,(132)

这两个东西一般就不是同一个 quantity。所以 SFC 不是“我们把 off-policy bias 修好了”。更准确地说,它做的是:

在 trainer-generator mismatch 已经存在的情况下,重新构造一个和真实 sampling distribution qϕq_\phi 对齐的 centered gradient signal,让我们至少重新拿回 zero-mean score 以及 baseline invariance 这两个非常重要的结构。

GRPO — 次阅读

评论

引用
@misc{zhang2026scorefunctioncentering,
  author = {Zhang, Yihua},
  title  = {Score Function Centering Made Simple: Understanding Trainer-Generator Mismatch in Async RL from the Score Function},
  year   = {2026},
  url    = {https://normaluhr.github.io/en/p/score-function-centering}
}