Skip to main content

SARSA vs Q-Learning on Cliff Walking

Key Insight

SARSA and Q-learning differ in exactly one spot — which next action they put into the TD target — and Cliff Walking is the environment designed to make that one difference visible. SARSA is on-policy: its target uses the action the ε-greedy policy actually takes next, so it "knows" it will sometimes explore and step off the cliff, and it learns a safer path that gives the edge a wide berth. Q-learning is off-policy: its target uses the greedy next action, so it learns the optimal cliff-edge path yet earns lower reward during training because exploration occasionally shoves it over the edge.

This highlights a key lesson: the theoretically optimal policy (Q-learning) and the policy that actually performs best while still exploring (SARSA) are not always the same. Because Q-learning ignores its own exploration, it hugs the risky edge and frequently falls. SARSA, however, learns to expect its own exploratory steps (like anticipating that it might occasionally "trip") and chooses a safer path further inland. Reproducing Sutton & Barto's Figure 6.5 makes this lesson concrete.


What's in this directory

FileRole
cliff_sarsa_q.pyBoth algorithms with the book's settings (alpha = 0.5, gamma = 1, fixed eps = 0.1, 500 episodes), averaged over 200 independent runs; the Figure 6.5 reproduction, the two greedy routes, and a decayed-ε follow-up.
python cliff_sarsa_q.py # ~17 s

One implementation note: CliffWalking-v1 is deterministic, so its dynamics are read once into flat arrays (next_state[s, a], reward[s, a]) and training steps become plain lookups — the same MDP, ~50× faster than stepping the wrapped env, which is what makes a 200-run average cheap. Stepping into the cliff costs −100 and teleports the agent back to the start (the episode does not end); every other move costs −1.

The one-line difference

# SARSA (on-policy): bootstrap from the action actually taken next
target = r + gamma * Q[s2, a2] # a2 ~ eps-greedy, cliff risk included

# Q-learning (off-policy): bootstrap from the best action, taken or not
target = r + gamma * Q[s2].max() # pretends exploration doesn't exist

Figure 6.5, reproduced

sum of rewards per episode

Averaged over the last 100 episodes: SARSA −26.4, Q-learning −50.1 — matching the book. Q-learning knows the better path (its greedy return is −13 to SARSA's −17, next figure) yet earns about half the online reward, because an ε-greedy walker on the cliff edge falls off roughly every other episode: twelve edge cells × 0.1 exploration × a 1-in-4 chance the random action is "down" gives about a 26% fall rate per crossing before compounding. SARSA's estimates contain that risk — Q[s2, a2] occasionally samples the −100 — so cells near the edge look bad to it and its greedy policy retreats one row per unit of danger.

What each one actually learned

the two greedy routes

The learned Q-tables, followed greedily: Q-learning marches along the edge (return −13, the optimal path; all 200/200 runs produce it), SARSA takes the top-row detour (return −17; 173/200 runs — in the rest, 500 episodes of fixed-ε data leave a greedy cycle that never reaches the goal, a reminder that SARSA's greedy readout is not what it optimizes).

Does SARSA drop the caution when exploration stops?

In theory, as eps -> 0 SARSA's target converges to Q-learning's, so decaying ε should erase the difference. Measured: decay ε from 0.1 to 0 over 1000 episodes, then train 1000 more fully greedy — SARSA's greedy return stays −17 in all 5 seeds. Once exploration stops, it also stops gathering the evidence that the edge is safe for a greedy walker, so the safe path is self-sealing. Actually converging to the optimal policy requires exploration that vanishes slowly (the GLIE condition — greedy in the limit with infinite exploration, e.g. eps = 1/k) and far more episodes than this budget. The gap between "converges in the limit" and "converges in your experiment" is a recurring RL theme — this is its first clean appearance.