Chainerで機械学習と戯れる: JumpゲームとTreasureゲームをChainerで強化学習した過程と結果のメモ


はじめに
=======

[Jumpで穴を飛び越えろゲーム（自作）を強化学習できるか？](http://qiita.com/mokemokechicken/items/23dd55aae4c22884277a) のような試みを２週間くらい続けた結果、なんとなく方向性が見えてきたので、次の試みを始める前にまとめておくためのメモです。

試行錯誤したこと
=============

画面の領域を狭くした
------------
以前は 40x24 だったのを 15x10 にしました。
これは単純に「画面が大きいと計算量が大きいので学習に時間がかかる」からです。
まあ、おそらく15x10でできることは、40x24でも時間をかければできると思われるので良いかなと思います。

入力の与え方を変えた
------------

以前は ASCIIコードの ch=32~127のCharCodeを `x=(ch-32)/95` として、入力していたのですが、前の[足し算ゲーム](http://qiita.com/mokemokechicken/items/aa1535696514bfe51329)での反省もあり、厳しい可能性が考えられます。そこで、 ch を ３つ程度の実数値にMappingして入力するようにしました。 `F.EmbedID`を使うのと同じイメージです。

１次元(数直線上)に均等に並んでいた点を、３次元(３次元空間上)の点にしてから与えるという感じになるのでしょうか。chの個数が少ない場合はそれほど意味はないかもですが、近い点が全く違う意味になる場合はおそらくシビアになるので、こうしたほうが無難かな、と。

この場合気をつけるのは、この １次元->３次元のMappingは固定しないといけないので、Chainer Modelを保存するときに一緒にこのパラメータも保存して、Loadするときに読み込んであげないといけない点です。`F.EmbedID`のときはそれはChainerが面倒みてくれますが、今回のは違うのでそこは注意です（最初はうっかりしていました）。

居残り学習（笑）
------------
そんなことしていいのか知りませんが、強化学習中に「予想が大きく外れた時」にはその予想が大きくハズレなくなるまで、同じデータを何度も学習させています。
今回のゲームが「一発（穴に落ちたら、敵に捕まったら）ゲームオーバー」的な性質がありすごくペナルティが厳しいのですが、それをなかなか学習してくれない感じがあってもどかしくてそうしてしまいました。

実装としては、強化学習においてLossを計算してそれを０にするように学習するわけですが、過去100回分のLossを記録していて、正規分布的に大きく外れていたら、何度も学習するようにしています。
ただ、あまりに小さい（この基準は適当ですが）場合は、居残りなしにしています。

この居残りの弊害もありそうですが、自分の子供の勉強方法をみていて、そんな感じだなぁと思ったのでやってみています。

High Score 記録モード
-------------

Viewerの方もパワーアップしました。

強化学習中は放置しているので、いつ頃どのような成長を遂げたかわかりません。
しかも1プレイの時間が長くなってきて「前回のプレイの様子」しか見れないのもツマラナイです（なかなか更新されないので）。

そこで、「HighScoreを出した時のゲームプレイ」を保存しておいて、それを後で順番に見られるようにしました。
これで1週間位放置しておいても、あとでゆっくり何が起こったのか調べることができるようになりました。

ただ、途中で実行をBreakするとHighScoreがクリアされてしまうので、そのときは微妙なんですが。。。

バグ修正・GPU実行・replay学習
--------------

* バグ修正： 過去の４フレームを状態として学習させていたところに酷いバグがあって、そもそも全体としてまともに学習できていなかったのを修正しています。
* GPU実行： 試してみたのですが、何も学習しなくなってしまったので、今は中断中。。 to_cpu, to_gpuを繰り返したりするのがいけないのかな、、今度原因を究明せねば。
* replay学習： これも上手く実装できてない予感があります。他にバグがないとわかれば、このreplay学習だけで学習させてみて、学習できれば実装OKと言えるので、そうなってから改めて着手しようと思っています。今はバグが多くて、何が悪いかもわからないことも多くて、、、(--;

Jumpゲームの現在の学習状況
==============
Jumpゲームも色々ルールを変更しながら学習させています。

* 得点の与え方で、「穴の上は加点」を廃止
	* 穴に落ちやすくなる気がする。上級者向けなルールなので、とりあえず廃止。
* 間違ったキーを押すことによるペナルティの廃止
	* しばらくすると、間違ったキーをおさなくなるものの、もうしばらくして煮詰まるとやはりペナルティのあるキーも押すようになります。「窮鼠猫を噛む？」みたいな感じでしょうか。まあそれなら制限せずに自由にやらせたほうがいいので、キーペナルティは廃止しました。ただ、「e-Greedyによるランダムな行動の選択」では有効なキーの組み合わせのみを発生させるようにしています。

で、現状のHighScoreのリプレイは以下のようになっています。

[Jumpゲームの学習経過の動画](https://youtu.be/F8p5KCeRGIo)

画面下の「LearnTimes」というのが、累計の学習回数(1Turnで約１回増える, 居残りした分も増える)です。
ここの回数がどの程度増えたら、どのくらい賢くなるのか、が一つの注目ポイントです。

Turnでいうと220, 300辺りで 連結した穴がやってくるのをなかなか超えられないのですが、何十万回かやっていると超えるようになっていました。しばらくすると、今まで越えられたものもまた超えられなくなるんですが、もうしばらくするとまた超えられるようになる、、という感じで進むみたいです。んー奥が深い。。


Treasureゲームの現在の学習状況
==============

Treasureゲームと名づけたゲームは、「敵から逃げつつお宝を集める」というものです。
800Turnの中でどれだけ集められるかという内容にしています。1 Turn生き延びると +0.01 です。

これも本当は 「敵Xは3ターンに1回8方向に動ける」「敵Yは2ターンに1回上下左右にだけ動ける」という、
敵X,Yの2種類あったのですが、全然進歩しないので、敵Yだけにしたら以下のようになっています。

[Treasureゲームの学習経過の動画](http://youtu.be/gwf6f3g5h7Y)

中盤から、単に敵を交わすだけになります。あーそうだよね。。ゲーム設定がマズかったか。。

ただ、諦めて放っておいたら 動画の7分20秒〜30秒頃に、Yの上に抜け出るような動きも見せ始めていて実はこの後見どころがあるのかもしれないので、もう少し放っておくことにしました。

見ていて思うこと
===========

現状実装されているのが「ある状況に対する最善の行動を学習する」というものだけです。

ただ、「何を教材に学習するのが最適か」とかいう視点が実装されていないから、すごく局所的な解に落ち着いたり、何十万回もやらないとある穴が超えられなかったりします。あの穴に落ちる前後を重点的に学習すればもっと短時間にクリアできるだろうに、と。スーパーマリオとかを人間がやるときはそうしますよね（まあゲームシステム的にもそういう工夫がされているわけですが）。

「同じ失敗を繰り返してる」「同じ行動をとり続けていてスコアが伸びない」というようなメタ的な「反省」する仕組みを作れると、より良い感じになるような気もします。e-greedyの発展とも言えるし、replay学習の工夫、とも言える部分なのでしょうが。

あと、ゲーム学習に限ったことで言えば、「魅せるプレイ」という基準も学習項目として表現できると見ていて楽しいだろうなぁ、と思います。

「反省」「魅せるプレイ」ってどうしたら実装できるのかなぁ、とつらつら考えたりしています。


さいごに
========

なんとなく学習ができていることは確認できました。
こういうミニチュアな環境でゲームの機械学習を練習するのも良いなと思います。
もう少しバグがとれたり整理ができたら、この環境を配布できる形式にしたいなぁ、と思っています。

続き：[さらに1ヶ月経過した時点の状況](http://qiita.com/mokemokechicken/items/7a398533042c2b40ddaa)