後方観測TD(λ)とEligibility Trace
Eligibility Trace は、いま生じた TD 誤差を「どの過去状態にどれだけ返すか」を数値で管理する仕組みです。TD( の後ろ向き実装を理解するうえで、ここがいちばん実装的な要所になります。
参考動画(外部)
授業本編ではなく、別の説明で見直したいときの参考材料です。
trace は「過去の責任の残り具合」を持っている
TD 誤差が出た瞬間、それを現在だけに当てるのではなく、少し前に通った状態にも配りたいことがあります。trace はそのための記憶で、最近訪れた状態ほど大きく、時間が経つと減衰していきます。
accumulating と replacing の差は、同じ状態を再訪したときに trace を積み増すか、1 に戻すかです。更新を強く通したいか、暴れすぎを抑えたいかで設計が分かれます。
この notebook の見どころ
E が時間とともにどう残るか、再訪の多い状態で accumulating / replacing の差がどう広がるか、同じ TD 誤差でも更新先が変わる様子を見ます。
trace は抽象論ではなく、実装上の credit assignment そのものです。どの過去状態がまだ責任を持っているかを陽に持つことで、長期依存をオンラインで扱えるようになります。
読み方の軸
ここで見るべきは V の最終値だけではなく、E の形です。誤差がどこへ流れているかが見えれば、trace の設計差も自然に読めます。
gamma = 0.9
lam = 0.8
alpha = 0.1
states = ['s0', 's1', 's0', 's2', 's0']
rewards = [0.1, 0.0, 0.8, -0.2, 0.0]
V_acc = {'s0': 0.0, 's1': 0.0, 's2': 0.0}
V_rep = {'s0': 0.0, 's1': 0.0, 's2': 0.0}
E_acc = {'s0': 0.0, 's1': 0.0, 's2': 0.0}
E_rep = {'s0': 0.0, 's1': 0.0, 's2': 0.0}
for t in range(len(states) - 1):
s = states[t]
s_next = states[t + 1]
r = rewards[t]
# accumulating trace
delta_acc = r + gamma * V_acc[s_next] - V_acc[s]
for k in E_acc:
E_acc[k] *= gamma * lam
E_acc[s] += 1.0
for k in V_acc:
V_acc[k] += alpha * delta_acc * E_acc[k]
# replacing trace
delta_rep = r + gamma * V_rep[s_next] - V_rep[s]
for k in E_rep:
E_rep[k] *= gamma * lam
E_rep[s] = 1.0
for k in V_rep:
V_rep[k] += alpha * delta_rep * E_rep[k]
print('Accumulating V =', {k: round(v, 6) for k, v in V_acc.items()})
print('Replacing V =', {k: round(v, 6) for k, v in V_rep.items()})
print('Accumulating E =', {k: round(v, 6) for k, v in E_acc.items()})
print('Replacing E =', {k: round(v, 6) for k, v in E_rep.items()})
状態再訪が多い問題では accumulating trace が更新を強めやすく、発散気味な問題では replacing trace が安定しやすい傾向があります。どちらがよいかは、責任をどれだけ強く残したいかで決まります。