keras-rl の example コードを実行するだけ

Keras を勉強します。
keras-rl でオリジナルの強化学習タスク・オリジナルのDQNモデルを学習したという記事が本日 Qiita に投稿されていましたが(参考記事)、まず keras-rl と gym がわからないので example コードを実行することにします。

参考記事

以下の記事を参考にさせていただきましたが、やったことは記事内容のトレースよりはるか低みです。
qiita.com

やること

強化学習で伝統的なポールバランシングタスクをエージェントに学習させます。
小学生のとき掃除の時間に、手のひらに箒をのせて倒れないようにバランスを取るのをよくやったと思います。
今回のタスクのポールの動く範囲は2次元平面内に制約されています。台車も直線上を動きます。
gym でのタスク設定は以下のページ参照。
OpenAI Gym CartPole-v0

  • その時間ステップにポールが直立していれば +1 の報酬がもらえます。
  • 選択できる行動は台車に +1 の力を加えるか、-1 の力を加えるかのどちらかです。
  • ポールが15°以上傾くか、台車がスタート位置から所定の距離以上右か左へ外れてしまったらゲームオーバー(エピソード終了)です。

これを行動価値関数  Q のパラメタライズされた関数で近似し最適パラメータを解くんですが、今回は関数というかディープネットにしてしまい、ディープネットの重みの最適化を図ります。

手順

keras はもう導入済みなので、参考記事通りに keras-rl と gym を導入します。
そして今回は以下のコードをそのまま実行するだけです。
https://github.com/matthiasplappert/keras-rl/blob/master/examples/dqn_cartpole.py

# -*- coding: utf-8 -*-
import numpy
import gym

from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam

from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory

if __name__ == "__main__":
  # 強化学習タスクの環境を構築する
  ENV_NAME = 'CartPole-v0'
  env = gym.make(ENV_NAME)
  numpy.random.seed(123)
  env.seed(123)
  nb_actions = env.action_space.n

  # DQNモデルを準備する
  model = Sequential()
  model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
  model.add(Dense(16))
  model.add(Activation('relu'))
  model.add(Dense(16))
  model.add(Activation('relu'))
  model.add(Dense(16))
  model.add(Activation('relu'))
  model.add(Dense(nb_actions))
  model.add(Activation('linear'))
  print(model.summary())

  # DQNモデルを最適化する上での目的関数の設定
  memory = SequentialMemory(limit=50000, window_length=1)
  policy = BoltzmannQPolicy()
  dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory,
                 nb_steps_warmup=10, target_model_update=1e-2, policy=policy)
  dqn.compile(Adam(lr=1e-3), metrics=['mae'])

  # トレーニング
  dqn.fit(env, nb_steps=50000, visualize=True, verbose=2)

  # トレーニングした重みの保存
  dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

  # テスト
  dqn.test(env, nb_episodes=5, visualize=True)

print(model.summary()) で以下のようにモデルの姿を出力してくれます。パラメータ数などわかりやすいです。
当たり前ですが、各層で最適化すべきパラメータ数は、前の層のアウトプットの数 + 1(定数項) に、その層のアウトプットの数を乗じたものになっています。
最終的な出力2は、「+1 の力を入れることの価値」「-1の力を入れることの価値」になっているはずです。

[2017-01-15 23:06:20,720] Making new env: CartPole-v0
__________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to            
==========================================================================================
flatten_1 (Flatten)              (None, 4)             0           flatten_input_1[0][0]  
__________________________________________________________________________________________
dense_1 (Dense)                  (None, 16)            80          flatten_1[0][0]
__________________________________________________________________________________________
activation_1 (Activation)        (None, 16)            0           dense_1[0][0]          
__________________________________________________________________________________________
dense_2 (Dense)                  (None, 16)            272         activation_1[0][0]     
__________________________________________________________________________________________
activation_2 (Activation)        (None, 16)            0           dense_2[0][0]          
__________________________________________________________________________________________
dense_3 (Dense)                  (None, 16)            272         activation_2[0][0]     
__________________________________________________________________________________________
activation_3 (Activation)        (None, 16)            0           dense_3[0][0]          
__________________________________________________________________________________________
dense_4 (Dense)                  (None, 2)             34          activation_3[0][0]     
__________________________________________________________________________________________
activation_4 (Activation)        (None, 2)             0           dense_4[0][0]          
==========================================================================================
Total params: 658
Trainable params: 658
Non-trainable params: 0
__________________________________________________________________________________________

そして訓練の結果を5エピソード分テストすると、こんなに長い時間ステップの間ポールを倒さず、部屋の中央からも離れずにいることができました。最初がどうだったのか並べないとこれが長いのか短いのかよくわからないですが、トレーニング中はなんかもっとよく倒れていました。

Testing for 5 episodes ...
Episode 1: reward: 21734.000, steps: 21734
Episode 2: reward: 7434.000, steps: 7434
Episode 3: reward: 5168.000, steps: 5168
Episode 4: reward: 13027.000, steps: 13027
Episode 5: reward: 22779.000, steps: 22779
感想

gym のサイトにあるように、実際にポールの動画が出てきて楽しい。