Backward View of TD(lambda)
Keep a fading memory of states you just visited. When surprise arrives, share it backward at once instead of waiting for the episode to end.
Why Does This Exist?
The forward view defines the return as a mix of all future -step returns. Clean theory, but you must see the future to compute it. In continuing tasks or live control you cannot pause for the episode to finish.
The backward view makes the same idea causal. Keep an eligibility trace per state, a fading note of how recently you were there. On each step compute one scalar surprise and broadcast it to every state weighted by its trace. Credit flows the moment the transition lands, with memory and no trajectory store.
This page covers the online mechanism. See eligibility traces for trace variants, forward view TD lambda for the lookahead definition, and their equivalence for when the totals match.
You need temporal difference learning first: as prediction error.
Think of It Like This
A megaphone echo down a trail
Runners pass checkpoints A, then B, then C. A marshal at C spots a spring and shouts once through a megaphone. The runner at C hears it full volume, the B runner hears a fainter echo, the A runner hears a faint rumble, and anyone still at base hears nothing.
The shout is the scalar error . Loudness by position is the trace . Everyone updates at once without waiting for the race to end.
Where it stops: sound fades with distance and wind, while trace decay is exact math, per step, plus a bump for the state you stand on.
How It Actually Works
Keep values and traces , both zeroed at episode start. With discount and decay , accumulating traces update as:
Here is one for the visited state and zero else. Then compute the one step error for with reward :
Then broadcast with step size :
Fresh states with take the full hit. Old states take a decayed share. Unvisited states with take nothing.
Worked example
Chain with . Set , , so decay is , and . Start , traces zero.
Step : traces . Error . Then .
Step : traces . Error . Then and . Note A learns while the agent stands at B.
Step : traces since . Error . Then , , .
One surprise updates all three states at once.
Code
Same three steps, no hidden state.
from typing import Dict, List
def td_broadcast( values: Dict[str, float], traces: Dict[str, float], gamma: float, lam: float, alpha: float, state: str, reward: float, v_next: float,) -> float: """Decay traces, bump the visit, broadcast one TD error.""" decay = gamma * lam for s in traces: traces[s] = decay * traces[s] + (1.0 if s == state else 0.0) delta = reward + gamma * v_next - values[state] for s in values: values[s] += alpha * delta * traces[s] return delta
states: List[str] = ["A", "B", "C"]v: Dict[str, float] = {"A": 1.0, "B": 2.0, "C": 3.0}z: Dict[str, float] = {"A": 0.0, "B": 0.0, "C": 0.0}
print(f"{td_broadcast(v, z, 0.9, 0.8, 0.1, 'A', 0.0, v['B']):.4f}") # -> 0.8000print(f"{td_broadcast(v, z, 0.9, 0.8, 0.1, 'B', 1.0, v['C']):.4f}") # -> 1.7000print(f"{td_broadcast(v, z, 0.9, 0.8, 0.1, 'C', 5.0, 0.0):.4f}") # -> 2.0000print(f"{v['A']:.4f} {v['B']:.4f} {v['C']:.4f}") # -> 1.3061 2.3140 3.2000Deltas and finals match the hand run: , , , ending .
Watch Out For
Decaying only on reward or forgetting episode reset
Traces must decay every step, even when , and must zero at each episode start.
Symptom: ancient states grab huge credit when a late reward lands, or episode two inherits episode one's memory. Fix: apply unconditionally and clear in the env reset. Use replacing traces, set to not , when loops revisit fast.
The Quick Version
- Traces are fading visit memory with per step factor , here .
- Each step broadcasts one scalar to all states via .
- The run gives deltas , , and finals , , .
- Decay every step and reset each episode or credit leaks across time.
- Offline totals match the forward return under small steps.