Nathan Lambert
Course on RLHF and post-training. Chapter 6, Part 1
After instruction tuning and training a reward model, the RL step updates the policy against a learned reward signal.
System diagram view.

After instruction tuning and training a reward model, the RL step updates the policy against a learned reward signal.
RLHF optimization view.

This lecture covers the math and theory of RL for language models. The next lecture covers implementation.
In lecture 2, we covered rejection sampling and later we’ll cover direct alignment algorithms (like DPO). They’re simpler, but RL has hard to measure benefits.
Implementing RL is far more complex infrastructure, but the gradient updates it provides “generally help the model a lot.” This is hard to quantify, but:
Overall, RL losses on language models are robust, scalable, effective, and flexible, which opened large new fields of experimentation. The original method that started us down this path was RLHF work.
RL is now the load-bearing step in training the most capable models.
Reasoning models (o1, DeepSeek R1, etc.) are trained with these exact algorithms on verifiable rewards.

The RL methods in this lecture power both RLHF and RLVR:
Same policy gradient algorithms, different reward source. We will cover tricks for reasoning training and a bunch of interesting models in depth in a future lecture.

Classical RL: agent in an MDP (\mathcal{S}, \mathcal{A}, P, r, \gamma)
RLHF: prompts from a dataset, no environment

This lecture uses (s, a) notation from the reinforcement learning literature, where s denotes states and a denotes actions. In the language model context, you will often see (x, y) instead, where x is the prompt and y is the completion.
The (s, a) framing is more general — these algorithms were designed for sequential decision problems where actions are taken at each timestep. However, many RLHF implementations treat the entire completion as a single action, making the (x, y) notation equally valid.
Notation: r_t for per-step rewards, R(\tau) for trajectory returns, \rho_t for importance-sampling ratios. We reserve \rho for ratios to avoid confusion with rewards.

| MDP concept | Language model |
|---|---|
| State s_t | Prompt + tokens generated so far: (x, y_{<t}) |
| Action a_t | Next token y_t |
| Transition | Deterministic: append token to sequence |
| Policy \pi_\theta(a_t \mid s_t) | LM next-token distribution |
| Episode | One prompt → completion |
| Terminal reward | RM score or verifier output |
| \gamma | Typically 1.0 (no discounting) |
Two views of this MDP: token-level (each token is a separate action, used in PPO with GAE) vs. sequence-level (entire completion is one action, used in REINFORCE/GRPO).
Choose policy parameters \theta that maximize reward on average under the current policy.
Let p_\theta(\tau) be the trajectory distribution induced by the current policy.
In practice, we estimate this expectation with sampled rollouts:
We craft a gradient/derivative that lets us optimize this.
Make actions more likely when they lead to better outcomes.
Read it as two questions answered at once:
Make actions more likely when they lead to better outcomes.
Read it as two questions answered at once:
An oversimplification (e.g. intuition in case of batch size of 1): the gradient is a vector with one entry per parameter. A positive entry means “increasing this parameter made the action more likely,” a negative entry means the opposite. In practice, the update averages over many such vectors — what survives is the net vote across the batch.
Multiply them: \Psi_t > 0 updates parameters to make a_t more likely, \Psi_t < 0 updates them to make it less likely.
The rest of this section is about choosing a smart \Psi_t — different choices (total return, advantage, TD residual) trade off variance and bias, but all plug into this same update.
A preview of where we end up:
The core idea is that we sample over trials in the environment and estimate the gradient.
Where \Psi_t is the learning signal telling the optimizer how good the action was. The choice of \Psi_t determines the algorithm’s variance, bias, and compute cost.
Three quantities appear throughout this lecture:
Value function V(s): expected future return from state s
Action-value Q(s, a): expected return after taking action a in state s
Advantage A(s, a) = Q(s, a) - V(s): how much better is action a compared to average? Positive → reinforce, negative → suppress, zero → no update.
In RLHF: often \gamma = 1 (no discounting) because the unit of optimization is the full completion.
Popular choices for \Psi_t (rewards can also be discounted by \gamma):
| \Psi_t | Description | Variance | Bias | |
|---|---|---|---|---|
| 1. | R(\tau) = \sum_{t=0}^{T} r_t | Total trajectory reward | Highest | None |
| 2. | \sum_{t'=t}^{T} r_{t'} | Future return from t (the return, G_t) | High | None |
| 3. | G_t - b(s_t) | Baselined return | Lower | None |
| 4. | Q^{\pi}(s_t, a_t) | State-action value function | Med | Depends |
| 5. | A^{\pi}(s_t, a_t) = Q - V | Advantage function | Low (with good V) | None |
| 6. | r_t + \gamma V(s_{t+1}) - V(s_t) | TD residual | Low | Some |
A baseline b(s_t) is any value subtracted from the reward signal to reduce variance. We’ll show below that this does not change the expected gradient.
Ideally, we want \nabla_\theta J(\theta), but we can’t do that (the state sampling distribution itself depends on \theta).
Written as an integral, the objective is:
Ideally, we want \nabla_\theta J(\theta), but we can’t do that (the state sampling distribution itself depends on \theta).
Written as an integral, the objective is:
Taking the gradient directly:
We can sample trajectories from p_\theta(\tau), but not from \nabla_\theta p_\theta(\tau), since it is not a probability distribution.
The derivation is a few key tricks to let us approximate or compute this.
Substituting the log-derivative identity (more on this soon) into the gradient:
Now we can estimate this with Monte Carlo sampling!
The only non-obvious step is the middle line:
This is the log-derivative identity, derived from the chain rule for \log.
It rewrites the gradient of a probability in terms of a log-probability, which is much easier to work with.
Where did the p_\theta(\tau) term go?
It became the sampling distribution inside the expectation:
Monte Carlo then estimates that expectation by sampling trajectories from the policy:
So there is no explicit p_\theta(\tau_i) term in code: more likely trajectories already appear more often in the sampled batch.
At a high level, rollout generation handles the sampling, and the loss code handles R(\tau_i)\,\nabla_\theta \log p_\theta(\tau_i).
The trajectory probability factorizes:
Taking the log:
For language models, this is the familiar autoregressive pattern: a sequence log-probability becomes a sum of token log-probabilities.
Now take the gradient w.r.t. \theta:
Now take the gradient w.r.t. \theta:
In language-model code, this is why you repeatedly see:
seq_log_probs = (token_log_probs * completion_mask).sum(dim=-1)
loss = -(seq_log_probs * advantages).mean()
loss.backward()
Autodiff turns that summed log-probability into the corresponding sum of per-token gradients!
An action at time t can’t affect past rewards. So instead of weighting by the full trajectory reward R(\tau):
We can replace R(\tau) with the return-to-go G_t = \sum_{t'=t}^{T} r_{t'} — only future rewards from t onward:
This doesn’t change the expected gradient, but removes noise from past rewards that the current action couldn’t have influenced. This is the step from \Psi_t option 1 → option 2 in our taxonomy.
Raw returns are noisy. The same action can appear in trajectories with very different total rewards because of later sampled actions and, in classical RL, environment randomness.
The role of a baseline is to center that noisy signal: instead of asking “was the return high?”, we ask “was it higher or lower than expected for this state?”
Raw returns are noisy. The same action can appear in trajectories with very different total rewards because of later sampled actions and, in classical RL, environment randomness.
The role of a baseline is to center that noisy signal: instead of asking “was the return high?”, we ask “was it higher or lower than expected for this state?”
Use a centered return:
where the baseline b(s_t) depends only on the state, not on which action was sampled.
Subtracting this baseline doesn’t change the expected gradient:
Raw returns are noisy. The same action can appear in trajectories with very different total rewards because of later sampled actions and, in classical RL, environment randomness.
Use a centered return, \Psi_t = G_t - b(s_t).
Subtracting this baseline doesn’t change the expected gradient:
The first term is the original estimator. The second vanishes:
The gradient of a normalized distribution sums to zero — so we’re free to subtract any function of state as a baseline. This is why \Psi_t options 3–5 reduce variance without introducing bias.
Popular choices for \Psi_t (rewards can also be discounted by \gamma):
| \Psi_t | Description | Variance | Bias | |
|---|---|---|---|---|
| 1. | R(\tau) = \sum_{t=0}^{T} r_t | Total trajectory reward | Highest | None |
| 2. | \sum_{t'=t}^{T} r_{t'} | Future return from t (the return, G_t) | High | None |
| 3. | \sum_{t'=t}^{T} r_{t'} - b(s_t) | Baselined return | Lower | None |
| 4. | Q^{\pi}(s_t, a_t) | State-action value function | Med | Depends |
| 5. | A^{\pi}(s_t, a_t) = Q - V | Advantage function | Lowest | None |
| 6. | r_t + \gamma V(s_{t+1}) - V(s_t) | TD residual | Low | Some |
A baseline b(s_t) is any value subtracted from the reward signal to reduce variance — we’ll show why this is unbiased shortly.
Combining the log-derivative trick, return-to-go, and baseline subtraction:
This is the policy gradient theorem. Every algorithm in this lecture is an instantiation with a specific choice of \Psi_t and regularization.
For language models, this means the sum runs over generated tokens in the completion, while \Psi_t determines how each token is weighted.
Before diving into specific algorithms, let’s revisit the full RLHF setup they plug into.

The reference model \pi_\text{ref} is a frozen copy of the policy at the start of RL training (typically the SFT checkpoint).
It serves one purpose: anchor the policy so it doesn’t drift too far during optimization.
Without it, the policy can exploit the reward model — finding high-scoring outputs that are degenerate or repetitive (reward hacking).

The KL penalty measures how far the current policy has drifted from the reference at each token:
In practice:
The shaped reward becomes: \tilde{r}_t = -\beta \, \text{KL}_t for intermediate tokens, \tilde{r}_T = R(\tau) - \beta \, \text{KL}_T for the final token.

RLHF (reward model signal): KL penalty is critical — the reward model is a learned proxy, and the policy will exploit any imperfections without regularization.
RLVR (verifiable rewards, e.g. math correctness): KL is often reduced or removed entirely. The reward is ground truth, so there’s less to exploit. Some RLVR setups (e.g. DeepSeek R1) drop the reference model altogether, saving memory.
The trend: as reward signals become more reliable, the need for KL regularization decreases.

| \pi_{\theta_\text{old}} | \pi_\text{ref} | |
|---|---|---|
| What | Policy at last rollout | Policy at start of RL training (SFT checkpoint) |
| Updates | Every batch (or every K steps) | Never (frozen) |
| Used for | Importance-sampling ratio \rho_t | KL penalty |
| If dropped | Must use 1 gradient step per batch (on-policy) | Risk of reward hacking |
In some implementations, “old logprobs” refers to the generation-time logprobs — the model has simply been updated since those logprobs were computed, not a separate model copy.
REINFORCE is the simplest instantiation of the policy gradient.
It is the Monte Carlo form of policy gradient: sample trajectories, compute their returns, and use those sampled returns to weight the log-prob gradients.
REINFORCE is the simplest instantiation of the policy gradient.
It is the Monte Carlo form of policy gradient: sample trajectories, compute their returns, and use those sampled returns to weight the log-prob gradients.
The name is an acronym for “REward Increment = Nonnegative Factor X Offset Reinforcement X Characteristic Eligibility.”
Three components:
REINFORCE is the simplest instantiation of the policy gradient.
It is the Monte Carlo form of policy gradient: sample trajectories, compute their returns, and use those sampled returns to weight the log-prob gradients.
The name is an acronym for “REward Increment = Nonnegative Factor X Offset Reinforcement X Characteristic Eligibility.”
Three components:
The update rule:
Without a baseline, the gradient weights each action by its raw return G_t. This doesn’t tell you whether an action was better or worse than expected — just how good the overall outcome was.
The gradient is high variance because the raw return mixes the quality of the action with the quality of the state. A good action in a bad state and a bad action in a good state can produce similar returns.
Subtracting a baseline b(s) centers the signal: now the gradient weight is (G_t - b), which answers “was this action better or worse than expected from this state?”
Because b(s) does not depend on which action was sampled, it factors out and cancels. The expected gradient is unchanged — but the variance drops dramatically.
The full REINFORCE gradient:
Here, G_t - b(s_t) is already an advantage estimate: how much better the realized return was than expected from state s_t.
Common baselines:
Basic REINFORCE needs no critic — just Monte Carlo returns and a simple baseline. Adding a learned V_\phi can reduce variance further but introduces the complexity of training a second model (… and moves towards PPO).

Key idea: generate K completions per prompt. Use the other K-1 rewards as the baseline:
The advantage for completion k:
This is a per-prompt baseline that naturally captures prompt difficulty — hard prompts get low rewards across all completions, so the baseline is low.
Detail: Like REINFORCE without a critic, this same sequence-level advantage is broadcast to every token in the completion.
K = 4 completions for one prompt, with rewards [0.8, 0.3, 0.6, 0.5]:
| Completion | Reward | Baseline (avg of others) | Advantage |
|---|---|---|---|
| 1 | 0.8 | (0.3 + 0.6 + 0.5)/3 = 0.467 | +0.333 |
| 2 | 0.3 | (0.8 + 0.6 + 0.5)/3 = 0.633 | -0.333 |
| 3 | 0.6 | (0.8 + 0.3 + 0.5)/3 = 0.533 | +0.067 |
| 4 | 0.5 | (0.8 + 0.3 + 0.6)/3 = 0.567 | -0.067 |
Completion 1 (best) gets reinforced. Completion 2 (worst) gets suppressed. Completions 3 and 4 get small updates.
REINFORCE: Simplest policy gradient. Needs a baseline to reduce variance. No value function required.
RLOO: REINFORCE + a smart, per-prompt leave-one-out baseline. Multiple completions per prompt provide the baseline for free.
Both are the foundation for everything that follows:

All the algorithms in this lecture are on-policy: they generate fresh rollouts from the current policy each batch, then update on those rollouts. This is in contrast to off-policy RL methods (e.g. DQN) that store and replay old experience.
All the algorithms in this lecture are on-policy: they generate fresh rollouts from the current policy each batch, then update on those rollouts. This is in contrast to off-policy RL methods (e.g. DQN) that store and replay old experience.
The problem: vanilla policy gradient is sensitive to step size. Too large an update and the policy can collapse; too small and training is painfully slow. TRPO (Schulman et al., 2015) solved this with a hard trust-region constraint, but required expensive second-order optimization.
All the algorithms in this lecture are on-policy: they generate fresh rollouts from the current policy each batch, then update on those rollouts. This is in contrast to off-policy RL methods (e.g. DQN) that store and replay old experience.
The problem: vanilla policy gradient is sensitive to step size. Too large an update and the policy can collapse; too small and training is painfully slow. TRPO (Schulman et al., 2015) solved this with a hard trust-region constraint, but required expensive second-order optimization.
Proximal Policy Optimization (PPO) (Schulman et al., 2017) gets TRPO-like stability with a simple clipped objective — and because the clipping keeps updates conservative, you can safely take multiple gradient steps per batch of rollouts, improving sample efficiency.
Extract more signal from the batch! This introduces new problems:
Large gradient steps can destroy the policy (instability, over-optimization, etc.)
The solution: trust regions — limit how far the policy can move in a single update.
We want to take multiple gradient steps on a batch, but the data came from an old policy \pi_{\theta_\text{old}}.
Define the importance sampling ratio:
This ratio reweights old-policy samples to estimate new-policy gradients. Plugging that ratio into the earlier policy-gradient form gives a surrogate objective we can optimize on old-policy data.
Using importance sampling, the policy gradient becomes:
Intermediate problem: without constraints, maximizing this can take arbitrarily large steps — the ratio \rho_t can diverge far from 1, making the estimate unreliable.
PPO clips the ratio to prevent large updates — a practical surrogate inspired by trust-region ideas. The original paper (Schulman et al., 2017) calls this L^{CLIP}, but it’s an objective we maximize, so we use J:
Where \rho_t(\theta) = \frac{\pi_\theta(a_t \mid s_t)}{\pi_{\theta_\text{old}}(a_t \mid s_t)} is the importance-sampling ratio.
Where \varepsilon is typically 0.1–0.2. The \min selects the more conservative estimate.
The action is better than average at that state — we want to increase its likelihood. Three sub-cases:
\rho_t < 1 - \varepsilon: Action is less likely under new policy.
Objective: \rho_t \hat{A}_t — normal gradient, push probability up
1 - \varepsilon \leq \rho_t \leq 1 + \varepsilon: Action is roughly equally likely.
Objective: \rho_t \hat{A}_t — normal gradient, push probability up
\rho_t > 1 + \varepsilon: Action is already more likely under new policy.
Objective: (1+\varepsilon)\hat{A}_t — gradient is zero, no update needed (CLIPPED)
Note: on the first gradient step, \rho_t = 1 (policy hasn’t changed yet), so the ratio starts near 1 and cases 1/3 only arise on subsequent steps.
\rho_t(\theta) = \pi_\theta / \pi_{\theta_\text{old}} (policy ratio)
The action was worse than average at that state — we want to decrease its likelihood. Three sub-cases:
\rho_t < 1 - \varepsilon: Action is already less likely under new policy.
Objective: (1-\varepsilon)\hat{A}_t — gradient is zero, no update needed (CLIPPED)
1 - \varepsilon \leq \rho_t \leq 1 + \varepsilon: Action is roughly equally likely.
Objective: \rho_t \hat{A}_t — normal gradient, push probability down
\rho_t > 1 + \varepsilon: Action is more likely under new policy.
Objective: \rho_t \hat{A}_t — normal gradient, push probability down
Note: on the first gradient step, \rho_t = 1 (policy hasn’t changed yet), so the ratio starts near 1 and cases 1/3 only arise on subsequent steps.
\rho_t(\theta) = \pi_\theta / \pi_{\theta_\text{old}} (policy ratio)
The action was exactly as good as expected. The loss is zero — no update.
In all cases, clipping stops the gradient when the policy has already moved enough in the right direction:
| Advantage | Within trust region | Outside trust region |
|---|---|---|
| \hat{A}_t > 0 | Normal gradient (reinforce) | Zero gradient if \rho_t > 1+\varepsilon |
| \hat{A}_t < 0 | Normal gradient (suppress) | Zero gradient if \rho_t < 1-\varepsilon |
| \hat{A}_t = 0 | No update | No update |
The clipping is one-sided per advantage sign: it caps movement when the policy has already moved enough in the beneficial direction, but never blocks correction in the detrimental direction.

PPO trains a value function V_\phi(s) alongside the policy — the expected future return from state s_t:
The value function serves as a learned baseline for advantage estimation.
PPO-style RLHF often starts with one scalar reward for the whole completion. How does that become a per-token training signal?
This is how PPO-RLHF turns a sequence-level reward into token-level training.
The simplest advantage: \hat{A}_t = G_t - V_\phi(s_t)
Why advantages help:
This Monte Carlo estimate is simple and unbiased, but it can be high variance. Temporal Difference (TD) methods and Generalized Advantage Estimation (GAE) trade some bias for lower variance.
The temporal difference (TD) residual measures how much the actual reward exceeded the value prediction:
This is the 1-step advantage estimate: low variance (uses learned V_\phi) but potentially high bias (if V_\phi is inaccurate).
We can extend to K steps:
As k \to \infty, we recover the full Monte Carlo advantage G_t - V_\phi(s_t) (no bias, highest variance).
GAE uses an exponentially-weighted average across all K-step estimates:
Where \lambda \in [0, 1] controls the bias-variance tradeoff.
| \lambda | Behavior | Variance | Bias |
|---|---|---|---|
| 0 | Pure TD (1-step) | Lowest | Highest |
| 0.95 | Typical default for LLMs | Balanced | Balanced |
| 1 | Monte Carlo advantage | Highest | None |
The \gamma here is typically 1.0 for language models (no discounting). These rankings assume an accurate V_\phi — in practice, a poorly trained critic can make even low-\lambda estimates unreliable.
A schematic high-level view of PPO-RLHF combines the clipped PPO objective with a KL regularizer:
Two layers of regularization:
These serve different purposes and are not redundant.
The PPO training loop:
Typical: K = 2–4 gradient steps per batch before re-generating.
PPO requires four models:
| Model | Purpose | Updates? |
|---|---|---|
| Policy \pi_\theta | Generates completions | Yes |
| Value function V_\phi | Estimates per-token expected return | Yes |
| Reference policy \pi_\text{ref} | KL penalty anchor | Frozen |
| Reward model r_\psi | Scores completions | Frozen |
This is memory-intensive — a key motivation for simpler alternatives like GRPO.

Group Relative Policy Optimization (GRPO) was introduced in DeepSeekMath (Shao et al., 2024) for math reasoning and has since become the go-to algorithm for RL on language models. It keeps PPO’s clipped objective but drops the value function entirely.
Core idea: generate G completions per prompt, use the group’s reward statistics as the baseline — no learned critic needed.
For a group of G completions with rewards R_1, \ldots, R_G:
Z-score normalization: positive advantage for above-average completions, negative for below.
Each token in completion i gets the same advantage (sequence-level, not per-token).
Clipped ratio (like PPO) + group-normalized advantages + KL penalty directly in loss:
Where the clipping applies per-token: \rho_{i,t} = \frac{\pi_\theta(a_{i,t} \mid s_t)}{\pi_{\theta_\text{old}}(a_{i,t} \mid s_t)}, but \hat{A}_i is shared across all tokens in the completion (sequence-level advantage, per-token ratio).
| PPO | GRPO | |
|---|---|---|
| Value function | Learned V_\phi | None |
| Advantage | Per-token via GAE | Sequence-level, group z-score |
| KL penalty | In reward (before advantages) | In loss (default, but optional) |
| Models in memory | 4 (policy, value, ref, RM) | 3 (policy, ref, RM) or 2 without KL |
| Complexity | Higher | Lower |
| Popular for | General RLHF | Reasoning / RLVR (DeepSeek R1) |
GRPO is PPO minus the value function, with a statistical baseline instead.
Both use multiple completions per prompt. The key difference is in the PPO-style clipping for GRPO:
| RLOO | GRPO | |
|---|---|---|
| Baseline | Leave-one-out mean | Group mean (z-scored) |
| Update style | REINFORCE (no clipping) | PPO-style clipped ratio |
| KL penalty | Optional (in reward) | In loss (default, but optional) |
| Advantage | R_k - \frac{1}{K-1}\sum_{j \neq k} R_j | \frac{R_i - \text{mean}}{\text{std}} |
Same principle (compare to peers), different mechanics. Without std normalization, the GRPO-style advantage estimate becomes equivalent to RLOO up to a scaling constant.
Problem: aggregating per-token importance ratios across long sequences is numerically unstable. A single token with a large ratio can dominate the update.
GSPO uses a geometric mean — a single, length-normalized importance weight per response:
The geometric mean stays in a reasonable numerical range for any sequence length — similar gradient signal, better numerics. The full objective mirrors GRPO but with the sequence-level ratio:
The clipping range \varepsilon now operates on a per-token average scale, making it comparable across different completion lengths.
CISPO clips the importance weights themselves rather than the objective, using a stop-gradient:
Key difference from PPO: every token still receives a gradient signal — the weight just bounds how much it’s amplified. Asymmetric bounds (\varepsilon^{+} > \varepsilon^{-}) allow more aggressive reward-increasing updates, encouraging exploration.

In most RLHF setups, data quality and reward signal quality dominate; algorithm choice mostly determines stability, efficiency, and engineering burden. All methods optimize the same policy gradient objective:
They differ in:
A one-page reference of all core RL loss functions is available at:
Lecture 4 turns these algorithms into working code: