Skip to content
AI360Xpert
Beta

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.

SARSA lambda broadcasts one on-policy error backward: with decay 0.63 a reward of 10 updates three actions by 1.0, 0.63 and 0.40 at once
SARSA lambda broadcasts one on-policy error backward: with decay 0.63 a reward of 10 updates three actions by 1.0, 0.63 and 0.40 at once

Why Does This Exist?

One-step SARSA learns slowly over long trails. It updates only the last state-action pair (St,At)(S_t, A_t) from reward Rt+1R_{t+1} and the next value Q(St+1,At+1)Q(S_{t+1}, A_{t+1}). 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 z(s,a)z(s, a) marking how recently it was chosen. When the on-policy error δt\delta_t 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 γλ\gamma \lambda each step, where γ\gamma discounts the future and λ\lambda 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 S\mathcal{S} and actions A\mathcal{A}. Let γ\gamma in [0,1][0, 1] discount the future, λ\lambda in [0,1][0, 1] set the trace reach, and α\alpha in (0,1](0, 1] be the step size. Q(s,a)Q(s, a) estimates action value and z(s,a)z(s, a) 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 γλ\gamma \lambda:

zt(s,a)=1if s=St and a=At, else γλzt−1(s,a)z_t(s, a) = 1 \quad \text{if } s = S_t \text{ and } a = A_t, \text{ else } \gamma \lambda z_{t-1}(s, a)

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 AtA_t in StS_t, the agent banks Rt+1R_{t+1}, lands in St+1S_{t+1}, and draws At+1A_{t+1} from its own behavior policy such as epsilon-greedy. The error evaluates the action actually drawn:

δt=Rt+1+γQ(St+1,At+1)−Q(St,At)\delta_t = R_{t+1} + \gamma Q(S_{t+1}, A_{t+1}) - Q(S_t, A_t)

At termination Q(St+1,At+1)Q(S_{t+1}, A_{t+1}) is zero. Using the max over actions here would turn the update into Q-learning; SARSA lambda must use the sampled At+1A_{t+1} to stay on-policy.

One error updates everything

The scalar δt\delta_t scales every pair by its trace:

Q(s,a)=Q(s,a)+αδtzt(s,a)Q(s, a) = Q(s, a) + \alpha \delta_t z_t(s, a)

With λ=0\lambda = 0 only the current pair carries trace 1 and the rest sit at zero, which recovers one-step SARSA. With λ=1\lambda = 1 traces decay by γ\gamma alone and the method behaves like online Monte Carlo. Values between blend speed against variance.

Worked example

Three moves with γ=0.9\gamma = 0.9, λ=0.7\lambda = 0.7, so decay is 0.9×0.7=0.630.9 \times 0.7 = 0.63, plus α=0.1\alpha = 0.1 and all QQ starting at 0:

  • Step 0 visits (S0,right)(S_0, \text{right}) with reward 0.00.0. Trace becomes 1.01.0, error is 0.00.0, no learning. Decay leaves 0.630.63.
  • Step 1 visits (S1,up)(S_1, \text{up}) with reward 0.00.0. Traces are 1.01.0 for the new pair and 0.630.63 for the old one. Error is 0.00.0. Decay leaves 0.630.63 and 0.63×0.63=0.39690.63 \times 0.63 = 0.3969.
  • Step 2 visits (S2,right)(S_2, \text{right}) and banks R=10.0R = 10.0 at termination. Traces are 1.01.0, 0.630.63, and 0.39690.3969. Error is δ=10.0+0.9×0.0−0.0=10.0\delta = 10.0 + 0.9 \times 0.0 - 0.0 = 10.0. With αδ=0.1×10.0=1.0\alpha \delta = 0.1 \times 10.0 = 1.0, updates are 1.0×1.0=1.01.0 \times 1.0 = 1.0 for (S2,right)(S_2, \text{right}), 1.0×0.63=0.631.0 \times 0.63 = 0.63 for (S1,up)(S_1, \text{up}), and 1.0×0.3969=0.39691.0 \times 0.3969 = 0.3969 for (S0,right)(S_0, \text{right}). Unvisited pairs hold trace zero and stay at zero.

One-step SARSA with λ=0\lambda = 0 would learn only the first number (1.01.0) 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.3969

Multiply each trace by αδ=1.0\alpha \delta = 1.0 and you get the QQ 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 max⁡aQ(St+1,a)\max_a Q(S_{t+1}, a) instead of the sampled Q(St+1,At+1)Q(S_{t+1}, A_{t+1}), 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 z(s,a)z(s, a) per pair that decays by γλ\gamma \lambda each step and marks the visited pair.
  • Its error is on-policy: Rt+1+γQ(St+1,At+1)−Q(St,At)R_{t+1} + \gamma Q(S_{t+1}, A_{t+1}) - Q(S_t, A_t) with the sampled next action.
  • One error updates all pairs through Q=Q+αδzQ = Q + \alpha \delta z, 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.