Expected SARSA
Instead of gambling on a single randomly sampled next action, Expected SARSA computes the exact average value over all available actions weighted by the probability of taking them.
Why Does This Exist?
In classical Temporal Difference learning, SARSA computes its bootstrap target using the exact action sampled by the agent's behavior policy:
Because is a random draw from an exploratory policy (such as -greedy), this single sample injects massive variance into the update. Two identical transitions landing in the exact same state can produce wildly contradictory target values: one step samples a greedy action yielding high value, while the very next step samples an exploratory blunder yielding a catastrophic penalty. To prevent divergence from this sampling noise, practitioners must reduce the learning rate , slowing convergence to a crawl.
Q-learning sidesteps this action variance by taking the greedy maximum . However, Q-learning is strictly off-policy: it assumes infallible future execution. In hazardous environments where the agent continues to explore, Q-learning ignores its own exploratory blunders, suffering from maximization bias and frequently driving the agent into catastrophic penalties during online learning.
Expected SARSA resolves this fundamental tension. Instead of rolling the dice on a single sampled action , it computes the exact mathematical expectation across all possible next-state actions under policy :
By replacing a random draw with its expected value, Expected SARSA completely eliminates action-selection sample variance while faithfully accounting for exploratory risk.
Think of It Like This
Calculating an exam grade: die roll vs best answer vs weighted course syllabus
Imagine a university grading policy for a multi-part final exam containing easy questions, moderate questions, and treacherous trap questions.
- The SARSA examiner rolls a multi-sided die to pick a single question at random and awards you that single question's score as your entire course grade. If you roll an easy question, your recorded grade spikes; if you roll a treacherous trap question by sheer bad luck, your recorded grade plummets. Over infinite repeated exams the average score is technically correct, but every individual update suffers extreme variance from the random die roll.
- The Q-learning examiner assumes you will answer only the easiest question on the entire test, completely ignoring that the exam rules force you to answer harder questions 10% of the time during testing.
- The Expected SARSA examiner consults the syllabus, takes the exact probability of every question appearing, multiplies each by its expected score, and assigns your deterministic expected value: . No dice are rolled. Every evaluation is smooth, predictable, and fully accounts for the presence of the hard questions.
Where the analogy stops: A course syllabus has static question weights, whereas in reinforcement learning, both the policy distribution and the action values continuously shift as the agent learns, requiring the expectation to be recomputed dynamically at every time step.
How It Actually Works
Expectation Backup and Policy Duality
Let denote the state at step , the action taken, the scalar reward, and the resulting state. The update rule for Expected SARSA is:
where:
- is the step-size parameter (learning rate).
- is the discount factor.
- is the probability of selecting action in state under the target policy .
- is the expected action value under .
Variance Reduction Analysis
Consider the variance of the TD target conditioned on the transition :
In standard SARSA, whenever policy assigns non-zero probability to multiple actions with distinct values. In Expected SARSA:
The action-sampling variance is identically zero. The only remaining stochasticity in the update stems from environment dynamics (transition probabilities and stochastic rewards ). Because variance is strictly lower, Expected SARSA can use substantially larger learning rates without becoming unstable, converging faster than SARSA across almost all tabular benchmarks.
Unifying On-Policy and Off-Policy Control
Expected SARSA separates the behavior policy (which selects actions in the environment) from the target policy (which defines the expectation in the TD target):
- On-Policy Expected SARSA: Set . For an -greedy policy with actions, the greedy action has probability , while every non-greedy action has probability . The expectation is:
- Off-Policy (Q-learning as a special case): Set to be purely greedy with respect to , assigning and for all . The expectation collapses to: Expected SARSA directly reproduces Q-learning without requiring importance sampling corrections on the next action.
Worked numerical example
Let an agent be in state and take action . The transition reaches state and yields immediate reward .
Parameters:
- Learning rate
- Discount factor
- Prior estimate
In state , there are three available actions :
- (Safe path): (greedy action, )
- (Detour):
- (Cliff edge):
The agent follows an -greedy behavior policy with :
1. Expected SARSA Update
First, compute the expected next-state action value:
Compute the TD Target and error:
Update the Q-value:
2. Contrast with SARSA (Sample-Dependent)
- Case A (Agent randomly samples greedy action ):
- Case B (Agent randomly samples cliff blunder ):
3. Contrast with Q-Learning (Greedy Max)
Key Takeaway: SARSA's estimate oscillates violently between and based entirely on the roll of the exploration dice. Q-learning blindly assumes future perfection (), ignoring that the agent falls off the cliff 10% of the time. Expected SARSA produces a steady, deterministic update (), accurately penalizing the cliff risk without sample variance.
Code
from typing import Dict, Listimport numpy as np
def compute_epsilon_greedy_probs( q_values: np.ndarray, epsilon: float,) -> np.ndarray: """Computes action probabilities under an epsilon-greedy policy.""" num_actions = len(q_values) best_action = int(np.argmax(q_values)) probs = np.full(num_actions, epsilon / num_actions, dtype=np.float64) probs[best_action] += 1.0 - epsilon return probs
def step_expected_sarsa( q_table: Dict[str, np.ndarray], s: str, a_idx: int, r: float, s_next: str, target_probs: np.ndarray, alpha: float = 0.5, gamma: float = 0.9,) -> float: """Updates Q-table using Expected SARSA probability-weighted target.""" expected_next_q = float(np.dot(target_probs, q_table[s_next])) target = r + gamma * expected_next_q td_error = target - q_table[s][a_idx] q_table[s][a_idx] += alpha * td_error return float(q_table[s][a_idx])
def step_sarsa( q_table: Dict[str, np.ndarray], s: str, a_idx: int, r: float, s_next: str, a_next_idx: int, alpha: float = 0.5, gamma: float = 0.9,) -> float: """Updates Q-table using standard SARSA sampled-action target.""" target = r + gamma * float(q_table[s_next][a_next_idx]) td_error = target - q_table[s][a_idx] q_table[s][a_idx] += alpha * td_error return float(q_table[s][a_idx])
def step_q_learning( q_table: Dict[str, np.ndarray], s: str, a_idx: int, r: float, s_next: str, alpha: float = 0.5, gamma: float = 0.9,) -> float: """Updates Q-table using Q-learning greedy max target.""" target = r + gamma * float(np.max(q_table[s_next])) td_error = target - q_table[s][a_idx] q_table[s][a_idx] += alpha * td_error return float(q_table[s][a_idx])
# Environment setup matching the worked numerical example# S1 actions: [Safe path = 20.0, Detour = 10.0, Cliff edge = -100.0]q_exp = {"S0": np.array([2.0]), "S1": np.array([20.0, 10.0, -100.0])}q_sarsa_safe = {"S0": np.array([2.0]), "S1": np.array([20.0, 10.0, -100.0])}q_sarsa_cliff = {"S0": np.array([2.0]), "S1": np.array([20.0, 10.0, -100.0])}q_ql = {"S0": np.array([2.0]), "S1": np.array([20.0, 10.0, -100.0])}
# Target policy: epsilon-greedy with epsilon = 0.3 -> probs = [0.8, 0.1, 0.1]probs = compute_epsilon_greedy_probs(q_exp["S1"], epsilon=0.3)
# 1. Expected SARSA updateval_exp = step_expected_sarsa(q_exp, "S0", 0, -1.0, "S1", probs)
# 2. SARSA updates: sampled greedy (index 0) vs sampled blunder (index 2)val_sarsa_safe = step_sarsa(q_sarsa_safe, "S0", 0, -1.0, "S1", a_next_idx=0)val_sarsa_cliff = step_sarsa(q_sarsa_cliff, "S0", 0, -1.0, "S1", a_next_idx=2)
# 3. Q-learning update (greedy max)val_ql = step_q_learning(q_ql, "S0", 0, -1.0, "S1")
print(f"Expected SARSA Q(S0, East): {val_exp:.2f}")# -> Expected SARSA Q(S0, East): 3.65
print(f"SARSA (sampled safe) Q(S0, East): {val_sarsa_safe:.2f}")# -> SARSA (sampled safe) Q(S0, East): 9.50
print(f"SARSA (sampled cliff) Q(S0, East): {val_sarsa_cliff:.2f}")# -> SARSA (sampled cliff) Q(S0, East): -44.50
print(f"Q-learning Q(S0, East): {val_ql:.2f}")# -> Q-learning Q(S0, East): 9.50Watch Out For
Computational Bottleneck When Action Space |A| Is Massive
While standard SARSA performs an array lookup for the sampled action and Q-learning requires an maximum, Expected SARSA must evaluate the full dot product across all actions on every transition.
For small action spaces (such as gridworlds or standard discrete control tasks with 4 to 20 actions), this dot product is trivial and the variance reduction yields immediate wall-clock speedups. However, in recommender systems or combinatorial control environments with hundreds of thousands of discrete actions, calculating the explicit expectation at every step creates a severe computational bottleneck. Furthermore, in continuous action spaces, computing the integral analytically is intractable without specialized parameterizations.
Concrete Fix:
- Under -greedy policies, avoid generating the full probability vector. Decompose the expectation into greedy and uniform components: , which can be computed in a single vectorized pass.
- For massive action spaces, use Sampled Expected SARSA: approximate the expectation using a mini-batch of sampled actions, capturing most variance-reduction benefits at a fraction of the computational cost.
The Quick Version
- Expected SARSA replaces the single sampled next action with the policy-weighted expectation .
- Eliminating action-selection sample variance allows Expected SARSA to train stably with significantly higher learning rates than standard SARSA.
- Expected SARSA smoothly unifies on-policy and off-policy TD learning: setting the target policy to the greedy policy reproduces Q-learning without importance sampling corrections.
- The primary trade-off is computational: evaluating the expectation requires an inner product over actions at every step, making it ideal for discrete spaces but requiring approximations for massive or continuous action domains.