さて、今回から世界の実装を始めていきます。今回のゴールは、最初のシンプルな世界のルールを決め、OpenAI Gym に登録できる形式で書き下し、Keras-rl2を使ってトレーニングができるところまでとします。細かいことは気にせず、とりあえず動くものを作ります。
コードは Github に置いておいたので、ここでは世界の仕様を書いていきます。
- 30 × 30 の二次元グリッドスペース空間
- エージェントは1つのみ
- エージェントは最初に100のエネルギーを持っている
- 空間には食べ物がランダムに配置されている(場所も量(0-100)もランダム)
- 時間は不連続(ステップ制)
- 各ステップに生き物は各方向に移動するか、待機するかを選択できる(選択肢は5つ)
- 生き物は移動するとエネルギーを1消費し、待機すると0.5消費する
- 生き物が食べ物に重なるとそのエネルギーを自分のものにでき、同時に食べ物の量/100分のスコアを獲得する
- 生き物が全てのエネルギーを使うと死亡し、スコアを3点失う
- 1ステップごとに2%の確率でランダムなマスにランダムな量(0-100)の食べ物が発生する
- 生き物が死ぬか2000ステップを過ぎたら終了する
OpenAI Gym に登録するときは、 pip -e gym-smallworld のように -e (editable) オプションをつけると、世界の仕様を変更するのが簡単になります。
一方、トレーニングの方法は、Keras-rl2の例にあるAtariゲーム用のトレーニングコードをほぼ流用しました。具体的には以下の仕様でトレーニングします。
- 3 × 3 × 16 の畳み込みレイヤー(ストライド数1)を2枚
- 32ニューロンの全結合層が1枚
- 活性化関数は scaled exponential linear unit (selu) (変更点)
- Deep Q Network (DQN) を epsilon greedy Q ポリシーと共に使い、線形焼き鈍し(linear annealing)をする
- 最適化には Adam をラーニングレート0.001で用いる
- ウォームアップステップは10000
- ターゲットモデルは10000ステップごとに更新される
ネットワークを小さくし、デフォルトの relu を selu に変更しました。それ以外はだいたい元のコードのままです。GPUで動かすようにはしていないので、適当なパッケージ(gym, keras-rl2あたり)を入れればどんなパソコンでも動くはずです。(Python 3 で動かしています。)動いた場合は下のようなアニメーションが表示され、トレーニングが始まります。
緑が生き物で、赤が食べ物です。明るいほど食べ物のエネルギーが大きいことを示しています。この画像ですが、バックグラウンドにもっていって描画されないようにすると、計算を省略するみたいで、効率が上がります。見たいときはフォアグラウンドにもってくると適切な速度で表示されます。どういう仕組みかはわかりませんが、便利です。
見える範囲にある食べ物を集めていればよく、邪魔するものも何もないので、非常に簡単なタスクです。Atariのゲームの方がよほど難しいと思われます。episode_rewardもとんとん拍子に上昇し、次の図のようにだいたい1.5-2Mステップで収束します(青がエピソードごとのスコア、オレンジがQ値です)。Core i7-3770kで半日程度かかりました。
現時点での問題として、テストの時にトレーニング時のパフォーマンスが発揮できないのですが、これについては後々原因を追求します。(トレーニング時とテスト時のポリシーの変更によるところかなと思っています)
とりあえず動く世界ができました。次は複数エージェントでのトレーニングの実装をしたいと思います。