【dqn】自由な運動を深層強化学習する♬

カオスな振舞をVR出来たので、今度は強化学習でフィッティングできるか試してみたくなった。

今回は、その準備として通常の関数を強化学習する例を学習した。
もはやかなり語りつくされた感があるので、原理的な説明は無しで行こうと思う。解説は参考を見ていただきたい。

###やったこと
（１）環境構築
（２）PointOnLineの強化学習
（３）FXTradeという周期運動の強化学習
###（１）環境構築
【参考】
・[Kerasで最短で強化学習（reinforcement learning）する with OpenAI Gym](https://qiita.com/kazetof/items/3c33f823be178d97f904)

```
\dqn>pip install gym
Successfully installed future-0.16.0 gym-0.10.5 pyglet-1.3.2

\dqn>pip install keras-rl
Successfully installed keras-rl-0.4.2
```
この環境で以下が通りました。
ちなみに、BASH環境ではインストール失敗しました。
※追求していないのでひと工夫すれば動くと思う
\dqn>python [dqn_cartpole_new.py](https://github.com/MuAuan/dqn/blob/master/dqn_cartpole_new.py)
これは特に変更せず、ほぼもともとの[OpenGymのExample](https://github.com/keras-rl/keras-rl/blob/master/examples/dqn_cartpole.py)の通り。

上記環境でこれが動きました。ということで一応環境OKです。
※この解説も上記参考見てください
###（２）PointOnLineの強化学習
次のステージが難しいところです。つまり、一般的な運動をシミュレートというか強化学習できるのかというところ。。。
ググっていると以下がありました。
【参考】
①[[Python] Keras-RLで簡単に強化学習(DQN)を試す](https://qiita.com/inoory/items/e63ade6f21766c7c2393#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)
②[Keras-rlでDQNを実装したいのですが・・・](https://teratail.com/questions/116306)

①がドンピシャですが、実際エラーが消えません。しかし、②のQAというか丁寧な解説で理解できました。
ということで、以下の[PointOnLine](https://github.com/MuAuan/dqn/blob/master/PointOnLine.py)が動きました。

```
done, took 732.881 seconds
Testing for 10 episodes ...
Episode 1: reward: 0.998, steps: 3
Episode 2: reward: 0.553, steps: 114
Episode 3: reward: 0.907, steps: 33
Episode 4: reward: 0.899, steps: 34
Episode 5: reward: 0.190, steps: 140
Episode 6: reward: 0.242, steps: 161
Episode 7: reward: 0.293, steps: 153
Episode 8: reward: 0.925, steps: 31
Episode 9: reward: 0.294, steps: 143
Episode 10: reward: 0.944, steps: 41
```
![plot_epoch_50000_dqn.png](https://qiita-image-store.s3.amazonaws.com/0/233744/b180237c-1545-905a-722c-d5f778603e48.png)
###（３）FXTradeという周期運動の強化学習
上記の例を拡張すればそれでも任意関数に基づく運動を記載できそうですが、さらにより近いものを探すと以下がありました。
【参考】
・[正弦曲線にしたがう為替の値動きについてDQNでバックテストをしてみた ](https://futurismo.biz/archives/6615/)
実はこの実装もこのままだとエラーをはいて止まってしまいます。
そして、構造はなんと上記のPointOnLineと同じ。
やはり、同じ部分がダメそうです。
ということで、_stepと_resetを書き換えました。
さらに、PointOnLineと同じようにグラフを書くように変更して以下が得れました。

```
Training for 9600 steps ...
Interval 1 (0 steps performed)
 9600/10000 [===========================>..] - ETA: 5s - reward: 9.6403done, took 126.583 seconds
Testing for 5 episodes ...
Episode 1: reward: 2191.486, steps: 240
Episode 2: reward: 2191.486, steps: 240
Episode 3: reward: 2191.486, steps: 240
Episode 4: reward: 2191.486, steps: 240
Episode 5: reward: 2191.486, steps: 240
Testing for 10 episodes ...
Episode 1: reward: 2191.486, steps: 240
Episode 2: reward: 2191.486, steps: 240
Episode 3: reward: 2191.486, steps: 240
Episode 4: reward: 2191.486, steps: 240
Episode 5: reward: 2191.486, steps: 240
Episode 6: reward: 2191.486, steps: 240
Episode 7: reward: 2191.486, steps: 240
Episode 8: reward: 2191.486, steps: 240
Episode 9: reward: 2191.486, steps: 240
Episode 10: reward: 2191.486, steps: 240```
```
![plot_epoch_50000_FXTrade_dqn.png](https://qiita-image-store.s3.amazonaws.com/0/233744/20fbeff0-4972-e636-b1e7-b97180d9f61c.png)

なんかこれだとあまり面白くないので、関数を少し変更してみました。

```py
self._sin_list = []
k=0.05
for t in np.linspace(0, 48, 240):
     self._sin_list.append(np.sin(t)*np.exp(-k*t)+0.1*randn())
     #self._sin_list.append(np.sin(t))
```
コメントで消しているのを上記のように減衰＋乱数に変更してみます。
ということで、以下の結果が得られました。

```
Training for 9600 steps ...
Interval 1 (0 steps performed)
 9600/10000 [===========================>..] - ETA: 5s - reward: 4.8689done, took 126.982 seconds
Testing for 5 episodes ...
Episode 1: reward: 1411.216, steps: 240
Episode 2: reward: 1411.216, steps: 240
Episode 3: reward: 1411.216, steps: 240
Episode 4: reward: 1411.216, steps: 240
Episode 5: reward: 1411.216, steps: 240
Testing for 10 episodes ...
Episode 1: reward: 1411.216, steps: 240
Episode 2: reward: 1411.216, steps: 240
Episode 3: reward: 1411.216, steps: 240
Episode 4: reward: 1411.216, steps: 240
Episode 5: reward: 1411.216, steps: 240
Episode 6: reward: 1411.216, steps: 240
Episode 7: reward: 1411.216, steps: 240
Episode 8: reward: 1411.216, steps: 240
Episode 9: reward: 1411.216, steps: 240
Episode 10: reward: 1411.216, steps: 240
```
![plot_epoch_50000_FXTrade_dqn.png](https://qiita-image-store.s3.amazonaws.com/0/233744/72d52ce7-cde6-a954-d547-2ac9db6e956f.png)

ということでノイジーな減衰振動が得られました。
ここまで、すべてモデルは以下のとおりです。

```
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
flatten_1 (Flatten)          (None, 2)                 0
_________________________________________________________________
dense_1 (Dense)              (None, 16)                48
_________________________________________________________________
activation_1 (Activation)    (None, 16)                0
_________________________________________________________________
dense_2 (Dense)              (None, 16)                272
_________________________________________________________________
activation_2 (Activation)    (None, 16)                0
_________________________________________________________________
dense_3 (Dense)              (None, 16)                272
_________________________________________________________________
activation_3 (Activation)    (None, 16)                0
_________________________________________________________________
dense_4 (Dense)              (None, 3)                 51
_________________________________________________________________
activation_4 (Activation)    (None, 3)                 0
=================================================================
Total params: 643
Trainable params: 643
Non-trainable params: 0
_________________________________________________________________
```
ちょっと、これだとオリジナルとObservationsがどうなっているかわからないのでそれが見えるようにsin_original = []をグローバル変数に定義して、_sin_listを計算した後に以下を追加しました。

```py
for i in range(len(self._sin_list)):
            sin_original.append(self._sin_list[i])
```
そして、グラフ表示を充実して以下のように追加しました。

```py
import matplotlib.pyplot as plt

for obs in cb_ep.observations.values():
    plt.plot([o[0] for o in obs],"r")
t = np.linspace(0,240,240)
plt.plot(t,sin_original,"b")    
plt.xlabel("step")
plt.ylabel("pos")
plt.pause(3)
plt.savefig('plot_epoch_{0:03d}_FXTrade_dqn.png'.format(9600), dpi=60)
plt.close()

print('Plotting Results')
plt.subplot(2, 1, 1)
plt.title('Original')
plt.plot(t,sin_original,"b")    
plt.xlabel("step")
plt.ylabel("pos")
plt.pause(3)
plt.subplot(2, 1, 2)
plt.title('observations&original')
for obs in cb_ep.observations.values():
    plt.plot([o[0] for o in obs],"r")
plt.plot(t,sin_original,"b")    
plt.xlabel("step")
plt.ylabel("pos")
plt.pause(3)
plt.savefig('plot_epoch_{0:03d}_FXTrade_obvs&original.png'.format(9600), dpi=60)
plt.close()
```
![plot_epoch_9600_FXTrade_dqn.png](https://qiita-image-store.s3.amazonaws.com/0/233744/5d0d816a-2b68-8fa6-5871-0aa3ff7b22ce.png)
![plot_epoch_9600_FXTrade_obvs&original.png](https://qiita-image-store.s3.amazonaws.com/0/233744/2d60f1a9-29ea-d77e-e2b6-7e0a41436878.png)

###コードは以下に置きました
[MuAuan/dqn](https://github.com/MuAuan/dqn)

###まとめ
・自由な運動を深層強化学習した
・すごいフィッティング精度が得られた

・ロジックを完全に読み切っていない
・次回はカオスな運動をdqnしようと思う