TD法

TD 法は、強化学習が「終端まで待たずに学ぶ」ための最初の一歩です。モンテカルロ法のように最後まで足し切る代わりに、1 ステップ先の推定値を借りて今を更新します。

参考動画(外部)

授業本編ではなく、別の説明で見直したいときの参考材料です。

TD法で初めて「未来を推定値で代用する」

ここでの主役は TD 誤差 δ=r+γV(s)V(s)\delta = r + \gamma V(s') - V(s) です。まだ見えていない将来全体を使わず、次状態の価値推定を借りて今を更新するので、オンラインで学習できます。

Monte Carlo 法との違いは、終端まで待つかどうかだけではありません。TD 法は自分の推定値を材料にして自分を更新するので、速い代わりに bootstrap 特有の癖も持ちます。この notebook では、その癖がどこで生まれるかを 1 ステップ更新で掴みます。

まず参照値を作ってから、bootstrapping の差分を見る

最初に Monte Carlo return を置くのは、TD の目標値が何を省略しているかを見やすくするためです。そのうえで r+γV(s)r + \gamma V(s') がどれだけ近似になっているかを読みます。

この notebook の見どころ

Monte Carlo の full return、TD(0) の 1-step update、そこから Q 学習へつながる max の導入、探索が更新の前提としてどこへ入るかを順番に追います。

探索セルは主役ではなく、更新に必要な遷移をどう集めるかを見る補助です。ここでは exploration を理論比較するより、TD 更新の周辺にある実務上の前提として置いています。

読み方の軸

TD 法の本質は「完全な未来を待たずに、次状態の推定で今を動かす」ことです。max や policy の違いは後続 notebook で増えていきますが、骨格はここで出揃います。

ここで押さえる境界

この notebook は TD(0) の骨格を掴むためのものです。探索戦略や制御性能の優劣ではなく、bootstrapping という更新原理に集中して読んでください。

手を動かす 1: Monte Carlo 参照値を作る

TD誤差を観察する前に、終端まで足し切った割引収益を参照用に作ります。これは bootstrapping ではなく、Monte Carlo return です。

rewards = [0.3, 0.0, 0.7, 1.0]
gamma = 0.87
g = 0.0
for r in reversed(rewards):
    g = r + gamma * g
print('task = td-learning', 'mc_return=', round(g, 6))

この値は終端までの実報酬を全部使った Monte Carlo return です。次のセルではこれを参照値として置き、TD(0) が r + gamma * V(s') という bootstrapped target で近似的に学ぶ様子を見ます。

手を動かす 2: TD(0) の状態価値更新を1回行う

次に、TD(0) で状態価値を 1 ステップだけ更新します。まずは max を使わない評価側の更新を見て、TD 法の中心であるブートストラップをはっきり区別します。

alpha = 0.2
V = {'s0': 0.3, 's1': 0.8}
r, s, s_next = 1.0, 's0', 's1'
td_target = r + gamma * V[s_next]
td_error = td_target - V[s]
V[s] = V[s] + alpha * td_error
print('TD target =', round(td_target, 6))
print('TD error =', round(td_error, 6))
print('updated V(s0)=', round(V[s], 6))

TD(0) では、r + gamma * V(s') を 1 ステップ先の目標値として使い、今の V(s) を少しだけ寄せます。これが TD 法の基本で、次のセルで Q 学習との差分として max を導入します。

数式メモ

  1. Gt=k0γkRt+k+1G_t = \sum_{k\ge 0} \gamma^k R_{t+k+1}
  2. V(st)V(st)+α[rt+1+γV(st+1)V(st)]V(s_t) \leftarrow V(s_t) + \alpha[r_{t+1} + \gamma V(s_{t+1}) - V(s_t)]

手を動かす 3: Q学習との差分を見る

ここでは TD の枠組みを Q 学習へ拡張し、状態価値 V の更新が行動価値 Qmax を使う形へどう変わるかを確認します。

Q = {('s0','left'): 0.3, ('s0','right'): 0.1, ('s1','left'): 0.5, ('s1','right'): 0.7}
alpha = 0.2
r, s, a, s_next = 1.0, 's0', 'right', 's1'
td_target = r + gamma * max(Q[(s_next,'left')], Q[(s_next,'right')])
Q[(s,a)] += alpha * (td_target - Q[(s,a)])
print('Q(s0,right)=', round(Q[(s,a)], 6))

更新後の値が過去の値とどれだけ違うかは、学習率と TD 誤差で決まります。ここが調整ポイントです。

手を動かす 4: 探索と活用の切り替え

次に、探索率を変えたときの行動選択を見ます。探索不足は局所最適に閉じる典型的な原因です。

import random

random.seed(7)


def choose_action(q_left, q_right, epsilon):
    greedy = 'left' if q_left >= q_right else 'right'
    if random.random() < epsilon:
        action = random.choice(['left', 'right'])
        return {'mode': 'explore', 'action': action, 'greedy': greedy}
    return {'mode': 'greedy', 'action': greedy, 'greedy': greedy}


for eps in [0.5, 0.1]:
    out = choose_action(0.4, 0.7, eps)
    print(f"epsilon={eps:.1f}", 'mode=', out['mode'], 'action=', out['action'], 'greedy=', out['greedy'])

探索率は固定せず、学習段階に応じて減衰させるのが一般的です。初期は広く探索し、後半で活用へ寄せます。

手を動かす 5: 方策評価の簡易チェック

最後に、方策の平均報酬を簡易的に比較します。アルゴリズムの評価は、更新式だけでなく結果の検証が不可欠です。

episode_rewards = [1.2, 0.8, 1.5, 1.1, 1.4]
avg_reward = sum(episode_rewards) / len(episode_rewards)
variance = sum((r - avg_reward) ** 2 for r in episode_rewards) / len(episode_rewards)
print('avg =', round(avg_reward, 4))
print('var =', round(variance, 4))

平均だけでなく分散を見ると、方策の安定性も評価できます。実運用ではこの二軸が重要です。

この節の要点

  1. TD 法は、将来全体の代わりに次状態の推定値で今を更新する。
  2. Monte Carlo return は参照値であり、TD target そのものではない。
  3. 後続の Q 学習や SARSA は、この TD 更新を行動価値へ広げたものとして読める。