ゲームの強化学習ハンズオン -- OpenAI GymOpenAI gymで強化学習入門（機械学習名古屋第１5回勉強会）

# 目次

* 強化学習とは
* 環境
* 解決方法
* ハンズオン

---

# 強化学習とは
---
# 強化学習が扱う問題
変化する環境下で判断・行動し課題を解決する問題

![reinforcement_2.png](https://qiita-image-store.s3.amazonaws.com/0/51820/c675f3fe-d58f-8b5a-f0e3-6a722537ee54.png)

エージェントが環境の状態を見て行動をし、その行動により環境の状態が更新される。望む状態を目指す。

---
# 問題の例
* ゲーム
* 制御
* 広告

大抵の場合、リアルな環境かシミュレータで訓練をする。

---
# 強化学習の訓練方法
状態がどれくらい良いかを「報酬」として数値化して、より多くの報酬が得られるように訓練する。

例えば、
* 勝った → +1
* 負けた → -1
のように自分で決める。

---
# 環境

---
# OpenAI gym
OpenAIのgymというライブラリが有名。

* いくつかの問題・ゲームのシミュレーション環境
* 統一されてたインターフェースを持つ

インターフェースが統一されていると、

* 自作の環境をgymの環境と同じように扱う拡張が可能
* 作ったモデルのコードが再利用できる（かも）

---
# gymの環境の例
* 倒立振子

下の台を左右に動かして棒を倒れないようにする

![vlcsnap-2018-04-16-22h38m05s417.png](https://qiita-image-store.s3.amazonaws.com/0/51820/96ba2b5b-6eb7-26e1-35d4-3c96a118d2f6.png)

---
# gymの環境の例
* atari1(Space Invaders)

レトロなゲームもある。third party製のゲームもある。

![out.png](https://qiita-image-store.s3.amazonaws.com/0/51820/4d6707a8-66c1-a654-6c8b-1ecd4b455907.png)

---
# gymでできること
* 環境のロード
* 環境から状態・報酬を取り出す
* 環境に行動を反映させる

---
# 解決方法

---
# 価値関数を用いて
強化学習のタスクでは、それぞれの状態に対して良い行動が導ければよい。
例えば、

状態$s$に対して行動$a$がどれだけ良いかを意味する**価値関数**$F(s, a)$を作る。それぞれの状態で$F$を最大にする行動を取る。

という方法が考えられる。

価値関数が上手く作れれば勝ち

---
# Q学習
報酬がどれだけ貰えそうかを表す関数を使うと良さそう。
しかし、次もらえる報酬を予測するだけだと、次の次で失敗する可能性がある。

ある行動を取ったときに、その後最善を尽くしたとして、将来に渡って得られそうな最大の報酬を予想する。→ **Q学習**

---
# 累積報酬
将来得られそうなトータルの報酬を**割引累積報酬**で表現する。
なんらかの行動を行い続けたときに、順に$r_0,r_1,\dots$ という報酬が得られたとする。割引率$\gamma \in [0, 1)$に対して、

```math
\begin{align}
R &= \sum_{t} \gamma^{t} r_{t} \\
  &= r_0 + \gamma r_1 + \gamma^2 r_2 + \dots
\end{align}
```
を割引累積報酬という。

---
# Q学習
状態$s$と行動$a$に対して、

```math
Q^*(s, a) = \text{状態$s$で行動$a$をとったときの割引累積報酬の最大値}
```

を考え、それぞれの状態でこれが最大となる行動を選ぶ。

$Q$学習では、この関数$Q^*$を機械学習で作る。

＊実際は確率が絡むから割引累積報酬の期待値の最大値とかに

---
# Bellman equation
$Q^*$を前の式で計算するのは現実的ではない。$T$を大きくすると組合せが急激に増える。上手く言い換える必要がある。

Bellman equation

```math
Q^*(s,a) = \mathbb{E}_{s'}\left[r+\gamma \max_{a'}Q^*(s', a') \right]
```
ただし、$s'$は状態$s$から行動$a$をとった次の状態で、確率が絡む場合を含めて考えている。

この等式を使って、右辺を予想するような訓練をする。これで訓練した$Q^*$を価値関数として使う。

---
# ハンズオン
---
# 準備
ソースコード: [https://github.com/n-kats/MLN_201804](https://github.com/n-kats/MLN_201804)

今回のハンズオンは、gymのGUIを含むためdockerよりホストか仮想環境ですることをおすすめします。
依存ライブラリの詳細は[connpass](https://machine-learning.connpass.com/event/79105/)を参照してください。

dockerを使う人は、`sh launch.sh`のように起動スクリプトを実行して、dockerコンテナ内でpythonコマンドなど実行してください。

以下では、pythonコマンドがpython3.5以上を指すとします。

---
# gymを動かす
とりあえず、gymを動かしてみましょう。

```sh
$ python sample_01_run_gym.py
```
をします。ちゃんとセットアップできていてば、ウィンドウが現れてなにか動くと思います。
dockerの場合は反応が無くて、しばらくして終了するかと思います。

---
# gymの使い方（環境との通信）
``` python
env = gym.make(env_name)  # 環境呼び出し
env.reset()               # 初期化 状態を取得
env.render()              # 可視化
env.step(action)          # 行動を行う 状態・報酬などを取得
env.close()               # 終了
```

---
# 動画として保存
そのままでは、dockerの場合が何も表示されない・・・
gymには最新のものを動画に残す機能がある。

```sh
$ python sample_02_write_movie.py
```

今度は`_output/`に動画が出力されます。dockerの外に出力されるので、ホスト側で動画を再生することができます。
（＊すぐ倒れて動画が終わっちゃう）

---
# gym.Monitor
gymの環境はgym.Envで定義されたインターフェースに従っている。
つまり、stepとかrender等を実装したもの。

逆に、こうれらがあればgymの環境のように使える。

その一例がgym.Monitorで、環境をラップした環境が作る。これを使うと動画として書き出せる。

--- 
# gymのインターフェース（行動）
`env.action_space`にその環境で取れる行動の型が定義されている。
倒立振子（CartPole-V0）の場合`Discrete(2)`で、0か1の離散値で行動を表現するという意味になる。

* 0のとき、台を左へ移動
* 1のとき、台を右へ移動

という意味になる。

[gymのwiki](https://github.com/openai/gym/wiki/Table-of-environments)に他の環境についてもまとめてある。

action_spaceを見る用に`sample_03_print_values.py`
を用意したので、適当にいじってみてください。

---
# gymのインターフェース（状態）
env.resetやenv.stepの返り値として状態が返ってくる。
gymではこれを**observation**と呼んでいる。

倒立振子の場合、長さ4の配列で、

1. 台の位置
2. 台の速度
3. ポールの角度
4. ポールの速度

のような内容になっている。
env.resetではその返り値として、env.stepでは返り値がtupleでその一つとして返ってくる。

---
# env.stepの返り値
env.stepは4つの要素からなるtupleを返す。
順に、

1. 状態（observation）
2. 報酬
3. 終了したか(bool)
4. メタ情報

を意味する。

---
# ランダムに動かす
インターフェースが分かったので、とりあえず、ランダムに動かして見ましょう。

```sh
$ python sample_04_random_agent.py
```

をするとランダムに動きます。`action_space.sample()`でランダムな行動を取得することができます。
このスクリプトでは、Agent.actで状態に対して行動を返すということをしています。

---
# エージェントを作る
ランダムだとつまらないので、適当に価値関数を作ってそれに従って行動するエージェントを作ってみましょう。

サンプルとして、台やポールの位置・速度に重さをつけて足しただけの価値関数を作ってみました。

``` sh
$ python sample_05_handmade_agent.py
```
これを実行するとその価値関数で動作し、何ステップ続いたかが表示されます。

実は、思いの外上手く行って、強化学習の方が良くならなくて苦労した・・・

---
# ChainerRLを使って強化学習
ChainerRLは、Chainer製の強化学習用のライブラリ

* gymのインターフェースに特化
* 最新のDQN系手法が実装されている

今回は、ChainerRLのサンプルをちょっといじったものを用意しました。

``` sh
$ python sample_06_chainer_rl_agent.py
```

---
# モデルの内容
* DQN  
  deep learningで$Q^*$を近似する
* $\varepsilon$-greedy法  
  訓練用の事例をたまにランラム行動させて作る
* experience replay  
  訓練用の事例を貯めて偏るのを避ける

といった手法を使っている。

---
# ハンズオン
* gymがどういうインターフェースかを確認する
* 価値関数を自作する
* ChainerRLを使った訓練をしてみる

のようなことに取り組んでみてください