SARSA Lambda
SARSA lambda gives every recent action a fading trace, so one reward updates the whole trail at once instead of one step at a time.
Why Does This Exist?
One-step SARSA learns slowly over long trails. It updates only the last state-action pair from reward and the next value . Walk a 20-step corridor to a goal and the first episode teaches only the final step; the start must succeed 20 times before the news reaches it.
Monte Carlo fixes the reach but adds waiting and noise: it holds every update until the episode ends, then averages whole returns with high variance. N-step methods sit between but need trajectory buffers and delayed updates.
SARSA lambda (Sutton and Barto; Rummery and Niranjan, 1994) removes the wait. Each pair keeps a trace marking how recently it was chosen. When the on-policy error arrives, every pair learns at once in proportion to its trace. One reward crosses the whole trail in a single episode, online, with no buffer. For the state-value version of the same idea see eligibility traces, for the one-step comparison see Q-learning versus SARSA, and for the one-step engine underneath see temporal-difference learning.
Think of It Like This
Praising the whole soccer move, not just the tap-in
A team scores after a 60-yard counter. One-step SARSA praises only the striker who tapped it in. Monte Carlo waits for the full 90 minutes, then praises everyone equally, including the throw-ins. SARSA lambda watches the replay at once: striker gets full credit at 1.0, the winger who crossed gets 0.63, the midfielder who launched it gets 0.40, and the bench gets nothing.
Point for point: the goal is the TD error, the replay weights are the traces, and bench players are unvisited pairs with trace zero. Fresh actions earn more because their traces decayed less.
Where it stops: a coach judges style. Traces multiply by exactly each step, where discounts the future and sets the reach, and the update evaluates the action actually taken next, not the best action imaginable.
How It Actually Works
Work with discrete states and actions . Let in discount the future, in set the trace reach, and in be the step size. estimates action value and stores its trace. All traces start at zero each episode.
The trace memory
Replacing traces reset the visited pair to 1 and decay the rest by :
Accumulating traces add instead of resetting, which can exceed 1 in loops and swing values. Replacing caps eligibility at 1.0 and suits control tasks, so the worked example uses replacing.
The on-policy error
After taking in , the agent banks , lands in , and draws from its own behavior policy such as epsilon-greedy. The error evaluates the action actually drawn:
At termination is zero. Using the max over actions here would turn the update into Q-learning; SARSA lambda must use the sampled to stay on-policy.
One error updates everything
The scalar scales every pair by its trace:
With only the current pair carries trace 1 and the rest sit at zero, which recovers one-step SARSA. With traces decay by alone and the method behaves like online Monte Carlo. Values between blend speed against variance.
Worked example
Three moves with , , so decay is , plus and all starting at 0:
- Step 0 visits with reward . Trace becomes , error is , no learning. Decay leaves .
- Step 1 visits with reward . Traces are for the new pair and for the old one. Error is . Decay leaves and .
- Step 2 visits and banks at termination. Traces are , , and . Error is . With , updates are for , for , and for . Unvisited pairs hold trace zero and stay at zero.
One-step SARSA with would learn only the first number () and leave the other two at zero. SARSA lambda teaches all three in the same episode. Those three numbers are what the diagram draws.
Code
Three operations carry the method: decay traces, set the visited pair, then scale one error by every trace.
gamma, lam, alpha = 0.9, 0.7, 0.1decay = gamma * lamprint(f"decay: {decay:.2f}")# -> decay: 0.63
z0, z1 = 0.63, 0.63 * 0.63print(f"traces: 1.0 {z1 / 1.0:.2f} {z1:.4f}")# -> traces: 1.0 0.63 0.3969
delta = 10.0step = alpha * deltaprint(f"updates: {step * 1.0:.2f} {step * 0.63:.2f} {step * 0.3969:.4f}")# -> updates: 1.00 0.63 0.3969Multiply each trace by and you get the changes from the worked example.
Watch Out For
Swapping in the max and silently building Q-learning
The symptom: you write the SARSA lambda loop but compute the error with instead of the sampled , keep traces running, and watch values oscillate. Past actions earn credit for a greedy move they never committed to, which breaks the on-policy guarantee.
Always use the action the behavior policy actually drew. Reset all traces to zero at each episode start, and prefer replacing traces in looping tasks so eligibility never piles past 1.0.
Carrying traces across episode boundaries
The symptom: early states of a new episode drift even though nothing rewarding happened yet. Last episode leftovers are still nonzero, so the next reward pays ghosts from the previous run.
Reset unconditionally on done, including timeout truncations unless your setup treats them differently on purpose. Traces are episode memory, not lifetime memory.
The Quick Version
- SARSA lambda keeps one trace per pair that decays by each step and marks the visited pair.
- Its error is on-policy: with the sampled next action.
- One error updates all pairs through , so a single reward crosses multi-step trails at once.
- With decay 0.63 and reward 10, three actions update by 1.0, 0.63 and 0.3969 together; one-step SARSA would update only the last one.
- Lambda 0 recovers one-step SARSA and lambda 1 behaves like online Monte Carlo.
- Reset traces every episode and use replacing traces when states recur.