AlphaGo Zeroの手法でリバーシの強化学習をやってみる


はじめに
=======

[AlphaGo Zero](https://deepmind.com/blog/alphago-zero-learning-scratch/)は、囲碁AIをゼロから強化学習で鍛え上げたもので、それまで最強だったAlphaGo Masterを打ち破るという偉業を成し遂げました。そしてこのZeroの手法自体は結構シンプル、かつ、強くなれるという美味しいところ取りな手法なのです。また、そのゲームの知識を知らなくても実装できます。

今回は手法の理解と検証のため、リバーシのAIを育てているので共有します。


システム概要
========

* ソースコード: https://github.com/mokemokechicken/reversi-alpha-zero

動作環境
-----------

確認済みなのは以下の環境です。

* Python 3.6.3
* tensorflow-gpu: 1.3.0
  * tensorflow==1.3.0 でも動きますが、非常に遅いです。`play_gui`の時は tensorflow(cpu) でもまあ問題ない感じで動きます。
* Keras: 2.0.8

Modules
-------

### 強化学習用

今回の実装は`self`, `opt`, `eval` の３つのWorkerで構成されています。

* `self` は最善モデル同士の自己対戦で訓練データを生成します。
* `opt` はモデルの学習をさせ、次世代モデルを生成します。
* `eval` は次世代モデルが最善モデルより優れているかを評価します。もし優れていれば、最善モデルを置き換えます。



### 評価用

評価のために、最善モデルと対戦することができます。

* `play_gui` は 最善モデルと対戦します。

Data
-----

* `data/model/model_best_*`: 最善モデル
* `data/model/next_generation/*`: 次世代モデル
* `data/play_data/play_*.json`: 生成された訓練データ
* `logs/main.log`: ログファイル
  
もし初期状態からモデルの訓練を行いたい場合は、上記のディレクトリを削除してください。

使い方
==========

Setup
-------
### install libraries
```bash
pip install -r requirements.txt
```

GPUを使いたい場合は、更に以下のようにしてください。

```bash
pip install tensorflow-gpu
```


### 環境変数の設定
`.env` ファイルを作成し、以下のように記述してください。

```text:.env
KERAS_BACKEND=tensorflow
```

### 最善モデルのDownload (必要なら)

ちょっと対戦してみたい、という場合やランダムな初期モデルから始めるのが面倒だという場合にDownloadしてください。

```bash
sh ./download_best_model.sh
```


基本的な使い方
------------

モデルの訓練には、以下の `Self-Play`, `Trainer`, `Evaluator` を実行します。

Self-Play
--------

```bash
python src/reversi_zero/run.py self
```

実行すると、最善モデルによる自己対戦を開始します。
もし、最善モデルが存在しない場合は、ランダムな初期モデルを作成し、それを最善モデルとします。

### options
* `--new`: create new BestModel
* `--type mini`: use mini config for testing, (see `src/reversi_zero/configs/mini.py`)

Trainer
-------

```bash
python src/reversi_zero/run.py opt
```

実行すると、訓練を開始します。
基準となるモデルは最新の次世代モデルです。が、存在しない場合は最善モデルを使用します。
訓練されたモデルは 各Epoch終了後の2000step(ミニBatch)毎に次世代モデルとして保存されます。

### options
* `--type mini`: use mini config for testing, (see `src/reversi_zero/configs/mini.py`)
* `--total-step`: specify total step(mini-batch) numbers. The total step affects learning rate of training. 

Evaluator
---------

```bash
python src/reversi_zero/run.py eval
```

実行すると、評価が開始されます。
最善モデルと一番古い次世代モデルを約200回対戦させます。
もし、次世代モデルが勝った場合(勝率55%以上)、最善モデルとなります。

### options
* `--type mini`: use mini config for testing, (see `src/reversi_zero/configs/mini.py`)

Play Game
---------

```bash
python src/reversi_zero/run.py play_gui
```

![play_gui.png](https://qiita-image-store.s3.amazonaws.com/0/11124/3a9c1597-8e09-bdbb-665d-da01db1c8d55.png "play_gui.png")

実行すると、リバーシの盤面が表示され、最善モデルと対戦できます。
最善モデルが打った後、数字が盤面上に表示されます(動作検証用)。

* マス目の左上の数字(1) は最後に探索した **その状態と指し手の探索回数 N(s,a)** を表しています。
* マス目の左下の数字(2) は最後に探索した **その状態と指し手のQ値(AI側)** を表しています。Q値は100倍されています(この方が見やすいかな...と)。

### Note: Mac の pyenv 環境の場合

`play_gui` は `wxPython` を使っています。
PythonがFrameworkとビルドされていない場合は、実行できなくなります。
その場合、pyenv でのPython Installを以下のようなOption付きで実行すると良くなると思います。

```bash
env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.6.3
```

Tips and Memo
====

GPU Memory
----------

私の環境には GeForce GTX 1080 があり、メモリが約8GBなのですが、よくメモリ不足になります。
メモリ不足でも大抵Warning止まりで、エラーにはなりません。が、たまに謎のエラーが発生します（原因はメモリ不足）。
その場合は、環境にあわせて `src/worker/{evaluate.py,optimize.py,self_play.py}` の `per_process_gpu_memory_fraction` を調整してみてください。

```python
tf_util.set_session_config(per_process_gpu_memory_fraction=0.2)
```

また、各Workerの実行順序を変えるとメモリを確保する順序が変わるので、上手く行くことがあります。`opt`がメモリをかなり使うので、最後に起動するとWarningを吐きますが、なんとか動いてくれいます。

また バッチサイズを小さくするとメモリ消費を抑えることになります（挙動も何かしら変わるとは思いますが）。
 `NormalConfig` の `TrainerConfig#batch_size` をいじってみてください。

学習速度
------

* CPU: 8 core i7-7700K CPU @ 4.20GHz
* GPU: GeForce GTX 1080
* 自己対戦 1 game: 約47秒
* 評価 1 game: 約50秒
* 訓練 1 step(ミニバッチ, batch size=512) : 秒2.3秒


さいごに
========
AlphaGo Zero論文に比べて、評価時の対戦回数などなど、色々スケールが縮小していますが、リッチな計算機環境をお持ちの方は、もっと大規模に実行してもらうと楽しいかもしれません(正常に強くなっていくかはまだわかってないのですが...)。

実際に訓練で強くなっていくか（実装が正しいか、モデルのキャパシティが十分か、設定がおかしくないか）は、今後の経過を観察するしかない感じです。

既存のリバーシAIに勝てるといいなぁ。

学習進捗
------
次の表は、最善モデルの世代の記録です。
モデルの性能評価のために、 iOS アプリ(https://itunes.apple.com/ca/app/id574915961) と時々対戦させています。

### 2017/12/08 所感
このアプリは少なくともLV20程度ではわざと愚かな手を打つことがあり（たぶんハンデの実装としてそうなっているのだと思いますが）、それで単に勝ち進んでいる気もします。本当は１００回くらい対戦して勝敗を比較すべきなのですが、自動対戦できる良いベンチマークアプリがあれば良いんですが。
ただまあ眺めていると、なかなか面白い手を打つことがありますが、人間が少し意識して打つとたぶん簡単に勝てる程度のレベルです。
初期(４０世代頃)より弱くなっているような気もしないでもないです。MCTSのパラメータなどを結構いじっているのでその辺の影響もあるかもしれません。パラメータを変えた自己対戦で最適な値を先に求めないといけないのかもしれません。


|最善モデル世代|日付|最善モデルに勝った時の勝率|世代交代にかかった時間|備考|
|-----|-----|-----|-----|-----|
|1|-|-|-|　|
|2|2017/10/24|94.1%|-|　|
|3|2017/10/24|63.4%|13|　|
|4|2017/10/25|62.0%|3|　|
|5|2017/10/25|56.7%|8|　|
|6|2017/10/25|67.3%|7|　|
|7|2017/10/25|59.0%|3|　|
|8|2017/10/26|59.7%|6|　|
|9|2017/10/26|59.4%|3|　|
|10|2017/10/26|55.7%|5|　|
|11|2017/10/26|57.9%|9|　|
|12|2017/10/27|55.6%|5|　|
|13|2017/10/27|56.5%|7|　|
|14|2017/10/28|58.4%|20|　|
|15|2017/10/28|62.4%|3|　|
|16|2017/10/28|56.0%|11|　|
|17|2017/10/29|64.9%|17|　|
|18|2017/10/30|55.2%|19|　|
|19|2017/10/31|57.2%|33|　|
|20|2017/11/01|55.7%|12|　|
|21|2017/11/01|59.7%|20|　|
|22|2017/11/02|57.8%|19|　|
|23|2017/11/03|55.8%|15|　|
|24|2017/11/03|64.2%|12|　|
|25|2017/11/04|55.4%|21|　|
|26|2017/11/04|56.7%|6|　|
|27|2017/11/05|57.5%|11|　|
|28|2017/11/06|58.5%|15|　|
|29|2017/11/06|55.3%|5|　|
|30|2017/11/06|55.0%|8|　|
|31|2017/11/06|56.9%|5|　|
|32|2017/11/07|56.1%|9|　|
|33|2017/11/08|55.7%|22|　|
|34|2017/11/08|56.1%|3|　|
|35|2017/11/08|59.0%|3|　|
|36|2017/11/08|59.4%|3|　|
|37|2017/11/08|56.2%|9|　|
|38|2017/11/10|55.4%|52|App LV9,10 に勝った。|
|39|2017/11/12|57.2%|29|　|
|40|2017/11/12|55.1%|12|App LV11に勝った|
|41|2017/11/13|55.7%|14|App LV12, 13, 14に勝った。もう私では勝てなくなった。|
|42|2017/11/15|57.8%|18|App LV18, 19 に勝った|
|43|2017/11/15|55.8%|16|　|
|44|2017/11/16|57.5%|8|　|
|45|2017/11/16|56.2%|3|App LV20 に勝った|
|46|2017/11/18|55.6%|49|　|
|47|2017/11/19|55.9%|34|何故かまた私が勝てるようになった。弱くなっているのか、私が強くなったかはわからない。|
|48|2017/11/19|59.4%|9|　|
|49|2017/11/20|55.9%|6|　|
|50|2017/11/22|56.0%|44|　|
|51|2017/11/26|55.8%|112|11/25の朝に c_puct を 3 から 1.5 に変更|
|52|2017/11/26|59.7%|6|　|
|53|2017/11/28|56.2%|33|App LV21 に勝った|
|54|2017/11/29|59.0%|24|　|
|55|2017/12/01|56.6%|58|　|
|56|2017/12/03|58.1%|49|self-play: always save policy of tau=1|
|57|2017/12/04|55.1%|24|　|
|58|2017/12/05|55.9%|35|　|
|59|2017/12/06|55.4%|6|　|
|-|2017/12/06|-|-|implement https://github.com/mokemokechicken/reversi-alpha-zero/issues/13|
|60|2017/12/07|61.7%|25|　|
|61|2017/12/07|58.1%|3|　|
|62|2017/12/07|57.8%|11|　|
|-|2017/12/07|-|-|fix bug about virtual loss W|
|63|2017/12/08|57.5%|9|　|
|64|2017/12/08|56.0%|9|　|