強化学習でスーパーマリオ1-1をクリアする
人間誰しも一度はAIを使ってスーパーマリオをクリアしたいと思ったことがあるかと思います。
本記事では、難しいことを考えずとりあえず便利なツールを使ってAIにスーパーマリオ1-1をクリアさせたいと思います。
スーパーマリオのROMを自分で用意する必要もありませんし、難しいアルゴリズムも理解する必要がありません。
「とにかく動かしたい!」という人向けです。
前提
・pythonを書ける。
・OpenAI Gymを知っている。
実行環境を作る
利用言語:python3.6
必要なライブラリ
・gym
・stable_baselines
・gym_super_mario_bros
・tensorflow==1.14.0(stable_baselinesが2.Xに対応してないようです。)
・pyqt5
・imageio
Open AI Gymのインストール方法
gym.openai.com
stable_baselinesのインストール方法
github.com
gym_super_mario_brosのインストール方法
github.com
tensorflow/pyqt5/imageioのインストール方法
$ pip install tensorflow==1.14.0 $ pip install pyqt5 $ pip install imageio
Stable Baselinesとは
Stable Baselineは、Open AIが提供する「OpenAI Baselines」の改良版です。
OpenAI Baselinesとは強化学習アルゴリズムの実装セットライブラリで、難しいアルゴリズムを簡単にゲームに応用することができます。
Stable BaselineとOpenAI Baselinesの比較は以下の様になっているそうです。
スーパーマリオの環境を見てみる
学習させるためにはまず環境が必要になります。
今回はインストールしたgym_super_mario_brosを使って環境を作ります。
以下を実行してみてください。
from nes_py.wrappers import JoypadSpace import gym_super_mario_bros from gym_super_mario_bros.actions import SIMPLE_MOVEMENT env = gym_super_mario_bros.make('SuperMarioBros-v0') env = JoypadSpace(env, SIMPLE_MOVEMENT) done = True for step in range(5000): if done: state = env.reset() state, reward, done, info = env.step(env.action_space.sample()) env.render() env.close()
おそらくこのような画面が出てきたと思います。これが今回学習する環境となります。
今回はランダムに動作をさせているので適当な動きをしています。
環境を作る
画像処理
上記の環境ではそのまま学習を行ってもなかなかうまく学習が進まないことが多いです。
そのため、上記の画面をAIが学習しやすいように処理をしてやります。
試行錯誤の結果以下で学習がうまく行きました。
from nes_py.wrappers import JoypadSpace import gym_super_mario_bros from gym_super_mario_bros.actions import SIMPLE_MOVEMENT,RIGHT_ONLY from stable_baselines.common.vec_env import DummyVecEnv from baselines.common.retro_wrappers import * from stable_baselines.bench import Monitor def make_env(): env = gym_super_mario_bros.make('SuperMarioBros-v3') # 画像荒く env = JoypadSpace(env, RIGHT_ONLY) # env = CustomRewardAndDoneEnv(env) # 報酬とエピソード完了の変更 <- 後ほど説明 env = StochasticFrameSkip(env, n=4, stickprob=0.25) # スティッキーフレームスキップ env = Downsample(env, 2) # ダウンサンプリング env = FrameStack(env, 4) # フレームスタック env = ScaledFloatFrame(env) # 状態の正規化 env = Monitor(env, log_dir, allow_early_resets=True) env.seed(0) # シードの指定 set_global_seeds(0) env = DummyVecEnv([lambda: env]) # ベクトル環境の生成 return env env = make_env() done = True for step in range(5000): if done: state = env.reset() env.render() env.close()
以下のような画面が出てきたかと思います。
もはや、「マリオとは・・」という感じですがこのような画像処理が大事になります。
報酬の設定
報酬などの説明は以下の記事を参考にしてください。
qiita.com
上記のコード内のここで報酬を設定しています。
env = CustomRewardAndDoneEnv(env) # 報酬とエピソード完了の変更 <- 後ほど説明
ここでは、マリオの位置が前回の位置よりも右にあれば報酬を+1,それ以外であれば報酬を-1にしています。
また、Goalをすると報酬を+2にしています。
# CustomRewardAndDoneラッパー class CustomRewardAndDoneEnv(gym.Wrapper): # 初期化 def __init__(self, env): super(CustomRewardAndDoneEnv, self).__init__(env) self._cur_x = 0 self._max_x = 0 self.reward = 0 # リセット def reset(self, **kwargs): self._cur_x = 0 self._max_x = 0 self.reward = 0 return self.env.reset(**kwargs) # ステップ def step(self, action): state, reward, done, info = self.env.step(action) # 報酬の変更 if (info['x_pos'] > self._cur_x) & (self._cur_x != 0): # reward += 1 self.reward += 1 else: # reward -= 1 self.reward -= 1 self.reward /= 1000 self._cur_x = info['x_pos'] if info['life'] <= 1: self.reward -= 0.3 if info['life'] == 1: done = True # エピソード完了の変更 if info['flag_get']: self.reward += 2 done =True print('GOAAL') return state, self.reward, done, info
学習させる
github.com
上記のコードをcloneして、以下を実行すればとりあえず学習が始まると思います。
$ python mario_baseline.py
mario_baseline.py
from nes_py.wrappers import JoypadSpace import gym_super_mario_bros from gym_super_mario_bros.actions import SIMPLE_MOVEMENT,RIGHT_ONLY import time from stable_baselines import PPO2,DQN from stable_baselines.common.policies import CnnPolicy from stable_baselines.common.vec_env import DummyVecEnv from baselines.common.retro_wrappers import * from stable_baselines.bench import Monitor from util import CustomRewardAndDoneEnv,log_dir,CustomCallback from stable_baselines.common import set_global_seeds from stable_baselines.common.callbacks import * from stable_baselines.deepq.policies import CnnPolicy as DQNCnnPolicy def make_env(): env = gym_super_mario_bros.make('SuperMarioBros-v3') env = JoypadSpace(env, RIGHT_ONLY) env = CustomRewardAndDoneEnv(env) # 報酬とエピソード完了の変更 env = StochasticFrameSkip(env, n=4, stickprob=0.25) # スティッキーフレームスキップ env = Downsample(env, 2) # ダウンサンプリング # env = Rgb2gray(env) # グレースケール env = FrameStack(env, 4) # フレームスタック env = ScaledFloatFrame(env) # 状態の正規化 env = Monitor(env, log_dir, allow_early_resets=True) env.seed(0) # シードの指定 set_global_seeds(0) env = DummyVecEnv([lambda: env]) # ベクトル環境の生成 print('行動空間: ', env.action_space) print('状態空間: ', env.observation_space) return env env = make_env() custom_callback = CustomCallback(env,render=True) model = PPO2(policy=CnnPolicy, env=env, verbose=0,learning_rate=0.000025,tensorboard_log=log_dir) # モデル定義 model.learn(total_timesteps=2000000, callback=custom_callback) # 学習 model = PPO2.load('./agents/best_mario_ppo2model', env=env, verbose=0) # ベストなモデルを読み込む state = env.reset() total_reward = 0 while True: # 環境の描画 env.render() # スリープ time.sleep(1/25) # モデルの推論 action, _ = model.predict(state) # 1ステップ実行 state, reward, done, info = env.step(action) total_reward += reward[0] # エピソード完了 if done: print('reward:', total_reward) state = env.reset() total_reward = 0 # state = env2.reset()
学習はPCのスペックにもよりますが普通のPCだと12時間程度かかるかと思います。
学習の過程はtensorboradを用いることで以下のように確認できます。
$ tensorboard --logdir=./logs/
「http://localhost:6006」にアクセスすれば以下のような画面が出てくるかと思います。
学習がうまく行っていれば、rewardが徐々に上がっていくかと思います。(学習がうまく行かない場合は報酬や画像処理の見直しをおすすめします)
学習後
私の環境では、2~3回に1回ゴールするようになりました。
自分のAIがゲームをクリアするの嬉しいですね😢
おわりに
今回は強化学習を用いてスーパーマリオ1-1をクリアしました。
みなさんも、自分なりに報酬を変更したりして試して見てください。
OpenAI Gymなどの他のゲームも試してみると面白いと思います!👾
参考
OpenAI Gym / Baselines 深層学習・強化学習 人工知能プログラミング 実践入門 | 布留川 英一, 佐藤 英一 |本 | 通販 | Amazon