Tree-Backup Algorithm
Tree backup learns from other policies' experience without unstable correction ratios: untaken actions contribute their expected value, and only the taken action carries the multi-step return forward.
Why Does This Exist?
Off-policy learning means collecting experience with one exploratory behavior policy while evaluating a different target policy . Stretch that over multiple steps and two classic fixes both break.
The first fix multiplies importance sampling ratios down the trajectory, . When the policies differ, the product collapses toward zero (throwing the experience away) or explodes (injecting wild variance). The second fix, used by Watkins's , cuts the trace to zero at the first exploratory action, which in a curious agent means every trace dies after a step or two and the method degrades to slow single-step learning. Both failure modes are covered on their own pages: Watkins's Q lambda and n-step TD prediction.
Tree backup takes a third route: never correct the trajectory at all. At each visited state, score every untaken action by its exact expectation under the target policy, and let only the taken action continue down the sampled path. No ratios appear anywhere, so there is nothing to explode. The one-step version of this idea is Expected SARSA; tree backup is its multi-step extension.
Think of It Like This
Reviewing a road trip from the trip log
You are auditing a friend's road trip after the fact. Your map rates every road: scenic highways score high, gravel detours score low. At each fork in the log, you do two things.
For every road your friend did not take, you write down the map's known average for that road and close the matter. No guessing, no what-ifs. For the road your friend did take, you read what actually happened next in the log (the motel, the flat tire, the great diner) and keep following the trip forward.
At the final stop there is nothing left to observe, so every remaining option gets its map average. Add it all up and you have the trip's value under your own taste in roads, built from a mix of recorded fact (the taken spine) and known averages (every side branch).
Where the analogy stops: map averages for nearby roads are correlated (construction hits the whole area), so closing each branch independently would mislead. In an MDP the Markov property makes the decomposition exact: given the arrival state, each branch's value is independent of how you got there.
How It Actually Works
The tree shape
The update targets one pair . Picture the trajectory as a tree: each visited state fans out into all actions, but only the taken action grows deeper. Side branches end immediately in their current estimates. The taken spine keeps sampling rewards and states until the horizon runs out, where the leaf closes with a full expectation over all actions.
The recursion
For horizon the return is exactly Expected SARSA:
where is the observed reward, in discounts the future, is the target policy's probability for action , and holds current estimates.
For , fold one level deeper. Split state into the untaken actions (closed with their expectations) and the taken action (continued recursively, scaled by its own target probability):
The leaf level uses the full expectation over all actions, and a terminal state contributes zero beyond its reward. Then update the root the usual TD way:
with step size in . No behavior probabilities appear anywhere: because every branch is weighted by and the weights over all actions sum to 1, the backup is exact for the target policy regardless of what did. When the target is greedy, an exploratory action simply gets weight and the recursion ends there on its own, no special cutoff needed. Sampling the next action fully gives multi-step SARSA; taking expectations gives tree backup; the blend interpolates between the two per step.
Worked example: a two-step backup by hand
Trajectory: with action pays into ; action pays into leaf . Discount , step size , starting estimate .
At the target policy says , , with estimates , . At leaf : , , values and .
Step 1, leaf expectation at : .
Step 2, one-step return from : .
Step 3, untaken branch at (action was not taken): . Taken spine weighted: . Combined: .
Step 4, full two-step return: .
Step 5, update: .
Every quantity stayed bounded, and no ratio was ever computed. The diagram above draws steps 3 and 4; the code below replays all five.
Code
The snippet folds the recursion backward from the leaf, then applies the update, printing the worked example's numbers.
from typing import Dict, List, Tuple
def tree_backup_return( rewards: List[float], taken: List[str], states: List[str], policy: Dict[str, Dict[str, float]], q: Dict[Tuple[str, str], float], gamma: float,) -> float: """n-step tree-backup return, folded backward from the leaf state.""" actions = sorted(policy[states[-1]].keys()) leaf = states[-1] current = rewards[-1] + gamma * sum(policy[leaf][a] * q[(leaf, a)] for a in actions) for k in range(len(rewards) - 2, -1, -1): s_next = states[k + 1] untaken = sum( policy[s_next][a] * q[(s_next, a)] for a in actions if a != taken[k + 1] ) current = rewards[k] + gamma * (untaken + policy[s_next][taken[k + 1]] * current) return current
if __name__ == "__main__": rewards = [2.0, 4.0] taken = ["a0", "a0"] states = ["S0", "S1", "S2"] policy = { "S1": {"a0": 0.6, "a1": 0.4}, "S2": {"a0": 0.7, "a1": 0.3}, } q = {("S1", "a0"): 5.0, ("S1", "a1"): 3.0, ("S2", "a0"): 8.0, ("S2", "a1"): 6.0} g = tree_backup_return(rewards, taken, states, policy, q, 0.9) print(round(g, 4)) # -> 8.8364 print(round(4.0 + 0.2 * (g - 4.0), 4)) # -> 4.9673Watch Out For
Double-correcting with importance ratios
The symptom: returns that swing wildly even though tree backup promised stability. The cause is bolting onto the spine "for safety," which corrects for the behavior policy twice. The recursion already weights every branch by , so an extra reintroduces exactly the variance tree backup was built to remove.
The fix: keep behavior probabilities out of the backup entirely. They decide which trajectories you see, never how the return is computed. If a rare action under worries you, collect more data, not bigger weights.
Cutting traces at exploratory actions like Watkins
The symptom: multi-step backups that mysteriously behave like one-step updates in curious agents. The cause is importing the Watkins habit of zeroing the trace after a non-greedy action. Tree backup needs no cutoff: with a greedy target, an exploratory action carries , so its spine contribution vanishes and the leaf expectation takes over automatically.
The fix: let the weights do the cutting. If your implementation has a branch that checks greediness and truncates, delete it and confirm the numbers match the worked example above.
The Quick Version
- Tree backup does multi-step off-policy learning with zero importance sampling ratios, so nothing explodes when behavior and target policies differ.
- Untaken actions at each state contribute their exact target-policy expectation; only the taken action continues down the sampled spine, scaled by its own target probability.
- The one-step case is Expected SARSA, and the recursion closes leaves with a full expectation over all actions.
- On the worked trace (rewards 2.0 and 4.0, discount 0.9) the two-step return is 8.8364 and the root estimate moves from 4.0000 to 4.9673.
- Never add importance ratios to the recursion, and never hand-cut traces at exploratory actions: the target weights already handle both.