好奇心駆動探索を利用してAtari2600ゲーム攻略に再挑戦

# はじめに

強化学習の研究でスタンダードになっているAtari2600のゲーム攻略において、ブレイクスルーとなった技術の一つが好奇心駆動探索(Curiosity-driven Exploration)である。今回はその代表的な手法であるRandom Network Distillation(RND)を自前で実装し、実際にAtari2600のゲームを数種類学習させてみた結果を記事にする。
本記事は筆者が以前に作成した下記記事の続編となっており、読者がその記事を把握済みという前提で書き進める。

https://qiita.com/T-STAR/items/37c1a09822bd88bdd026

# RNDとは何か

好奇心駆動探索では「見慣れない状況に遭遇したら報酬を与える」という仕組みを導入することになり、RNDはそれを比較的効率良くできる仕組み。
好奇心駆動探索やRNDについては、偶然にも本記事作成直前に以下の記事が公開されており、それを確認してもらうのが良いと思うので本記事では解説しない。

https://qiita.com/kazupc/items/9e7418e1024ed8cf6ab0

## 補足

上記の記事に記載されていないことについて、筆者の実装に関連させて数点補足しておく。

### Non-Episodic Return

強化学習では将来報酬の計算に関してはEpisodeが終わる(つまりゲームオーバー)時点で打ち切るのが普通である。しかし内部報酬に関しては、これを打ち切らないで継続させた方が結果が良いらしく、これをNon-Episodicと呼ぶ。ゲームオーバーでの打ち切りは失敗へのペナルティになるので好奇心探索を阻害する、という解釈のようだ。
ちなみに外部報酬でこれをやると「わざとゲームオーバーにして報酬を取りに行く」という戦略が発生する場合があるので、してはいけないとのこと。
今回の記事に載せた実験では内部報酬に関してはNon-Episodicにしている。

### Sticky Action

Atari2600のゲーム攻略においては、画面のピクセルデータを入力として、目新しい画面になったら高い報酬が入るようにしたいのだが、画面切り替え型のゲーム(Montezuma's RevengeやPrivateEye) などでは画面切り替えの境目を行ったり来たりできるので、そこで繰り返して無駄に報酬を得ようとしてしまうことがあるという。それを軽減するために「入力されたActionを一定の確率(25%)で無視して、直前のActionが続けて入力されたことにする」という処理を入れている。Stickyは「ベタベタする」というような意味で、「押したボタンがすぐに戻らずに複数回分入力された」ような状況になる。
筆者が試した限りでは明確な効果がわからなかったので、本記事の実験では基本的に適用しないが、実装自体はしてある。

### エントロピー項の係数

DQN系ではε-greedy方策でεの設定で探索度合いを制御するが、Actor-Critic系のPPOではロスの計算時にエントロピー項を導入して、選択されるActionの偏りを操作することで探索の度合いを制御する。この効き具合を設定するための係数がパラメータとして存在し、この数値はオリジナルのPPOでは0.01だが、RNDの論文では0.001になっていて、エントロピー項の効きが小さくなっている。理由を推測すると、探索は主に内部報酬によって行うのでエントロピー項の重要度が低いということと、Sticky Actionを導入しているのでランダム性が上がっているため、とも思われる。
本記事の実験ではSticky Actionを使っていないことも考慮して、0.0025にしてある。

### 内部報酬の正規化

最終的に内部報酬は外部報酬と足し合わされるが、その前に論文の実装では割引報酬和をとって標準偏差で割って正規化する。この辺は上述の紹介記事でも触れられている通り、どうしてこうなっているのかよくわからず、github等で公開されている独自にRNDを実装しているコードをいくつか見た限りでは、その通り実装しているものはほとんどないようだ。
おそらく以下のような条件が満たされればなんでも良いと思われる。
 * 正規化後の値と外部報酬の割合いに極端な違いが出ない
 * 学習が進むにつれて徐々に低減していく 

本記事での実装では割引報酬和を取らないで、学習開始から全ての値の標準偏差をとって正規化している。ここを直近のNステップ分などとしている実装もあるのだが、そうすると２番目の条件が怪しくなるように思う。

## MountainCarでの効果確認

MountainCarはOpenAI Gymで提供されているシンプルなゲームだが、ゴールに到達しないと報酬が得られないため、そこに至る手掛かりが無く、CartPoleに比べて学習が格段に難しい。こういう外部報酬が疎な問題に対しては内部報酬を導入すると有効な場合があって、実際に大きな効果があった。Google ColabのCPUのみ環境でも1分以内でゴールに到達できるようになる。MountainCarでは目新しい観測値が即ゴールに結びつくので相性がいいと思われる。

以下、学習させた結果の動画。64個並行で実行しているエージェントのうちの一つについて、学習過程を最初から再現させたもので、全体のエピソード数としては動画内表記の約64倍になる。

<iframe width="320" height="240" src="https://www.youtube.com/embed/ROO0356ZvIo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

以下、報酬のグラフ。報酬はゴールに辿り着いた時だけ1が入るように変更してある。

![MountainCar_replay_reward2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/824193/e90ee98f-4c02-22a4-5a89-19603101362f.png)

グラフ上で内部報酬がフラットに見えるところでも実際は細かい凹凸があるので学習の手掛かりになっているようだ。外部報酬だけの場合はここは全く平らになって、ランダム試行の運にたよることになる。
ゴールに入る直前あたりから大きな内部報酬が入り始めるが、ゴール到達以降のエピソードでは慣れてしまってあまり内部報酬が入らなくなる。しかしゴール到達による外部報酬が入ってくるので、それを頼りに学習が進むことになる。

上記のサイクルが継続的に起こればより複雑なゲームでも順次学習が進むことになるが、実際はそういうゲームだけでも無いので、どんな場合にでも有効なわけではない。実際、外部報酬が密なCartPoleに対して適用しても特に学習が早くなることはなかった。

# 実験

以下、Atariゲーム攻略に適用した実験結果を記す。
前回は点数を取るようにいくつか手を入れていたが、今回は「点数ではなく面数」ということを主眼にして調整した。
前回実験コードを改造したのだが、いくつか違いがあるので以下、主なものを上げておく。

* 実行エージェント数を8から32に増加
  * 論文では128にしているので32でも少ない
* 自機Lost時の負の報酬をやめる
  * 好奇心による探索を阻害しないように負の報酬になるものは除去
  * 前回実験では自機Lostで将来報酬計算を打ち切ったりしていたがそれもやめる
* 報酬のスケーリングをやめて、±1.0にクリッピング
  * 前回は高得点を狙うように報酬に差がつくようにしていたが、これは好奇心探索を阻害する可能性があるので止める
* 方策に使うモデルパラメータ数を1.6Mから1.1Mに削減
  * 層を増やした代わりにパラメータを減らした
  * 論文ではRNNタイプでの実験がメインだが、この実験ではCNNタイプで実施
  * RNDに使うモデルのパラメータ数は0.5M

学習全体のAtari環境で使用したフレーム数は前回と同じで40Mフレームになっている(ステップ数では10M)。

RND導入分の処理の増大に関しては、GoogleColabのT4環境で計測したところ、Breakoutでは20％程度処理時間が増えた。ただし、AtariのゲームはNNの学習よりもゲーム実行処理の方が時間が長くなるので、ゲームの種類やCPUの性能によってこの割合は変わってくる。
今回の実験程度のステップ数では、上記の環境で大体5〜6時間程度で完了する。

本記事の実装では、RNDの入力の正規化を-1〜+1に固定しているので、動的に更新する論文の実装よりも性能が落ちると思われる。その他、論文通りでないところも多々あるが、ある程度効果確認はできたので、概ね論文の趣旨に沿った実装になっているはず。

[Google Colab 実験用コード](https://gist.github.com/T-STAR-LTD/903cabc28c1028ad04cdc5389ba65a3a)

## Breakout

定番のブロック崩しだが、「多くのブロックを崩す🟰新しい状況」ということで外部報酬なしで学習できる。以下、内部報酬だけで学習させたプレイ。
<iframe width="320" height="240" src="https://www.youtube.com/embed/cYmhh8TiBN4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

Breakoutでは開始時後にFireを選択しないとボールが動かないので、開始直後に自動的にFireを選択したことにするという細工をすることが多いが、自機ロストのペナルティなしで内部報酬を導入すると「Fireを押した方が得である」という状況が自然に生まれるので、そのような細工をする必要がなくなるのも利点の一つ。

## Bowling

Bowlingも外部報酬なしで学習できる。これはスペアやストライクを取ると特殊エフェクトが出るので、そこで大きな内部報酬が出るため。以下動画は、内部報酬だけで学習させたプレイ。

<iframe width="320" height="240" src="https://www.youtube.com/embed/0vf7YLdIjv4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

前回よりも明らかに高得点が出るので外部報酬なしの方が良いことになるが、これは「スペア／ストライクの外部報酬は次回以降の投球が終わるまで得られない」のに対し、「内部報酬が出るのは事象の直後」ということにより学習しやすいことが理由と思われる。

## Montezuma's Revenge

RNDの元論文でメインの攻略対象となっているゲームで、外部報酬が疎な割に自機が死にやすく難易度が高い。論文ではベンチマークとして「点数」では無く「訪れた部屋数」で評価している。
前回の実験のような設定だと、２番目の部屋に行けるかどうかの程度なのだが、今回の設定では結構進めるようになった。

<iframe width="320" height="240" src="https://www.youtube.com/embed/4Rx-D3rC4Jw" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

このゲームでは死んでリスポーンした方が早く次に行ける場面が序盤にあるので、自機ロストのペナルティを無くした効果も大きいと思われる。

## Venture

最初に４つある部屋のどれかに移動させ、その後その部屋の中の宝を取っていき、全部屋分の宝を取るとステージクリアとなる。敵に触ると即死亡なので、好奇心駆動でないと最初の部屋に入ることすらしないで0点で終わる場合がある。
今回の実装では、部屋に入った時点で画面が切り替わることによる内部報酬があるので、安定して部屋に入ることができるようになる。

<iframe width="320" height="240" src="https://www.youtube.com/embed/xo9KWPw83_w" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

宝を取ってもその部屋を出ないと取得は確定しないので、「宝を取ってから死亡」を繰り返すようになる。仕様上はこの手法で最初の部屋だけで2000点いくので、他手法の論文でもスコアが2000点以下の場合は同様の点数稼ぎをしているだけの可能性が高い。Agent57でもスコアが2500点で頭打ちで、最初のステージの2部屋目をクリアできたかどうか、というレベルと推測され、人間にはそんなに難しいゲームではないと思われるがAIにとってはかなり難しいゲームのようだ。

## Seaquest

「潜水艦を操り、サメや敵潜水艦を撃退しつつ、ダイバーを救助する」というゲーム。酸素量という名目で時間制限があり、ダイバーを保護した状態で水上に浮上すれば酸素が供給され時間が延長される。ダイバーを同時に６人保護した状態で水上に上がるとボーナスがもらえ、以降の敵を倒した際の得点も上がる。

保護していない状態で浮上すると何故か爆発して自機を失うことと、ダイバーの出現は水深に応じて数が決まっていることがAIにとっては攻略を難しくしていると思われ、前回は浅い場所にとどまってしまいダイバーをほとんど救出せずに終わっている。

<iframe width="320" height="240" src="https://www.youtube.com/embed/GPamDTTn69g" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

これが意外と好奇心探索の導入が効くようで、今回の実験の設定では以下のように順調にボーナスが取れるようになる。

<iframe width="320" height="240" src="https://www.youtube.com/embed/7K0vf29X6aw" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

## Ms.PAC-MAN

前回の実験では１面もクリアできていなかった。この時には報酬をスケーリングして高得点を狙うようにしているので、パワーエサがある間はそれなりに効率よく動くが、パワーエサがなくなると非合理的な動きが目立つ。

<iframe width="320" height="240" src="https://www.youtube.com/embed/3S8KoquRT6s" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

今回の実験では、１面は高確率でクリアできるようになり、２面はもう少しでクリアできるところ。３面目からは迷路の配置や配色が変わるので、そこからはさらに学習時間が必要になるだろう。

<iframe width="320" height="240" src="https://www.youtube.com/embed/TNDvwR2Y5uQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

このゲームは、外部報酬だけにしても大体同じ学習速度で１面クリアできるようになった。前回からの違いとして、報酬を均一化して高得点を狙わなくなったことや、自機をロスしても明示的なペナルティを無くしたことなどが効いていると推測される。パラメータの調節不足なのかもしれないが、内部報酬を導入してもあまり効果がない例と言えそう。

## Kangaroo

初代ドンキーコング型のゲームで自機を操作して最上階まで行くとボーナスが貰えて次の面に行ける。序盤に得点稼ぎできる場所があるため、前回の実験では時間切れまでそこで粘るという学習をしてしまい、全く上の階に行かないでゲームが終わった。
<iframe width="320" height="240" src="https://www.youtube.com/embed/MgOD1u1n3gg" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

今回は、得点稼ぎを防ぐために、恣意的な介入ではあるがボーナス入手時にしか外部報酬が入らないように調整した。したがって内部報酬だけで最上階まで進むことになっている。
<iframe width="320" height="240" src="https://www.youtube.com/embed/eWhzukRdB8s" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>


自機Lost時にちょっとしたエフェクトが入るのだが、内部報酬はそこに強く反応する。「いろんな場所の死亡エフェクトを見る学習をしていたら、結局ステージクリアしていた」という学習をしているようにも見える。
点数稼ぎをしていないので、当然前回よりスコア的には劣るのだが、こちらの方が普通の人間のプレイに近いと思う。

## Up'n Down

スクロール型のゲームで旗を8つ集めると次のステージに行ける。ステージはループ構成になっているが、ステージクリアにボーナスが無く、条件となる旗取得の得点も低い。一方、車を進めているだけでも得点は入るので、ステージクリアしないでループさせて得点稼ぎするのが合理的な戦略となってしまう。
ちなみに、このゲームは自機に残りがない状態でステージクリアするとゲームオーバーになるという(おそらく実機にはない)バグがあるようで、ステージクリアするように学習することを阻害している要因の一つと思われる。

<iframe width="320" height="240" src="https://www.youtube.com/embed/fMGCEQAUDoA" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

元のままの報酬は密すぎると思われるので、今回は旗を取った時だけ外部報酬が入るように調整(介入)した。その結果、運が良ければ３ステージまで行けるようになった。

<iframe width="320" height="240" src="https://www.youtube.com/embed/uXn2_TjSP44" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

スクロール型のためアクションに対応した画面変化が大きく、内部報酬も大きくなりがちなので、正規化時に1/10にスケールダウンさせているのだが、ステージの変化で内部報酬が大きくなるのでステージクリアボーナスの役目も果たしているようにも思う。
Atariのゲームではスクロールするものは少ないのだが、より高性能なゲーム機では複雑な画面でスクロールするものが増えるので、そのような機種のゲームではAtariの単純なゲームほどにはRNDが内部報酬として機能しない場合が多くなりそう。

## その他

[Tutankham](https://youtu.be/fa8jkhx71po)と[Gravitar](https://youtu.be/4JJV3zfs-Hw)は比較的うまく機能するようなので動画として公開しておく。
前回保留したPitfallはやはりダメだったが、これは元論文でも失敗している(プラスにならない)ので予想通り。PrivateEyeは得点はできるようになったが学習が不安定だった。

前回最初の階のすらクリアできない残念な結果だった[Kung-Fu Master](https://youtu.be/m5p24Rh-rXI)も内部報酬ありにしてもうまくいかなかった。ここでは画面端に進んで欲しいのだが、前進しても大きな画面変化がなく今回実装した内部報酬では有効な報酬が得られないと思われ、前回同様に雑魚敵を倒している間にゲームオーバーになる。人間にとっては、どちらに進めば良いか説明なしでもわかるはずだが、こういう判断は流石に現状のAIにはできないだろう。

そのほかにもいくつかやってみたが、内部報酬にははっきりした効果が出なかった(成績が下がるものもある)。ただし、並列実行数の増加や、自機ロスト時ペナルティの削除などはそれなりに効果があるようで、内部報酬なしで実行すると前回よりも好成績になるものもあった。

# まとめ

好奇心駆動探索を実装し効果を確認した。成績が大幅に上がるものがある一方、特に効果が見れらないものもある。
同様にRNDを使うNGUやAgent57では、擬似訪問カウンタも組み合わせて使って改良しているようだ。以下の記事にこの辺の詳しい解説がある。

https://qiita.com/pocokhc/items/04f6770ab8b6d066aee6

Atari2600より新しいハードのゲームでは画面効果が派手で複雑になるので、RNDは有効性が下がる気がするのだが、この辺は[Procgen Benchmark](https://openai.com/blog/procgen-benchmark/)のゲーム等を対象にしてみると確認できるのかもしれない。