ML Grind

Advantage Estimation (GAE) - Reinforcement Learning

Implement Advantage Estimation (GAE) in Python with NumPy. hard exercise with examples, hints, and instant feedback. No setup or signup required.

Generalized Advantage Estimation (GAE)

What you'll build

GAE(γ, λ) advantages for one trajectory, computed with the standard backward recursion used in every PPO implementation.

Background

The advantage A(s, a) = Q(s, a) - V(s) says how much better an action was than the policy's average. Policy gradients weighted by advantages instead of raw returns have far lower variance. GAE (Schulman et al., 2016) blends one-step TD errors with an exponential weight λ, trading bias (small λ, trusts the critic) against variance (large λ, trusts the sampled rewards).

Formula

δ_t = r_t + γ · V(s_{t+1}) - V(s_t)                 (TD error)
Â_t = Σ_{l=0}^{T-1-t} (γλ)^l · δ_{t+l}
    = δ_t + γλ · Â_{t+1}        with Â_T = 0         (backward recursion)
  • λ = 0: Â_t = δ_t (one-step TD: low variance, biased)
  • λ = 1: Â_t = G_t - V(s_t) (Monte Carlo return minus baseline: unbiased, high variance)
  • Typical: γ = 0.99, λ = 0.95

Inputs

  • rewards: (T,) rewards r_0 ... r_{T-1}
  • values: (T+1,) critic values V(s_0) ... V(s_T). The last entry is the bootstrap value of the state after the final step: pass 0 if the episode terminated there, or the critic's estimate if the trajectory was truncated.
  • gamma, lam: floats in [0, 1]

Expected return format

A NumPy array of shape (T,), rounded to 2 decimals.

Edge cases and conventions

  • T = 1 gives Â_0 = δ_0.
  • Iterate from t = T-1 down to 0; Â_{T-1} = δ_{T-1}.

Constraints

NumPy only. O(T) time: don't compute the double sum.

Follow-up

  • Implementations also return value targets: returns = advantages + values[:-1]. Why is that the TD(λ) return?
  • With several episodes in one rollout buffer, how do you stop the recursion from leaking across an episode boundary? (Multiply by (1 - done_t).)

Examples

Input: rewards = [1, 1, 1], values = [0, 0, 0, 0], gamma = 0.99, lam = 0.95
Output: [2.83, 1.94, 1.0]

All δ = 1; Â_2 = 1, Â_1 = 1 + 0.9405·1 = 1.9405, Â_0 = 1 + 0.9405·1.9405 = 2.825

Input: rewards = [1, 2, 3], values = [1, 2, 3, 0], gamma = 0.9, lam = 0.9
Output: [3.99, 2.7, 0.0]

δ = [1 + 0.9·2 - 1, 2 + 0.9·3 - 2, 3 + 0 - 3] = [1.8, 2.7, 0]; Â_1 = 2.7 + 0.81·0 = 2.7, Â_0 = 1.8 + 0.81·2.7 = 3.987

Practice in Python

import numpy as np

def compute_gae(rewards: np.ndarray, values: np.ndarray,
                gamma: float, lam: float) -> np.ndarray:
    """
    Compute Generalized Advantage Estimation.

    Args:
        rewards: (T,) rewards r_0 ... r_{T-1}
        values: (T+1,) values V(s_0) ... V(s_T); the last entry is the
                bootstrap value (0 if the episode terminated)
        gamma: discount factor
        lam: GAE lambda

    Returns:
        (T,) advantages A_0 ... A_{T-1}, rounded to 2 decimals
    """
    rewards = np.array(rewards, dtype=float)
    values = np.array(values, dtype=float)
    T = len(rewards)
    advantages = np.zeros(T)

    # Your code here
    pass

The interactive editor loads with Python and NumPy. Run your implementation against 6 tests, or use the progressive hints.

More exercises in this section →