RL Fundamentals

By the end you will be able to read the method section of any LLM post-training paper, see the words REINFORCE, advantage, PPO, GRPO or KL penalty, and know exactly which number moves which weight, because you will have computed every one of them by hand on a two-armed bandit.

Suppose you want a model to solve a maths problem, and the only thing you can check is whether the final answer is right. There is no "correct next token" to imitate. There is a whole response, and a single number at the end that says good or bad. Next-token prediction has no way to use that number. Reinforcement learning is the set of tools for exactly this situation: learning from a score instead of a target.

This chapter is RL for people who already know how a language model works. We will not talk about robots or Atari. Every symbol will be mapped onto prompts, tokens and responses, and every formula will be checked with numbers small enough to verify on paper. The next chapter, RLHF Deep Dive, assembles these pieces into the full system.

The problem: a score is not a target

Supervised fine-tuning gives the model a target for every position. The loss at position $t$ is $-\log p(\text{correct token}_t)$, the gradient flows back, done. That works whenever someone has written down the response you want.

But many of the things we actually care about are properties of the whole response, and they only show up after the response is finished. Did the proof reach the right answer? Did the code pass the tests? Did a human prefer this reply to that one? Was it too long? None of these can be attached to a single token while the model is writing it.

Two things make this genuinely harder than supervised learning.

  • Credit assignment. The response had 300 tokens and got a 7 out of 10. Which tokens deserve the credit and which deserve the blame?
  • The data depends on the model. The model produced the text it is being scored on. Change the weights and you change the data. There is no fixed dataset to loop over.
Intuition

Supervised learning is a tutor who corrects every word as you write. RL is a teacher who reads the whole essay and writes "7/10" at the bottom. To improve, you have to guess what you did well, try variations, and see whether the number goes up. Everything in this chapter is a way of making that guessing efficient and stable.

The vocabulary: an MDP, mapped onto a language model

RL papers describe problems as Markov decision processes (MDPs). The vocabulary sounds abstract, but for language models every term has a very concrete meaning. Here is the dictionary. Keep it open; we will use every entry.

RL termSymbolFor a language model
State$s_t$The prompt plus every token generated so far.
Action$a_t$The next token. One discrete choice out of a vocabulary of, say, 100k.
Policy$\pi_\theta(a \mid s)$The language model itself: the softmax over the vocabulary given the context.
Transition$s_{t+1} = s_t \oplus a_t$Append the chosen token. Deterministic, no environment randomness.
Reward$r_t$Almost always $0$ while writing, then a score $R$ when the response ends.
Episode$\tau$One complete generation: prompt in, tokens out, until end-of-sequence or a length limit.
state $s_t$ prompt + tokens so far policy $\pi_\theta$ the language model action $a_t$ sample one token transition: append the token, $s_{t+1} = s_t \oplus a_t$ reward: $r_t = 0$ … until EOS, then $R$ one loop around the cycle = one token; one episode = one full response
Figure 1. The MDP loop for a language model. Look at where the randomness lives: only in the policy's sampling step. The "environment" does nothing but append the token and, at the very end, hand back a score.
Common confusion: the environment is boring on purpose

In games and robotics the environment is the hard part: it is stochastic and unknown. For LLMs the environment is "append a token", fully known and deterministic. All the uncertainty is inside the policy. This is why LLM RL papers barely mention transitions, and why methods that assume a fixed dataset (like DPO) can get away with skipping the RL machinery entirely.

Common confusion: token or whole response?

Two views of the same generation are both common. The token-level view treats each token as an action in a 300-step episode. The bandit view treats the whole response as one action in a 1-step episode. They are mathematically consistent (the sequence log-probability is the sum of token log-probabilities), but they lead to different variance-reduction tricks. When a paper says "we treat generation as a bandit", it means one reward, one action, no per-token values.

Return, discounting, and why LLM RL usually sets γ = 1

Rewards can arrive at several time steps. The return $G_t$ is the total reward collected from step $t$ onwards, with a discount factor $\gamma \in [0,1]$ that makes far-away rewards count less. Here is the definition: it just sums future rewards with a geometric fade.

$$G_t = r_{t+1} + \gamma\, r_{t+2} + \gamma^2 r_{t+3} + \dots = \sum_{k \ge 0} \gamma^k\, r_{t+k+1}$$

A small $\gamma$ says "I care about now". Discounting exists for two reasons: infinite-horizon problems need the sum to converge, and in noisy environments far-off rewards are less informative about the current action.

Neither reason applies to a language model. The episode is short and finite, the reward is at the end, and there is no sense in which the 5th token "caused" the final score less than the 200th. So LLM RL almost always uses $\gamma = 1$, and then something simple happens: with a single terminal reward $R$, the return from every position is just $G_t = R$.

Worked example

A six-token response earns $R = 1$ at the end and $0$ everywhere else. With $\gamma = 1$: $G_1 = G_2 = \dots = G_6 = 1$. With $\gamma = 0.9$ the first token would see $G_1 = 0.9^5 \approx 0.59$ while the last sees $G_6 = 1$, so early tokens would be told they mattered less. Nothing about writing text justifies that, so we set $\gamma = 1$.

Value, action-value, and the advantage

Now three quantities that let us talk about "how good" a situation is. All three are expectations under the current policy, which means they change whenever the weights change.

The state value $V(s)$ is the expected return if you are at $s$ and keep sampling from $\pi$. For an LLM: given this partial response, how well does this model typically finish it? The action value $Q(s,a)$ is the same, but with the next token forced to be $a$. And the advantage is the difference.

$$A(s,a) = Q(s,a) - V(s)$$

The advantage answers a single question: was choosing $a$ better than what this policy would usually do here? Positive means better than expected, negative means worse. The sign is what matters, and it will drive every update in this chapter.

Worked example

The state is the partial response "The answer is". Under the current model, 60% of continuations end up correct, so $V(s) = 0.6$. If the next token is "56" the model gets it right 90% of the time: $Q(s,\text{"56"}) = 0.9$, so $A = +0.3$. If the next token is "54" it recovers only 10% of the time: $Q = 0.1$, $A = -0.5$. Reinforce "56", suppress "54", and suppress harder than you reinforce, because it was further from expectation.

Why "better than expected" and not "good"

If every response to an easy prompt scores 0.95, then a response scoring 0.95 taught us nothing. If every response to a hard prompt scores 0.1 and one scores 0.4, that one is a big deal. The advantage subtracts the prompt's difficulty out of the reward. This single idea is why baselines, critics and GRPO's group mean all exist.

The policy gradient

Here is what we want to maximise: the expected reward of a response sampled from the model.

$$J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\big[R(\tau)\big]$$

The catch is the subscript. The expectation is over samples from the thing we are differentiating. You cannot backprop through "sample a token" the way you backprop through a matrix multiply. The reward $R$ might not even be differentiable: a unit test either passes or it does not.

The log-derivative trick, on a two-armed bandit

The way out is an identity from calculus. For any probability $\pi(a)$ that depends on $\theta$, the derivative of $\pi$ equals $\pi$ times the derivative of $\log \pi$. That is all the trick is.

$$\nabla_\theta\, \pi_\theta(a) = \pi_\theta(a)\, \nabla_\theta \log \pi_\theta(a)$$

Now push the gradient inside the expectation. The expectation is a sum over actions weighted by $\pi(a)$, and the identity turns "gradient of a probability" into "probability times gradient of a log", which is an expectation again.

$$\nabla_\theta J = \sum_a r(a)\, \nabla_\theta \pi_\theta(a) = \sum_a \pi_\theta(a)\, r(a)\, \nabla_\theta \log \pi_\theta(a) = \mathbb{E}_{a \sim \pi_\theta}\big[ r(a)\, \nabla_\theta \log \pi_\theta(a) \big]$$

Read the right-hand side as a recipe: sample an action, look at its reward, and move the weights in the direction that increases the log-probability of that action, scaled by the reward. No derivative of $r$ needed. That is why RL can use a unit test as a reward.

Worked example: two arms, by hand

A bandit with two arms. The policy is a softmax over two logits $\theta = (\theta_1, \theta_2) = (1, 0)$, so $\pi = (0.731, 0.269)$. Arm 1 always pays $r = 1$, arm 2 always pays $r = 2$. The true objective is $J = 0.731 \cdot 1 + 0.269 \cdot 2 = 1.269$.

For a softmax, the gradient of the log-probability has a closed form: $\partial \log \pi(a) / \partial \theta_j = \mathbf{1}[a = j] - \pi_j$. So $\nabla \log \pi(1) = (1 - 0.731,\; 0 - 0.269) = (0.269, -0.269)$ and $\nabla \log \pi(2) = (-0.731, 0.731)$.

The single-sample gradient estimate $\hat g = r(a) \nabla \log \pi(a)$ takes one of two values:

  • If we sample arm 1 (probability 0.731): $\hat g = 1 \cdot (0.269, -0.269) = (0.269, -0.269)$.
  • If we sample arm 2 (probability 0.269): $\hat g = 2 \cdot (-0.731, 0.731) = (-1.462, 1.462)$.

Its expectation is $0.731 \cdot (0.269, -0.269) + 0.269 \cdot (-1.462, 1.462) = (-0.197, +0.197)$. Check against the direct derivative: $J = 1 + \pi_2$, and $\partial \pi_2 / \partial \theta_2 = \pi_2(1 - \pi_2) = 0.269 \cdot 0.731 = 0.197$. It matches. The estimator is unbiased, and it says: push $\theta_2$ up, $\theta_1$ down. Arm 2 is better, and the gradient found that out without ever differentiating the reward.

REINFORCE

Applying the same trick to a full episode gives the REINFORCE estimator (Williams, 1992). The probability of a whole trajectory is a product of per-token probabilities, so its log is a sum, and the gradient becomes a sum over positions of "gradient of the log-prob of the token we produced, weighted by the return that followed".

$$\nabla_\theta J = \mathbb{E}_{\tau \sim \pi_\theta}\left[ \sum_{t} \nabla_\theta \log \pi_\theta(a_t \mid s_t)\; G_t \right]$$

In words: for every token in the sampled response, raise its log-probability in proportion to the reward collected after it. With $\gamma = 1$ and a terminal reward, $G_t = R$ for all $t$, so this is literally "raise the log-prob of every token of a good response, lower it for a bad one". The loss you actually write in PyTorch is the negative of this, so that gradient descent goes the right way.

Symbols
$\pi_\theta(a \mid s)$ = the LM's next-token distribution
$\tau$ = one sampled response
$G_t$ = return from position $t$ (equals $R$ when $\gamma=1$)
$\hat g$ = one-sample gradient estimate
STEP 1
Sample
Generate a response from the current policy. Record the tokens and their log-probabilities.
STEP 2
Score
Compute the reward $R$ for the finished response (a checker, a reward model, a human).
STEP 3
Weight
Multiply each token's $\nabla \log \pi(a_t \mid s_t)$ by $G_t$. Good response: push every token up. Bad: push down.
STEP 4
Step
Average over the batch, take a gradient step, throw the samples away, repeat. Every step needs fresh samples.
import torch

def reinforce_grad(logits, action, reward, baseline=0.0):
    """One-sample REINFORCE estimate for a bandit.
    logits: (K,) tensor with requires_grad=True. Returns d(-surrogate)/d(logits)."""
    logp = torch.log_softmax(logits, dim=-1)[action]
    surrogate = (reward - baseline) * logp     # what we want to *maximise*
    (-surrogate).backward()                    # so we minimise its negative
    return logits.grad
logits=(1.0, 0.0), action=1 (arm 2), reward=2.0 → grad = (1.462, -1.462) (the negative of the (-1.462, 1.462) we computed by hand, because this is the gradient of the loss)

Why the estimate is so noisy

Look at the two possible values of $\hat g$ in the worked example: $(0.269, -0.269)$ and $(-1.462, 1.462)$. They point in opposite directions. The average is right, but any single sample is mostly noise. The variance of the first component is $0.731 \cdot 0.269^2 + 0.269 \cdot 1.462^2 - 0.197^2 \approx 0.589$, a standard deviation of $0.77$ around a mean of $-0.197$. The signal is four times smaller than the noise.

It gets worse for language models. The rewards are often all positive (a score from 0 to 10), so every sampled token is pushed up, and the only thing distinguishing a good token from a bad one is how hard it is pushed. A response with 300 tokens gives you 300 gradient terms that all share the same scalar weight. And you get one trajectory sample from a space of $100000^{300}$ possibilities.

Intuition

REINFORCE with raw rewards is like grading essays on a scale from 90 to 100 and telling the student "you got 94, do more of whatever that was". Every essay gets praise; the student cannot tell what was actually good. What they need is "that was 4 points above your usual", which is a baseline.

Baselines: less variance, no bias

The fix is to subtract a number $b$ from the reward before weighting. The claim is that this does not change the expected gradient at all, only its variance. Here is the estimator with a baseline.

$$\nabla_\theta J = \mathbb{E}\left[ \sum_t \nabla_\theta \log \pi_\theta(a_t \mid s_t)\, \big(G_t - b\big) \right]$$

Why is it still unbiased? Because the extra term has expectation zero: $\mathbb{E}_a[b\, \nabla \log \pi(a)] = b \sum_a \pi(a) \nabla \log \pi(a) = b \sum_a \nabla \pi(a) = b\, \nabla \sum_a \pi(a) = b\, \nabla 1 = 0$. Probabilities sum to one, and the gradient of a constant is zero. Any $b$ that does not depend on the action is allowed.

Worked example: the same bandit, with a baseline

Use $b = 1.269$, the expected reward (this is $V$ for a bandit). The two possible estimates become:

  • Arm 1: $(1 - 1.269) \cdot (0.269, -0.269) = (-0.072, +0.072)$.
  • Arm 2: $(2 - 1.269) \cdot (-0.731, 0.731) = (-0.534, +0.534)$.

Both now point the same way: down on $\theta_1$, up on $\theta_2$. The mean is $0.731 \cdot (-0.072) + 0.269 \cdot (-0.534) = -0.197$, identical to before. The variance of the first component is $0.731 \cdot 0.072^2 + 0.269 \cdot 0.534^2 - 0.197^2 \approx 0.042$. From $0.589$ to $0.042$: fourteen times less noise, for free.

And here is the zero-mean check in numbers: $0.731 \cdot (0.269, -0.269) + 0.269 \cdot (-0.731, 0.731) = (0, 0)$. The baseline term really does average to nothing.

The best constant baseline is close to the expected reward, and the best state-dependent baseline is $V(s)$, which turns $G_t - b$ into an estimate of the advantage $A_t$. This is the moment the advantage stops being a definition and becomes an algorithm: policy gradient methods differ mostly in how they estimate $A_t$.

InteractiveREINFORCE on a 3-armed bandittoggle the baseline, run updates

Three arms with hidden mean rewards 0.2, 0.5 and 0.8 plus noise. Watch the policy's probabilities move, and compare the size of the gradient noise with and without a baseline (same random seed for both).

Actor–critic: learning the baseline

A constant baseline helps, but the ideal baseline is $V(s_t)$, which is different for every state. We do not know $V$. So we learn it: a second network, the critic, is trained to predict the return from a state, and the policy (the actor) uses its predictions as the baseline.

For a language model the critic is usually a small value head: a linear layer on top of the transformer's hidden states that outputs one scalar per position. It is trained by regression on the returns we observe.

$$\mathcal{L}_V(\phi) = \big(V_\phi(s_t) - G_t\big)^2$$

That is the whole critic. It looks at "The answer is" and learns to output 0.6 because, on average, the current policy finishes that state correctly 60% of the time. The advantage estimate for the actor is then $\hat A_t = G_t - V_\phi(s_t)$.

tokens so far prompt + response transformer trunk hidden state $h_t$ LM head → $\pi_\theta(\cdot \mid s_t)$ the actor: a distribution over the vocab value head → $V_\phi(s_t)$ the critic: one scalar per position
Figure 2. Actor and critic can share a trunk or be separate models. Notice the asymmetry: the actor outputs a full vocabulary distribution, the critic outputs a single number. In RLHF (next chapter) the critic is typically a whole separate copy of the model, which is where much of the memory goes.

Bootstrapping and GAE, in one paragraph

Once you have a critic you can do more than subtract it. Instead of waiting for the true return $G_t$, you can estimate it one step ahead as $r_{t+1} + \gamma V(s_{t+1})$, using the critic's guess for the rest. The difference from the current estimate is the TD error $\delta_t = r_{t+1} + \gamma V(s_{t+1}) - V(s_t)$, which is itself a (biased, low-variance) advantage estimate. Generalised Advantage Estimation, GAE (Schulman et al., 2015), blends every horizon with a decay $\lambda$:

$$\hat A_t^{\text{GAE}} = \sum_{l \ge 0} (\gamma\lambda)^l\, \delta_{t+l}$$

At $\lambda = 0$ this is the pure one-step TD error (low variance, trusts the critic completely). At $\lambda = 1$ the sum telescopes to $G_t - V(s_t)$ (unbiased, trusts only the observed return). Values like $\lambda = 0.95$ are the usual compromise. With $\gamma = 1$ and rewards only at the end, most LLM implementations end up close to the $\lambda=1$ end anyway.

Worked example

Three positions with critic values $V = (0.5, 0.6, 0.8)$, rewards $(0, 0, 1)$ arriving after each, and $V = 0$ after the end. The TD errors are $\delta = (0 + 0.6 - 0.5,\; 0 + 0.8 - 0.6,\; 1 + 0 - 0.8) = (0.1, 0.2, 0.2)$. With $\gamma = \lambda = 1$: $\hat A = (0.5, 0.4, 0.2)$; check the first: $G_1 - V_1 = 1 - 0.5 = 0.5$. With $\lambda = 0$: $\hat A = \delta = (0.1, 0.2, 0.2)$. The critic thought this response was on track, so the advantages are small and positive: "slightly better than expected".

Reusing samples: importance sampling

REINFORCE has an expensive rule: after every gradient step, throw the samples away and generate new ones. For a language model, generation is the slow part. One batch of responses might take minutes; the gradient step takes seconds. It would be nice to take several steps on the same batch.

But after one step the weights have changed, and the samples are now from the old policy $\pi_{\text{old}}$, not the current $\pi_\theta$. Using them as if they were from $\pi_\theta$ is biased. The correction is importance sampling: reweight each sample by how much more (or less) likely the new policy would have been to produce it.

$$\mathbb{E}_{a \sim \pi_\theta}[f(a)] = \mathbb{E}_{a \sim \pi_{\text{old}}}\left[ \frac{\pi_\theta(a)}{\pi_{\text{old}}(a)}\, f(a) \right]$$

The fraction is the ratio $r_t(\theta) = \pi_\theta(a_t \mid s_t) / \pi_{\text{old}}(a_t \mid s_t)$. It is $1$ at the moment of sampling and drifts as we take steps. This ratio is the central object in PPO.

Worked example

Old policy $\pi_{\text{old}} = (0.5, 0.5)$, new policy $\pi_\theta = (0.8, 0.2)$, rewards $(1, 2)$. The true expected reward under the new policy is $0.8 \cdot 1 + 0.2 \cdot 2 = 1.2$. Using only old-policy samples with importance weights: $0.5 \cdot \frac{0.8}{0.5} \cdot 1 + 0.5 \cdot \frac{0.2}{0.5} \cdot 2 = 0.8 + 0.4 = 1.2$. Same answer, no new samples.

Common confusion: importance sampling is exact, not safe

The identity is exact, but the ratio can be enormous. If the new policy puts probability 0.9 on a token the old policy gave 0.001, the ratio is 900 and one sample dominates the whole batch. The estimate is unbiased and useless. Every practical method limits how far the ratio can go, which is exactly what PPO's clip does.

PPO: the clipped objective

Proximal Policy Optimization (Schulman et al., 2017) starts from the importance-weighted policy gradient objective $r_t(\theta)\, \hat A_t$ and adds one guard rail. If the ratio has already moved past $1 \pm \epsilon$ in the direction the advantage wants, stop rewarding further movement. Here is the objective; it is a minimum of two terms.

$$\mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E}_t\Big[ \min\big( r_t(\theta)\, \hat A_t,\;\; \text{clip}(r_t(\theta),\, 1-\epsilon,\, 1+\epsilon)\, \hat A_t \big) \Big]$$

The first term is the plain importance-weighted objective. The second is the same thing with the ratio clamped to $[1-\epsilon, 1+\epsilon]$. Taking the minimum makes the objective pessimistic: whichever term is smaller wins, so the policy never gets credit for pushing the ratio beyond the band, but it always gets penalised for being on the wrong side of it. The usual $\epsilon$ is $0.2$.

Worked example: four cases at ε = 0.2

The band is $[0.8, 1.2]$.

  • $A = +1$, $r = 1.5$. Unclipped: $1.5$. Clipped: $\text{clip}(1.5) \cdot 1 = 1.2$. Minimum $= 1.2$, the clipped term. Its derivative with respect to $\theta$ is zero (the clamp is flat), so no further push. The token was good and we already increased it 50%; enough.
  • $A = +1$, $r = 0.9$. Unclipped: $0.9$. Clipped: $0.9$. They agree inside the band, gradient flows, keep pushing up.
  • $A = -1$, $r = 0.5$. Unclipped: $-0.5$. Clipped: $0.8 \cdot (-1) = -0.8$. Minimum $= -0.8$, the clipped term, gradient zero. The token was bad and we already halved it; stop.
  • $A = -1$, $r = 1.5$. Unclipped: $-1.5$. Clipped: $-1.2$. Minimum $= -1.5$, the unclipped term, gradient flows. The token was bad and yet we made it 50% more likely (because of other tokens sharing weights). The clip does not protect it: keep pushing it down.

The pattern: clipping is one-sided. It stops you from over-doing a good thing, and never stops you from correcting a bad thing.

InteractivePPO clippingdrag ratio, advantage, ε

The solid curve is the PPO objective as a function of the ratio $r$; the dashed line is the unclipped $r \cdot A$. The flat parts are where the gradient is zero.

The full PPO loss, and the KL-penalty variant

The clipped surrogate is one of three terms in the loss that actually gets minimised. The critic needs its regression loss, and an entropy bonus (more on that below) discourages collapse. With weights $c_1$ and $c_2$:

$$\mathcal{L}(\theta,\phi) = -\mathcal{L}^{\text{CLIP}}(\theta) + c_1\, \big(V_\phi(s_t) - G_t\big)^2 - c_2\, \mathcal{H}\big[\pi_\theta(\cdot \mid s_t)\big]$$

The same paper proposes an alternative to clipping: keep the plain ratio objective but subtract a penalty $\beta \cdot \text{KL}(\pi_{\text{old}} \| \pi_\theta)$ that grows when the new policy strays from the old one, with $\beta$ adjusted adaptively. Clipping won in practice because it has one hyperparameter and no tuning loop. But the KL idea survived in a different place: in RLHF, a KL penalty against the reference model (not the previous iterate) is folded into the reward itself. That is a different KL with a different purpose, and the next chapter is built around it.

Symbols
$r_t(\theta) = \pi_\theta(a_t|s_t)/\pi_{\text{old}}(a_t|s_t)$
$\hat A_t$ = advantage estimate (GAE)
$\epsilon$ = clip width (0.2)
$V_\phi$ = critic / value head
STEP 1
Roll out
Sample a batch of responses from $\pi_{\text{old}}$. Store tokens, old log-probs, critic values, rewards.
STEP 2
Estimate advantages
Run GAE over each response using the stored rewards and values.
STEP 3
Optimise for a few epochs
For each minibatch recompute $\pi_\theta$, form the ratio, apply the clipped surrogate plus value and entropy losses, step.
STEP 4
Refresh
Copy $\theta$ into $\pi_{\text{old}}$, discard the batch, go back to step 1.
def ppo_clip_loss(logp_new, logp_old, adv, eps=0.2):
    """logp_new / logp_old / adv: (B, T) per-token tensors (masked outside the response)."""
    ratio = torch.exp(logp_new - logp_old)             # pi_new / pi_old, per token
    unclipped = ratio * adv
    clipped = torch.clamp(ratio, 1 - eps, 1 + eps) * adv
    return -torch.min(unclipped, clipped).mean()       # negative because we maximise the surrogate
Where it came from

PPO (Schulman et al., 2017) is a cheap approximation of TRPO (Schulman et al., 2015), which enforced a hard KL constraint between successive policies using second-order optimisation. PPO's clip replaced the constraint with a first-order heuristic that "usually" keeps the policy nearby. It became the default for RLHF largely because it was the default in the labs that built RLHF.

GRPO: REINFORCE with a group-mean baseline and no critic

The critic is expensive. For an LLM it is another model of the same size, with its own optimiser state, that must be trained alongside the policy and is only ever used to produce a baseline. Group Relative Policy Optimization, GRPO (Shao et al., 2024, DeepSeekMath), asks a pointed question: if the baseline's job is to say "what does this policy usually score on this prompt", why not just measure that by sampling several responses to the same prompt?

So GRPO generates a group of $G$ responses per prompt, scores each, and uses the group's own statistics as the baseline. Here is the advantage, which is the same for every token of response $i$.

$$\hat A_i = \frac{r_i - \text{mean}(r_1, \dots, r_G)}{\text{std}(r_1, \dots, r_G)}$$

Subtracting the mean is the baseline. Dividing by the standard deviation rescales every prompt's rewards to unit spread, so an easy prompt where all rewards are 0.9 or 1.0 produces the same size of update as a hard one where they are 0 or 1. The rest of the objective is PPO's: the per-token clipped ratio, plus a KL term against the reference model. The critic is simply gone.

prompt "7 × 8 = ?" sample 1 → r = 1 sample 2 → r = 0 sample 3 → r = 0 sample 4 → r = 0 group stats mean 0.25, std 0.43 Â₁ = +1.73 Â₂ = −0.58 Â₃ = −0.58 Â₄ = −0.58
Figure 3. GRPO on one prompt with a group of four. The baseline is not learned; it is measured from the group. Notice that the single correct sample gets a large positive advantage and the three wrong ones share the blame. If all four had been wrong (or all right), every advantage would be zero and the prompt would teach nothing this round.
Worked example: four samples

Rewards $(1, 0, 0, 0)$. Mean $= 0.25$. Deviations $(0.75, -0.25, -0.25, -0.25)$. Population variance $= (0.5625 + 3 \cdot 0.0625)/4 = 0.1875$, so std $\approx 0.433$. Advantages: $0.75 / 0.433 \approx +1.73$ and $-0.25 / 0.433 \approx -0.58$ for each of the other three. They sum to zero, as any mean-subtracted quantity must. Every token of sample 1 gets weight $+1.73$; every token of samples 2 to 4 gets $-0.58$.

One subtlety: some implementations use the sample standard deviation (dividing by $G-1$), which here gives $0.5$ and advantages $(+1.5, -0.5, -0.5, -0.5)$. The direction is the same, only the scale changes. Check which one your library uses before comparing learning rates across papers.

InteractiveGRPO group advantagesedit the six rewards

Six samples from one prompt. Set their rewards and watch the normalised advantages. Try making them all equal.

Symbols
$G$ = group size (4 to 64 in practice)
$r_i$ = reward of sample $i$
$\hat A_i$ = group-normalised advantage
$\pi_{\text{ref}}$ = frozen reference model
STEP 1
Sample a group
For each prompt, draw $G$ responses from $\pi_{\text{old}}$.
STEP 2
Score and normalise
Reward each response; subtract the group mean, divide by the group std.
STEP 3
Broadcast
Give every token of response $i$ the same $\hat A_i$.
STEP 4
PPO-style step
Clipped ratio objective per token, plus a KL term to $\pi_{\text{ref}}$. No critic to update.
def group_advantages(rewards, eps=1e-8):
    """GRPO-style advantages for one prompt's group of samples."""
    r = torch.as_tensor(rewards, dtype=torch.float32)
    return (r - r.mean()) / (r.std(unbiased=False) + eps)
group_advantages([1, 0, 0, 0]) → tensor([ 1.7321, -0.5774, -0.5774, -0.5774])
Cousins: RLOO and plain REINFORCE

Once you drop the critic, the design space is small. RLOO (Ahmadian et al., 2024) uses a leave-one-out baseline: sample $i$ is compared against the mean of the other $G-1$ samples, which keeps the estimate exactly unbiased. The same paper argues that plain REINFORCE with a good baseline, treating the whole response as one action, is competitive with PPO for RLHF. GRPO's std normalisation is a design choice, not a law: later variants drop it because it over-weights prompts where the group barely disagrees.

Where the credit lands: broadcasting the advantage over tokens

Step through a single episode with the vocabulary in hand. Watch where the reward appears and how, with $\gamma = 1$ and no critic, one number ends up attached to every token.

InteractiveOne generation as an MDPstep through the episode

A six-token response to "7 × 8 = ?". Blue is the state, amber is the action being taken, and the reward only exists at the end.

The last frame shows the honest limitation of terminal-reward RL: the filler words get the same credit as the answer. In practice this is fine on average, because "The answer is" appears in both correct and incorrect responses and its net push cancels out over many samples. But it is a real source of noise, and it is why per-token reward shaping (like the KL penalty in RLHF) and process rewards exist.

Common confusion: averaging over tokens or over sequences?

When you turn the per-token objective into a scalar loss, you have to average. Averaging over all tokens in the batch gives long responses more weight. Averaging per sequence first, then over sequences gives every response equal weight regardless of length. The two choices produce different length biases, and papers disagree about which is right. This is a detail that changes results, so check what your implementation does.

Exploration and the entropy bonus

Here is a failure that policy gradients are prone to. The policy finds one response that scores well, its probability goes up, it gets sampled more, it gets reinforced more, and within a few hundred steps the model produces the same answer to everything. It never tries the alternatives, so it never learns whether they were better. This is exploration collapse.

The standard fix is to add the entropy of the policy's distribution as a bonus to the objective. Entropy measures how spread out a distribution is; here is the formula for one position.

$$\mathcal{H}\big[\pi(\cdot \mid s)\big] = -\sum_a \pi(a \mid s) \log \pi(a \mid s)$$

A peaked distribution has low entropy, a flat one has high entropy. Adding $c_2 \mathcal{H}$ to the objective (it is the $-c_2 \mathcal{H}$ term in the PPO loss) pays the policy a little for staying uncertain, which keeps alternative tokens alive long enough to be tried.

Worked example

Two-token vocabulary. $\pi = (0.5, 0.5)$ has entropy $-2 \cdot 0.5 \ln 0.5 = 0.693$ nats. $\pi = (0.9, 0.1)$ has entropy $-(0.9 \ln 0.9 + 0.1 \ln 0.1) = 0.095 + 0.230 = 0.325$. $\pi = (0.99, 0.01)$: $0.056$. The bonus is largest when the policy is most uncertain, and it fades as the policy commits.

In LLM RL the sampling temperature plays the same role at rollout time, and entropy is one of the first diagnostics to plot. Reports from reasoning-RL runs describe entropy dropping quickly early in training; whether that is healthy commitment or premature collapse is often only clear in hindsight, which is why many recipes monitor it and intervene (via the bonus, the temperature, or the KL term) when it falls too fast.

Reward hacking and specification gaming

Everything above assumes the reward measures what you want. It never quite does. A reward is a proxy: a classifier, a reward model, a unit test, a length-normalised score. RL is an optimiser that will find every gap between the proxy and your intent, because gaps are where the easy reward is. This is Goodhart's law in its purest form: when a measure becomes a target, it stops being a good measure.

RL training steps → reward proxy reward (what we optimise) true quality (what we wanted) early stop here
Figure 4. The shape to remember (illustrative, not measured). The proxy keeps rising; the thing you actually cared about peaks and then falls as the policy finds the proxy's blind spots. Every RLHF system has some version of this curve, and the next chapter shows real ones.

The failures have LLM-specific flavours, and you will meet all of them.

  • Length. Reward models trained on human preferences tend to score longer answers higher. The policy learns to pad: restate the question, add caveats, add a summary of the summary. Reported in nearly every RLHF paper since 2022.
  • Formatting. Bullet points, bold headers, markdown tables. If the judge likes structure, the policy produces structure whether or not the content deserves it.
  • Sycophancy. Agreeing with the user's stated opinion, praising the question, backing down when challenged even when right. Human raters reward agreement, so the policy learns to agree.
  • Test gaming. With unit tests as reward, models have been observed to special-case the expected outputs, or to edit the test file. The reward said "tests pass", not "solve the problem".
  • Judge-word stuffing. If an LLM judge is told to rate "helpfulness and honesty", responses that literally say "to be helpful and honest…" score better.
  • Confident nonsense. Hedged answers score lower than confident ones with a preference-trained RM, so the policy learns to sound sure.

The defences map onto the tools in this chapter. A KL penalty to a reference model limits how far the policy can drift toward the proxy's blind spots. Length penalties and length-controlled judging target the most common hack directly. Verifiable rewards (a real answer key, a real compiler) remove the proxy where you can. Ensembles of reward models, and stopping early on the curve above, do the rest. None of this makes the problem go away, which is why reward design gets its own treatment in the RLHF chapter.

Intuition

The policy is not "cheating". It is doing exactly what the gradient told it: increase the number. Whenever an RL run does something absurd, the first question is not "why is the model misbehaving" but "what did the reward actually measure, and was the absurd thing the cheapest way to raise it?" Almost always, yes.

Putting the map together

Here is how the methods in this chapter relate. They are all the same gradient, $\nabla \log \pi \cdot (\text{something})$, and they differ in what the something is and how many samples it costs.

MethodWeight on $\nabla \log \pi(a_t \mid s_t)$Needs a critic?Reuses samples?Variance
REINFORCE$G_t$ (raw return)NoNoVery high
REINFORCE + baseline$G_t - b$NoNoHigh
Actor–critic / A2C$\hat A_t$ from a value head (GAE)YesNoMedium
PPO$\hat A_t$ via clipped ratioYesYes, a few epochsMedium, stable
GRPO$(r_i - \bar r)/\sigma$ via clipped ratioNoYes, a few epochsMedium; needs $G$ samples per prompt
RLOO$r_i - \bar r_{-i}$ (leave-one-out)NoTypically noMedium; unbiased

Reading a paper's method section is now a matter of locating it in this table. "We use PPO with GAE ($\lambda = 0.95$), a value head initialised from the reward model, and a KL coefficient of 0.05" is a row of this table plus three hyperparameters. "We use GRPO with a group size of 16 and verifiable rewards" is another row. The rest of the paper is about the reward.

Practice

Exercise 1 — REINFORCE on a bandit, with and without a baseline

Using code/lumen/rl.py, implement a 3-armed Gaussian bandit and REINFORCE with a softmax policy over 3 logits. Run 500 updates at a learning rate of 0.1 with (a) no baseline and (b) a running-mean baseline. Record every single-sample gradient vector and report the trace of its empirical covariance for each case, and the final probability on the best arm. Repeat over 10 seeds and report means.

Solution sketch

The gradient for a softmax policy is $(r - b)(\mathbf{e}_a - \pi)$, which you can compute by hand or with torch.log_softmax and backward(). Store each gradient in a list and compute grads.var(0).sum() at the end. You should see the covariance trace drop by a large factor with the baseline (the exact factor depends on the reward noise) while the final policy is similar or better. Run both variants with the same seed for a fair comparison.

Exercise 2 — PPO's clip has a dead zone

Implement ppo_clip_loss as shown above. Construct a batch of 4 tokens with $\log\pi_{\text{old}} = 0$ and $\log\pi_{\text{new}}$ chosen so that the ratios are $(0.5, 0.9, 1.1, 1.5)$, with advantages $(-1, -1, +1, +1)$. Compute the gradient of the loss with respect to $\log\pi_{\text{new}}$ and check which entries are exactly zero. Then flip the signs of the advantages and repeat.

Solution sketch

With the first set of advantages, tokens 1 and 4 are clipped in the "already moved far enough in the right direction" sense, so their gradient is exactly 0; tokens 2 and 3 are inside the band and get gradient $-A \cdot r$ (from the chain rule through $\exp$). After flipping the signs, every ratio is on the wrong side of its advantage, so all four gradients are nonzero: the unclipped term is active everywhere. This is the one-sidedness from the worked example, now in code.

Exercise 3 — GRPO advantages and the "dead prompt" problem

Implement group_advantages and verify that the outputs always sum to zero (up to floating point). Then simulate a policy with a per-prompt success probability $p$ and group size $G = 8$: for $p \in \{0.01, 0.1, 0.5, 0.9, 0.99\}$, estimate the fraction of groups in which every reward is identical (so the prompt yields no gradient). Plot it. What does this suggest about which prompts to include in a GRPO training set?

Solution sketch

The probability that all 8 samples agree is $p^8 + (1-p)^8$. At $p = 0.5$ that is under 1%; at $p = 0.01$ or $0.99$ it is about 92%. Prompts the current policy almost always gets right or almost always gets wrong are wasted compute under GRPO, which is why practical pipelines filter prompts by measured pass rate and re-filter as the policy improves.

Exercise 4 — watch a reward get hacked

Train a tiny character-level model (a few hundred parameters, from code/lumen/rl.py) with REINFORCE to produce 10-character strings where the reward is "number of vowels". Then change the reward to "number of vowels, minus 2 if the string contains a repeated character". Observe what the policy converges to in each case and how quickly.

Solution sketch

The first reward is maximised by "aaaaaaaaaa" or similar: the policy collapses to one vowel within a few hundred updates and entropy goes to zero. The second reward forces variety and the policy finds strings like "aeiouaeiou" more slowly. Neither is what you would call "good text", which is the point: the policy optimised the number you gave it, and the number was not the thing you wanted.

Check yourself
In the MDP view of a language model, what is a single action?
At the token level, each sampled token is one action and the state is the prompt plus everything sampled so far. Treating a whole response as one action is the bandit view, a valid but different framing.
Subtracting a baseline $b$ (independent of the action) from the return in REINFORCE…
Because $\sum_a \pi(a)\nabla\log\pi(a) = \nabla\sum_a\pi(a) = 0$, the baseline term averages to zero. Any action-independent $b$ is unbiased; $V(s)$ is just the choice that reduces variance the most.
With $\epsilon = 0.2$, advantage $A = -1$ and ratio $r = 1.5$, what does PPO's clipped objective do?
The minimum of $-1.5$ and $-1.2$ is $-1.5$, the unclipped term. The clip only removes the incentive to move further in the advantage's direction; it never prevents correcting a move in the wrong direction.
A GRPO group of 8 samples all receive reward 1. What is the advantage of each?
The group mean is 1, so every $r_i - \bar r = 0$. The prompt contributes no policy gradient this round (the KL term may still act). Prompts that are too easy or too hard for the current policy are wasted under GRPO.
Why do LLM RL methods almost always use a discount factor $\gamma = 1$?
Discounting exists for infinite horizons and for noisy environments where distant rewards say little about current actions. Neither applies to a finite generation with one score at the end, so $\gamma = 1$ and every token's return equals the final reward.

Key takeaways

  • For an LLM: state = prompt plus tokens so far, action = next token, policy = the model, reward = a score at the end, episode = one generation. The environment just appends tokens.
  • The log-derivative trick turns "gradient of an expectation over samples" into "expectation of reward times gradient of log-probability". That is why RL can learn from non-differentiable scores.
  • REINFORCE is unbiased but noisy. Subtracting a baseline keeps it unbiased and cuts the variance; the best baseline is $V(s)$, and the weight becomes the advantage: "better than expected?".
  • PPO reuses samples via the importance ratio and clips it so the policy cannot run away. The clip is one-sided: it stops over-doing good moves, never correcting bad ones.
  • GRPO replaces the critic with the mean and std of a group of samples for the same prompt. Cheaper, but groups with identical rewards teach nothing.
  • The reward is a proxy and RL will find its gaps: length, formatting, sycophancy, test gaming. Plan for it with KL penalties, verifiable rewards, and early stopping.

Further reading