深層強化学習を用いたSnake AIの構築
本稿では、強化学習アルゴリズムを活用したSnake AIの実装について、理論的背景から実装上の注意点、最適化手法に至るまで包括的に解説する。対象コードはGitHub上で公開されている林亦LYi氏による実装をベースとしている。
理論的基盤
機械学習の概要
機械学習は 크게大きく三つのパラダイムに分類できる。教師あり学習(Supervised Learning)はラベル付きデータを用いて予測モデルを構築する手法であり、画像分類や回帰分析などのタスクに適している。教師なし学習(Unsupervised Learning)はラベルなしでデータのパターンや構造を発見することを目的とし、、クラスタリングや次元削減などに用いられる。半教師あり学習(Semi-supervised Learning)はこれらの中間に位置し、少量のラベル付きデータと大量のラベルなしデータを組み合わせて学習する手法である。
一方、強化学習(Reinforcement Learning、RL)は根本的に異なるアプローチを取る。強化学習では、エージェント(Agent)が環境(Environment)与えられる報酬(Reward)を最大化するように行動を選択する方法を学習する。具体的には、エージェントが現在状態(State)において行動(Action)を取り、その結果として報酬を受け取り、次の状態に遷移するという過程を繰り返す。この報酬 сигナルを通じて、エージェントは試行錯誤を通じて最適な方策(Policy)を獲得していく。
強化学習の手法分類
強学習アルゴリズムは大きく二種類の手法に分類される。
価値関数ベースの手法は、状態や行動の価値を数値化し、その価値関数を最適化するアプローチである。Q-learningやDQN(Deep Q-Network)などが代表的であり、離散的な行動空間を持つ問題に対して特に有効である。囲碁や Atari ゲームなどで顕著な成功を収めている。
方策ベースの手法は、直接パラメータ化された方策関数を最適化するアプローチである。方策勾配法(Policy Gradient)がその代表例であり、連続的な行動空間を持つ問題に適している。、ロボット制御や連続的な動作生成などの分野で広く応用されている。
Actor-Critic アルゴリズムは、これら二つのアプローチを組み合わせた手法である。Actor が方策を更新し、Critic が価値関数を学習して方策更新の方向性を提供する。実質的には方策ベースの手法の一つだが、価値関数を補助的に学習することで学習の効率性と安定性を向上させている。
方策勾配法の改良版として、TRPO(Trust Region Policy Optimization)やPPO(Proximal Policy Optimization)が提案されている。PPOは実装の簡便性と高い性能から、現在最も広く使用されている強化学習アルゴリズムの一つである。
方策勾配アルゴリズムの原理
方策勾配法は、パラメータ化された方策π(a|s;θ) を直接最適化する手法である。方策は通常ニューラルネットワークとして実装され、そのパラメータθは勾配上昇法によって更新される。
目標関数は通常、期待累積報酬として定義される。具体的には、開始状態から終了状態までの割引累積報酬の期待値を最大化することを目指す。この目標関数を方策パラメータについて微分したものを方 Policy Gradient と呼び、勾配の方向に沿ってパラメータを更新することで、期待報酬を最大化する方策を発見する。
更新プロセスは以下のステップを繰り返す。まず、パラメータを初期化し、方 Policy を決定する。次に、環境との相互作用データ(状態、行動、報酬の系列)を収集する。その後、収集したデータを用いて勾配を計算し、パラメータを更新する。このサイクルを方 Policy が収束するまで反復する。
勾配上昇(Gradient Ascent)は報酬関数を最大化させる方向へパラメータを更新する手法であり、勾配降下(Gradient Descent)は損失関数を最小化させる方向へ更新する手法である。両者は数学的には符号が異なるだけで本質的に同じ原理に基づいている。
PPO(Proximal Policy Optimization)アルゴリズム
PPOは 方 Policy 勾配法の実用的な改良版として開発されたアルゴリズムである。従来の方 Policy 勾配法では、更新步長が小さすぎると学習が遅くなり、大きすぎると方 Policy が大きく崩れて学習が不安定になるという問題があった。
PPOは重要度サンプリング(Importance Sampling)とクリップ機構(Clipping Mechanism)を導入することで、この問題に対処している。重要度サンプリングにより、過去の古い方 Policy で収集したデータを現在の更新に活用できるようになり、サンプリング効率が向上する。クリップ機構は、方 Policy の更新率を制限し、大幅な方 Policy 崩壊を防止する。
これらの機構により、PPOは高いサンプル効率と学習の安定性を両立させている。OpenAI の研究により、Atari ゲームや連続制御タスクにおいて、従来手法と同等以上の性能を達成することが示されている。
MLP(Multi-Layer Perceptron)の構造と動作原理
MLPは、三層以上のニューラルネットワークで構成される前馈型ニューラルネットワークである。入力層と出力層のみを持つ単層ネットワークは線形分離可能な問題しか扱えないが、隠れ層を導入することで非線形な複雑な関数を近似できるようになる。
入力層(Input Layer)は外部からの特徴ベクトルを受け取る層であり、各ニューロンが一つの入力特徴に対応する。隠れ層(Hidden Layers)は入力層と出力層の間に位置し、複数の層を持つことができる。各層のニューロンは前一層の全ニューロンと結合しており、この結合を全結合(Fully Connected)と呼ぶ。活性化関数(Activation Function)は非線形性を導入する重要な要素であり、ReLUやSIGMOIDなどが一般的に使用される。
出力層(Output Layer)はタスクに応じた設計が行われる。分類タスクでは Softmax 関数を、出力タスクでは線形活性化関数を使用することが多い。
順伝播(Forward Propagation)では、入力信号が隠れ層を通って出力層まで伝播する。各層で加重和を計算し、活性化関数を適用することで出力を生成する。逆伝播(Backward Propagation)では、出力層から入力層に向かって誤差を伝播させ、各層の重みに関する勾配を計算する。計算された勾配を用いて、確率的勾配降下法或其の最適化アルゴリズムによって重みが更新される。
CNN(Convolutional Neural Network)の構造
CNNは主に画像データ処理に特化したニューラルネットワークアーキテクチャである。畳み込み層(Convolutional Layer)は画像から局所的な特徴を抽出する役割を担う。フィルタ(またはカーネル)と呼ばれる小さな行列を入力画像上で滑动させ、要素ごとの積和演算を行うことで、のエッジやテクスチャなどの低レベル特徴を検出する。
プーリング層(Pooling Layer)は、特徴マップの次元数を削減し、的位置の変動に対する不変性を獲得する。最大プーリング(Max Pooling)は対象領域の最大値を代表値として選択し、平均プーリング(Average Pooling)は平均値を計算する。これらにより、計算量の削減と過学習の防止が達成される。
全結合層(Fully Connected Layer)は、畳み込み層とプーリング層で抽出された特徴を集約し、最終的な分類或は回帰の出力を生成する。出力層はタスクに応じた形式で結果を提供する。
CNNは逆伝播アルゴリズムを用いて、畳み込み層のフィルタ重みと全結合層の重みを同時に最適化する。予測値と実際の値との誤差を最小化するようにパラメータが調整される。
Python 3.12環境での実装上の注意点
Python 3.12 環境において、元のコードをそのまま実行すると互換性の問題が発生することが確認されている。主要な問題は random モジュールに関連する型の厳格化である。
# Python 3.12 以前(動作)
seed = random.randint(0, 1e9)
# Python 3.12 以降(修正が必要)
seed = random.randint(0, int(1e9))
random.randint() の第一引数と第二引数には整数が必要であるが、Python 3.12 では浮動小数点数リテラルの暗黙的な整数変換が行われなくなった。 float 型の値を明示的に int 型に変換する必要がある。
# Python 3.12 以前(動作)
food = random.sample(self.non_snake, 1)[0]
# Python 3.12 以降(修正が必要)
food = random.sample(list(self.non_snake), 1)[0]
random.sample() シーケンス(リスト、タプルなど)に対してのみ動作するため、セットなどの反復可能なオブジェクトを変換する必要がある。 list() を用いて明示的にリストに変換することで解決できる。
同様の修正が train.py や test.py ファイル内の randint() 呼び出しにも適用する必要がある。
MLPとCNNの比較分析
MLPアーキテクチャにおける観察空間と報酬設計
MLPでは、環境の状態を32ビット浮動小数点数ベクトルとして表現する。入力データは正規化され、値の範囲は-1から1の間である。この表現により、ネットワークは直接的に環境の状態を参照できる。
報酬設計は以下の三つの要素で構成される。長さ報酬(Length Reward)は Snake が成長した場合に与えられ、ゲーム終了時に最大長に達した場合には特に大きな報酬が与えられる。食物報酬(Food Reward)は Snake が食物を獲得した場合に与えられ、報酬値は実験的な関数に基づいて計算される。方向報酬(Direction Reward)は Snake の進行方向と食物との相対関係に基づいて設定され、食物に向かう場合は正の報酬、背を向ける場合は負の報酬が与えられる。
反復処理では、 Snake の頭部を1、食物を-1、蛇身を0.8から0.2まで線形に減少させる値を割り当てることで、ネットワークが異なる重要な要素を識別できるよう学習を促進する。
CNNアーキテクチャにおける観察空間と報酬設計
CNNでは、環境状態を84×84ピクセルのRGB三チャネル画像として表現する。入力データは0から255の範囲の符号なし8ビット整数で表される。この視覚的表現により、ネットワークは画像処理の手法を活用して環境を理解する。
報酬設計はMLPとは異なる計算式が採用されている。長さ報酬はゲーム終了時に最大長に達しなかった場合にペナルティとして作用し、その計算式はより複雑な指数関数を含む。食物報酬は Snake の現在の長さと最大長の比率に基づいて計算され、0から1の範囲の値を取る。方向報酬はMLPと同様に設定される。
反復処理では、色による視覚的手がかりを活用する。 Snake の頭部を赤色、食物を青色、蛇身を緑色のグラデーションで表現することで、ネットワークが重要なオブジェクトを視覚的に識別できるようになる。
報酬関数最適化に関する考察
問題の分析
実際の学習過程で、MLPアーキテクチャを用いた場合にエージェントが原地回転しながら食物を取ろうとする行動パターンが観察される。この行動の背景には、報酬設計に起因する複数の要因が存在すると考えられる。
死亡ペナルティの過重は最も顕著な問題である。特に食物がマップの端にある状況でエージェントが原地回転する傾向が強まる。過度に大きな死亡ペナルティは、エージェントにリスク回避行動を促し、食物を取る安全な経路があっても原地回転を優先させる可能性がある。
食物報酬関数の設計も課題である。現在採用されている指数関数型の報酬は、時間経過に伴う報酬減少が先急而后緩という特性を持つ。初期段階で食物を取れなかった場合、報酬は急速に低下し、その後の追加ステップに対する報酬減少はわずかとなる。これにより、エージェントは「もう取り返せない」と判断して行動規範を失う可能性がある。
方向報酬の設定も問題の一因である。報酬とペナルティの絶対値が同等であり、割引率が適用される状況では、エージェントが一周することで正味の正の報酬を得られる可能性がある。これは、エージェントに原地回転行動を incentivo 与える結果となる。
改善案的提案
死亡ペナルティに対しては、指数関数的な減衰形式の導入を提案する。 Snake が長いほど死亡時のペナルティが軽減される設計とし、全体のペナルティ度を0.8倍に調整することで、過度なペナルティを回避する。
食物報酬関数については、二つのアプローチを検討する。第一の案は、報酬減少の特性を先緩而后急に変更し、エージェントに早期の食物取得を促す設計である。第二の案は、ステップカウンタを累積的な方式に変更し、全体の進行状況に基づいて報酬を計算するものである。ただし、第二の案はエージェントのモチベーション低下を招くリスクがあり、実装には注意が必要である。
方向報酬については、食物から離れる方向へのペナルティを増大させることで、エージェントがより直接的な食物への経路を選択するよう誘導する。これにより、原地回転行動を抑制し、目的志向的な行動を促進できる。
これらの変更を組み合わせることで、エージェントの行動パターンの改善が期待される。ただし、具体的な効果はハードウェア资源和学习パラメータに大きく依存するため、実際の実験による検証が必要である。
学習スクリプトの実装詳細
import torch
import os
from stable_baselines3 import PPO
from stable_baselines3.common.monitor import Monitor
from stable_baselines3.common.env_checker import check_env
from action_mask_action_masker import ActionMasker
# 環境数の設定(GPU使用可能な場合は倍増)
ENV_COUNT = 64 if torch.backends.mps.is_available() else 32
OUTPUT_DIR = "training_logs"
os.makedirs(OUTPUT_DIR, exist_ok=True)
def create_lr_scheduler(start_value, end_value=0.0):
"""
線形減少スケジュールを生成する。
引数:
start_value: 開始時の値(浮動小数点数または文字列)
end_value: 終了時の値
戻り値:
進行度を引数に取り、補間後の値を返す関数
"""
if isinstance(start_value, str):
start_value = float(start_value)
end_value = float(end_value)
def scheduler(progress):
return end_value + progress * (start_value - end_value)
return scheduler
def generate_environment(seed_value=0):
"""
設定されたシード値を持つSnake環境を生成する工場関数。
引数:
seed_value: 乱数シード値
戻り値:
初期化された環境オブジェクト
"""
def initializer():
env = SnakeGameEnv(seed=seed_value)
env = ActionMasker(env, SnakeGameEnv.get_action_mask)
env = Monitor(env)
env.reset(seed=seed_value)
return env
return initializer
def main():
# メイン学習処理の実行
if __name__ == "__main__":
main()
本稿で解説した実装は、GitHub上の linayiLYi/snake-ai リポジトリを参考としている。強化学習の基礎概念から実践的な実装まで涵盖了しており、 深層強化学習の学習において優れた参考資料となる。