Notes / Reinforcement Learning / Note 6 of 9

Leer en español

Eligibility traces

Averaging every n-step return with weights that fade by λ, and learning from that average as the episode runs, with a fading memory of the pairs just visited.

An n-step method trusts its estimates after a fixed number of steps, n, and a good n depends on the task. Nothing forces a single choice. A target can average several n-step returns, say half of a short one and half of a long one, and still be a sound target.

The λ-return averages all of them. It gives the one-step return the largest weight and each longer return a little less, by a factor λ per extra step, so one number, λ, moves the target smoothly from one-step TD (λ = 0) to Monte Carlo (λ = 1).

0.5 0.25 0.125 0.125 Gt:t+1 Gt:t+2 Gt:t+3 Gt
The weights of the λ-return with λ = 0.5, for a state four steps before the end: the one-, two- and three-step returns, then the full return Gt. Each longer return gets half the weight of the one before, and the full return takes all the weight that is left.

The catch is that this average reaches to the end of the episode, so it can only be computed once the episode is over. Eligibility traces remove the wait. The agent keeps a fading memory of the pairs it has visited, and each time it computes a TD error it passes that error back to them, more to recent pairs and less to old ones.

Throughout this page, the steps of an episode are drawn in blue ink, and targets and the TD errors that carry them in red ink.

1Averaging n-step returns

The n-step return keeps n rewards and then trusts the estimate of the state or pair it reaches:

Gt:t+n ≐ Rt+1 + γRt+2 + ⋯ + γn−1Rt+n + γnv(St+n, wt+n−1)

This is the general form, for any way of estimating values: v(s, w) is the estimated value of state s given a vector of weights w.

  • A table is the simplest case, with one weight per state, so that v(s, w) is just the table's entry V(s).
  • With action values, the last term is the estimate of the pair (St+n, At+n) instead, as in n-step Sarsa.

Any n-step return is a valid target, and so is any average of them, as long as the weights are positive and add up to 1. Such an average keeps the error reduction property of the n-step returns, so it leads to sound learning too.

Compound update: an update toward an average of simpler targets. It can only be made once its longest part is complete.

For example, an update toward half of a two-step return and half of a four-step return must wait four steps:

½Gt:t+2 + ½Gt:t+4

An example. A robot drives home to its charger from the kitchen, through the hallway and the study. Reaching the charger gives +10, every other step 0, and γ = 0.9, which sets the true values. The robot's table is still poor:

RoomTrue valueRobot's table
Kitchen8.12
Hallway94
Study106

From the kitchen, the one-step return is 0 + 0.9 · 4 = 3.6, the two-step return 0.81 · 6 = 4.86, and the three-step return reaches the charger, 0.81 · 10 = 8.1. Half of the first and half of the last is 5.85.

2The λ-return

The λ-return averages every n-step return, with a weight that shrinks by λ at each extra step:

Gλt ≐ (1 − λ) Σ∞n=1λn−1Gt:t+n

λ is between 0 and 1. The weights are 1 − λ for the one-step return, (1 − λ)λ for the two-step return, (1 − λ)λ2 for the three-step return, and so on. They add up to 1, since 1 + λ + λ2 + ⋯ = 1/(1 − λ); the factor 1 − λ is there to make them.

Once an n-step return reaches the end of the episode, it is the full return Gt, and so is every longer one. Gathering all their weights gives the form used for episodes:

Gλt = (1 − λ) ΣT−t−1n=1λn−1Gt:t+n + λT−t−1Gt

The two ends of the range are the methods we already know. With λ = 1, the sum vanishes and Gλt = Gt: learning from it is Monte Carlo. With λ = 0, only the one-step return is left, Gλt = Gt:t+1: learning from it is one-step TD.

From the kitchen, two steps before the last, the λ-return has three terms. With λ = 0.5 their weights are 0.5, 0.25 and 0.25:

Gλ0 = 0.5 · 3.6 + 0.25 · 4.86 + 0.25 · 8.1 = 5.04
λλ-return from the kitchen
03.6, the one-step return
0.55.04
0.86.68
18.1, the full return

From 3.6 at λ = 0 to the full return, 8.1, at λ = 1, the target climbs with λ: the larger λ, the more the target relies on real rewards and the less on the poor estimates.

3Learning from the λ-return

The λ-return can replace the target of any update. Sarsa moved toward it becomes

Q(St, At) ← Q(St, At) + α[Gλt − Q(St, At)]

and the same holds for state values. Gλt needs every reward until the end, so the simplest algorithm waits. The off-line λ-return algorithm changes nothing during the episode; when it ends, it makes one update per step, toward that step's λ-return:

wt+1 ≐ wt + α[Gλt − v(St, wt)] ∇v(St, wt), t = 0, …, T − 1

The gradient ∇v says which weights produced the estimate of St, and how much. In a table it is 1 for the entry of St and 0 for every other, so the update is the familiar V(St) ← V(St) + α[Gλt − V(St)].

In the example with λ = 0.5 and α = 0.5, the table moves at the end of the episode:

Roomλ-returnBeforeAfter
Kitchen5.0423.52
Hallway7.245.6
Study1068

How well does it learn? About as well as n-step TD at its best n, and slightly better at their best settings. As with n, an intermediate λ usually works best, and averaging over all horizons tends to be more robust than betting on a single n.

Forward and backward views

The λ-return is a forward view: each state's update looks forward in time. A backward view turns this around:

  • Forward view: uses all the rewards and states that follow the state, so it updates only after the end, like Monte Carlo, and never in tasks without an end.
  • Backward view: sends the current TD error back to the states or pairs visited recently, so every estimate is updated a little at every step, from information that already exists.

The next section shows why the two views can agree.

4The λ-return as a sum of TD errors

Take the TD error of Sarsa, with the table held fixed during the episode:

δk = Rk+1 + γQ(Sk+1, Ak+1) − Q(Sk, Ak)

Write the λ-return out term by term, collect the rewards and the estimates step by step, and the estimates cancel in pairs. What is left is a discounted sum of the TD errors from time t on:

Gλt − Q(St, At) = δt + γλδt+1 + (γλ)2δt+2 + ⋯ = ΣT−1k=t(γλ)k−tδk

For the kitchen, the TD errors are δ0 = 0 + 0.9 · 4 − 2 = 1.6, δ1 = 0.9 · 6 − 4 = 1.4 and δ2 = 10 − 6 = 4, and with γλ = 0.45 the sum is 1.6 + 0.45 · 1.4 + 0.2025 · 4 = 3.04, which is 5.04 − 2.

Now read the sum the other way. The TD error δk appears in the update of every pair visited before or at time k, with weight (γλ)k−t, where k − t is how many steps ago that pair was visited. So there is no need to wait: as soon as δk is known, it can be passed back to every earlier pair.

Eligibility trace of a pair: the weight with which each new TD error reaches it, (γλ) raised to the time since its visit.

δt 0.41 0.51 0.64 0.8 1 t − 4 t − 3 t − 2 t − 1 t
The traces of the last five pairs visited, with γλ = 0.8: 1 for the pair just visited, then 0.8, 0.64, 0.51 and 0.41. When the TD error δt is known, each of these pairs moves by αδt times its trace.

5Sarsa(λ)

Sarsa(λ) keeps a trace z(s, a) for every pair, a short-term memory next to the long-term table Q. When a pair is visited its trace goes up by 1, and after every step all traces shrink by the factor γλ. Each step then moves every pair by its share of the current TD error:

Q(s, a) ← Q(s, a) + αδtz(s, a) for all s, a

A pair that was never visited, or visited long ago, has a trace near 0 and barely moves. With λ = 0 only the current pair has a trace, and Sarsa(λ) is one-step Sarsa. With λ = 1 the traces fade only by γ, and every pair of the episode shares each TD error as in Monte Carlo. The trace is also written e(s, a).

The algorithm

For a step size α, a discount γ, a trace decay λ and a small ε > 0, with Q(s, a) initialized arbitrarily except Q(terminal, ·) = 0, and for each episode:

  1. Set every trace to 0: z(s, a) ← 0. Observe the first state S and choose A ε-greedily with respect to Q.
  2. Take A, and observe R and S′. Choose A′ in S′ ε-greedily.
  3. Compute the TD error δ ← R + γQ(S′, A′) − Q(S, A), and mark the pair just visited: z(S, A) ← z(S, A) + 1.
  4. For every pair: Q(s, a) ← Q(s, a) + αδz(s, a), then z(s, a) ← γλz(s, a).
  5. Set S ← S′ and A ← A′, and go back to step 2 until S is terminal.

Here is the hallway episode, with one action per room, go on, so that each Q plays the role of the room's value. λ = 0.5 and α = 0.5, so γλ = 0.45, and the table starts at 2, 4 and 6:

tTD errorTraces (kitchen, hallway, study)Table after the step
00.9 · 4 − 2 = 1.61, 0, 02.8, 4, 6
10.9 · 6 − 4 = 1.40.45, 1, 03.115, 4.7, 6
210 − 6 = 40.2025, 0.45, 13.52, 5.6, 8

At t = 2, for example, the kitchen moves by 0.5 · 4 · 0.2025 = 0.405. The final table, 3.52, 5.6 and 8, is exactly what the off-line λ-return algorithm gives, because every TD error was computed from values that had not changed yet. In general they do change during the episode, since Sarsa(λ) updates as it goes, so a later TD error already reflects earlier updates. Sarsa(λ) is then not equivalent to the λ-return, but it is a close approximation, and it learns at every step instead of at the end.

Credit for a whole path

The traces spread each TD error over the whole recent path. Take a robot crossing an open room to its charger, with reward 0 on every move except +1 on arrival, all values 0 at first, γ = 1 and α = 0.5, and λ = 0.9. Its first episode wanders for nine moves. Every TD error is 0 until the last move, whose TD error is 1, and at that moment the trace of the move k steps before the end is 0.9k.

+1 0.5 0.22
After one episode of Sarsa(λ) with λ = 0.9, every move of the path has a higher value, from 0.5 for the last to 0.22 for the first; thicker arrows rose more. One-step Sarsa would have raised only the last move, and 4-step Sarsa the last four.

The traces do not remember which moves were useful, only which were recent, so the detour in the middle of the path gets credit too. Later episodes sort this out, as paths that go straight collect their reward sooner.

The figure runs Sarsa(λ) on the cliff of the TD note, with λ = 0.9 as in the room, γ = 1, ε = 0.1 and a smaller step size, α = 0.1: a move visited again and again adds 1 to its trace each time, so its trace can grow well above 1, and a large α would overshoot.

  • Each press is one line of the algorithm above.
  • Every move with a trace is outlined in green, as strongly as its trace.
  • The value the TD error uses is outlined in red.

Next goes forward, Back goes back. Keys: → and ←.

The same mechanism works with any way of estimating values. The trace is then a vector zt, the size of the weight vector: when a weight takes part in an estimate, its trace is bumped up, and then it fades. In a table, the weight that takes part is the entry of the pair just visited, which is the +1 of the algorithm above.

6Summary

The key ideas next to their NumPy equivalents. g_n holds the returns Gt:t+1 to Gt:T−1, g the full return, and q and z the table and the traces; λ is lam, since lambda is a Python keyword.

λ-return
Gλt = (1 − λ) ΣT−t−1n=1λn−1Gt:t+n + λT−t−1Gt
k = np.arange(T - t - 1)
w = (1 - lam) * lam**k
g_lam = w @ g_n + lam**(T-t-1) * g
The two ends
λ = 0: Gt:t+1 (one-step TD); λ = 1: Gt (Monte Carlo)
No code: special cases of the line above.
Off-line λ-return update
V(St) ← V(St) + α[Gλt − V(St)], at the end of the episode
v[s] += alpha * (g_lam - v[s])
As a sum of TD errors
Gλt − Q(St, At) = Σk (γλ)k−tδk
k = np.arange(len(deltas))
err = (gamma * lam)**k @ deltas
TD error of Sarsa
δ = R + γQ(S′, A′) − Q(S, A)
g = r + gamma * q[s_next, a_next]
delta = g - q[s, a]
Sarsa(λ), one step
z(S, A) += 1; Q ← Q + αδz; z ← γλz
z[s, a] += 1
q += alpha * delta * z
z *= gamma * lam
Start of an episode
z(s, a) = 0 for every pair
z = np.zeros_like(q)