取引におけるニューラルネットワーク:ResNeXtモデルに基づくマルチタスク学習
はじめに
人工知能の急速な発展により、深層学習手法は金融分野を含むデータ分析への統合が活発に進んでいます。金融データは高次元性、異質性、時系列構造を特徴としており、従来の処理方法では対応が困難です。一方で、深層学習は複雑で非構造化されたデータの分析において高い効率を示しています。
現代の畳み込みアーキテクチャの中でも、「Aggregated Residual Transformations for Deep Neural Networks」で紹介されたResNeXtは特に注目されます。ResNeXtは局所的および大域的依存関係の両方を捉え、多次元データを効率的に処理できる一方で、グループ畳み込みによって計算コストを削減します。
深層学習を用いた金融分析における重要な分野のひとつが、マルチタスク学習(MTL)です。このアプローチにより、複数の関連タスクを同時に解決でき、モデルの精度と汎化性能を向上させることができます。従来の方法では各モデルが単一のタスクを担当していましたが、MTLではデータ表現を共有することで、市場の変動に対するモデルの堅牢性が高まり、学習プロセスが改善されます。この手法は特に、市場動向予測、リスク評価、資産評価などに価値があります。なぜなら、金融市場は動的であり、多数の要因に影響されるからです。
研究「Collaborative Optimization in Financial Data Mining Through Deep Learning and ResNeXt」では、ResNeXtアーキテクチャをマルチタスクモデルに統合するフレームワークが紹介されています。このソリューションにより、時系列データの処理、時空間パターンの特定、精度の高い予測生成が可能となります。ResNeXtのグループ畳み込みと残差ブロックは学習を加速し、重要な特徴の損失リスクを低減するため、金融分析において特に有用です。
さらに、提案手法の大きな利点のひとつは、生データから意味のある特徴を自動的に抽出できることです。従来の金融分析では大規模な特徴量設計が必要でしたが、深層ニューラルネットワークは重要なパターンを自律的に識別できます。これは、複数の情報源(市場指標、マクロ経済レポート、ニュース記事など)を含むマルチモーダルな金融データの分析において特に重要です。MTLの柔軟性により、タスクの重みや損失関数を動的に調整でき、市場変動への適応性が向上し、予測精度が改善されます。
ResNeXtアーキテクチャ
ResNeXtアーキテクチャは、モジュラー設計とグループ畳み込みに基づいています。その中心には残差接続を持つ畳み込みブロックがあり、以下の2つの原則に従っています。
- 出力特徴マップのサイズが同じ場合、ブロックは同一のハイパーパラメータ(幅やフィルターサイズ)を使用します。
- 特徴マップのサイズが縮小される場合、ブロックの幅は比例して増加します。
これらの原則を守ることで、モデル全体の計算量をほぼ一定に保つことができ、設計が簡略化されます。1つのテンプレートモジュールを定義するだけで、他のブロックは自動生成されるため、標準化やチューニングの容易化、構造解析の効率化が可能になります。
従来の人工ニューラルネットワークの標準的なニューロンは、入力の加重和を計算します。これは畳み込み層や全結合層の基本操作です。このプロセスは、分割、変換、集約の3段階に分けられます。ResNeXtでは、変換関数をより複雑に、場合によっては小規模なモデルとして設計する柔軟な方法を導入しています。これによりNetwork-in-Neuronの概念が生まれ、アーキテクチャの能力が新たな次元で拡張されます。これがカーディナリティです。幅や深さとは異なり、カーディナリティは各ブロック内で独立した複雑な変換をいくつおこなうかを決定します。実験では、深さや幅を増やすよりも、カーディナリティを増やす方が性能向上と計算効率のバランスが良いことが示されています。
すべてのResNeXtブロックは、統一されたボトルネック構造を持っています。構成は以下の通りです。
- 特徴次元を削減する1×1の畳み込み層(初期層)
- コアとなるデータ処理を行う3×3の畳み込み層(主要層)
- 元の次元に戻す1×1の畳み込み層(最終層)
この設計により、計算量を抑えつつ高い表現力を維持できます。また、残差接続により学習時の勾配を保持し、勾配消失を防ぐことができるため、深いネットワークでも安定した学習が可能です。
ResNeXtの大きな改良点のひとつはグループ畳み込みの利用です。入力データを複数の独立したグループに分割し、それぞれ別の畳み込みフィルターで処理した後、結果を集約します。これによりモデルパラメータが削減され、高いネットワークスループットを維持しつつ、計算効率を向上させます。精度の大幅な低下も抑えられます。
グループ数を変更する際に計算量を安定させるため、ResNeXtではボトルネックブロックの幅を調整し、内部層のチャネル数を制御します。これにより、計算コストを過剰に増やすことなくスケーラブルなモデルを構築できます。
ResNeXtに基づくマルチタスク学習フレームワークは、金融データ処理における共有特徴の活用や協調的モデリング手法に対応した先進的アプローチです。構造は主に3つの要素から成り立っています。
- 特徴量抽出モジュール
- 共有学習モジュール
- タスク固有の出力レイヤー
このアプローチにより、効率的な深層学習の仕組みを金融時系列データに統合し、高精度な予測を実現しつつ、動的な市場状況にも適応できます。
特徴量抽出モジュールはResNeXtアーキテクチャに依存しており、金融データのローカル特性とグローバル特性の両方を効果的にキャプチャします。多次元財務データでは、モデル内のグループの数が重要なパラメータとなります。これは、詳細な特徴表現と計算コストとのバランスを考慮しています。ResNeXtにおける各グループ畳み込みは、チャネルグループごとに特定のパターンを識別し、それらを集約することで統一された特徴表現を生成します。
非線形変換層を通過した後、抽出された特徴は、後続のマルチタスク学習およびタスク固有の適応の基盤となります。共有学習モジュールでは、重み共有メカニズムを採用し、共通特徴をタスク固有の空間へ投影します。これにより、タスク間の干渉を回避しつつ、各タスクに対して個別の表現を生成することが可能となり、同時に高い特徴共有効率を実現します。さらに、タスク間の相関に基づいてタスクをクラスタリングすることで、共有学習メカニズムを一層強化しています。
タスク固有の出力層は、特殊化された特徴を最終的な予測空間へ写像する全結合パーセプトロンから構成されます。出力層は各タスクの性質に応じて設計されており、分類タスクではクロスエントロピー損失を使用し、回帰タスクでは平均二乗誤差(MSE)を用います。マルチタスク学習においては、最終的な損失関数を各タスク損失の重み付き和として定義します。
学習は複数段階で実施します。まず、タスク固有MLPの有効な収束を確保するため、各タスクに対して個別に事前学習をおこないます。その後、マルチタスクアーキテクチャにおいてファインチューニングをおこない、全体性能の向上を図ります。最適化には、動的な学習率調整を伴うAdamアルゴリズムを使用します
MQL5を使用した実装
ResNeXtベースのマルチタスク学習フレームワークに関する理論的側面を確認した後、本研究ではMQL5を用いてその解釈を実装します。まず、ResNeXtの基本構成要素であるボトルネックモジュールの構築から開始します。
ボトルネックモジュール
ボトルネックモジュールは、3つの畳み込み層から構成されており、それぞれが生データ処理において重要な役割を果たします。第1層では特徴次元を削減し、後続処理における計算量を低減します。
第2の畳み込み層は主要な畳み込み処理を担い、データの正確な解釈に必要な高次かつ複雑な特徴を抽出します。この層では要素間の相互依存関係を解析します。これにより、後続段階で重要となるパターンを識別することが可能となります。このアプローチにより、モデルは金融データに内在する非線形依存関係に適応でき、予測精度の向上が期待されます。
最終層ではテンソルの元の次元を復元し、重要な情報をすべて保持します。先行する特徴抽出段階で時間軸方向の次元削減が行われる一方で、特徴空間を拡張することでその影響を補償しており、これはResNeXtの設計原則と整合しています。
学習の安定性を向上させるため、各畳み込み層の後にはバッチ正規化を適用します。これにより内部共変量シフトが抑制され、収束が高速化されます。また、ReLU活性化関数を用いることでモデルの非線形性を強化し、複雑な依存関係の表現能力および汎化性能を向上させます。
上記のアーキテクチャは、以下の構造を持つCNeuronResNeXtBottleneckオブジェクトとして実装されています。
class CNeuronResNeXtBottleneck : public CNeuronConvOCL { protected: CNeuronConvOCL cProjectionIn; CNeuronBatchNormOCL cNormalizeIn; CNeuronTransposeRCDOCL cTransposeIn; CNeuronConvOCL cFeatureExtraction; CNeuronBatchNormOCL cNormalizeFeature; CNeuronTransposeRCDOCL cTransposeOut; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronResNeXtBottleneck(void){}; ~CNeuronResNeXtBottleneck(void){}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint window, uint step, uint units_count, uint group_size, uint groups, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronResNeXtBottleneck; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual CLayerDescription* GetLayerInfo(void) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; };
親クラスとしては、特徴空間を復元する機能を持つ畳み込み層オブジェクトを使用します。加えて、本構造には複数の内部オブジェクトが含まれており、それぞれが構築中のアルゴリズムにおいて重要な役割を担います。これらの機能については、新しいクラスのメソッドを実装する過程で順次詳述します。
すべての内部オブジェクトは静的として宣言されているため、コンストラクタとデストラクタを空のままにすることができます。これらの宣言済みおよび継承されたオブジェクトの初期化は、Initメソッド内でおこなわれます。このメソッドは、生成されるモジュールのアーキテクチャを一意に定義する定数の集合を受け取ります。
bool CNeuronResNeXtBottleneck::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint window, uint step, uint units_count, uint group_size, uint groups, ENUM_OPTIMIZATION optimization_type, uint batch) { int units_out = ((int)units_count - (int)window + (int)step - 1) / (int)step + 1; if(!CNeuronConvOCL::Init(numOutputs, myIndex, open_cl, group_size * groups, group_size * groups, chanels_out, units_out, 1, optimization_type, batch)) return false;
メソッド本体では、通常、同名の親クラスメソッドを呼び出します。このメソッドには、継承されたオブジェクトおよびインターフェースの初期化アルゴリズムが含まれています。しかし本ケースでは、親クラスはモジュールの最終畳み込み層として機能します。その入力には特徴抽出後のデータが渡され、この過程で処理対象テンソルの次元が変化している可能性があります。そのため、まずモジュール出力における系列長を決定し、その後に親クラスメソッドを呼び出します。
継承オブジェクトの初期化が正常に完了した後、新たに宣言したオブジェクトの初期化処理に進みます。最初にデータ投影ブロックの処理をおこないます。第1の畳み込み層では、生データを所定数の作業グループに対応する投影へと変換します。
//--- Projection In uint index = 0; if(!cProjectionIn.Init(0, index, OpenCL, chanels_in, chanels_in, group_size * groups, units_count, 1, optimization, iBatch)) return false; index++; if(!cNormalizeIn.Init(0, index, OpenCL, cProjectionIn.Neurons(), iBatch, optimization)) return false; cNormalizeIn.SetActivationFunction(LReLU);
これらの投影は、その後、正規化処理が施され、LReLU関数によって活性化されます。
なお、これらの処理結果は、[Time, Group, Dimension]という3次元テンソルとなります。各グループを独立して処理できるようにするため、3次元テンソル転置オブジェクトを用いて、グループ識別子に対応する次元を先頭の位置へ移動します。
index++; if(!cTransposeIn.Init(0, index, OpenCL, units_count, groups, group_size, optimization, iBatch)) return false; cTransposeIn.SetActivationFunction((ENUM_ACTIVATION)cNormalizeIn.Activation());
次に特徴抽出ブロックを構築します。ここでは、独立した系列数をグループ数として指定した畳み込み層を使用します。これにより、各グループの値が相互に「混合」されることを防止できます。各グループは、それぞれ独立した学習可能パラメータ行列を使用します。
//--- Feature Extraction index++; if(!cFeatureExtraction.Init(0, index, OpenCL, group_size * window, group_size * step, group_size, units_out, groups, optimization, iBatch)) return false;
また、本メソッドは畳み込みウィンドウサイズおよび時間軸方向のステップ幅に関するパラメータを受け取ります。これらのパラメータを内部の畳み込み層初期化処理へ渡す際には、対応する値にグループサイズを乗算します。
畳み込み層の後には、LReLU活性化関数を伴うバッチ正規化を追加します。
index++; if(!cNormalizeFeature.Init(0, index, OpenCL, cFeatureExtraction.Neurons(), iBatch, optimization)) return false; cNormalizeFeature.SetActivationFunction(LReLU);
最終的な特徴空間への逆投影ブロックは、グループを単一の系列へ統合するための3次元テンソル転置オブジェクトのみで構成されます。実際のデータ投影処理は、前述のとおり、親クラスに継承されたメソッドを用いて実行されます。
//--- Projection Out index++; if(!cTransposeOut.Init(0, index, OpenCL, groups, units_out, group_size, optimization, iBatch)) return false; cTransposeOut.SetActivationFunction((ENUM_ACTIVATION)cNormalizeFeature.Activation()); //--- return true; }
ここでは、処理結果の論理値を呼び出し元に返し、オブジェクトの初期化メソッドを終了します。
次のステップとして、feedForwardメソッド内に実装される順伝播アルゴリズムを構築します。
bool CNeuronResNeXtBottleneck::feedForward(CNeuronBaseOCL *NeuronOCL) { //--- Projection In if(!cProjectionIn.FeedForward(NeuronOCL)) return false;
このメソッドは、生データオブジェクトへのポインタを受け取り、これをデータ投影ブロック内の最初の内部畳み込み層に定義された同名メソッドへ即座に渡します。このポインタの妥当性チェックはおこないませんが、内部層において既に検証処理が実装されているため、追加のチェックは不要です。
投影結果は正規化され、その後、各グループごとの表現へ転置されます。
if(!cNormalizeIn.FeedForward(cProjectionIn.AsObject())) return false; if(!cTransposeIn.FeedForward(cNormalizeIn.AsObject())) return false;
特徴抽出ブロックでは、グループ畳み込み処理が実行され、その出力は正規化されます。
//--- Feature Extraction if(!cFeatureExtraction.FeedForward(cTransposeIn.AsObject())) return false; if(!cNormalizeFeature.FeedForward(cFeatureExtraction.AsObject())) return false;
各グループから抽出された特徴は、再び単一の多次元系列へ転置され、親クラスを用いて所定の特徴空間へ投影されます。
//--- Projection Out if(!cTransposeOut.FeedForward(cNormalizeFeature.AsObject())) return false; return CNeuronConvOCL::feedForward(cTransposeOut.AsObject()); }
これらの操作の論理結果を呼び出し元プログラムに返して、メソッドを終了します。
ご覧のとおり、順伝播アルゴリズムは線形構造となっています。誤差勾配も同様に線形に伝播するため、バックプロパゲーションメソッドについては独立した学習対象として扱います。このオブジェクトおよびすべてのメソッドの完全なコードは、添付ファイルにて提供されています。
残差接続モジュール
ResNeXtアーキテクチャでは、各ボトルネックモジュールに残差接続を備えています。これにより、バックプロパゲーション時の誤差勾配の効率的な伝播が可能となります。残差接続は、以前に抽出された特徴を再利用できるようにするため、収束速度を向上させ、勾配消失のリスクを低減します。その結果、計算コストを大幅に増加させることなく、より深いネットワークの学習が可能となります。
ボトルネックモジュールの出力テンソルは、全体としてほぼ同じサイズを維持しますが、各次元は変化する場合があります。時間軸方向のステップ数の削減は、特徴空間の次元増加によって補償され、重要な情報を保持しつつ長期依存関係を捉えることができます。残差接続を正しく統合するためには、入力データを所定の次元にマッピングする専用の投影モジュールが必要です。これにより次元の不一致を防ぎ、深いアーキテクチャにおいても学習の安定性が確保されます。
本実装では、このモジュールをCNeuronResNeXtResidualオブジェクトとして作成しており、その構造は以下のとおりです。
class CNeuronResNeXtResidual: public CNeuronConvOCL { protected: CNeuronTransposeOCL cTransposeIn; CNeuronConvOCL cProjectionTime; CNeuronBatchNormOCL cNormalizeTime; CNeuronTransposeOCL cTransposeOut; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronResNeXtResidual(void){}; ~CNeuronResNeXtResidual(void){}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint units_in, uint units_out, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronResNeXtResidual; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual CLayerDescription* GetLayerInfo(void) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; };
本オブジェクトの開発にあたっては、ボトルネックモジュールの構築時に用いた手法と類似のアプローチを採用しています。ただし、入力テンソルの次元が異なるため、適宜調整をおこなっています。
提示されたオブジェクト構造には、いくつかのネストされた内部オブジェクトが含まれています。これらの機能については、新しいクラスのメソッドを実装する過程で順次説明します。内部オブジェクトはすべてstaticとして宣言されているため、クラスのコンストラクタおよびデストラクタは空のままとしています。継承オブジェクトを含むすべてのオブジェクトの初期化は、オブジェクトのアーキテクチャを定義する定数の集合を受け取るInitモジュール内で実施されます。
bool CNeuronResNeXtResidual::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint units_in, uint units_out, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronConvOCL::Init(numOutputs, myIndex, open_cl, chanels_in, chanels_in, chanels_out, units_out, 1, optimization_type, batch)) return false;
メソッド内では、まず同名の親クラスメソッドを呼び出し、必要なパラメータを渡します。ボトルネックモジュールと同様に、親クラスとして畳み込み層を使用しています。この親クラスは、新しい特徴空間へのデータ投影も担当します。
継承オブジェクトおよびインターフェースの初期化が正常に完了した後、新たに宣言したオブジェクトの処理に移ります。時間軸方向のデータ操作を容易にするため、まず入力データを転置します。
int index=0; if(!cTransposeIn.Init(0, index, OpenCL, units_in, chanels_in, optimization, iBatch)) return false;
次に、畳み込み層を用いて、各ユニット系列を指定された次元に投影します。
index++; if(!cProjectionTime.Init(0, index, OpenCL, units_in, units_in, units_out, chanels_in, 1, optimization, iBatch)) return false;
得られた結果は、ボトルネックモジュールと同様に正規化されます。ただし、残差モジュールでは情報を損なわずにすべて通す必要があるため、活性化関数は適用しません。
index++; if(!cNormalizeTime.Init(0, index, OpenCL, cProjectionTime.Neurons(), iBatch, optimization)) return false;
続いて特徴空間を調整します。このために逆転置をおこない、データの投影は親クラスで処理されます。
index++; if(!cTransposeOut.Init(0, index, OpenCL, chanels_in, units_out, optimization, iBatch)) return false; //--- return true; }
ここまでで、処理の論理結果を呼び出し元に返し、新しいオブジェクトの初期化メソッドを完了します。
次のステップとして、feedForwardメソッド内に実装される順伝播アルゴリズムを構築します。
bool CNeuronResNeXtResidual::feedForward(CNeuronBaseOCL *NeuronOCL) { //--- Projection Timeline if(!cTransposeIn.FeedForward(NeuronOCL)) return false;
このメソッドは、入力データを保持するオブジェクトへのポインタを受け取ります。このポインタは内部のデータ転置層に渡され、データがユニット系列に変換されます。
続いて、畳み込み層を用いてユニット系列の次元を目的のサイズに調整します。
if(!cProjectionTime.FeedForward(cTransposeIn.AsObject())) return false;
結果は正規化されます。
if(!cNormalizeTime.FeedForward(cProjectionTime.AsObject())) return false;
最後に逆転置が適用され、データが特徴空間へ投影されます。
//--- Projection Chanels if(!cTransposeOut.FeedForward(cNormalizeTime.AsObject())) return false; return CNeuronConvOCL::feedForward(cTransposeOut.AsObject()); }
最終的な投影は親クラスによって実行され、処理の論理結果が呼び出し元プログラムに返されることで、feedForwardメソッドが完了します。
順伝播アルゴリズムは線形であるため、バックプロパゲーション時も勾配は線形に伝播します。そのため、バックプロパゲーションメソッドについては、CNeuronResNeXtBottleneckオブジェクトと同様に独立した学習対象として扱います。これらのオブジェクトおよびそのすべてのモジュールの完全なコードは、添付ファイルにて提供されています。
ResNeXtブロック
ここまでで、ResNeXtフレームワークの2つの情報ストリームを表す個別オブジェクトを作成しました。ここからは、これらのオブジェクトを単一の構造体に統合し、より効率的にデータ処理をおこなえるようにします。そのために、CNeuronResNeXtBlockオブジェクトを作成し、これを後続のデータ処理における主要ブロックとして使用します。オブジェクト構造は以下のとおりです。
class CNeuronResNeXtBlock : public CNeuronBaseOCL { protected: uint iChanelsOut; CNeuronResNeXtBottleneck cBottleneck; CNeuronResNeXtResidual cResidual; CBufferFloat cBuffer; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronResNeXtBlock(void){}; ~CNeuronResNeXtBlock(void){}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint window, uint step, uint units_count, uint group_size, uint groups, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronResNeXtBlock; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual CLayerDescription* GetLayerInfo(void) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; };
この構造には、既知のオブジェクトと標準的な仮想メソッドが含まれており、必要に応じてオーバーライドします。
すべての内部オブジェクトは静的として宣言されているため、コンストラクタとデストラクタを空のままにすることができます。これらの宣言済みおよび継承されたオブジェクトの初期化は、Initメソッド内でおこなわれます。そのパラメータ構造は、CNeuronResNeXtBottleneckオブジェクトから完全に継承されています。
bool CNeuronResNeXtBlock::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint window, uint step, uint units_count, uint group_size, uint groups, ENUM_OPTIMIZATION optimization_type, uint batch) { int units_out = ((int)units_count - (int)window + (int)step - 1) / (int)step + 1; if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units_out * chanels_out, optimization_type, batch)) return false;
メソッド本体では、まずブロック出力時の系列次元を決定し、親オブジェクトから継承した基本インターフェースを初期化します。
親クラスの初期化が正常に完了した後、必要なパラメータをオブジェクト内変数に保存します。
iChanelsOut = chanels_out;
次に、前章で構築した情報ストリームの内部オブジェクトを初期化します。
int index = 0; if(!cBottleneck.Init(0, index, OpenCL, chanels_in, chanels_out, window, step, units_count, group_size, groups, optimization, iBatch)) return false; index++; if(!cResidual.Init(0, index, OpenCL, chanels_in, chanels_out, units_count, units_out, optimization, iBatch)) return false;
ブロック出力では、2つの情報ストリームの値の合計を受け取ることが想定されます。そのため、受け取った誤差勾配は両方のデータストリームに完全に伝播させることができます。不要なデータコピーを避けるため、対応するデータバッファへのポインタを入れ替えます。
if(!cResidual.SetGradient(cBottleneck.getGradient(), true)) return false; if(!SetGradient(cBottleneck.getGradient(), true)) return false; //--- return true; }
ここまでで、処理の論理結果を呼び出し元に返し、初期化メソッドを完了します。
次に、feedForwardメソッド内に実装される順伝播アルゴリズムを構築します。処理は非常にシンプルです。
bool CNeuronResNeXtBlock::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cBottleneck.FeedForward(NeuronOCL)) return false; if(!cResidual.FeedForward(NeuronOCL)) return false;
このメソッドは、入力データオブジェクトへのポインタを受け取り、これを2つの情報ストリームオブジェクトの各メソッドに渡します。出力は合計され、正規化されます。
if(!SumAndNormilize(cBottleneck.getOutput(), cResidual.getOutput(), Output, iChanelsOut, true, 0, 0, 0, 1)) return false; //--- result return true; }
処理の論理結果が呼び出し元プログラムに返されることで、feedForwardメソッドが完了します。
一見シンプルに見える構造ですが、実際には2つの情報ストリームを含んでいるため、誤差勾配の分配が複雑になります。この処理を担当するのがcalcInputGradientsメソッドです。
bool CNeuronResNeXtBlock::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
このメソッドは、順伝播で使用された入力データオブジェクトへのポインタを受け取ります。この場合、誤差勾配は入力データが最終モデル出力に与える影響に応じて伝播される必要があります。データは有効なオブジェクトにのみ渡すことができるため、操作を継続する前に、受け取ったポインタの有効性を確認します。
検証を通過した後、誤差勾配はまず最初の情報ストリームに伝播されます。
if(!NeuronOCL.calcHiddenGradients(cBottleneck.AsObject())) return false;
次に2番目のストリームに勾配を伝播する前に、先に取得したデータを保持する必要があります。データを完全にコピーする代わりに、ポインタ入れ替えの仕組みを使用します。入力データの誤差勾配バッファへのポインタをローカル変数に保存します。
CBufferFloat *temp = NeuronOCL.getGradient();
補助バッファのサイズが勾配バッファと一致するか確認し、必要に応じて調整します。
if(cBuffer.GetOpenCL() != OpenCL || cBuffer.Total() != temp.Total()) { if(!cBuffer.BufferInitLike(temp)) return false; }
ポインタを入力データオブジェクトに渡した後、
if(!NeuronOCL.SetGradient(GetPointer(cBuffer), false)) return false;
誤差勾配を安全に2番目の情報ストリームに伝播させることができます。
if(!NeuronOCL.calcHiddenGradients(cResidual.AsObject())) return false;
2つのストリームから得られた値を合計し、バッファポインタを元の状態に戻します。
if(!SumAndNormilize(temp, NeuronOCL.getGradient(), temp, 1, false, 0, 0, 0, 1)) return false; if(!NeuronOCL.SetGradient(temp, false)) return false; //--- return true; }
ここまでで、処理の論理結果を呼び出し元に返し、誤差勾配分配メソッドを完了します。
以上で、ResNeXtブロックオブジェクトのアルゴリズム的構築方法の概要は終了です。このオブジェクトおよびすべてのメソッドの完全なコードは、添付ファイルにて提供されています。
この記事はここで終了となりますが、作業はまだ完了していません。少し休憩して、次の記事に続きます。
結論
本記事では、金融データ処理に適したResNeXtアーキテクチャに基づくマルチタスク学習フレームワークを紹介しました。本フレームワークにより、高次元かつ時系列データ環境において、分類および回帰タスクを最適化しつつ、効率的な特徴抽出と処理が可能となります。
実践セクションでは、ResNeXtアーキテクチャの主要要素を構築しました。次回の記事では、マルチタスク学習フレームワークの構築を行い、実際の過去データを用いて実装手法の有効性を評価します。
参照文献
- Aggregated Residual Transformations for Deep Neural Networks
- Collaborative Optimization in Financial Data Mining Through Deep Learning and ResNeXt
- Other articles from this series
記事で使用されているプログラム
| # | 名前 | 種類 | 説明 |
|---|---|---|---|
| 1 | Research.mq5 | EA | 事例収集用EA |
| 2 | ResearchRealORL.mq5 | EA | Real-ORL法を用いたサンプル収集用EA |
| 3 | Study.mq5 | EA | モデル学習用EA |
| 4 | Test.mq5 | EA | モデルテスト用EA |
| 5 | Trajectory.mqh | クラスライブラリ | システム状態とモデルアーキテクチャ記述構造 |
| 6 | NeuroNet.mqh | クラスライブラリ | ニューラルネットワークを作成するためのクラスのライブラリ |
| 7 | NeuroNet.cl | ライブラリ | OpenCLプログラムコード |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/17142
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引におけるニューラルネットワーク:ResNeXtモデルに基づくマルチタスク学習(最終回)
初級から中級まで:構造体(I)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
市場シミュレーション(第7回):ソケット(I)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
私の経験では、本当に有益なものを共有できるトレーダーは何も共有しない。
そう、彼らは(1998年以来の私のように)、機能している戦略は配布後すぐに機能しなくなることを知っているのだ。
だから、フォーラムのプログラマーは個々の解決策を共有するのであって、動く(儲かる)戦略は一度も公開されたことがないのだ。あるいは売られたこともない。
そう、彼らは(1998年以来私がそうであるように)、うまくいった戦略は、それが広まるとすぐに通用しなくなることを知っているのだ。
だからこそ、フォーラムのプログラマーは個々の解決策を共有し、機能する(儲かる)戦略は公表されたことがないのだ。あるいは売られたこともない。
そして、国間の資金移動の必要性は、もはやカウントされていない?)
どうすればそのようなシステムになるのでしょうか?
取引ロボットは、プルバックで買えば必ず機能する。
私は翻訳を見た。
正直言って、原文を理解できるほど頭が良くなかった。
"一晩中独り言を言っていたのに、理解してもらえなかった!"(シュヴァネツキー
そう、彼らは(1998年以来私がそうであるように)、有効な戦略はいったん広まるとすぐに機能しなくなることを知っている。
これは流動性の限られた取引所に当てはまることで、FXには当てはまらない。
追伸:ミハイルのことを思い出した。彼はモスクワ取引所でヘッジのシステムを持っている。100ドルでは何もできない。
ここでは、誰もが100ポンドで、1日あたり10%の収益性を持つシステムを探している。だから、このような検索結果が出るのだ。