The Most Intuitive Way to Understand Score Function Centering: Understand Trainer-Generator Mismatch in Async RL from the Score Function
You can always find my latest articles (also the Chinese version if you are a native Chinese speaker) in my personal blog webiste, which has better rendering and interactive experience and you can also leave comments there! It astonishes me that I need to pay for a pro subscription in order to post community article in HuggingFace. I probably will stop my subscription some day and you know where to find me ⬆️
I find that the explanations of score function centering available online are all more or less roundabout, and not direct enough. I want this article to give you the most direct explanation of score function centering, starting from intuition. This article was written entirely by hand, not by AI. Of course, if you are reading the English version, I am sorry, that one was translated by AI.
This article assumes you know how to use PPO or GRPO (what the loss looks like) and some simple probability theory (that really is enough). If you have not read it yet, please take ten minutes to read this article.
If any one of the following describes you, this article should suit you:
- You only know how to use GRPO, but you do not know the principle behind it
- You want to deepen your understanding of trainer-generator mismatch
- You work on async RL with long rollout training and are still not sure how to improve the stability of the algorithm
- You have no idea what score function centering is (you may not even know what the score here is) but you want to find out
Score Function Centering is not an established term. I know that every frontier lab has its own name for this technique, alternative token, covariance optimization and so on. Call it whatever you like. This article will also explain where these names come from.
TL;DR:
- After section 1 you will know that the score function is an important bridge for computing the policy gradient, and the most direct thing we can get out of auto-differentiation.
- After section 2 you will know the important property of the score function: it is already centered, so its expectation is 0. We need that expectation to stay 0 in order to replace reward with the so-called advantage in the policy gradient. You will also see how trainer-generator mismatch breaks that property. And you will see what breaking it actually costs: the mean advantage produces an extra gradient that pulls the current trainer toward the old generator, which is why we need to repair it.
- After section 3 you will know what score function centering is really doing, why it repairs that zero-expectation property, how we should use it in practice, and one direction for churning out papers in this area.
1. Let us start from the score function. What is a score? Why do we use it?
1.1 In GRPO/PPO, what gradient are we actually computing?
Below is a quick derivation of the policy gradient. If you are already quite familiar with this part, skip this subsection.
Suppose that at some state, that is, at some LLM prefix , the policy is:
For simplicity, let us drop for now and write:
Our goal is very natural:
"Make high reward actions easier for the model to sample."
So we can write the simplest possible objective:
What this says is: "Sample an action from the current model , compute the reward, and hope its expectation keeps going up."
The question is: how do we compute ? There is a difficulty here: does not appear directly inside . It is hidden inside the sampling distribution . In other words, we are not computing . In fact, the reward in RL is perfectly allowed to be non-differentiable. For example: 1 point for a correct answer to a maths problem and 0 otherwise, or a human expert handing out a score directly. None of that matters. What really depends on is .
Writing the expectation out:
Differentiating:
Then we use a very simple but extremely important identity:
Because:
So:
Writing it back as an expectation:
Here, at last, is the most critical object in the policy gradient:
This thing is called the score function. Note that the score here is not a reward score. It is not "how many points did this answer get". It is a specific term from probability and statistics:
That is, the gradient, with respect to the model parameters, of the log probability of some sampled action.
1.2 Why do we study the score?
Because the core structure of the policy gradient really is just:
reward score
(I know you want to say that what we actually use is , but hold that thought.)
Written the way we are more used to when training LLMs:
So you can read the two parts like this: tells us "should this action be reinforced or weakened, and by how much?" while tells us "if I want to raise or lower the probability of this action, which direction should the parameters move in?"
This is also why I think the most important sentence for understanding the policy gradient is:
The score function is the bridge that turns "the gradient of an expectation over a distribution", which you have no way of computing, into "a gradient estimate you can compute by sampling".
More precisely:
distribution-level differentiation sample-level gradient estimation
What we originally wanted to compute was:
It looks like it requires differentiating through the whole action distribution.
But the score-function trick turns it into this: first sample an action
then compute:
Sample a few more times and average:
This is the core computational mechanism of REINFORCE and of policy gradient. And although GRPO adds group-relative advantage, importance ratio, clipping and so on, the basic object inside it that actually produces the policy gradient still comes down to:
At the same time, you will notice that this is the most direct thing a single backward hands you when you actually use GRPO.
2. The score function and trainer-generator mismatch
2.1 What is the beautiful property of the score function, and why does it matter?
Let us now talk about the advantage. We all know that in GRPO/PPO we do not use the reward directly, we use the advantage (why?). The advantage is the reward minus a constant:
Here can be a value function, it can be a group mean, and it can be any baseline that depends only on the current state and not on the specific action. In GRPO, for instance, the thing we are most familiar with is sampling many responses for the same prompt and then using some group-relative reward as the advantage. Never mind exactly how the normalization is done, in essence it is all doing one thing: subtracting from the raw reward a part that everyone shares, leaving only whether this action is better or worse than average. But there is a very basic question here that is also very easy to overlook:
What gives us the right to subtract this baseline at all?
The original policy gradient is:
If we swap the reward for the advantage:
then the new gradient is:
Expanding it:
The first term is our original policy gradient. In other words, if we want the expectation of the policy gradient to be completely unchanged after subtracting the baseline, the second term has to vanish:
And is clearly not necessarily 0, so what we really need is:
This is a very important property of the score function inside the policy gradient. You can ignore for now why the property holds and simply remember what it means in practice:
As long as the expectation of the score is 0, we can subtract from the reward any baseline that does not depend on the action, without changing the expected policy gradient.
This is why we can write the reward as an advantage, and why reward centering, value baselines and group-relative baselines all work inside PPO and GRPO. Some papers explain this starting from covariance, writing something like:
and then, because , only the covariance is left. There is of course nothing wrong with writing it this way, but personally I feel it makes something very simple sound roundabout. What we actually care about is one sentence: I want to subtract a baseline from the reward, and I do not want that baseline to produce a gradient of its own. That naturally requires . It really is that simple.
And this property fits intuition very well. Suppose the rewards of two actions are:
and:
From the point of view of policy optimization, these two sets of rewards carry exactly the same information: is 2 points better than in both. We clearly do not want the expected update direction of the model to change simply because we added 99 to every reward. Adding or subtracting the same constant from all rewards never changes the expected policy gradient. This is also why a baseline is fundamentally a very natural operation and not merely a mathematical trick.
Now the question is:
Why does the expectation of the score happen to equal 0?
To answer that, we finally need to look at what the score really is.
2.2 What exactly is the score function?
Next we do some calculus on the score function, the kind anyone who has taken a calculus course can do. We defined earlier:
Suppose the current model outputs, at some prefix, the logits of all tokens in the vocabulary:
We assume as usual that the output distribution is given by Softmax:
So:
Let us give the first term above a nicer name, the logit Jacobian:
You can read this as: if I change the current trainer's model parameters slightly, in which direction does the raw logit of token move.
Here is a concrete example. Suppose the model parameters change from to:
then to first order:
So is essentially a "logit sensitivity direction" for token in parameter space. It tells us which way the parameters have to move to push this token's logit up or pull it down.
Next we deal with the second term:
This needs the chain rule twice. First differentiate the outer log, which puts the sum inside the log into the denominator:
Then move the gradient inside the sum and use the chain rule once more on every :
Putting the two steps together gives:
And it happens that:
So the second term can be written as:
That is:
Therefore:
This expression is especially important, because it tells us that inside the score function, Softmax is already centering the logit Jacobian by itself. is the logit gradient of the action we just sampled, while is the average logit gradient over all actions, weighted by the current policy .
So the score really is the logit Jacobian of the sampled action minus the average logit Jacobian under the current policy.
With that expression in hand, the fact that the expectation of the score is 0 barely needs proving. Because the action itself is sampled from:
we have:
Splitting the expectation:
Here and are just two dummy variables with different names. They both come from exactly the same distribution , so the two terms are the same thing:
So now we can put the whole story together: the Softmax score is centered by construction, therefore:
and this zero-mean property in turn guarantees that we can use reward minus baseline in place of the raw reward in the policy gradient, without changing the expected policy gradient.
Up to here everything is lovely. But notice that the whole derivation above quietly uses one assumption we have not yet stated carefully:
The distribution the expectation is computed over, and the distribution the action is generated from, are both .
In other words, we assume:
the generator that produces the rollout is the very trainer that is now running backward.
The story only really begins once that assumption fails.
2.3 What exactly does trainer-generator mismatch break?
It must break , the zero-mean property of the score function, otherwise all that discussion above would have been for nothing.
But the most important question here is not "it is broken", it is: which step exactly changed?
Let us first separate the trainer and the generator clearly. Suppose the trainer policy that is currently running backward and about to update the parameters is:
And the generator that actually produced the rollout is a slightly older policy:
I deliberately used different parameters and here. You can read as the trainer's current parameters, and as the version the generator took away to do the rollout. In synchronous training we approximately have:
and we can even simply treat it as:
But in async RL, especially when rollouts are long, there are many generators, and the trainer keeps updating, the two distributions naturally start to drift apart:
Now recall the formula we just used to prove the zero-mean property:
Why is it 0? Essentially because both expectations are taken over the same . In the first expectation, the action is sampled from . In the second, Softmax itself uses to center all the logit Jacobians. So the two terms match exactly.
But now the generator has become . The action that actually reaches the trainer no longer satisfies:
but instead:
Note that the trainer has not changed the way it computes the score. What we run backward is still the current trainer's:
The here still comes from the current trainer, and the Softmax centering still uses the current trainer distribution . Only one thing has actually changed: which distribution the action on the outside was sampled from.
So what we should really be looking at now is no longer:
but:
Expanding the score:
Splitting the expectation:
Now the problem is obvious. Originally we had:
where both expectations are over the same distribution, so it equals 0. Now it has become:
The left side averages under the generator distribution , while the right side still averages under the trainer distribution . As long as:
we generally can no longer expect:
So in the end:
This is the most direct effect of trainer-generator mismatch on the score function.
You will find there is nothing mysterious about this at all. The score did not suddenly go bad, the Softmax formula did not change, and the the trainer computes did not change either. The only thing that actually changed is that the distribution the action is sampled from went from to .
And the zero-mean property happens to depend on "the distribution the action is sampled from" and "the distribution the centering inside the score uses" being the same distribution. Those two things are now different:
So the old:
has become:
At this point we have in fact explained the most critical layer of trainer-generator mismatch:
In synchronous RL, the generator and the trainer are the same distribution, so the standard score is zero-mean by nature. In async RL, the action comes from the old generator while the score is still centered by the current trainer , and so the zero-mean property is broken.
2.4 What does this non-zero mean actually cause in practice?
At this point you may feel that still sounds a bit abstract. We said above that it breaks baseline invariance, but what kind of gradient does it actually produce in training?
This becomes very intuitive if we look at it directly in logit space. For a Softmax, the gradient of the log probability of some action with respect to the whole logit vector is:
where is the one-hot vector of action . In synchronous training , so taking the expectation of it gives:
But now the action is sampled by the old generator :
This formula makes the intuition behind it easier to see than did: if some token is more common in the old generator than in the current trainer, that is , this direction pushes its logit up. The other way around, if , this direction pulls it down.
Now let us go back to the baseline we have been discussing all along. Suppose you add a constant that has nothing to do with the action to every reward or advantage. In a normal on-policy setting this constant should not produce any expected gradient. But after trainer-generator mismatch it produces an extra:
If , what this gradient does is very direct: it pulls the current trainer toward the distribution of the old generator .
The other way around, if , the trainer is pushed away from the generator. That is a bad thing to have, because the here is playing the role of the baseline, and in theory a baseline is free to be positive or negative. So we have effectively added a perturbation to the distance between the trainer and the generator, and the direction and the size of that perturbation are decided entirely by the value of the baseline.
There is another way to look at this direction. Suppose we really do take the samples of the old generator and maximize the log likelihood of the current trainer:
its gradient in logit space is exactly:
In other words:
So when the mean reward or the mean advantage is positive, that thing which was supposed to be no more than "the part that everyone shares" quietly turns into a gradient whose direction is exactly the direction of doing behavior cloning on the old generator. This is quite strange: the average says nothing about which action is better or worse than which, so what gives it the right to decide whether we move toward the old generator, and by how much?
The same thing shows up in the parameter-space notation we used earlier. Split the advantage into:
Then:
And the second term is:
This is one of the most practical consequences of breaking the zero-mean property: the term that really describes how good one token is relative to another is the first one, but the mean advantage additionally multiplies a trainer-generator mismatch direction.
Looking at Score Function Centering now, the motivation is more direct. We do not only want a mathematical expectation to look like 0 again, we also want to take this old-policy cloning direction, the one quietly controlled by "the average reward level", out of the reward gradient. How far apart the trainer and the generator should be allowed to drift is a separate question, and should not get to decide it for us in passing here.
3. What is Score Function Centering actually doing?
The answer is almost shouting at you by now. Score Function Centering simply wants the broken zero-mean property of the score function to hold again, so that we can go back to happily using a baseline.
We just saw that after trainer-generator mismatch, the real problem becomes:
Why is this not 0? The reason is very simple: the action was sampled from the generator's , but the "center" subtracted inside the score is still the trainer's .
To correct that, given that the action on the outside already comes from , the most direct approach is of course: stop centering with , and center with , the distribution that actually generated these actions.
That is, take the original standard score:
and change it to:
This is the core step of Score Function Centering.
Note that there are two distributions and model parameters here, and they should not be mixed up. is still:
so it is still the logit Jacobian of the current trainer. We are not going back to the old generator to compute gradients, and we are not updating . The only thing that changes is the probability distribution used for centering:
You can remember it as this: the generator decides "how to average", the trainer decides "which parameter direction to update".
With that single change, the zero-mean property comes straight back. Because the real action was from:
we get:
and are still two dummy variables, and this time they finally come from the same distribution again:
So we recover:
In other words, under the distribution that actually produced the rollout, the baseline once again stops producing an extra expected gradient of its own.
This is the most essential intuition of Score Function Centering: align the centering distribution of the score with the distribution that actually generates the actions.
3.1 But there is a big problem in practice: how do we get ?
If you are really training an LLM and you use the method above, you immediately run into a very practical problem:
Suppose the vocabulary has 128K tokens. Then this expectation appears to require knowing the generator's probability for all 128K tokens at this prefix:
The problem is that this information only exists at the moment the generator produces the token, while the trainer that actually runs backward may be on another machine, in another pipeline stage, or even dozens of train steps later.
Of course you could say: then I will just store the generator's entire vocabulary distribution and send it to the trainer, problem solved.
In theory you can. In engineering you cannot.
Because for every token position in the rollout you would have to store an extra distribution of length vocabulary size. Suppose the vocab is 128K and a rollout has thousands or even tens of thousands of tokens, then multiply by batch size, by the number of generators, and by the number of parallel rollouts. That communication and memory overhead very quickly becomes completely unacceptable.
So the practical problem becomes:
We want the expectation under , but we cannot move the complete to the trainer. What do we do?
The answer is Monte Carlo again: since the expectation is an average over in the first place, let us sample a few more tokens from on the generator side.
3.2 Alternative tokens: do not give the trainer the whole distribution, a few samples are enough
Suppose the token actually chosen by the generator and entered into the rollout is:
Besides this primary action, at exactly the same prefix we additionally sample tokens from the generator distribution:
These tokens are never actually appended to the trajectory, and they do not change the subsequent generation. They only tell the trainer:
"This is roughly what my distribution looked like at this prefix."
Different places give these tokens different names, alternative tokens, alter tokens, negative tokens, auxiliary samples, and so on. They are all doing the same thing in essence: using a small number of samples to describe the generator's local action distribution at the time.
If these really are sampled IID from , then:
So the SFC score can be approximated as:
And this Monte Carlo estimator is unbiased for the behavior-centered score we actually want:
So the generator does not have to hand the whole to the trainer. It only needs to attach a small amount of extra information at each token position, such as:
More realistically, the log probability of these tokens under the generator is stored alongside them:
This information then travels with the rollout all the way from the generator to the trainer. So you can picture an ordinary RL rollout token as originally carrying only:
while after SFC it also carries a very small local policy sketch:
This local policy sketch tells the trainer: besides the action it actually chose, which other tokens the generator considered possible at the time.
Clever as you are, you will have noticed that if you want to churn out a few conference papers, you can swap in a few different sampling methods for this generator distribution, see which beats which, and there are several papers right there (LoL).
3.3 Hold on, do we really have to compute explicitly in the trainer?
At this point your head may be starting to hurt, because:
and is every parameter of the whole LLM. For a 70B model, is itself a vector with tens of billions of dimensions.
Do we really have to materialize a huge Jacobian for every alternative token and then compute:
Of course not. Here is mainly a mathematical object that helps us understand the algorithm. When we actually write the code, all we need is to construct a scalar surrogate and let autograd differentiate it for us. Go back to the gradient SFC wants:
Because:
we have:
So the scalar we actually need to construct is very simple:
With the alternative samples from before:
Then multiply by the advantage, just like an ordinary policy gradient:
Run one ordinary backward on it:
This is exactly the SFC gradient we wanted, and real code works the same way. The rough logic is:
- the generator tells the trainer which token is the primary one and which are the alternative ones;
- the trainer runs a normal forward of the current model;
- gather the logits of those tokens from the current trainer's logits;
- compute
primary_logit - mean(alternative_logits); - multiply by the advantage;
- backward.
3.4 Why do we not even need a full log-softmax here?
All along we have been talking about the score:
so why does the SFC implementation suddenly work directly on logits, ?
The reason is that the Softmax normalization term cancels exactly. We can start from this quantity:
Because:
where:
we get:
For a fixed prefix, has nothing to do with which was sampled, so:
The two normalization terms therefore cancel outright:
This is very nice from an engineering point of view: for this SFC loss path, we only need the logits of the selected tokens, and we do not need to materialize the log-softmax over the whole vocabulary for its sake.
To be a little more rigorous: this does not mean the LLM forward never needs full vocabulary logits again. It only means that the SFC loss and gradient path itself can depend on a small number of selected token logits.
3.5 Purely random sampling is not good enough: why do people use head plus tail in practice?
If we simply sample at random from :
we do of course already get an unbiased estimator. But the next-token distribution of an LLM is often extremely uneven: a small number of top tokens can take up the vast majority of the probability mass, and behind them is a very long tail.
Suppose that at some prefix:
the first three tokens alone already account for 80% of the probability mass.
If we still treat every token as an ordinary Monte Carlo sample here, it is a bit wasteful: clearly matter almost every time, and yet we are still leaving it to chance whether we happen to draw them.
So a very natural approach is: compute the high-probability head exactly, and use Monte Carlo sampling only for the long tail.
Suppose the generator picks the top-\(H\) tokens to form the head set:
whose total probability mass is:
Then any expectation can be split as:
The first term is computed exactly, and for the second we sample tokens from the conditional tail distribution:
So:
In SFC, substitute:
and you are done.
You can understand this method with one plain sentence:
I want all of the few most important tokens; for the long tail, which has tiny probabilities but an enormous number of entries, I use a few samples to stand in for it.
This head/tail sampling is nothing new in itself, it is essentially stratified Monte Carlo. But for a distribution like an LLM's, with a large vocabulary and probability highly concentrated, it is very practical.
3.6 So what does SFC actually require in a real system?
At this point we can walk through the whole procedure again in very concrete terms.
At some prefix , the generator has its own behavior policy:
It samples, as usual, one token that really enters the trajectory:
At the same time it keeps a small number of extra tokens that describe the current distribution. The simplest option is to sample alternative tokens directly from . More realistically, it can keep the top-\(H\) head tokens and sample a few more from the conditional tail.
The generator then passes these token ids, their probabilities or log probabilities, and its own model version at the time, along with the rollout, to the trainer.
Once the trainer receives this data, it runs a normal forward with the current parameters and then gathers only:
It uses these selected logits to build a sparse estimator of:
and finally forms:
Multiply by the advantage:
and then run a normal backward.
Autograd will naturally give:
So if I had to sum up Score Function Centering in one sentence: the generator no longer tells the trainer only "which token I finally chose", it also brings back a little information about "what my local distribution looked like at the time"; the trainer then uses these sparse samples to turn a score that was centered on into one centered on , the distribution that actually generated the data. The problem it solves is:
We want to compute , but we cannot move the complete from the generator to the trainer.
So all we can do is find a way to keep, on the generator side, a sparse approximation that is cheap enough and still informative enough.
One last boundary is worth stressing. What SFC restores is:
that is, the zero-mean and baseline-invariance property under the behavior distribution. It does not mean that:
As long as:
these two are generally not the same quantity. So SFC is not "we fixed the off-policy bias". More precisely, what it does is:
given that trainer-generator mismatch already exists, rebuild a centered gradient signal that is aligned with the real sampling distribution , so that we at least get back the two very important structures of a zero-mean score and baseline invariance.
