みなさんこんにちは! この記事では、ニューラルネットワークを使用して時系列を予測する際の基本的なパイプライン、この場合、おそらく分析が最も困難な時系列-性質がランダムで一見予測できない金融データについてお話したいと思います。 それともまだないですか?
エントリー
現在、私はベローナ大学で応用数学の学位を取得し、修士課程の最終年にいます。CISの典型的なIT学生として、キエフ工科大学で学士号を取得し、その後、さまざまなプロジェクトで勢いを得ている機械学習を応用しました今やっています。 大学では、私の研究のテーマは時系列、特に金融に関する深層学習です。
この記事の目的は、データ処理からニューラルネットワークの構築および結果の検証まで、時系列で作業するプロセスを示すことです。 例として、金融シリーズは完全にランダムに選択されており、通常のニューラルネットワークアーキテクチャが金融商品の振る舞いを予測するために必要なパターンをキャッチできるかどうかは一般的に興味深いです。
この記事で説明するパイプラインは、他のデータや分類アルゴリズムに簡単に適用できます。 コードをすぐに実行したい場合は、 IPython Notebookをダウンロードできます。
データ準備
たとえば、2005年から現在までのAppleのような控えめな会社の株価を考えてみましょう。 Yahoo Financeで.csv形式でダウンロードできます。 データをアップロードして、この美しさがどのように見えるかを見てみましょう。
まず、ロードする必要があるライブラリをインポートします。
import matplotlib.pylab as plt import numpy as np import pandas as pd
データを読み取り、グラフを描画します(Yahoo Financeの.csvで、データは逆順でダウンロードされます-2017年から2005年まで、最初に[::-1]を使用して「フリップ」する必要があります)。
data = pd.read_csv('./data/AAPL.csv')[::-1] close_price = data.ix[:, 'Adj Close'].tolist() plt.plot(close_price) plt.show()
これは典型的なランダムプロセスのように見えますが、1日または数日先の予測の問題を解決しようとします。 「予測」のタスクは、最初に機械学習のタスクにより近く説明する必要があります。 市場での株価の動きを単純に予測することができます-多かれ少なかれ-これはバイナリ分類のタスクになります。 一方、翌日(または数日間)の価格の値、または最終日と比較した翌日の価格の変化、またはこの差の対数のいずれかを予測することができます-つまり、数であるタスクを予測したいのです回帰。 しかし、回帰問題を解決する際には、データの正規化の問題に対処する必要があります。これについては、次に検討します。
分類の場合、回帰の場合、時系列のウィンドウを入力(たとえば、30日)として、翌日の価格の動き(分類)または変化の値(回帰)を予測しようとします。
財務時系列の主な問題は、それらが少しでも静止していないことです(たとえば、 Dickey-Fullerテストを使用して自分で確認できます)。つまり、マットとしての特性です。 ウィンドウの期待値、分散、平均最大値および最小値は時間とともに変化します。つまり、ウィンドウに30日間ある場合、ウィンドウのMinMaxまたはzスコアの正規化にこれらの値を使用することはできません。いくつかの特徴がありますが、翌日またはウィンドウの中央で変更される場合があります。
しかし、分類問題を注意深く見ると、マットにはあまり興味がありません。 翌日に期待または分散、私たちは上下に移動することにのみ興味があります。 したがって、チャンスをつかみ、30日間のウィンドウをzスコアで正規化しますが、「将来」の影響は受けずに、それらのみを正規化します。
X = [(np.array(x) - np.mean(x)) / np.std(x) for x in X]
回帰タスクの場合、これはすでに実行できません。平均を減算して偏差で割ると、この値を翌日に価格値に戻す必要があり、これらのパラメーターはすでに完全に異なる可能性があるためです。 したがって、次の2つのオプションを試してみます。生データでトレーニングし、翌日に価格の割合を変更してシステムをだまそうとする-パンダがこれを支援します。
close_price_diffs = close.price.pct_change()
これは次のように見えます。また、ご覧のように、統計特性を操作せずに取得したこのデータは、すでに-0.5〜0.5の範囲にあります。
トレーニングとトレーニングサンプルに分離するために、トレーニングに間に合うように最初の85%のウィンドウを使用し、最後の15%をニューラルネットワークの動作のチェックに使用します。
したがって、ニューラルネットワークをトレーニングするには、次のX、Yペアを取得します。30日間の市場終値の価格と、バイナリ分類の価格値が増加したか減少したかに応じて、[1、0]または[0、1]。 30日間の価格の変化率と回帰の翌日の変化。
ニューラルネットワークアーキテクチャ
基本モデルとして、多層パーセプトロンを使用します。 ニューラルネットワークの基本的な概念に慣れていない場合は、 ここから始めるのが最適です 。
Kerasは実装フレームワークとして採用されています。非常にシンプルで直感的で、非常に複雑な計算グラフをひざの上に実装できますが、現時点では必要ありません。 単純なグリッドを実現します-30個のニューロン(ウィンドウの長さ)を持つ入力層、64個のニューロンを持つ最初の隠れ層、 BatchNormalization-それをほぼすべての多層ネットワークに使用することをお勧めします。 LeakyReLUのようなファッショナブル)。 出力では、1つのニューロン(または分類用に2つ)を配置します。タスク(分類または回帰)に応じて、出力にソフトマックスがあるか、値を予測できるように非線形のままにします。
分類コードは次のようになります。
model = Sequential() model.add(Dense(64, input_dim=30)) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(2)) model.add(Activation('softmax'))
最後の回帰タスクの場合、アクティベーションパラメーターは「線形」である必要があります。 次に、エラー関数と最適化アルゴリズムを決定する必要があります。 勾配降下変動の詳細に入らずに、Adamのステップ長を0.001にします。 分類の損失パラメータはクロスエントロピー-「categorical_crossentropy」に設定する必要があり、回帰の場合は二乗平均誤差-「mse」に設定する必要があります。 Kerasでは、トレーニングプロセスを非常に柔軟に制御することもできます。たとえば、勾配降下ステップの値を減らすことをお勧めします。結果が改善されない場合、これはモデルトレーニングへのコールバックとして追加したReduceLROnPlateauの機能です。
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.9, patience=5, min_lr=0.000001, verbose=1) model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
ニューラルネットワークトレーニング
history = model.fit(X_train, Y_train, nb_epoch = 50, batch_size = 128, verbose=1, validation_data=(X_test, Y_test), shuffle=True, callbacks=[reduce_lr])
トレーニングプロセスが完了したら、エラーと精度の値のダイナミクスグラフを表示すると便利です。
plt.figure() plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) plt.title('model loss') plt.ylabel('loss') plt.xlabel('epoch') plt.legend(['train', 'test'], loc='best') plt.show() plt.figure() plt.plot(history.history['acc']) plt.plot(history.history['val_acc']) plt.title('model accuracy') plt.ylabel('acc') plt.xlabel('epoch') plt.legend(['train', 'test'], loc='best') plt.show()
トレーニングを開始する前に、重要なポイントに注意を払いたいと思います。そのようなデータのアルゴリズムを少なくとも50〜100時代より長く学習する必要があります。 これは、たとえば5〜10エポックでトレーニングして55%の精度を確認した場合、パターンを見つけることを学んだという意味ではない可能性が高いためです。トレーニングデータを分析すると、55%ウィンドウは1つのパターン用(増加など)で、残りの45%は別のパターン用(減少)です。 私たちの場合、ウィンドウの53%が「下降」クラス、47%が「上昇」クラスであるため、53%より高い精度を得ようとします。つまり、標識を見つけることを学んだことを意味します。
終値や単純なアルゴリズムなどの生データの精度が高すぎると、トレーニングサンプルの準備において再トレーニングまたは「先読み」を意味する可能性が高くなります。
分類タスク
最初のモデルをトレーニングして、グラフを見てみましょう。
ご覧のとおり、エラーは、テストサンプルの精度が常にプラスまたはマイナス1の値のままであり、トレーニングサンプルのエラーが減少し、精度が向上することです。これにより、再トレーニングがわかります。 2つのレイヤーでより深いモデルを取得してみましょう。
model = Sequential() model.add(Dense(64, input_dim=30)) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(16)) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(2)) model.add(Activation('softmax'))
彼女の仕事の結果は次のとおりです。
ほぼ同じ写真。 再訓練の効果に出くわしたら、モデルに正則化を追加する必要があります。 つまり、再トレーニング中に、トレーニングデータを単に「記憶」し、知識を新しいデータに一般化できないモデルを構築します。 正則化のプロセスでは、ニューラルネットワークの重みに特定の制限を課すため、値に大きな広がりはなく、多数のパラメーター(つまり、ネットワークの重み)があるにもかかわらず、単純化のためにそれらの一部をゼロにする必要があります。 最も一般的な方法から始めます-エラー関数に重みの合計のL2ノルムを持つ追加の項を追加します。Kerasでは、これはkeras.regularizers.activity_regularizerを使用して行われます。
model = Sequential() model.add(Dense(64, input_dim=30, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(16, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(2)) model.add(Activation('softmax'))
このようなニューラルネットワークは、エラー関数の点ですでに少し良く学習していますが、精度は依然として低下しています。
高ノイズまたはランダムな性質のデータを処理する場合、エラーの減少というような奇妙な効果がありますが、精度の低下ではありません-これは、誤差がクロスエントロピー値に基づいて計算されるためです。エラーが変更された場合でも、不正解のままになる場合があります。
したがって、近年人気のあるドロップアウト手法を使用して、モデルにさらに正則化を追加する価値があります。大まかに言えば、これは、ニューロンの同時適応を回避するために、学習プロセスでいくつかの重みをランダムに「無視する」ことです(同じ符号を学習しないように)。 コードは次のとおりです。
model = Sequential() model.add(Dense(64, input_dim=30, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dropout(0.5)) model.add(Dense(16, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(2)) model.add(Activation('softmax'))
ご覧のとおり、2つの隠されたレイヤーの間で、トレーニング中に重みを50%の確率で接続を「ドロップ」します。 通常、入力レイヤーと最初の非表示レイヤーの間にドロップアウトは追加されません。この場合、ノイズの多いデータから学習するためであり、出力の直前にも追加されません。 もちろん、ネットワークテスト中にドロップアウトは発生しません。 このようなグリッドはどのように学習しますか:
ご覧のとおり、エラーと精度のグラフは適切です。少し早くネットワークのトレーニングを停止すると、価格変動予測の精度の58%を取得できます。これは、ランダムな占いよりも確実に優れています。
金融時系列を予測する上でのもう1つの興味深い直観的なポイントは、翌日の変動が本質的にランダムであるということですが、チャートとローソクを見ると、今後5〜10日間の傾向に気付くことができます。 ニューロンがこのタスクに対処できるかどうかを確認しましょう-成功した最後のアーキテクチャで5日間の価格変動を予測し、興味のために、より多くの時代にトレーニングします。
ご覧のとおり、トレーニングを十分早期に停止すると(時間の経過とともにオーバーフィットが発生します)、 60%の精度を得ることができます。これは非常に良いことです。
回帰タスク
回帰タスクでは、最後に成功した分類のアーキテクチャ(必要な属性を学習できることが既に示されています)を使用して、ドロップアウトを削除し、より多くの反復でトレーニングします。
また、この場合、エラーの値だけでなく、次のコードを使用して予測の品質を視覚的に評価することもできます。
pred = model.predict(np.array(X_test)) original = Y_test predicted = pred plt.plot(original, color='black', label = 'Original data') plt.plot(predicted, color='blue', label = 'Predicted data') plt.legend(loc='best') plt.title('Actual and predicted') plt.show()
ネットワークアーキテクチャは次のようになります。
model = Sequential() model.add(Dense(64, input_dim=30, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(16, activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Dense(1)) model.add(Activation('linear'))
「生」調整に近いトレーニングを行うとどうなるか見てみましょう。
遠くからは良く見えますが、よく見ると、ニューラルネットワークの予測が遅れていることがわかります。これは失敗と見なすことができます。
価格の変更についてトレーニングすると、次の結果が得られます。
一部の値は適切に予測され、一部の場所では傾向が正しく推測されますが、一般的にはまあまあです。
議論
原則として、一見したところ、結果は一般的に印象的ではありません。 それは事実ですが、多くの前処理を行わずに、1次元データで最も単純な形式のニューラルネットワークをトレーニングしました。 精度を60〜70%のレベルにするためのいくつかの手順があります。
- 高頻度データでトレーニング(1時間ごと、5分ごと)-より多くのデータ-より多くのパターン-より少ない再トレーニング
- シーケンスで動作するように設計されたより高度なニューラルネットワークアーキテクチャを使用する-畳み込みニューラルネットワーク、リカレントニューラルネットワーク
- 終値だけでなく、.csvのすべてのデータ(高値、安値、始値、終値、量)を使用します。つまり、あらゆる瞬間に、利用可能なすべての情報に注意を払ってください。
- ハイパーパラメーター(ウィンドウサイズ、隠れ層のニューロンの数、トレーニングステップ)を最適化するために、これらすべてのパラメーターはランダムに取得され、 ランダム検索を使用して、45日前に調べて、より小さなステップでより深いグリッドを学習する必要があることがわかります。
- タスクにより適した損失関数を使用します(たとえば、価格の変化を予測するために、間違った符号に対してニューラルを微調整できます。通常のMSEは数値の符号に対して不変です)
時系列予測を追求し、主な目標であるこのデータを取引に使用して収益性を高めることを無視しました。 これをオンラインウェビナーモードで示し、予測問題に畳み込みネットワークとリカレントネットワークを適用し、さらにこれらの予測を使用して戦略の収益性を確認したいと思います。 興味がある人は、5月5日18:00 UTC にハングアウトオンエアで待っています。
おわりに
この記事では、最も単純なニューラルネットワークアーキテクチャを使用して、市場価格の動きを予測しました。 このパイプラインは、あらゆる時系列に使用できます。主なことは、適切なデータ前処理を選択し、ネットワークアーキテクチャを決定し、アルゴリズムの品質を評価することです。 今回のケースでは、過去30日間の価格枠を使用して、5日間で60%の精度で傾向を予測できました。これは良い結果と考えられます。 価格の変化を定量的に予測すると、失敗が判明しました。このタスクでは、より深刻なツールと時系列の統計分析を使用することをお勧めします。 IPython Notebookで使用されているすべてのコードは、リンクから取得できます。