モンテカルロ法(Monte Carlo Method)とは?
(画像はGeminiで作成)
モンテカルロ法の概要
モンテカルロ法(Monte Carlo Method: MC法)は、強化学習においてエージェントが「経験から直接学習する」ための最も直感的で基礎的なアルゴリズムの一つです。(※一般的なモンテカルロ法自体は、1949年にNicholas MetropolisとStanislaw Ulamらによって発表された論文「The Monte Carlo Method」が起源とされていますが、強化学習の文脈においてはRichard S. Suttonらによる1998年の名著『Reinforcement Learning: An Introduction』で価値推定の基礎的な枠組みとして体系化されました。)
最大の特徴は、「エピソードが完全に終了するまで学習を待ち、最後までやり切って得られた実際の結果(合計報酬)を使って、途中の行動の良し悪しを評価する」 という点にあります。これは、例えるなら「テストを最後まで受けて、返ってきた合計点数を見てから、各科目の勉強法が正しかったかを振り返る」ようなアプローチです。
本記事では、強化学習における価値推定の基本となるモンテカルロ法の仕組みを解説し、Pythonを用いたシンプルなスクラッチ実装を通じてその挙動を学びます。この手法を理解することは、後に続くTD学習や、最新のアルゴリズムであるPPOにおける「バイアスと分散のトレードオフ」を理解するための重要な土台となります。
はじめに:強化学習における「価値」の推定
強化学習の目的は、環境の中でエージェントが得られる報酬の合計を最大化することです。そのためには、「ある状態 にいることが、将来的にどれくらい良いことなのか」という状態価値(State Value) を正確に予測する必要があります。
未知のゲーム(環境)に放り込まれたエージェントは、最初はどの状態が良いか悪いか全くわかりません。そこで、実際にゲームをプレイ(サンプリング)して、その結果から を少しずつ修正(学習)していく必要があります。モンテカルロ法は、この修正を「実際に得られた結果の平均をとる」というシンプルな方法で行います。
モンテカルロ法のコア技術
モンテカルロ法による価値の学習は、以下の3つのステップで成り立っています。
1. エピソードの完了(経験の蓄積)
まず、エージェントは現在の方策(ルール)に従って、ゲームの開始から終了(ゲームオーバーやクリア)までの1エピソードを完全にプレイし切ります。 その間に通った「状態 」と、その時に得た「即時報酬 」の履歴(軌跡)をすべて記録します。
2. 収益(Return)の計算
エピソードが終了したら、時間を遡るようにして収益 を計算します。 収益とは、ある時点 からエピソードの最後までにもらえた報酬の合計値(割引報酬和)のことです。REINFORCEやA2C、PPO等のアルゴリズムでも、「実際に得られた将来までの報酬の合計」として登場する強化学習において非常に重要な概念です。
(※ は将来の報酬をどれくらい重視するかを決める割引率です。)
3. 価値の更新(平均化)
過去にその状態 を訪れた際に得られたすべての収益 の平均値を計算し、それを新たな状態価値 とします。 プログラム上では、すべての履歴を保持して平均を計算し直すのはメモリの無駄になるため、以下のような「逐次更新式」を用いて、新しい結果が得られるたびに少しずつ予測値を修正します。
ここで、
- は学習率(新しく得た結果をどれくらい信用して予測を修正するか)
- は、「実際の結果」と「事前の予測」とのズレ(誤差)
を意味します。この式を繰り返すことで、予測値 は徐々に真の価値(実際の結果の平均)へと近づいていきます。
モンテカルロ法の実装(概念的な実装)
理論を理解したところで、実際にモンテカルロ法をPyTorchを用いずに純粋なPythonで実装してみましょう。環境には、滑る氷の床の上を歩いてゴールを目指すシンプルな迷路タスクである FrozenLake-v1 を使用します。
FrozenLakeのルール
FrozenLakeは4x4のグリッドで構成されており、S(スタート)から始まり、H(穴)を避けてG(ゴール)を目指します。
| 行 \ 列 | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 0 | S (スタート) | F (氷) | F (氷) | F (氷) |
| 1 | F (氷) | H (穴) | F (氷) | H (穴) |
| 2 | F (氷) | F (氷) | F (氷) | H (穴) |
| 3 | H (穴) | F (氷) | F (氷) | G (ゴール) |
報酬のルール:
- ゴール(G)に到達した場合:報酬 +1 を得てエピソード終了。
- 穴(H)に落ちた場合:報酬 0 でエピソード終了(ペナルティ値はありませんが、ゴール報酬も得られません)。
- その他の氷(F)を移動中:報酬 0。
ライブラリのインストール
Gymnasiumなどの強化学習環境と、数値計算用のNumPy、プロット用のMatplotlibをインストールします。
!pip install gymnasium numpy matplotlib
ここでは、強化学習の標準的な環境を提供する gymnasium、配列計算を高速に行う numpy、そして学習結果をヒートマップとして可視化するための matplotlib をインストールしています。
MCAgentの定義
以下のコードでは、状態価値を推定するためのモンテカルロ法エージェントを定義します。 今回は行動の最適化(方策制御)ではなく、「ランダムに歩き回った場合、どのマスがどれくらい安全で価値があるのか?」という状態価値の評価(予測)に焦点を当てます。
import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
# MCAgent: モンテカルロ法を用いて状態価値 V(s) を学習するエージェント
class MCAgent:
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, episode):
"""
1エピソード分の履歴を受け取り、状態価値を更新する
episode: [(state, reward), (state, reward), ...] のリスト
"""
G = 0 # 収益の初期化
visited_states = set() # 初回訪問MC法のためのセット
# エピソードの終端から開始地点に向かって逆順に計算する
# (数式: G_t = R_{t+1} + γR_{t+2} + ... を効率よく再帰計算)
for t in reversed(range(len(episode))):
state, reward = episode[t]
# 収益 G の計算 (1ステップ戻るごとに割引率γを掛け、その時点の報酬を足す)
G = self.gamma * G + reward
# 初回訪問MC法 (First-visit MC):
# 1つのエピソード内で同じ状態を複数回訪れた場合、最初の訪問時のみ更新する
if state not in visited_states:
visited_states.add(state)
# 価値の更新式: V(S_t) <- V(S_t) + α * [G_t - V(S_t)]
self.V[state] = self.V[state] + self.alpha * (G - self.V[state])
MCAgent クラスは、ディープニューラルネットワークなどの関数近似器を使用せず、defaultdict(float) を用いて各状態 ごとの価値を直接記録する**テーブルベース(表形式)**の手法を採用しています。初期値はすべて0.0に設定されます。
learn メソッドがモンテカルロ法の核心部です。ここでは引数として「1エピソード分の完全な履歴」を受け取り、以下の処理を行います。
- 収益の逆順計算: 配列の後ろ(ゲーム終了時)から前(ゲーム開始時)に向かって reversed(range(len(episode))) で逆ループを回します。これにより、数式 をそのまま愚直に計算するのではなく、G = self.gamma * G + reward という漸化式を用いて、再帰的かつ効率よく収益 を計算しています。
- 初回訪問MC法 (First-visit MC): visited_states というSetを用いて、1つのエピソード内で同じ状態を複数回訪れた場合でも、最初に訪れた時のみ 価値を更新するように制御しています。これにより、予測の偏り(バイアス)を防ぎます。
- 価値の逐次更新: 状態価値の更新式 を、コード上では self.V[state] = self.V[state] + self.alpha * (G - self.V[state]) としてそのまま実装しています。計算された実際の収益(G)と現在の予測値(self.V[state])の差分に学習率(self.alpha)を掛け合わせることで、予測値を実際の結果に徐々に近づけています。
訓練ループ
エージェントに FrozenLake-v1 を何度もプレイさせ、価値を学習させます。
env = gym.make('FrozenLake-v1', is_slippery=False)
agent = MCAgent(gamma=0.99, alpha=0.1)
EPISODES = 2000
print("--- モンテカルロ法 学習開始 ---")
for episode_num in range(EPISODES):
state, _ = env.reset()
episode_history = []
done = False
# 1. エピソードが終了するまでデータを収集(サンプリング)
while not done:
# 今回は価値評価のため、ランダムに行動を選択
action = env.action_space.sample()
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# 状態と「次に得た報酬」のペアを履歴に保存
episode_history.append((state, reward))
state = next_state
# 2. エピソード終了後、履歴を使って価値を更新(ここで初めて学習が行われる)
agent.learn(episode_history)
print("--- モンテカルロ法 学習完了 ---")
ここでは、Gymnasiumから提供される FrozenLake-v1 環境を初期化し、2000エピソードにわたってエージェントを学習させる訓練ループを実装しています。
- 決定論的な環境設定: 初学者の理解を助けるため、is_slippery=False を指定し、氷の上で滑らない(選んだ方向に必ず進む)設定にしています。
- ランダムなサンプリング: 今回の目的は方策の最適化(賢い行動を見つけること)ではなく「価値の評価」であるため、env.action_space.sample() を用いて完全にランダムに歩き回る方策(ランダムウォーク)を実行し、データを収集しています。
- 遅延された学習(モンテカルロ法の特徴): while not done: のループの中(エージェントが行動している最中)では一切の学習が行われません。状態と報酬のペアを episode_history.append((state, reward)) で単に蓄積していき、エピソードが完全に終了してループを抜けた後に初めて agent.learn(episode_history) が呼ばれます。これが「最後までやり切った結果(事実)のみを用いて学習する」というモンテカルロ法の最大の特徴をコード上で体現しています。
実行結果と可視化
学習によって得られた各マスの状態価値 を、マップ構造がわかるようにヒートマップとして可視化してみましょう。
import matplotlib.pyplot as plt
import numpy as np
# FrozenLakeのマップ配置 (S:スタート, F:氷, H:穴, G:ゴール)
lake_map = [
['S', 'F', 'F', 'F'],
['F', 'H', 'F', 'H'],
['F', 'F', 'F', 'H'],
['H', 'F', 'F', 'G']
]
# 状態価値テーブルを4x4のグリッドに変換
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 (Monte Carlo)")
# 各セルにマップ記号(S, G, Hなど)と数値を表示
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)を、NumPyとMatplotlibを用いて視覚的なヒートマップに変換しています。
- 1次元から2次元グリッドへの変換: FrozenLakeの16個の状態(0〜15のインデックス)を、divmod(state, 4) を用いて4行4列の2次元座標(row, col)に変換し、二次元配列 V_grid に格納しています。
- ヒートマップのレンダリング: plt.imshow(V_grid, cmap='hot') によって、価値が高いマスほど明るい色(赤〜白)、価値が低いマスほど暗い色(黒)になるように描画しています。
- テキスト情報のオーバーレイ: 描画された色のグリッドの上に、plt.text() を使って実際の状態価値の数値(小数点以下3桁)と、事前に定義したマップ記号(S, F, H, G)を重ねて表示し、どのマスの価値がどう評価されたかを直感的に分かりやすくしています。
実行結果

(※実行環境により数値はランダムに変動します)
ヒートマップを見ると、ゴール(右下)に近いマスほど価値が高く(赤く)、
スタート地点や落とし穴に近いマスほど価値が低く(黒く)なっていることがわかります。
モンテカルロ法が、エピソードごとの最終結果から逆算して、各状態の正しい価値を学習できたことが確認できます。
モンテカルロ法のメリットと限界(バイアスと分散のジレンマ)
モンテカルロ法はシンプルで直感的ですが、強化学習における「バイアスと分散のトレードオフ」という永遠の課題を理解する上で重要な立ち位置にあります。
- メリット(バイアスがない):
途中の予測に頼らず、「実際に最後までプレイして得られた結果(事実)」だけを使って学習するため、予測の間違い(バイアス)による悪影響を受けません。 - デメリット(分散が非常に大きい・分散の爆発):
PPOの記事でも触れられるように、エピソードの最後まで待って得られる実際の収益 をそのまま使うと、「途中の様々なランダムな出来事」もすべて結果に含まれてしまうため、データのブレ(分散)が極めて大きくなります。一回まぐれでゴールできた時のデータと、不運で失敗した時のデータが極端に混ざるため、学習が安定するまでに膨大な回数のエピソードが必要になります。この問題は、モンテカルロ推定に依存するREINFORCE(モンテカルロ方策勾配法)などのアルゴリズムにおいて「分散の爆発」と呼ばれる致命的な課題として立ちはだかります。 - デメリット(学習が遅い):
ゲームオーバーになるまで学習を一切行えないため、1プレイが長いタスクや、永遠に終わらない(継続的な)タスクには適用できません。
TD(時間的差分)学習への橋渡し
モンテカルロ法の「最後まで待たないと学習できない」「分散が大きい」という弱点を克服するために考案されたのが、次回解説する TD学習(時間的差分学習) です。
TD学習は、エピソードの終了を待たず、1ステップ先の実際の報酬と「次の状態の予測価値」だけを見て学習を進めます。これは1ステップ先しか見ないため分散は小さい反面、もし予測価値自体が間違っていた場合、その間違い(バイアス)に強く影響されてしまう弱点を持っています。
事実のみを信じるモンテカルロ法(分散大・バイアス小)か、予測を信じて歩きながら学ぶTD学習(分散小・バイアス大)か。このジレンマをどのように解決していくかが、PPOなどの最新アルゴリズム(GAEの導入など)へと繋がる強化学習進化のメインストーリーとなります。
まとめ
本記事では、強化学習の最も基礎的な価値推定アルゴリズムである「モンテカルロ法」について解説しました。
エピソードを最後までやり切り、得られた実際の収益を遡って各状態の価値を更新するという直感的なアプローチを学び、Pythonを用いたスクラッチ実装を通じて、エージェントが迷路の各マスの価値を正しく評価できることを確認しました。
次回は、モンテカルロ法と対をなすもう一つの強力なアプローチ「TD学習」について詳しく見ていきます。
本記事の文章・構成の一部に生成AIを使用しています。