ChainerRLでライントレーサーする

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">書きました <a href="https://t.co/m4YhZolDjD">https://t.co/m4YhZolDjD</a></p>&mdash; mooopan (@mooopan) <a href="https://twitter.com/mooopan/status/833624161083760640">February 20, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

ということでChainerの強化学習版ChainerRLが公開されていました。

このところ手を広げすぎていて中々丁寧な仕事ができない中、
30分 x 2日でDouble DQNでライントレーサーできました。ライブラリ便利で助かる！

# 使用環境

* Windows 10 - 64bit　　　　　　　　　　　　<諦めが悪い心の強さ…！>
* Python 3.6.0 |Anaconda 4.3.0 (64-bit) 　　　　<プライドより実利！>
    - ChainerRL 0.2 　　　　　　　　　　　　　　< Dependency緩くしてほしい>
    - Chainer 1.19 　　        　　　　　　　<1.20でTheanoのお世話できてない!会社ではTheano動いたのに。>
    - cached_property
    - gym (0.7.3)
    - gym environment [ライントレーサーの環境をOpenAI I/F的にした \- Qiita](http://qiita.com/chachay/items/aa7b94f22163905b4c2b)

# 環境&サンプル リポジトリ
[Chachay/Gym\_LineFollower: Simple Open AI gym like Environment](https://github.com/Chachay/Gym_LineFollower)

GUI tool kitをQt5化しました

# ChainerRL

pipで入れようとすると Arcade Learning Environment入れようとしてコケる。
ALEをWindowsに突っ込むところに時間をかけてもいいけど、
Windowsでpongしたいとも思わないので、ショートカット！！

## 1. GitHubからリポジトリを落とす

[GitHub \- pfnet/chainerrl: ChainerRL is a deep reinforcement learning library built on top of Chainer\.](https://github.com/pfnet/chainerrl)

## 2. 同梱のChainerRLフォルダを作業フォルダに移す

以降のexampl2.pyと同じフォルダね

# ライントレーサー環境定義
[ライントレーサーの環境をOpenAI I/F的にした \- Qiita](http://qiita.com/chachay/items/aa7b94f22163905b4c2b)の環境は、ライントレーサーが受け取る車輪速度を連続値で環境を定義しています。

これを離散値にするため、継承で離散値環境にしなおします。
同時に一つ目センシングを4ステップ分の履歴観測にします。

```python
class SState(object):
    def __init__(self, STATE_NUM, DIM):
        self.STATE_NUM = STATE_NUM
        self.DIM = DIM
        self.seq = np.zeros((STATE_NUM, DIM), dtype=np.float32)
        
    def push_s(self, state):
        self.seq[1:self.STATE_NUM ] = self.seq[0:self.STATE_NUM -1]
        self.seq[0] = state
        
    def reset(self):
        self.seq = np.zeros_like(self.seq)
        
    def fill_s(self, state):
        for i in range(0, self.STATE_NUM):
            self.seq[i] = state

# Define Agent And Environment
class LineTracerEnvDiscrete(LineTracerEnv):
    actions = np.array([[0.5, 0.1], [1.0, 1.0],  [0.1, 0.5]]) 
    OBSDIM = 4
    
    def __init__(self):
        self.action_space_d = spaces.Discrete(self.actions.shape[0])
        self.observation_space_d = spaces.Discrete(self.OBSDIM)
        self.MyState = SState(self.OBSDIM, 1)
        
        super().__init__()

    def _step(self, action):
        tempState, tmpReward, tmpDone, tmpInfo = super()._step(self.actions[action])
        self.MyState.push_s(tempState)
        return self.MyState.seq.flatten(), tmpReward, tmpDone, tmpInfo
        
    def _reset(self):
        s = super()._reset()
        self.MyState.reset()
        return self.MyState.seq.flatten()

env = LineTracerEnvDiscrete()
```

あとは藤田さん（@mooopann)の[クイックスタート](https://github.com/pfnet/chainerrl/blob/master/examples/quickstart/quickstart.ipynb)からコピペ・コピペ

env.OBSDIM, env.action_space_d.nとenv.action_space_d.sampleだけ若干注意。

```python
q_func = chainerrl.q_functions.FCStateQFunctionWithDiscreteAction(
    env.OBSDIM, env.action_space_d.n,
    n_hidden_layers=2, n_hidden_channels=50)

optimizer = chainer.optimizers.Adam(eps=1e-2)
optimizer.setup(q_func)

gamma = 0.95

explorer = chainerrl.explorers.ConstantEpsilonGreedy(
    epsilon=0.3, random_action_func=env.action_space_d.sample)

replay_buffer = chainerrl.replay_buffer.ReplayBuffer(capacity=10 ** 6)

agent = chainerrl.agents.DoubleDQN(
    q_func, optimizer, replay_buffer, gamma, explorer,
    replay_start_size=500, update_frequency=1,
    target_update_frequency=100)
```

# 実行

動く…！ 

```python
python example2.py
```

![20170221.png](https://qiita-image-store.s3.amazonaws.com/0/117379/d7b3e6f7-3bab-fb22-491a-e9a7ebf1a1a0.png)

※ 周回路回れるような行動を学習できたかは…試してないっす
   そのあたりは過去記事を… ⇒ [ライントレーサーをDeep Q Learningで教育する \- Chainer \- Qiita](http://qiita.com/chachay/items/555638e3079fce9d59c9)

# 感想

## 初日:正味30分
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">ChainerRL使おうとしたけど、見本の書き方が<a href="https://t.co/cDBp9FgwA5">https://t.co/cDBp9FgwA5</a>ではないところまで理解して終わってしまった。ENV環境を連続値で作っといてなんだけど、離散値に定義しなおした環境への作り直しが必要&gt; <a href="https://t.co/x57EkRzQfj">https://t.co/x57EkRzQfj</a></p>&mdash; chachay (@chachay) <a href="https://twitter.com/chachay/status/833685506202284032">February 20, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## 2日目：正味30分
* Envの継承クラス作成
* 動いた

## まとめ

* エージェントづくりが捗りまくって素晴らしい
* pipにてWindowsで蹴られないような気づかいはいただきたい
* やっぱり↓はある

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">連続値を普段入力値として扱っているエンジニアとしては、連続値に対して行動候補(離散化)をエージェントで定義できるようにして、離散・連続値間の間を取り持ってくれると助かる。gymの設計コンセプトから外れるとしても。</p>&mdash; chachay (@chachay) <a href="https://twitter.com/chachay/status/833688196877660160">February 20, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# スクリプト
[Chachay/Gym\_LineFollower: Simple Open AI gym like Environment](https://github.com/Chachay/Gym_LineFollower)

example2.py参照のこと