2019.10.08

TCNを用いてFX予測してみる

Pocket

こんにちは。次世代システム研究室のT.D.Qです。 最近、業務でGPUクラウド上にDeep Learning環境を構築したり、ビッグデータ解析で金融データを触ったりしていますので、この技術を使ってFX予測できないかと思い始めました。同僚がFXの予測をテーマとして複数のブログを書いていますが、時系列処理は再帰型ニューラルネットワーク(RNN)、特にLSTMが優れている印象を受けました。関連する技術を調査した結果、LSTMより精度が高いというTCNを発見しましたので、今回はTCNを用いてFXやってみたいと思います。

実行環境

今回はGPUクラウド by GMOの1GPUプランで下記のマシンで、nvidia-docker、Googleが提供するtensorflow:latest-gpu-jupyterイメージを使って機械学習の開発環境を構築しました。ちなみに、マシンのスペックは下記の通りで、少しハイスペックですね。

基本仕様 GPUカード NVIDIA® Tesla® V100 SXM2(16GB)
GPUカード搭載数 1
GPU搭載メモリ 16GB
単精度浮動小数点数演算 約15.7TFLOPS
倍精度浮動小数点数演算 約7.8TFLOPS
NVIDIA® Tensorコア数 640
NVIDIA® CUDA®コア数 5,120
標準OS Ubuntu 18.04 LTS/ CentOS 7.5
CPU 16vCPU(Intel® Xeon® Gold 5122 4コア 3.60GHz ×2)

実際にコマンドでGPUマシンの開発環境を確認してみましょう。

> nvidia-smi
Wed Sep 25 13:29:59 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 410.72       Driver Version: 410.72       CUDA Version: 10.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:05.0 Off |                    0 |
| N/A   47C    P0    54W / 300W |   8785MiB / 16130MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

TCNの概要

TCNが「Temporal Convolutional Networks」の略でAn Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modelingで紹介されました。畳み込みニューラルネットワーク(CNN)のアーキテクチャーにRNNの特徴を加えて時系列データを処理します。TCNの特徴には下記の2点があります。

  1. どんな長さのInputでも同じ長さのOutputを作成できる。またOutputはInputより短くても可能。
  2. 畳み込み(Convolutional)構造がCausalで、情報が未来から過去に”漏れる”(leakage)ことがない

1を保証するため1D fully-convolutional network (FCN) architectureを使います。これで、ネットワークのhidden layerもinput layerと同じ長さとなります。また、 (kernel size − 1)ゼロパディングという手法を使って、次のシーケンスをInputシーケンスの長さと等しくします。 また、2を保証するため、時刻tのOutputはそれ以前のデータのみを利用します。これは「causal convolutions」と言います。つまり、「TCN = 1D FCN + causal convolutions」で、挙動は下記の画像を見た方が分かりやすいですね。

Dilated convolution

TCNの強い点

  • 並列処理が可能:これがRNNと違うところです。TCNは同じFilterを各Layerに適用するため、長いInputシーケンスを同時に処理することができます。
  • トレーニングの際にRNN(LSTM、GRUなど)より必要なメモリが少ない。これは各Layerに同じFilterを共有するためです。この特徴を持つことでメモリにあまり余裕がなくてとも長いInputを処理可能です。
  • 時系列からパータンを学習する以外にも各Layerでデータから特徴的なパターンを発見可能。

学習の準備

データ入手

Forexのデータはhistdata.comからドル円の2019年のレートデータを入手しました。無料で複数のFormat(MetaTrader, MetaStockなど)を提供しているので非常に便利です。今回は2019年1月から7月までのレートデータをダウンロードして集約しました。

tmp = []
for fname in sort(glob.glob('../data/DAT_ASCII_USDJPY_T_*.csv')):
    print(fname)
    tmp.append(pd.read_csv(fname, 
                names=['datetime_stamp', 'bid_quote', 'ask_quote', 'volume']))
    
df = pd.concat(tmp)

../data/DAT_ASCII_USDJPY_T_201901.csv
../data/DAT_ASCII_USDJPY_T_201902.csv
../data/DAT_ASCII_USDJPY_T_201903.csv
../data/DAT_ASCII_USDJPY_T_201904.csv
../data/DAT_ASCII_USDJPY_T_201905.csv
../data/DAT_ASCII_USDJPY_T_201906.csv
../data/DAT_ASCII_USDJPY_T_201907.csv

データの前処理

・Tickデータとして入手しましたが、実際データを確認してみると欠損が多くてTickデータとしては使えなさそうですので、1分足のデータにDownSamplingを行いました。

特徴エンジニアリング

レートデータはそのまま使えないので、下記のデータの前処理を行いました。

BIDレートとASKレートからMIDレートを作る MA、MACD、BollingerBandなどの特徴量を作る

作った特徴量を確認しましょう。今回のMIDプライスを表現するため、21個の特徴量を使います。約20万点の1分足のFXデータを用いて学習・検証を行います。

raw_forex_df.columns

Index(['datetime_stamp', 'mid', 'MA_5min', 'MA_8min', 'MA_13min', 'MA_1hour',
       'MA_1day', 'MA_5day', 'STD_5min', 'STD_8min', 'STD_13min', 'STD_1hour',
       'STD_1day', 'STD_5day', 'momentum', 'MA_20min', 'STD_20min',
       'upper_band', 'lower_band', 'ema', 'ema_12', 'ema_26', 'MACD',
       'MACD_signal'],
      dtype='object')


raw_forex_df.values.shape

(205590,21)

トレーニングデータセットと検証データセットを分割する

from sklearn.model_selection import train_test_split
df_train, df_test = train_test_split(raw_forex_df, train_size=0.9, test_size=0.1, shuffle=False)

Feature Scaling

データを確認しましょう。

データセットの特徴量によってスケールが異なるので揃える必要があります。今回はSklearnのMinMaxScalerを用いて正規化し、特徴量を[0, 1]の範囲に変換しました。

x_scaler = MinMaxScaler()

df_x_train_scaled = x_scaler.fit_transform(df_train)
df_x_train_scaled[0:5]

array([[0.62589499, 0.61497242, 0.6002301 , 0.58236372, 0.51455824,
        0.00253999, 0.0021764 , 0.0028962 , 0.01220264, 0.        ,
        0.        , 0.72659446, 0.56524956, 0.0055043 , 0.51399822,
        0.67436233, 0.62083891, 0.55097021, 0.51782655, 0.8427443 ,
        0.83256743],
       [0.6254129 , 0.61480089, 0.6001928 , 0.58219051, 0.51458329,
        0.00360003, 0.00245516, 0.00304832, 0.01224252, 0.        ,
        0.        , 0.72924187, 0.56531401, 0.00513634, 0.51387099,
        0.67454783, 0.62029185, 0.5508006 , 0.51780168, 0.84193072,
        0.83208187],
       [0.62382904, 0.61423204, 0.59987749, 0.58187377, 0.51457216,
        0.00674563, 0.00481407, 0.00423895, 0.01224236, 0.        ,
        0.        , 0.72539109, 0.56529046, 0.00535693, 0.51396402,
        0.67444758, 0.61903777, 0.55036191, 0.51762208, 0.84040926,
        0.83130946],
       [0.62265901, 0.61347754, 0.5994243 , 0.58148465, 0.51453598,
        0.00965206, 0.00739651, 0.00597524, 0.01228387, 0.        ,
        0.        , 0.72683513, 0.56517272, 0.00643653, 0.51441822,
        0.67395759, 0.61782847, 0.54977148, 0.517343  , 0.83857018,
        0.83022741],
       [0.62272833, 0.61256781, 0.59895189, 0.58115604, 0.51450259,
        0.00822098, 0.00859928, 0.00708083, 0.01232   , 0.        ,
        0.        , 0.73116726, 0.56501409, 0.0074225 , 0.51477803,
        0.67347134, 0.61747123, 0.54928433, 0.51709017, 0.83716339,
        0.82900676]])

目的変数

今回は、ドル円の過去のデータを学習して将来のMIDプライスを予測するので、datasetの「mid」が目的変数ですね。また、時系列データを予測する問題なので、タイムステップ(lookback window)が512でTrain, Testデータを作りました。過去の512分の観察を学習して次の1分のMIDプライスを予測するイメージです。TCNは長いスパンの過去データの特徴を覚えられるので、タイムステップを少々長めに設定しました。 データセットから説明変数、目的変数の時系列に変換します。

from tqdm import tqdm_notebook
def build_timeseries(dataset, target_column_index, time_steps):
    # Total number of time-series samples would be len(dataset) - time_steps
    dim_0 = dataset.shape[0] - time_steps
    print(dim_0)
    dim_1 = dataset.shape[1]
    print(dim_1)
    
    # Init the x, y matrix
    x = np.zeros((dim_0, time_steps, dim_1))
    # Number of output variables is 1 for this mid price prediction
    y = np.zeros((dim_0, 1) )
    
    # fill data to x, y
    for i in tqdm_notebook(range(dim_0)):
        x[i] = dataset[i: i + time_steps]
        y[i] = dataset[i + time_steps, target_column_index]
    
    print("Lenght of time-series x, y", x.shape, y.shape)
    return x, y
lookback_window = 512 

x_train_scaled, y_train_scaled = 
build_timeseries(df_x_train_scaled, target_column_index, lookback_window)

x_test_scaled, y_test_scaled  = build_timeseries(df_x_test_scaled, target_column_index, lookback_window)

モデル構築

TCN に関しては,深層学習フレームワーク Keras で実装されているTCN-tensorflowを TensorFlow のバックグラウンドのもと使用し、TCN-tensorflowのソースを参考してモデルを作成しました。

i = Input(shape=(lookback_window, num_x_signals, ))
m = TCN(nb_stacks=1)(i)
m = Dense(num_y_signals, activation='linear')(m)
model = Model(inputs=[i], outputs=[m])
optimizer = RMSprop(lr=1e-4)
model.compile(optimizer=optimizer,
              loss='mse',  
              metrics=['mae'])
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, 512, 21)]    0                                            
__________________________________________________________________________________________________
conv1d (Conv1D)                 (None, 512, 64)      1408        input_1[0][0]                    
__________________________________________________________________________________________________
conv1d_1 (Conv1D)               (None, 512, 64)      8256        conv1d[0][0]                     
__________________________________________________________________________________________________
activation (Activation)         (None, 512, 64)      0           conv1d_1[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d (SpatialDropo (None, 512, 64)      0           activation[0][0]                 
__________________________________________________________________________________________________
conv1d_2 (Conv1D)               (None, 512, 64)      8256        spatial_dropout1d[0][0]          
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 512, 64)      0           conv1d_2[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d_1 (SpatialDro (None, 512, 64)      0           activation_1[0][0]               
__________________________________________________________________________________________________
conv1d_3 (Conv1D)               (None, 512, 64)      4160        conv1d[0][0]                     
__________________________________________________________________________________________________
add (Add)                       (None, 512, 64)      0           conv1d_3[0][0]                   
                                                                 spatial_dropout1d_1[0][0]        
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 512, 64)      0           add[0][0]                        
__________________________________________________________________________________________________
conv1d_4 (Conv1D)               (None, 512, 64)      8256        activation_2[0][0]               
__________________________________________________________________________________________________
activation_3 (Activation)       (None, 512, 64)      0           conv1d_4[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d_2 (SpatialDro (None, 512, 64)      0           activation_3[0][0]               
__________________________________________________________________________________________________
conv1d_5 (Conv1D)               (None, 512, 64)      8256        spatial_dropout1d_2[0][0]        
__________________________________________________________________________________________________
activation_4 (Activation)       (None, 512, 64)      0           conv1d_5[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d_3 (SpatialDro (None, 512, 64)      0           activation_4[0][0]               
__________________________________________________________________________________________________
conv1d_6 (Conv1D)               (None, 512, 64)      4160        activation_2[0][0]               
__________________________________________________________________________________________________
add_1 (Add)                     (None, 512, 64)      0           conv1d_6[0][0]                   
                                                                 spatial_dropout1d_3[0][0]        
__________________________________________________________________________________________________
activation_5 (Activation)       (None, 512, 64)      0           add_1[0][0]                      
__________________________________________________________________________________________________
conv1d_7 (Conv1D)               (None, 512, 64)      8256        activation_5[0][0]               
__________________________________________________________________________________________________
activation_6 (Activation)       (None, 512, 64)      0           conv1d_7[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d_4 (SpatialDro (None, 512, 64)      0           activation_6[0][0]               
__________________________________________________________________________________________________
conv1d_8 (Conv1D)               (None, 512, 64)      8256        spatial_dropout1d_4[0][0]        
__________________________________________________________________________________________________
activation_7 (Activation)       (None, 512, 64)      0           conv1d_8[0][0]                   
__________________________________________________________________________________________________
spatial_dropout1d_5 (SpatialDro (None, 512, 64)      0           activation_7[0][0]               
__________________________________________________________________________________________________
conv1d_9 (Conv1D)               (None, 512, 64)      4160        activation_5[0][0]               
__________________________________________________________________________________________________
add_2 (Add)                     (None, 512, 64)      0           conv1d_9[0][0]                   
                                                                 spatial_dropout1d_5[0][0]        
__________________________________________________________________________________________________
activation_8 (Activation)       (None, 512, 64)      0           add_2[0][0]                      
__________________________________________________________________________________________________
conv1d_10 (Conv1D)              (None, 512, 64)      8256        activation_8[0][0]               
__________________________________________________________________________________________________
activation_9 (Activation)       (None, 512, 64)      0           conv1d_10[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_6 (SpatialDro (None, 512, 64)      0           activation_9[0][0]               
__________________________________________________________________________________________________
conv1d_11 (Conv1D)              (None, 512, 64)      8256        spatial_dropout1d_6[0][0]        
__________________________________________________________________________________________________
activation_10 (Activation)      (None, 512, 64)      0           conv1d_11[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_7 (SpatialDro (None, 512, 64)      0           activation_10[0][0]              
__________________________________________________________________________________________________
conv1d_12 (Conv1D)              (None, 512, 64)      4160        activation_8[0][0]               
__________________________________________________________________________________________________
add_3 (Add)                     (None, 512, 64)      0           conv1d_12[0][0]                  
                                                                 spatial_dropout1d_7[0][0]        
__________________________________________________________________________________________________
activation_11 (Activation)      (None, 512, 64)      0           add_3[0][0]                      
__________________________________________________________________________________________________
conv1d_13 (Conv1D)              (None, 512, 64)      8256        activation_11[0][0]              
__________________________________________________________________________________________________
activation_12 (Activation)      (None, 512, 64)      0           conv1d_13[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_8 (SpatialDro (None, 512, 64)      0           activation_12[0][0]              
__________________________________________________________________________________________________
conv1d_14 (Conv1D)              (None, 512, 64)      8256        spatial_dropout1d_8[0][0]        
__________________________________________________________________________________________________
activation_13 (Activation)      (None, 512, 64)      0           conv1d_14[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_9 (SpatialDro (None, 512, 64)      0           activation_13[0][0]              
__________________________________________________________________________________________________
conv1d_15 (Conv1D)              (None, 512, 64)      4160        activation_11[0][0]              
__________________________________________________________________________________________________
add_4 (Add)                     (None, 512, 64)      0           conv1d_15[0][0]                  
                                                                 spatial_dropout1d_9[0][0]        
__________________________________________________________________________________________________
activation_14 (Activation)      (None, 512, 64)      0           add_4[0][0]                      
__________________________________________________________________________________________________
conv1d_16 (Conv1D)              (None, 512, 64)      8256        activation_14[0][0]              
__________________________________________________________________________________________________
activation_15 (Activation)      (None, 512, 64)      0           conv1d_16[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_10 (SpatialDr (None, 512, 64)      0           activation_15[0][0]              
__________________________________________________________________________________________________
conv1d_17 (Conv1D)              (None, 512, 64)      8256        spatial_dropout1d_10[0][0]       
__________________________________________________________________________________________________
activation_16 (Activation)      (None, 512, 64)      0           conv1d_17[0][0]                  
__________________________________________________________________________________________________
spatial_dropout1d_11 (SpatialDr (None, 512, 64)      0           activation_16[0][0]              
__________________________________________________________________________________________________
add_6 (Add)                     (None, 512, 64)      0           spatial_dropout1d_1[0][0]        
                                                                 spatial_dropout1d_3[0][0]        
                                                                 spatial_dropout1d_5[0][0]        
                                                                 spatial_dropout1d_7[0][0]        
                                                                 spatial_dropout1d_9[0][0]        
                                                                 spatial_dropout1d_11[0][0]       
__________________________________________________________________________________________________
lambda (Lambda)                 (None, 64)           0           add_6[0][0]                      
__________________________________________________________________________________________________
dense (Dense)                   (None, 1)            65          lambda[0][0]                     
==================================================================================================
Total params: 121,345
Trainable params: 121,345
Non-trainable params: 0
__________________________________________________________________________________________________

コールバック関数の設定

効率的にトレーニングを行うため、KerasのCallback Functionを使いました。

Checkpointを作成する

トレーニングではロス関数の最小値が改善された場合、その時点でのモデルを一旦保存します。今回は、Validationの際の値で評価したいと思いますので、「val_loss」を使いました。

path_checkpoint = 'alpha_trader_checkpoint.keras'
callback_checkpoint = ModelCheckpoint(filepath=path_checkpoint,
                                      monitor='val_loss',
                                      verbose=1,
                                      save_weights_only=True,
                                      save_best_only=True)
Early Stopping

トレーニングを行ってもValidationのパフォーマンスが改善されない場合に学習を強制的に終了する機能です。Overfitting問題を回避する効果もあります。今回は5 epochs間で精度が改善しなければ終了するようにします。

callback_early_stopping = EarlyStopping(monitor='val_loss',
                                        patience=5, verbose=1)
TensorBoard logに出力

トレーニングの際にTensorboard logに出力することで、トレーニング後に色々なことを確認できるので、使いました。

from datetime import datetime
logdir="/tf/alpha-trader/source/logs/fit/" + datetime.today().strftime("%Y%m%d-%H%M%S") 
callback_tensorboard = TensorBoard(log_dir=logdir, histogram_freq=0, write_graph=False)	
Learning Rateの自動調整

トレーニングしている時にValidationのパフォーマンスが改善されない際にLearning Rateを自動的に調整するための設定です。 下記の設定では、patience=0なので今回のepochの評価にパフォーマンスが改善しなければすぐに次のepochにLearning Rateが10倍(factor=0.1)減って、 トレーニングを行うことになります。また、Learning rateは1e-5を最小値として設定してありますので、この値よりは小さくなりません。

callback_reduce_lr = ReduceLROnPlateau(monitor='val_loss',
                                       factor=0.1,
                                       min_lr=1e-5,
                                       patience=0,
                                       verbose=1)

学習

これまで作成したTCNモデルにトレーニングデータで30epochsくらい学習させましょう。念のため、Epochごとに精度の改善がなければ学習を終了するように設定しました。

%%time
model.fit(x_train_scaled, 
          y_train_scaled, 
          epochs=30, 
          validation_split = 0.1,
          shuffle=True,
          callbacks=callbacks)
Train on 166067 samples, validate on 18452 samples
Epoch 1/30
165952/166067 [============================>.] - ETA: 0s - loss: 0.0035 - mean_absolute_error: 0.0430
Epoch 00001: val_loss improved from inf to 0.00035, saving model to alpha_trader_checkpoint.keras
166067/166067 [==============================] - 80s 481us/sample - loss: 0.0035 - mean_absolute_error: 0.0430 - val_loss: 3.4900e-04 - val_mean_absolute_error: 0.0137
Epoch 2/30
166048/166067 [============================>.] - ETA: 0s - loss: 4.4827e-04 - mean_absolute_error: 0.0166
Epoch 00002: val_loss improved from 0.00035 to 0.00032, saving model to alpha_trader_checkpoint.keras

Epoch 00002: ReduceLROnPlateau reducing learning rate to 1e-05.
166067/166067 [==============================] - 77s 465us/sample - loss: 4.4827e-04 - mean_absolute_error: 0.0166 - val_loss: 3.1790e-04 - val_mean_absolute_error: 0.0169
Epoch 3/30
166048/166067 [============================>.] - ETA: 0s - loss: 2.9954e-05 - mean_absolute_error: 0.0026
Epoch 00003: val_loss improved from 0.00032 to 0.00013, saving model to alpha_trader_checkpoint.keras
166067/166067 [==============================] - 82s 495us/sample - loss: 2.9951e-05 - mean_absolute_error: 0.0026 - val_loss: 1.2694e-04 - val_mean_absolute_error: 0.0079
Epoch 4/30
...
Epoch 27/30
166016/166067 [============================>.] - ETA: 0s - loss: 5.5239e-06 - mean_absolute_error: 0.0017
Epoch 00027: val_loss did not improve from 0.00005
166067/166067 [==============================] - 78s 467us/sample - loss: 5.5232e-06 - mean_absolute_error: 0.0017 - val_loss: 5.3255e-05 - val_mean_absolute_error: 0.0050
Epoch 28/30
165984/166067 [============================>.] - ETA: 0s - loss: 5.5814e-06 - mean_absolute_error: 0.0017
Epoch 00028: val_loss did not improve from 0.00005
166067/166067 [==============================] - 76s 459us/sample - loss: 5.5801e-06 - mean_absolute_error: 0.0017 - val_loss: 5.1092e-05 - val_mean_absolute_error: 0.0050
Epoch 00028: early stopping
CPU times: user 39min 16s, sys: 1min 59s, total: 41min 15s
Wall time: 36min 6s

23 epochまで学習しましたね。それ以上、精度が改善されなかったので28 epoch目に学習が強制終了されました。良さそうです。

評価

最後に保存したモデルがベストモデルなので、それをロードして評価を行いましょう。

try:
    model.load_weights(path_checkpoint)
except Exception as error:
    print("Error trying to load checkpoint.")
    print(error)

 

result = model.evaluate(x_test_scaled, y_test_scaled)

20047/20047 [==============================] - 6s 298us/sample - loss: 3.0293e-05 - mean_absolute_error: 0.0042

  評価関数を実装

評価するため、実際のMidプライスと予測プライスの乖離はどのくらいあるか可視化する関数を実装しておきましょう。

import seaborn as sns
def plot_comparison(y_train, y_test, y_scaler_min, y_scaler_max, lookback_window, start_idx, length=100, train=True):
    """
    Plot the predicted and true output-signals.
    
    :param start_idx: Start-index for the time-series.
    :param length: Sequence-length to process and plot.
    :param train: Boolean whether to use training- or test-set.
    """
    
    if train:
        # Use training-data.
        x = x_train_scaled
        y_true = y_train
    else:
        # Use test-data.
        x = x_test_scaled
        y_true = y_test
    
    # End-index for the sequences.
    end_idx = start_idx + length
    
    # Select the sequences from the given start-index and
    # of the given length.
    x = x[start_idx:end_idx]
    y_true = y_true[lookback_window+ start_idx:lookback_window+end_idx]
    
    # Input-signals for the model.
   # x = np.expand_dims(x, axis=0)

    # Use the model to predict the output-signals.
    y_pred = model.predict(x)
    
    # The output of the model is between 0 and 1.
    # Do an inverse map to get it back to the scale
    # of the original data-set.
    #y_pred_rescaled = y_scaler.inverse_transform(y_pred[0])
    y_pred_rescaled = y_pred*(y_scaler_max - y_scaler_min) + y_scaler_min
    
    # For each output-signal.
    for signal in range(len(target_names)):
        # Get the output-signal predicted by the model.
        signal_pred = y_pred_rescaled[:, signal]
        
        # Get the true output-signal from the data-set.
        #signal_true = y_true[:, signal]
        signal_true = y_true

        # Make the plotting-canvas bigger.
        plt.figure(figsize=(20,8))
        
        # Plot and compare the two signals.
        plt.plot(signal_true, label='true')
        plt.plot(signal_pred, label='pred')
        
        # Plot grey box for warmup-period.
        p = plt.axvspan(0, warmup_steps, facecolor='black', alpha=0.15)
        
        # Plot labels etc.
        plt.ylabel(target_names[signal])
        plt.legend()
        plt.show()
        
        # Some more benchmark
        diff = signal_pred - signal_true
        fig, ax = plt.subplots(figsize=(20, 8))
        sns.distplot(diff, kde=True, rug=True)
        ax.set_xlim(-0.25, 0.25)
        plt.title('Distribution of differences between actual and prediction ')
        plt.show()

トレーニングセットで評価してみる

y_train_mid_min = np.min(df_train)["mid"]
y_train_mid_max = np.max(df_train)["mid"]

plot_comparison(y_train, y_test, y_train_mid_min, y_train_mid_max, lookback_window, start_idx=0, length=100000, train=True)

見事、トレーニングデータで評価結果は良さそうですね。Histogramから見ると予測値と実際値の乖離がありますが、基本的に0周辺に集中されていますね。

テストデータで評価してみる

次はテストデータで予測結果はどうなるか確認しましょう。

plot_comparison(y_train, y_test, y_train_mid_min, y_train_mid_max, lookback_window, start_idx=0, length=20000, train=False)

うん、テストデータセットの予測と実際の数値との乖離はトレーニングデータセットの評価結果より大きですね。差分のHistogramは0周りに分布されていることも見えますが。とは言え、プライスのトレンドの再現性は良さそうですね。

所感

今回は時系列データを対応可能なTCNでFX予測をしてみました。検証結果は、プライスの予測はまだイマイチですが上下トレンドの予測は良さそうですね。今後、特徴量の見直しやHyper Parameterのチューニングをして予測精度を改善していきたいと思います。ではまた!

最後に


次世代システム研究室では、ビッグデータ解析プラットホームの設計・開発を行うアーキテクトとデータサイエンティストを募集しています。次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。




皆さんのご応募をお待ちしています。