Session #12: Reinforcement learning
Free University Berlin
2026-04-26
Game plan
This lecture provides an introduction to the Reinforcement Learning with an emphasis on its use to train LLMs
image source: Reinforcement Learning: An Introduction, Richard Sutton and Andrew G. Barto
\[ P(s_{t+1} | s_t, a_t) \tag{1}\]
image source: https://www.freecodecamp.org/
Intuitively, we can conceptualize RL as a trial and error process. As our agent acts in its environment, the training process reinforces—either positively or negatively—observed behavior via the reward. Hence, the name “reinforcement” learning.
Training:
| Question (Prompt) | Answer 1 | Answer 2 | Chosen |
|---|---|---|---|
| Where is Jena? | Niedersachsen | Thüringen | 2 |
| What is 2+3? | 5 | 42 | 1 |
| Explain quantum mechanics | It’s about cats | Quantum mechanics is the rulebook for super tiny things that act like magic. | 2 |
A trajectory, represented by tau (\(\tau\)), is a series of (state, action, reward) – see slide 7.
The probability of a trajectory \(\tau\) given a policy \(\pi\) is thus modeled as the probability of the initial state multiplied by the product of the transition probability and the policy for each step: \[ P(\tau) = \rho(s_0)\prod_{t=o}^{T-1}P(s_{t+1}\mid s_t,a_t)\pi(a_t\mid s_t) \tag{2}\]
We will present Policy gradient optimization, showing how to modify the gradient to simplify the expression and reduce variance (which tends to improve convergence of training)
It is important to keep the distinction between these three concepts clear!
\[ \begin{align} \nabla_{\theta}J(\pi) &=\nabla_{\theta}\mathbb{E}_{\tau\sim \pi}\left[R(\tau)\right] & \bullet \text{take gradient of expected return}\\ &=\nabla_{\theta}\int_{\tau}P(\tau\mid\theta) R(\tau) &\bullet \text{definition of expected return}\\ &=\int_{\tau}\nabla_{\theta} P(\tau\mid\theta) R(\tau) & \bullet \text{linearity of gradient operator}\\ &=\int_{\tau} P(\tau\mid\theta) \nabla_{\theta} \log P(\tau\mid\theta) R(\tau) & \bullet \text{log derivative trick}\\ \end{align} \] - To understand the log derivative trick, consider that \(\tfrac{d}{dx}\log f(x) = \tfrac{1}{f(x)}\tfrac{d}{dx}f(x)\)
\[ \begin{align} \nabla_{\theta}J(\pi_{\theta}) &=\int_{\tau} P(\tau\mid\theta) \nabla_{\theta} \log P(\tau\mid\theta) R(\tau)&\\ &=\mathbb{E}_{\tau\sim \pi}\left[ \underbrace{\nabla_{\theta} \log P(\tau\mid\theta)}_{\text{let's focus on this}} R(\tau)\right] &\bullet \text{equivalent representation as an expectation}\\ \end{align} \]
\[ \begin{align} \nabla_{\theta}\log P(\tau) &= \nabla_{\theta} \log \left[ \rho(s_0)\prod_{t=0}^{T-1}P(s_{t+1}\mid s_t,a_t)\pi_{\theta}(a_t\mid s_t)\right]&\\ &= \underbrace{\nabla_{\theta} \log\rho(s_0)}_{=0} + \sum_{t=0}^{T-1}\left[\underbrace{\nabla_{\theta} \log P(s_{t+1}\mid s_t,a_t)}_{=0} + \nabla_{\theta} \log\pi_{\theta}(a_t\mid s_t)\right]& \dagger\dagger\\ &= \sum_{t=0}^{T-1} \nabla_{\theta} \log\pi_{\theta}(a_t\mid s_t)\\ \end{align} \]
\[ \begin{align} \nabla_{\theta}J(\pi_{\theta}) &=\int_{\tau} P(\tau\mid\theta) \nabla_{\theta} \log P(\tau\mid\theta) R(\tau) &\\ &=\mathbb{E}_{\tau\sim \pi_{\theta}}\left[ \nabla_{\theta} \log P(\tau\mid\theta)R(\tau)\right] & \bullet\text{by definition of the expectation} \\ &=\mathbb{E}_{\tau\sim \pi_{\theta}}\left[ \sum_{t=0}^{T-1} \nabla_{\theta}\log\pi_{\theta}(a_t\mid s_t)\,R(\tau)\right] & \bullet\text{substituting from previous slide} \\ \end{align} \] - So, we have an expression to calculate the gradient, but, we are still taking the expectation over all possible trajectories: \(\tau\sim \pi_{\theta}\) - If we have a trajectory of 100 tokens and a vocabulary of 100,000 tokens, there are \[ 100^{100000}\quad\text{possible trajectories} \]
It is clearly not tractable to calculate all \(100^{100000}\) trajectories…
Instead, we generate a sample \(\mathcal{D}\) of trajectories and calculate the sample mean \[ \hat{g} = \frac{1}{|\mathcal{D}|} \sum_{\tau\in\mathcal{D}}\sum_{t=0}^{T-1} \nabla_{\theta} \log \pi_{\theta}(a_t\mid s_t)R(\tau) \tag{4}\]
and then use this to update the parameters \[ \theta_{k+1} = \theta_k + \eta\hat{g} \]
This is known as stochastic gradient ascent.
The gradient typically would be calculated using automatic differentiation by pytorch (autograd)
The so-called reinforce algorithm repeatedly performs rounds of stochastic gradient ascent, updates parameters, runs \(|\mathcal{D}|\) trajectories, etc.
Reward to go
A “trick” to reduce the variance of policy gradient.
The policy gradient theorem:
\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\left[\nabla_\theta \log \pi_\theta(\tau) \, R(\tau)\right]\]
Trajectory probability factors as:
\[p(\tau) = p(s_0)\prod_{t=0}^T \pi_\theta(a_t|s_t)\,p(s_{t+1}|s_t,a_t)\]
Only the policy terms depend on \(\theta\), so:
\[ \nabla_\theta \log \pi_\theta(\tau) = \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) \]
This gives the REINFORCE estimator:
\[ \nabla_\theta J(\theta) = \mathbb{E}_\tau\left[\left(\sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t)\right)\left(\sum_{t'=0}^T r_{t'}\right)\right] \tag{5}\]
Every log-probability term \(\nabla_\theta \log\pi_\theta(a_t|s_t)\) in Equation 5 gets multiplied by the entire trajectory return — including rewards \(r_{t'}\) that occurred before \(a_t\) was taken.
Note
Action \(a_t\) cannot have caused a reward that already happened.
\[ \nabla_\theta J(\theta) = \sum_{t=0}^T \mathbb{E}_\tau\left[\nabla_\theta \log\pi_\theta(a_t|s_t)\left(\underbrace{\sum_{t'<t} r_{t'}}_{\text{past}} + \underbrace{\sum_{t'\geq t} r_{t'}}_{\text{future}}\right)\right] \]
For \(t' < t\), \(r_{t'}\) is fixed once we condition on the trajectory prefix up to \(s_t\) (i.e., \(s_{0:t},a_{0:t-1}\)). ::: {.small-text} Tower rule - The overall average of something can be obtained by first averaging within groups, then averaging those group-averages together. \[ \mathbf{E}\left[ X \right] = \mathbf{E}\left[ \mathbf{E}\left[X\mid Y\right] \right] \] - We’ll use this with \(Y\) = “the trajectory prefix up to \(s_t\)” and \(X\) = the term we’re taking expectation of — averaging over \(a_t\) first, then over everything before it. :::
-By the tower rule:
\[\mathbb{E}_\tau\left[\nabla_\theta\log\pi_\theta(a_t|s_t)\, r_{t'}\right]\] \[= \mathbb{E}_{s_{0:t},a_{0:t-1}}\Big[ \underbrace{\mathbb{E}_{a_t\sim\pi_\theta(\cdot|s_t)}\big[\nabla_\theta\log\pi_\theta(a_t|s_t)\, r_{t'} \mid s_{0:t},a_{0:t-1}\big]}_{\text{average over } a_t \text{ only}}\Big]\]
Since \(r_{t'}\) is already fixed given the prefix, it pulls out of the inner average:
\[= \mathbb{E}_{s_{0:t},a_{0:t-1}}\Big[ r_{t'} \cdot \mathbb{E}_{a_t\sim\pi_\theta(\cdot|s_t)}\big[\nabla_\theta\log\pi_\theta(a_t|s_t)\mid s_t\big]\Big]\]
The inner expectation is the score-function identity.
The Proof slide showed that each past term reduces (via the tower rule) to \(r_{t'} \cdot \mathbb{E}_{a_t\sim\pi_\theta(\cdot|s_t)}\big[\nabla_\theta\log\pi_\theta(a_t|s_t)\mid s_t\big]\). We now show that factor is exactly zero.
By definition of expectation for a discrete random variable:
\[ \begin{align} \mathbb{E}_{a\sim\pi_\theta(\cdot|s)}\left[\nabla_\theta \log\pi_\theta(a|s)\right] &= \sum_a \pi_\theta(a|s)\, \nabla_\theta \log\pi_\theta(a|s) &\bullet \text{by definition} \\ & = \sum_a \pi_\theta(a|s)\,\frac{\nabla_\theta \pi_\theta(a|s)}{\pi_\theta(a|s)}&\bullet \tfrac{d}{dx}\log f(x) = \frac{1}{f(x)} \tfrac{d}{dx}f(x)\\ &= \sum_a \nabla_\theta \pi_\theta(a|s) &\bullet \text{canceling} \\ &=\nabla_\theta \sum_a \pi_\theta(a|s) &\bullet \text{linearity of gradient operator} \\ &=\nabla_\theta 1 &\bullet \text{definition of probability} \\ &= 0 \end{align} \]
\[ \nabla_\theta J(\theta) = \mathbb{E}_{\tau\sim\pi_\theta}\left[\sum_{t=0}^T \left( \nabla_\theta\log\pi_\theta(a_t|s_t)\right) \cdot \underbrace{\sum_{t'=t}^{T} r_{t'}}_{G_t \;=\; \text{reward-to-go}}\right] \tag{6}\]
\(G_t\) is the return accumulated from time \(t\) onward, not the full trajectory return. - Rewards to go means that at each step, we only focus on rewards that are not in the past! - This makes sense, because a reward cannot alter the rewards we received in the past!
\[A_t = G_t - b(s_t)\]
\(G_t\): Intuitively, \(G_t\) has some baseline level that represents the goodness of the state as well as a component that is specific to the component taken.
How do we capture the idea of a baseline: good vs. bad state?
As a baseline, we will choose the value function \(V^{\pi}(s_t)\) - If we start from state \(s_t\), what is the future expected reward of trajectories starting from state \(s_t\) and following policy \(\pi_{\theta}\)?
What is the capital of Niedersachsen? The capital is
In contrast, if for some reason our current state is
What is the capital of Niedersachsen? Cats are selfish because
\[ Q^{\pi}(s,a) = \mathbb{E}_{\tau\sim\pi}\left[ \sum_{t'}^T r(s_{t'},a_{t'}) \big\mid s_t=s,a_t=a \right] \]
State-value \(V^\pi(s)\): expected return starting in state \(s\), following \(\pi\) thereafter.
\[V^\pi(s) = \mathbb{E}_{\tau\sim\pi}\left[\sum_{t'=t}^{T}\gamma^{t'-t} r_{t'} \,\Big|\, s_t=s\right]\]
Action-value \(Q^\pi(s,a)\): expected return starting in state \(s\), taking action \(a\), then following \(\pi\).
\[Q^\pi(s,a) = \mathbb{E}_{\tau\sim\pi}\left[\sum_{t'=t}^{T}\gamma^{t'-t} r_{t'} \,\Big|\, s_t=s,\, a_t=a\right]\]
Note
\(Q\) conditions on the action too; \(V\) averages it out over the policy’s own action distribution.
\(V\) is \(Q\) with the action integrated out, weighted by the policy — this is the tower rule again, conditioning on \(a_t\) instead of the trajectory prefix:
\[V^\pi(s) = \mathbb{E}_{a\sim\pi(\cdot|s)}\left[Q^\pi(s,a)\right] = \sum_a \pi(a|s)\,Q^\pi(s,a)\]
Where \(G_t\) fits in: \(G_t\) is a single Monte Carlo sample of the return from one rollout; \(Q^\pi(s_t,a_t)\) is its expectation over all rollouts from \((s_t,a_t)\):
\[Q^\pi(s_t,a_t) = \mathbb{E}_{\tau\sim\pi}[G_t \mid s_t,a_t]\]
\(G_t\) is a noisy, unbiased estimator of \(Q^\pi(s_t,a_t)\) — replacing \(G_t\) with a learned \(\hat Q_\phi(s,a)\) trades a little bias for much lower variance.
\(G_t\) is a sum over an entire trajectory. Split off just the first term:
\[Q^\pi(s,a) = \mathbb{E}_{\tau\sim\pi}\left[r(s_t,a_t) + \gamma\sum_{t'=t+1}^{T}\gamma^{t'-t-1} r(s_{t'},a_{t'}) \,\Big|\, s_t=s,\, a_t=a\right]\]
Condition the remaining sum on the next state \(s_{t+1}=s'\sim p(\cdot|s,a)\) (environment dynamics), then the next action \(a_{t+1}=a'\sim\pi(\cdot|s')\):
\[Q^\pi(s,a) = r(s,a) + \gamma\,\mathbb{E}_{s'\sim p(\cdot|s,a)}\Big[\,\mathbb{E}_{a'\sim\pi(\cdot|s')}\big[\underbrace{\mathbb{E}_\tau[\cdots \mid s_{t+1}=s',a_{t+1}=a']}_{=\,Q^\pi(s',a')\text{ by definition, shifted one step}}\big]\Big]\]
Note
Same trick as the Proof slide: average in stages — first over what happens after \((s',a')\), then over \(a'\), then over \(s'\).
Collapsing the nested expectations gives the recursive (Bellman) form:
\[Q^\pi(s,a) = r(s,a) + \gamma\,\mathbb{E}_{s'\sim p(\cdot|s,a)}\left[\mathbb{E}_{a'\sim\pi(\cdot|s')}\big[Q^\pi(s',a')\big]\right]\]
Note
Note: \(r(s,a)\) sits outside the \(s'\)-expectation here since it depends only on \((s,a)\). If your reward convention is \(r(s,a,s')\) instead, it moves inside.
Put \(Q^\pi(s_t,a_t)\) in place of \(G_t\), and \(V^\pi(s_t)\) in place of the baseline \(b(s_t)\):
\[A^\pi(s_t,a_t) \;=\; Q^\pi(s_t,a_t) - V^\pi(s_t)\]
“How much better or worse than the policy’s own average behavior is this specific action?”
Note
A state that’s great regardless of the action taken (all actions have high \(Q\)) contributes no gradient signal for any particular action — correctly, since the policy doesn’t need to change anything there.
From the deck: \(A_t = G_t - b(s_t)\), with \(b(s_t) = V^\pi(s_t)\).
Since \(\mathbb{E}[G_t\mid s_t,a_t] = Q^\pi(s_t,a_t)\):
\[A_t = G_t - V^\pi(s_t) \quad\text{is a (noisy) unbiased estimator of}\quad A^\pi(s_t,a_t) = Q^\pi(s_t,a_t) - V^\pi(s_t)\]
Same object — sample vs. expectation. This is exactly what appears in A2C / PPO’s objective.
The same zero-mean identity justifies subtracting a baseline \(b(s_t)\) (commonly \(V^\pi(s_t)\)):
\[A_t = G_t - b(s_t)\]
\[\mathbb{E}\left[\nabla_\theta\log\pi_\theta(a_t|s_t)\,b(s_t)\right] = 0\]
This is the standard justification for A2C / PPO-style advantage estimators.
How do we capture the idea of a baseline: good vs. bad state? As a baseline, we will choose the value function \(V^{\pi}(s_t)\)
What is the capital of Niedersachsen? The capital is
In contrast, if for some reason our current state is
What is the capital of Niedersachsen? Cats are selfish because
\(V\) is \(Q\) with the action integrated out, weighted by the policy — this is the tower rule again, conditioning on \(a_t\) instead of the trajectory prefix:
\[V^\pi(s) = \mathbb{E}_{a\sim\pi(\cdot|s)}\left[Q^\pi(s,a)\right] = \sum_a \pi(a|s)\,Q^\pi(s,a)\]
Where \(G_t\) fits in: \(G_t\) is a single Monte Carlo sample of the return from one rollout; \(Q^\pi(s_t,a_t)\) is its expectation over all rollouts from \((s_t,a_t)\):
\[Q^\pi(s_t,a_t) = \mathbb{E}_{\tau\sim\pi}[G_t \mid s_t,a_t]\]
\(G_t\) is a noisy, unbiased estimator of \(Q^\pi(s_t,a_t)\) — replacing \(G_t\) with a learned \(\hat Q_\phi(s,a)\) trades a little bias for much lower variance.
Put \(Q^\pi(s_t,a_t)\) in place of \(G_t\), and \(V^\pi(s_t)\) in place of the baseline \(b(s_t)\):
\[A^\pi(s_t,a_t) \;=\; Q^\pi(s_t,a_t) - V^\pi(s_t)\]
“How much better or worse than the policy’s own average behavior is this specific action?”
Note
A state that’s great regardless of the action taken (all actions have high \(Q\)) contributes no gradient signal for any particular action — correctly, since the policy doesn’t need to change anything there.
From the deck: \(A_t = G_t - b(s_t)\), with \(b(s_t) = V^\pi(s_t)\).
Since \(\mathbb{E}[G_t\mid s_t,a_t] = Q^\pi(s_t,a_t)\):
\[A_t = G_t - V^\pi(s_t) \quad\text{is a (noisy) unbiased estimator of}\quad A^\pi(s_t,a_t) = Q^\pi(s_t,a_t) - V^\pi(s_t)\]
Same object — sample vs. expectation. This is exactly what appears in A2C / PPO’s objective.
The same zero-mean identity justifies subtracting a baseline \(b(s_t)\) (commonly \(V^\pi(s_t)\)):
\[A_t = G_t - b(s_t)\]
\[\mathbb{E}\left[\nabla_\theta\log\pi_\theta(a_t|s_t)\,b(s_t)\right] = 0\]
This is the standard justification for A2C / PPO-style advantage estimators.
# G_t (or A_t) is treated as a fixed scalar coefficient —
# NOT something we backprop through
returns = compute_reward_to_go(rewards, gamma) # tensor
returns = returns.detach()
log_probs = policy_dist.log_prob(actions) # differentiable
loss = -(log_probs * returns).mean()
loss.backward() # autograd differentiates only log pi_theta
optimizer.step()Autograd differentiates only \(\log\pi_\theta(a_t|s_t)\) through the policy network — \(G_t\) is .detach()-ed.