
データサイエンスとML(第32回):AIモデルを最新の状態に保つ、オンライン学習
内容
- オンライン学習について
- オンライン学習のメリット
- MetaTrader 5のオンライン学習インフラストラクチャ
- 訓練と導入のプロセスの自動化
- ディープラーニングAIモデルのオンライン学習
- 増分機械学習
- 結論
オンライン学習について
オンライン機械学習とは、モデルがリアルタイムでデータストリームから継続的に学習する機械学習手法です。これは、時間の経過とともに予測アルゴリズムを適応させ、新しいデータが追加されるたびにモデルが更新される動的なプロセスです。この手法は、取引データのような急速に変化するデータが豊富な環境において、タイムリーで正確な予測を提供できるため、非常に重要です。
取取引データを扱う際、モデルをいつ、どの頻度で更新すべきかを判断するのは常に難しい課題です。 たとえば、過去1年間のビットコインデータで訓練されたAIモデルがある場合、最近の情報が外れ値と見なされる可能性があります。特に、この暗号通貨が先週、新たな最高値を更新したことを考慮すると、その影響は大きいでしょう。
通常、外国為替市場の通貨ペアは歴史的に特定の範囲内で上下動する傾向がありますが、NASDAQ100やS&P500といった指数、さらには個別株式は、長期的に上昇し続け、新たな最高値を更新する傾向があります。
オンライン学習は、過去の訓練データが陳腐化することを防ぐだけでなく、市場の最新動向に影響を与える可能性のある新しい情報を取り入れ、モデルを常に最新の状態に保つためにも重要です。
オンライン学習のメリット
- 適応性
サイクリストが走りながら学習するように、オンライン機械学習もデータの新しいパターンに適応し、時間の経過とともにパフォーマンスを向上させることができます。 - スケーラビリティ
一部のモデルでは、オンライン学習手法によってデータを1つずつ順番に処理することができます。このため、計算リソースが限られている環境でも安全に運用できる手法となり、最終的にはビッグデータを活用するモデルのスケーリングにも貢献します。 - リアルタイム予測
バッチ学習では実装が完了する頃にはデータが古くなっている可能性がありますが、オンライン学習はリアルタイムのインサイトを提供し、多くの取引アプリケーションにおいて重要な役割を果たします。 - 効率
増分学習により、モデルは継続的に学習・更新されるため、より迅速かつコスト効率の高い訓練プロセスを実現できます。
この手法の利点を理解したところで、MetaTrader 5で効果的なオンライン学習を実現するために必要なインフラストラクチャについて見ていきましょう。
MetaTrader 5のオンライン学習インフラストラクチャ
私たちの最終目標は、MetaTrader 5での取引にAIモデルを活用することです。そのため、Pythonベースのアプリケーションで一般的に使用されるものとは異なるオンライン学習インフラストラクチャが求められます。
手順01:Pythonクライアント
Pythonクライアント(スクリプト)内で、MetaTrader 5から受信した取引データに基づいてAIモデルを構築します。
MetaTrader 5(Pythonライブラリ)を使用して、プラットフォームを初期化することから始めます。
import pandas as pd import numpy as np import MetaTrader5 as mt5 from datetime import datetime if not mt5.initialize(): # Initialize the MetaTrader 5 platform print("initialize() failed") mt5.shutdown()
MetaTrader 5プラットフォームを初期化した後は、copy_rates_from_posメソッドを使用してそこから取引情報を取得できます。
def getData(start = 1, bars = 1000): rates = mt5.copy_rates_from_pos("EURUSD", mt5.TIMEFRAME_H1, start, bars) if len(rates) < bars: # if the received information is less than specified print("Failed to copy rates from MetaTrader 5, error = ",mt5.last_error()) # create a pnadas DataFrame out of the obtained data df_rates = pd.DataFrame(rates) return df_rates
取得した情報を出力して確認することができます。
print("Trading info:\n",getData(1, 100)) # get 100 bars starting at the recent closed bar
以下が出力です。
time open high low close tick_volume spread real_volume 0 1731351600 1.06520 1.06564 1.06451 1.06491 1688 0 0 1 1731355200 1.06491 1.06519 1.06460 1.06505 1607 0 0 2 1731358800 1.06505 1.06573 1.06495 1.06512 1157 0 0 3 1731362400 1.06512 1.06564 1.06512 1.06557 1112 0 0 4 1731366000 1.06557 1.06579 1.06553 1.06557 776 0 0 .. ... ... ... ... ... ... ... ... 95 1731693600 1.05354 1.05516 1.05333 1.05513 5125 0 0 96 1731697200 1.05513 1.05600 1.05472 1.05486 3966 0 0 97 1731700800 1.05487 1.05547 1.05386 1.05515 2919 0 0 98 1731704400 1.05515 1.05522 1.05359 1.05372 2651 0 0 99 1731708000 1.05372 1.05379 1.05164 1.05279 2977 0 0 [100 rows x 8 columns]
copy_rates_from_posメソッドを使用すると、インデックス1に配置された最近閉じたバーにアクセスできるため、固定された日付を使用してアクセスする場合に比べて非常に便利です。
インデックス1にあるバーからコピーすることで、常に、最近クローズしたバーから、必要な指定数のバーまでの情報を取得できることが保証されます。
この情報を受け取った後、このデータに対して一般的な機械学習をおこなうことができます。
モデルごとに個別のファイルを作成し、各モデルを個別のファイルに配置することで、すべての主要なプロセスと関数が実装されているmain.pyファイルでこれらのモデルを簡単に呼び出すことができます。
[catboost_models.py]
from catboost import CatBoostClassifier from sklearn.metrics import accuracy_score from onnx.helper import get_attribute_value from skl2onnx import convert_sklearn, update_registered_converter from sklearn.pipeline import Pipeline from skl2onnx.common.shape_calculator import ( calculate_linear_classifier_output_shapes, ) # noqa from skl2onnx.common.data_types import ( FloatTensorType, Int64TensorType, guess_tensor_type, ) from skl2onnx._parse import _apply_zipmap, _get_sklearn_operator_name from catboost.utils import convert_to_onnx_object # Example initial data (X_initial, y_initial are your initial feature matrix and target) class CatBoostClassifierModel(): def __init__(self, X_train, X_test, y_train, y_test): self.X_train = X_train self.X_test = X_test self.y_train = y_train self.y_test = y_test self.model = None def train(self, iterations=100, depth=6, learning_rate=0.1, loss_function="CrossEntropy", use_best_model=True): # Initialize the CatBoost model params = { "iterations": iterations, "depth": depth, "learning_rate": learning_rate, "loss_function": loss_function, "use_best_model": use_best_model } self.model = Pipeline([ # wrap a catboost classifier in sklearn pipeline | good practice (not necessary tho :)) ("catboost", CatBoostClassifier(**params)) ]) # Testing the model self.model.fit(X=self.X_train, y=self.y_train, catboost__eval_set=(self.X_test, self.y_test)) y_pred = self.model.predict(self.X_test) print("Model's accuracy on out-of-sample data = ",accuracy_score(self.y_test, y_pred)) # a function for saving the trained CatBoost model to ONNX format def to_onnx(self, model_name): update_registered_converter( CatBoostClassifier, "CatBoostCatBoostClassifier", calculate_linear_classifier_output_shapes, self.skl2onnx_convert_catboost, parser=self.skl2onnx_parser_castboost_classifier, options={"nocl": [True, False], "zipmap": [True, False, "columns"]}, ) model_onnx = convert_sklearn( self.model, "pipeline_catboost", [("input", FloatTensorType([None, self.X_train.shape[1]]))], target_opset={"": 12, "ai.onnx.ml": 2}, ) # And save. with open(model_name, "wb") as f: f.write(model_onnx.SerializeToString())
このCatBoostモデルの実装の詳細については、こちらの記事を参照してください。 ここではCatBoostモデルを例として使用しましたが、お好みのモデルを自由に使用してください。
これで、catboostモデルの初期化、訓練、保存に役立つこのクラスができました。このモデルをmain.pyファイルに実装してみましょう。
[main.py]
ここでも、MetaTrader 5デスクトップアプリからデータを受信することから始めます。
data = getData(start=1, bars=1000)
CatBoostモデルをよく見ると、それが分類モデルであることがわかります。この分類器の目的変数はまだないので、作成してみましょう。
# Preparing the target variable data["future_open"] = data["open"].shift(-1) # shift one bar into the future data["future_close"] = data["close"].shift(-1) target = [] for row in range(data.shape[0]): if data["future_close"].iloc[row] > data["future_open"].iloc[row]: # bullish signal target.append(1) else: # bearish signal target.append(0) data["target"] = target # add the target variable to the dataframe data = data.dropna() # drop empty rows
2D配列「X」から、ゼロ値が多数含まれるすべての将来の変数とその他の機能を削除し、target変数を1D配列「y」に割り当てることができます。
X = data.drop(columns = ["spread","real_volume","future_close","future_open","target"]) y = data["target"]
次に、情報を訓練サンプルと検証サンプルに分割し、市場のデータを使用してCatBoostモデルを初期化し、訓練します。
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, random_state=42) catboost_model = catboost_models.CatBoostClassifierModel(X_train, X_test, y_train, y_test) catboost_model.train()
最後に、このモデルをONNX形式で MetaTrader 5のCommonディレクトリに保存します。
手順02:Commonフォルダ
MetaTrader 5 Pythonを使用すると、Commonのパスに関する情報を取得できます。
terminal_info_dict = mt5.terminal_info()._asdict()
common_path = terminal_info_dict["commondata_path"]
このPythonクライアントから訓練されたすべてのAIモデルをここに保存します。
MQL5を使用してCommonフォルダにアクセスする場合、通常はCommonフォルダの下にあるFilesサブフォルダを参照します。MQL5の観点からこれらのファイルに簡単にアクセスできるようにするには、そのサブフォルダにモデルを保存する必要があります。
# Save models in a specific location under the common parent folder models_path = os.path.join(common_path, "Files") if not os.path.exists(models_path): #if the folder exists os.makedirs(models_path) # Create the folder if it doesn't exist catboost_model.to_onnx(model_name=os.path.join(models_path, "catboost.H1.onnx"))
最後に、これらすべてのコード行を1つの関数にまとめて、必要なときにいつでもさまざまなプロセスを簡単に実行できるようにする必要があります。
def trainAndSaveCatBoost(): data = getData(start=1, bars=1000) # Check if we were able to receive some data if (len(data)<=0): print("Failed to obtain data from Metatrader5, error = ",mt5.last_error()) mt5.shutdown() # Preparing the target variable data["future_open"] = data["open"].shift(-1) # shift one bar into the future data["future_close"] = data["close"].shift(-1) target = [] for row in range(data.shape[0]): if data["future_close"].iloc[row] > data["future_open"].iloc[row]: # bullish signal target.append(1) else: # bearish signal target.append(0) data["target"] = target # add the target variable to the dataframe data = data.dropna() # drop empty rows X = data.drop(columns = ["spread","real_volume","future_close","future_open","target"]) y = data["target"] X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, random_state=42) catboost_model = catboost_models.CatBoostClassifierModel(X_train, X_test, y_train, y_test) catboost_model.train() # Save models in a specific location under the common parent folder models_path = os.path.join(common_path, "Files") if not os.path.exists(models_path): #if the folder exists os.makedirs(models_path) # Create the folder if it doesn't exist catboost_model.to_onnx(model_name=os.path.join(models_path, "catboost.H1.onnx"))
それでは、この関数を呼び出して、何がおこなわれるか見てみましょう。
trainAndSaveCatBoost()
exit() # stop the script
結果
0: learn: 0.6916088 test: 0.6934968 best: 0.6934968 (0) total: 163ms remaining: 16.1s 1: learn: 0.6901684 test: 0.6936087 best: 0.6934968 (0) total: 168ms remaining: 8.22s 2: learn: 0.6888965 test: 0.6931576 best: 0.6931576 (2) total: 175ms remaining: 5.65s 3: learn: 0.6856524 test: 0.6927187 best: 0.6927187 (3) total: 184ms remaining: 4.41s 4: learn: 0.6843646 test: 0.6927737 best: 0.6927187 (3) total: 196ms remaining: 3.72s ... ... ... 96: learn: 0.5992419 test: 0.6995323 best: 0.6927187 (3) total: 915ms remaining: 28.3ms 97: learn: 0.5985751 test: 0.7002011 best: 0.6927187 (3) total: 924ms remaining: 18.9ms 98: learn: 0.5978617 test: 0.7003299 best: 0.6927187 (3) total: 928ms remaining: 9.37ms 99: learn: 0.5968786 test: 0.7010596 best: 0.6927187 (3) total: 932ms remaining: 0us bestTest = 0.6927187021 bestIteration = 3 Shrink model to first 4 iterations. Model's accuracy on out-of-sample data = 0.5
.onnxファイルはCommon\Filesの下になります。
手順03:MetaTrader 5
ここで、MetaTrader 5で、ONNX形式で保存されたこのモデルをロードする必要があります。
まず、このタスクに役立つライブラリをインポートします。
[Online Learning Catboost.mq5]
#include <CatBoost.mqh> CCatBoost *catboost; input string model_name = "catboost.H1.onnx"; input string symbol = "EURUSD"; input ENUM_TIMEFRAMES timeframe = PERIOD_H1; string common_path;
Oninit関数内で最初におこなうことは、Commonフォルダにファイルが存在するかどうかを確認することです。存在しない場合は、モデルが訓練されていないことを示している可能性があります。
その後、ONNX_COMMON_FOLDERフラグを渡してONNXモデルを初期化し、Commonフォルダからモデルを明示的に読み込みます。
int OnInit() { //--- Check if the model file exists if (!FileIsExist(model_name, FILE_COMMON)) { printf("%s Onnx file doesn't exist",__FUNCTION__); return INIT_FAILED; } //--- Initialize a catboost model catboost = new CCatBoost(); if (!catboost.Init(model_name, ONNX_COMMON_FOLDER)) { printf("%s failed to initialize the catboost model, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } //--- }
この読み込まれたモデルを使用して予測をおこなうには、Pythonスクリプトに戻って、いくつかの特徴量が削除された後に訓練に使用された特徴量を確認します。
MQL5では同じ特徴量を同じ順序で収集する必要があります。
[main.pyファイルのPythonコード]
X = data.drop(columns = ["spread","real_volume","future_close","future_open","target"]) y = data["target"] print(X.head())
結果
time open high low close tick_volume 0 1726772400 1.11469 1.11584 1.11453 1.11556 3315 1 1726776000 1.11556 1.11615 1.11525 1.11606 2812 2 1726779600 1.11606 1.11680 1.11606 1.11656 2309 3 1726783200 1.11656 1.11668 1.11590 1.11622 2667 4 1726786800 1.11622 1.11644 1.11605 1.11615 1166
ここで、OnTick関数内でこの情報を取得し、クラスを予測するpredict_bin関数を呼び出します。
この関数は、Pythonクライアントで準備した目的変数に表示された2つのクラス( 0(強気)、1(弱気))を予測します。
void OnTick() { //--- MqlRates rates[]; CopyRates(symbol, timeframe, 1, 1, rates); //copy the recent closed bar information vector x = { (double)rates[0].time, rates[0].open, rates[0].high, rates[0].low, rates[0].close, (double)rates[0].tick_volume}; Comment(TimeCurrent(),"\nPredicted signal: ",catboost.predict_bin(x)==0?"Bearish":"Bullish");// if the predicted signal is 0 it means a bearish signal, otherwise it is a bullish signal }
結果
訓練と導入のプロセスの自動化
MetaTrader 5でモデルを訓練して導入することはできましたが、これは本来の目的ではありません。私たちの主な目標は、プロセス全体を自動化することです。
Python仮想環境内に、スケジュールライブラリをインストールする必要があります。
$ pip install schedule
この小さなモジュールは、特定の関数を実行するスケジュールを設定するのに役立ちます。データの収集、訓練、モデルの保存をおこなうコードをすでに1つの関数にラップしているので、この関数が1分ごとに呼び出されるようにスケジュールしましょう。
schedule.every(1).minute.do(trainAndSaveCatBoost) #schedule catboost training # Keep the script running to execute the scheduled tasks while True: schedule.run_pending() time.sleep(60) # Wait for 1 minute before checking again
このスケジュール機能はうまく機能します :)
メインのエキスパートアドバイザー(EA)では、EAがCommonディレクトリからモデルをロードするタイミングと頻度もスケジュールします。これにより、自動売買ロボットのモデルが効果的に更新されます。
OnTimer関数も使用できますが、これも非常にうまく機能します :)
int OnInit() { //--- Check if the model file exists .... //--- Initialize a catboost model .... //--- if (!EventSetTimer(60)) //Execute the OnTimer function after every 60 seconds { printf("%s failed to set the event timer, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- if (CheckPointer(catboost) != POINTER_INVALID) delete catboost; } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- .... } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer(void) { if (CheckPointer(catboost) != POINTER_INVALID) delete catboost; //--- Load the new model after deleting the prior one from memory catboost = new CCatBoost(); if (!catboost.Init(model_name, ONNX_COMMON_FOLDER)) { printf("%s failed to initialize the catboost model, error = %d",__FUNCTION__,GetLastError()); return; } printf("%s New model loaded",TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); }
結果
HO 0 13:14:00.648 Online Learning Catboost (EURUSD,D1) 2024.11.18 12:14 New model loaded FK 0 13:15:55.388 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:15 New model loaded JG 0 13:16:55.380 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:16 New model loaded MP 0 13:17:55.376 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:17 New model loaded JM 0 13:18:55.377 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:18 New model loaded PF 0 13:19:55.368 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:19 New model loaded CR 0 13:20:55.387 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:20 New model loaded NO 0 13:21:55.377 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:21 New model loaded LH 0 13:22:55.379 Online Learning Catboost (GBPUSD,H1) 2024.11.18 12:22 New model loaded
ここまで、訓練プロセスをスケジュールし、MetaTrader 5のEAと新しいモデルを同期させる方法を見てきました。このプロセスは、シンプルな機械学習手法を展開する場合は比較的容易ですが、再帰型ニューラルネットワーク(RNN)などのディープラーニングモデルを扱う場合は、より困難になる可能性があります。これは、RNNがSklearnパイプラインに収めることができず、さまざまな機械学習モデルを扱う際に便利なワークフローが使えなくなるためです。
再帰型ニューラルネットワークの特殊な形式であゲート付き回帰型ユニット(GRU)を操作するときに、この手法をどのように適用できるかを見てみましょう。
ディープラーニングAIモデルのオンライン学習
Pythonクライアントの場合
GRUClassifierクラス内で典型的な機械学習を適用します。GRUの詳細については、こちらの記事を参照してください。
モデルを訓練した後、それをONNXに保存します。今回は、StandardScalerの情報もバイナリ ファイルに保存します。これにより、Pythonで現在使用しているのと同じ方法で、MQL5内でも新しいデータを正しく正規化できるようになります。
[gru_models.py]
import numpy as np import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import GRU, Dense, Input, Dropout from keras.callbacks import EarlyStopping from keras.optimizers import Adam import tf2onnx class GRUClassifier(): def __init__(self, time_step, X_train, X_test, y_train, y_test): self.X_train = X_train self.X_test = X_test self.y_train = y_train self.y_test = y_test self.model = None self.time_step = time_step self.classes_in_y = np.unique(self.y_train) def train(self, learning_rate=0.001, layers=2, neurons = 50, activation="relu", batch_size=32, epochs=100, loss="binary_crossentropy", verbose=0): self.model = Sequential() self.model.add(Input(shape=(self.time_step, self.X_train.shape[2]))) self.model.add(GRU(units=neurons, activation=activation)) # input layer for layer in range(layers): # dynamically adjusting the number of hidden layers self.model.add(Dense(units=neurons, activation=activation)) self.model.add(Dropout(0.5)) self.model.add(Dense(units=len(self.classes_in_y), activation='softmax', name='output_layer')) # the output layer # Compile the model adam_optimizer = Adam(learning_rate=learning_rate) self.model.compile(optimizer=adam_optimizer, loss=loss, metrics=['accuracy']) early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) history = self.model.fit(self.X_train, self.y_train, epochs=epochs, batch_size=batch_size, validation_data=(self.X_test, self.y_test), callbacks=[early_stopping], verbose=verbose) val_loss, val_accuracy = self.model.evaluate(self.X_test, self.y_test, verbose=verbose) print("Gru accuracy on validation sample = ",val_accuracy) def to_onnx(self, model_name, standard_scaler): # Convert the Keras model to ONNX spec = (tf.TensorSpec((None, self.time_step, self.X_train.shape[2]), tf.float16, name="input"),) self.model.output_names = ['outputs'] onnx_model, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=13) # Save the ONNX model to a file with open(model_name, "wb") as f: f.write(onnx_model.SerializeToString()) # Save the mean and scale parameters to binary files standard_scaler.mean_.tofile(f"{model_name.replace('.onnx','')}.standard_scaler_mean.bin") standard_scaler.scale_.tofile(f"{model_name.replace('.onnx','')}.standard_scaler_scale.bin")
main.pyファイル内に、GRUモデルで実行したいすべての処理を担当する関数を作成します。
def trainAndSaveGRU(): data = getData(start=1, bars=1000) # Preparing the target variable data["future_open"] = data["open"].shift(-1) data["future_close"] = data["close"].shift(-1) target = [] for row in range(data.shape[0]): if data["future_close"].iloc[row] > data["future_open"].iloc[row]: target.append(1) else: target.append(0) data["target"] = target data = data.dropna() # Check if we were able to receive some data if (len(data)<=0): print("Failed to obtain data from Metatrader5, error = ",mt5.last_error()) mt5.shutdown() X = data.drop(columns = ["spread","real_volume","future_close","future_open","target"]) y = data["target"] X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, shuffle=False) ########### Preparing data for timeseries forecasting ############### time_step = 10 scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) x_train_seq, y_train_seq = create_sequences(X_train, y_train, time_step) x_test_seq, y_test_seq = create_sequences(X_test, y_test, time_step) ###### One HOt encoding ####### y_train_encoded = to_categorical(y_train_seq) y_test_encoded = to_categorical(y_test_seq) gru = gru_models.GRUClassifier(time_step=time_step, X_train= x_train_seq, y_train= y_train_encoded, X_test= x_test_seq, y_test= y_test_encoded ) gru.train( batch_size=64, learning_rate=0.001, activation = "relu", epochs=1000, loss="binary_crossentropy", layers = 2, neurons = 50, verbose=1 ) # Save models in a specific location under the common parent folder models_path = os.path.join(common_path, "Files") if not os.path.exists(models_path): #if the folder exists os.makedirs(models_path) # Create the folder if it doesn't exist gru.to_onnx(model_name=os.path.join(models_path, "gru.H1.onnx"), standard_scaler=scaler)
最後に、CatBoost関数をスケジュールしたのと同様に、このtrainAndSaveGRU関数を呼び出す頻度をスケジュールできます。
schedule.every(1).minute.do(trainAndSaveGRU) #scheduled GRU training
結果
Epoch 1/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 7s 87ms/step - accuracy: 0.4930 - loss: 0.6985 - val_accuracy: 0.5000 - val_loss: 0.6958 Epoch 2/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - accuracy: 0.4847 - loss: 0.6957 - val_accuracy: 0.4931 - val_loss: 0.6936 Epoch 3/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - accuracy: 0.5500 - loss: 0.6915 - val_accuracy: 0.4897 - val_loss: 0.6934 Epoch 4/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - accuracy: 0.4910 - loss: 0.6923 - val_accuracy: 0.4690 - val_loss: 0.6938 Epoch 5/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - accuracy: 0.5538 - loss: 0.6910 - val_accuracy: 0.4897 - val_loss: 0.6935 Epoch 6/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 20ms/step - accuracy: 0.5037 - loss: 0.6953 - val_accuracy: 0.4931 - val_loss: 0.6937 Epoch 7/1000 ... ... ... 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 22ms/step - accuracy: 0.4964 - loss: 0.6952 - val_accuracy: 0.4793 - val_loss: 0.6940 Epoch 20/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - accuracy: 0.5285 - loss: 0.6914 - val_accuracy: 0.4793 - val_loss: 0.6949 Epoch 21/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - accuracy: 0.5224 - loss: 0.6935 - val_accuracy: 0.4966 - val_loss: 0.6942 Epoch 22/1000 11/11 ━━━━━━━━━━━━━━━━━━━━ 0s 21ms/step - accuracy: 0.5009 - loss: 0.6936 - val_accuracy: 0.5103 - val_loss: 0.6933 10/10 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - accuracy: 0.4925 - loss: 0.6938 Gru accuracy on validation sample = 0.5103448033332825
MetaTrader 5
まず、GRUモデルと標準スケーラーをロードするタスクを支援するライブラリをロードします。
#include <preprocessing.mqh> #include <GRU.mqh> CGRU *gru; StandardizationScaler *scaler; //--- Arrays for temporary storage of the scaler values double scaler_mean[], scaler_std[]; input string model_name = "gru.H1.onnx"; string mean_file; string std_file;
OnInit関数で最初におこなうことは、スケーラーバイナリファイルの名前を取得することです。これらのファイルを作成するときに、同じ原則を適用しました。
string base_name__ = model_name; if (StringReplace(base_name__,".onnx","")<0) { printf("%s Failed to obtain the parent name for the scaler files, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } mean_file = base_name__ + ".standard_scaler_mean.bin"; std_file = base_name__ + ".standard_scaler_scale.bin";
最後に、Commonフォルダから ONNX形式のGRUモデルをロードし、scaler_mean配列とscaler_std配列に値を割り当てることで、バイナリ形式のスケーラーファイルも読み取ります。
int OnInit() { string base_name__ = model_name; if (StringReplace(base_name__,".onnx","")<0) //we followed this same file patterns while saving the binary files in python client { printf("%s Failed to obtain the parent name for the scaler files, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } mean_file = base_name__ + ".standard_scaler_mean.bin"; std_file = base_name__ + ".standard_scaler_scale.bin"; //--- Check if the model file exists if (!FileIsExist(model_name, FILE_COMMON)) { printf("%s Onnx file doesn't exist",__FUNCTION__); return INIT_FAILED; } //--- Initialize the GRU model from the common folder gru = new CGRU(); if (!gru.Init(model_name, ONNX_COMMON_FOLDER)) { printf("%s failed to initialize the gru model, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } //--- Read the scaler files if (!readArray(mean_file, scaler_mean) || !readArray(std_file, scaler_std)) { printf("%s failed to read scaler information",__FUNCTION__); return INIT_FAILED; } scaler = new StandardizationScaler(scaler_mean, scaler_std); //Load the scaler class by populating it with values //--- Set the timer if (!EventSetTimer(60)) { printf("%s failed to set the event timer, error = %d",__FUNCTION__,GetLastError()); return INIT_FAILED; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- if (CheckPointer(gru) != POINTER_INVALID) delete gru; if (CheckPointer(scaler) != POINTER_INVALID) delete scaler; }
OnTimer関数で、Commonフォルダからスケーラーファイルとモデルファイルを読み取るプロセスをスケジュールします。
void OnTimer(void) { //--- Delete the existing pointers in memory as the new ones are about to be created if (CheckPointer(gru) != POINTER_INVALID) delete gru; if (CheckPointer(scaler) != POINTER_INVALID) delete scaler; //--- if (!readArray(mean_file, scaler_mean) || !readArray(std_file, scaler_std)) { printf("%s failed to read scaler information",__FUNCTION__); return; } scaler = new StandardizationScaler(scaler_mean, scaler_std); gru = new CGRU(); if (!gru.Init(model_name, ONNX_COMMON_FOLDER)) { printf("%s failed to initialize the gru model, error = %d",__FUNCTION__,GetLastError()); return; } printf("%s New model loaded",TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); }
結果
II 0 14:49:35.920 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:49 New model loaded QP 0 14:50:35.886 Online Learning GRU (GBPUSD,H1) Initilaizing ONNX model... MF 0 14:50:35.919 Online Learning GRU (GBPUSD,H1) ONNX model Initialized IJ 0 14:50:35.919 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:50 New model loaded EN 0 14:51:35.894 Online Learning GRU (GBPUSD,H1) Initilaizing ONNX model... JD 0 14:51:35.913 Online Learning GRU (GBPUSD,H1) ONNX model Initialized EL 0 14:51:35.913 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:51 New model loaded NM 0 14:52:35.885 Online Learning GRU (GBPUSD,H1) Initilaizing ONNX model... KK 0 14:52:35.915 Online Learning GRU (GBPUSD,H1) ONNX model Initialized QQ 0 14:52:35.915 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:52 New model loaded DK 0 14:53:35.899 Online Learning GRU (GBPUSD,H1) Initilaizing ONNX model... HI 0 14:53:35.935 Online Learning GRU (GBPUSD,H1) ONNX model Initialized MS 0 14:53:35.935 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:53 New model loaded DI 0 14:54:35.885 Online Learning GRU (GBPUSD,H1) Initilaizing ONNX model... IL 0 14:54:35.908 Online Learning GRU (GBPUSD,H1) ONNX model Initialized QE 0 14:54:35.908 Online Learning GRU (GBPUSD,H1) 2024.11.18 13:54 New model loaded
GRUモデルから予測を受け取るには、再帰型ニューラルネットワーク(RNN)がデータ内の時間的依存性を理解するのに役立つタイムステップ値を考慮する必要があります。
関数trainAndSaveGRU内では、タイムステップ値として10を使用しました。
def trainAndSaveGRU(): data = getData(start=1, bars=1000) .... .... time_step = 10
MQL5で最近閉じたバーから始めて、履歴から最後の10バー(タイムステップ)を収集してみましょう(これが本来のやり方です)。
input int time_step = 10;
void OnTick() { //--- MqlRates rates[]; CopyRates(symbol, timeframe, 1, time_step, rates); //copy the recent closed bar information vector classes = {0,1}; //Beware of how classes are organized in the target variable. use numpy.unique(y) to determine this array matrix X = matrix::Zeros(time_step, 6); // 6 columns for (int i=0; i<time_step; i++) { vector row = { (double)rates[i].time, rates[i].open, rates[i].high, rates[i].low, rates[i].close, (double)rates[i].tick_volume}; X.Row(row, i); } X = scaler.transform(X); //it's important to normalize the data Comment(TimeCurrent(),"\nPredicted signal: ",gru.predict_bin(X, classes)==0?"Bearish":"Bullish");// if the predicted signal is 0 it means a bearish signal, otherwise it is a bullish signal }
結果
増分機械学習
訓練方法に関しては、一部のモデルは他のモデルよりも優れており、堅牢です。インターネットで「オンライン機械学習」を検索すると、ほとんどの人が、これは、より大きな訓練目標のために、少量のデータをモデルに再訓練するプロセスであると言います。
これに関する問題は、多くのモデルが、少量のデータサンプルが与えられた場合にはサポートされないか、適切に機能しないことです。
CatBoostのような最新の機械学習技術は、増分学習を念頭に置いています。この訓練方法はオンライン学習に使用でき、データを小さなチャンクに分割して初期モデルに再訓練できるため、ビッグデータで作業するときに大量のメモリを節約できます。
def getData(start = 1, bars = 1000): rates = mt5.copy_rates_from_pos("EURUSD", mt5.TIMEFRAME_H1, start, bars) df_rates = pd.DataFrame(rates) return df_rates def trainIncrementally(): # CatBoost model clf = CatBoostClassifier( task_type="CPU", iterations=2000, learning_rate=0.2, max_depth=1, verbose=0, ) # Get big data big_data = getData(1, 10000) # Split into chunks of 1000 samples chunk_size = 1000 chunks = [big_data[i:i + chunk_size].copy() for i in range(0, len(big_data), chunk_size)] # Use .copy() here for i, chunk in enumerate(chunks): # Preparing the target variable chunk["future_open"] = chunk["open"].shift(-1) chunk["future_close"] = chunk["close"].shift(-1) target = [] for row in range(chunk.shape[0]): if chunk["future_close"].iloc[row] > chunk["future_open"].iloc[row]: target.append(1) else: target.append(0) chunk["target"] = target chunk = chunk.dropna() # Check if we were able to receive some data if (len(chunk)<=0): print("Failed to obtain chunk from Metatrader5, error = ",mt5.last_error()) mt5.shutdown() X = chunk.drop(columns = ["spread","real_volume","future_close","future_open","target"]) y = chunk["target"] X_train, X_val, y_train, y_val = train_test_split(X, y, train_size=0.8, random_state=42) if i == 0: # Initial training, training the model for the first time clf.fit(X_train, y_train, eval_set=(X_val, y_val)) y_pred = clf.predict(X_val) print(f"---> Acc score: {accuracy_score(y_pred=y_pred, y_true=y_val)}") else: # Incremental training by using the intial trained model clf.fit(X_train, y_train, init_model="model.cbm", eval_set=(X_val, y_val)) y_pred = clf.predict(X_val) print(f"---> Acc score: {accuracy_score(y_pred=y_pred, y_true=y_val)}") # Save the model clf.save_model("model.cbm") print(f"Chunk {i + 1}/{len(chunks)} processed and model saved.")
結果
---> Acc score: 0.555 Chunk 1/10 processed and model saved. ---> Acc score: 0.505 Chunk 2/10 processed and model saved. ---> Acc score: 0.55 Chunk 3/10 processed and model saved. ---> Acc score: 0.565 Chunk 4/10 processed and model saved. ---> Acc score: 0.495 Chunk 5/10 processed and model saved. ---> Acc score: 0.55 Chunk 6/10 processed and model saved. ---> Acc score: 0.555 Chunk 7/10 processed and model saved. ---> Acc score: 0.52 Chunk 8/10 processed and model saved. ---> Acc score: 0.455 Chunk 9/10 processed and model saved. ---> Acc score: 0.535 Chunk 10/10 processed and model saved.
モデルを段階的に構築しながら同じオンライン学習アーキテクチャに従い、最終モデルを MetaTrader 5 で使用するためにCommonフォルダに ONNX 形式で保存できます。
最後に
オンライン学習は、手動による介入を最小限に抑えながらモデルを継続的に更新するための優れたアプローチです。このインフラストラクチャを実装することで、モデルが最新の市場動向に沿った状態を維持し、新しい情報に迅速に適応できるようになります。ただし、オンライン学習では、モデルがデータの処理順序に非常に敏感になる場合があり、モデルと訓練情報が人間の視点から論理的に意味を成すかどうかを確認するために、人間による監視が必要になることが非常に多いことに注意することが重要です。すべてが期待どおりに進むようにするには、学習プロセスの自動化とモデルの定期的な評価の間で適切なバランスを見つける必要があります。
添付ファイルの表
インフラストラクチャ(フォルダ) | ファイル | 説明と使用法 |
---|---|---|
Pythonクライアント | catboost_models.py gru_models.py main.py incremental_learning.py | CatBoostモデルのファイル GRUモデルのファイル すべてをまとめるためのメインのPythonファイル CatBoostモデルの増分学習の実装 |
Commonフォルダ | catboost.H1.onnx gru.H1.onnx gru.H1.standard_scaler_mean.bin gru.H1.standard_scaler_scale.bin | ONNX形式のすべてのAIモデルとバイナリ形式のスケーラーファイルを含むフォルダ |
MetaTrader 5 (MQL5) | Experts\Online Learning Catboost.mq5 Experts\Online Learning GRU.mq5 Include\CatBoost.mqh Include\GRU.mqh Include\preprocessing.mqh | MQL5でのCatBoostモデルの実装 MQL5でのGRUモデルの実装 ONNX形式でCatBoostモデルを初期化および導入するためのライブラリファイル ONNX形式でGRUモデルを初期化および導入するためのライブラリファイル MLモデルの使用のためにデータを正規化するためのStandardScalerを含むライブラリファイル |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16390
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。




- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
オメガ・J・ミシグワ
この記事のために、あなたが使用しているpythonのバージョンを尋ねました。私はそれをインストールし、ライブラリの競合があります。
コンフリクトの原因は
ユーザは protobuf==3.20.3 を要求しました。
onnx 1.17.0 は protobuf>=3.20.2 に依存しています。
onnxconverter-common 1.14.0はprotobuf==3.20.2に依存します。
その後、提案されたとおりにバージョンを編集したところ、別のインストールエラーが発生しました。
これを解決するには、次のことを試してみてください:
1. 指定したパッケージバージョンの範囲を緩める。
2. パッケージのバージョンを削除して、pipが依存関係の衝突を解決できるようにする。
コンフリクトの原因
ユーザが指定した protobuf==3.20.2
onnx 1.17.0 は protobuf>=3.20.2 に依存しています。
onnxconverter-common 1.14.0 は protobuf==3.20.2 に依存します。
tensorboard 2.18.0 は protobuf!=4.24.0 および >=3.19.6 に依存します
tensorflow-intel 2.18.0 は protobuf!=4.21.0, !=4.21.1, !=4.21.2, !=4.21.3, !=4.21.4, !=4.21.5, <6.0.0dev and >=3.20.3 に依存します。
これを解決するには、次のことを試してみてください:
1. 指定したパッケージバージョンの範囲を緩める。
2. パッケージのバージョンを削除して、pip が依存関係の衝突を解決できるようにする。
より詳しい指示をお願いします。