On-policy vs off-policy

Every RL algorithm answers one question before any other: whose actions is it allowed to learn from? On-policy methods (A2C, PPO, GRPO) may only use data generated by the policy they are currently improving, so they throw each batch away after one update. Off-policy methods (DDPG, and its descendants TD3 and SAC) learn from any transition ever collected, so they keep a replay buffer and reuse each sample hundreds of times. Neither choice is a convention. Each one falls out of a different equation, and that equation also decides how the method fails.

Companion pages: A2C · PPO · GRPO · DDPG. Every number marked measured comes from the from-scratch implementations in deeplearning/rl_lunarlander/ run on this machine (RTX 6000 Ada, 16 CPU cores), 5 seeds per method. None are paper numbers.

1 · Two policies, not one

An agent interacts with a Markov decision process: state $s_t$, action $a_t$, reward $r_t = r(s_t, a_t)$, next state $s_{t+1} \sim P(\cdot \mid s_t, a_t)$, discount $\gamma \in [0, 1)$. The objective is the expected discounted return of a parameterised policy $\pi_\theta$:

$$ J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\Big[\sum_{t=0}^{T} \gamma^t r_t\Big] \tag{1} $$

The distinction this page is about needs two policies to be named separately:

SymbolNameRole
$\pi$ (or $\pi_\theta$)Target policyThe policy whose value we want and whose parameters we update.
$\mu$Behaviour policyThe policy that actually chose the actions in the data.
On-policy means $\mu = \pi_\theta$ at the moment the gradient is taken. Off-policy means $\mu$ may be anything: an older $\pi_\theta$, a noisy version of it, a human, or a random controller. Every trade-off below follows from which of the two equations, (2) or (8), the algorithm is built on.

2 · Why the policy gradient is on-policy

Write the trajectory probability as $p_\theta(\tau) = p(s_0)\prod_t \pi_\theta(a_t \mid s_t)\,P(s_{t+1} \mid s_t, a_t)$ and differentiate (1). The log-derivative identity $\nabla_\theta p_\theta = p_\theta \nabla_\theta \log p_\theta$ removes the unknown dynamics $P$, because they do not depend on $\theta$. With a baseline subtracted, the result is the policy-gradient theorem:

$$ \nabla_\theta J(\theta) = \mathbb{E}_{s \sim d^{\pi_\theta},\; a \sim \pi_\theta(\cdot\mid s)} \big[\nabla_\theta \log \pi_\theta(a \mid s)\; A^{\pi_\theta}(s, a)\big] \tag{2} $$

Here $d^{\pi_\theta}$ is the discounted state-visitation distribution of $\pi_\theta$ and $A^{\pi}(s,a) = Q^{\pi}(s,a) - V^{\pi}(s)$ is the advantage. Read the subscript on the expectation carefully. Both the states and the actions must be drawn from the current policy. A sample $(s, a)$ collected under an older $\theta'$ is a sample from the wrong distribution, and averaging $\nabla \log \pi_\theta \cdot A$ over it estimates the gradient of a different objective.

This is why A2C discards its data. After one gradient step $\theta$ has moved, every stored sample is now off-policy, and (2) no longer applies to it. A2C collects $N \times n = 16 \times 8 = 128$ transitions, takes one step, and starts over. Each environment step is used exactly once.

3 · Importance sampling: reusing data, at a price

Expectations under one distribution can be rewritten under another by reweighting: $\mathbb{E}_{x\sim p}[f(x)] = \mathbb{E}_{x\sim q}\big[\tfrac{p(x)}{q(x)} f(x)\big]$. Applied to a whole trajectory, the dynamics cancel again and only action probabilities remain:

$$ \frac{p_\theta(\tau)}{p_\mu(\tau)} = \prod_{t=0}^{T} \frac{\pi_\theta(a_t\mid s_t)}{\mu(a_t\mid s_t)} = \prod_{t=0}^{T} \rho_t \tag{3} $$

That is exact and unbiased, and in practice it is useless. If each per-step ratio has mean 1 and even a small spread, $\mathbb{E}[\rho_t]=1,\ \operatorname{Var}[\rho_t] = \sigma^2$, and the ratios are independent, then

$$ \operatorname{Var}\Big[\prod_{t=0}^{T}\rho_t\Big] = (1+\sigma^2)^{T+1} - 1 \tag{4} $$

which grows exponentially in the horizon. A LunarLander episode is 200–1000 steps. Even with $\sigma^2 = 0.01$ and $T = 500$, the variance factor is $1.01^{501} \approx 146$. A policy that drifts a little further pushes it to millions.

3.1 PPO's answer: one-step ratios and a clip

PPO (Schulman et al., 2017) makes two approximations that turn (3) into something usable. First, it keeps only the single-step ratio and ignores the mismatch between $d^{\pi_\theta}$ and $d^{\mu}$ in the state distribution. Second, it reuses a batch for only a few epochs, so $\mu = \pi_{\theta_\text{old}}$ stays close to $\pi_\theta$. The surrogate is

$$ L^{\text{CLIP}}(\theta) = \mathbb{E}_t\Big[\min\big(\rho_t(\theta) \hat A_t,\; \operatorname{clip}(\rho_t(\theta), 1-\epsilon, 1+\epsilon)\,\hat A_t\big)\Big], \qquad \rho_t = \frac{\pi_\theta(a_t\mid s_t)}{\pi_{\theta_\text{old}}(a_t\mid s_t)} \tag{5} $$

Ignoring the state-distribution shift is justified only while the policy stays close. Kakade & Langford's bound (and TRPO's version of it) makes this precise. The true improvement differs from the surrogate by a term that scales with the maximum KL between old and new policy:

$$ J(\pi_\theta) \ \ge\ J(\pi_{\theta_\text{old}}) + L_{\theta_\text{old}}(\theta) - \frac{4\gamma\,\max_{s,a}|A(s,a)|}{(1-\gamma)^2}\; \max_s D_{\mathrm{KL}}\big(\pi_{\theta_\text{old}}(\cdot\mid s)\,\|\,\pi_\theta(\cdot\mid s)\big) \tag{6} $$
The clip is a cheap trust region, not a regulariser. Once $\rho_t$ leaves $[1-\epsilon, 1+\epsilon]$ in the direction that would increase the objective, the clipped branch is constant and its gradient is zero. That sample stops pushing. PPO is therefore nearly on-policy: it reuses each sample $K$ times (here $K=4$ epochs), but only within the region where (6) says the one-step ratio is still trustworthy. The measured approximate KL $\mathbb{E}[(\rho-1) - \log\rho]$ and the clip fraction are the two dials that show how far "nearly" has been stretched.

3.2 GRPO: on-policy without a critic

GRPO uses the same clipped ratio as (5), but replaces the learned advantage $\hat A_t$ with a group statistic. It runs $G$ rollouts from the same initial state and standardises each episode's return $R_i$ against the group: $\hat A_{i,t} = (R_i - \operatorname{mean}_j R_j)/\operatorname{std}_j R_j$ for every step $t$ of episode $i$. That estimator is valid only if the $G$ samples were drawn from the current policy, because the group mean is a Monte-Carlo estimate of $V^{\pi_\theta}(s_0)$. GRPO is therefore on-policy for a second, independent reason: its baseline is itself an on-policy estimate.

4 · Why the Bellman equation is off-policy

Now start from the action-value function instead of the return. For any policy $\pi$, $Q^\pi$ satisfies the Bellman expectation equation:

$$ Q^{\pi}(s,a) = r(s,a) + \gamma\,\mathbb{E}_{s' \sim P(\cdot\mid s,a)} \Big[\,\mathbb{E}_{a'\sim\pi(\cdot\mid s')} Q^{\pi}(s', a')\Big] \tag{7} $$

Look at what the expectations range over. The outer one is over the environment's dynamics $P$, which do not depend on who chose $a$. The inner one is over the target policy at the next state, and we evaluate it ourselves. Nothing in (7) asks how $a$ was chosen. So any stored tuple $(s, a, r, s')$ is a valid sample of the outer expectation, whatever policy generated it. For a deterministic target policy $\mu_\phi$ the inner expectation collapses to a point, giving DDPG's critic target:

$$ y = r + \gamma\,(1 - d)\; Q_{w'}\big(s', \mu_{\phi'}(s')\big), \qquad \mathcal L_Q(w) = \mathbb{E}_{(s,a,r,s',d)\sim\mathcal D}\big[(Q_w(s,a) - y)^2\big] \tag{8} $$

where $\mathcal D$ is the replay buffer and $w', \phi'$ are Polyak-averaged target networks. The actor is improved by the deterministic policy gradient (Silver et al., 2014):

$$ \nabla_\phi J \approx \mathbb{E}_{s\sim\mathcal D}\big[\nabla_a Q_w(s,a)\big|_{a=\mu_\phi(s)}\, \nabla_\phi \mu_\phi(s)\big] \tag{9} $$
Two honest caveats are hidden in the $\approx$. First, (9) averages over states from the buffer, not from $d^{\mu_\phi}$, so it shares the state-distribution approximation that PPO makes. DDPG simply makes it with far older data. Second, there is no importance ratio anywhere, because a deterministic policy has no density to divide by. The method is off-policy at the cost of trusting $Q_w$ completely, so any error in the critic flows directly into the actor.

5 · How each family fails

5.1 Off-policy: over-estimation is structural

The actor in (9) climbs $Q_w$. Write the critic as truth plus zero-mean error, $Q_w = Q^\pi + \varepsilon$. The actor then lands where $Q^\pi + \varepsilon$ is largest, which is preferentially where $\varepsilon$ happens to be positive. By Jensen's inequality,

$$ \mathbb{E}_\varepsilon\Big[\max_a\big(Q^\pi(s,a) + \varepsilon(a)\big)\Big] \ \ge\ \max_a Q^\pi(s,a) \tag{10} $$

and the target (8) bootstraps from exactly that over-estimated value, so the bias is copied backward through every state. Thrun & Schwartz (1993) derived this for Q-learning. Fujimoto et al. (2018) showed it persists for deterministic actor-critics, and TD3's twin critics exist to cancel it.

Measured. The DDPG plot logs the critic's $Q(s,\mu(s))$ on every state of the deterministic evaluation episodes, next to the actual discounted return-to-go from those states. Final numbers across 5 seeds. The peak over-estimation was +250 to +495, reached at 100k–250k steps. On seed 1, for example, the critic predicted $Q = 373$ for states whose actual return-to-go was $-122$. As the policy improved, the bias shrank but never reached zero: it was +18 to +39 (median of the last 20 evals) at the end of training. That is the Jensen gap of (10), still present at convergence. The critic loss peaked at 67–314 during the over-estimation phase and settled at 13–30. There was no divergence, but 2 of 5 seeds ended 34–80 points below their own best checkpoint.

5.2 The deadly triad

Sutton & Barto name three ingredients that, together, can make value estimates diverge even on a simple linear problem: function approximation, bootstrapping (the target $y$ depends on $Q_{w'}$), and off-policy data. Each of the four methods here uses some subset:

MethodFunction approx.BootstrappingOff-policy data Triad complete?
A2Cyesyes ($n$-step, $n=8$)nono
PPOyesyes (GAE, $\lambda=0.98$)mildly (4 epochs)no, kept in check by the clip
GRPOyesno (Monte-Carlo returns)mildly (4 epochs)no
DDPGyesyes (1-step TD)yes (1M-transition replay)yes

Target networks (Polyak $\tau = 0.005$) and a large replay buffer are DDPG's damping against the triad. They slow the feedback loop in (8) down, but they do not break it.

5.3 On-policy: variance and wasted samples

On-policy methods avoid the triad, and the price is paid in samples and variance. A2C's single-use batches make its gradient noisy, and one bad step can undo progress with no clip to stop it. PPO's clip bounds the damage, but its data still expires after 4 epochs. GRPO pays most. It needs $G = 16$ complete episodes from each initial state before it can compute a single advantage, and the advantage then carries no temporal credit at all: every action in a crashed episode is blamed equally.

6 · Data reuse, counted

The cleanest way to compare the two families is to count how many gradient updates each environment transition contributes to. These ratios come straight from the hyper-parameters actually used:

MethodSamples per update cycleGradient steps per cycle Gradient steps per env stepTimes each sample is used
A2C$16 \times 8 = 128$1 (full batch)$1/128$1
PPO$16 \times 1024 = 16{,}384$$4 \times 16384/64 = 1024$$1/16$4
GRPO$4 \times 16$ episodes $\approx$ 6k–60k$4 \times n/64$$1/16$4
DDPG1 (then sample 256 from buffer)11 $\approx 256 \cdot \tfrac{T - T_0}{T} \approx$ 250 on average

DDPG's figure: after the $T_0 = 10^4$-step warm-up it draws 256 transitions from the buffer on each of the remaining $T - T_0 = 990{,}000$ steps. That is $2.5\times10^8$ draws over at most $10^6$ stored transitions, so each one is replayed about 250 times. Older transitions are replayed more, because they have been in the buffer longer.

Off-policy pays for its reuse with compute and with stability risk; whether it buys sample efficiency is an empirical question (§8 shows it did not, here). DDPG takes 16× more gradient steps per environment step than PPO, each on a batch 4× larger, through networks about 25× bigger ($\sim$125k vs $\sim$5k parameters). Whether that wins depends on what is expensive. A real robot charges for environment steps. A simulator running at thousands of steps per second charges for gradient steps.

7 · Training and inference, step by step

The pseudo-code below is a line-by-line summary of the four training scripts that produced every number on this page (a2c.py, ppo.py, grpo.py, ddpg.py). Hyper-parameters are the values that actually ran, not suggestions. Tensor shapes are written [dim, dim, …]. Comments start with # and point back to the equation each step implements. The on-/off-policy split is visible in the structure: the first three scripts have a collect → update → discard loop, and DDPG has a step → store → sample old data → update loop.

7.1 Shared notation and networks

SymbolValue hereMeaning
D_obs8Observation: x, y, vx, vy, angle, angular rate, left-leg contact, right-leg contact
A4Discrete actions: no-op, left engine, main engine, right engine
D_act2Continuous action: main throttle, lateral throttle, each in [−1, 1]
N16Parallel environments (A2C, PPO)
T_max1000Time-limit truncation per episode (50 Hz → 20 s)
NetworkLayers (input → output)ActivationParametersUsed by
Categorical actor πθ8 → 64 → 64 → 4 logitstanh4,996A2C, PPO, GRPO
Gaussian actor πθ8 → 64 → 64 → 2 means, + logσ [2]tanh4,868PPO-continuous
Value critic Vψ8 → 64 → 64 → 1tanh4,801A2C, PPO
Deterministic actor μφ8 → 400 → 300 → 2, then tanhReLU124,502DDPG
Q critic Qwconcat(8, 2) = 10 → 400 → 300 → 1ReLU125,001DDPG

The on-policy networks use orthogonal initialisation (gain √2 on hidden layers, 0.01 on the policy head so the initial policy is near-uniform, 1.0 on the value head). DDPG uses PyTorch's default initialisation, as in the TD3 reference code. GRPO trains only the 4,996-parameter actor, with no critic at all.

7.2 A2C: one gradient step per 128 transitions

# hyper-parameters: N=16 envs, n=8 steps, gamma=0.995, lambda=1 (pure n-step return), # RMSprop lr=7e-4 alpha=0.99, c_v=0.5, c_e=0.001, grad-clip 0.5, budget 2,000,000 steps init actor pi_theta (8->64->64->4), critic V_psi (8->64->64->1) s ← reset 16 envs # s: [16, 8] repeat until 2M env steps: # ---- collect: n=8 steps in each of the 16 envs, KEEPING the autograd graph ---- for t = 0..7: logits ← pi_theta(s) # [16, 4] a ← Categorical(logits).sample() # [16] int in {0,1,2,3} logp[t], H[t] ← log pi(a|s), entropy # [16], [16] V[t] ← V_psi(s) # [16] s', r, term, trunc ← envs.step(a) # s': [16, 8], r/term/trunc: [16] if trunc[i]: r[i] += gamma * V_psi(final_obs[i]) # time limit is not a crash: bootstrap done[t] ← term OR trunc # [16] s ← s' # auto-reset envs already return the new start state # ---- n-step returns = GAE with lambda = 1 ---- V_last ← V_psi(s) # [16] R, A ← nstep_returns(r[0:8], V[0:8], done[0:8], V_last, gamma) # R, A: [8, 16] # ---- ONE update on all 128 samples, then the batch is thrown away (eq. 2 is on-policy) ---- L_pi ← −mean(logp · stopgrad(A)) # scalar, over [8, 16] L_V ← mean((V − R)²) L ← L_pi + 0.5·L_V − 0.001·mean(H) RMSprop step on (theta, psi) with ‖grad‖ clipped to 0.5

7.3 PPO: 16,384 transitions, reused 4 times inside the clip

# hyper-parameters: N=16, T=1024 steps/env/rollout (batch 16,384), gamma=0.999, lambda=0.98, # Adam lr=3e-4 eps=1e-5, K=4 epochs, minibatch 64, eps_clip=0.2, c_v=0.5, c_e=0.01, # grad-clip 0.5, value clipping OFF, budget 2M (discrete) / 1M (continuous) init actor pi_theta, critic V_psi # discrete: 4 logits | continuous: 2 means + log_sigma[2] s ← reset 16 envs # [16, 8] repeat until budget: # ---- collect with the CURRENT policy, no gradients ---- for t = 0..1023: discrete: a ~ Categorical(pi_theta(s)) # a: [16] continuous: a ~ Normal(mu_theta(s), sigma) # a: [16, 2]; env receives clip(a, −1, 1) logp_old[t] ← log pi_theta(a|s) # [16] (continuous: summed over the 2 dims) V_old[t] ← V_psi(s) # [16] step envs, bootstrap truncations as in A2C, store s, a, r, done # buffers: obs [1024, 16, 8], act [1024, 16] or [1024, 16, 2], logp_old/V_old/r/done [1024, 16] A, R ← GAE(r, V_old, done, V_psi(s_last), gamma=0.999, lambda=0.98) # [1024, 16] each flatten every buffer to a batch of 16,384 # e.g. obs [16384, 8] # ---- K=4 epochs × 256 minibatches = 1,024 Adam steps on the same data ---- for epoch = 1..4: for each random minibatch idx of 64: logp ← log pi_theta(act[idx] | obs[idx]) # [64] rho ← exp(logp − logp_old[idx]) # [64] importance ratio  ← (A[idx] − mean) / (std + 1e-8) # [64] per-minibatch normalisation L_clip ← −mean(min(rho·Â, clip(rho, 0.8, 1.2)·Â)) # eq. (5) L_V ← mean((V_psi(obs[idx]) − R[idx])²) L ← L_clip + 0.5·L_V − 0.01·mean(entropy) Adam step, ‖grad‖ ≤ 0.5 log approx_kl = mean((rho − 1) − log rho), clip_frac = mean(|rho − 1| > 0.2) discard the whole batch # it is now off-policy w.r.t. the updated theta

7.4 GRPO: 64 whole episodes, no critic, group-relative advantage

# hyper-parameters: B=4 prompts (initial states) × G=16 samples = 64 episodes per iteration, # Adam lr=3e-4, K=4 epochs, minibatch 64, eps_clip=0.2, beta_KL=0.04, # reference refresh every 10 iterations, no entropy bonus, no discount, budget 2M steps init actor pi_theta (8->64->64->4); pi_ref ← copy(pi_theta) # NO value network repeat until budget: # ---- sample groups: "same prompt" = same reset seed = identical initial state and terrain ---- q[1..4] ← 4 random training seeds run 64 envs, env (b, g) reset with seed q[b], each until termination or 1000 steps a ~ Categorical(pi_theta(s)), store s, a, logp_old for every step # episode i has length L_i (~70 for a crash, up to 1000 for a hover); n = Σ L_i ~ 6k-60k steps R ← undiscounted episode returns, reshaped [4, 16] Â_ep ← (R − mean_g R) / (std_g R + 1e-4) # [4, 16] group-relative, one scalar per episode  ← repeat Â_ep[i] for each of the L_i steps # [n] every action in episode i gets the same credit w ← n / (64 · L_i) per step # [n] the paper's 1/|o_i| length normalisation logp_ref ← log pi_ref(a | s) # [n] # ---- K=4 epochs over the n steps, minibatch 64 ---- for epoch = 1..4: for each random minibatch idx of 64: rho ← exp(log pi_theta(a|s) − logp_old) # [64] surr ← min(rho·Â, clip(rho, 0.8, 1.2)·Â) # [64] u ← logp_ref − log pi_theta(a|s) # log(pi_ref / pi_theta) KL ← exp(u) − u − 1 # [64] unbiased, ≥ 0 (DeepSeekMath eq. 4) L ← −mean(w · (surr − 0.04·KL)) Adam step, ‖grad‖ ≤ 0.5 if iteration % 10 == 0: pi_ref ← copy(pi_theta) log degenerate_frac = fraction of the 4 groups with std_g R < 1e-4 # those groups give  = 0
Where GRPO's sample bill comes from. One iteration costs 64 complete episodes. Once the policy learns to hover, episodes run to the 1000-step limit and an iteration consumes up to 64,000 environment steps. 2M steps then bought only 74–79 policy iterations (measured, 5 seeds), against 123 PPO rollouts of 16,384 steps. Contrary to the usual worry, the fraction of degenerate groups (all 16 returns equal, so every advantage is zero) was 0.00 in every iteration of every seed. Sampling from a stochastic policy always produced spread, with a within-group std of 41–82 at the end of training. GRPO's real failure here was different. Its policy entropy stayed at 0.97–1.21 nats (uniform is 1.39), because an episode-level advantage gives every action in the episode the same credit. The sampled policy scores +31 to +234 on the final evaluation seeds, while its argmax scores −1,878 to +41.

7.5 DDPG: one gradient step per env step, on 256 old transitions

# hyper-parameters: 1 env, buffer 1,000,000, batch 256, gamma=0.99, tau=0.005 (Polyak), # Adam lr=1e-3 actor and critic, exploration N(0, 0.1²) Gaussian, 10,000 uniform warm-up steps, # budget 1,000,000 steps, continuous=True only (needs grad_a Q) init actor mu_phi (8->400->300->2, tanh), critic Q_w (10->400->300->1) mu_phi' ← copy(mu_phi), Q_w' ← copy(Q_w) # target networks, never trained directly buffer D: s [1e6, 8], a [1e6, 2], r [1e6], s' [1e6, 8], term [1e6] s ← reset env # [8] for step = 1..1,000,000: # ---- act: behaviour policy = actor + noise (or uniform during warm-up) ---- if step ≤ 10,000: a ← Uniform([−1, 1]²) # [2] else: a ← clip(mu_phi(s) + 0.1·N(0, I), −1, 1) s', r, term, trunc ← env.step(a) D.add(s, a, r, s', term) # store TERM only: a time-limit cut still bootstraps s ← reset() if term or trunc else s' if step ≤ 10,000: continue # ---- learn from 256 transitions drawn uniformly from up to 1M OLD ones (eq. 8 ignores who acted) ---- (s_b, a_b, r_b, s'_b, d_b) ← D.sample(256) # [256,8], [256,2], [256], [256,8], [256] y ← r_b + 0.99 · (1 − d_b) · Q_w'(s'_b, mu_phi'(s'_b)) # [256] target, no gradient L_Q ← mean((Q_w(s_b, a_b) − y)²) # eq. (8); Adam step on w L_mu ← −mean(Q_w(s_b, mu_phi(s_b))) # eq. (9); Adam step on phi only w' ← 0.995·w' + 0.005·w # Polyak averaging phi' ← 0.995·phi' + 0.005·phi

7.6 Inference: running the trained controller

At deployment the four methods collapse into one loop. Only the actor is kept: the value critic, the Q critic, the target networks, the reference policy and the replay buffer are training-time scaffolding and are discarded. Exploration is switched off. Every evaluation number, video and progression figure on this site uses exactly this loop.

# inputs: actor checkpoint (best.pt), environment, a reset seed load actor weights # 4,996 (discrete) | 4,868 (Gaussian) | 124,502 (DDPG) params actor.eval(); torch.no_grad() # no autograd graph is built s ← env.reset(seed) # s: [8] float32 repeat (at most 1000 times; the env runs at 50 Hz): discrete (A2C / PPO / GRPO): logits ← pi_theta(s) # [4] a ← argmax(logits) # int in {0: no-op, 1: left, 2: main, 3: right} continuous PPO: a ← clip(mu_theta(s), −1, 1) # [2] Gaussian MEAN; sigma is ignored DDPG: a ← mu_phi(s) # [2] already in [−1, 1] via tanh; NO exploration noise s, r, term, trunc ← env.step(a) return += r if term or trunc: break outcome ← landed if the lander came to rest (Box2D body asleep) crashed if the hull touched the ground out_of_bounds if |x| ≥ 1 timeout if the 1000-step limit was reached
Why deterministic at test time. The stochastic training policy is a search distribution. Its spread exists so that (2) and (5) have something to estimate a gradient from. The policy you ship is its mode. The two can perform very differently, and GRPO shows this most clearly on this page: its stochastic training return and its argmax evaluation return disagree substantially (§8). Per-step cost of the deployed controller, measured on one CPU thread (bench_inference.py, batch 1, torch.no_grad(), taken while 13 training jobs were loading the machine, so these are upper bounds): 92 µs for the 4,996-parameter categorical actor, 99 µs for the Gaussian actor, 138 µs for the 124,502-parameter DDPG actor. That is 0.5–0.7 % of the 20,000 µs control period at 50 Hz, and comparable to one Box2D env.step() (84–88 µs). The same forward pass on the RTX 6000 Ada takes about 590 µs. At batch size 1, kernel-launch and host↔device copy overhead dominate, so the GPU is about 5× slower. Deploy these controllers on CPU.

8 · Measured on LunarLander

The like-for-like comparison is on the continuous action space, because DDPG cannot act in the discrete one. PPO with a Gaussian policy runs on exactly the same environment, with the same 1M-step budget, the same evaluation seeds and the same deterministic evaluation policy.

DDPG vs PPO on continuous LunarLander, mean and std over 5 seeds
DDPG (off-policy) vs PPO (on-policy) on LunarLander-v3, continuous=True. Left: 100-episode rolling training return. Right: deterministic evaluation on 10 held-out seeds. Bands are ±1 std over 5 seeds.
Evaluation return against environment steps and against wall-clock time for all five configurations
The same runs plotted twice: against environment steps (sample efficiency) and against wall-clock seconds on this machine (compute efficiency, log scale). On steps, DDPG and PPO-continuous overlap. On wall-clock, DDPG needs about 16× longer to reach the same score.
ConfigFamilyEnv stepsFinal eval (50 eps) Solved rateSteps to eval ≥ 200Wall-clockEnv steps / s
PPOon2.0M275.9 ± 2.599.6 %613k (5/5)946 s2,134
A2Con2.0M−64.7 ± 62.23.6 %never (0/5)549 s3,647
GRPOon2.0M−388.9 ± 746.62.0 %never (0/5)743 s2,735
PPO (continuous)on1.0M265.0 ± 2.9100 %419k (5/5)511 s1,993
DDPG (continuous)off1.0M249.1 ± 27.885.6 %406k (5/5)8,249 s121
What the measurements say. On the like-for-like continuous comparison, off-policy DDPG and on-policy PPO are equally sample-efficient: the first deterministic eval ≥ 200 comes at 406k vs 419k environment steps (means over 5 seeds). The replay buffer's 250-fold reuse of every transition did not buy fewer environment steps here. It bought a 16× larger wall-clock bill (8,249 s vs 511 s per seed) and a 10× larger seed-to-seed spread in final score (±27.8 vs ±2.9). DDPG's best checkpoints are marginally better (270.5 vs 265.2), and 2 of its 5 seeds lost ground late in training. That instability is what §5.1 predicts, and the Q-bias numbers above show its source. The textbook "off-policy is far more sample-efficient" claim is true on average across benchmarks. It is not guaranteed on any one task, and it did not hold on this one.

8.1 Watching both families learn

The two videos replay the same held-out initial state with the policy saved at several points in training (0 %, then roughly 2.5 %, 10 %, 25 %, 50 % and 100 %; DDPG snapshots every 50k steps, so its first non-zero stage is 10 %), for the median seed of each method. Measured over 20 held-out starts per stage, the two curves have nearly the same shape. Median-seed DDPG lands 0 % / 10 % / 80 % / 100 % at 10 / 25 / 50 / 100 % of training. PPO lands 0 % / 45 % / 100 % / 100 % at 10 / 25 / 50 / 100 %. Neither shows the "competent within a few percent of the budget" behaviour that off-policy methods are often credited with.

DDPG learning progression (continuous actions).
PPO learning progression on the same continuous environment.

9 · Advantages, disadvantages, and when to use which

On-policy (A2C, PPO, GRPO)Off-policy (DDPG, TD3, SAC)
Gradient isAn unbiased (A2C) or trust-region-approximate (PPO) estimate of $\nabla J$ itself, eq. (2)/(5)The gradient of a learned critic, eq. (9), which is only as good as $Q_w$
Sample efficiencyLow: each sample is used 1–4 times High: each sample is replayed $\sim$250 times
Compute per env stepLow: 1/128 to 1/16 gradient steps High: $\ge 1$ gradient step, bigger nets
ParallelismExcellent: collection is embarrassingly parallel over envs Awkward: the update loop is sequential
StabilityGood (PPO); triad avoidedFragile; triad complete, over-estimation (10) is structural
Action spacesDiscrete and continuous, same codeDDPG/TD3: continuous only (needs $\nabla_a Q$)
ExplorationBuilt into the stochastic policy (entropy)Must be bolted on (Gaussian/OU noise); a deterministic actor never explores by itself
Can learn from logged / human / old dataNoYes. This is the basis of offline RL.
Hyper-parameter sensitivityModerateHigh ($\tau$, noise scale, lr, buffer size, warm-up)
Rule of thumb. If environment steps are cheap and parallel (a fast simulator, or an LLM sampling completions), pick on-policy. PPO's stability and trivial parallelism win, which is why RLHF and GRPO are on-policy. If environment steps are expensive (real hardware, slow physics, human feedback) or you already have logged data, pick off-policy, and use TD3 or SAC rather than vanilla DDPG to keep (10) in check.

10 · References

  • Sutton, McAllester, Singh, Mansour, Policy Gradient Methods for RL with Function Approximation, NeurIPS 2000. The policy-gradient theorem, eq. (2).
  • Kakade & Langford, Approximately Optimal Approximate RL, ICML 2002; Schulman et al., Trust Region Policy Optimization, ICML 2015. Eq. (6).
  • Schulman, Wolski, Dhariwal, Radford, Klimov, Proximal Policy Optimization Algorithms, 2017. Eq. (5).
  • Mnih et al., Asynchronous Methods for Deep RL, ICML 2016. A3C/A2C.
  • Silver et al., Deterministic Policy Gradient Algorithms, ICML 2014. Eq. (9).
  • Lillicrap et al., Continuous Control with Deep RL (DDPG), ICLR 2016.
  • Fujimoto, van Hoof, Meger, Addressing Function Approximation Error in Actor-Critic Methods (TD3), ICML 2018. Over-estimation in actor-critics, and the "OurDDPG" settings used here.
  • Thrun & Schwartz, Issues in Using Function Approximation for RL, 1993. Eq. (10).
  • Sutton & Barto, Reinforcement Learning: An Introduction, 2nd ed., §11.3. The deadly triad.
  • Precup, Sutton, Singh, Eligibility Traces for Off-Policy Policy Evaluation, ICML 2000. Per-decision importance sampling, eq. (3)–(4).
  • Shao et al., DeepSeekMath, 2024. GRPO, §3.2.

Related: RL overview · A2C · PPO · GRPO · DDPG · PPO & GRPO for LLMs