Part III · RL for LLMs · Chapter 16

The critic-free algorithm zoo

RLOO, GRPO, Dr. GRPO, DAPO, GSPO, CISPO and friends — one estimator, different knobs.

~150 minBefore this: Policy gradients, PPO, RLVR & GRPOUpdated 2026-10

Open three reinforcement-learning papers from 2025 and you will meet three new acronyms: DAPO, Dr. GRPO, GSPO. A month later there are three more. Each paper prints a long objective full of sums, mins, clips and fractions, and claims it fixes something the previous one got wrong. It feels like learning a new algorithm every week.

It isn’t. Almost every method in this family computes the same thing: REINFORCE from Chapter 7, with a baseline taken from a group of samples, applied to the tokens of a response. The papers differ in a handful of choices: what to subtract from the reward, whether to divide by a standard deviation, how to average over tokens, how to clip the importance ratio, whether to keep a KL term, and which samples to keep. This chapter calls those choices knobs. You will learn the master formula once, see what each knob does on a batch of eight responses small enough to compute by hand, and then read every algorithm in the zoo as a setting of the knobs. After that, the next new acronym will take you five minutes to place.

You should know GRPO from Chapter 15 (group sampling, group-normalized advantages, the clipped token-level objective) and PPO’s clip from Chapter 9. We recap what we need.

One estimator, six knobs

Where we start: REINFORCE on a response

Take one prompt xx and treat the whole response yy as a single action, as in the contextual-bandit view of Chapter 11. The reward r(x,y)r(x, y) arrives at the end. The policy gradient with a baseline bb (Chapter 7) is

∇θJ=Ey∼πθ[(r(x,y)−b) ∇θlog⁡πθ(y∣x)].\nabla_\theta J = \mathbb E_{y\sim\pi_\theta}\big[(r(x,y) - b)\,\nabla_\theta \log \pi_\theta(y\mid x)\big].

The log-probability of a response is the sum of its token log-probabilities, so the same gradient, written token by token, is

∇θJ=Ey∼πθ[(r(x,y)−b)∑t=1∣y∣∇θlog⁡πθ(yt∣x,y<t)].\nabla_\theta J = \mathbb E_{y\sim\pi_\theta}\Big[(r(x,y) - b)\sum_{t=1}^{\lvert y\rvert}\nabla_\theta \log \pi_\theta(y_t\mid x, y_{<t})\Big].

Read it as an instruction: every token of the response gets the same push, of size r−br - b. Tokens of better-than-expected responses become more likely, tokens of worse-than-expected ones less likely. This is the true gradient. Everything below is a way of estimating it from a finite batch, more cheaply, more stably, or (sometimes) of deliberately estimating something slightly different.

The master formula

Sample GG responses y(1),…,y(G)y^{(1)}, \dots, y^{(G)} for each prompt. A batch has NN responses in total (prompts × GG). Every critic-free method computes an update of this form:

g^  =  ∑i=1N  ∑t=1∣y(i)∣  c(i)⏟aggregation  wt(i)⏟ratio, clip  A^(i)⏟baseline, scale  ∇θlog⁡πθ(yt(i)∣x,y<t(i))  −  β ∇θ(KL term).\hat g \;=\; \sum_{i=1}^{N}\;\sum_{t=1}^{\lvert y^{(i)}\rvert}\; \underbrace{c^{(i)}}_{\text{aggregation}}\; \underbrace{w^{(i)}_t}_{\text{ratio, clip}}\; \underbrace{\hat A^{(i)}}_{\text{baseline, scale}}\; \nabla_\theta \log\pi_\theta\big(y^{(i)}_t \mid x, y^{(i)}_{<t}\big) \;-\;\beta\,\nabla_\theta(\text{KL term}).

In words: each token’s log-probability is pushed by the response’s advantage A^(i)\hat A^{(i)}, times a per-token weight wt(i)w^{(i)}_t that corrects (or clips) for the batch having been sampled by a slightly older policy, times a normalizing constant c(i)c^{(i)} that decides how tokens are averaged. Here are the six knobs:

KnobQuestion it answersTypical settings
1. BaselineWhat do we subtract from r(i)r^{(i)}?nothing, batch mean, group mean, leave-one-out, greedy reward
2. ScaleDo we divide by a standard deviation?no, group std, batch std
3. AggregationHow do we average over tokens?per-response mean, per-token mean, constant
4. Ratio and clipHow do we handle off-policy samples?none, PPO clip, asymmetric clip, clipped weight, sequence ratio
5. KLDo we leash the policy to πref\pi_\text{ref}, and how?in reward, in loss, dropped
6. DataWhich samples enter the batch at all?all, drop zero-signal groups, drop or penalize overlong ones

GRPO as a setting of the knobs

Here is GRPO’s objective as the DeepSeekMath paper writes it (their oio_i is our y(i)y^{(i)}, their qq is our xx; Chapter 15 derives it):

JGRPO(θ)=E[1G∑i=1G1∣y(i)∣∑t=1∣y(i)∣{min⁡ ⁣(ρt(i)A^(i), clip(ρt(i),1−ϵ,1+ϵ) A^(i))−β DKL[πθ ∥ πref]}],A^(i)=r(i)−mean⁡(r)std⁡(r).J_\text{GRPO}(\theta) = \mathbb E\Bigg[\frac1G\sum_{i=1}^{G}\frac{1}{\lvert y^{(i)}\rvert}\sum_{t=1}^{\lvert y^{(i)}\rvert}\Big\{\min\!\big(\rho^{(i)}_t \hat A^{(i)},\ \mathrm{clip}(\rho^{(i)}_t, 1-\epsilon, 1+\epsilon)\,\hat A^{(i)}\big) - \beta\,\mathbb D_\text{KL}\big[\pi_\theta \,\Vert\, \pi_\text{ref}\big]\Big\}\Bigg], \qquad \hat A^{(i)} = \frac{r^{(i)} - \operatorname{mean}(r)}{\operatorname{std}(r)}.

Match it against the knobs. Baseline: the group mean. Scale: the group std. Aggregation: 1G∑i1∣y(i)∣∑t\frac1G\sum_i \frac{1}{\lvert y^{(i)}\rvert}\sum_t, an average over tokens inside each response, then over responses. Ratio and clip: PPO’s clip on a per-token ratio ρt(i)=πθ(yt(i)∣⋅)/πold(yt(i)∣⋅)\rho^{(i)}_t = \pi_\theta(y^{(i)}_t\mid\cdot)/\pi_\text{old}(y^{(i)}_t\mid\cdot). KL: in the loss, with the k3k_3 estimator, β=0.04\beta = 0.04. Data: every group is used. At the first update on a fresh batch, πθ=πold\pi_\theta = \pi_\text{old}, every ρ=1\rho = 1, nothing is clipped, and the gradient of the bracket is exactly the master formula with w=1w = 1 and c(i)=1/(G∣y(i)∣)c^{(i)} = 1/(G\lvert y^{(i)}\rvert).

Every other algorithm turns one or more of these knobs. Before the zoo, let’s understand each knob on a concrete batch.

The running example: eight responses

Two prompts, G=4G = 4 responses each, a verifier that says right (1) or wrong (0). The lengths differ on purpose.

ResponsePrompt 1: reward, lengthPrompt 2: reward, length
y(1)y^{(1)}1 (right), 12 tokens0, 6 tokens
y(2)y^{(2)}0 (wrong), 4 tokens0, 16 tokens
y(3)y^{(3)}0 (wrong), 20 tokens0, 10 tokens
y(4)y^{(4)}1 (right), 8 tokens1, 14 tokens

Prompt 1 is the rewards 1, 0, 0, 1 group from Chapter 15: half right. It has a short wrong answer (y(2)y^{(2)}, 4 tokens) and a long wrong one (y(3)y^{(3)}, 20 tokens). Prompt 2 is harder: one right answer out of four. The batch has N=8N = 8 responses and 90 tokens. The generation budget is Lmax=20L_\text{max} = 20 tokens. The demo later in the chapter uses exactly this batch.

Knob 1: the baseline

What a baseline is for

Chapter 7 proved two facts. Subtracting any baseline bb that does not depend on the sampled response leaves the gradient unbiased, because Ey∼π[∇log⁡π(y∣x)]=0\mathbb E_{y\sim\pi}[\nabla\log\pi(y\mid x)] = 0. And a good baseline (close to the expected reward) cuts the variance, often enormously. With 0/1 rewards and no baseline, every wrong answer has r=0r = 0 and gets no push at all: the policy learns only from its successes. With a baseline, wrong answers get a negative advantage and are pushed down.

The ideal baseline is V(x)=Ey∼π[r(x,y)]V(x) = \mathbb E_{y\sim\pi}[r(x,y)], the expected reward for this prompt. PPO learns it with a critic (Chapter 8). For a language model the critic is a second network as large as the policy, and per-token values for reasoning traces are hard to learn. Critic-free methods estimate V(x)V(x) from the samples themselves. They differ in how.

Five baselines, computed

None (plain REINFORCE). A^(i)=r(i)\hat A^{(i)} = r^{(i)}.

Batch mean. Average all rewards in the batch: 3/8=0.3753/8 = 0.375 here. Cheap, but it uses one number for an easy and a hard prompt alike.

Group mean (GRPO). Average the GG rewards of the same prompt: 0.50.5 for prompt 1, 0.250.25 for prompt 2. This is a Monte Carlo estimate of V(x)V(x) for this prompt.

Leave-one-out (RLOO). For response ii, average the other G−1G-1 rewards of its group:

bLOO(i)=1G−1∑j≠ir(j).b^{(i)}_\text{LOO} = \frac{1}{G-1}\sum_{j\neq i} r^{(j)}.

The idea goes back to Kool, van Hoof and Welling (2019), “Buy 4 REINFORCE samples, get a baseline for free!”. Ahmadian et al. (2024) brought it to RLHF as RLOO (REINFORCE Leave-One-Out). Because b(i)b^{(i)} never looks at r(i)r^{(i)}, it does not depend on the sampled response y(i)y^{(i)}, and the estimator is exactly unbiased.

Greedy reward (ReMax, Li et al. 2023). Decode one extra response greedily (always the most likely token) and use its reward as the baseline. One extra generation per prompt instead of a group.

Here are the advantages on our batch, before any scaling:

Responserrnonebatch meangroup meanleave-one-out
P1 y(1)y^{(1)}, y(4)y^{(4)} (right)110.6250.50.667
P1 y(2)y^{(2)}, y(3)y^{(3)} (wrong)00−0.375−0.5−0.667
P2 y(1)y^{(1)}, y(2)y^{(2)}, y(3)y^{(3)} (wrong)00−0.375−0.25−0.333
P2 y(4)y^{(4)} (right)110.6250.751.0

Look at prompt 2’s lone success. The group baselines give it the biggest advantage in the batch (0.75 or 1.0): a rare success on a hard prompt is the most surprising thing that happened, so it gets the biggest push. The batch mean misses this: it gives the same 0.625 to every right answer, easy prompt or hard.

Quick check A group has rewards (1, 0, 0, 0). With GRPO's group-mean baseline and no std division, what advantage does the correct response get?

Group mean versus leave-one-out: a rescaling

Look at the last two columns: the leave-one-out advantages are the group-mean ones multiplied by 4/34/3. That is no coincidence. Write S=∑jr(j)S = \sum_j r^{(j)} for the group’s total. Then

r(i)−SG=G−1G(r(i)−S−r(i)G−1).r^{(i)} - \frac{S}{G} = \frac{G-1}{G}\Big(r^{(i)} - \frac{S - r^{(i)}}{G-1}\Big).

The group-mean advantage is exactly (G−1)/G(G-1)/G times the leave-one-out advantage, response by response. Since the leave-one-out estimator is unbiased, the group-mean estimator has expectation G−1G∇J\frac{G-1}{G}\nabla J: it is biased, but only by a constant factor shared by every prompt with the same GG. A constant factor is absorbed by the learning rate (and almost entirely ignored by Adam). So GRPO’s baseline “includes the sample itself” in a harmless way. Exercise 16.2 asks you to prove the expectation directly.

Knob 2: the scale (to divide by std, or not)

What GRPO’s std does

GRPO divides each group’s centered rewards by the group’s standard deviation. For prompt 1 (rewards 1, 0, 0, 1) the std is 0.5, so the advantages become ±1. For prompt 2 (0, 0, 0, 1) it is 0.25×0.75=0.433\sqrt{0.25\times0.75} = 0.433, giving −0.577-0.577 for the wrong answers and +1.732+1.732 for the right one. (We use the population std, dividing by GG. Some implementations use G−1G-1; the difference is a constant factor G/(G−1)\sqrt{G/(G-1)}.)

The intent is sensible: put every prompt’s advantages on the same scale, as in Chapter 8’s advantage normalization. But a group’s std is not a neutral number. With binary rewards and pass rate pp, the std is p(1−p)\sqrt{p(1-p)}. It is largest at p=0.5p = 0.5 and goes to zero as pp approaches 0 or 1. Dividing by it up-weights the prompts the model almost always gets right and the prompts it almost always gets wrong.

How much, exactly?

Ask what each prompt contributes to the expected gradient. Without std, using the true mean pp as the baseline,

E[(r−p) ∇log⁡π(y∣x)]=E[r ∇log⁡π(y∣x)]=∇θ p(x),\mathbb E\big[(r - p)\,\nabla\log\pi(y\mid x)\big] = \mathbb E\big[r\,\nabla\log\pi(y\mid x)\big] = \nabla_\theta\, p(x),

where p(x)p(x) is the probability that a sample for prompt xx is right. So mean-centering follows the gradient of ∑xp(x)\sum_x p(x), the expected accuracy, with every prompt weighted equally. With the std, every term is divided by p(1−p)\sqrt{p(1-p)}:

E[r−pp(1−p) ∇log⁡π(y∣x)]=∇θ p(x)p(x) (1−p(x)).\mathbb E\Big[\frac{r - p}{\sqrt{p(1-p)}}\,\nabla\log\pi(y\mid x)\Big] = \frac{\nabla_\theta\, p(x)}{\sqrt{p(x)\,(1-p(x))}}.

The extra factor 1/p(1−p)1/\sqrt{p(1-p)} is a prompt weight:

Pass rate pp0.50.9 or 0.10.99 or 0.01
Weight with std, 1/p(1−p)1/\sqrt{p(1-p)}2.03.310.1
Weight without std111

A prompt the model solves 99% of the time is pushed five times harder per unit of ∇p\nabla p than a prompt it solves half the time. Liu et al. (2025) call this the question-level difficulty bias of GRPO and remove the std in Dr. GRPO (“GRPO Done Right”).

Is the difficulty bias good or bad?

Reasonable people disagree, and the evidence is mixed:

  • Against the std. Dr. GRPO removes it. Olmo 3’s recipe and DeepSeek-V3.2 use mean-centered advantages without std. Cursor’s Composer 2 report drops it because “small behavioral differences get massively upweighted” when all rollouts are about equally good. You can see why with non-binary rewards: a group with rewards 0.80, 0.81, 0.79, 0.80 has std 0.007, so the advantages become 0, +1.41, −1.41, 0. Noise of one hundredth becomes a full-sized training signal.
  • For some reweighting. DAPO and GSPO keep the group std. REINFORCE++ (Hu et al. 2025) argues that per-prompt (“local”) std normalization is biased and unstable when the std is near zero, and divides by one global batch std instead. That keeps the overall scale but weights every prompt the same.
  • Go further the other way. MaxRL (Tajwar et al. 2026) argues we should up-weight hard prompts much more, by aiming at ∑xlog⁡p(x)\sum_x \log p(x) (the log-likelihood of success) instead of ∑xp(x)\sum_x p(x). Its gradient is ∇p/p\nabla p / p, a weight of 100 at p=0.01p = 0.01 and about 1 at p=0.99p = 0.99. That is asymmetric: hard prompts count, easy ones don’t.
  • It may not matter much at the end. ScaleRL (Khatri et al. 2025) fit compute-performance curves for many recipe variants and found that advantage normalization mainly changes how fast a run improves, not the level it eventually reaches.

The three weightings in one picture: mean-only weights every prompt 1; std weights both extremes; MaxRL weights only the hard extreme. Each one is the exact gradient of a different objective (∑p\sum p, ∑2arcsin⁡p\sum 2\arcsin\sqrt p, ∑log⁡p\sum\log p). “Bias” just means “not the objective you thought you were optimizing”.

Knob 3: aggregation, and the length bias

Three ways to average over tokens

After computing a per-token term (advantage times ratio, clipped or not), you have a ragged array: response ii has ∣y(i)∣\lvert y^{(i)}\rvert terms. You must turn it into one number to differentiate. Three choices are in use:

NameFormulaUsed by
Sequence-mean1N∑i1∣y(i)∣∑t(⋅)\displaystyle\frac1N\sum_i \frac{1}{\lvert y^{(i)}\rvert}\sum_t(\cdot)GRPO (as written), GSPO
Token-mean1∑i∣y(i)∣∑i∑t(⋅)\displaystyle\frac{1}{\sum_i \lvert y^{(i)}\rvert}\sum_i\sum_t(\cdot)DAPO, CISPO, Olmo 3
Constant1N Lmax∑i∑t(⋅)\displaystyle\frac{1}{N\,L_\text{max}}\sum_i\sum_t(\cdot)Dr. GRPO

They look like bookkeeping. They are not, because they decide the weight c(i)c^{(i)} each token gets, and that weight can depend on the length of the response the token sits in.

The worked example

Take prompt 1’s two wrong answers under GRPO’s advantages (A^=−1\hat A = -1 for both). The short one has 4 tokens, the long one 20. Each token’s weight c⋅A^c\cdot\hat A:

AggregationEach token of y(2)y^{(2)} (4 tokens)Each token of y(3)y^{(3)} (20 tokens)Total push, y(2)y^{(2)} vs y(3)y^{(3)}
Sequence-mean, c=1/(8∣y∣)c = 1/(8\lvert y\rvert)−1/32=−0.031-1/32 = -0.031−1/160=−0.006-1/160 = -0.006−0.125-0.125 vs −0.125-0.125
Token-mean, c=1/90c = 1/90−0.011-0.011−0.011-0.011−0.044-0.044 vs −0.222-0.222
Constant, c=1/(8⋅20)c = 1/(8\cdot 20)−0.006-0.006−0.006-0.006−0.025-0.025 vs −0.125-0.125

Under sequence-mean, each response gets the same total push no matter how long it is, so each token of the long wrong answer is pushed down five times less than each token of the short one. Under the other two, every token is pushed equally, and the long answer, with five times as many tokens, gets five times the total push.

Which one is the policy gradient? Go back to the first section: the true gradient gives every token of response ii the same weight r(i)−br^{(i)} - b, with no 1/∣y∣1/\lvert y\rvert. The constant normalizer gives exactly that, up to one global factor. Token-mean is almost that: its normalizer 1/∑i∣y(i)∣1/\sum_i\lvert y^{(i)}\rvert is shared by the whole batch, so it only rescales the step, by an amount that varies a little from batch to batch. Sequence-mean is the odd one out.

Quick check Under sequence-mean aggregation, two wrong answers have the same advantage. One is 100 tokens long, the other 1000. How hard is each token of the long answer pushed down, compared with each token of the short one?

What sequence-mean actually optimizes

There is a cleaner way to see the bias. Under sequence-mean, response ii contributes

A^(i)∣y(i)∣∑t∇log⁡πθ(yt(i)∣⋅)=r(i)−b∣y(i)∣ ∇log⁡πθ(y(i)∣x).\frac{\hat A^{(i)}}{\lvert y^{(i)}\rvert}\sum_t \nabla\log\pi_\theta(y^{(i)}_t\mid\cdot) = \frac{r^{(i)} - b}{\lvert y^{(i)}\rvert}\,\nabla\log\pi_\theta(y^{(i)}\mid x).

That is the REINFORCE estimator for a different reward, r~(y)=(r(y)−b)/∣y∣\tilde r(y) = (r(y) - b)/\lvert y\rvert (treating the baseline as fixed; with the leave-one-out baseline it is exactly independent of y(i)y^{(i)}). Now look at what r~\tilde r prefers, with b=0.5b = 0.5:

  • A right answer earns 0.5/∣y∣0.5/\lvert y\rvert: shorter is better. A 100-token right answer earns 0.005; a 1000-token one earns 0.0005.
  • A wrong answer earns −0.5/∣y∣-0.5/\lvert y\rvert: longer is better. A 100-token wrong answer costs 0.005; a 1000-token one costs only 0.0005.

So GRPO as written pays the model to be brief when it is right and to ramble when it is wrong. Liu et al. (2025) call this the response-level length bias: “longer responses are penalized less” when they are incorrect, so incorrect responses grow. It matters whenever the model can tell, mid-response, that it is going wrong, which language models can (they hedge, loop, re-check). Part of the famous growth in response length during R1-Zero-style training may come from this bias rather than from useful reasoning, which is one of the paper’s points.

Dr. GRPO’s fix is to replace each response’s ∣y(i)∣\lvert y^{(i)}\rvert by one constant, the generation budget LmaxL_\text{max}. The paper also points out that the same per-response length normalization was already present in open-source PPO implementations before GRPO, so this is not a GRPO-only issue.

∂ Why does the constant normalizer give the unbiased gradient?

The true gradient for one prompt, with any baseline bb independent of the sampled response, is

∇J=Ey∼π[(r(y)−b)∑t=1∣y∣∇log⁡π(yt∣x,y<t)].\nabla J = \mathbb E_{y\sim\pi}\Big[(r(y) - b)\sum_{t=1}^{\lvert y\rvert}\nabla\log\pi(y_t\mid x,y_{<t})\Big].

The sample estimate from NN responses is

g^=1N∑i=1N(r(i)−b(i))∑t∇log⁡π(yt(i)∣⋅),\hat g = \frac1N\sum_{i=1}^N (r^{(i)} - b^{(i)})\sum_{t}\nabla\log\pi(y^{(i)}_t\mid\cdot),

so E[g^]=∇J\mathbb E[\hat g] = \nabla J when b(i)b^{(i)} is leave-one-out. Dr. GRPO’s loss gives 1NLmax∑i∑tA^(i)∇log⁡π(yt(i))\frac{1}{N L_\text{max}}\sum_i\sum_t \hat A^{(i)}\nabla\log\pi(y^{(i)}_t), which is g^/Lmax\hat g / L_\text{max}: the same direction, one fixed scale. Token-mean gives g^⋅N/∑i∣y(i)∣\hat g\cdot N/\sum_i \lvert y^{(i)}\rvert. The factor N/∑i∣y(i)∣N/\sum_i\lvert y^{(i)}\rvert is one over the batch’s average length. It is the same for every token in the batch, so it does not reweight responses against each other; it only makes the step size depend slightly on how long this batch happened to be. Sequence-mean instead multiplies each response by its own 1/∣y(i)∣1/\lvert y^{(i)}\rvert, which changes the expected direction: E[g^seq]=∇ E[(r−b)/∣y∣]\mathbb E[\hat g_\text{seq}] = \nabla\,\mathbb E[(r - b)/\lvert y\rvert], the gradient of a different objective, as in the main text.

What the lab shows

The lab for this chapter includes a tiny “language model” built to expose the bias. A response is an answer token followed by filler tokens; the filler never changes the reward, but the policy may learn to emit more filler after some answers than after others. With everything else equal, sequence-mean aggregation (the GRPO preset) roughly doubles the average length of wrong responses within 300 updates (from about 4 tokens to about 8, with noisy spikes past 10), and shortens right responses from about 3.5 tokens to 2.2. Under Dr. GRPO’s constant normalizer and DAPO’s token-mean, both stay flat. The effect is not subtle once length is something the policy controls.

Knob 4: importance ratios and clipping

Why ratios appear at all

If you generated a batch and took exactly one gradient step on it, you would need no ratio: the samples come from the current policy. In practice you reuse a batch for several minibatch updates, and in large systems the samples are also a few versions stale (asynchronous training) and come from an inference engine whose probabilities differ slightly from the trainer’s (Chapter 20). So from the second update on, πθ≠πold\pi_\theta \neq \pi_\text{old}, and as in Chapter 9 each token’s term is reweighted by the importance ratio

ρt(i)(θ)=πθ(yt(i)∣x,y<t(i))πold(yt(i)∣x,y<t(i)).\rho^{(i)}_t(\theta) = \frac{\pi_\theta\big(y^{(i)}_t\mid x, y^{(i)}_{<t}\big)}{\pi_\text{old}\big(y^{(i)}_t\mid x, y^{(i)}_{<t}\big)}.

PPO’s clip then keeps the update from running away. Using ∇ρ=ρ ∇log⁡π\nabla\rho = \rho\,\nabla\log\pi, the weight a token gets in the master formula under the PPO clip is

wt(i)={0if A^(i)>0 and ρt(i)>1+ϵhigh,0if A^(i)<0 and ρt(i)<1−ϵlow,ρt(i)otherwise.w^{(i)}_t = \begin{cases} 0 & \text{if } \hat A^{(i)} > 0 \text{ and } \rho^{(i)}_t > 1+\epsilon_\text{high},\\ 0 & \text{if } \hat A^{(i)} < 0 \text{ and } \rho^{(i)}_t < 1-\epsilon_\text{low},\\ \rho^{(i)}_t & \text{otherwise.} \end{cases}

Here ϵlow\epsilon_\text{low} and ϵhigh\epsilon_\text{high} are the lower and upper edges of the clip band; standard PPO and GRPO use one value for both, ϵlow=ϵhigh=ϵ=0.2\epsilon_\text{low} = \epsilon_\text{high} = \epsilon = 0.2. A token that has already moved far enough in the direction its advantage wants stops contributing a gradient (the “four cases” of Chapter 9). The methods in this section disagree about three things: where the band should be, whether a clipped token should lose its gradient entirely, and whether the ratio should be computed per token at all.

Clip-higher: give rare tokens room to grow (DAPO)

The symmetric clip treats a ratio of 1.2 the same for every token. In probability, that is very unequal. A token with πold=0.6\pi_\text{old} = 0.6 may rise to 0.72 before its gradient switches off: a gain of 0.12. A rare token with πold=0.01\pi_\text{old} = 0.01, say the word “Wait” that starts a self-correction, may only rise to 0.012: a gain of 0.002. Rare tokens are exactly the ones whose probability the policy needs to raise to explore new behaviour, and the upper clip throttles them sixty times harder in absolute terms.

DAPO (Yu et al. 2025) argues that this throttle is one cause of entropy collapse, where the policy becomes nearly deterministic early and stops improving (Chapter 19). Its fix, clip-higher, decouples the two edges: ϵlow=0.2\epsilon_\text{low} = 0.2 but ϵhigh=0.28\epsilon_\text{high} = 0.28. In DAPO’s ablation on Qwen2.5-32B, adding clip-higher raised AIME 2024 accuracy from 36 to 38 points. (AIME 2024 is a set of 30 competition-math problems, a standard reasoning benchmark; avg@32 means accuracy averaged over 32 sampled answers per problem, which reduces the noise of a single sample.) The lower edge stays at 0.2 because pushing bad tokens’ probabilities toward zero too fast is the other way to collapse entropy.

CISPO: clip the weight, keep the gradient (MiniMax-M1)

MiniMax’s M1 report observed that low-probability “fork” tokens such as “However”, “Recheck” and “Wait” get large ratios after the first on-policy update and are then clipped out, losing their gradient for all the remaining off-policy updates on that batch (they used 16). Their fix, CISPO (Clipped IS-weight Policy Optimization), does not clip the objective. It clips the importance weight and stops its gradient:

JCISPO=E[1∑i∣y(i)∣∑i∑tsg⁡ ⁣(ρ^t(i)) A^(i)log⁡πθ(yt(i)∣⋅)],ρ^t(i)=clip(ρt(i), 1−ϵlow, 1+ϵhigh),J_\text{CISPO} = \mathbb E\Bigg[\frac{1}{\sum_i\lvert y^{(i)}\rvert}\sum_i\sum_t \operatorname{sg}\!\big(\hat\rho^{(i)}_t\big)\,\hat A^{(i)}\log\pi_\theta\big(y^{(i)}_t\mid\cdot\big)\Bigg], \qquad \hat\rho^{(i)}_t = \mathrm{clip}\big(\rho^{(i)}_t,\ 1-\epsilon_\text{low},\ 1+\epsilon_\text{high}\big),

where sg⁡\operatorname{sg} means stop-gradient: the weight is treated as a constant. In practice only the upper bound is used. The gradient weight is then w=min⁡(ρ,1+ϵhigh)w = \min(\rho, 1+\epsilon_\text{high}), which is never zero: every token keeps a gradient, but no token’s weight can exceed 1+ϵhigh1 + \epsilon_\text{high}. This is truncated importance sampling, an old idea in off-policy RL, applied to REINFORCE. MiniMax reports that in a controlled comparison on Qwen2.5-32B, CISPO reached DAPO’s performance with half the training steps.

Compare the two rules on four tokens (with ϵ=0.2\epsilon = 0.2 for PPO, and only an upper bound of 1.28 for CISPO):

Token ratio ρ\rhoAdvantagePPO weight wwCISPO weight ww
1.5+10 (clipped)1.28 (capped)
1.5−11.51.28
0.5+10.50.5
0.5−10 (clipped)0.5

PPO’s zeros are where it believes the token has “done enough”. CISPO never believes that; it only refuses to let one token’s weight explode. Note the second row: CISPO caps a weight that PPO would leave unclipped. The two rules are different trade-offs, not one a superset of the other.

Quick check A token has ratio ρ = 0.6 and a negative advantage. Which rule still gives it a gradient: PPO-clip with ε = 0.2, or CISPO with only an upper bound?

Sequence-level ratios (GSPO)

The reward belongs to a whole response, so the “correct” importance weight for a response is the ratio of sequence probabilities, πθ(y∣x)/πold(y∣x)=∏tρt\pi_\theta(y\mid x)/\pi_\text{old}(y\mid x) = \prod_t \rho_t. Its trouble is variance: a product of hundreds of ratios near 1 can still land anywhere from 0.01 to 100. GRPO’s per-token ratios dodge that, but each token is then weighted by its own noisy ratio. The GSPO paper argues that this is not a proper importance correction for a sequence-level reward at all, only noise injected token by token.

GSPO (Group Sequence Policy Optimization; Zheng et al. 2025, the Qwen team) takes the principle “the unit of optimization should match the unit of reward” and uses one ratio per response, the geometric mean of its token ratios:

s(i)(θ)=(πθ(y(i)∣x)πold(y(i)∣x))1/∣y(i)∣=exp⁡(1∣y(i)∣∑tlog⁡ρt(i)),JGSPO=E[1G∑imin⁡(s(i)A^(i), clip(s(i),1−ϵ,1+ϵ) A^(i))].s^{(i)}(\theta) = \Big(\frac{\pi_\theta(y^{(i)}\mid x)}{\pi_\text{old}(y^{(i)}\mid x)}\Big)^{1/\lvert y^{(i)}\rvert} = \exp\Big(\frac{1}{\lvert y^{(i)}\rvert}\sum_t \log\rho^{(i)}_t\Big), \qquad J_\text{GSPO} = \mathbb E\Big[\frac1G\sum_i \min\big(s^{(i)}\hat A^{(i)},\ \mathrm{clip}(s^{(i)}, 1-\epsilon, 1+\epsilon)\,\hat A^{(i)}\big)\Big].

Every token of a response now shares one weight, and the whole response is clipped or not, together. A worked example: three tokens with ratios 1.3, 0.8, 1.0 and A^>0\hat A > 0. GRPO’s token clip (ε = 0.2) zeroes the first token’s gradient and keeps the other two with weights 0.8 and 1.0. GSPO computes s=(1.3×0.8×1.0)1/3=1.041/3=1.013s = (1.3\times0.8\times1.0)^{1/3} = 1.04^{1/3} = 1.013 and gives all three tokens the same weight, unless ss is outside its band.

That band is tiny. Because log⁡s\log s is an average per-token log-ratio, it barely moves even when individual tokens move a lot. The GSPO paper uses a clip range of 3×10−43\times10^{-4} on the left and 4×10−44\times10^{-4} on the right, against 0.2 and 0.27 for its GRPO baseline. Even so, it reports that GSPO clips about two orders of magnitude more tokens than GRPO, yet trains more efficiently, which the authors read as evidence that GRPO’s token-level gradients are noisy.

The motivation that made GSPO matter in practice is mixture-of-experts (MoE) models. In an MoE, a small change in the weights can route a token to different experts, so a single token’s ratio can jump wildly between πold\pi_\text{old} and πθ\pi_\theta. The GSPO paper reports that GRPO on its MoE models needed Routing Replay (reuse the old policy’s expert choices when computing ratios) to converge, and GSPO did not. The Qwen team credits GSPO for improvements in Qwen3.

Knob 5: the KL leash

Chapter 11 introduced the KL-regularized objective and three per-token estimators of KL(πθ ∥ πref)\mathrm{KL}(\pi_\theta\,\Vert\,\pi_\text{ref}): k1=log⁡πθπrefk_1 = \log\frac{\pi_\theta}{\pi_\text{ref}}, k2=12(log⁡πθπref)2k_2 = \frac12\big(\log\frac{\pi_\theta}{\pi_\text{ref}}\big)^2, and k3=πrefπθ−log⁡πrefπθ−1k_3 = \frac{\pi_\text{ref}}{\pi_\theta} - \log\frac{\pi_\text{ref}}{\pi_\theta} - 1. The zoo uses three placements:

  • In the reward. Subtract β∑tk1\beta\sum_t k_1 from each response’s reward before computing advantages. This is classic RLHF (Chapter 12) and RLOO.
  • In the loss. Add β k3\beta\,k_3 per token to the loss, outside the advantage. This is GRPO, with β=0.04\beta = 0.04 in DeepSeekMath. REINFORCE++ puts k2k_2 in the loss instead.
  • Dropped. DAPO, Dr. GRPO, CISPO, Magistral, Olmo 3 and ScaleRL train RLVR with no KL term at all. DAPO’s argument: for long chain-of-thought reasoning “the model distribution can diverge significantly from the initial model, thus this restriction is not necessary.”

Why would anyone drop the leash? Its main job in RLHF is to keep the policy where a learned reward model is still trustworthy (Chapter 19). A verifier that checks a final answer cannot be fooled by moving off-distribution in the same way, and reasoning models are supposed to move far from the base model. Dropping the KL also saves a forward pass through the reference model. On the other side, DeepSeek-V3.2 keeps an importance-corrected k3k_3 and says the right strength is domain-dependent (weak or none for math), and NVIDIA’s ProRL keeps the KL and periodically resets the reference model to sustain long training runs.

There is also a subtlety about which placement gives the gradient you think it does. “A Comedy of Estimators” (Shah et al. 2025) analyzed the on-policy gradients: k1k_1 in the reward gives an unbiased gradient of the reverse KL; GRPO’s k3k_3 in the loss gives a biased gradient that nevertheless trains stably; k3k_3 in the reward led to collapse in their runs. The lab implements all three placements so you can check the formulas; Chapter 11 has the estimator math.

Knob 6: which samples enter the batch

Zero-signal groups

If all GG responses to a prompt get the same reward, every baseline that uses the group (mean or leave-one-out) gives every advantage exactly 0. The group costs a full round of generation and contributes no gradient. How often does that happen? With pass rate pp, all right or all wrong has probability pG+(1−p)Gp^G + (1-p)^G:

ppG=4G = 4G=8G = 8G=16G = 16
0.512.5%0.8%0.003%
0.965.6%43.0%18.5%
0.9581.5%66.3%44.0%

As training succeeds, more prompts become easy, and a growing share of the batch is dead weight. Worse, the dead groups still count in the normalizer (NN for sequence-mean, ∑i∣y(i)∣\sum_i\lvert y^{(i)}\rvert for token-mean), so they also shrink everyone else’s step.

Quick check With pass rate p = 0.5 and G = 4, what fraction of groups give zero signal?

Dynamic sampling (DAPO) over-samples prompts and filters out groups whose rewards are all equal, refilling until the batch is full of informative groups. In DAPO’s ablation it was the largest single step, from 42 to 50 on AIME 2024 avg@32. Olmo 3 uses a more efficient variant it calls active sampling, and ScaleRL adds “No-Positive-Resampling”: prompts with a pass rate of at least 0.9 are retired from the dataset for good.

Overlong responses

Responses that hit the generation limit are cut off and usually scored as wrong, even if the reasoning was on track. That injects noise: the model is punished for a good but long attempt. DAPO’s overlong filtering masks truncated responses out of the loss. Its soft overlong punishment adds a length penalty that ramps up in a buffer zone of LcacheL_\text{cache} tokens before the hard limit:

Rlength(y)={0,∣y∣≤Lmax−Lcache,(Lmax−Lcache)−∣y∣Lcache,Lmax−Lcache<∣y∣≤Lmax,−1,∣y∣>Lmax.R_\text{length}(y) = \begin{cases} 0, & \lvert y\rvert \le L_\text{max} - L_\text{cache},\\[2pt] \dfrac{(L_\text{max} - L_\text{cache}) - \lvert y\rvert}{L_\text{cache}}, & L_\text{max} - L_\text{cache} < \lvert y\rvert \le L_\text{max},\\[6pt] -1, & \lvert y\rvert > L_\text{max}. \end{cases}

With DAPO’s settings, Lmax=20,480L_\text{max} = 20{,}480 and Lcache=4,096L_\text{cache} = 4{,}096, responses up to 16,384 tokens are not penalized at all; an 18,000-token response sits 1,616 tokens into the buffer and gets −1616/4096≈−0.39-1616/4096 \approx -0.39. In the ablation, overlong filtering and the soft punishment were worth +6 and +3 points. Note this is a reward-design knob more than an estimator knob; Chapter 19 discusses length penalties as a defence against verbosity.

Build an algorithm

Everything so far fits in one picture. The demo shows the running example’s eight responses as rows of tokens. Each cell is one token; its color is the gradient weight c(i)wt(i)A^(i)c^{(i)}w^{(i)}_t\hat A^{(i)} that multiplies ∇log⁡π\nabla\log\pi for that token (blue: pushed up; orange: pushed down; hatched: clipped to exactly zero). The bar at the right of each row is the response’s total push. The numbers are relative: the largest per-token weight in the batch reads 1.00, and so does the largest total. Under the bars, three readouts compare the short and long wrong answers, the signal each prompt gets, and how many tokens are clipped. Tap a response’s label (for example “y₂ ✗”) to flip its reward.

Interactive The algorithm builder: one batch, every knob
Presets

pushed up (Â > 0)pushed down (Â < 0)clipped: gradient exactly 0no signal (Â = 0) or droppedTap a response's label to flip its reward.

What to try: start on GRPO and read the Length line: each token of the short wrong answer is pushed 5× harder than each token of the long one. Click Dr. GRPO: per-token weights equalize and the long answer now gets 5× the total push. Switch back to GRPO and tap prompt 1's y₁ and y₄ to make them wrong: the whole group goes grey (zero signal); tick dynamic sampling to drop it. Reset, then raise the staleness slider to 0.1 and compare GRPO, DAPO (clip-higher), CISPO and GSPO: count the hatched cells. At 0.001 GSPO already clips whole rows while GRPO clips nothing.

A few things to notice as you play:

  • On-policy, half the knobs are invisible. With staleness 0 every ratio is 1, so GRPO and GSPO give identical weights, and every clip setting does nothing. Ratio and clip knobs only matter for the second and later updates on a batch, or under asynchronous or mismatched sampling.
  • Baselines change who learns from failure. On REINFORCE, wrong answers have no color at all. On every group baseline they are pushed down, and prompt 2’s single success is the brightest row in the batch.
  • The std changes the balance between prompts. Compare the “signal per prompt” line for GRPO and Dr. GRPO: the std inflates the 1-of-4 prompt relative to the 2-of-4 prompt. Flip prompt 2’s y(4)y^{(4)} to make it a 0-of-4 group and its signal vanishes. Make it 3 of 4 instead and it gets exactly the same signal as 1 of 4: p(1−p)\sqrt{p(1-p)} is symmetric in pp and 1−p1-p.

The zoo, species by species

The comparison table

Each row follows the cited paper (dates are arXiv v1 months). “Seq-mean”, “token-mean” and “constant” are the three aggregations above. “Group” means computed within the GG samples of one prompt.

MethodYearBaselineScaleAggregationRatio and clipKLKey idea or claim
REINFORCE1992none (or a learned one)nonesequence sumnonenoneunbiased, high variance
ReMax (Li et al.)Oct 2023reward of a greedy responsenonesequence sumnoneoptionalno critic; the value model and its optimizer took 46% of GPU memory in their 7B PPO setup
RLOO (Ahmadian et al.)Feb 2024leave-one-outnonesequence sumnonek1k_1 in rewardREINFORCE-style RLOO beats PPO, DPO and RAFT for RLHF; PPO’s clip fired in under 5% of cases
GRPO (Shao et al.)Feb 2024group meangroup stdseq-meantoken, symmetric εk3k_3 in loss, β = 0.04drops PPO’s critic; G=64G = 64 in DeepSeekMath
REINFORCE++ (Hu et al.)Jan 2025group mean (or none)batch stdtoken-meantoken, symmetric εk1k_1 in reward or k2k_2 in lossper-prompt std is biased and unstable; normalize globally
Dr. GRPO (Liu et al.)Mar 2025group meannoneconstanttoken, symmetric εnoneremoves the length and difficulty biases
DAPO (Yu et al.)Mar 2025group meangroup stdtoken-meantoken, 0.2 / 0.28none+ dynamic sampling, overlong shaping: AIME 2024 from 30 (naive GRPO) to 50 on Qwen2.5-32B
CISPO (MiniMax)Jun 2025groupgroup stdtoken-meanclipped, stop-grad weightnoneevery token keeps its gradient; 2× faster than DAPO in their study
GSPO (Zheng et al.)Jul 2025group meangroup stdone term per responsesequence ratio, 3e-4 / 4e-4nonestable MoE training without Routing Replay; used for Qwen3
ScaleRL (Khatri et al.)Oct 2025groupbatch stdprompt-level meanCISPOnonefits sigmoid compute-scaling curves; most knobs change efficiency, not the ceiling
SAPO (Gao et al.)Nov 2025groupgroup stdseq-meansoft sigmoid gatenot part of the methodsmooth, sign-dependent down-weighting instead of a hard cut
OlmoRL (Olmo 3)Dec 2025group meannonetoken-meantoken, clip-higher, × truncated ISnonean open “consensus” recipe: GRPO + the 2025 fixes
MaxRL (Tajwar et al.)Feb 2026n/a÷ number of successesn/an/an/aaims at log⁡p\log p instead of pp: up-weights hard prompts

Species notes

RLOO is the cleanest member: leave-one-out baseline, no std, no clip, the KL folded into the reward. Ahmadian et al. argue that the RLHF setting doesn’t need PPO’s machinery: with a good starting policy and a sequence-level reward, the clip rarely fires, and treating the whole response as one action (a bandit) works as well as modeling partial completions with a critic, at a fraction of the cost.

GRPO is the reference point everyone modifies. Its group mean and std came from DeepSeekMath, which used a learned reward model. Its popularity came with DeepSeek-R1 and verifiable rewards (Chapter 15).

Dr. GRPO is a correction, not an invention: drop the two divisions (∣y∣\lvert y\rvert and the std) that make GRPO optimize something other than expected reward. Its paper also contains useful skepticism about R1-Zero-style training, including evidence that some base models already show “aha”-style self-reflection before any RL.

DAPO is a bundle of four engineering fixes found while reproducing R1-Zero at 32B scale: clip-higher, dynamic sampling, token-level loss, and overlong shaping. Its ablation table is one of the best places to see how much each knob is worth in one setting:

DAPO ablation (Qwen2.5-32B base)AIME 2024 avg@32
naive GRPO30
+ overlong filtering36
+ clip-higher38
+ soft overlong punishment41
+ token-level loss42
+ dynamic sampling (= DAPO)50

Remember these are cumulative and measured once, in one setting; the order of addition affects how much each step appears to contribute.

CISPO and GSPO both attack the off-policy knob, in opposite directions. CISPO keeps per-token ratios but never discards a token’s gradient. GSPO discards per-token ratios entirely in favour of one per sequence, and clips whole responses. Both appear in ScaleRL’s comparison as having a higher performance ceiling than DAPO’s loss.

REINFORCE++ and ScaleRL prefer a batch-level std: one number for the whole batch, so no prompt is up-weighted for having low variance.

Critics strike back

“Critic-free” is a trend, not a law. VAPO (Yue et al. 2025, ByteDance Seed) is PPO with a value model, made to work for long chain-of-thought: the value network is pretrained, GAE uses different λ for critic and policy, and λ adapts to response length. It reports 60.4 on AIME 2024 from Qwen2.5-32B base, more than 10 points above DAPO in the same setting. VinePPO (Kazemnejad et al. 2024) replaces the learned critic with Monte Carlo value estimates: because a language environment can be reset to any prefix, you can sample a few continuations from the middle of a response and average their rewards, giving unbiased per-step values (Chapter 17 uses the same trick for process rewards). It reports beating PPO and RLOO-style baselines with up to 3× less wall-clock time to peak. The honest summary: group baselines give up per-token credit assignment in exchange for simplicity and memory. When credit assignment within a long response matters, critics (learned or Monte Carlo) can win back the difference, at a cost.

Does the choice matter?

Less than the number of papers suggests, and more than zero. Yue et al. (2025), the pass@kk paper (not the VAPO authors), compared six popular RLVR algorithms and found they performed similarly, all far from the base model’s large-kk pass@kk (Chapter 15 discusses that debate). ScaleRL found that most knobs change how quickly a run reaches its ceiling rather than the ceiling itself, with a few exceptions (the loss type, FP32 precision for the LM head, batch size, generation length). The lab’s TinyLM experiment shows the same flavour at toy scale: REINFORCE, RLOO, GRPO and Dr. GRPO all improve the addition task by similar amounts within noise. Knob choices matter most when they interact with a specific failure: lengths that the model can inflate, entropy that collapses, MoE ratios that explode, prompts that become too easy.

Beyond the knobs: changing the objective

One 2026 line of work changes not the estimator but the target. MaxRL (Tajwar et al. 2026) observes that with binary rewards the log-probability of success expands as

log⁡p=−∑k≥1(1−p)kk,∇log⁡p=∑k≥11k ∇ pass@k,\log p = -\sum_{k\ge1}\frac{(1-p)^k}{k}, \qquad \nabla\log p = \sum_{k\ge1}\frac1k\,\nabla\,\mathrm{pass@}k,

so ordinary RL (∇p=∇ pass@1\nabla p = \nabla\,\mathrm{pass@}1) keeps only the first term of the maximum-likelihood gradient. Its estimator is a one-line change to REINFORCE: average the score function over the successful samples only, dividing by the number of successes instead of by GG. That turns the per-prompt weight 11 (mean-centered) into roughly 1/p1/p, the third of the three weightings compared in Knob 2. The authors report it Pareto-dominates the baselines they tested; it is recent, so treat it as a promising direction rather than settled practice.

Reading the next acronym

When a new critic-free method appears, ask six questions, in order:

1. Baseline:     what is subtracted from r? (none / group mean / leave-one-out / batch / greedy / learned)
2. Scale:        divided by a std? whose? (group / batch / none)
3. Aggregation:  how are token terms averaged? (1/|y| per response, 1/total tokens, constant)
4. Ratio & clip: per token or per sequence? clipped, capped, masked, or soft-gated? which band?
5. KL:           in the reward, in the loss (which estimator?), or absent?
6. Data:         which groups are dropped or refilled? how are truncated responses treated?

Then ask the question that separates a real improvement from a knob shuffle: which failure does the change target, and does the paper show that failure in its baseline? A method that fixes length bias should show lengths growing without the fix. A method that fixes entropy collapse should show entropy collapsing. If the paper only shows a better final number, on one model family, with one seed, read it skeptically (Chapter 21 has a checklist).

Here is the full step every method in the table implements, with the knobs marked:

for each training step:
  1. sample a batch of prompts; for each prompt sample G responses from π_old (the current policy)
  2. score them: r_i = verifier(x, y_i)
     (knob 6) drop groups with all-equal rewards and refill; mask or penalize truncated responses
  3. (knob 5a) if KL in reward: r_i ← r_i − β Σ_t log(π_old/π_ref)
  4. (knobs 1-2) A_i = (r_i − baseline_i) / scale_i
  5. record log π_old(y_t) for every token (and log π_ref(y_t) if KL in loss)
  6. for each of K minibatch updates:
       ratio: ρ_t = π_θ/π_old per token, or s_i per response           (knob 4)
       term_t = clip rule(ρ, A_i)                                      (knob 4)
       term_t ← term_t − β · KL estimate_t     if KL in loss           (knob 5b)
       loss = − aggregate(term)                                        (knob 3)
       gradient step on loss

Summary

  • The critic-free family is one estimator: each token’s log-probability gradient is weighted by an advantage, an off-policy weight, and an aggregation constant. Algorithms differ in six knobs: baseline, scale, aggregation, ratio and clip, KL, and data.
  • Baselines: group mean (GRPO) is exactly (G−1)/G(G-1)/G times leave-one-out (RLOO), a harmless rescaling; leave-one-out is unbiased; no baseline means wrong answers are never pushed down.
  • Dividing by the group std reweights prompts by 1/p(1−p)1/\sqrt{p(1-p)}, up-weighting nearly-always-right and nearly-always-wrong prompts (Dr. GRPO’s difficulty bias), and amplifies noise when rewards barely differ. Batch std, no std, and MaxRL’s 1/p1/p are alternatives, each the gradient of a different objective.
  • Sequence-mean aggregation optimizes (r−b)/∣y∣(r-b)/\lvert y\rvert: it pays the model to be short when right and long when wrong. Token-mean and constant normalizers give every token equal weight and remove the bias.
  • Clipping matters only off-policy. Clip-higher (DAPO) lets rare tokens grow; CISPO caps the importance weight but never zeroes a gradient; GSPO uses one geometric-mean ratio per response with a tiny band, which stabilizes MoE training.
  • KL can go in the reward, in the loss, or nowhere; most 2025 RLVR recipes dropped it, and the estimator and placement change which gradient you actually get.
  • Data knobs (dynamic sampling, overlong filtering) were among the largest single gains in DAPO’s ablation, because zero-variance groups and truncated responses waste compute or inject noise.
  • Measured head to head, many variants perform similarly; the knobs pay off when they target a failure you can actually see in your run.

Exercises

Exercise 16.1Advantages by handPen & paper●○○

A prompt gets G=6G = 6 responses with rewards (1,1,0,0,0,0)(1, 1, 0, 0, 0, 0). Compute the advantage of a right and of a wrong response under: (a) the group-mean baseline, (b) leave-one-out, (c) GRPO (group mean, divided by the population std), (d) ReMax, if the greedy response happens to be right. Check that (a) is (G−1)/G(G-1)/G times (b).

Solution

The group mean is 2/6=1/32/6 = 1/3.

(a) Right: 1−1/3=2/3≈0.6671 - 1/3 = 2/3 \approx 0.667. Wrong: 0−1/3=−1/3≈−0.3330 - 1/3 = -1/3 \approx -0.333.

(b) For a right response, the other five rewards contain one 1: baseline 1/5=0.21/5 = 0.2, advantage 0.80.8. For a wrong response, the other five contain two 1s: baseline 2/5=0.42/5 = 0.4, advantage −0.4-0.4.

Check: G−1G×0.8=56×0.8=0.667\frac{G-1}{G}\times 0.8 = \frac56\times0.8 = 0.667 and 56×(−0.4)=−0.333\frac56\times(-0.4) = -0.333. They match.

(c) Population std: p(1−p)\sqrt{p(1-p)} with p=1/3p = 1/3, so 2/9=0.471\sqrt{2/9} = 0.471. Right: 0.667/0.471=1.4140.667/0.471 = 1.414. Wrong: −0.333/0.471=−0.707-0.333/0.471 = -0.707. (Exactly 2\sqrt2 and −1/2-1/\sqrt2.)

(d) The greedy response scored 1, so b=1b = 1: right answers get 00 and wrong ones get −1-1. ReMax learns only from failures here. If the greedy answer had been wrong, b=0b = 0 and it would learn only from successes. A single extra sample is a noisier baseline than a group mean.

Exercise 16.2Leave-one-out is unbiased; the group mean is biased by (G−1)/GPen & paper●●○

Responses y(1),…,y(G)y^{(1)}, \dots, y^{(G)} are sampled independently from πθ(⋅∣x)\pi_\theta(\cdot\mid x). Write gi=∇log⁡πθ(y(i)∣x)g_i = \nabla\log\pi_\theta(y^{(i)}\mid x) and ri=r(x,y(i))r_i = r(x, y^{(i)}).

(a) Show that E[(ri−biLOO) gi]=∇J\mathbb E\big[(r_i - b^\text{LOO}_i)\,g_i\big] = \nabla J, where biLOO=1G−1∑j≠irjb^\text{LOO}_i = \frac{1}{G-1}\sum_{j\ne i} r_j and ∇J=E[r g]\nabla J = \mathbb E[r\,g].

(b) Show that with rˉ=1G∑jrj\bar r = \frac1G\sum_j r_j, E[(ri−rˉ) gi]=G−1G∇J\mathbb E\big[(r_i - \bar r)\,g_i\big] = \frac{G-1}{G}\nabla J.

(c) Why is the bias in (b) harmless in practice, and when would it stop being harmless?

(d) In one batch, prompt A keeps only 2 of its responses (the others were masked out as truncated) and prompt B keeps all 8. Relative to the true gradient, by what factor is each prompt’s expected contribution scaled under the group-mean baseline? Under leave-one-out?

If you did Exercise 15.3, parts (a) and (b) are a quick recap; (c) and (d) are the new part.

Hint
Use independence: for j≠ij \neq i, rjr_j is independent of gig_i, and E[gi]=0\mathbb E[g_i] = 0 (the score function has mean zero).
Solution

(a) Split the expectation: E[(ri−bi)gi]=E[rigi]−1G−1∑j≠iE[rjgi]\mathbb E[(r_i - b_i)g_i] = \mathbb E[r_i g_i] - \frac{1}{G-1}\sum_{j\ne i}\mathbb E[r_j g_i]. For j≠ij\ne i, rjr_j depends only on y(j)y^{(j)} and gig_i only on y(i)y^{(i)}, which are independent, so E[rjgi]=E[rj] E[gi]=E[rj]⋅0=0\mathbb E[r_j g_i] = \mathbb E[r_j]\,\mathbb E[g_i] = \mathbb E[r_j]\cdot 0 = 0. Hence E[(ri−bi)gi]=E[rigi]=∇J\mathbb E[(r_i - b_i)g_i] = \mathbb E[r_i g_i] = \nabla J. Unbiased.

(b) rˉ=1Gri+1G∑j≠irj\bar r = \frac1G r_i + \frac1G\sum_{j\ne i} r_j. So E[(ri−rˉ)gi]=(1−1G)E[rigi]−1G∑j≠iE[rj]E[gi]=G−1G∇J\mathbb E[(r_i - \bar r)g_i] = \big(1 - \frac1G\big)\mathbb E[r_i g_i] - \frac1G\sum_{j\ne i}\mathbb E[r_j]\mathbb E[g_i] = \frac{G-1}{G}\nabla J. The term that survives is the response competing with its own share of the mean.

(Equivalently, use the identity from the chapter: ri−rˉ=G−1G(ri−biLOO)r_i - \bar r = \frac{G-1}{G}(r_i - b^\text{LOO}_i) for every sample, then take expectations and use (a).)

(c) The factor (G−1)/G(G-1)/G is the same for every prompt as long as every prompt uses the same GG, so it only rescales the gradient, which the learning rate (and Adam’s normalization) absorbs. It stops being harmless if group sizes vary across prompts in a batch (for example after filtering or with variable sampling budgets): then prompts with small groups are down-weighted relative to large ones, a real reweighting. With G=2G = 2 the factor is 1/21/2, with G=64G = 64 it is 0.980.98.

(d) Under the group mean, prompt A’s contribution is scaled by (2−1)/2=0.5(2-1)/2 = 0.5 and prompt B’s by (8−1)/8=0.875(8-1)/8 = 0.875. So A is down-weighted relative to B by 0.5/0.875=4/7≈0.570.5/0.875 = 4/7 \approx 0.57, a real reweighting that the learning rate cannot absorb. Under leave-one-out both factors are exactly 1: every prompt is unbiased whatever its group size. This is one practical reason to prefer leave-one-out when group sizes vary.

Exercise 16.3The length bias of sequence-mean aggregationPen & paper●●○

(a) Show that under sequence-mean aggregation, response ii‘s contribution to the gradient equals the REINFORCE estimator for the modified reward r~(y)=(r(y)−b)/∣y∣\tilde r(y) = (r(y) - b)/\lvert y\rvert, with bb held fixed.

(b) Let b=0.25b = 0.25 (a hard prompt). Compute r~\tilde r for a right answer of 50 tokens and of 500 tokens, and for a wrong answer of 50 and of 500 tokens. Which lengths does the modified reward prefer for right and for wrong answers?

(c) Suppose the model’s correctness on a prompt is decided by its first few tokens, and after that it chooses how many more tokens to write. Explain qualitatively what sequence-mean training will do to the length of right and wrong responses, and what token-sum (constant normalizer) training will do.

Hint
For (a), pull the factor A^(i)/∣y(i)∣\hat A^{(i)}/\lvert y^{(i)}\rvert out of the token sum and recognize ∑t∇log⁡π(yt∣⋅)=∇log⁡π(y∣x)\sum_t\nabla\log\pi(y_t\mid\cdot) = \nabla\log\pi(y\mid x). For (c), the true reward does not depend on length at all.
Solution

(a) Sequence-mean gives response ii the term 1N1∣y(i)∣∑tA^(i)∇log⁡π(yt(i)∣⋅)=1Nr(i)−b∣y(i)∣∇log⁡π(y(i)∣x)\frac{1}{N}\frac{1}{\lvert y^{(i)}\rvert}\sum_t \hat A^{(i)}\nabla\log\pi(y^{(i)}_t\mid\cdot) = \frac{1}{N}\frac{r^{(i)} - b}{\lvert y^{(i)}\rvert}\nabla\log\pi(y^{(i)}\mid x), using A^(i)=r(i)−b\hat A^{(i)} = r^{(i)} - b and the sum of token log-probs. This is 1Nr~(y(i))∇log⁡π(y(i)∣x)\frac1N\tilde r(y^{(i)})\nabla\log\pi(y^{(i)}\mid x): the REINFORCE estimator, with no baseline, for reward r~\tilde r. Its expectation is ∇θ E[r~(y)]\nabla_\theta\,\mathbb E[\tilde r(y)] (with bb fixed). (If you use the std as well, r~\tilde r is divided by a constant per prompt; the conclusion is the same.)

(b) Right answers: (1−0.25)/50=0.015(1 - 0.25)/50 = 0.015 and 0.75/500=0.00150.75/500 = 0.0015. Wrong answers: −0.25/50=−0.005-0.25/50 = -0.005 and −0.25/500=−0.0005-0.25/500 = -0.0005. The modified reward prefers short right answers (0.015 > 0.0015) and long wrong answers (−0.0005 > −0.005).

(c) Under sequence-mean, the policy is effectively rewarded by r~\tilde r. After its first tokens have committed it to a wrong answer, writing more tokens raises r~\tilde r toward 0, so the policy learns to ramble when it is wrong; after a right start, it learns to stop early. Wrong responses grow, right ones shrink, and overall length can rise as a symptom of failure rather than of reasoning. Under the constant normalizer, the expected gradient is the true ∇ E[r]\nabla\,\mathbb E[r], and the reward is independent of length, so the length decisions get no systematic gradient (only noise). This is exactly what the lab’s LengthToy shows: under the GRPO preset wrong responses roughly double in length (about 4 to about 8 tokens), while Dr. GRPO and DAPO stay flat.

Exercise 16.4The std as a prompt weightPen & paper●●●

A prompt has binary reward with pass rate p(θ)p(\theta). Use the true mean pp as the baseline and the true std σ=p(1−p)\sigma = \sqrt{p(1-p)} as the scale.

(a) Show that E[r−pσ∇log⁡π(y∣x)]=∇p/p(1−p)\mathbb E\big[\frac{r - p}{\sigma}\nabla\log\pi(y\mid x)\big] = \nabla p/\sqrt{p(1-p)}, and that this is ∇ϕ(p)\nabla\phi(p) for ϕ(p)=2arcsin⁡p\phi(p) = 2\arcsin\sqrt p.

(b) Fill in the per-prompt weights (the factor multiplying ∇p\nabla p) for mean-only, std-normalized, and MaxRL (weight 1/p1/p, the gradient of log⁡p\log p) at p=0.01,0.1,0.5,0.9,0.99p = 0.01, 0.1, 0.5, 0.9, 0.99.

(c) In one or two sentences each: which prompts does each scheme emphasize, and why might each be a reasonable choice?

Hint
For (a), E[p ∇log⁡π]=p E[∇log⁡π]=0\mathbb E[p\,\nabla\log\pi] = p\,\mathbb E[\nabla\log\pi] = 0, and E[r∇log⁡π]=∇p\mathbb E[r\nabla\log\pi] = \nabla p for r∈{0,1}r \in \lbrace 0, 1\rbrace. Then differentiate arcsin⁡p\arcsin\sqrt p with the chain rule.
Solution

(a) E[(r−p)∇log⁡π]=E[r∇log⁡π]−p E[∇log⁡π]=∇p−0\mathbb E[(r - p)\nabla\log\pi] = \mathbb E[r\nabla\log\pi] - p\,\mathbb E[\nabla\log\pi] = \nabla p - 0, by the log-derivative trick (E[r∇log⁡π]=∇E[r]=∇p\mathbb E[r\nabla\log\pi] = \nabla\mathbb E[r] = \nabla p). Dividing by the constant σ\sigma (treated as fixed, as the algorithm does) gives ∇p/p(1−p)\nabla p/\sqrt{p(1-p)}. Now ddp2arcsin⁡p=2⋅11−p⋅12p=1p(1−p)\frac{d}{dp}2\arcsin\sqrt p = 2\cdot\frac{1}{\sqrt{1-p}}\cdot\frac{1}{2\sqrt p} = \frac{1}{\sqrt{p(1-p)}}, so by the chain rule ∇ϕ(p)=∇p/p(1−p)\nabla\phi(p) = \nabla p/\sqrt{p(1-p)}.

(b)

pp0.010.10.50.90.99
mean-only11111
std, 1/p(1−p)1/\sqrt{p(1-p)}10.053.332.003.3310.05
MaxRL, 1/p1/p1001021.111.01

(c) Mean-only treats a gain of 0.01 in pass rate the same on every prompt: it maximizes expected accuracy, the quantity usually reported. The std emphasizes both extremes symmetrically: it measures progress in Fisher-distance units, so polishing a nearly solved prompt (0.98 → 0.99) and cracking a nearly hopeless one (0.01 → 0.02) both count a lot. That stabilizes step sizes across prompts but spends effort on easy prompts that may not need it. MaxRL emphasizes only hard prompts: going from 0.01 to 0.02 doubles the likelihood of success and is worth as much as going from 0.5 to 1.0, which favours learning to solve new problems (and pass@kk at large kk) over perfecting ones already solved. Remember that in expectation ∇p\nabla p itself is small on very hard prompts (few successes to learn from), so the weights partly compensate for a weak raw signal.

Exercise 16.5Clip rules by hand: PPO, CISPO, GSPOPen & paper●●○

(a) Fill in the gradient weight ww (the factor multiplying A^ ∇log⁡π\hat A\,\nabla\log\pi, so a weight of 0 means no gradient) for PPO-clip with ϵ=0.2\epsilon = 0.2, DAPO’s clip-higher (0.20.2 / 0.280.28), and CISPO (upper bound 1.28 only), for these tokens: (i) ρ=1.25\rho = 1.25, A^>0\hat A > 0; (ii) ρ=1.4\rho = 1.4, A^>0\hat A > 0; (iii) ρ=0.7\rho = 0.7, A^<0\hat A < 0; (iv) ρ=0.7\rho = 0.7, A^>0\hat A > 0.

(b) A response has three tokens with ratios 1.3, 0.8, 1.0 and A^>0\hat A > 0. Compute GSPO’s sequence ratio. Is it inside GSPO’s band (1−3×10−41 - 3\times10^{-4}, 1+4×10−41 + 4\times10^{-4})? What weights do the three tokens get under GSPO, and under GRPO’s token clip with ϵ=0.2\epsilon = 0.2?

Hint
PPO’s weight is 0 exactly when the token has moved past the band in the direction its advantage pushes: above 1+ϵhigh1+\epsilon_\text{high} for A^>0\hat A > 0, below 1−ϵlow1 - \epsilon_\text{low} for A^<0\hat A < 0. Otherwise it is ρ\rho.
Solution

(a)

TokenPPO ε = 0.2clip-higher 0.2 / 0.28CISPO, cap 1.28
(i) ρ = 1.25, Â > 00 (above 1.2)1.25 (inside)1.25
(ii) ρ = 1.4, Â > 000 (above 1.28)1.28
(iii) ρ = 0.7, Â < 00 (below 0.8)0 (below 0.8)0.7
(iv) ρ = 0.7, Â > 00.70.70.7

Row (i) is clip-higher’s whole point: a good token that has grown 25% keeps learning. Row (ii) is CISPO’s: even past the cap, the token keeps a gradient, with its weight capped. Row (iv) is the “wrong way” case of Chapter 9: no rule clips a good token whose probability has fallen; all of them pull it back up.

(b) s=(1.3×0.8×1.0)1/3=1.041/3≈1.0132s = (1.3\times0.8\times1.0)^{1/3} = 1.04^{1/3} \approx 1.0132. That is far above 1+4×10−4=1.00041 + 4\times10^{-4} = 1.0004, and A^>0\hat A > 0, so GSPO clips the whole response: all three tokens get weight 0. Under GRPO’s token clip, token 1 (ρ=1.3>1.2\rho = 1.3 > 1.2) gets 0, token 2 gets 0.8, token 3 gets 1.0. The example shows why GSPO’s band must be so narrow: these token ratios are much larger than in a real LLM update (where per-token log-ratios are typically tiny), and their geometric mean still moves by only 1.3%.

Exercise 16.6The cost of dynamic samplingPen & paper●●○

(a) With pass rate pp and group size GG, show that the probability that a group carries signal (rewards not all equal) is q=1−pG−(1−p)Gq = 1 - p^G - (1-p)^G.

(b) DAPO-style dynamic sampling keeps sampling prompts until it has BB informative groups. If every prompt has the same pp, how many prompts does it need on average? Evaluate for B=512B = 512, G=16G = 16 and p=0.5p = 0.5, 0.90.9, 0.950.95.

(c) Real batches mix easy and hard prompts. Why does ScaleRL’s “No-Positive-Resampling” (retire prompts with pass rate at least 0.9) save more compute late in training than early?

Hint
Each prompt is informative independently with probability qq, so the number of prompts needed for BB successes has mean B/qB/q (a negative binomial).
Solution

(a) A group gives zero signal exactly when all GG rewards are 1 (probability pGp^G) or all are 0 (probability (1−p)G(1-p)^G). These are disjoint, so q=1−pG−(1−p)Gq = 1 - p^G - (1-p)^G.

(b) Each sampled prompt is informative with probability qq, independently, so collecting BB informative ones takes B/qB/q prompts on average.

  • p=0.5p = 0.5: q=1−2×0.516≈0.99997q = 1 - 2\times0.5^{16} \approx 0.99997, about 512 prompts.
  • p=0.9p = 0.9: 0.916=0.1850.9^{16} = 0.185, 0.116≈00.1^{16}\approx 0, q≈0.815q \approx 0.815, about 512/0.815≈628512/0.815 \approx 628 prompts: 23% extra generation.
  • p=0.95p = 0.95: 0.9516=0.4400.95^{16} = 0.440, q≈0.560q \approx 0.560, about 914914 prompts: 79% extra.

(c) Early in training few prompts are near-solved, so few groups are all-correct and filtering wastes little. Late in training many prompts have p≥0.9p \ge 0.9; each of them is sampled, generated with GG long responses, and then (often) thrown away. Retiring them from the dataset avoids paying for those generations at all, and the saving grows as more prompts become easy.

Exercise 16.7Choose the knobs for the failureConcept●●○

For each training symptom, name the knob(s) you would turn first and say why, citing the method that introduced the fix.

(a) Policy entropy drops to near zero in the first hundred steps, and accuracy plateaus early.

(b) Average response length keeps growing, but almost all the growth is in incorrect responses.

(c) Training a mixture-of-experts model, the per-token ratios occasionally spike to 50 and training diverges.

(d) Late in training, 60% of groups are all-correct and the gradient norm keeps shrinking.

(e) RLHF with a learned reward model: reward climbs steadily, but human raters say outputs got worse.

(f) Rewards are continuous scores from a judge, and groups often have nearly identical scores; training is noisy.

Solution

(a) Knob 4, clip-higher (DAPO): raise ϵhigh\epsilon_\text{high} so that low-probability tokens can grow, or switch to CISPO so clipped tokens keep their gradient. Entropy-specific methods (Chapter 19) are the next step.

(b) Knob 3, aggregation: replace sequence-mean with a constant normalizer (Dr. GRPO) or token-mean (DAPO). Sequence-mean penalizes long wrong answers less per token. Also check knob 6: if truncated responses get reward 0, overlong shaping prevents noise from cut-off answers.

(c) Knob 4, ratio level: use a sequence-level ratio (GSPO), or keep token ratios but add Routing Replay; on the systems side, align training and inference routing (Chapter 20). Also reduce the number of off-policy updates per batch.

(d) Knob 6, data: dynamic sampling (DAPO) or active sampling (Olmo 3) to refill with informative groups, and retiring easy prompts (ScaleRL’s No-Positive-Resampling); add harder prompts to the dataset. Knob 3 matters too: under token-mean, zero groups also dilute the denominator.

(e) Knob 5, KL: this is reward-model overoptimization. Keep (or strengthen) a KL leash to πref\pi_\text{ref}, preferably in the reward with k1k_1 for an unbiased gradient; the “drop the KL” recipes were designed for verifiable rewards, not learned ones (Chapter 19).

(f) Knob 2, scale: stop dividing by the group std (it turns score noise into full-size advantages); use mean-only advantages (Dr. GRPO) or a batch-level std (REINFORCE++), which keeps a sensible scale without amplifying near-ties.

Exercise 16.8Experiments with the algorithm builderExplore the demo●○○

Use the demo to answer these.

(a) On the GRPO preset (staleness 0), what is the ratio of per-token push between the short wrong y(2)y^{(2)} and the long wrong y(3)y^{(3)}? Which presets make it 1.00?

(b) On REINFORCE, which responses get any push? Explain from the advantage column.

(c) Flip prompt 2’s y(4)y^{(4)} to wrong. What happens to prompt 2 under GRPO, and what changes when you tick dynamic sampling?

(d) Set staleness to 0.1. How many tokens get zero gradient under GRPO, DAPO and CISPO? Now set staleness to 0.001 and compare GRPO with GSPO.

(e) Set GRPO’s knobs by hand, then change only the aggregation to “constant” and the scale to “none”. Which preset lights up?

Solution

(a) 5.00: sequence-mean gives each response the same total, spread over 4 vs 20 tokens. GSPO also shows 5.00 (it uses sequence-mean too). Dr. GRPO, DAPO, CISPO and RLOO show 1.00 per token (with the long answer getting 5× the total), since none divides by the response’s own length. REINFORCE shows no ratio at all: with no baseline both wrong answers have A^=0\hat A = 0 and get no push, which is part (b).

(b) Only the three right answers (P1 y(1)y^{(1)}, y(4)y^{(4)}, P2 y(4)y^{(4)}). With no baseline, A^=r\hat A = r, which is 0 for every wrong answer, so wrong answers are never pushed down.

(c) Prompt 2 becomes all wrong: zero variance, every advantage 0, the rows go grey, and the readout says the group is still generated but carries no signal. With dynamic sampling the group is dropped (and in a real system refilled with a fresh prompt). In a real loss the remaining tokens’ weights would also grow, because the dropped group no longer counts in the normalizer (NN for sequence-mean, the token count for token-mean). The demo’s colours are relative to the largest weight, so you see this only as the dropped rows fading out.

(d) With the demo’s fixed scenario at staleness 0.1: about 11 of 90 tokens are clipped under GRPO, about 9 under DAPO (clip-higher spares some positive-advantage tokens), and 0 under CISPO (it caps a few weights instead). At staleness 0.001, GRPO clips nothing (all token ratios are within 1 ± 0.2), but GSPO already clips most of the responses whole, because its band is ±0.0004 on the geometric-mean ratio. Real GSPO training uses much smaller updates than this toy, but the pattern (many more clipped tokens, in whole-response blocks) matches the paper’s report.

(e) Dr. GRPO. The two presets differ only in those two knobs (and in GRPO’s KL term, which the demo leaves out).

Exercise 16.9Lab, Tasks 1–5: one loss for the whole zooCode●●○

In zoo.py, implement in order:

  1. group_advantages(rewards, group_ids, baseline, scale): knobs 1 and 2.
  2. aggregate(per_token, mask, aggregation, max_len): knob 3.
  3. token_surrogate(logp, old_logp, adv, mask, cfg): knob 4, token or sequence ratio, with no clip, PPO clip, or CISPO weights.
  4. kl_per_token(logp, ref_logp, estimator): knob 5, the k1k_1, k2k_2, k3k_3 estimators.
  5. pg_loss(...): all of them together, with KL in the reward or in the loss.

Every algorithm in the chapter is then one line in common.PRESETS. The Task 5 tests compare each preset’s loss and gradient with the formula as its paper writes it (explicit loops), so a passing test means your knob-based loss is GRPO, Dr. GRPO, DAPO, GSPO, CISPO, RLOO and REINFORCE.

Hint
Work on the toy batch in the docstrings first (rewards 1,0,0,1, 0,0,0,1). For the sequence ratio, average the masked log-ratios over each response’s real tokens, exponentiate, and broadcast to every token. For CISPO, the clipped ratio must be .detach()ed and multiplied by logp, not by the ratio.
Solution

The reference is solutions/zoo.py. The key lines:

  • Advantages. Per-group sums and counts (with index_add_, or a loop over groups), then b = sums[g]/counts[g] for the group mean and b = (sums[g] - r)/(counts[g] - 1) for leave-one-out; divide by the group’s population std plus eps, or re-centre and divide by the batch std.
  • Aggregation. seq_mean: ((x*mask).sum(1) / mask.sum(1)).mean(); token_mean: (x*mask).sum() / mask.sum(); constant: (x*mask).sum() / (N*max_len).
  • Surrogate. rho = exp((logp - old_logp)*mask), or for sequence ratios s = exp(((logp - old_logp)*mask).sum(1)/len) broadcast with s[:, None].expand_as(logp). Then rho*A, torch.min(rho*A, clamp(rho, lo, hi)*A), or clamp(rho, lo, hi).detach()*A*logp, all times the mask.
  • KL. With d = ref_logp - logp: -d, 0.5*d**2, exp(d) - d - 1.
  • Loss. Shape the rewards if kl == "reward", compute advantages, the surrogate, subtract beta*KL*mask if kl == "loss", return minus the aggregate.

Two tests are worth reading even after they pass: test_task1_group_mean_is_rescaled_rloo (Exercise 16.2 in code) and test_task2_length_bias_in_gradients (each token’s weight under seq-mean is 1/(N∣y∣)1/(N\lvert y\rvert)).

Exercise 16.10Lab: run the zooCode●●○

Run python run.py (about 2 minutes). It trains four presets on the TinyLM addition task from the same SFT checkpoint, and three presets on the LengthToy, and saves zoo_comparison.png.

(a) On TinyLM, do the presets differ by more than the evaluation noise (about ±2 percentage points with 500 prompts)? Why does the mean response length barely move?

(b) On LengthToy, describe the wrong-answer and right-answer length curves for GRPO, DAPO and Dr. GRPO, and connect them to Exercise 16.3.

(c) Run python run.py --toy-presets grpo gspo and explain why the two curves coincide exactly.

(d) Optional: run python run.py --presets grpo dapo cispo gspo. With two updates per batch, does the clip knob make a visible difference on this task? What would you change to make it matter more?

Solution

(a) With the reference solution, all four presets go from about 0.51 sampled accuracy to roughly 0.63–0.70 in 120 steps, and the ranking changes between evaluations: the differences are mostly within noise, the same “algorithms perform similarly” picture as in larger studies. The length barely moves because in this task the answer’s length is fixed by the sum (two or three digits plus <eos>); the model has almost no freedom to be longer or shorter, so the length bias has nothing to act on.

(b) Under GRPO (sequence-mean), the average length of wrong answers climbs from about 4 to about 8 tokens (with noisy spikes past 10), and right answers shrink from about 3.5 to about 2.2 tokens: the modified reward (r−b)/∣y∣(r - b)/\lvert y\rvert of Exercise 16.3 at work, since the toy lets the policy choose its filler length after choosing its answer. Under DAPO (token-mean) and Dr. GRPO (constant), both curves stay near their starting values, drifting only by noise: the true reward does not depend on length, so the unbiased gradient leaves length alone. All three solve the task (reward near 1); only GRPO picks up the length habit along the way. The wrong-answer curve gets noisy late in training because few wrong answers remain.

(c) The toy trains with one update per batch, so πθ=πold\pi_\theta = \pi_\text{old} when the gradient is computed. Every sequence ratio is exactly 1, nothing is clipped, and GSPO’s gradient, sA^1∣y∣∑t∇log⁡πs\hat A\frac{1}{\lvert y\rvert}\sum_t\nabla\log\pi, equals GRPO’s on-policy gradient term for term (both have the group std and the 1/∣y∣1/\lvert y\rvert). With seeded sampling, the two runs are identical.

(d) In one reference run (seed 0), final sampled accuracies were about 0.66 (GRPO), 0.73 (DAPO), 0.62 (CISPO) and 0.66 (GSPO). Two cautions before reading a winner into that. First, it is one seed, and differences of a few points move around between evaluations. Second, DAPO differs from GRPO in more than its clip: its dynamic sampling trains only on informative groups, which is the most likely source of its edge here. The printed clip fractions tell you how much the clip knob itself did: for the token-level presets only a few percent of tokens leave the band on the second update, so the clip barely acts; for GSPO the fraction is close to 1, because almost every response’s geometric-mean ratio leaves the ±0.0004 band after one update, so GSPO effectively takes a single update per batch. To make the clip matter, take more updates per batch (updates_per_batch=8 in train), raise the learning rate, or simulate asynchrony by sampling with an older copy of the model. Then the PPO-style presets clip far more tokens, and you can test whether CISPO, which never zeroes a gradient, behaves differently.

Files: README.md, zoo.py, common.py, test_zoo.py, run.py

cd 16-policy-gradient-zoo
python -m pytest -q          # your code: tests fail until you fill in the TODOs
RL_SOLUTIONS=1 python -m pytest -q   # reference solutions: all pass

First time? See Exercises & setup.

Further reading

Next, Chapter 17 asks what to do when one reward at the end of a long response is not enough: rewarding individual steps, and spending more compute at test time instead of training time.