Explains TD(lambda)'s forward-backward equivalence, how it leads to GAE for advantage estimation in PPO, and how eligibility traces work with O(1) memory.
From TD(lambda) to GAE: Eligibility Traces and the Forward-Backward Equivalence#
From TD(lambda) to Generalized Advantage Estimation — Eligibility Traces and the Forward-Backward Equivalence
Starting from TD(lambda)‘s forward-backward equivalence, this post explains why PPO uses GAE to estimate advantage, how eligibility traces achieve n-step credit assignment with O(1) extra memory, and how it all connects to the reward signal backpropagation in RLHF training.
Prerequisites: you should know what advantage is and the basics of TD learning.
Gt(n)=n steps of real rewardrt+γrt+1+⋯+γn−1rt+n−1+bootstrapγnV(st+n)
Larger n means closer to MC (low bias, high variance). Smaller n means closer to TD(0). The problem: the optimal n is task-dependent, and tuning it is painful.
Zero bias, no bootstrap. But variance is ∝(T−t)⋅σr2, making long sequences nearly unusable.
3. TD(lambda): Mixing All n-step Returns with Geometric Weighting#
The issue with a fixed n is that the reward at step n+1 is completely cut off from the update — it can only leak through indirectly via the bootstrap, which is painfully slow.
TD(lambda)‘s solution: mix together returns for n=1,2,3,…,∞:
λ=0 gives only Gt(1), i.e. TD(0).
λ=1 gives only Gt(∞), i.e. MC (in finite-horizon settings).
Any intermediate λ interpolates continuously between the two. No need to agonize over a fixed n.
2. Eligibility Traces
If the weights weren’t geometric, implementing an n-step mixture would require storing all data from the next n steps in memory — you’d have to wait until step t+n to receive the reward before updating step t. This is the forward view — you must wait for the episode to end; it cannot run online.
Geometric weights have a special property: the current weight equals the previous weight times lambda. From this, we derive the equivalent backward view — each state only needs to maintain a single scalar trace (the accumulating trace):
Et(s)=γλ⋅Et−1(s)+I{St=s}
Once we have δt, we update all states simultaneously:
V(s)←V(s)+α⋅δt⋅Et(s)
No n-step buffer needed, no waiting for the episode to end. O(1) extra memory per state, fully online.
3. Robustness to Noise
If you only use a fixed n, one unusually noisy step at position n can contaminate the entire update. A geometric mixture is effectively a weighted smoothing filter over all n — the noise from any single long-step return gets diluted. Lambda serves as a single knob, far less sensitive to hyperparameters than a fixed n.
3.2 Lemma: Expanding Gt:t+n−V(St) via TD Errors#
This identity underpins all subsequent derivations, so let’s prove it separately:
For a finite episode, once n exceeds the trajectory endpoint, V(st+n) lands on a terminal state (V=0), and the bootstrap term vanishes naturally. For the infinite case, ∣γ∣<1 guarantees γnV(st+n)→0 (since V is bounded). In both cases, only the reward portion remains in the lambda-return:
Gtλ=(1−λ)∑n=1∞λn−1∑k=0n−1γkrt+k
Swap the order of the double sum. The outer n runs from 1 to ∞, the inner k from 0 to n−1. Equivalently, k runs from 0 to ∞ and n runs from k+1 to ∞:
Gtλ=(1−λ)∑k=0∞γkrt+k∑n=k+1∞λn−1
Change variables in the inner sum: let m=n−k−1, so n=m+k+1:
∑n=k+1∞λn−1=∑m=0∞λm+k=λk∑m=0∞λm=1−λλk
Substituting back:
Gtλ=(1−λ)∑k=0∞γkrt+k⋅1−λλk=∑k=0∞(λγ)krt+k
This is the forward view in reward form — “the sum of future rewards decaying by (λγ)k.“
3.4 Forward View to Backward View: Straight from the Definition#
There is no need to first derive the reward form and then substitute r→δ back in — that route introduces an extra S2−S3 cancellation step with unnecessary technical detail. The shortest path uses the lemma from section 3.2 directly.
Start from the forward view definition:
Gtλ=(1−λ)∑n=1∞λn−1Gt(n)
Subtract V(st) from both sides and apply section 3.2’s result Gt(n)−V(st)=∑i=0n−1γiδt+i:
Gtλ−V(st)=(1−λ)∑n=1∞λn−1∑i=0n−1γiδt+i
Swap the summation order — the exact same trick as section 3.3, with i from 0 to ∞ and n from i+1 to ∞:
This is the equivalent backward view — expressing lambda-return as V(st) plus a decaying sum of TD errors. Note that δt=rt+γV(st+1)−V(st) only depends on two adjacent states, so it can be computed online at each step, broadcast backward along the eligibility trace, with no need to wait for the episode to end.
The derivation never uses the S2−S3 cancellation — the lemma in section 3.2 already took care of the V(st) cancellation upfront. All we did here was reorganize the weighted sum of δt.
3.5 A Rigorous Proof of Forward-Backward Equivalence (Offline Case)#
Under offline updates, V stays fixed throughout the entire episode, so all δt are constant. We now prove that the total update from the forward view equals the total update from the backward view.
Forward view: at each time t, update V toward the target Gtλ:
The expressions inside the brackets are identical, therefore:
ΔVForward(s)=ΔVBackward(s)
The forward and backward views are strictly equivalent in the offline sense. Through eligibility traces Et(s), the backward view achieves — with O(∣S∣) extra memory — the same credit assignment that the forward view would need to buffer the entire trajectory to compute.
Sections 3.2 through 3.4 all used the accumulating trace:
Et(s)=γλEt−1(s)+I{St=s}
When a state is visited repeatedly, the trace has no upper bound (it can grow past 1), which may cause gradient explosion under function approximation. In practice, there are two variants.
Replacing trace:
Et(s)={1γλEt−1(s)if St=sotherwise
On each revisit, the trace is reset to 1 rather than accumulated. Bounded above by 1, numerically more stable. PPO and GAE implementations typically use a replacing trace or compute offline (in which case the trace type is irrelevant, since GAE bypasses explicit traces via backward scanning).
Dutch trace (interpolating between the two, α∈[0,1]):
α=0 degenerates to replacing (reset to 1 on revisit), α=1 degenerates to accumulating (decay then +1 on revisit). Intermediate α controls how much of the old trace is retained.
The proof in section 3.5 relies on a critical assumption: V remains fixed throughout the episode. In an online setting, V changes after every update, and subsequent δt shift accordingly — the forward and backward views are no longer strictly equivalent.
Seijen & Sutton (2014) proposed true online TD(lambda), which applies an extra correction to the trace at each update to restore equivalence, at the cost of increasing per-step computation from O(∣S∣) to O(d) (where d is the parameter dimension of V). This correction is rarely used in practice, because the online approximation error is negligible on most tasks, especially in algorithms like PPO that already perform offline batch updates after episodes finish.
“How much better the actual return is compared to what V(st) predicted.”
GAE’s approach: replace Gt with Gtλ:
AtGAE(γ,λ)=Gtλ−V(st)
Substituting Gtλ=V(st)+∑(γλ)kδt+k:
AtGAE(γ,λ)=∑k=0∞(γλ)kδt+k
V(st) cancels out. The GAE advantage estimate does not directly depend on the absolute value of V(st) — it only depends on TD errors (the differences between V at adjacent states). Part of the bias cancels in the subtraction, which is why GAE is more stable than directly using Gt−V(st).
The line delta + gamma * lam * gae is a direct translation of At=δt+γλ⋅At+1. The entire function is just this one recurrence; everything else is index alignment.
State st: the prompt x plus the first t−1 generated tokens
Action at: generating the t-th token (choosing from the vocabulary)
Reward: the reward model rϕ(x,y) scores the entire text — this means sparse rewards: only the final token receives a non-zero reward; all intermediate tokens get zero immediate reward
So in a sequence of length T, only rT−1 (or rT, depending on indexing convention) is non-zero. Every r0,r1,…,rT−2 is exactly zero.
Text generation tasks typically set γ=1. The reason isn’t “text has no discounting” — it is more specific:
Text generation is a finite-horizon task (the episode naturally terminates at the EOS token), so there is no need for γ<1 to keep the return bounded
γ<1 introduces positional bias — earlier tokens get inherently less credit than later ones, but this makes no sense for text (the first sentence matters just as much as the last paragraph for overall quality)
With γ=1, the TD errors for all intermediate tokens simplify to:
δt=rt0+γ1⋅V(st+1)−V(st)=V(st+1)−V(st)
Only the final step differs: δT−1=rT−1+V(sT)−V(sT−1), where V(sT)=0 (terminal state).
Intuition: the final reward rT−1 “seeps” into earlier δt through the differences in V(sT−1), then propagates to even earlier tokens with λl decay. Lambda controls the propagation distance:
λ≈1: rT−1 is shared almost equally; every token gets credit
λ≈0: only the token immediately before the reward gets non-zero advantage
λ≈0.95 (a common default): 0.9520≈0.36 — contribution drops to about 1/3 at 20 tokens away
Here is an easily overlooked detail: GAE’s lambda and PPO’s clip epsilon are complementary. If lambda is set too high (high variance), you can compensate with a tighter epsilon (more conservative policy updates). If lambda is set too low (high bias), the policy learns something that is fundamentally biased, and no amount of epsilon tuning will fix it. In practice, you typically tune lambda first (to control credit assignment quality), then tune epsilon (to control update stability).