メインコンテンツまでスキップ

TD学習(時間的差分学習)とは?

TD学習のコンセプト (画像はGeminiで作成)

TD学習の概要

TD学習(Temporal Difference Learning:時間的差分学習)は、モンテカルロ法と並ぶ強化学習の最も基礎的かつ重要なアルゴリズムの一つです。

モンテカルロ法が「エピソードが最後まで終わるのを待ってから振り返る」アプローチだったのに対し、TD学習は 「1ステップ行動するたびに、直近の報酬と『1歩先の予測』を使って、現在の予測を歩きながら修正していく」 というアプローチをとります。

例えるなら、「目的地に到着してからカーナビの所要時間予測が正しかったかを確認する」のがモンテカルロ法、「渋滞に巻き込まれた(1ステップ進んだ)時点で、到着予想時刻を逐次アップデートする」のがTD学習です。

本記事では、TD学習のコアとなる「TD誤差」の仕組みを数式とともに解説し、Pythonによるスクラッチ実装を通じてモンテカルロ法との挙動の違いを学びます。この概念は、Q学習DQN、そしてPPOなどの最先端アルゴリズムを理解するための必須知識となります。

はじめに:モンテカルロ法の弱点とTD学習のアプローチ

前回のモンテカルロ法の記事では、実際に得られた合計報酬(収益 GtG_t)の平均をとることで状態価値 V(s)V(s) を正確に学習できることを学びました。しかし、モンテカルロ法には2つの大きな弱点がありました。

  1. 最後まで待たないと学習できない: エピソードが終了するまで一切の学習が行えないため、1プレイが非常に長いゲームや、永遠に終わらないタスクには適用できません。
  2. 分散(データのブレ)が大きい: エピソードの最後まで行動するため、途中の「まぐれ」や「不運」といったランダムな要素がすべて結果に乗っかってしまい、学習が安定するまでに時間がかかります。(参考: モンテカルロ法の分散の爆発

これらを解決するのがTD学習です。エピソードの終了を待たず、ステップごとの「予測の差分」を使って学習を進めることで、よりスピーディかつデータ効率の良い学習を実現します。

TD学習のコア技術

TD学習(ここでは最も基本的な TD(0) を扱います)は、以下の要素で構成されています。

1. ブートストラップ(予測で予測を更新する)

TD学習の最大の特徴はブートストラップ(Bootstrapping) と呼ばれる性質です。これは「まだ確定していない将来の予測値を使って、現在の予測値を更新する」ことを意味します。

ある状態 StS_t にいて、行動を起こして次の状態 St+1S_{t+1} に移り、即時報酬 Rt+1R_{t+1} を得たとします。 このとき、「状態 StS_t の本当の価値」は、「今もらった報酬 Rt+1R_{t+1}」+「次の状態の価値 V(St+1)V(S_{t+1}) になるはずです。

2. TDターゲットとTD誤差

この考え方に基づき、TD学習では更新の目標値(正解ラベルのようなもの)を以下のように定めます。これを TDターゲット と呼びます。

TDターゲット=Rt+1+γV(St+1)\text{TDターゲット} = R_{t+1} + \gamma V(S_{t+1})

(※ γ\gamma は将来の報酬を割り引く割引率です) モンテカルロ法ではこの目標値が「エピソードの最後まで足し合わせた実際の収益 GtG_t」でしたが、TD学習では「1歩先の報酬+1歩先の予測価値」で代用します。

そして、このTDターゲットと「現在の予測 V(St)V(S_t)」とのズレを TD誤差(TD Error:δt\delta_t と呼びます。PPOなど多くのアクタークリティック手法で登場する非常に重要な数式です。

δt=Rt+1+γV(St+1)V(St)\delta_t = R_{t+1} + \gamma V(S_{t+1}) - V(S_t)

3. 価値の更新

算出したTD誤差を使って、状態価値を1ステップごとに以下のように更新します。

V(St)V(St)+αδtV(S_t) \leftarrow V(S_t) + \alpha \delta_t

または、展開して以下のように書くことも一般的です。

V(St)V(St)+α[Rt+1+γV(St+1)V(St)]V(S_t) \leftarrow V(S_t) + \alpha \left[ R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \right]

(※ α\alpha は学習率です) これにより、エピソードの途中であっても1ステップ進むごとにリアルタイムで学習を行うことが可能になります。

TD学習の実装(概念的な実装)

理論を理解したところで、モンテカルロ法の時と同じように、滑る氷の床の上を歩いてゴールを目指す FrozenLake-v1 を用いてTD学習を実装してみましょう。

FrozenLakeのルール

FrozenLakeは4x4のグリッドで構成されており、S(スタート)から始まり、H(穴)を避けてG(ゴール)を目指します。

行 \ 列0123
0S (スタート)F (氷)F (氷)F (氷)
1F (氷)H (穴)F (氷)H (穴)
2F (氷)F (氷)F (氷)H (穴)
3H (穴)F (氷)F (氷)G (ゴール)

報酬のルール:

  • ゴール(G)に到達した場合:報酬 +1 を得てエピソード終了。
  • 穴(H)に落ちた場合:報酬 0 でエピソード終了(ペナルティ値はありませんが、ゴール報酬も得られません)。
  • その他の氷(F)を移動中報酬 0

ライブラリのインストール

!pip install gymnasium numpy matplotlib

ここでは、強化学習の標準的な環境を提供する gymnasium、配列計算を高速に行う numpy、そして学習結果をヒートマップとして可視化するための matplotlib をインストールしています。

TDAgentの定義

以下のコードでは、状態価値 V(s)V(s) をTD学習によって更新するエージェントを定義します。

import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict

# TDAgent: TD学習を用いて状態価値 V(s) を学習するエージェント
class TDAgent:
def __init__(self, gamma=0.99, alpha=0.1):
self.gamma = gamma # 割引率
self.alpha = alpha # 学習率

# 状態価値 V(s) を保存するテーブル。初期値はすべて0.0
self.V = defaultdict(float)

def learn(self, state, reward, next_state, done):
"""
1ステップの経験(s, r, s')を受け取り、リアルタイムに状態価値を更新する
"""
# 終了状態(ゴールまたは穴)の場合、その先の価値は存在しないため0とする
next_v = 0.0 if done else self.V[next_state]

# TDターゲットの計算: 数式の R_{t+1} + γV(S_{t+1}) に該当
td_target = reward + self.gamma * next_v

# TD誤差の計算: 数式の [TDターゲット - V(S_t)] に該当
td_error = td_target - self.V[state]

# 価値の更新式: V(S_t) <- V(S_t) + α * [R_{t+1} + γV(S_{t+1}) - V(S_t)]
self.V[state] += self.alpha * td_error

TDAgent クラスは、モンテカルロ法(MC法)の時と同様に defaultdict(float) を使ったテーブルベース(表形式)の手法を採用しています。

最大の変更点は learn メソッドです。MC法が episode(全履歴のリスト)を要求していたのに対し、TD学習では state, reward, next_state, done という「たった1ステップ分の遷移データ」だけを受け取ります。

  • 終端状態の処理: doneTrue (ゴールや穴に落ちてゲーム終了)の場合、その先の未来は存在しないため next_v を強制的に 0.0 に設定します。
  • TDターゲットの計算: 記事上部で解説した数式における Rt+1+γV(St+1)R_{t+1} + \gamma V(S_{t+1}) の部分を、コード上では td_target = reward + self.gamma * next_v として計算しています。これが「実績(1歩分の報酬)+予測(次の状態の価値)」に基づく新しい目標値です。
  • TD誤差と価値の更新: 目標値(td_target)と現在の予測値(self.V[state])の差分であるTD誤差(td_error)を算出し、それに学習率(self.alpha)を掛けて価値を更新します。逆順のループなどを回すことなく、数式 V(St)V(St)+α[Rt+1+γV(St+1)V(St)]V(S_t) \leftarrow V(S_t) + \alpha \left[ R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \right] をワンステップで直接実行しています。

訓練ループ

エージェントに FrozenLake-v1 をプレイさせます。ループの構造がモンテカルロ法からどう変わったかに注目してください。

env = gym.make('FrozenLake-v1', is_slippery=False)
agent = TDAgent(gamma=0.99, alpha=0.1)

EPISODES = 2000

print("--- TD学習 学習開始 ---")
for episode_num in range(EPISODES):
state, _ = env.reset()
done = False

# エピソードが終了するまでループ
while not done:
# ランダムに行動を選択
action = env.action_space.sample()
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated

# ★ ここが重要:1ステップ進むたびに、即座に学習(learn)を行う!
agent.learn(state, reward, next_state, done)

# 状態を更新して次のステップへ
state = next_state

print("--- TD学習 学習完了 ---")

ここでは、MC法と同様に FrozenLake-v1 環境を is_slippery=False で初期化し、ランダムウォークによる価値評価の訓練ループを実装しています。

  • 即時学習(リアルタイム更新): MC法からの決定的な違いは、while not done: のループの中でエージェントが1ステップ行動し、env.step(action) から結果を受け取った直後に、即座に agent.learn(state, reward, next_state, done) が呼び出されている点です。
  • メモリ効率の向上: 履歴をエピソード終了まで蓄積するための episode_history リストが不要になりました。これによりメモリ消費を抑えつつ、終わりのない(エピソードが無限に続く)タスクにも適用可能になります。これが「歩きながら学ぶ」TD学習の特徴をコード上で体現したものです。

実行結果と可視化

学習によって得られた状態価値 V(s)V(s) を、モンテカルロ法と全く同じ方法でヒートマップ化して確認します。

# FrozenLakeのマップ配置 (S:スタート, F:氷, H:穴, G:ゴール)
lake_map = [
['S', 'F', 'F', 'F'],
['F', 'H', 'F', 'H'],
['F', 'F', 'F', 'H'],
['H', 'F', 'F', 'G']
]

V_grid = np.zeros((4, 4))
for state in range(16):
row, col = divmod(state, 4)
V_grid[row, col] = agent.V[state]

plt.figure(figsize=(6, 6))
plt.imshow(V_grid, cmap='hot', interpolation='nearest')
plt.colorbar(label='State Value V(s)')
plt.title("FrozenLake State Values (TD Learning)")

for i in range(4):
for j in range(4):
cell_text = f"{lake_map[i][j]}\n{V_grid[i, j]:.3f}"
plt.text(j, i, cell_text, ha="center", va="center", color="cyan", fontweight="bold")

plt.xticks([])
plt.yticks([])
plt.show()

学習後のエージェントが保持している状態価値テーブル(agent.V)を、MC法と全く同じロジックでヒートマップ化しています。

  • グリッドへのマッピング: 16マスの状態を divmod(state, 4) で4×4の2次元配列 V_grid に変換します。
  • テキスト情報のオーバーレイ: plt.text() を用いて、各マスの状態価値とマップ記号(S, F, H, G)をセル上に重ねて描画しています。また、不要なXY軸の目盛りは plt.xticks([]) 等で非表示にし、純粋に価値の分布を直感的に確認できるように整えています。

実行結果

実行結果

(※実行環境により数値は変動します)

モンテカルロ法と同様に、ゴールに近いマスほど価値が高く学習できていることがわかります。 結果の見た目は似ていますが、その価値に至るまでの「学習の過程(エピソード終了を待つか、毎ステップ更新するか)」が全く異なるアプローチで達成されています。

MC法とTD法の比較(バイアスと分散のジレンマ)

強化学習において避けて通れないのが、バイアス(Bias: 予測の偏り・間違い)と分散(Variance: データのブレ・ばらつき)のトレードオフです。 両極端に位置するMC法とTD法を比較してみましょう。

アルゴリズム学習のタイミング更新の目標値(ターゲット)バイアス(予測の偏り)分散(データのブレ)
モンテカルロ(MC)法エピソード終了後実際の収益 GtG_tなし(結果の事実に基づくため)大(途中のノイズが蓄積するため)
TD学習毎ステップRt+1+γV(St+1)R_{t+1} + \gamma V(S_{t+1})大(初期の予測はデタラメなため)小(1歩分のノイズしか入らないため)

TD学習は、1ステップ先のデータしか使わないためデータのブレ(分散)が小さく、効率的に学習が進みます。しかし、初期段階では「次の状態の予測価値 V(St+1)V(S_{t+1})」自体が完全にデタラメであるため、間違った予測をベースに現在の価値を更新してしまうバイアス(思い込みの連鎖)が発生します。

PPOとQ学習への橋渡し

この「バイアスと分散のジレンマ」をどのように克服し、より複雑な問題を解くかが、現代の強化学習アルゴリズムの進化の歴史です。

  1. Q学習・DQNへの進化:今回学んだTD学習は「状態の価値 V(s)V(s)」を評価するものでしたが、これを「ある状態で特定の行動をとった時の価値 Q(s,a)Q(s, a)」に拡張し、価値が最も高い行動を常に選ぶようにしたのが Q学習 であり、それをディープラーニングと組み合わせたのが DQN です。DQNの記事で登場する「TDターゲット」という言葉は、まさにこの記事で解説した概念です。
  2. PPO(GAE)への進化:「MC法(分散が大)」と「TD法(バイアスが大)」の間には、2歩先、3歩先まで見てから予測を混ぜる「nnステップTD学習」という中間のアプローチが存在します。これを究極に推し進め、1歩先のTD誤差、2歩先のTD誤差、3歩先のTD誤差…と未来のすべてのTD誤差を、パラメータ λ\lambda(ラムダ)を用いて絶妙なバランスで足し合わせる魔法の数式が、PPO の記事で登場する GAE(Generalized Advantage Estimation: 一般化アドバンテージ推定) です。
A^t=δt+(γλ)δt+1++(γλ)Tt1δT1\hat{A}_t = \delta_t + (\gamma\lambda)\delta_{t+1} + \dots + (\gamma\lambda)^{T-t-1}\delta_{T-1}

ここで登場する δt\delta_t こそが、本記事で学んだ「TD誤差」に他なりません。

まとめ

本記事では、強化学習のもう一つの柱である「TD学習(時間的差分学習)」について解説しました。
エピソードの終了を待たず、1歩先の「予測」を用いて「現在の予測」を更新(ブートストラップ)するTD誤差の仕組みを学び、それがモンテカルロ法よりもスピーディかつ分散の少ない学習を実現することを確認しました。

モンテカルロ法とTD学習という2つの基礎を理解したことで、いよいよ実用的なアルゴリズムである「Q学習」や「DQN」、そして「PPO」のメカニズムを深く理解するための準備が整いました。ぜひ次のステップへ進んでみてください。

本記事の文章・構成の一部に生成AIを使用しています。