← All tutorials

Policy Gradients: REINFORCE, Actor-Critic and PPO

Learn the move itself, judge it with a critic, and keep each update small.

On this page

1 Policy-based methods 2 Actor-critic methods 3 PPO clipping 4 References
policy π, baseline optionalwhole-episode returnsREINFORCEpolicy + critic V(s)on-policyA2C, A3CPPOactor + critic Q(s, a)off-policy, continuousDDPG, TD3SAC
Figure 1. What each method learns besides the policy; names link their sections.

These methods learn the policy, the rule that picks the move, directly: a network turns the state into move probabilities, or into the action itself, and training shifts the policy toward moves that led to more reward. REINFORCE learns once per episode; actor-critic adds a critic that judges every step; PPO, proximal policy optimization, reuses each batch for several small updates. All three are on-policy: they learn from moves the current policy just made. Moves that are real numbers, such as a robot’s joint torques, are on Continuous-Action Actor-Critic: TD3 and SAC.

1 Policy-based methods

A policy-based agent skips the scores. Its network turns the state into a probability for each move, and the agent samples from them. Sampling matters when any fixed move can be beaten, as in rock-paper-scissors, where the best play is each move a third of the time (Prisner 2014).

Take a language model writing a reply to the prompt of Aligning Language Models with RL, “Explain the moon landing to a 6 year old”. It picks the first token of its reply from three candidates; replies starting with “People” score best on average (toy scores, Figure 2). The reward here is a reward model’s score of the finished reply, and γ is 1 inside one reply; a score averaged over replies is Q, one reply’s score is G.

0.5PeopleQ = 50.3TheQ = 40.2ExplainQ = 3averagescore 4.3People drawn+0.5−0.3−0.2The drawn−0.5+0.7−0.2Explain drawn−0.5−0.3+0.8average push+0.35−0.09−0.26
Figure 2. Three first tokens: each sample’s push, and in bold their weighted average.
This is the policy gradient. J, the policy’s quality, is the chance-weighted average of the Q-values; to raise it, push each token’s score by how often it is sampled times how good it is. The log-probability’s gradient gives that push from single samples.
π(a |⁠ s) = ezaΣb ezb
Softmax (Softmax, Temperature and Decoding). za is the token’s score from the network, a logit: 0, −0.51 and −0.92 in Figure 2. ez makes it positive, and dividing by the sum over every token b makes the chances add to 1: 0.5, 0.3 and 0.2.
J = Σa π(a |⁠ s) · Q(s, a)
J, the objective, is the average return from this state: each token’s Q-value weighted by its chance, 4.3 in Figure 2. It is the value V(s) of RL Basics written with the policy’s chances.
∇ log π(a |⁠ s) = ∇π(a |⁠ s)π(a |⁠ s)
The log-derivative. ∇ reads “how it changes as the scores z change”; backpropagation carries that on to the network’s weights. log is the natural log. It turns the push on π into that push divided by π, so a token sampled often gets a smaller push per sample: frequent sampling and small push cancel. Each row of Figure 2 is this push for one drawn token.
∇J =
Σa π(a |⁠ s) · ∇ log π(a |⁠ s) · Q(s, a)
The policy gradient. Multiplying line 3 by π gives back ∇π, so this is the change of line 2 with Q held fixed; the policy gradient theorem says the same form holds over whole episodes (Sutton and Barto’s RL textbook, 2018, chapter 13). Together: the average push of single samples, each weighted by its Q, is the direction that raises J, Figure 2’s bold row. Because each term is weighted by π, line 4 is an average over sampled tokens, so one sampled token’s ∇ log π times Q is a fair one-sample estimate: the log form lets the agent learn from samples alone. Builds on the return and the value of RL Basics; REINFORCE, next, samples this line, and PPO’s ratio in Section 3 returns to line 3.

REINFORCE (Williams 1992), the simplest policy-based method, learns once per episode. After one reply, REINFORCE pushes the token it sampled by the reply’s score (Figure 3).

beforeafter one update0.5People0.3The0.2Explain0.41People0.40The0.19Explainsampled: The, reply score G = 4, step 0.1
Figure 3. Token chances before and after one REINFORCE update.
This is REINFORCE: after each episode, make every move more likely in proportion to the return that followed it. Over many episodes the better moves rise most.
loss = −Σt log π(at |⁠ st) · Gt
Σt adds over the episode’s steps, here each token of the reply. π(at | st) is the chance the policy gave the token written at step t (the bar reads “given”), and its log is pushed as in line 3 of the policy-gradient card. A reply’s chance is the product of its token chances, so its log is this sum over t. Gt is the return of RL Basics from step t on, one sample of Q: 4 for the reply in Figure 3. The minus sign turns “raise” into a loss to minimize. Together: the sampled version of the policy gradient’s last line. Figure 3 shows its noise: The rose although People pays more, because every score here is positive. Builds on the policy-gradient card; the baseline, next, removes that noise.

One REINFORCE episode on gymnasium’s CartPole:

import gymnasium as gym
import torch
from torch.distributions import Categorical

gamma = 0.99
env = gym.make("CartPole-v1")
policy = torch.nn.Sequential(
    torch.nn.Linear(4, 128), torch.nn.ReLU(),
    torch.nn.Linear(128, 2))
optimizer = torch.optim.Adam(policy.parameters(), lr=1e-2)

s, info = env.reset()
log_probs, rewards, done = [], [], False
while not done:
    dist = Categorical(logits=policy(torch.as_tensor(s)))
    a = dist.sample()
    s, r, terminated, truncated, info = env.step(a.item())
    log_probs.append(dist.log_prob(a))
    rewards.append(r)
    done = terminated or truncated
G, returns = 0.0, []
for r in reversed(rewards):
    G = r + gamma * G
    returns.insert(0, G)
G_t = torch.tensor(returns)
loss = -(torch.stack(log_probs) * G_t).sum()
optimizer.zero_grad()
loss.backward()
optimizer.step()
  1. 1Sets γ, CartPole and a small policy network with its optimizer.
  2. 2Plays one full episode, keeping each move’s log-probability and reward.
  3. 3Computes the return Gt backward from the last reward.
  4. 4The loss from the REINFORCE card, then one gradient step.

Its weakness is noise: Gt includes the luck of every later reward, so updates vary a lot between episodes (the RL textbook, chapter 13). Subtract what the state usually returns, here J = 4.3, and the push keeps its direction with far smaller swings (Figure 4).

from G: spread 4.65from G − 4.3: spread 0.030PeopleTheExplainsame average +0.35the reply starting with The: chance 0.30 to 0.29
Figure 4. The push on People’s score from each sampled token, without and with the baseline.
This is the baseline: judge each token against what the state usually returns. The average direction stays and the swings shrink.
At = Gt − b(st)
The baseline b(st) is any guess that depends on the state but not on the move, usually a learned V; here J = 4.3. At, the advantage, says how much better the return was than that guess: +0.7, −0.3 and −1.3 for People, The and Explain, a move’s edge over the average as in the dueling card of Value-Based RL.
loss = −Σt log π(at |⁠ st) · At
The same loss as REINFORCE with At in place of Gt.
Σa ∇π(a |⁠ s) · b =
b · ∇ Σa π(a |⁠ s) = 0
The chances always add to 1, so their changes add to 0, and pushing every token by the same b changes nothing on average. Together: the same gradient as REINFORCE on average, with far less noise. The spread, the variance, is how far single pushes swing around their average: 4.65 without the baseline and 0.03 with it in Figure 4. Builds on REINFORCE and the policy gradient’s last line; the actor-critic in Section 2 makes b a learned critic.

Each reply’s score G is its token’s Q plus some noise. With no baseline every score here is above 0, so whichever token is drawn gets pushed up. A token drawn a few times early gains chance, gets drawn more and gains more, so over many replies a run (one training from the starting chances) can lock onto a worse token. Subtracting b keeps the average push but sets how far separate runs drift apart: they agree most when b sits near J, the state’s average score before any update, while a b far above J lets them drift almost as far as with no baseline, with almost every push down instead of up (Figure 5).

Figure 5. 1 One run. 2 Where 20 runs end. Scores illustrative.

A policy that puts almost all its chance on one token stops trying the others (Figure 6).

0.5, 0.3, 0.2entropy 1.03a third each1.10, the mostone certainentropy 0for 3 tokens, in nats
Figure 6. Three ways to spread chance over three tokens, and the entropy of each.
This is the entropy bonus: H measures how spread the chances are, in nats, the unit the natural log gives. Subtracting β · H from the loss pays for keeping options open.
H(π(· |⁠ s)) = −Σa π(a |⁠ s) · log π(a |⁠ s)
H, the entropy, measures how spread out the move probabilities are: largest when every move is equally likely, zero when one move is certain. The dot in π(· | s) stands for all the chances at once. Σa adds over the candidate tokens; π(a | s), each token’s chance, weights its term; log π(a | s), the natural log of that chance, is below 0 for any chance under 1 and lower for rarer tokens; the minus sign makes H positive.
losstotal = loss − β · Σt H(π(· |⁠ st))
β is a small weight, loss is the baseline loss above, and Σt adds the entropy over the steps of a reply. Subtracting the entropy rewards keeping options open. Together: H is 1.10 when three tokens share chance equally, 1.03 for Figure 2’s 0.5, 0.3, 0.2 and 0 when one token is certain (Figure 6), so subtracting β times it makes the policy pay for locking onto one token too early. Builds on the policy-gradient card; SAC on Continuous-Action Actor-Critic: TD3 and SAC pays it at every step.

1.1 REINFORCE in practice

Runsonce per episode, after it ends, no replay
Carriesthe policy network only, no value network
Costone forward pass per step while playing, one backward pass over the episode
Learning rateAdam step size; higher learns faster but can diverge, 0 never learns; 0.01 (PyTorch example)
γshrinks later rewards in Gt; lower favors near rewards, 0 counts only the next one, near 1 the whole episode; 0.99 (the PyTorch example also rescales returns, which the snippet skips)
Entropy βweights the bonus on the entropy card; higher keeps moves spread out, 0 lets the policy lock in early, too high keeps the policy random; 0.01, the entropy weight in CleanRL’s PPO (Section 3)
Codepytorch/examples reinforce.py
Gotchasnever detach the log-probabilities

2 Actor-critic methods

Waiting for the end of an episode to judge every move is slow and noisy. An actor-critic agent judges each move one step later, by whether things went better or worse than it expected.

In actor-critic, the actor is the REINFORCE policy. The critic V(s) is a learned baseline that estimates the return from a state. Scoring both the state before and the state after a move lets it judge that move one step later, not at the episode’s end (the RL textbook, chapter 13). After the token The, the critic’s guess for the rest of the reply drops from 4.3 to 3.8; that drop, δ, judges the token now (Figure 7).

state s actor π world critic V move: The r = 0s′ δ = −0.5 = 0 + 3.8 − 4.3 δ V(s) = 4.3V(s′) = 3.8γ = 1
Figure 7. δ from one step trains the actor (up arrow) and the critic itself (loop).
This is actor-critic: the critic’s guess replaces waiting for the reply’s end. One step’s surprise both judges the move (actor) and corrects the guess (critic).
A(s, a) = Q(s, a) − V(s)
A(s, a), the advantage, is the baseline card’s A with b = V and the average return Q(s, a) in place of one reply’s G: how much better move a is than the critic expects from s, −0.3 for The.
A ≈ δ = r + γ · V(s′) − V(s)
≈ reads “is estimated by”. δ is the TD error of RL Basics with V in place of Q: one real reward plus the discounted guess at s′, the Bellman target in words, minus the guess at s. In Figure 7, r is 0 because the reward model scores only the finished reply, γ is 1, and δ is −0.5. It estimates A from one step and is only as good as the critic: it misses the true −0.3 by 0.2. REINFORCE with a baseline used G − V(s), which waits for the episode’s end; δ is less noisy.
actor loss = −log π(a |⁠ s) · A
The baseline card’s loss for one step with A = δ: the move gets more likely only if it beat the guess.
critic loss = (r + γ · V(s′) − V(s))2
δ squared, a squared gap to a target, 0.25 here, so a miss in either direction counts. Making it smaller pulls V(s) toward one real reward plus the guess at s′, which is held fixed as the target (no_grad in the code). Together: REINFORCE with a learned per-step baseline. Builds on the baseline card and the update card of RL Basics; PPO in Section 3 uses this A, and Continuous-Action Actor-Critic: TD3 and SAC uses the same actor and critic with a real-number move.

One update on the last B steps the current policy played, where no_grad holds V(s′) fixed.

import torch
from torch.distributions import Categorical

gamma = 0.99

def actor_critic_update(actor, critic, optimizer, batch):
    s, a, r, s_next, done = batch   # tensors, B steps each
    v_s = critic(s).squeeze(-1)
    with torch.no_grad():
        v_next = critic(s_next).squeeze(-1)
    delta = r + gamma * v_next * (1 - done) - v_s
    A = delta.detach()
    log_pi = Categorical(logits=actor(s)).log_prob(a)
    actor_loss = -(log_pi * A).mean()
    critic_loss = delta.pow(2).mean()
    optimizer.zero_grad()
    (actor_loss + critic_loss).backward()
    optimizer.step()
  1. 1Critic values and δ, eq card in Section 2, line 2.
  2. 2Actor loss with A = δ, line 3, the up arrow in Figure 7.
  3. 3Critic loss, line 4, the loop in Figure 7 that trains the critic.
  4. 4One gradient step on both losses.

A3C, asynchronous advantage actor-critic (Mnih et al. 2016), runs many workers on copies of the world, all updating one shared network, and uses an n-step return (Figure 8) with the entropy bonus. A2C, advantage actor-critic, is the synchronous version: all copies step, then make one joint update.

rt+1+γrt+2+γ2rt+3+γ3V(st+3)n-step return, n = 3n = 1 is the TD targetn = all is REINFORCE’s return
Figure 8. The n-step return: n real rewards, then the critic’s guess V for the rest.

2.1 A3C and A2C in practice

Runseach A3C worker updates after 5 actions or at an episode’s end
Carriesno replay; workers on CPU threads share one network and the statistics of RMSProp, an Adam-like optimizer
Costa forward pass of actor and critic per action, one backward pass per 5 actions per worker (the A3C paper)
nreal rewards before the critic’s guess takes over; higher is less biased but noisier, 1 is the one-step δ, a whole episode is REINFORCE with a baseline; 5 in A3C and in Stable-Baselines3 A2C
Workerscopies of the world feeding one network; more gives more varied experience at more CPU, 1 loses that steadying; 16 (the A3C paper)
Learning rateRMSProp step size; higher learns faster but can diverge, 0 never learns; 0.0007 in Stable-Baselines3 A2C
CodeStable-Baselines3 A2C
Gotchaszero V(s′) at an episode’s end; detach A in the actor’s loss

3 PPO clipping

A batch of steps only describes the policy that played it, so learning from it several times is safe only while the policy stays close to that one. PPO reuses one batch of replies for several updates, but stops paying once a token’s chance has moved 20% from where the batch was collected (Figure 9).

advantage above 0flat past 1.2unclipped00.811.22ratioobjectiveadvantage below 0flat below 0.8unclipped00.811.22ratioobjectivePeople, ratio 1.3counted as 1.2: 0.84The, ratio 0.7counted as 0.8: −0.24The, wrong way, ratio 1.3counted in full: −0.39
Figure 9. Past 0.8 or 1.2 in the advantage’s direction, a token’s change stops counting.

PPO is actor-critic that reuses each on-policy batch for several updates but stops rewarding any change that moves the policy far from the one that collected it. Without that stop, every extra pass pushes the same moves further using advantages measured under the old policy, the new policy drifts far from it, and performance can collapse (Schulman et al. 2017):

PPO stops rewarding a change once the new policy leaves a band around the old one.
ratio = πnew(a |⁠ s)πold(a |⁠ s)
How much more likely (above 1) or less likely (below 1) the policy being trained makes the move than πold, the policy that collected the batch. Its gradient at ratio 1 equals the push of line 3 of the policy-gradient card, so the first update on each batch is the actor-critic step.
clipped = clip(ratio, 1 − ε, 1 + ε)
clip holds the ratio inside the band from 1 − ε to 1 + ε. ε here is the band’s half-width, 0.2 in the paper (the PPO paper, section 3), not the exploration chance, so Figure 9’s 0.8 and 1.2 are 1 − ε and 1 + ε.
objective = min(ratio · A, clipped · A)
min keeps the smaller of the plain and clipped terms. Past the band, moving the ratio further earns nothing, while a change that hurts still counts in full: People at ratio 1.3 with A = +0.7 counts as 1.2 and scores 0.84, while The pushed the wrong way to ratio 1.3 with A = −0.3 scores −0.39 in full. Training makes the objective larger; the loss is its negative. Together: the actor-critic step held inside a trust band. Builds on the policy-gradient card and the actor-critic card; stage 3 of RLHF and GRPO (group relative policy optimization) reuse it.

One PPO update over a rollout, the steps just played, EPOCHS passes over shuffled minibatches:

import torch
from torch.distributions import Categorical

EPS_CLIP, EPOCHS, MINIBATCH = 0.2, 4, 128   # CleanRL ppo.py

def ppo_update(actor, optimizer, s, a, old_log_pi, A):
    old_log_pi, A = old_log_pi.detach(), A.detach()
    for epoch in range(EPOCHS):
        for mb in torch.randperm(len(s)).split(MINIBATCH):
            dist = Categorical(logits=actor(s[mb]))
            ratio = torch.exp(dist.log_prob(a[mb]) - old_log_pi[mb])
            clipped = ratio.clamp(1 - EPS_CLIP, 1 + EPS_CLIP)
            objective = torch.min(ratio * A[mb], clipped * A[mb])
            loss = -objective.mean()
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
  1. 1Freezes old log-probabilities and advantages; loops over epochs and minibatches.
  2. 2Ratio, eq card in Section 3, line 1: exp of a difference of logs is the ratio of the chances.
  3. 3Clip, line 2, the band in Figure 9.
  4. 4Objective, line 3, turned into a loss.
  5. 5One gradient step per minibatch.

PPO gets A from GAE (Schulman et al. 2016), a weighted blend of the one-step δ over the next steps. The paper’s full loss adds the critic’s loss and an entropy bonus. PPO is also the RL step of RLHF, reinforcement learning from human feedback, in Aligning Language Models with RL.

3.1 PPO in practice

RunsN copies each play T steps, then K epochs of minibatches over those N × T steps
Carriesthe N × T rollout with old log-probabilities and advantages, dropped after K epochs, plus the critic
CostK × (N × T / minibatch) gradient steps per rollout, 10 × 32 at MuJoCo (simulated robot) settings (the PPO paper)
Clip εhalf-width of the band; wider allows bigger changes per round, 0 freezes the policy, very wide is plain policy gradient; 0.2 MuJoCo, 0.1 lowered linearly to 0 on Atari video games (the PPO paper), 0.2 CleanRL
Learning rateAdam step size for the policy and critic; higher learns faster but can diverge, 0 never learns; 0.0003 MuJoCo, 0.00025 lowered linearly to 0 on Atari (the PPO paper), 0.00025 lowered linearly to 0 in CleanRL
Kpasses over each batch; more squeezes each batch but drifts further from the policy that collected it, 1 is a single A2C-like pass; 10 MuJoCo, 3 Atari, 4 CleanRL
Tsteps each copy plays per round; longer gives more data but fewer updates per step played; 2,048 MuJoCo, 128 Atari with 8 copies (the PPO paper), 128 CleanRL with 4
Minibatchsteps per gradient step; larger gives a less noisy gradient but fewer steps per epoch; 64 MuJoCo, 128 CleanRL
GAE λhow fast the blend’s weight on later steps fades; lower is steadier but leans on the critic, 0 is the one-step δ, 1 the full return less V; 0.95 (the PPO paper, CleanRL)
CodeCleanRL ppo.py (docs)
Gotchasdetach the old log-probabilities and A; CleanRL also rescales A per minibatch and clips the gradient norm at 0.5

When the move is a real number, such as a joint torque, there is no list of chances to push. Continuous-Action Actor-Critic: TD3 and SAC carries the actor-critic idea there.

4 References

  1. Sutton, R. S., and Barto, A. G. (2018). Reinforcement Learning: An Introduction (2nd ed.). MIT Press. Book site.
  2. Prisner, E. (2014). Game Theory Through Examples. MAA. Chapter 27 (Theory 8), Mixed Strategies.
  3. Williams, R. J. (1992). Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning. Machine Learning, 8, 229-256.
  4. Mnih, V., et al. (2016). Asynchronous Methods for Deep Reinforcement Learning. ICML. arXiv:1602.01783.
  5. Schulman, J., et al. (2016). High-Dimensional Continuous Control Using Generalized Advantage Estimation. ICLR. arXiv:1506.02438.
  6. Schulman, J., et al. (2017). Proximal Policy Optimization Algorithms. arXiv:1707.06347.