無料で使える機械学習クラウド（Google Colaboratory)で、ディープラーニングなFX自動トレードシミュレーション

どうも、[オリィ研究所](http://orylab.com) の ryo_grid こと神林です。
(以下は趣味でやっているもので、業務とは関係ありません）

# はじめに
もう、結構前になってしまいましたが、こんなことをやっていました。

[ディープラーニングでFXシステムトレード](https://qiita.com/ryo_grid/items/7746528f8cae8026b936)
♯ Qiitaの記事を綺麗に埋め込むにはどうすればいいのでしょう

で、掲題の Google Colaboratory で、うまくやれば Nvidia Tesla K80 というかなり強力なGPUを占有で、ずっと使える、それも無料で、という情報を耳にしたので、試しに上の記事のコードを動かしてみようと思ったわけです。

# 結論（結果）
パラメータを変更する必要はありましたが、GPUで最も高速な学習処理が行われることが確認されました(TPUよりも高速。まあ、いろいろ適性とかがあるのでしょう）。
Kerasの進捗表示を見る限り 10us/sampleで処理が進んでいる(実際は10-9usでブレてはいる) ので、そこから算出すると、学習時のサンプル数が28107で、そのスピードで3000 epoch 回すと、単純計算では 0.00001  * 28107 * 3000 / 60 => 約14.05分程度で学習処理が完了することになる、はず。多分。  
で、以前、MBA 2011 midで計算していた時は 1000 epochで 一時間程度かかっていたようなので、そこから比べると、学習処理の進行速度は約12.81倍高速、であると言えるのかなと思います（本当か？）。
まあ、プログラムに処理時間を計測して出力するコードを仕込んでおけばいいだけなんですが、忘れたので、こんな見積もりを書いています・・・。

# 残った謎 (追記 19/04/01 8:00)
バッチサイズを大きくしたら手元のラップトップでもGPU・TPUを用いた場合と同等の速度が出ました・・・・。
どういうことだろう・・・。
謎が残ってしまった・・・。

- 完走させてないものもあるけど、Colaborateryでの実行結果。  
　
自作のKeraを使った為替予測モデル作成からのトレードシミュレーション（の学習処理）の学習処理では、GPU・TPUを押さえてCPUが最速・・・  
https://github.com/ryogrid/fx_systrade/blob/master/run_keras_trade_procedure_on_qiita_post_use_gpu_epoch10000_batch1000_contain_output.ipynb  
https://github.com/ryogrid/fx_systrade/blob/master/run_keras_trade_procedure_on_qiita_post_use_cpu_epoch3000_batch1000_contain_output.ipynb  
　
一方、公式サンプル (Fashion MNIST) だとTPUが爆速。  
https://github.com/ryogrid/fx_systrade/blob/master/Keras_Fashion_MNIST_execed_at_TPU.ipynb  
TPUで走らせた時はせいぜい20分程度で全ての処理が完了していたので、  
下のCPU実行は、ETA が 3:51:44 の時点でお察し。  
https://github.com/ryogrid/fx_systrade/blob/master/Keras_Fashion_MNIST_execed_at_CPU_in_progress.ipynb  
さて、何が違うのだろう。

# Google Colaboratory とは
詳細はぐぐってください（完）。

だと、さすがにあれなので、少し説明すると、Jupyter NotebookをUIとしたインタラクティブに使える機械学習用クラウド（？）です。
　Nvidia Tesla K80 というGPUが占有で使えます（誤っていたら指摘お願いします）。また、Google神謹製のニューラルネットワークなプログラムの専用アクセラレータであるTPUも使えます（こちらは占有かは分からないし、複数セッションで使い続けるとかすると、利用にあたって制限を受けるかもしれません。ぶっちゃけ、まだちゃんと調べられていないです）

で、無料です。マジで。
　ただ、時間制限という制約はあります。ですが、時間制限といっても、使える時間の総量？が制限されているわけではなく、ブラウザからのセッションが切れて90分立ってしまうとインスタンスが死ぬというのと、一度動かしたコードは12時間でインスタンスが死んで打ち切られる（正確には、インスタンスを立ち上げた時点から12時間、ということだったはず）というのがあるだけで、12時間たったあとに、再度セッションを開始すれば、また12時間は同じようにインスタンスを使えます。ということは、90分制限を回避する仕組みを用意し、動かすプログラムは途中で実行中の状態をスナップショットとるようにして、次のセッションではそのスナップショットを読み込んで処理を継続すればいいわけですねえ。
　時間制限がうっとおしいから、なくすために課金させてくれえ、とかはどうもできないらしく、やりたければ自前のリソースを接続して使ってね、というスタイルのようです。なので、基本無料、ではなく、無料、です。

時間制限の回避については以下の記事などを読まれるとよいでしょう。
[Google Colaboratoryの90分セッション切れ対策【自動接続】](https://qiita.com/enmaru/items/2770df602dd7778d4ce6)

# FX自動トレードシミュレーションプログラムを動かす
Colaboratoryのセットアップについてはぐぐってもらうとして、私のプログラムの動作に必要な手順だけ書いておきます。
♯ jupyter notebook 使ってるんだから、その内容晒せばええやろ？、と言われると、その通りなのですが、まだ整理できていないので、堪忍して下さい。

ただ、最初に書いておきますが、

- 動作した、と書いているものは、学習処理が動作している様子がKerasの？進捗表示で確認できたというだけで、完走は確認していません。モデルの保存のところでこける可能性とかあります。また、作成したモデルを用いた推論処理（の結果を使ったトレードのシミュレーション）はまだ動かした実績がありません。これらは、手元の開発環境でも、同様です（確認していない）。   
=> トレードのシミュレーション含めて完走を確認しました。
- TPUでの実行はまだ成功していません。  
=> 成功しました
- GPUでは動作したが、手元のさしてスペックが高いわけでもないラップトップより二倍ぐらい遅くなりました（進捗表示で確認できるエポックごとの処理時間からすると）。原因は今のところ調査中ですが、パッケージ？がkeras.xxx ではダメで、tensorfrow.keras.xxx でないとダメという話か（TPU利用時は後者である必要があります。今は後者に書き換えてあります）、処理量が小さすぎるためにGPU利用のオーバヘッドの方が大きい、ということではないかと推測しています。前者は再度試すだけ、後者はバッチサイズを大きくするなどしてみることを考えています。  
=> TPUでもラップトップより4倍くらい遅いという性能でしたが、バッチサイズを100から1000に変更したら、ラップトップより4倍くらい速くなりました。(TPU実行だけで比較するとパラメータ変更により16倍速くなったことになる）。まだ試していませんがGPUにおいても、おそらく同様に期待通りの？高速化が得られるようになるのではないかと思います。なお、バッチサイズをさらに大きくしたらどうなるかは、これから試していこうと思っています。  
=> 5000まで上げてみましたが、少し速くはなったものの大差ありませんでした。アクセラレータ含めた計算機の最適なバッチサイズ（性能が出る）ってのはあったりするんでしょうかね？
- CPUのみのランタイム設定で動かしたら、上で言及したラップトップで動作させた場合より2倍程度高速に動作しました。

# 手順 (GPU利用)
Python3のノートブックを作成。ランタイムはGPUを使うように設定。

以下、コマンド・コード実行手順。
ですが、その内容を丸っと書いたJupyter Notebookを用意してみました。
これを開いてコピーして？、ランタイムをGPUにして、ランタイム - 全てのセルを実行、とすると一気に実行できるはずです（Google Driveのマウントのところは手動で認証の操作が必要）。
[以下手順を書いたJupyter Notebook](https://colab.research.google.com/drive/1azbpzYQ9QbU2wmMNRwRl_JQUWRiUpwt5)

pipパッケージを入れていく。

```bash
!pip install h5py
!pip install keras
!pip install sklearn
!pip install tensorflow-gpu
``` 

ta-libはちょっと面倒

```bash
%cd ~/
!wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
!tar -xzvf ta-lib-0.4.0-src.tar.gz
%cd ta-lib
!./configure --prefix=/usr
!make
!make install
!pip install ta-lib
```

Google Driveをマウントする。
Google Driveにはルート直下にgcolab_workdirというディレクトリを事前に作っておいてください。(別の名前でも構いませんが、その場合はこの記事の手順の中のパスも置き換えるよう注意してください)

```bash
from google import colab
colab.drive.mount('/content/gdrive')
```

コードと入力データをgithubから持ってくる。

```bash
%cd '/content/gdrive/My Drive/gcolab_workdir/'
!git clone -b for_try_keras_trade_learning_at_google_colab_qiita1 https://github.com/ryogrid/fx_systrade.git
%cd 'fx_systrade'
```

いざ、実行！

```bash
import keras_trade_at_google_colab_cpu_gpu
keras_trade_at_google_colab_cpu_gpu.run_script("TRAIN")
keras_trade_at_google_colab_cpu_gpu.run_script("TRADE")
```

ちゃんと完走すれば生成されたモデルと、シミュレーションのログがマウントしたディレクトリにあるはず。
また、Google DriveのWebインタフェースから見ても確認できるはずです。

実行した際の出力も含む jupyter notebook はこちら。
[実行結果](https://colab.research.google.com/drive/11EBV_ZiGvhAzCDAv3rLafo7GtjkBzYOY)
[実行結果 at github](https://github.com/ryogrid/fx_systrade/blob/83f88570a9986ba3ec0135c777083a0ddae55d9d/run_keras_trade_procedure_on_qiita_post_use_gpu_contain_output.ipynb)

# 助けていただけるとありがたい (3/30 10:00 -> 解決しました!)
ランタイムのアクセラレータをTPUに設定した上で、インストールする tensorflow を tensorflow-gpu ではなく tensorflowにして、

```bash
import keras_trade_at_google_colab_tpu
keras_trade_at_google_colab_tpu.run_script("TRAIN")
```

とすると、コードはTPUを利用しにいくのですが、以下のようなエラーになってしまっています。
原因をご存知の方がいればコメント欄で教えていただけるとありがたいです 〇刀乙

```bash
data size: 836678
train len: 83667
2 classes
17 dims
INFO:tensorflow:Querying Tensorflow master (grpc://10.12.186.98:8470) for TPU system metadata.
INFO:tensorflow:Found TPU system:
INFO:tensorflow:*** Num TPU Cores: 8
INFO:tensorflow:*** Num TPU Workers: 1
INFO:tensorflow:*** Num TPU Cores Per Worker: 8
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:CPU:0, CPU, -1, 16514675533203383013)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:XLA_CPU:0, XLA_CPU, 17179869184, 4585360841116493997)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:0, TPU, 17179869184, 16984093160179709170)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:1, TPU, 17179869184, 8193093783894359376)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:2, TPU, 17179869184, 12076229141210125913)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:3, TPU, 17179869184, 16700992325723477645)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:4, TPU, 17179869184, 6430518941424391562)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:5, TPU, 17179869184, 3947497457430632353)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:6, TPU, 17179869184, 1795967380953314994)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU:7, TPU, 17179869184, 16400414982824425925)
INFO:tensorflow:*** Available Device: _DeviceAttributes(/job:worker/replica:0/task:0/device:TPU_SYSTEM:0, TPU_SYSTEM, 17179869184, 16039450443299016384)
WARNING:tensorflow:tpu_model (from tensorflow.contrib.tpu.python.tpu.keras_support) is experimental and may change or be removed at any time, and without warning.
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-73cd67faf7af> in <module>()
      1 import keras_trade_at_google_colab2
----> 2 keras_trade_at_google_colab2.run_script("TRAIN")
/content/gdrive/My Drive/gcolab_workdir/fx_systrade/keras_trade_at_google_colab2.py in run_script(mode)
    470         if exchange_dates == None:
    471             setup_historical_fx_data()
--> 472         train_and_generate_model()
    473     elif mode == "TRADE":
    474         if exchange_dates == None:
/content/gdrive/My Drive/gcolab_workdir/fx_systrade/keras_trade_at_google_colab2.py in train_and_generate_model()
    349     tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(tpu_grpc_url)
    350     strategy = keras_support.TPUDistributionStrategy(tpu_cluster_resolver)
--> 351     model = tf.contrib.tpu.keras_to_tpu_model(model, strategy=strategy)
    352 
    353     print("Training model...")
/usr/local/lib/python3.6/dist-packages/tensorflow/contrib/framework/python/framework/experimental.py in new_func(*args, **kwargs)
     62         'any time, and without warning.',
     63         decorator_utils.get_qualified_name(func), func.__module__)
---> 64     return func(*args, **kwargs)
     65   new_func.__doc__ = _add_experimental_function_notice_to_docstring(
     66       func.__doc__)
/usr/local/lib/python3.6/dist-packages/tensorflow/contrib/tpu/python/tpu/keras_support.py in tpu_model(model, strategy)
   2198     A new `KerasTPUModel` instance.
   2199   """
-> 2200   _validate_shapes(model)
   2201   # TODO(xiejw): Validate TPU model. TPUModel only?
   2202   # TODO(xiejw): Validate replicas. Full or 1. Shall we allow subset?
/usr/local/lib/python3.6/dist-packages/tensorflow/contrib/tpu/python/tpu/keras_support.py in _validate_shapes(model)
   2136   """Validate that all layers in `model` have constant shape."""
   2137   for layer in model.layers:
-> 2138     if isinstance(layer.input_shape, tuple):
   2139       input_shapes = [layer.input_shape]
   2140     else:
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in input_shape(self)
   1116     """
   1117     if not self._inbound_nodes:
-> 1118       raise AttributeError('The layer has never been called '
   1119                            'and thus has no defined input shape.')
   1120     all_input_shapes = set(
AttributeError: The layer has never been called and thus has no defined input shape.

```
=> 最初のDenceクラスに input_shapeキーワード引数を与えることで解決できました! (keras.xxx の Denseだと指定するとエラーになるが、tensorflow.keras.xxx だと逆に指定しないとダメ、ということだったぽい）

# おまけ (Windows10 64bit Python3.7環境でCPU実行する）
なんか最近はWindowsでもTensorFlowが使えるらしいので、手元での動作確認のために環境構築した時の手順です。
　
なお、以下の手順が誤った tensorflow 環境を作ってしまうものである可能性はなきにしにあらずなので、virtualenv を使っておくことをおすすめします。

```bash
cd <適当な作業ディレクトリ>
pip install h5py
pip install keras
pip install sklearn
pip install tensorflow
```

ta-libは面倒くさい。

以下のページの手順で C:\ta-lib に、ta-libパッケージがラップするそもそものライブラリをインストールしておいてください。
https://githubja.com/mrjbq7/ta-lib
依存関係 - windows のところです。
最後のところはnmakeを叩くだけです。

ta-lib pipパッケージをインストール

```bash
pip install ta-lib
```

コードと入力データをを持ってくる

```bash
git clone -b for_try_keras_trade_learning_at_google_colab_qiita1 https://github.com/ryogrid/fx_systrade.git
```

いざ、実行! (ちゃんと動くかな？どきどき）
なお、残念ながらこの手順だとCPU実行しかされません。

```bash
cd fx_systrade
# 学習処理とそれにより生成されたモデルを用いた推論に基づくトレードシミュレーションが続けて走る
python keras_trade_at_google_colab_cpu_gpu.py
```

出力の冒頭。なんかいろいろワーニングやらが出ていて気になる。

```bash
> python .\keras_trade_at_google_colab_cpu_gpu.py
Using TensorFlow backend.
data size: 836678
train len: 83667
2 classes
17 dims
WARNING:tensorflow:From F:\work\fx_systrade\lib\site-packages\tensorflow\python\ops\resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
Training model...
WARNING:tensorflow:The `nb_epoch` argument in `fit` has been renamed `epochs`.
WARNING:tensorflow:From F:\work\fx_systrade\lib\site-packages\tensorflow\python\keras\layers\core.py:143: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.
Instructions for updating:
Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.
Train on 28107 samples, validate on 4961 samples
WARNING:tensorflow:From F:\work\fx_systrade\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Epoch 1/3000
2019-03-29 16:17:27.299420: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
28107/28107 [==============================] - 2s 85us/sample - loss: 0.7033 - val_loss: 0.5223
Epoch 2/3000
28107/28107 [==============================] - 1s 41us/sample - loss: 0.5596 - val_loss: 0.5052
Epoch 3/3000
28107/28107 [==============================] - 1s 44us/sample - loss: 0.5
```