English Русский Español Português
preview
取引におけるニューラルネットワーク:高精度分類のための効果的な特徴量抽出(Mantis)

取引におけるニューラルネットワーク:高精度分類のための効果的な特徴量抽出(Mantis)

MetaTrader 5トレーディングシステム |
20 0
Dmitriy Gizlyk
Dmitriy Gizlyk

はじめに

ミリ秒単位の差や、ごくわずかな価格変動さえも重要となる世界では、トレーダーは将来の価格変動を予測できるだけでなく、チャート上に現れる最も複雑なパターンを正確に分類できるツールを求めています。このような状況において、論文「Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification」で提案された基盤モデルMantisは、時系列分析に新たな章を開きます。軽量な設計、よく考えられたアーキテクチャ、そして優れたキャリブレーションにより、Mantisは分類器をトレーディングシステムへ迅速に統合し、リスク管理のための信頼性の高い信頼度推定を得ることを可能にします。

著者らは、後続の値を予測するように学習された従来のアプローチは、しばしば動作が遅すぎると指摘しています。新しい戦略ごとに慎重なファインチューニングが必要であり、大きなボラティリティの急上昇によって、その性能が大きく低下する可能性があるためです。一方、近年の時系列予測向け基盤モデルは目覚ましい成果を上げていますが、それらは基本的に回帰を目的として設計されており、市場局面の分類を目的としたものではありません。まさにこの空白を埋めるため、著者らは対照学習による事前学習を備えたMantisフレームワークを導入しました。

その考え方はシンプルかつ洗練されています。時系列を一連のパッチに分割し、コンピュータビジョンにおけるVision Transformerと同様に、ハイブリッドアテンションを適用します。しかし、標準的なグローバルアテンション機構は計算量が系列長に対して二次的(O(n²))となるため、高頻度データではボトルネックになります。この影響を軽減するため、Mantisではローカルトークンとグローバルトークンを同時に使用します。ローカルトークンは小さなウィンドウに対する畳み込みとプーリングによって得られ、グローバルトークンはセグメント全体のトレンド情報を集約します。この組み合わせにより、系列長に対してほぼ線形のスケーラビリティを実現しながら、小さな変動と長期的なパターンの両方を捉える能力を維持します。

Mantisにおける対照学習による事前学習は、いわば秘密兵器です。同じ時系列セグメントに異なる変換を施したものを互いに近づける一方で、まったく異なる時系列同士を遠ざけます。その結果、異なる変化を含んでいても同じパターンを表す埋め込みは空間上で近くに集まり、異なるイベントは互いに離れていきます。この方法により、ピークや谷の振幅や時間がずれていたとしても、さまざまなパターンに共通する安定した特徴をモデルが学習できるようになります。

トレーダーにとって重要なのは、ポジションを開いても安全なのかを評価することです。単に「これはトレンド転換です」と言うだけでは十分ではありません。モデルがその結論にどれほど確信を持っているのかを理解する必要があります。Mantisは、この問題を組み込みの温度スケーリングモジュールによって解決します。このモジュールは出力ロジットを調整し、事後確率が、クラスへの所属の経験的頻度をより適切に反映するようにします。その結果、あるシグナルが「トレンド転換の確率80%」と表示された場合、トレーダーは、およそ5回に4回はそのシグナルが正しいと期待できます。

著者らは、多変量分析にも特に注意を払っています。アルゴリズム取引では、数十種類のインジケータ(移動平均、RSI、取引量、通貨ペア間の相関など)を同時に使用することがよくあります。すべてのチャネルを単一の高次元表現に単純に結合すると、パラメータ数が急増し、必要なメモリ量も増加します。一方、単変量系列として分析すると、チャネル間の依存関係に関する情報が失われます。そこでフレームワークの開発者は、異なるインジケータ間の関係に関する情報を失うことなく、チャネル間の相互作用をコンパクトな空間へ圧縮する軽量なアダプタを組み込みました。

Mantisの実用的な価値を示すため、暗号資産市場における「flash crash」の状況を考えてみましょう。1秒以内に価格が数パーセント急落し、その後ほぼ同じ速さで反発したとします。従来のモデルなら、この異常な動きをノイズとして解釈し、エントリーシグナルを無視するかもしれません。Mantisは、数百万件の実データおよび合成データの例による事前学習のおかげで、そのようなイベントが「偽」と「真」のいずれにも該当し得ることを学習しており、「短期的な異常」または「本物のトレンド転換」といったラベルを、それぞれ異なる信頼度推定とともに割り当てることで、トレーダーが適切な資金保全戦略を構築できるようにします。

もう一つの実世界での例は、テクノロジー株における市場局面の分類です。上昇トレンドの間は、全体的なニュース環境の影響によって価格がほぼ同調して動きます。一方、調整局面では、それぞれの資産が独自の動きをします。Mantisは、アダプタのおかげでこうしたチャネル間の相違を検出します。類似した資産間の低い相関は調整サイクル開始のシグナルとなり、モデルはそれを信頼度推定とともに報告します。


Mantisアルゴリズム

Mantisフレームワークは、よく考えられたアーキテクチャと綿密な学習によって、一連のアイデアを信頼性の高いツールへと変えられることを示しています。このフレームワークの著者らが提示した基盤モデルのアプローチは、4つの主要な柱に基づいています。時系列のトークン化、ハイブリッドアテンション、多変量データ向けのアダプタ、そして自己教師あり対照学習による事前学習と、その後のキャリブレーションです。

Mantisの基本的な考え方は、時系列を固定されたウィンドウに分割する従来の方法から離れることです。その代わりに、系列を固定数のパッチに分割します。これにより、入力系列の長さに依存しなくなり、計算コストも安定します。たとえば、長さ1024と2048の系列は、どちらも同じ32個のパッチへ変換されます。この方法は、異種の時系列を大規模に処理するうえで非常に重要です。

埋め込みはいくつかの段階で形成されます。まず、出力チャネル数256の畳み込み層を適用します。この層によって時系列は、よりコンパクトな潜在表現へ変換されます。次に、32個の各パッチに対して平均化(mean pooling)をおこない、次元(32, 256)のテンソルを得ます。各パッチは、ピーク、変動、価格変動のマイクロストラクチャを含む、時系列の局所的特徴を表現します。

これと並行して、2つ目のデータストリームである差分ストリームを作成します。これは時系列の隣接する値の一次差分に基づきます。この変換は長期的なトレンドを取り除き、短期的なダイナミクスに関連するシグナルを強調するのに役立ちます。一定水準からの逸脱や、サポートおよびレジスタンス付近での急激な動きに関心がある状況では、特に有効です。

両方のストリームには、同じ処理が適用されます。畳み込み、平均化、正規化です。その結果、それぞれ256次元の32個のトークンを含む2組のパッチが得られます。これにより、モデルはシグナルの形状と時間経過に伴う変化の両方について、バランスの取れた情報を得られます。

さらに、スケールとボラティリティという2種類の情報も抽出されます。そのために時系列を32個の等しいウィンドウに分割し、それぞれのウィンドウについて平均値と標準偏差を計算します。これらの統計量はMulti-Scaled Scalar Encoderを使用してエンコーディングされ、モデルがシグナルの背景的な特徴を捉えられるようにします。したがって、モデルは一度に4つのデータストリームを受け取ります。正規化された値、差分、平均値、標準偏差です。

4つのデータストリームは連結されます。その前に、それぞれを個別の線形プロジェクタへ通し、次元の整合性を確保します。次に、結合された埋め込みは射影層を通り、続いてレイヤー正規化が適用されます。その結果、時系列は256次元の32個のトークンとして最終的に表現されます。これらのトークンは、マーケットの挙動および統計情報を保持する汎用的なコンテナです。

アーキテクチャの次の段階では、クラストークンを追加します。これは、すべてのトークンから情報を集約する役割を持つ学習可能なベクトルです。これにより、モデルは局所的なパターンとグローバルなパターンの両方を考慮しながら、系列の最終的な表現を形成できます。パッチの順序に関する情報を保持するため、正弦波位置エンコーディングを適用し、その後データをTransformerブロックへ入力します。

Transformerは6層で構成されます。各層には、8ヘッドのMulti-Head Attention機構、正規化層、そしてGELU活性化を備えた2層のフィードフォワードブロックが含まれます。事前学習フェーズでは、10%の確率でdropoutが適用され、過学習の防止に役立ちます。すべての層を通過した後、クラストークンの出力ベクトルを使用して最終的な系列埋め込みを形成します。

Mantisは、対照学習を取り入れた自己教師ありパラダイムを使用して学習されます。モデルは元のデータにさまざまな時系列変換を適用します。具体的には、RandomCropResizeメソッドによって、系列の0~20%がランダムに削除され、残った部分が元の長さまで引き伸ばされます。これにより、イベントの順序を変更することなく、シグナル全体の構造を維持できます。

バッチ内の各サンプルxiに対して、2つの変換がランダムに選択されます。得られた表現は投影ヘッドへ渡され、コサイン類似度を用いて比較されます。

その後、クロスエントロピー損失が計算されます。

フレームワークの著者らは研究において、10個のデータセットを組み合わせたサンプルを使用してモデルを事前学習しました。このサンプルには700万以上の時系列が含まれています。学習は100エポック実行されました。バッチサイズには2048を使用し、4基のTesla V100-32GB GPUを使用しました。

ファインチューニングでは、埋め込みに分類ヘッドを接続しました。得られた予測値には温度スケーリングが適用され、期待キャリブレーション誤差を最小化し、モデルの結果を確率的な意味で解釈できるようにします。

しかし、時系列の多変量性は依然として主要な課題の一つです。タスクによってはチャネル数が異なるため、モデルを適応させる必要があります。他の基盤モデルと同様に、Mantisは単変量形式で学習され、各チャネルに同じ機構を適用します。これは計算リソースへの負担を増加させるだけでなく、チャネル間の相関も無視してしまいます。

これらの制限に対処するため、フレームワークの著者らはチャネルアダプタを提案しています。これは、元のdチャネルをdnewへ変換する関数aです。このアプローチによって、入力データを計算予算に合わせて適応させ、時間構造を保持し、あらゆるモデルとの互換性を確保することができます。

ここで、Mantisフレームワークの著者らは5種類のアダプタを検討しています。最初の4つは、古典的な次元削減手法です。

  • PCA主成分分析:分散の大部分がより少ない数の成分に集中する直交空間を見つけます。
  • 打ち切り特異値分解PCAと類似していますが、中心化されていないデータ行列に適用されます。
  • ランダム射影:低次元空間へのランダムな線形写像です。
  • 分散ベースセレクタ:分散が最も大きいチャネルを選択します。

これらのアダプタに入力するデータは、(n*t, d)の形状へ変形されます。ここでnはサンプル数です。射影行列Wがチャネルのマッピングをおこないます。

5番目のアダプタであるDifferentiable Linear Combiner (LComb)は、メインモデルとともに学習され、逆伝播の際にそのパラメータを調整します。これにより、タスクのコンテキストを考慮できるようになり、より高い精度を実現できます。

その結果、Mantisは時系列の構造やチャネル間の関係を失うことなく、幅広いタスクに適用できます。実世界の多変量シナリオにおいても、モデルは汎用性と効率性を維持します。

著者によるMantisフレームワークの可視化を以下に示します。


MQL5を使用した実装

Mantisフレームワークの理論的側面について十分に分析したところで、次に最も興味深い部分、すなわち実際の実装へと進みます。ここでは、これまで述べてきたアイデアがコードとして具体化され、抽象的な原理が実際の成果を生み始めます。ここで焦点を当てるのは、MQL5言語を用いた、このフレームワークの主要なアーキテクチャ上の設計判断をソフトウェアとして実装することです。概念自体は非常に複雑ですが、私たちが取り組む課題は明確かつ意欲的です。それは、現実の市場データを効率的に処理し、ボラティリティに適応しながら、同時に可能な限り少ないリソースで動作するモデルを構築することです。

アプローチの検討


コードを書き始める前に、エンジニアリングにおける標準的な手順として、まず戦略を立てる必要があります。明確な計画がなければ、どのようなアーキテクチャであっても、現実の市場が要求する条件に耐えることはできません。まず、体系的な分析から始めます。具体的に何を実装したいのか、それぞれのコンポーネントがどのような役割を担うのか、そして潜在的なボトルネックがどこに存在する可能性があるのかを明確にします。重要なのは、著者のアイデアを単純にコピーすることではありません。それを私たちの目的と、独自の制約や特徴を持つMQL5環境に適応させることです。

実装の中心となるのが、トークンの形成です。モデルの将来的な性能を左右する基盤がここで構築されます。というのも、Transformerのデータをそのまま効率的に処理することができないからです。Transformerには、トークン列の形式で入力を与える必要があります。それぞれのトークンは、時系列の一部分を圧縮しながらも情報量を保った形で表現します。

元のMantisアーキテクチャでは、偏差系列、セグメント平均、セグメント標準偏差などを含むマルチチャネル方式を使用することが提案されています。しかし、私たちのモデルが通常扱うような正規化済みの入力では、平均値と標準偏差のストリームが持つ情報量は低下します。そのため、これらのチャネルは意図的に削除し、代わりに2つの主要なストリーム、すなわち元の時系列とその1階差分に焦点を当てます。この組み合わせにより、大域的な水準と局所的な変化の両方を同時に捉えることができます。言い換えれば、これは安定性と感度の間の一種の妥協点です。

これらのストリームを準備するプロセスは、まず元の時系列を正規化することから始まります。これは重要なステップです。大規模な変動を平滑化し、モデルの外れ値に対する頑健性を高めるのに役立ちます。次に、隣接する観測値の差を計算するために、差分処理を適用します。こうして得られる系列は急激な変化に対して非常に敏感であり、市場のインパルスや反転を捉えようとする場合に特に有用です。その後、この2つの系列を結合して、2チャネルのテンソルを作成します。各チャネルは、それぞれ独立した情報ストリームを表します。

しかし、単純に2つを結合するだけでは十分ではありません。マルチチャネル時系列には豊富な情報が含まれているものの、モデルには依然として時間的コンテキストに関する重要な情報が不足しています。よく知られているように、Transformerには、入力系列に含まれる要素の順序を自動的に理解するための組み込み機能がありません。そのため、各観測値が他の観測値に対してどの位置にあるのかを示す追加情報が必要になります。

元のMantisの実装では、この問題を、「Attention Is All You Need」で提案された方式に類似した正弦波位置エンコーディングを追加することで解決しています。この方法は、汎用性と移植性に優れているため便利です。学習する必要がなく、さまざまな種類のタスクに対して良好に機能します。一方で、このエンコーディングが追加されるのは、チャネルを結合し、さらにそれをパッチに分割した後です。

しかし、私たちは別のアプローチを提案します。系列を断片に分割する前に、Mamba4Castフレームワークと同様の時間エンコーディングを導入します。このエンコーディングは、単に時間軸上におけるデータ点の位置を示すだけではありません。時系列が持つ時間構造に関する情報も伝達します。これにより、モデルは市場データに潜む季節性、繰り返しパターン、非対称性、そしてリズムを検出できるようになります。正弦波エンコーディングとは異なり、時間エンコーディングは市場のマイクロストラクチャに対してより敏感であり、現在の分析ウィンドウを超えて存在するパターンも捉えることができます。

このエンコーディングを追加して初めて、次の論理的なステップであるチャネルのセグメンテーションに進むことができます。このステップは一種のフィルタとして機能し、時間情報によって拡張されたデータを、モデルが扱いやすい形式へと変換します。言い換えると、この時点でデータには単なる値とその導関数だけでなく、時間経過に伴うデータの挙動のダイナミクスを明らかにする、もう一層のコンテキスト情報が加わっています。これにより、モデルが複雑な関係性を特定する作業が大幅に容易になり、市場ノイズに対するモデルの頑健性も向上します。

セグメンテーションアルゴリズムの変更点については、実際の実装に入った段階で少し後に詳しく説明することにしましょう。ここでは、他のモジュールについての説明はいったん意図的に脇へ置き、基盤に焦点を当てます。すなわち、時系列を、情報量があり、標準化され、構造化された系列へと変換するプロセスです。モデル全体の成功を左右する基礎は、まさにこの段階で築かれます。適切に準備されたデータによって、パターンを効率的に識別し、イベントの順序を正しく考慮し、市場の挙動における重要なパターンを見分けることが可能になります。

ここまでで概念的な枠組みが整い、実装へのロードマップも明確になりました。いよいよ、言葉から行動へと移ります。この段階から、モデルの実際の実装が始まります。つまり、理論がコードへと変換される瞬間です。まずは最も基本的でありながら、非常に重要な部分から始めましょう。それは、時系列の前処理です。この処理によって、データはマルチチャネル表現へと変換されます。ここで最初の特徴量抽出がおこなわれ、その結果が、後にTransformerが何を見るのか、そしてそれをどのように解釈するのかを決定します。今回のタスクは、2つのストリーム、すなわち元の系列とその1階差分を抽出し、それらを1つのテンソルに結合することです。これらすべてをOpenCLを使用して実装します。

OpenCLプログラムの変更点


OpenCLを使用するのは、特に大量の時系列を扱う場合に、計算を高速化する必要があるためです。GPUを利用することで、複数の変数に対する処理を並列化し、メインプロセッサに負荷をかけることなく、データの前処理を高速化できます。そこで、まず実装するのが専用のConcatDiffカーネルです。その目的は、1階差分を計算すると同時に、その結果を元のデータと結合することです。これにより、各時間区間について2つのチャネルを持つテンソルを形成します。

このカーネルには、パラメータとしてデータバッファへのポインタを2つだけ渡します。これは、実績のあるミニマルなアプローチです。1つ目は入力配列dataです。ここには、分析対象となる時系列が格納されています。2つ目は出力バッファoutputで、計算結果がここに格納されます。この方法により、カーネルのインターフェースを可能な限りシンプルで直感的なものにでき、不要なオーバーヘッドを排除できます。

追加のパラメータとして、定数stepを使用します。これは、差分を計算する際のステップ幅です。このパラメータを追加することで、アルゴリズムの柔軟性が大幅に向上します。これにより、従来の1階差分、つまり隣接するデータ点間の差分だけでなく、たとえば「現在の値」と「5ステップ先の値」との差分も計算できるようになります。後者は金融分野において特に有用です。このパラメータを利用することで、異なる期間における変化を反映した特徴量を作成できるようになります。

__kernel void ConcatDiff(__global const float* data,
                         __global float* output,
                         const int step)
  {
   const size_t i = get_global_id(0);
   const size_t v = get_local_id(1);
   const size_t inputs = get_local_size(0);
   const size_t variables = get_local_size(1);

各ワークアイテムには、一意のインデックスの組み合わせ、すなわち時間インデックスiと変数インデックスvが割り当てられます。したがって、各ワークアイテムは、元のデータ行列に含まれる特定の1つの値を処理する役割を担います。

入力データ配列dataは、正規化された時系列データを線形化された形式で表したものです。つまり、時間方向とチャネル方向の次元が展開され、1次元の配列として格納されています。

次に、カーネル内部でshiftを計算します。これは、現在の時間・チャネル位置に対応するdata配列内のオフセットです。

const int shift = i * variables;
const float d = data[shift + v];

元の値dを取得したら、次に時間軸方向の1階差分を計算します。そのために、現在の値とstepステップ先に位置する値との差を求めます。この方法は、増加や減少、変化の加速や減速といった局所的なダイナミクスを反映する特徴量を作成することを目的としています。この際、インデックスの有効性を確認する必要があります。つまり、配列の範囲外を参照しないようにする必要があります。

float diff = 0;
if(step > 0 && (i + step) < inputs)
   diff = IsNaNOrInf(d - data[shift + step * variables + v], 0);

差分の計算の最後には、保護用の関数IsNaNOrInfが使用されています。この関数は、NaNや無限大が発生した場合に結果をゼロに設定します。これは、欠損値やデータの異常が存在する場合に特に重要です。

次に重要な段階である出力配列outputの形成に進みます。ここでは興味深い処理がおこなわれます。データはペア単位で書き込まれます。まず元の値が記録され、その直後に対応する1階差分の値が記録されます。ここでは、変数のストライドを2倍にしているため、つまりチャネル数を2倍にしているため、各時点におけるテンソルには各変数について2つの値が格納されます。したがって、outputのデータ構造のサイズは(T * 2 * V)となります。ここで、Tは時間ステップ数、Vは変数の数を表します。

 output[2 * shift + v] = d;
 output[2 * shift + v + variables] = diff;
}

この方法により、モデルへの入力データを1回のパスで前処理できます。生成されたテンソルには、すでに市場の静的特性と動的特性の両方が含まれています。

このケースでOpenCLを使用することには、2つのメリットがあります。1つ目は並列処理によって処理時間を短縮できること、2つ目はコードのロジックを簡潔にできることです。インデックスや配列に関するすべての処理がカーネル内部でおこなわれるためです。CPU側で複雑なループ処理をおこなう代わりに、データの長さや幅に応じて容易にスケールできる、コンパクトで効率的な処理ブロックを構築できます。

このモジュールは、モデル全体の基盤を構成する最初の構成要素として機能します。ここでは重要な概念が実装されています。それは、時系列を2つの相補的なストリームへ初期分解することです。元の値からは、データの水準やトレンドを捉えることができます。一方、1階差分は変化に対する感度を高めます。これは一種の導関数として機能し、転換点を示すシグナルとなります。この2つを組み合わせることで、ノイズに対して頑健でありながら、急激な変化にも適応できる、バランスの取れたデータ表現が得られます。

ここで、この段階におけるアーキテクチャ上の重要な特徴を強調しておく必要があります。ConcatDiffカーネルには、学習可能なパラメータが一切ありません。その目的は純粋に機能的な処理をおこなうことです。すなわち、値の抽出や差分の計算といった単純な線形演算を実行し、その結果を1つの配列に結合します。このような性質から、ConcatDiffはOpenCLによる実装に非常に適しています。複雑な状態管理を必要としない大規模な並列処理は、OpenCLが特に得意とする処理だからです。

さらに、金融時系列データの特性を考慮すると、1回の前処理の反復処理の中では、入力データそのものを固定値として扱うのは極めて合理的です。そのため、このケースでは、誤差勾配の逆伝播をおこなうカーネルや、パラメータを更新するためのカーネルは実装しません。このような処理が必要になるのは、モデルを構成する学習可能な層だけです。一方、ここで扱っているのは前処理です。この段階では、処理速度、信頼性、そして完全に決定論的な計算が重要になります。

OpenCLプログラムの完全なコードは添付ファイルに記載されています。

差分計算オブジェクト


次のステップは、ConcatDiffカーネルをメインプログラムの構造に統合することです。そのために、CNeuronConcatDiffクラスとして実装された専用オブジェクトを作成します。このオブジェクトは、高レベルのニューラルネットワークモデルのロジックと低レベルのOpenCLコードとの間をつなぐインターフェースとして機能します。具体的には、パラメータを適切に準備し、カーネルを起動し、そしてその計算結果を受け取る役割を担います。

class CNeuronConcatDiff:  public CNeuronBaseOCL
  {
protected:
   uint              iUnits;
   uint              iVariables;
   uint              iStep;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override   { return true; }
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronConcatDiff(void) :  iUnits(0), iVariables(1), iStep(1) {  activation = None; }
                    ~CNeuronConcatDiff(void) {};
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint units_count, uint step, uint variables,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override  const   {  return defNeuronConcatDiff;   }
   //--- methods for working with files
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
  };

クラス自体には、いくつかの重要な変数が含まれています。

  • iUnits:時間ステップ数
  • iVariables:入力データに含まれるチャネル数または変数の数
  • iStep:差分を計算する際のステップ幅を指定するシフトパラメータ。この値により、コンポーネントをさまざまな時間ウィンドウ幅やサンプリングレートに適応させることができます。

クラスのコンストラクタでは、主要なパラメータがデフォルト値で初期化されます。すなわち、チャネル数は1(iVariables = 1)、ステップ幅は1(iStep = 1)、そして活性化関数は無効(activation = None)に設定されます。これは理にかなった設定です。というのも、このコンポーネントは前処理の段階で動作するため、非線形性を導入すべきではないからです。

また、このクラスにはInitというオブジェクト初期化メソッドも実装されています。このメソッドのパラメータには、作成するオブジェクトのアーキテクチャを明確に解釈できるようにする定数が渡されます。

bool CNeuronConcatDiff::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                             uint units_count, uint step, uint variables,
                             ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, 2 * units_count * variables, optimization_type, batch))
      return false;
   if(step<=0 || step >= units_count)
      return false;
//---
   iUnits = units_count;
   iVariables = variables;
   iStep = step;
//---
   return true;
  }

Initメソッドのロジックは、可能な限りシンプルかつ簡潔になっています。メソッド本体では、データ構造の特性を考慮したうえで、同名の親クラスのメソッドが呼び出されます。入力データと1階差分の値を出力配列に結合するため、出力バッファのサイズは2倍にする必要があります。

親クラス側の初期化が完了すると、入力として受け取ったすべてのパラメータがクラス内部の変数に格納されます。これにより、それらの値をOpenCLカーネルに渡すパラメータを生成する際に利用できるようになります。

このアプローチにより、このオブジェクトは柔軟性が高く、独立性があり、再利用可能なものになります。

特に重要なのが、フォワードパスを担当する仮想メソッドfeedForwardです。したがって、このメソッドがOpenCLカーネルを呼び出す役割も担います。

メソッドのパラメータとして、入力元となるデータオブジェクトへのポインタを受け取り、まずその有効性を検証します。

bool CNeuronConcatDiff::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL || !Output)
      return false;
   if(getOutputIndex() < 0)
      return false;

一連のチェックを正常に通過すると、ConcatDiffカーネルが実行キューに投入されます。ここでは、すでに説明したものと同じアルゴリズムを使用します。クラス内部の変数にあらかじめ保存しておいた値を使用して、タスク空間の次元数を指定します。

{
 uint global_work_offset[2] = {0};
 uint global_work_size[2] = {iUnits, iVariables};
 const int kernel = def_k_ConcatDiff;
 if(!OpenCL.SetArgumentBuffer(kernel, def_k_concdiff_data, NeuronOCL.getOutputIndex()))
   {
    printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel),
                                                               GetLastError(), __LINE__);
    return false;
   }
 if(!OpenCL.SetArgumentBuffer(kernel, def_k_concdiff_output, getOutputIndex()))
   {
    printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel), 
                                                               GetLastError(), __LINE__);
    return false;
   }
 if(!OpenCL.SetArgument(kernel, def_k_concdiff_step, iStep))
   {
    printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel), 
                                                               GetLastError(), __LINE__);
    return false;
   }
 //---
 if(!OpenCL.Execute(kernel, global_work_size.Size(), global_work_offset, global_work_size))
   {
    printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel), 
                                                               GetLastError(), __LINE__);
    return false;
   }
}

しかし、feedForwardメソッドはここで終わりではありません。次のステップでは、生成されたテンソルを正規化します。これは基本的に、データを統一されたスケールに揃える処理であり、ニューラルネットワークを学習させるうえで極めて重要です。さらに、この実装では、特徴量と差分に対して、それぞれ個別に正規化をおこないます。つまり、変数の元の値は特徴量として正規化され、1階差分の値はそれとは別に処理されます。これにより、モデルは時系列における急激な変化に対する感度を維持しながら、大規模な変動に対しても安定性を失わずに済みます。

     {
      uint global_work_offset[1] = {0};
      uint global_work_size[1] = {2 * iUnits};
      const int kernel = def_k_Normilize;
      if(!OpenCL.SetArgumentBuffer(kernel, def_k_norm_buffer, getOutputIndex()))
        {
         printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel),
                                                                    GetLastError(), __LINE__);
         return false;
        }
      if(!OpenCL.SetArgument(kernel, def_k_norm_dimension, iVariables))
        {
         printf("Error of set parameter kernel %s: %d; line %d", OpenCL.GetKernelName(kernel), 
                                                                    GetLastError(), __LINE__);
         return false;
        }
      if(!OpenCL.Execute(kernel, global_work_size.Size(), global_work_offset, global_work_size))
        {
         string error;
         CLGetInfoString(OpenCL.GetContext(), CL_ERROR_DESCRIPTION, error);
         printf("Error of execution kernel %s: %d -> %s", OpenCL.GetKernelName(kernel), 
                                                                GetLastError(), error);
         return false;
        }
     }
//---
   return true;
  }

このような後処理によって入力値の範囲が標準化されるため、学習の安定性が大幅に向上し、モデルの収束も速くなります。

ここで、誤差勾配の伝播をおこなうcalcInputGradientsメソッドについても少し触れておきましょう。この層用の逆伝播カーネルは作成していませんが、対応するメソッド自体は実装しています。

これはなぜでしょうか。応えは簡単です。このようにしておくことで、誤差逆伝播の連鎖を壊すことなく、このオブジェクトをモデルのどの階層にも組み込めるようになるからです。この層自身が勾配を計算する必要がない場合でも、誤差信号を前段のオブジェクトへ正しく伝達しなければなりません。そうしなければ、この層でモデル全体の学習が止まってしまい、勾配フローが停滞する原因となります。その結果、下位層のパラメータを最適化することができなくなります。

このメソッドのアルゴリズムは、シンプルかつ信頼性の高いものです。まず、入力元のデータオブジェクトである(NeuronOCL)へのポインタを取得し、その有効性を直ちに確認します。

bool CNeuronConcatDiff::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;

現在のオブジェクトに蓄積された誤差勾配を、入力データのフォワード方向に沿ってそのまま渡し、前の層への信号として利用します。この処理は、特別な計算処理をおこなうものではなく、単純な分離処理です。

if(!DeConcat(NeuronOCL.getGradient(), getPrevOutput(), getGradient(), iVariables, iVariables, iUnits))
   return false;

必要に応じて(アーキテクチャに応じて)、活性化関数の導関数を用いて誤差勾配を調整することもできます。

   if(NeuronOCL.Activation() != None)
      if(!DeActivation(NeuronOCL.getOutput(), NeuronOCL.getGradient(), NeuronOCL.getGradient(),
                                                                       NeuronOCL.Activation()))
         return false;
//---
   return true;
  }

このアプローチにより、アーキテクチャの互換性と柔軟性が確保されます。CNeuronConcatDiffオブジェクトは勾配の伝播を妨げることなく、複雑なモデルの中で使用できます。入力側の浅い層だけでなく、計算グラフの深い位置にも組み込むことが可能です。

これで、CNeuronConcatDiffクラスの各メソッドを実装するために使用したアルゴリズムについての説明は終了です。このクラスとそのすべてのメソッドの完全なコードは、添付ファイルに記載されています。

ここまでで、この記事として適切な長さの限界に徐々に近づいてきました。それでも、作業の大部分はまだこれからです。ここでは、まだ基盤を構築したにすぎません。しかし、話はここで終わりではありません。読者に過度な負担をかけず、記事を読みやすいものにするため、ここで少し区切りを入れ、実装の続きは本連載の次の記事で扱うことにしましょう。次回は、同じように興味深く、技術的にも充実した段階へと進みます。


結論

本記事では、軽量な設計と高い精度を両立するMantisフレームワークについて検討しました。フレームワークの著者らは、畳み込みと平均プーリングを利用した革新的なトークン化手法を提案しています。この手法により、時系列を256次元の32個のトークンとして効率的に表現することができ、従来の手法と比較して計算コストを削減できます。さまざまなデータセットの例を用いた事前対照学習によって、頑健で他のタスクにも転用可能な特徴表現が得られました。この特徴表現は、ゼロショットおよびファインチューニングの設定において他の手法を上回り、キャリブレーション誤差についても過去最小の値を達成しています。

実践セクションでは、OpenCLMQL5を用いて、主要なデータ前処理層を実装しました。これにより、入力テンソルを並列かつ決定論的に前処理することが可能になります。次回の記事では、Mantisフレームワークの著者らが提案した手法をもとに、私たちなりのアプローチをさらに実装していきます。


リンク


本記事で使用されているソフトウェア

# 名前 種類 説明
1 Research.mq5 EA 事例収集用EA
2 ResearchRealORL.mq5
EA
Real-ORL法を用いた例収集用のEA
3 StudyContrast.mq5 EA エンコーダの対照学習用EA
4 Study.mq5 EA オフラインモデル学習用EA
5 StudyOnline.mq5
EA
オンラインモデル学習用EA
6 Test.mq5 EA モデルテスト用EA
7 Trajectory.mqh クラスライブラリ システム状態とモデルアーキテクチャ記述構造
8 NeuroNet.mqh クラスライブラリ ニューラルネットワーク構築用のクラスライブラリ
9 NeuroNet.cl ライブラリ OpenCLプログラム用のコードライブラリ


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/18246

添付されたファイル |
MQL5.zip (2794.71 KB)
競争学習アルゴリズム(CLA) 競争学習アルゴリズム(CLA)
教育プロセスのシミュレーションに基づく新しいメタヒューリスティック最適化手法である競争学習アルゴリズム(CLA)を紹介します。このアルゴリズムでは、解の集団を生徒と教師からなるクラスに編成し、エージェントが3つのメカニズムを通じて学習します。それは、クラス内で最も優れた生徒に従うこと、個人的な経験を活用すること、そしてクラス間で知識を共有することです。
市場シミュレーション:ポジション表示(III) 市場シミュレーション:ポジション表示(III)
以前の記事では、ZOrderプロパティに値を設定する必要がある場合があることについて説明しました。しかし、なぜでしょうか。チャートにオブジェクトを追加する多くのコードが、このプロパティを使用していない、より正確に言えば、このプロパティに値を定義していないからです。すべてのプログラマーが何をすべきか、あるいはすべきでないか、また、どのようにコードを書くべきか、あるいは書くべきでないかを私が決めるために、ここにいるわけではありません。私は、読者の皆さん、そしてこれらの処理が内部でどのように動作しているのかを本当に理解したいすべての方々に対して、実際に舞台裏で何が起こっているのかを示すために、ここにいます。
初級から中級まで:MetaTrader 5のサンドボックスにおけるファイル操作 初級から中級まで:MetaTrader 5のサンドボックスにおけるファイル操作
サンドボックスとは何かご存じでしょうか。また、サンドボックスをどのように扱うのかご存じでしょうか。もし、これらの質問のいずれかに「いいえ」と答えるのであれば、サンドボックスの基本的な動作原理を理解するために、この記事を読んでください。また、MetaTrader 5が内部データの整合性を保護するためにサンドボックスを使用している理由についても理解できるでしょう。ここで紹介する内容は、純粋に教育目的です。いかなる場合においても、本稿で扱うアプリケーションを、ここで説明する概念を学習すること以外の目的を持つ完成品として扱わないでください。
MQL5での量子ニューラルネットワーク(第1回):インクルードファイルの作成 MQL5での量子ニューラルネットワーク(第1回):インクルードファイルの作成
量子原理と人工知能に基づく取引システムを構築するための新しいアプローチを紹介しています。著者は、量子力学と最新のAIアーキテクチャを組み合わせることで、従来の機械学習の枠を超える独自のニューラルネットワークの開発について説明しています。