English Deutsch
preview
Pythonを使用した深層学習GRUモデルとEAによるONNX、GRUとLSTMモデルの比較

Pythonを使用した深層学習GRUモデルとEAによるONNX、GRUとLSTMモデルの比較

MetaTrader 5テスター | 21 5月 2024, 09:41
126 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

はじめに

これはPythonとMetaTrader5 Pythonパッケージを使用した深層学習による予測と注文とONNXモデルファイルの続きですが、前回と独立して続行できます。すべて説明します。使用するものはすべてこの記事に含まれています。このセクションでは、取引とその後のテストのためのエキスパートアドバイザー(EA)の作成に至るまでのプロセス全体をご案内します。

機械学習は人工知能(AI)のサブセットで、コンピュータが明示的にプログラムされることなくタスクを実行できるようにするアルゴリズムと統計モデルの開発に重点を置いています。機械学習の主な目的は、コンピュータがデータから学習し、時間の経過とともにパフォーマンスを向上させることです。

モデルの仕組み

機械学習モデルの操作と応用の基礎となる基本原理を活用しましょう。すでに統計モデリングや機械学習の経験がある人にとっては初歩的なことに思えるかもしれませんが、すぐに洗練された頑丈なモデルの開発に移るのでご安心ください。

まず、決定木と呼ばれるモデルに注目します。より高い予測精度を提供する複雑なモデルがある一方で、決定木はそのシンプルさと、データサイエンスの分野で最も先進的なモデルの構築における基本的な役割により、利用しやすい入口としての役割を果たしています。

物事を単純化するために、決定木の最も初歩的な形から始めましょう。

木

ここでは住宅を2つのグループに分類しています。各対象住宅の予想価格は、同じカテゴリ内の住宅の過去の平均価格から導き出されます。

データを使用して住宅をこの2つのグループに分類する最適な方法を決定し、各グループの予想価格を決定します。モデルがデータからパターンを捕捉するこの重要な手順は、モデルのフィッティングまたは訓練として知られています。この目的で使用されるデータセットを訓練データと呼びます。

データのセグメンテーションの決定など、モデルフィッティングの複雑さは十分に複雑であり、後で詳しく説明します。一度モデルフィッティングをおこなえば、新たなデータに適用して追加の住宅価格を予測することができます。


決定木の改善

下図に示す2つの決定木のうち、不動産の学習データを調整する過程で出現する可能性が高いのはどちらでしょうか。

2本の決定木

左の決定木(決定木1)は、ベッドルームの数と住宅販売価格の高さの相関関係を反映しており、おそらく現実に即しています。しかし、その主な欠点は、バスルームの数、敷地の広さ、立地など、住宅価格に影響を与える他の多くの要素が考慮されていないことです。

より広範な要因を考慮するには、「より深い」木と呼ばれる「分割」を追加した木を使用することができます。例えば、各住宅の敷地面積の合計を考慮した決定木は次のようになります。

木3

住宅の価格を見積もるには、意思決定木の枝をたどり、常にその住宅の特性に合った道を選択します。住宅の予想価格は木の最後にあります。予想がおこなわれるこの特定のポイントを葉と呼びます。

これらの葉の分割と値はデータに影響され、作業しているデータセットに目を向けるよう促します。


データにPandasを使用する

機械学習プロジェクトの初期段階では、データセットに慣れる必要があります。そこで、Pandasライブラリが不可欠となります。データサイエンティストは通常、Pandasをデータを調査・処理するための主要ツールとして使用します。コードでは通常、Pandasはpdと略されます。

import pandas as pd

モデリング用データの選択

このデータセットには圧倒的な数の変数があり、理解するのはもちろん、明確に提示することも難しいです。この膨大な量のデータを、より理解しやすいように管理しやすい形に整理するにはどうすればいいのでしょうか。

最初のアプローチは、直感に基づいて変数のサブセットを選択することです。その上で、変数の自動優先順位付けを可能にする統計的手法を導入します。

選択する変数や列を特定するために、まずデータセットのすべての列の包括的なリストを調べる必要があります。

このデータをインポートしました。

mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, start_date, end_date)


モデルの構築

モデルの作成に最適なリソースは、scikit-learnライブラリです。Scikit-learnは、一般的にDataFramesに格納されるデータのタイプのモデリングに適しています。

モデルを作成し、使用するための主な手順は以下の通りです。

  • 定義:作成したいモデルのタイプを決定します(決定木なのか、それとも別のモデルを選択するのか)。また、選択したモデルタイプに固有のパラメータも定義します。
  • カスタマイズ:この段階はモデリングの中核であり、モデルが提供されたデータからパターンを学習し、捕捉します。データセットでモデルを訓練します。
  • 予測:単純に聞こえるかもしれませんが、この手順では、訓練したモデルを使用して、新しいデータや未知のデータに対して予測をおこないます。モデルは学習したことを汎化して、教育的な予測をおこないます。
  • 評価:モデルの予測精度を評価します。この重要な手順では、モデルの出力と実際の結果を比較し、そのパフォーマンスと信頼性を評価します。

scikit-learnを使用することで、DataFramesで一般的に見られるさまざまなデータに合わせたモデルを効率的に構築、訓練、評価するための構造化された枠組みを提供します。


ゲート付き回帰型ユニット(GRU)

ウィキペディアにはこうあります。

ゲート付き回帰型ユニット(GRU)は、2014 年にKyunghyun Choによって導入された、title回帰型ニューラルネットワークtitle回帰型ニューラルネットワーク のゲートメカニズムです。GRUは、特定の特徴を入力または忘れるためのゲートメカニズムを備えた長・短期記憶(LSTM)に似ていますが、コンテキストベクトルや出力ゲートが欠けているため、パラメータがLSTMよりも少なくなります。 ポリフォニック音楽モデリング、音声シグナルモデリング、自然言語処理の特定のタスクにおけるGRUのパフォーマンスは、LSTMのパフォーマンスと同等であることがわかりました。 GRUは一般的にゲーティングが確かに有用であることを示し、ベンジオのチームは2つのゲート付きユニットのどちらが優れているかという具体的な結論には至りませんでした。

GRUはGated Recurrent Unitの頭文字をとったもので、LSTM (Long Short-Term Memory)に似た回帰型ニューラルネットワーク(RNN)アーキテクチャの一種です。

LSTMと同様に、GRUは逐次的なデータをモデル化するために作られ、時間経過に伴う情報の選択的な保持や省略を可能にします。特筆すべきは、GRUはLSTMと比較して、より少ないパラメータを特徴とする合理的なアーキテクチャを誇っていることです。この特徴によって、訓練のしやすさと計算効率が高まります。

GRUとLSTMの主な違いは、メモリセルの状態の扱いにあります。LSTMでは、メモリセルの状態は隠れ状態とは区別され、3つのゲート(入力ゲート、出力ゲート、忘却ゲート)を通じて更新されます。逆に、GRUはメモリセルの状態を「活性化候補ベクトル」に置き換え、リセットゲートと更新ゲートの2つのゲートを介して更新します。

まとめると、GRUは逐次データモデリングにおいてLSTMに代わる選択肢として、特に計算量に制約がある場合や、よりシンプルなアーキテクチャが好まれる場合に適しています。


GRUの動き

他の回帰型ニューラルネットワークアーキテクチャと同様に、GRUは逐次データを要素ごとに処理し、現在の入力とその前の隠れ状態に基づいて隠れ状態を調整します。各時間ステップで、GRUは入力と前の隠れ状態からの情報を統合した「活性化ベクトル候補」を計算します。このベクトルによって、次の時間ステップの隠れ状態が更新されます。


活性化ベクトル候補は、リセットゲートと更新ゲートの2つのゲートを使用して計算されます。リセットゲートは前の隠れ状態からの忘却の度合いを決定し、更新ゲートは新しい隠れ状態への活性化ベクトル候補の統合に影響します。

これが、今回取り上げるモデル(GRU)です。

model.add(Dense(128, activation='relu', input_shape=(inp_history_size,1), kernel_regularizer=l2(k_reg)))
model.add(Dropout(0.05))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dropout(0.05))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dropout(0.05))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dropout(0.05))
model.add(Dense(1, activation='linear'))

まず、特徴量と目標変数の入力を選択します。

if 'Close' in data.columns:
    data['target'] = data['Close']
else:
    data['target'] = data.iloc[:, 0]

# Extract OHLC columns
x_features = data[[0]]
# Target variable
y_target = data['target']

データを訓練セットとテストセットに分けます。

x_train, x_test, y_train, y_test = train_test_split(x_features, y_target, test_size=0.2, shuffle=False)

通常、テストサイズは30%以下になるように選択されます(過剰適合を避けるため)。

Sequentialモデルを初期化します。

model = Sequential()

この行は空のSequentialモデルを作成し、層をステップごとに追加できるようにします。

密結合層を追加します。

model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))

密結合層はニューラルネットワークの全結合層です。

括弧内の数字は各層のニューロン数を示します。したがって、第1層は128ニューロン、第2層は256ニューロン、第3層は128ニューロン、第4層は64ニューロンで構成されます。

活性化関数「relu」(Rectified Linear Unit、正規化線形関数)は、各層の後に非線形性を導入するために使用され、モデルが複雑なパターンを学習するのに役立ちます。

パラメータinput_shapeは第1層でのみ指定され、入力データの形状を定義します。この場合、入力データの特徴数に相当します。

kernel_regularizer=l2(k_reg)は、層の重みにL2正則化を適用し、大きな重み値にペナルティを課すことで過剰適合を防ぐのに役立ちます。

出力層

model.add(Dense(1, activation='linear'))

  • 最後の層は単一ニューロンで構成されます。これは、回帰タスク(連続値の予測)に典型的です。
  • 「線形」活性化関数が使用され、これは出力がさらなる変換なしに入力の線形結合であることを意味します。
  • 要約すると、このモデルは、正規化線形活性化関数を持ついくつかの密結合層と、それに続く線形出力層で構成されています。正則化のために、L2正則化が重みに適用されます。このアーキテクチャは通常、数値を予測することを目的とする回帰タスクに使用されます。

モデルをコンパイルします。

# Compile the model[]
model.compile(optimizer='adam', loss='mean_squared_error')

オプティマイザー

オプティマイザーは、訓練プロセスの重要な要素です。損失関数を最小限に抑えるために、訓練中にモデルの重みがどのように更新されるかを決定します。 adamは、ニューラルネットワークの訓練における効率性で知られる人気の最適化アルゴリズムです。各パラメータの学習率を個別に調整するため、幅広い問題に適しています。

損失関数

損失パラメータは、学習中にモデルが最小化しようとする目標を定義します。この場合、損失関数としてmean_squared_errorが使用されます。平均2乗誤差(MSE)は、予測値と実際の値の間の平均2乗差を最小化することを目標とする回帰問題では一般的な選択です。出力が連続値の問題に適しています。MAEは、予測値と実際の値の差の絶対値の平均を計算します。

maeとmse

すべての誤差は、その方向に関係なく、同じ重要性をもって扱われます。MAEが低いということは、モデルのパフォーマンスが高いということでもあります。

要約すると、model.compile文は訓練用のニューラルネットワークモデルを構成します。訓練中に重みを更新するオプティマイザー(adam)と、モデルを最小化する損失関数(mean_squared_error)を指定します。このコンパイルの手順は、データを使用してモデルを訓練するために必要な前段階です。

そして、先に定義したニューラルネットワークモデルを訓練します。

# Train the model
model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2, verbose=1)
訓練データ

X_train_scaled :これは訓練用の入力特徴データで、おそらく数値の安定性を確保するためにスケーリングまたは前処理が施されています。

y_train :訓練データに対応する目標値またはラベル

訓練の構成

epochs=int(epoch) :訓練データ セット全体がニューラルネットワークを介して前後に渡される回数

int(epoch):エポック数が変数epochによって決定されることを指定します。

batch_size=256 :各エポックの間、訓練データはバッチに分割され、各バッチが処理された後にモデルの重みが更新されます。ここで、各バッチは256点のデータから構成されます。

検証データ

validation_split=0.2 :訓練データの20%を検証セットとして使用することを指定します。このセットでのモデルのパフォーマンスは訓練中に監視されますが、重みの更新には使用されません。

冗長性
verbose=1 :訓練出力の冗長性を制御します。1の場合、訓練の進行状況がコンソールに表示されます。

学習プロセスにおいて、モデルは与えられた入力データ(X_train_scaled)と目標値(y_train)に基づいて重みを調整することで予測をおこなうことを学習します。検証分割は、未見のデータに対するモデルのパフォーマンスを評価するのに役立ち、訓練の進捗状況は冗長性の設定に基づいて表示されます。


線形ユニットについて

ニューロンネットワークを見るとき、私たちは基本的な構成要素である個々のニューロンから始めます。図式的に言うと、ニューロンはユニットとも呼ばれ、1つの入力で構成されるとこのようになります。

y = x*w + b

線形ユニットの力学について


ニューラルネットワークの核となるコンポーネント、単一ニューロンの複雑さを探ってみましょう。視覚化すると、1つの入力xを持つニューロンは次のように表されます。

xとラベル付けされた入力はニューロンへの接続を形成し、この接続にはwとラベル付けされた重みが付加されます。情報がこの接続を通過すると、その値に接続に割り当てられた重みが掛けられます。入力xの場合、最終的にニューロンに到達するのは、積w * xです。これらの重みを調整することで、ニューラルネットワークは時間とともに「学習」します。

ここで、バイアスと呼ばれる特殊な重み付けであるbを導入します。他の重みとは異なり、バイアスは入力データを持ちません。その代わりに、ニューロンに到達する値が単純にbであることを保証するために、グラフに1の値が挿入されます(1 * bがbに等しいため)。バイアスを導入することで、ニューロンは入力とは無関係に出力を変化させることができます。

Y = X*W + b*1


複数の入力を受け入れる

もっといろいろな要素を入れたい場合はどうするのでしょうか。解決策はいたって簡単なので、ご心配なく。私たちのモデルを拡張することで、ニューロンへの入力接続をシームレスに追加することができ、それぞれが特定の特徴に対応します。

出力を導き出すには、簡単なプロセスを踏みます。各入力に適切な接続の重みが掛けられ、その結果が瑞々しく結合されます。その結果、ニューロンは複数の入力を巧みに処理し、モデルをよりニュアンス豊かなものにし、異なる特徴の複雑な相互作用を反映した、全体的な表現となります。この方法により、ニューラルネットワークはより幅広い情報を捉えることができ、パターンを総合的に認識する能力が高まります。

y = w0*x0 + w1*x1 + w2*x2

数学的に表現すると、このニューロンの動作は数式で簡潔にとらえられます。

y = w 0・x 0 + w 1・x 1 + w 2・x 2 + b = y=w0⋅x0+w1⋅x1+w2⋅x2+b

この方程式では

  • y:ニューロンの出力
  • w 0 , w 1 , w 2: それぞれ入力 x 0 , x 1 , x 2に関連する重み.を使用します。
  • b:バイアス項

この線形ユニットは、2つの入力を備え、3次元空間の平面をモデル化する能力を持ちます。入力の数が2つを超えると、このユニットは超平面(複数の入力特徴間の関係を複雑に捉えた多次元曲面)のフィッティングに熟達します。この柔軟性により、ニューラルネットワークは、単純な線形関係を超えた複雑なデータパターンをナビゲートし、理解することができます。

Kerasの線形ユニット

Kerasでのニューラルネットワークの作成は、keras.Sequential()によってシームレスに実現されます。このユーティリティは、層をスタッキングすることでニューラルネットワークを構築し、モデル作成への簡単なアプローチを提供します。層はネットワークアーキテクチャのエッセンスをカプセル化したもので、そのなかでも密結合層は、先に検討したようなモデルを構築するのに特に適しています。

model = Sequential()

近日中に、密結合層の複雑さを掘り下げ、その能力と、頑丈で表現力豊かなニューラルネットワークアーキテクチャを構築する上での役割を明らかにする予定です。


ディープニューラルネットワーク

隠れ層を統合することで、ネットワークの深みと表現力を強化します。これらの隠された層は、データ内の複雑な関係を解明する上で極めて重要な役割を果たし、ニューラルネットワークに複雑なパターンを識別して捉える力を与えます。戦略的に隠し層を追加することで、モデルの洗練度を高め、より包括的で正確な予測のために、ニュアンスに富んだ特徴を学習して表現できるようにします。

複雑なニューラルネットワークの構築を探る

ディープニューラルネットワークの実力を特徴づける複雑な関係を把握する能力を備えたニューラルネットワークを構築する旅に出ます。

アプローチの中心は、モジュール性の概念です。モジュール性とは、初歩的な機能ユニットから洗練されたネットワークをつなぎ合わせる戦略です。線形ユニットがどのように線形関数を計算するのかについて掘り下げてきましたが、次はこれらの個々のユニットの融合と適応に焦点を移します。これらの基礎となる構成要素を戦略的に組み合わせ、修正することで、複雑なデータセットに内在する、より複雑で多面的な関係をモデル化し、理解する可能性を解き放ちます。これは、深層学習の領域を定義する微妙なパターンを巧みに操り、理解できるニューラルネットワークを作るための入り口となります。

層について

ニューラルネットワークの複雑なアーキテクチャでは、ニューロンは体系的に階層化されています。注目すべき構成として、入力の共通セットを共有する線形ユニットの統合である密結合層が挙げられます。

このような配置は、強力で相互連結された構造を促進し、層内のニューロンが情報をまとめて処理し、解釈することを可能にします。層の複雑さを掘り下げていくと、密結合層は基礎的な構成として際立っており、ニューロンはデータ内の複雑な関係を理解し、学習するネットワークの能力にどのように協調的に貢献できるかを示しています。

入力、高密度出力 


Kerasの多様な層

Kerasの領域では、層は非常に汎用性の高いエンティティを包含しています。基本的には、あらゆる形式のデータ変換として現れます。畳み込み層や回帰層に代表される多数の層は、ニューロンを活用してデータを変形し、主に形成される接続の複雑なパターンによって区別されます。逆に、他の層は、特徴エンジニアリングから初歩的な演算まで、ニューラルネットワークのモジュラーフレームワークの中で編成できる変換の幅広いスペクトルを示しています。層の多様性は、ニューラルネットワークアーキテクチャの豊かなタペストリーに貢献する適応性と拡張性を強調しています。

活性化関数によるニューラルネットワークの強化

驚くべきことに、間に介在する要素のない2つの密結合層を組み込んでも、単独の密結合層の効果を上回ることはありません。孤立した密結合層は、私たちを線状構造の領域内に閉じ込め、線と面の境界を超えることができません。この線形性から脱却するために、非線形性という重要な要素を導入します。この極めて重要な要素は、活性化関数によって具現化されます。

活性化関数は変換力として機能し、ニューラルネットワークに非線形性を注入します。直線的な制約を超え、モデルがデータ内の複雑なパターンと関係を識別できるようにするための重要なツールが提供されます。要するに、活性化関数はニューラルネットワークを複雑な領域へと推進する触媒であり、多様なデータセットに内在する微妙な特徴を捉える能力を解き放ちます。

rectifier関数を線形ユニットと融合させると、正規化線形関数(ReLU)と呼ばれる強力なユニットが誕生します。一般的な言い回しでは、rectifier関数はこの理由からReLU関数と呼ばれることが多いです。線形ユニットにReLU活性化を適用すると、出力はmax(0, w * x + b)に変換されます。これを図で表すと次のようになります。

w*x + b

高密度ネットワークによる戦略的階層化

新たに発見した非線形性を武器に、層スタッキングの威力と、複雑なデータ変換の編成を可能にする方法を探ってみましょう。

入力、非表示、出力

ニューラルネットワークの隠れ層について

出力層に先立って、その間にある層は、その出力が直接の観察から隠蔽されたままであるため、多くの場合「隠れ層」と呼ばれます。

最終(出力)層は、活性化関数のない線形ユニットを採用しています。このアーキテクチャの選択は、数値を予測することを目的とする回帰の性質を持つタスクに合致しています。しかし、分類のようなタスクでは、手元の特定のタスクの要件により適した活性化関数を出力層に組み込む必要があるかもしれません。


Sequentialモデルの構築

Sequentialモデルは、これまで利用されてきたように、最初の層から最後の層まで、一連の層を順次シームレスにつないでいきます。この構造的な編成では、最初の層が入力の受け手となり、最終的な層が切望された出力を生み出す頂点となります。この順次組み立ては、上の図に描かれたモデルを反映しています。

model = keras.Sequential([
    # the hidden ReLU layers
    layers.Dense(units=4, activation='relu', input_shape=[2]),
    layers.Dense(units=3, activation='relu'),
    # the linear output layer 
    layers.Dense(units=1),
])


層を別々にリストアップするのではなく、[layer, layer, layer, ...]のように、すべての層を一緒にリストアップすることで、層のまとまりを確保します。活性化関数をシームレスに層に組み込むには、activation引数にその名前を指定するだけです。この合理的なアプローチにより、ニューラルネットワークアーキテクチャの簡潔で整理された表現が保証されます。

密結合層のユニット数の選択

layers.Dense層(例えば、layers.Dense(units=4, ...) )のユニット数に関する決定は、問題のユニークな特性と、データから明らかにすることを目的とするパターンの複雑さによって決まります。以下の要因を考えてみましょう。

問題の複雑さ

  • データ内の関係がそれほど複雑でないより単純な問題の場合は、4ユニットなど、より少ないユニット数が適切な出発点になるかもしれません。
  • 微妙で多面的な関係を特徴とするより複雑なシナリオでは、より多くのユニット数を選択することがしばしば有益です。

データサイズ

  • データセットのサイズも一役買っています。データセットが大きければ、モデルが学習するユニットの数も多くなります。
  • データセットが小さいほど、過剰適合やモデルがノイズを学習する可能性を防ぐため、より慎重なアプローチが必要となります。

モデル容量

  • ユニットの数は、複雑なパターンを捉えるモデルの能力に影響し、一般的にユニットが増えるほど表現力が高まります。
  • 特に限られたデータを扱う場合は、過剰適合につながる可能性があるため、過剰なパラメータ化は避けるよう注意が必要です。

実験

  • 最初は控えめなユニット数から始め、モデルを訓練し、パフォーマンス指標と観察に基づいて改良を加えながら、さまざまな構成を実験します。
  • 交差検証のようなテクニックは、様々なデータサブセットにわたるモデルの汎化パフォーマンスについての洞察を提供します。

ユニットの選択は一律ではなく、試行錯誤が必要な場合もあることを忘れないでください。検証セットでモデルのパフォーマンスを監視し、アーキテクチャを反復的に調整することは、モデル開発プロセスの重要な部分を形成します。

これを選択します。

model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(1, activation='linear'))

第1層(入力層):

ユニット(128):第1層のユニット数が128と比較的多いため、モデルは入力データの多様で複雑なパターンを捉えることができます。これは、ネットワークの初期段階で複雑な特徴を抽出するのに有利です。 活性化(relu):Rectified Linear Unit (ReLU)の活性化によって非線形性が導入され、モデルが複雑な関係やパターンから学習できるようになります。 正則化(L2):L2正則化項( kernel_regularizer=l2(k_reg) )は、層の大きな重みにペナルティを与えることで、過剰適合を防ぐのに役立ちます。

第2層、第3層:

ユニット(256と128):それ以降の層(256と128)では、より多くのユニット数を維持することで、モデルが複雑な情報を捕捉し、処理することを可能にしています。ユニット数を徐々に減らしていくことで、特徴のヒエラルキーを作ることができます。 活性化(relu):ReLUの活性化は持続し、各層の非線形性を促進します。 正則化(L2):L2正則化を層を超えて一貫して適用することで、過剰適合を防ぐことができます。

第4層 

ユニット(64):ユニットを減らすことで、特徴の表現がさらに洗練され、複雑さとシンプルさのバランスを保ちながら、必要な情報を抽出するのに役立ちます。 活性化(relu):ReLUの活性化は持続し、非線形特性の維持が保証されます。 正則化(L2):正則化項は安定性のために一貫して適用されます。

第5層(出力層):

ユニット(1):単一ユニットを持つ最終層は、連続的な数値を予測する回帰タスクに適しています。 活性化(linear):線形活性化は回帰に適しており、モデルは追加の変換なしに予測値を直接出力できます。

全体として、選択されたアーキテクチャは、表現能力と過剰適合を防ぐための正則化の間の思慮深いバランスで、回帰タスク用に調整されているようです。ユニット数を徐々に減らすことで、階層的な特徴の抽出が容易になります。この設計は、タスクの複雑さを包括的に理解し、未知のデータにもうまく汎化できるモデルを構築する努力を示唆しています。

要約すると、隠れ層のユニット数の徐々な減少と各層の具体的な選択は、入力データの階層的で抽象的な表現を捕捉することを目的とした設計を示唆しています。このアーキテクチャは、モデルの複雑さと過剰適合を避ける必要性とのバランスをとっているように見え、ユニットの選択は手元の回帰タスクの性質と一致しています。具体的な数値は、検証データにおけるモデルのパフォーマンスに基づいて、実験とチューニングを経て決定されたのかもしれません。


モデルのコンパイル

# Compile the model[]
model.compile(optimizer='adam', loss='mean_squared_error')

すでに、密結合層を積み重ねて完結合ネットワークを構築することを掘り下げてきました。作成初期段階では、ネットワークの重みはランダムに設定されます。これはネットワークに予備知識がないことを意味します。さて、ニューラルネットワークを訓練するプロセスに焦点を移し、これらのネットワークがどのように学習するのか、その本質を解き明かしていきます。

機械学習ではよくあることですが、学習データから始めます。このデータセット内の各例は、特徴(入力)と予想される目標(出力)から構成されます。ネットワークの訓練の要点は、入力特徴を目標出力の正確な予測に変換するために、その重みを調整することにあります。

このようなタスクのためのネットワークの訓練が成功するということは、その重みが、訓練データに現れているように、これらの特徴と目標との間の関係をある程度カプセル化していることを意味します。

訓練データの他に、2つの重要な要素があります。

  1. ネットワークの予測の有効性を測る「損失関数」
  2. オプティマイザーは、パフォーマンスを向上させるために重みを繰り返し調整する方法をネットワークに指示する役割を担います。

訓練プロセスをさらに進めると、これらのコンポーネントの複雑さを理解することが、ニューラルネットワークの汎化能力を育み、未知のデータに対して正確な予測をおこなう上で極めて重要になります。

損失関数

ここまでネットワークのアーキテクチャ設計について説明してきましたが、ネットワークが取り組むべき具体的な問題をネットワークに指示するという重要な側面は、まだ解明されていません。この責任は損失関数にあります。

要するに、損失関数は目標の真の値とモデルによって予測された値との差を定量化します。これは、モデルがどれだけ効果的に予測と実際の結果を一致させているかを評価する基準となります。

回帰問題でよく使用される損失関数は平均絶対誤差(MAE)です。y_predとして示される各予測のコンテキストにおいて、MAEは絶対差abs(y_true - y_pred)を計算することにより、真の目標y_trueとの差を評価します。

データセット全体の累積MAE損失は、これらの絶対差の平均として計算されます。この指標は、予測誤差の平均的な大きさの包括的な尺度を提供し、予測値と真の目標との間の全体的な不一致を最小化するようにモデルを導きます。

mae

平均絶対誤差は、フィッティングされた曲線と実際のデータ点との間の平均距離を表します。

MAEに加えて、回帰問題でよく遭遇する代替損失関数には、平均二乗誤差(MSE)とHuber損失があり、どちらもKerasでアクセス可能です。

訓練プロセスを通じて、モデルは損失関数をナビゲーションのガイドとして利用し、重みの最適値を決定します。損失を最小限に抑えることを目指します。要するに、損失関数はネットワークの目的を伝達し、予測精度を高めるためにパラメータを学習し、改良するよう導きます。

オプティマイザー:確率的勾配降下法

ネットワークが解決すべき問題を定義したら、次の重要な手順は、その問題を解決する方法を概説することです。この責任はオプティマイザー(損失を最小化する目的で重みを微調整する専用のアルゴリズム)が負います。

深層学習の領域では、最適化アルゴリズムの大半は確率的勾配降下の傘下にあります。これらは、ネットワークを段階的に訓練する反復アルゴリズムです。訓練の各手順は、この順序に従います。

  1. 訓練データをサンプリングし、それをネットワークに入力して予測を生成します。
  2. 予測値と真の値を比較して損失を評価します。
  3. 損失を減らす方向に重みを調整します。

このプロセスは、望ましいレベルの損失削減が達成されるか、それ以上の削減が現実的でなくなるまで繰り返しおこなわれます。基本的に、オプティマイザーは、複雑な重み調整を通してネットワークをガイドし、損失を最小限に抑え、予測精度を高める構成へと舵を切ります。

各反復でサンプリングされた学習データの各セットはミニバッチと呼ばれ、単にバッチと呼ばれることも多いです。一方、訓練データを完全に掃引することをエポックと呼びます。指定されたエポック数は、ネットワークが各訓練例を処理する回数を決定します。

学習率とバッチサイズ

ラインは完全なオーバーホールではなく、各バッチの方向性をわずかに変えるだけです。これらのシフトの大きさは、学習率に支配されます。学習率が小さいほど、ネットワークは、重みが最適値に落ち着くまでに、より多くのミニバッチに触れる必要があることを意味します。

学習率とミニバッチのサイズは、SGD学習の軌跡に影響を与える2つの最も重要なパラメータです。これらの相互作用のナビゲートは微妙であり、最適な選択は必ずしも明らかではありません。

ありがたいことに、ほとんどのタスクにおいて、最適なハイパーパラメータを徹底的に探索することは、満足のいく結果を得るために必須ではありません。適応学習率を持つSGDアルゴリズムであるAdamでは、大規模なパラメータチューニングは必要ありません。その自己チューニングの性質により、Adamはさまざまな問題に適した優れた万能オプティマイザーとなっています。

この例では、SGDとしてADAMを、損失としてMSEを選択しました。

model.compile(optimizer='adam', loss='mean_squared_error')

フィッティングをおこなうと

# Train the model
model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2, verbose=1)

以下のようなものが表示されます。

44241/44241 [==============================] - 247s 6ms/step - loss: 0.0021 - val_loss: 8.0975e-04
Epoch 2/30
44241/44241 [==============================] - 247s 6ms/step - loss: 2.3062e-04 - val_loss: 0.0010
Epoch 3/30
44241/44241 [==============================] - 288s 7ms/step - loss: 2.3019e-04 - val_loss: 8.5903e-04
Epoch 4/30
44241/44241 [==============================] - 248s 6ms/step - loss: 2.3003e-04 - val_loss: 7.6378e-04
Epoch 5/30
44241/44241 [==============================] - 257s 6ms/step - loss: 2.2993e-04 - val_loss: 9.5630e-04
Epoch 6/30
44241/44241 [==============================] - 247s 6ms/step - loss: 2.2988e-04 - val_loss: 7.3110e-04
Epoch 7/30
44241/44241 [==============================] - 224s 5ms/step - loss: 2.2985e-04 - val_loss: 8.7191e-04


過剰適合と過小適合

Kerasはモデルの訓練中、エポックを通じて訓練の損失(training loss)と検証の損失(validation loss)の記録を保持します。これらの学習曲線の解釈を掘り下げ、モデル開発を強化するために学習曲線を活用する方法を探ります。具体的には、学習曲線を分析して過小適合と過剰適合の兆候を特定し、これらの問題に対処するためのいくつかの戦略を探ります。

学習曲線の解釈

訓練データの情報を考慮するとき、それはシグナルとノイズの2つの要素に分類することができます。シグナルは汎化する部分を表し、モデルが新しいデータに対して予測をおこなうのを助けます。一方、ノイズとは、実世界のデータから生じるランダムな揺らぎや、モデルの予測能力に寄与しない非情報的なパターンを指します。この違いを見極め、理解することが重要です。

モデルの訓練では、訓練セットでの損失を最小化する重みまたはパラメータを選択することを目指します。ただし、モデルのパフォーマンスを総合的に評価するためには、新しいデータセット(検証データ)で評価することが不可欠です。

深層学習モデルの訓練を成功させるためには、曲線を(プロットする際に)効果的に解釈することが不可欠です。

学習曲線

さて、学習損失は、モデルがシグナルまたはノイズのいずれかを取得したときに減少します。ただし、訓練セットから得たノイズは新しいデータに汎化できないため、検証の損失が減少するのは、モデルがシグナルを学習したときだけです。その結果、モデルがシグナルを学習すると、両曲線は減少を示し、ノイズを学習すると、両曲線の間にギャップが生じます。このギャップの大きさは、モデルが獲得したノイズの程度を示します。

over_under_fitting



理想的なシナリオでは、すべてのシグナルを学習し、ノイズを全く学習しないモデルを構築することを目指すでしょう。しかし、この理想的な状態を実現することは現実的に不可能です。その代わり、トレードオフをナビゲートします。より多くのノイズを獲得する代償として、より多くのシグナルを学習するようにモデルを促すことができます。このトレードオフが有利である限り、検証の損失は減り続けるでしょう。とはいえ、トレードオフが不利になり、コストが利益を上回り、検証の損失が増大し始める時点が来ます。


このトレードオフは、モデル学習における2つの潜在的な課題を浮き彫りにしています。訓練セットの過小適合は、モデルが十分なシグナルを学習していないために損失が最小化されない場合に起こります。一方、モデルがノイズを吸収しすぎたために損失が最小化されない場合、訓練セットの過剰適合が起こります。深層学習モデルを訓練する鍵は、これら2つのシナリオの最適なバランスを発見することにあります。

もう一方のグラフはこのようになります。

過剰適合と過小適合

モデル容量

モデルの能力は、複雑なパターンを把握し理解する能力を示します。ニューラルネットワークの文脈では、これはニューロンの数と相互接続性によって大きく左右されます。ネットワークがデータの複雑さを十分に捉えていない(過小適合)ようであれば、その能力を高めることを検討します。

ネットワークの容量は、広げる(既存の層にさらにユニットを追加する)か、深める(より多くの層を組み込む)のどちらかによって増やすことができます。より広いネットワークはより線形的な関係を学習することに優れ、より深いネットワークはより非線形的なパターンをとらえる傾向があります。この2つのどちらを選択するかは、データセットの性質によります。

早期停止

前述したように、訓練中にモデルが過度にノイズを取り込むと、検証の損失が上昇し始める可能性があります。この問題を回避するために、早期停止を実装することができます。これは、検証の損失が減少しなくなったことが明らかになった時点で、訓練プロセスを停止するテクニックです。この積極的な介入は、過剰適合を防ぎ、モデルが新しいデータに対してうまく汎化できるようにするのに役立ちます。

検証の損失が増加したことを確認したら、重みを最小になった時点にリセットすることができます。この予防措置は、モデルがノイズの学習に固執しないことを保証し、過剰適合を回避します。

早期停止による訓練の実施は、ネットワークがシグナルを完全に把握する前に訓練プロセスを早期に停止してしまうリスクも軽減します。過剰な長時間の訓練による過剰適合を防ぐだけでなく、早期の停止は、訓練期間の不足による過小適合に対するセーフガードとしても機能します。訓練エポックを非常に大きな数(必要以上)に設定するだけで、早期停止が検証の損失傾向に基づいて終了を管理します。

早期停止の統合

Kerasでは、早期停止はコールバックによって訓練に組み込まれます。コールバックは基本的に、ネットワークの訓練プロセス中に一定間隔で実行される関数です。特に早期停止コールバックは、各エポック終了後にトリガーされます。Kerasは利便性のために様々な定義済みのコールバックを提供しますが、特定の要件を満たすためにカスタムコールバックを作成することもできます。

これが私たちの選択です。

from tensorflow import keras
from tensorflow.keras import layers, callbacks

early_stopping = callbacks.EarlyStopping(
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=20, # how many epochs to wait before stopping
    restore_best_weights=True,
)

# Train the model
model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2,callbacks=[early_stopping], verbose=1)


そして、さらにユニットを追加し、隠れ層を1つ追加しました(チューニング後の.pyではモデルはより複雑になっています)。

model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(1, activation='linear'))

これらのパラメータは、次のような指示を伝えます。直前の20エポックと比べて、検証の損失に少なくとも0.001の改善が見られない場合、訓練を中止し、これまでに特定された最もパフォーマンスの良いモデルを保持します。検証の損失が過剰適合によって増加しているのか、それとも単なるランダムなバッチのばらつきによるものなのかを判断することは、時として困難な場合があります。指定されたパラメータは、一定の許容範囲を設定することを可能にし、訓練プロセスを停止するタイミングをシステムに導きます。

最初はエポック数を300に設定し、訓練プロセスの早期終了を期待しました。

欠損値の処理

データセットに欠損値が存在する原因は様々な状況にあります。

scikit-learnのような機械学習ライブラリで作業する場合、欠損値を含むデータを使用してモデルを構築しようとすると、通常エラーになります。したがって、この問題に対処するためには、以下のいずれかの戦略を採用しなければなりません。

3つのアプローチ

  1. 合理化されたソリューション(drop nan):欠損値を含む列の除去。簡単な方法は、欠損値を含む列を取り除くことです。
df2 = df2.dropna()

しかし、廃棄された列の値のかなりの部分が欠落していない限り、このアプローチを選択すると、モデルは潜在的に価値のある相当量の情報へのアクセスを失うことになります。10,000行のデータセットで、重要な列が1つだけ欠落している場合を考えてみましょう。この戦略を採用すると、列全体を取り除くことになります。

2) 改善された代替案:代入(imputation)

代入では、欠損値を特定の数値で埋めます。例えば、各列の平均値を記入することもできます。

ほとんどの場合、インポートされた値は正確ではないかもしれませんが、この方法は一般的に、行を完全に破棄するよりも正確なモデルが得られます。


3)進化する代入技術

代入は従来のアプローチであり、しばしば効果的でした。とはいえ、入力された値は(データセットで入手できない)真の値から系統的に乖離している可能性があります。あるいは、欠損値を持つ行は、明確な特徴を示す可能性があります。このような場合、欠損値のオリジナリティを考慮してモデルを改良することで、予測精度を高めることができます。

この方法では、前述のように欠損値の代入を継続します。さらに、最初のデータセットで欠損エントリを含む列ごとに、入力されたエントリの位置を示す新しい列を導入します。

このテクニックは特定のシナリオでは結果を大幅に向上させることができますが、その効果はさまざまで、場合によっては改善が見られないこともあります。

ONNXモデルの出力

1 データの読み込み

モデルを学習するために作成した.pyファイルの基本的な理解ができたので、学習してみましょう。 

ここにパスを書くべきです。

# get rates
eurusd_rates = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, start_date, end_date)

# create dataframe
df = pd.DataFrame(eurusd_rates)

このようなコードになります(GRU_create_model.py)。

訓練をすると、このような結果になります。

Mean Squared Error: 0.0031695919830203693

Mean Absolute Error: 0.05063149001883482

R2 Score: 0.9263800140852619
Baseline MSE: 0.0430534174061265
Baseline MAE: 0.18048216851868318
Baseline R2 Score: 0.0

「  Forex exchange rate forecasting using deep recurrent neural networks」稿にある通りです。GRUとLTSMの結果は似ています。 

論文

論文の表


ONNX_GRU.pyを実行すると、ONNXモデルが訓練pythonファイル(ONNX_GRU.py)と同じフォルダに生成されます。このONNXモデルは、EAから呼び出すためにMQL5 Filesフォルダに保存する必要があります。

こうしてEAが記事に追加されます。


これでストラテジーテスターや取引でモデルをテストできます。


LSTMとGRUの比較トップ


GRUとLSTMの比較

LSTMセルはセル状態を維持し、そのセル状態から読み書きします。これは、入力とセル状態の値に応じて、セル状態への値の読み出し、書き込み、およびセル状態からの値の出力のプロセスを制御する4つのゲートを包含します。初期ゲートは、隠された状態が忘れるべき情報を指示します。後続のゲートは、書き込むべきセル状態のセグメントを特定する責任があります。第3のゲートは、刻まれる内容を決定します。最後に、最後のゲートはセル状態から情報を取り出し、出力を生成します。

LSTM


GRUセルはLSTMセルと似ていますが、いくつかの重要な違いがあります。第一に、LSTMセル設計における隠れ状態の機能はセル状態によって仮定されるため、隠れ状態を欠いています。その後、セル状態が何を忘れるか、セル状態のどの部分に書き込むかを決定するプロセスが、特異なゲートに統合されます。そして、消去されたセル状態の部分だけが刻まれます。最後に、セルの状態全体が出力として機能し、セルの状態から選択的に読み出して出力を生成するLSTMセルから逸脱します。これらの修正により、LSTMと比較して、より少ないパラメータでより簡単な設計が可能になりました。しかし、パラメータの減少は表現力の低下につながる可能性があります。

GRU


実験的比較

GRU

# Split the data into training and testing sets
x_train, x_test, y_train, y_test = train_test_split(x_features, y_target, test_size=0.2, shuffle=False)


# Standardize the features StandardScaler()
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(x_train)
X_test_scaled = scaler.transform(x_test)

scaler_y = StandardScaler()
y_train_scaled = scaler_y.fit_transform(np.array(y_train).reshape(-1, 1))
y_test_scaled = scaler_y.transform(np.array(y_test).reshape(-1, 1))

# Define parameters

learning_rate = 0.001
dropout_rate = 0.5
batch_size = 1024
layer_1 = 256
epochs = 1000
k_reg = 0.001
patience = 10
factor = 0.5
n_splits = 5  # Number of K-fold Splits
window_size = days  # Ajusta esto según tus necesidades

def create_windows(data, window_size):
    return [data[i:i + window_size] for i in range(len(data) - window_size + 1)]

custom_optimizer = Adam(learning_rate=learning_rate)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=factor, patience=patience, min_lr=1e-26)

def build_model(input_shape, k_reg):
    model = Sequential()
    
    layer_sizes = [ 512,1024,512, 256, 128, 64]
    model.add(Dense(layer_1, kernel_regularizer=l2(k_reg), input_shape=input_shape))
    for size in layer_sizes:
        model.add(Dense(size, kernel_regularizer=l2(k_reg)))
        model.add(BatchNormalization())
        model.add(Activation('relu'))
        model.add(Dropout(dropout_rate))

    model.add(Dense(1, activation='linear'))
    model.add(BatchNormalization())
    model.compile(optimizer=custom_optimizer, loss='mse', metrics=[rmse()])
    
    return model



# Define EarlyStopping callback
early_stopping = EarlyStopping(monitor='val_loss', patience=patience, restore_best_weights=True)

# KFold Cross Validation
kfold = KFold(n_splits=n_splits, shuffle=True, random_state=42)
history = []
loss_per_epoch = []
val_loss_per_epoch = []

for train, val in kfold.split(X_train_scaled, y_train_scaled):
    x_train_fold, x_val_fold = X_train_scaled[train], X_train_scaled[val]
    y_train_fold, y_val_fold = y_train_scaled[train], y_train_scaled[val]
    
    # Flatten the input data
    x_train_fold_flat = x_train_fold.flatten()
    x_val_fold_flat = x_val_fold.flatten()

    # Create windows for training and validation
    x_train_windows = create_windows(x_train_fold_flat, window_size)
    x_val_windows = create_windows(x_val_fold_flat, window_size)

    # Rebuild the model
    model = build_model((window_size, 1), k_reg)

    # Create a new optimizer
    custom_optimizer = Adam(learning_rate=learning_rate)
    
    # Recompile the model
    model.compile(optimizer=custom_optimizer, loss='mse', metrics=[rmse()])
    
    hist = model.fit(
        np.array(x_train_windows), y_train_fold[window_size - 1:],
        epochs=epochs,
        validation_data=(np.array(x_val_windows), y_val_fold[window_size - 1:]),
        batch_size=batch_size,
        callbacks=[reduce_lr, early_stopping]
    )
    history.append(hist)
    loss_per_epoch.append(hist.history['loss'])
    val_loss_per_epoch.append(hist.history['val_loss'])




mean_loss_per_epoch = [np.mean(loss) for loss in loss_per_epoch]
val_mean_loss_per_epoch = [np.mean(val_loss) for val_loss in val_loss_per_epoch]

print("mean_loss_per_epoch", mean_loss_per_epoch)
print("unique_min_val_loss_per_epoch", val_loss_per_epoch)

# Create a DataFrame to display the mean loss values
epoch_df = pd.DataFrame({
    'Epoch': range(1, len(mean_loss_per_epoch) + 1),
    'Train Loss': mean_loss_per_epoch,
    'Validation Loss': val_loss_per_epoch
})



LSTM

model = Sequential()
model.add(Conv1D(filters=256, kernel_size=2, activation='relu',padding = 'same',input_shape=(inp_history_size,1)))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(100, return_sequences = True))
model.add(Dropout(0.3))
model.add(LSTM(100, return_sequences = False))
model.add(Dropout(0.3))
model.add(Dense(units=1, activation = 'sigmoid'))
model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()])


スライディングウィンドウ評価


LSTMとGRUを比較するための.pyと、交差検証の.pyは残してあります。

また、ONNXモデルを作るためのGRUのシンプルな.pyも残してあります。

GRUのシンプルなモデルを使えば、2024年1月までの結果はこうなります。

バックテスト

グラフ


結論と今後の課題

この比較は、どちらのモデルを使用するかを決定する上で極めて重要です。あるいは、両方をスタッキングしたりオーバーレイして使用することも考えられます。このアプローチにより、バッチサイズや層構成に固有の違いがあるにもかかわらず、採用したモデルから本質的な情報を抽出することができます。同じような結果で再開するのであれば、GRUの方がはるかに速いです。

今後の研究の一環として、パフォーマンスを向上させる可能性のある、各セルタイプに合わせた様々なカーネル初期化器や回帰的初期化器を探求することは有益でしょう。

ONNXモデルと取引するための良いアプローチは、同じEAに両方を統合することです。mql5でONNXモデルをアンサンブルする方法の例


結論

GRUのようなモデルは良い結果を得ることができるし、頑丈に見えます。私がこの記事を書くのを楽しんだのと同じように、読者がこの記事を楽しんでいただけたなら幸いです。また、GRUとLSTMモデルの比較を見ました。その.pyコードを使用して、(入力データ数を考慮して)エポックを停止するタイミングを知ることができます。


免責事項

過去の実績は将来の結果を示すものではありません。


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/14113

MetaTrader 5用のMQTTクライアントの開発:TDDアプローチ(第6回) MetaTrader 5用のMQTTクライアントの開発:TDDアプローチ(第6回)
この記事は、MQTT 5.0プロトコル用のネイティブMQL5クライアントの開発ステップを説明する連載の第6部です。今回は、私たちの最初のリファクタリングにおける主な変更点、私たちがどのようにしてパケット構築クラスのための実行可能な設計図にたどり着いたか、どのようにPUBLISHとPUBACKパケットを構築しているか、そしてPUBACK Reason Codeの背後にあるセマンティクスについてコメントします。
プログラミングパラダイムについて(第2部):オブジェクト指向アプローチによるプライスアクションエキスパートアドバイザーの開発 プログラミングパラダイムについて(第2部):オブジェクト指向アプローチによるプライスアクションエキスパートアドバイザーの開発
オブジェクト指向プログラミングのパラダイムとMQL5コードへの応用について学びます。この第2回目の記事では、オブジェクト指向プログラミングの具体的な内容をより深く掘り下げ、実践的な例を通して実体験を提供します。EMA指標とローソク足価格データを使用した、手続き型プライスアクションエキスパートアドバイザー(EA)をオブジェクト指向コードに変換する方法を学びます。
MQL5の高度な変数とデータ型 MQL5の高度な変数とデータ型
変数とデータ型は、MQL5プログラミングだけでなく、どのプログラミング言語でも非常に重要なトピックです。MQL5の変数とデータ型は、単純なものと高度なものに分類できます。単純なものについては前回の記事ですでに述べたので、今回は高度なものを特定し、それについて学ぶことにします。
MQL5における修正グリッドヘッジEA(第3部):シンプルヘッジ戦略の最適化(I) MQL5における修正グリッドヘッジEA(第3部):シンプルヘッジ戦略の最適化(I)
この第3部では、以前に開発したシンプルヘッジとシンプルグリッドエキスパートアドバイザー(EA)を再考します。最適な戦略の使用を目指し、数学的分析と総当り攻撃アプローチを通じてシンプルヘッジEAを改良することに焦点を移します。戦略の数学的最適化について深く掘り下げ、後の回でコーディングに基づく最適化を探求するための舞台を整えます。