ゲームで強化学習した結果 〜私はいかにして使えないAIを創ったか〜

# ことの顛末

強化学習の技術書を買って喜び勇んでゲームを学習してみたが、全然賢くならなかった。せっかくやったので、ここに手法とコードと改善方法を公開して供養とする。

# 強化学習とは

AIに「まずは行動させてみせ、うまくいけば褒め、失敗したらやり直す」を繰り返して最適な解を学習させる方法。人間が試行錯誤して（例えば、子供が自転車に乗れるようになる）コツを習得していく過程と似ている、とされる。

# 学習させたゲームの紹介

今回、Gymnasium（ジムナジウム）という、ChatGPTでご存じOpenAIが開発公開している強化学習用のゲームフレームワークを利用。1970年代のいわゆるアーケードゲームが数十種類提供されている。その中から、物理演算エンジンBox2Dを利用した Car Racing を選択した。

https://gymnasium.farama.org/environments/box2d/car_racing/

強化学習を実装する場合、ゲーム画面を提供したり、進行を把握して報酬を与えたりといった操作が必要である。そのためにはゲーム側に何らかの修正を行う必要があるが、ゲームは著作権法で保護されているため、変更を加えることができない。かくなる上は自前でゲームを実装して...ということになりかねないが、強化学習それ自体より大変な作業である。
Gymnasiumは強化学習用にゲームを提供してくれているので、大変ありがたいのである。

# Car Racingのルール

アクセル・ブレーキ・ステアリングを操作して、決められたコースを走行する。

# CarRacingのコース

他にもいくつかバリエーションがあるようなのだが、キャプチャを取得できていない。

<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/85f5bdb7-7def-7828-1fef-f137a8c58e83.png">

<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/9e9ef31b-b688-bec9-4747-9b2432c782e4.png">


# 強化学習の仕組み

### 環境とエージェント

「環境」と「エージェント」と呼ばれるモジュールを用意する。環境はゲームの画面・操作・進行など、ゲーム環境をとりまとめている。エージェントは人間のプレーヤーに相当し、ここに強化学習のAIアルゴリズムが実装されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/218c7914-4b0c-577f-218b-ac87af908438.png)

### 環境から出力される情報

環境からは、

- **ゲーム画面**
96x96で3チャネル(カラー)を持つndarrayが得られる。
- **報酬**
コース上にいる・加速するなどポジティブな操作に対しては正の報酬(6〜8)、それ以外では1フレームごとのゲーム進行のたびに負の報酬(-0.1)が付与される。ゲームの進行だけで負の報酬が付与されるのは、早くゴールさせる意図があると思われる。なお、ゲーム領域から出た場合は-100の報酬で即終了する。

が得られる。

### エージェントで扱う情報

エージェントでは、

- **アクセル**
[0, 1]の実数。1に近いほど加速する。
- **ブレーキ**
[0, 1]の実数。1に近いほど減速する。
- **ステアリング**
[-1, 1]の実数。-1が左へめいっぱい、1が右へめいっぱい。

の組合せを選択する。

### 学習時のやり取り

学習の環境とエージェントのやり取りの流れはおおむね以下である。

1. 環境はゲームの画面を出力し、その情報をエージェントに渡す
1. エージェントは受け取った情報をもとに、次の操作を決定し、それを環境に渡す
1. 環境は受け取った操作でゲームを進行させる。その際、ゲームからその操作に対する報酬を受け取る
1. エージェントはゲーム画面・選択した操作・得られ報酬で学習する
1. 環境がゲーム終了ならば次のゲームを開始し、そうでなければ元に戻って次のゲーム画面を出力する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/84bcc00e-0a2e-844a-3538-b83ded36729b.png)

### 学習モデル

学習のモデルはCNNを実装していて、学習データはゲーム画面。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/186a7902-d155-de2d-0f2f-36c77fc9064e.png)

# ソースコード

恥ずかしながら公開。

https://github.com/qOpenDev/car_racing

# 学習の結果

トータルで24時間以上学習させたのだが、まったく賢くならなかった。

### 実際にプレイさせてみると

<iframe width="560" height="315" src="https://www.youtube.com/embed/ORftQXD3Fb8?si=iGVJXvGY7NaDQnAk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

### プレイ動画の考察

- **定期的にコースを横断している**
コースに復帰すると、正の報酬が与えられる。そのため、定期的にコースへ戻って報酬を確保しているようにも見えなくもない。

- **コース上は走らないが、コースの周囲を走行している**
時折コースに戻って報酬をもらえれば、あとは近くをうろうろしていればいいと思っていないか...？

# なぜうまくいかない

学習に失敗した原因を考えてみた。ちなみに、ゲーム開始直後にコースをズームインする部分は、学習時にはカットしている。

### なめてた

2D上の一本道のコースを走行させるなど簡単にできると思っていた。しかしよく考えてみると、ゲームの各画面における状態が無数にあり、それに対する行動のQ値を学習させるには途方もない時間がかかるのではと思うようになった。
また、ゲーム画面がコース全体ではなく、コースの一部しか表示されていないのも、学習を難しくしていたのではと思う。

### 情報表示部分がいらない

アクセルやブレーキ、ステアリングの情報などがゲーム画面下の黒い部分（下図の赤破線で囲った部分）に表示されている。これは学習の邪魔になりそうだから、トリミングしてあげた方が良かったのでは。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853810/60a8b073-7c71-02d1-d8f8-605a3d942bf5.png)

### モデルの初期状態を考慮するべきだった

学習開始の段階で、CNNモデルの初期化の状態に引っ張られて、学習に変なクセが付いてしまったように思えることがあった。学習初期はイプシロン・グリーディー法の乱雑度を最大にして、完全にランダムな行動選択で学習させるのがいいのでは。

### 学習制度の評価方法を考えていなかった

なめてた手前、学習が進行すればそれなりに成果が出る（ゲームがプレイできるようなる）と思っていたので、評価方法を考慮してなかった。学習に数十時間かかるので、学習評価の如何では早めのやり直しも可能。

### 報酬の与え方が適切でない

今回の失敗結果のように、コース上の同じ場所をぐるぐるしていてもゲームが続行できてしまう。コース上を走行させてゴールさせるよう、報酬の与え方に工夫する必要がある。

# 参考文献

https://amzn.asia/d/afcQW0m