←Home KnowML
Decision & Retrieval SystemsChapter 15

Reinforcement Learning

An agent that learns from trial and error, with feedback that arrives late and gets blamed on the wrong decision. The field that gave us DQN, AlphaGo, and the RL loop now sitting underneath every aligned language model.

24 min read Assumes: probability & optimization (01), gradient descent (04)
Start reading
TL;DR

Reinforcement learning is about an agent that takes actions in an environment and learns purely from reward signals. There is no labeled "correct action" ever, only "that sequence of decisions was worth +8" arriving many steps after the decisions that caused it. Everything in RL is machinery for solving that credit-assignment problem.

Value functions estimate how good a state or action is. The Bellman equation gives a recursive way to compute those estimates. Policy-gradient methods directly nudge the policy's parameters toward actions that led to higher reward.

PPO is the algorithm that made policy optimization stable and simple enough to become the default. It is also the exact RL algorithm sitting inside RLHF, which page 10 covers for language models. This page builds the foundations underneath it.

01Intuition

Supervised learning tells you the right answer for every example. RL only tells you, much later, whether things went well.

The problem every RL algorithm is solving

Imagine learning to play chess where nobody ever tells you "that specific move was a mistake." You play out an entire game, and at the very end you learn "you won" or "you lost."

Every one of the 40 moves you made contributed to that outcome by some unknown amount. Maybe move 12 was brilliant and move 31 threw the game away. The only signal you got was a single number at the end.

Figuring out which decisions deserve the credit or the blame for that final outcome is called credit assignment. It is the central difficulty, and most of this page exists to solve it.

An RL agent is a trial-and-error learner operating under exactly this constraint. It observes a state, picks an action, the environment responds with a reward and a new state, and the loop repeats.

There's no teacher whispering the correct action at every step. The agent has to discover, purely from the pattern of rewards it eventually receives, which actions tend to lead somewhere good. That's a fundamentally different learning problem from anything in supervised learning. It's why RL needs its own vocabulary: states, actions, rewards, policies, value functions.

Once that vocabulary is in place, algorithms like Q-learning and PPO are just different, principled ways of attacking the same credit-assignment problem.

A reinforcement learning agent has to balance trying new things against repeating what has worked. Three slot machines win 20%, 50% and 80% of the time, and the agent doesn't know that.

Try it Compare three exploration strategies on three slot machines
import numpy as np

rng = np.random.default_rng(1)
true = np.array([0.2, 0.5, 0.8])       # win rate of 3 slot machines

def run(eps, steps=5000):
    est, n, total = np.zeros(3), np.zeros(3), 0
    for _ in range(steps):
        a = rng.integers(3) if rng.random() < eps else est.argmax()
        r = float(rng.random() < true[a])
        n[a] += 1
        est[a] += (r - est[a]) / n[a]  # running average of this machine
        total += r
    return total / steps

for eps in (0.0, 0.1, 1.0):
    print(f"epsilon {eps:.1f}: average reward {run(eps):.3f}")
Never exploring earns 0.201, exploring 10% of the time earns 0.770, and exploring all the time earns 0.507. The purely greedy agent starts with every estimate at zero, picks machine 0, and never tries the others, so it settles for the worst machine. Random choice averages the three win rates, about 0.5. A little exploration finds the 80% machine and then mostly plays it. The best possible average is 0.8.

02Timeline

Before

Classical RL (dynamic programming, tabular Q-learning, SARSA) stored a value estimate for every state in a table, which is exact but cannot generalize to unseen states, so high-dimensional inputs like raw pixels were out of reach.

→
Innovation

Mnih et al. (2013) replaced the table with a neural network, the deep Q-network, which needed a replay buffer and a target network to train stably and learned Atari from pixels, and Schulman et al.'s PPO (2017) made policy optimization stable and simple enough to become the default algorithm.

→
After

RL became the standard second stage for aligning language models, with RLHF running PPO against a learned reward model (page 10), and the same machinery underlies robotics control (page 21) and systems like AlphaGo and MuZero.

03The agent-environment loop, and the vocabulary built on it

Every RL problem is framed the same way. An agent sits in an environment. At each time step it observes a state $s$, chooses an action $a$ according to its policy, and the environment returns a reward $r$ and a next state $s'$. That's the entire loop, repeated thousands or millions of times during training.

The agent-environment interaction loop
Agent policy π(a|s) Environment dynamics + reward action a reward r, next state s'
Agent observes state s, picks action a from its policy π(a|s), sends it to the environment. Environment responds with a reward r and a new state s'. Repeat. Everything an RL algorithm does is try to make the agent's policy produce actions that lead to higher cumulative reward over this loop — not just the next reward, the sum of all future rewards.

The MDP formalism

Formally this loop is a Markov decision process (MDP), defined by five pieces:

  • $S$, a set of states.
  • $A$, a set of actions.
  • $P(s'|s,a)$, a transition function giving the probability of landing in state $s'$ after taking action $a$ in state $s$.
  • $R(s,a,s')$, a reward function.
  • $\gamma \in [0,1)$, a discount factor.

"Markov" means the future depends only on the current state and not on the full history that led there, so the state is assumed to already summarize everything relevant.

The discount factor needs some intuition on top of the symbol. A reward received right now is worth more than the identical reward received ten steps from now, for two separate reasons.

  • Uncertainty. The world between now and ten steps from now might change, the episode might end early, you might never collect that later reward. A bird in the hand.
  • Convergence. In a task that can run forever (no natural end point), summing an infinite sequence of undiscounted rewards can diverge to infinity, which makes it meaningless to compare one policy against another.

Multiplying reward $k$ steps in the future by $\gamma^k$ fixes both problems at once. It encodes "sooner is better" and it guarantees the infinite sum converges to a finite number as long as $\gamma < 1$, since it becomes a geometric series.

Value functions, Q-functions, and advantage

Once you're discounting future reward, you need a way to ask "how good is this situation, in total, going forward?" That's what a value function is.

$V(s)$ is the expected discounted sum of all future rewards, starting from state $s$, assuming the agent acts optimally (or according to some fixed policy) from here on. It answers "how good is this state to be in?": a single number summarizing the entire rest of the episode.

$Q(s,a)$ answers a more specific question: how good is it to take this particular action $a$ in this state $s$, and then act optimally after that? The difference between $V$ and $Q$ is exactly one decision. $V(s)$ already assumes you'll pick the best action, while $Q(s,a)$ fixes the first action for you and lets the value function take over afterward.

So Q-learning can learn a full control policy just from Q-values: the best action in any state is $\arg\max_a Q(s,a)$.

Advantage combines the two: $A(s,a) = Q(s,a) - V(s)$. It's literally "how much better than average is this specific action, compared to what I'd get by just following my usual policy in this state?" A positive advantage means this action beat the state's baseline expectation; a negative advantage means it underperformed it.

Advantage matters enormously in practice because policy-gradient methods use it (or an estimate of it) to decide how hard to push the policy toward or away from an action. Pushing based on raw reward is noisy; pushing based on "better or worse than expected" is a much cleaner signal, which is exactly the motivation behind actor-critic methods below.

04The Bellman equation, and PPO's clipped objective

Almost every value-based RL algorithm exploits one recursive fact about value functions, called the Bellman equation. That includes dynamic programming, Q-learning, TD learning, and DQN.

$$V(s) = \mathbb{E}\big[\,r + \gamma V(s')\,\big]$$
  • V(s) the value of the current state: the expected discounted sum of all future reward, starting here.
  • r the immediate reward received for the transition out of state $s$.
  • γ the discount factor: how much less a unit of reward is worth one step in the future, versus right now.
  • V(s') the value of wherever the agent ends up next, computed by exactly the same function, applied one step later.
  • 𝔼[·] an expectation over the randomness in the transition (which next state you land in) and, for $Q$, over the policy's choice of action.
Why one equation makes infinite futures tractable

In plain language: today's value equals the reward you get right now, plus the (discounted) value of wherever you end up next. That's it: the whole recursive structure that makes value-based RL tractable.

It means you never have to know the value of the entire rest of an infinite future directly. You only ever need one step of real reward plus your own current estimate of what comes after.

Dynamic programming applies this equation exactly, sweeping over all states. Q-learning and TD learning apply an approximate, sampled version of it, updating one estimate at a time from lived experience, where no exact model of the environment is available.

The Bellman equation is the backbone of value-based methods. Policy-gradient methods rest on a different result, and it is worth seeing where it comes from, because the whole of PPO is built on top of it.

Derivation The policy gradient theorem, and why the environment drops out

We want to maximise expected return $J(\theta) = \mathbb{E}_{\tau \sim p_\theta}\!\left[R(\tau)\right]$ over trajectories $\tau$. The obstacle is subtle: $\theta$ does not appear in $R$. It appears in the distribution we are averaging over. Assume an episodic task and the usual regularity conditions that let us exchange a gradient and an integral.

  1. $$\nabla_\theta J(\theta) = \nabla_\theta \int p_\theta(\tau)\, R(\tau)\, d\tau = \int \nabla_\theta p_\theta(\tau)\, R(\tau)\, d\tau$$
    Why: the return $R(\tau)$ of a fixed trajectory carries no $\theta$, so the gradient passes straight through it and lands only on $p_\theta(\tau)$.
  2. $$\int \nabla_\theta p_\theta(\tau)\, R(\tau)\, d\tau \quad \text{is not an expectation}$$
    Why this is the problem: $\nabla_\theta p_\theta(\tau)$ is not a probability distribution. It does not integrate to one and it goes negative. So we cannot estimate this integral by sampling trajectories, which is the only thing we can actually do.
  3. $$\nabla_\theta p_\theta(\tau) = p_\theta(\tau)\, \nabla_\theta \log p_\theta(\tau)$$
    Why: the log-derivative trick. Differentiating a log gives $\nabla \log f = \nabla f / f$. Multiply both sides by $f$ and you have this identity. It is ordinary calculus, and it is the hinge the entire method turns on.
  4. $$\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim p_\theta}\!\left[\nabla_\theta \log p_\theta(\tau)\, R(\tau)\right]$$
    Why this rescues us: substituting step 3 puts $p_\theta(\tau)$ back in front of the integrand. That makes it an expectation under the policy again, and expectations under the policy are exactly what rolling out the policy samples.
  5. $$\begin{aligned}\log p_\theta(\tau) = \log p(s_0) \;&+\; \sum_t \log \pi_\theta(a_t \mid s_t) \\ &+\; \sum_t \log P(s_{t+1} \mid s_t, a_t)\end{aligned}$$
    Why: a trajectory's probability is the start-state probability times the policy's action probabilities times the environment's transition probabilities. Taking a log turns that product into a sum.
  6. $$\nabla_\theta \log p_\theta(\tau) = \sum_t \nabla_\theta \log \pi_\theta(a_t \mid s_t)$$
    Why this is the punchline: $p(s_0)$ and $P(s_{t+1} \mid s_t, a_t)$ belong to the environment. Neither contains $\theta$, so both gradients are zero and both terms vanish. Only the policy survives.
  7. $$\nabla_\theta J(\theta) = \mathbb{E}\!\left[\left(\sum_t \nabla_\theta \log \pi_\theta(a_t \mid s_t)\right) R(\tau)\right]$$
    Why this is usable: every quantity here is something you have. Run the policy, record which actions it took, compute the log-probabilities it assigned them, and weight by the return you observed. That estimate is unbiased.
You just computed a gradient of expected reward without ever differentiating the reward, and without knowing the environment's dynamics at all. $R(\tau)$ can be a black box. It can be discontinuous, non-differentiable, or a human clicking a thumbs-up. That is precisely why RLHF is possible: a reward model's score never has to be differentiable with respect to the sampling process that produced the text.
Derivation Why a baseline is free, and why advantage is the right one

The REINFORCE estimator above is unbiased but brutally noisy. The standard fix is to subtract a baseline $b(s)$ from the return before weighting. That looks like it should change the answer. It does not, provided $b$ depends on the state but never on the action. The sums below assume a discrete action space; for continuous actions every sum becomes an integral and the argument is unchanged.

  1. $$\begin{aligned}\mathbb{E}_{a \sim \pi}&\!\left[\nabla_\theta \log \pi_\theta(a \mid s)\, b(s)\right] \\ &= b(s) \sum_a \pi_\theta(a \mid s)\, \nabla_\theta \log \pi_\theta(a \mid s)\end{aligned}$$
    Why: $b(s)$ does not depend on $a$, so it is a constant with respect to the expectation and factors straight out.
  2. $$= b(s) \sum_a \pi_\theta(a \mid s)\, \frac{\nabla_\theta \pi_\theta(a \mid s)}{\pi_\theta(a \mid s)} = b(s) \sum_a \nabla_\theta \pi_\theta(a \mid s)$$
    Why: the log-derivative trick again, run backwards this time. The $\pi_\theta$ in front cancels the one in the denominator.
  3. $$= b(s)\, \nabla_\theta \sum_a \pi_\theta(a \mid s) = b(s)\, \nabla_\theta (1) = 0$$
    Why: swap the finite sum and the gradient, which linearity permits. What is left inside is the total probability mass over all actions. That is $1$ by definition, for every $\theta$. The gradient of a constant is zero.
  4. $$\nabla_\theta J(\theta) = \mathbb{E}\!\left[\nabla_\theta \log \pi_\theta(a \mid s)\,\big(Q(s,a) - b(s)\big)\right]$$
    Why this is now safe: the term you just subtracted has expectation exactly zero. Subtracting something with zero mean cannot shift the mean. The gradient stays unbiased for any action-independent $b$.
  5. $$b(s) = V(s) \;\Longrightarrow\; Q(s,a) - V(s) = A(s,a)$$
    Why this choice: setting the baseline to the state's own value recentres the weight around zero. Better-than-usual actions get a positive push, worse-than-usual ones a negative push. Without it, in a task where all rewards are positive, every sampled action gets pushed up and learning has to rely on differences in magnitude alone.
Advantage is not a heuristic somebody preferred. It is the baseline-subtracted estimator that provably keeps the gradient unbiased while stripping out the variance caused by which state you happened to be in, leaving only the part that answers was this action better than my usual behaviour here. One caveat worth knowing: unbiasedness holds for any action-independent baseline, but variance reduction is not guaranteed for a bad one. $V(s)$ is the standard choice and works well in practice, though it is not strictly the variance-minimising baseline.

PPO's clipped surrogate objective, conceptually

Policy gradients want to increase the probability of actions that led to high advantage and decrease the probability of actions that led to low advantage. The naive way to do this computes a ratio between the new policy's probability of an action and the old policy's probability of that same action, and scales the gradient step by that ratio times the advantage.

The problem: nothing stops a single update from moving the policy enormously if the estimated advantage is large. And a policy that has moved too far in one update can collapse. It starts producing actions so different from what generated the training data that the whole learning process destabilizes and never recovers.

$$L^{CLIP}(\theta) = \mathbb{E}\Big[\min\big(r_t(\theta)\,A_t,\ \text{clip}(r_t(\theta),\,1-\epsilon,\,1+\epsilon)\,A_t\big)\Big]$$
  • r_t(θ) the probability ratio between the new policy and the old policy for the action taken: how much the policy wants to shift toward or away from this action.
  • A_t the estimated advantage of that action. Positive means push toward it, negative means push away.
  • clip(·, 1-ε, 1+ε) caps the ratio so it can't move more than $\epsilon$ (typically 0.1–0.2) away from 1 in either direction. The new policy is not allowed to become more than roughly 20% more or less likely to take this action in a single update.
  • min(·, ·) takes the more pessimistic of the unclipped and clipped objective, which means clipping only ever removes the incentive to keep pushing once you've already moved far enough. It never creates an incentive to push further.

The whole point in one sentence: take a policy-improving step, but clip it so the new policy can't move too far from the old policy in a single update. It's a cheap, first-order stand-in for what TRPO (PPO's more complicated predecessor) enforced with an explicit, expensive constraint on how different the new and old policies are allowed to be.

Derivation How TRPO's constraint became PPO's clip

This one is more argument than algebra, and it is worth saying that plainly. The clip is not derived from the constraint. It is a cheaper thing that behaves similarly, and the steps below trace why anyone would settle for that.

  1. $$L(\theta) = \mathbb{E}_{a \sim \pi_{\text{old}}}\!\left[\frac{\pi_\theta(a \mid s)}{\pi_{\text{old}}(a \mid s)} A(s,a)\right] = \mathbb{E}\!\left[r_t(\theta) A_t\right]$$
    Why we need the ratio at all: the data was collected under $\pi_{\text{old}}$, but we want to score $\pi_\theta$. Importance sampling reweights each sample by how much more or less likely the new policy makes it. This is what buys you several gradient steps from one batch of rollouts instead of one.
  2. $$\max_\theta\ \mathbb{E}\!\left[r_t A_t\right] \;\Longrightarrow\; r_t \to \infty \ \text{ when } A_t \gt 0$$
    Why the naive version breaks: nothing in that objective bounds the ratio. Push $r_t$ arbitrarily high and the objective keeps improving. But the importance-sampling estimate is only trustworthy while $\pi_\theta$ stays near $\pi_{\text{old}}$. Optimise it hard and you leave the region where it means anything.
  3. $$\max_\theta\ \mathbb{E}\!\left[r_t(\theta) A_t\right] \quad \text{subject to} \quad \mathbb{E}\!\left[D_{\mathrm{KL}}\!\left(\pi_{\text{old}} \,\|\, \pi_\theta\right)\right] \leq \delta$$
    TRPO's answer: make the trust region explicit. Bound the average KL between old and new policy, so the optimiser cannot leave the region where the surrogate is valid. The cost is real: this is a constrained problem needing conjugate gradient and a line search, with curvature information from Fisher matrix-vector products.
  4. $$L^{\text{CLIP}}(\theta) = \mathbb{E}\Big[\min\big(r_t A_t,\ \operatorname{clip}(r_t, 1-\epsilon, 1+\epsilon)\, A_t\big)\Big]$$
    PPO's answer: drop the constraint and flatten the objective instead. Once $r_t$ leaves $[1-\epsilon, 1+\epsilon]$, the clipped branch stops changing with $\theta$, so its gradient is zero. No constraint solver, just a clamp on a scalar.
  5. $$\begin{aligned}A_t \gt 0 \;&:\ \text{capped at } (1+\epsilon)A_t \\ A_t \lt 0 \;&:\ \text{floored at } (1-\epsilon)A_t\end{aligned}$$
    Why the $\min$ is load-bearing: it selects the more pessimistic of the two branches. With $A_t \gt 0$ that caps the reward for raising $r_t$. With $A_t \lt 0$ the unclipped term actually grows as $r_t$ falls, so the $\min$ is what stops the objective from paying you to drive the probability toward zero. Clipping only ever removes incentive. It never manufactures one.
What this actually tells you, honestly. PPO does not implement a trust region. It approximates the behaviour of one. A bounded ratio does not imply bounded KL, and because clipping works by zeroing gradients, it removes the very corrective signal that would pull a policy back once it has escaped. TRPO's monotonic improvement guarantee does not survive the substitution. PPO won anyway, because it is a few lines of code instead of a constrained optimiser and it works nearly as well in practice. That is a fair trade, but knowing it is a trade is the part worth carrying into an interview. It is also why serious implementations still monitor KL directly and stop early when it drifts.

05Why bootstrapping wins, and why PPO clips

Two "why" questions come up constantly, and both have a clean answer.

Why does TD/Bellman-style bootstrapping usually beat waiting for the full Monte Carlo return? The two differ in what they use as the learning target.

  • Monte Carlo waits for an entire episode to finish, then uses the actual observed total reward. That target is unbiased, since it is the real quantity you're trying to estimate and not an approximation of it. It is also extremely noisy, because it depends on every random action and every random environment transition for the rest of the episode.
  • Temporal-difference (TD) doesn't wait and bootstraps instead, using the immediate reward plus its own current estimate of the value of the next state, exactly as the Bellman equation describes. That estimate is biased (it depends on a value function that's still being learned and is currently wrong), but it has far lower variance, because it only depends on one step of randomness instead of an entire trajectory's worth.

In practice, lower variance usually wins. TD methods learn faster and more stably from the same amount of experience. TD also has a structural advantage Monte Carlo cannot offer: it works in continuing tasks that never terminate, since it never needs to wait for an episode to end at all.

Why does PPO's clipping specifically matter? Without it, a single large policy-gradient update (triggered by, say, one unusually high-advantage rollout) can shove the policy into a region of action-space it has never explored well.

Once there, the next batch of rollouts is collected under this new, poorly-understood policy, the advantage estimates for that batch are unreliable, and the next update compounds the damage. This failure mode happens in practice.

An RL run can look like it's training fine and then collapse in a handful of updates with no way back. The very data being used to compute the next gradient step is generated by the policy that just got wrecked.

Clipping prevents the update from moving the probability ratio more than $\epsilon$ away from 1, which caps how far any single step can push the policy.

That gives most of the stability guarantee TRPO achieved with an explicit, expensive trust-region constraint, but computed with a simple min-and-clip on scalar ratios and with no constrained optimization problem. PPO displaced almost everything before it because it offers close to TRPO's stability at a small fraction of the implementation complexity.

06Families of methods, and where each one fails

Value-based (DQN)Policy gradient (REINFORCE / PPO)Actor-critic
What it learns directlyQ(s,a) for every actionThe policy π(a|s) itselfBoth a policy (actor) and a value function (critic)
Sample efficiencyHigher — off-policy, reuses old data via a replay bufferLower — classic REINFORCE is on-policy and high-varianceBetter than plain REINFORCE — the critic reduces variance
Training stabilityCan be unstable without replay buffer + target networkHigh variance gradients; PPO's clipping tames this substantiallyGenerally the most practical stability/efficiency balance
Action space fitNaturally discrete — argmax over a finite action setWorks for both, but especially natural for continuous actionsBoth — this is why it's the dominant modern recipe

Actor-critic is best understood as the direct fusion of the other two ideas. Two components, two jobs:

  • The actor is the policy, improved by a policy-gradient update. It uses the advantage the way REINFORCE uses raw return.
  • The critic is a learned value function, used in place of the raw, noisy Monte Carlo return to judge how good each action was. It supplies a low-variance advantage estimate the way TD learning does.

PPO is, precisely, an actor-critic method with a clipped policy-gradient update for the actor.

Where training breaks

  • Sparse reward environments. If the agent almost never receives a nonzero reward (reach the end of a long maze, win a long game), then for a long stretch of training every action looks equally (un)informative, and gradient-based learning has almost nothing to learn from. This is the credit-assignment problem at its worst, and it is why reward shaping, curiosity-driven exploration bonuses, and hierarchical RL exist as whole subfields.
  • Reward hacking. The agent optimizes what you told it to reward, which may differ from what you wanted. Think of a boat-racing agent that spins in circles collecting the same power-up over and over instead of finishing the race, because that scores higher under the literal reward function. Any time a reward function is a proxy for the real goal, and it almost always is, the optimizer will find the gap between the proxy and the goal if the gap is exploitable.
  • Offline RL's distribution-shift trap. Offline RL means learning a policy from a fixed, previously-collected dataset with no further interaction with the real environment at training time. The danger is that the learned policy can drift toward actions the data-collecting policy rarely or never took. For those actions, the training data gives no reliable signal about what would happen. The value function's estimate there can be badly wrong and badly overoptimistic, with no way to catch the error since there's no environment left to test against.
Common misconception

"DQN is just Q-learning with a neural network instead of a table" understates the change. Q-learning's convergence guarantees assume you're doing exact, tabular updates. Swap in a neural network and two things break: the updates become correlated (consecutive experiences are highly similar), and the target you're regressing toward moves every time you update the network. The target moves while the network chases it. DQN needed two specific fixes for this, covered below, on top of a bigger function approximator.

07Build this

Why DQN needs a replay buffer and a target network

Interviewers often ask why each fix exists, and each one solves a distinct instability.

Training a neural network with plain gradient descent assumes roughly independent, identically distributed training examples. But an agent playing through an environment produces a highly correlated stream. Consecutive frames of the same game look nearly identical. Training on them in that order badly biases the network toward whatever situation the agent happens to be in right now, then violently biases it toward the next situation.

A replay buffer breaks this correlation: store past transitions (state, action, reward, next state) in a large buffer, and train on randomly sampled batches from it and not on the live stream. It also improves sample efficiency, since each transition can be reused in many updates instead of being seen once and discarded.

Separately, Q-learning's update target is itself built from the network's own current Q-value estimate at the next state. You're regressing the network toward a target that's computed by the same network you're currently updating. Every gradient step changes the network, which changes the target, which changes what "correct" means for the very next step. The target keeps moving, so training can oscillate or diverge and may never settle.

DQN fixes this by keeping a separate target network, a lagging copy of the main network's weights that's only updated (or slowly blended in) every so many steps. The target used for each update comes from this frozen copy, so the regression target stays stable for a while, giving the main network something fixed to converge toward before the target itself shifts.

Both of those fixes are repairs to a method that already works in a table, with no network anywhere in it. Print that table after every episode and the Bellman equation stops being an equation.

Project Print a Q-table and watch value spread backwards ~2 hours · NumPy

Run tabular Q-learning on a 5×5 gridworld small enough to print in full. It uses no network, no Gym and no replay buffer, so the entire value function fits on one screen. You can watch reward travel backwards out of the goal cell and do not have to infer it from a reward curve.

  1. Write the world in about thirty lines. A 5×5 grid, start in one corner, goal in the opposite one, four actions, walls that clamp. Reward is +1 for entering the goal and 0 everywhere else. Put one pit next to the shortest route, worth -1 and ending the episode.
  2. Make Q a zeros array of shape (25, 4). Write the update yourself from the Bellman equation in section 04: $Q(s,a) \leftarrow Q(s,a) + \alpha\big[r + \gamma \max_{a'} Q(s',a') - Q(s,a)\big]$. That one line is all the learning in the project.
  3. Train with ε-greedy action selection. After every episode, print $\max_a Q(s,a)$ for all 25 cells as a grid of two-decimal numbers. Then read the printouts in order.
  4. Read the greedy policy off the finished table with an argmax per cell, and print it as arrows.
  5. Break it by setting $\gamma = 0$, retraining from zeros and printing the same grids.
  6. Restore $\gamma$ and make the world stochastic instead. Give every action some chance of slipping into a random direction. First run the deterministic table's arrows in this world, then retrain in it and compare the two arrow maps.
You'll know it worked when the printed grids sit flat at zero, away from the pit, until the agent first stumbles into the goal. After that a nonzero region starts spreading out of the goal cell, roughly a ring at a time. Each backup can only carry information one step, so the wave is slow enough to watch its edge advance. Nothing in your code draws that wave. It is one line of Bellman backup, iterated.
What the two breakages teach. With $\gamma = 0$ the update collapses to $Q(s,a) \leftarrow r$. Only transitions that touch the goal or the pit are ever worth anything. The wave never starts, and the rest of the table stays at zero. The discount is what carries reward backwards at all. It is not a knob for how far-sighted the agent feels. Then the slip. The tight route past the pit stops being the best route. Section 04's expectation is now averaging over transitions that can shove you in. A table trained in the deterministic world still recommends that route and takes the penalty. Retrain under slip and the arrows should bend away from the pit.

Where this runs in production

The most common place PPO shows up in a 2026 interview is RLHF. A reward model, trained on human preference comparisons, stands in for the reward function. The language model itself is the policy. Generating a response token-by-token is the sequence of actions.

PPO takes the update step described above (improve the policy, but clip so it doesn't move too far from the previous policy in one step) with a KL penalty added on top, to keep the fine-tuned model from drifting too far from the original supervised-fine-tuned model it started from.

Page 10 covers RLHF's full pipeline in depth: reward modeling, the KL penalty, RLHF vs. DPO. What matters here is that everything RLHF does with PPO applies the MDP, advantage, and clipped-objective machinery on this page directly, with no new machinery at the LLM layer.

08Interview questions

BeginnerWhat's the difference between V(s) and Q(s,a)?

V(s) is the expected discounted return from state s, assuming the agent acts optimally (or under a fixed policy) from here on — a property of the state alone. Q(s,a) is the expected discounted return from taking a specific action a in state s, then acting optimally afterward — a property of the state-action pair. The difference is exactly one decision: Q fixes the first action for you, V assumes the best one is already chosen. Advantage, Q(s,a) − V(s), measures how much better this specific action is than the state's average.

BeginnerWhy do we discount future rewards?

Two reasons. A reward now is worth more than an identical reward later because of uncertainty — the environment could change, the episode could end, you might never actually collect it. And mathematically, discounting keeps the sum of an infinite sequence of future rewards finite (a convergent geometric series) rather than diverging, which is necessary to even compare policies in tasks with no natural end point.

IntermediateWhy does DQN need a replay buffer and a target network?

The replay buffer breaks the correlation between consecutive experiences in a live trajectory by sampling random past transitions to train on, which both stabilizes gradient descent (which assumes roughly i.i.d. data) and reuses data for better sample efficiency. The target network fixes a different problem: Q-learning's update target is built from the network's own current estimate at the next state, so without a frozen, lagging copy of the network to compute that target from, the regression target shifts every single update step, which can cause oscillation or divergence instead of convergence.

IntermediateMonte Carlo vs. temporal-difference learning — what's the actual tradeoff?

Monte Carlo waits for a full episode to end and uses the actual observed return as the learning target — unbiased, but high variance, since it depends on every random action and transition for the rest of the episode. TD bootstraps: it uses one real reward plus the current value estimate of the next state (the Bellman equation) as the target — biased, because that value estimate is still being learned and is currently wrong, but much lower variance, since it only depends on one step of randomness. In practice TD usually wins because lower variance means faster, more stable learning from the same data, and TD also works in continuing tasks that never terminate, which Monte Carlo structurally cannot handle.

IntermediateWhat does the advantage function buy you that raw reward doesn't?

Raw reward (or raw return) tells you an action's absolute outcome, which is noisy and depends heavily on which state you happened to be in. Advantage, Q(s,a) − V(s), tells you how much better or worse this action was than what you'd expect on average from this state — a relative, baseline-subtracted signal. Policy gradients that use advantage instead of raw return have substantially lower variance, because subtracting a well-chosen baseline (the state's own value) removes a large chunk of noise that doesn't actually depend on which action was chosen.

DeepExplain PPO's clipped objective and why it matters.

PPO computes the probability ratio between the new and old policy for the action taken, multiplies it by the estimated advantage, and takes the minimum of that unclipped term and a version where the ratio is clipped to [1−ε, 1+ε]. The min means clipping only ever removes the incentive to keep pushing the policy further once it's already moved enough — it never creates a new incentive to push harder. This matters because unconstrained policy-gradient updates can move the policy so far in one step that it starts producing actions unlike anything in its training data, destabilizing all subsequent updates (which use rollouts from that now-broken policy) with no way to recover. PPO gets most of TRPO's stability guarantee — bounding how far the policy moves per update — using a simple clip-and-min on scalar ratios instead of an explicit, expensive trust-region constraint, which is exactly why it displaced TRPO as the default and is the algorithm underneath RLHF.

DeepWhy might you reach for SAC or TD3 instead of PPO for a continuous-control robotics task?

SAC adds an entropy bonus to the objective, explicitly rewarding the policy for staying stochastic/exploratory rather than collapsing too early to a narrow, possibly-suboptimal behavior — valuable in continuous action spaces where naive exploration is easy to get stuck in. TD3 (and SAC) both use twin critics — train two separate Q-function estimators and take the minimum of the two when computing targets — specifically to counter the systematic overestimation bias that a single Q-function tends to develop, since taking a max/argmax over a noisy estimate tends to pick out the noise, not the true best action. Both are also typically more sample-efficient than PPO in continuous control because they're off-policy and can reuse a replay buffer, whereas PPO is on-policy and needs comparatively fresh rollouts.

DeepWhat's the core danger in offline RL, and why doesn't it show up in online RL the same way?

Offline RL learns entirely from a fixed, previously-collected dataset with no further environment interaction. If the learned policy drifts toward actions rarely or never taken by the policy that collected the data, the value function has no reliable evidence for what happens there and can become badly overoptimistic about exactly the actions the policy is being pushed toward — a compounding error with nothing to correct it, since there's no environment left to test against. Online RL doesn't have this problem in the same way because the agent can always go collect more real data about whatever action it's currently curious about, which grounds its estimates; offline RL loses that safety net entirely, which is why offline algorithms explicitly constrain the learned policy to stay close to the data-collecting policy's behavior.

09Go deeper

●Now write it yourself

Reading the derivation and being able to produce it are different skills. These are Deep-ML problems that exercise what this page covers — each one is checked against real test cases, not multiple choice.

Matched to this page from Deep-ML's catalogue of 1,380 problems. More at deep-ml.com, and Where to practise covers the other platforms and what each one trains.

Other techniques for this problem

A scoped slice of the full Technique Map — every technique this page covers, grouped by what it solves.

My Notes — 15 Reinforcement Learning

Free notes

Highlights on this page