回心誌

日々是回心

"Wavelet Denoised-ResNet CNN and LightGBM Method to Predict Forex Rate of Change"を追試する。その2

前回の続き。
"Wavelet Denoised-ResNet CNN and LightGBM Method to Predict Forex Rate of Change"を追試する。その1 - 回心誌


とりあえず、動くようにはなったので、今度はもっと行数を増やして再度テスト。

行数を増やして再度テスト

10万行にして、ハイパーパラメータをNotebookの元々の値に直して再度実施。
データ全行を使うとカーネルが落ちてしまったので10万行にした。
2004/01/01から2005/05/03までのデータとなる。
ResNetの学習にとにかく時間がかかって、10時間くらいはかかった。その間、GPU使用率はほぼ100%。
論文だとバッチサイズは128となっているが、ノートだと32。
32でもVRAMを半分以上使っているので、128は無理だ。

ノートブックの処理をそのまま実行すると
MAE 0.394327*10-3 MSE: 0.000303*10-3 RMSE: 0.550503*10-3 MAPE: 181.215688% ACC: 0.739244
になった。
ただ、いくつか怪しい点がある。

この論文はここが怪しい

怪しい点1:label4について

まず、予測の対象であるlabel4という列の生成について。

以下のようになっているが、1つ前の行の終値(data['Close'][i-1])から4つ先の行の終値(data['Close'][i+4])への変化率を求めているが、なぜ1つ前の行を参照するのか分からない。

# Label
data['label4'] = ''

for i in range(1,data.shape[0]-4):
    data['label4'][i]=(data['Close'][i+4]-data['Close'][i-1])/data['Close'][i-1]

# In case that the label is too small to affect the prediction, multiply by 1000 and it will be processed in the final result
data["label4"]=data["label4"]*1000
data=data[0:-4]

論文では以下のようになっており、対象行の終値から5つ先の行の終値への変化率となっている。

5つ先でも4つ先でも未来に対しての予測であることは変わりないが、これが1つ先となると実装されたコードのほうでは予測にならない。意味が分からない。
訓練データを作成する箇所で平仄を合わせていれば問題がないが、そこまでは読み切れていない。

怪しい点2:学習データと検証データの分離について

この論文での提案手法は、
1)ウェーブレット変換でOHLCデータのノイズを除去する
2)ノイズ除去後のOHLCの値に対してテクニカル分析指標を追加する
3)1および2で作成したノイズ除去後のOHLC値およびテクニカル分析指標から終値の変化率を予測させるようにResNetを学習させる
4)3で学習させたResNetにデータ入力した際の、出力層の1個手前の層の値を用い(これをfeatureとし)featureをLightGBMに入力して再度終値の変化率を予測させるようLightGBMを学習させる
という形で、1,2でデータの前処理、3,4がResNetとLightGBMを用いたモデルの構築、となっている。
1,2のデータ前処理は「DataProcess.py」で、3,4は「Prediction.ipynb」で実装されている。

問題は、3,4での学習データと検証データの取り扱いで、ノートブックのコードを見ると、3から4でfeatureを渡す際、学習データと検証データを分離せず、データ全量を渡しているように見える。その上で、4では学習データと検証データを9:1で分離している。

つまり、ResNetに対しては学習データと検証データが混同している。このため、モデル全体としては正しく分離できていないことになる。
MAE 0.394327*10-3 MSE: 0.303*10-6 RMSE: 0.550503*10-3 MAPE: 181.215688% ACC: 0.739244
という結果も、分離できていない状態での結果になるため、信頼できない。
論文ではMAEが0.241*10-3、MSEが0.156*10-6、RMSEが0.395*10-3となっているが、これは検証に用いたデータ期間が異なるためである可能性もある。

怪しい点3:データ期間

論文ではデータの期間を2019-01-01から2020-06-10と説明している。
しかし、githubリポジトリのデータを見ると、2018-01-18までのデータしか含まれていない。
丁度論文で検証に使ったデータ期間が抜け落ちているのはちょっと怪しい。

データ内容を確認したが、データ自体に誤りは無さそうに見える。
dukascopyから取得したUSD/JPY Bid 5分足のデータと2018-01-01から2018-01-18の期間のものを比較したところ、時差のずれはあった。
dukascopyはUTC+0に対して、リポジトリのデータはUTC+2で取得されたもののようだ。
行数や値は一致した。

怪しい点4:ノイズ除去の結果未来の値が過去の値に影響していないか

データ前処理としてノイズ除去やインディケーターの追加を行っている。
ノイズ除去ではウェーブレット変換という処理が使われている。

ウェーブレット変換についてはまだ勉強中でよくわかっていないが、ノイズ除去で未来のデータが入り込むことは考えられる。

この論文で言及されている別の論文では、未来のデータが入り込むことをどのように防止するかも書かれている。
(PDF) Combining the real-time wavelet denoising and long-short-term-memory neural network for predicting stock indexes

To avoid involving the future data, a sliding window mechanism is employed in the proposed real-time wavelet denoising as shown in Fig.2. The denoising is conducted in a window based on the multiresolution decomposition. The window moves forward one unit each time until all data points have been covered. This mechanism instead of the one-time wavelet denoising ensures that the denoising process is realtime excluding the future data, which is practical in the real investment scenario.

一方で、今回取り上げている論文のコードを見ると、前処理としてデータ全量に対して変換をかけており、ウィンドウを使っているようには見えない。

怪しい点を修正

ラベルの作成方法を修正

for i in range(1,data.shape[0]-4):
    data['label4'][i]=(data['Close'][i+4]-data['Close'][i-1])/data['Close'][i-1]

上のようになっていたところを、下のコードに直した。

    labelname = f'label{num}'
    # Label
    data[labelname] = ''
    
    shift_data = data.shift(-num)
    data[labelname] = (shift_data['Close'] - data['Close']) / data['Close']

forでなくshiftを使ったほうが処理が早い。

ウェーブレット変換で未来のデータを巻き込まないように修正

30本分のローソクデータが1回の学習や予測に使う入力となっている。
このデータを作る際、過去128本分のデータをウェーブレット変換でノイズ除去し、さらにそのうちの直近30本分に切り詰めて入力データを作成した。

学習データと検証データを分離

学習データと検証データを明確に分離し、ResNetとLightGBMいずれでも使い分けた。
ResNetとLightGBMどちらでも学習では同じ学習データ、検証では同じ検証データを用いた。

実行結果

うーん、、、、かなり微妙。
まず、予測結果として常に一定の値を出すように学習してしまうケースに何度か遭遇した。
局所解に陥ってしまっていると言えそう。

そのような局所解に陥らない場合でも、labelのバラつき(標準偏差)よりRMSEが大きく、精度が良いとは言えない結果になった。