一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • 強化学習の基礎:Q学習からDQNまでをPythonで実装する

強化学習とは何か

強化学習(Reinforcement Learning)は、エージェントが環境と相互作用しながら、報酬を最大化する行動方針(ポリシー)を学習する機械学習の一分野です。教師あり学習とは異なり、正解ラベルが与えられるのではなく、行動の結果として得られる報酬信号を頼りに学習を進めます。

本記事では、強化学習の最も基本的なアルゴリズムであるQ学習から、深層学習と組み合わせたDQN(Deep Q-Network)までを、Pythonの実装コードとともに段階的に解説します。理論だけでなく、実際に動くコードで手を動かしながら理解を深められる構成になっています。

マルコフ決定過程(MDP)の基礎

強化学習の多くのアルゴリズムは、マルコフ決定過程(Markov Decision Process、MDP)という数学的フレームワークに基づいています。MDPは以下の要素で構成されます。

  • 状態(State):エージェントが認識する環境の状態
  • 行動(Action):エージェントが取れる行動の集合
  • 遷移確率:ある状態である行動を取った場合に次の状態に遷移する確率
  • 報酬(Reward):行動の結果として得られる即時報酬
  • 割引率(Gamma):将来の報酬をどの程度重視するかを決めるパラメータ

マルコフ性(現在の状態のみが将来の状態を決定する性質)が成り立つ問題設定では、Q学習が理論的に最適解に収束することが証明されています。

Q学習の理論と実装

Q値とベルマン方程式

Q学習の核心は、状態と行動のペアに対する価値(Q値)をテーブルとして管理し、経験を通じて更新していくことです。Q値の更新はベルマン方程式に基づきます。

直感的に説明すると、Q値は「この状態でこの行動を取ったら、将来的にどれだけの報酬が得られるか」の期待値です。

Q学習の実装

OpenAI Gymの「FrozenLake」環境を使って、Q学習を実装してみましょう。

import numpy as np
import gymnasium as gym

def q_learning(env, episodes=10000, alpha=0.1, gamma=0.99, epsilon=1.0, epsilon_decay=0.9995, epsilon_min=0.01):
q_table = np.zeros((env.observation_space.n, env.action_space.n))
rewards_history = []

for episode in range(episodes):
state, _ = env.reset()
total_reward = 0
done = False

while not done:
if np.random.random() < epsilon:
action = env.action_space.sample()
else:
action = np.argmax(q_table[state])

next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated

best_next = np.max(q_table[next_state])
q_table[state, action] += alpha * (
reward + gamma * best_next * (1 - terminated) - q_table[state, action]
)

state = next_state
total_reward += reward

epsilon = max(epsilon_min, epsilon * epsilon_decay)
rewards_history.append(total_reward)

if (episode + 1) % 1000 == 0:
avg = np.mean(rewards_history[-1000:])
print(f"Episode {episode+1}, Avg Reward: {avg:.3f}, Epsilon: {epsilon:.3f}")

return q_table, rewards_history

env = gym.make('FrozenLake-v1', is_slippery=True)
q_table, history = q_learning(env)

このコードで注目すべきポイントは、epsilon-greedy戦略です。学習初期は高い確率でランダムな行動を取り(探索)、学習が進むにつれて学習済みのQ値に従った行動を増やしていきます(活用)。この探索と活用のバランスが、強化学習の成否を分ける重要な要素です。

テーブル型の限界とDQNへの発展

Q学習はシンプルで強力ですが、状態空間が大きくなるとQテーブルのサイズが爆発的に増加するという根本的な限界があります。この限界を克服するために登場したのが、DQN(Deep Q-Network)です。DQNは、Qテーブルの代わりにニューラルネットワークでQ値を近似します。

DQNの実装

PyTorchを使ってDQNを実装しましょう。ここではCartPole環境を使用します。

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import gymnasium as gym
from collections import deque
import random

class DQNetwork(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.network = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, 128),
nn.ReLU(),
nn.Linear(128, action_dim)
)

def forward(self, x):
return self.network(x)

class ReplayBuffer:
def __init__(self, capacity=10000):
self.buffer = deque(maxlen=capacity)

def push(self, state, action, reward, next_state, done):
self.buffer.append((state, action, reward, next_state, done))

def sample(self, batch_size):
batch = random.sample(self.buffer, batch_size)
states, actions, rewards, next_states, dones = zip(*batch)
return (
torch.FloatTensor(np.array(states)),
torch.LongTensor(actions),
torch.FloatTensor(rewards),
torch.FloatTensor(np.array(next_states)),
torch.FloatTensor(dones)
)

def __len__(self):
return len(self.buffer)

class DQNAgent:
def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99):
self.policy_net = DQNetwork(state_dim, action_dim)
self.target_net = DQNetwork(state_dim, action_dim)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.buffer = ReplayBuffer()
self.gamma = gamma
self.action_dim = action_dim

def select_action(self, state, epsilon):
if random.random() < epsilon:
return random.randrange(self.action_dim)
with torch.no_grad():
q_values = self.policy_net(torch.FloatTensor(state))
return q_values.argmax().item()

def train_step(self, batch_size=64):
if len(self.buffer) < batch_size:
return
states, actions, rewards, next_states, dones = self.buffer.sample(batch_size)

current_q = self.policy_net(states).gather(1, actions.unsqueeze(1))
with torch.no_grad():
max_next_q = self.target_net(next_states).max(1)[0]
target_q = rewards + self.gamma * max_next_q * (1 - dones)

loss = nn.MSELoss()(current_q.squeeze(), target_q)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

DQNの重要なテクニックとして、Experience Replay(経験再生)とTarget Network(ターゲットネットワーク)があります。Experience Replayは過去の経験をバッファに保存し、ランダムにサンプリングして学習することで、データの相関を減らし学習を安定化させます。Target Networkは、Q値の更新先を固定したネットワークで計算することで、学習の発散を防ぎます。

学習と改善のポイント

  1. 報酬設計:報酬関数の設計は学習の成否を大きく左右します。スパースな報酬(ゴール時のみ報酬が出る)は学習が困難になるため、中間的な報酬を設計することが有効
  2. ハイパーパラメータ調整:学習率、割引率、イプシロンの減衰率は相互に影響するため、系統的な探索が必要
  3. 学習の可視化:報酬の推移やQ値の分布をグラフ化し、学習の進捗を常にモニタリングする
  4. Double DQN:標準DQNのQ値過大評価問題を緩和する改良版。行動選択と評価に別々のネットワークを使う

強化学習は奥が深い分野ですが、Q学習の基本を理解すれば、その後の発展的なアルゴリズム(A3C、PPO、SACなど)の理解もスムーズになります。まずは本記事のコードを動かしてみて、パラメータを変えながら挙動の変化を観察してみてください。実験を通じた直感が、この分野では何よりも重要です。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア