12 Jul 2017, 13:57

USDJPYのヒストリカルデータをダウンロードしてDQN Agentを動かしてみた

前回の続き。

USDJPYでバックテスト

今回は、DukascopyからUSDJPYのヒストリカルデータをダウンロードして、DQN Agentを動かしてみました。

すごく苦労して、なんとかDQN Agentに学習させることに成功した。

ロジックは、前回とほぼ同じです。今回の Jupyter Notebookは以下。

苦労した点

  • はじめ、keras-rlで testメソッドを走らせたあとに、rewardが0になってしまった。いくら論理を見直しても、0になってしまう。
  • 波の形で学習できるときとできないときがあることが分かった。たとえば、サイン波は学習できるがコサイン波は学習できない。
  • 標準化を適用して、なるべくサイン波に近い形にヒストリカルデータを編集した。

11 Jul 2017, 14:02

正弦曲線にしたがう為替の値動きについてDQNでバックテストをしてみた

DQNを勉強しているので、以下の記事に触発されて自分もFXでDQNをしてみたくなった。

2年前、OANDAでのシステムトレードで5万円を損した屈辱を今こそ晴らすのだ!

まずは、GMOの記事でも実施しているように、正弦曲線に従う為替の値動きについて、

DQNを適用してバックテストを行ってみた。

今回のJupyter Notebookは Gistにあげました。

正弦曲線

以下のような正弦曲線について、DQNでバックテストを試みる。

MDP

  • 状態
    • (-1.0, 1.0) の範囲の値
    • 売買のステータス(SELL, NONE, BUY)
  • 行動
    • SELL
    • HOLD
    • BUY
  • 報酬
    • 売買の即時報酬

コード

`import enum
class Action(enum.Enum):
    SELL = 2
    HOLD = 0
    BUY  = 1

class Position():
    def __init__(self):
        self.NONE = 0
        self.BUY = 1
        self.SELL = 2

        self.bid = 0.0
        self.pos_state = self.NONE

    def state(self):
        return np.array([self.bid, self.pos_state])

    def change(self, action, bid):
        self.bid = bid
        self.pos_state = action

    def close(self, action, bid):
        if self.pos_state == action:
            return 0

        self.pos_state = self.NONE
        if action == Action.BUY.value:
            reward = (bid - self.bid) * 1000
        else:
            reward = (self.bid - bid) * 1000
        return reward`

keras-rl を利用するために、環境を OpenAI gym ライクで作成する。

`import gym
import gym.spaces

class FXTrade(gym.core.Env):
    def __init__(self):
        self.action_space = gym.spaces.Discrete(3)
            high = np.array([1.0, 1.0])
            self.observation_space = gym.spaces.Box(low=-high, high=high)

            self._position = Position()

            self._sin_list = []
            for t in np.linspace(0, 48, 240):
                self._sin_list.append(np.sin(t))
            self.cur_id = 0

    def _step(self, action):
        bid = self._sin_list[self.cur_id]
        self.cur_id +=1
        done = True if self.cur_id == 240 else False

        if action == Action.HOLD.value:
            reward = 0
        else:
            if self._position.pos_state == self._position.NONE:
                self._position.change(action, bid)
                reward = 0
            else:
                reward = self._position.close(action, bid)
        return np.array([bid, self._position.pos_state]), reward, done ,{}

    def _reset(self):
        self.cur_id = 0
        self._position = Position()
        return np.array([0.0, 0.0])`

keras-rlの登場

強化学習を笑っちゃうほど簡単に実施するために Keras-rlをつかう。

このライブラリは抽象度が高すぎてもはやなにをやっているのかわからない。

exampleを参考にして、それっぽいコードを書いてみる。

`from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam

from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory

nb_actions = env.action_space.n

model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
model.summary()

# DQNに必要な Experience Replayのためのメモリ
memory = SequentialMemory(limit=50000, window_length=1)
# ボルツマン分布に従うポリシー
policy = BoltzmannQPolicy()
# DQN Agentの生成
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory,
               target_model_update=1e-2, policy=policy)
# optimizersはAdam, 損失関数は 平均二乗誤差
dqn.compile(Adam(lr=1e-3), metrics=['mae'])

# トレーニングを開始。同じ正弦曲線を9600 = 240 x 400回 回す。
dqn.fit(env, nb_steps=9600, visualize=False, verbose=1)

# トレーニング結果を確認
dqn.test(env, nb_episodes=5, visualize=False)

`

おお、rewardがプラスだ。。

`Testing for 5 episodes ...
Episode 1: reward: 603.962, steps: 240
Episode 2: reward: 603.962, steps: 240
Episode 3: reward: 603.962, steps: 240
Episode 4: reward: 603.962, steps: 240
Episode 5: reward: 603.962, steps: 240`

おわりに

  • 理論を抑えていないので何をやっているのかがいまいちわからないのだけれども、おそらく上がり調子のときは報酬もたくさんもらえるので、買い注文を多くしているのだと思う。
  • keras-rlは非常に強力なライブラリだけれども、抽象度が高すぎてなにやってるのかよくわからない。理解を深めるために numpyで実装してみるのもありかもしれない。
  • 状態は、その時の値のみを扱ったが、過去5bin分の状態を考慮にいれたらどうなるだろうか?いわゆるwindowの概念を強化学習にいれてみると、結果がよくなるだろうか?

09 Jul 2017, 08:58

CartPole問題にDQN(numpy only)で挑戦したけど解けなかった

前回の続き。

前回は、Kerasを利用したのだが、今回は numpyだけで実装してみた。ゼロから作るDeepLearningを大いに参考にした。

  • Kerasと同じことを実装はずなのに、結果が同じにならない。
  • エピソードを重ねても生存率が頭打ちになって、伸びない。
  • 調子のいいときと調子の悪いときがある。エピソードの開始時に運良く生存すると、その後の生存率が上がる。

結果

解けなかった。

https://gym.openai.com/evaluations/eval_iNrsSMkNSxW1wGF0b1lspg

コード

08 Jul 2017, 08:16

OpenAI Gymの CartPole問題をDQNで解いた

また、CartPole問題なのだが、今回はDeep Q-Network(DQN)で解いた。

解いたといっても、自力で解いたわけではなくて、Udacity DLNDの Reinforcement Learningの回のJupyter Notebookを参考にした。

このNotebookは tensorflowを利用しているのだけれども、理解を深めるためにKerasに移植してみた。

DQNのポイント

Deep Q-Network(DQN)とは、ディープラーニングと強化学習(Q学習)を組み合わせたアルゴリズム。

これがなんなのか、いまいちよくわからなかったので、参考になりそうなリンクをかき集めた。

Q学習で利用するテーブルをニューラルネットワークで関数近似しただけだと、

DQNとは呼ばないらしい。David Silver さんのスライドによると、

以下の3つの学習を促進するための工夫をしたことによって、DQNは安定した学習を可能にしている。

  • Experience Replay
  • Fixed Target Q-Network
  • 報酬のclipping

Fixed Target Q-Networkをいまいち理解していないので、正式なDQNではないのかも。Experience Replayは実装した。

これは、状態の相関関係によって学習がうまくいかないことを防ぐための手法。 の組を保存しておき、学習時に保存したデータ組からランダムにサンプリングしてミニバッチをつくり、それをニューラルネットで学習するという手法。アルゴリズムを Udacity DLNDから引用する。

  • Initialize the memory
  • Initialize the action-value network with random weights
  • For episode = 1, do
    • For , do
      • With probability select a random action , otherwise select
      • Execute action in simulator and observe reward and new state
      • Store transition in memory
      • Sample random mini-batch from :
      • Set if the episode ends at , otherwise set
      • Make a gradient descent step with loss
    • endfor
  • endfor

コード

結果

前回のQ学習が3000エピソードくらいで解けたのに対して、今回のDQNは250エピソードで解けた。大幅な進歩だ。

https://gym.openai.com/evaluations/eval_DYySvAnSxuP3oHjZchxaQ

05 Jul 2017, 15:56

Deep Q-Network(DQN)リンク集

日本語

英語

course

03 Jul 2017, 08:42

TD法による tic-tac-toe

前回の続き。

以下の本のoctaveでかかれたコードをpythonで書き直した。

勝率がよくないので、自分の実装がバグっている可能性大。でも、本でもそれほど勝率は高くなかったので、何とも言えない。

間違ったコードを公開することは抵抗があるけれども、今後誰かが勉強するための足がかりになればと思い公開。

SARSA法

サンプルコードのQテーブルの更新式が怪しい。Q学習と同じになっている。s-a-r-s-aになっていないのだ。なので、書籍とは違う実装にした。

Q学習

30 Jun 2017, 20:15

LEGO Mindstormsの crawler を強化学習で前に進むことを学習させた

LEGO x 強化学習の初の成果が出た。

強化学習の古典的問題、crawler に Q-Learningを適用して前に進むことを学習させたのだ

まずは見るのが早い。一つ目の動画は学習を開始して間もない動画。ランダムに尾をうごかして、運良く前にすすんでいることがわかる。

[https://www.youtube.com/embed/UD0_tK9DToc?ecver=1]

次に、10分ほど学習させた結果が以下。明らかに、意図して前にすすんでいることが分かる。

[https://www.youtube.com/embed/El13ZG2m_wY?ecver=1]

今回利用したアルゴリズムはQ-Learning。

赤外線センサで壁との位置を計測して、壁に塚づいたら報酬を与える。

以下が今回のソースコードです。

27 Jun 2017, 13:10

3000 回転んでも立ち上がれなかったロボット

前回、CartPole 問題が Q-Learning でやっと解けたので、 CartPole 問題を応用して、レゴマインドストームの GyroBoy で倒立振子を試みた。

結果

3000 回転んでも立ち上がらなかった。ヾ(´∀`)ノ

現実は、シミュレーション通りにはいかなかった。

しかし、悲観的になってはいけない。

始めは 1 秒しか立てなかったのが、5 秒立てるようになったのだ!!

ソースコード

gist にあげました。

26 Jun 2017, 13:33

OpenAI Gym の CartPole 問題が Q-Learning で解けたぞ

CartPole 問題は、今までこのブログでもなんども取り上げてきた。

CartPole 問題は、今までこのブログでもなんども取り上げてきた。 - [[http://futurismo.biz/archives/6481][OpenAI Gym の CartPole-v0 を試したメモ | Futurismo]] - [[http://futurismo.biz/archives/6549][CEM で CartPole-v0 を試したメモ | Futurismo]]

今日、ついに解けたのだ。

とくに、最後のリンクは、Reinforcement Learning のチュートリアルがまとまっている良リポジトリ。 この場を借りて、紹介したい。

課題

環境から取得できる情報は次の 4 つ。(observation_space) - 横軸座標(x) - 横軸速度(x_dot) - ポール角度(theta) - ポール角速度(theta_dot)

エージェントが取れる行動は環境状態によらず次の 2 つ。(acnton_space) - 左に動く - 右に動く

課題は、環境から取得できる値が連続値であること。 Q テーブル(状態、行動)対に対して一つの値を扱う。

つまり、連続値である状態を範囲で区切って離散の値に変換して、Q テーブルのインデックスに用いることが必要。

ここでは gdb さんのコード を参考に以下のように書いた。

def build_state(features):
    """get our features and put all together converting into an integer"""
    return int("".join(map(lambda feature: str(int(feature)), features)))

def to_bin(value, bins):
    return np.digitize(x=[value], bins=bins)[0]

cart_position_bins = np.linspace(-2.4, 2.4, 2)
cart_velocity_bins = np.linspace(-2, 2, 10)
pole_angle_bins = np.linspace(-0.4, 0.4, 50)
pole_velocity_bins = np.linspace(-3.5, 3.5, 20)

def transform(observation):
    # return an int
    cart_pos, cart_vel, pole_angle, pole_vel = observation
    return build_state([
        to_bin(cart_pos, cart_position_bins),
        to_bin(cart_vel, cart_velocity_bins),
        to_bin(pole_angle, pole_angle_bins),
        to_bin(pole_vel, pole_velocity_bins)
    ])

これで、transform を呼ぶと、ovservation に対して一意な離散値が出力されるので、それを状態値として扱う。

注目すべきは、ここ。横軸座標(x)は荒く、ポール角度は細かく値を区切っている。こうすると精度がよかったのでこうしている。

cart_position_bins = np.linspace(-2.4, 2.4, 2)
cart_velocity_bins = np.linspace(-2, 2, 10)
pole_angle_bins = np.linspace(-0.4, 0.4, 50)
pole_velocity_bins = np.linspace(-3.5, 3.5, 20)

Code

このコードは、Practical RL の week2 の宿題である。

以下を参考にした。 - Berkeley’s CS188 pacman project code http://ai.berkeley.edu/ - Victor Mayoral Vilches’s RL tutorial https://github.com/vmayoral/basic_reinforcement_learning - gdb openAI https://gym.openai.com/evaluations/eval_aHf1Kmc4QIKm5oPcJJToBA

Result

14 Jun 2017, 11:36

三目並べ(tic-tac-toe) にモンテカルロ法を試した

三目並べ(tic-tac-toe) をモンテカルロ法をつかって学習させました。

元ネタは、これ。

この本のコードは Octave でかかれているのだけれども、それを Python にポーティングしてくれた人がいた。

自分はなにをしたかというと、このコードを OpenAI Gym の tic-tac-toe に対応させてみた(tic-tac-toe は非公式)

ただ移しただけだと動かなかった。それから 10 時間くらいでばっくをしていた。頭がおかしくなる。

コード

おまけ: プルリク奮戦記

じつは、OpenAI Gym の tic-tac-toe リポジトリにバグっぽいのをみつけた。

ピンチ!いやチャンス?

自分は他人の Github リポジトリにプルリクをしたことがないのだ。

まずは気さくな感じを醸し出して質問をしてみた。Hello!! :-)

1 分も立たずに返信が帰ってきたのでビビる。はえーよ!

feel free to provide a PR!!

もうこの言葉が理解できずにガクガクした。なにかへんな英語表現を使っちゃったかな??

大丈夫、PR は Pull Reqest のことだ。って、ぅえ〜〜〜〜〜。やったことないよ〜〜〜。

プルリクエストの作法を調べる。

  1. branch 経由
  2. fork repository 経由

検索上位に引っかかるのは、初心者は branch を切る方法が勧められている。

  1. 初心者向け Github への PullRequest 方法 - Qiita
  2. Pull Request の仕方 - Qiita

バカだから何を思ったのか、fork ポタンをポチリ。。。。。fork してしまったぁぁ!!!

とりあえず、バグの修正をして、fork した自分のリポジトリに push

そして、GUI 画面から PULL REQUEST !!!!

ふう、まつこと数分・・・ 10 分でレスポンスが帰ってきた。もう心臓に悪りーよ。

  • コメントの単語が適切でない
  • 冗長な for 文の回し方をしている

その通りです、なにもいうことはありません。 こっちも急いで修正して、再コミット!!

祈った。。。そうして待つこと 15 分、ついに Merge されました〜〜〜〜!!!

うらららららららららら〜〜〜〜〜〜〜!