OnnxRun関数の働き・役割
OnnxRun関数は、ONNXモデルを実行するために使用されます。この関数は、指定されたONNXセッションハンドルに従い、モデルの入出力を処理します。
具体的には、あらかじめ作成されたONNXモデルに対して入力データを与え、モデルによって生成された出力結果を取得する役割を果たします。ONNX(Open Neural Network Exchange)は、さまざまなニューラルネットワークフレームワーク間でモデルの共有や利用を容易にする形式であり、OnnxRun関数を使用することで、ONNX形式のモデルをMetaTrader5上で実行することが可能になります。
OnnxRun関数の引数について
bool OnnxRun(
long onnx_handle, // ONNXセッションハンドル
ulong flags, // 実行モードを説明するフラグ
... // モデルの入出力
);
第1引数: onnx_handle
onnx_handleは、ONNXセッションを識別するためのハンドルです。このハンドルは、事前にOnnxCreate関数やOnnxCreateFromBuffer関数を用いて作成されたONNXセッションオブジェクトを指します。モデルの実行に必要な準備やセッション管理を行うため、この引数は必須です。
第2引数: flags
flags
は、モデルの実行モードを制御するためのフラグです。フラグには、ENUM_ONNX_FLAGS列挙型を使用します。
指定するフラグは、ビット単位のOR演算を使用して複数指定することができます。
ulong flags = ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION;
第3引数以降: …
第3引数以降は、モデルの入出力データを示します。これらは、入力と出力の行列やベクトルであり、モデルに与えるデータとモデルから取得するデータの両方を指定します。入力データはモデルの処理対象であり、出力データにはモデルの予測結果が格納されます。
ENUM_ONNX_FLAGSについて
ENUM_ONNX_FLAGSは、OnnxRun関数で指定する実行モードを制御するためのフラグです。これにより、モデルの実行時に特定の動作や設定を行うことができます。以下では、それぞれの識別子について詳しく説明します。
ONNX_DEBUG_LOGS
ONNX_DEBUG_LOGSは、モデル実行中にデバッグログを出力するためのフラグです。モデルの実行に関する詳細な情報をエキスパートログに記録し、エラーや実行状況を確認するのに役立ちます。特に、モデルが期待通りに動作しない場合や、データの処理がうまくいかない場合に、このフラグを使用することで問題の特定が容易になります。
ONNX_NO_CONVERSION
ONNX_NO_CONVERSIONは、入力データや出力データの自動変換を無効にするフラグです。通常、ONNXモデル実行時には、データの形式や型が自動的に適合されますが、このフラグを指定するとユーザーが提供したデータをそのまま使用します。データ形式に依存する特定のモデルや、入力データを正確に保持したい場合に有効です。
ONNX_COMMON_FOLDER
ONNX_COMMON_FOLDERは、Common\Filesフォルダからモデルファイルを読み込む際に使用されるフラグです。このフラグを使用すると、指定されたフォルダからモデルを自動的に検索し、読み込みます。このフラグはFILE_COMMONフラグと同じ動作をします。モデルファイルを一元的に管理したい場合や、他のプロジェクトとモデルを共有する際に便利です。
各フラグを適切に使用することで、モデルの実行動作を柔軟に制御でき、さまざまな環境での実行をサポートします。
OnnxRun関数の戻り値について
OnnxRun関数は、ONNXモデルの実行結果に応じて「true」または「false」を返します。
- true: モデルの実行が成功した場合、この関数は「true」を返します。つまり、指定された入力データが正常に処理され、モデルから期待される出力データが得られたことを意味します。
- false: 何らかの理由でモデルの実行が失敗した場合、「false」を返します。この場合、エラーの詳細はGetLastError関数を使用して取得することができます。
例えば、入力データの形式が適合していない、セッションハンドルが無効、またはフラグ設定が正しくない場合など、さまざまな原因で失敗する可能性があります。エラーが発生した場合は、エラーログやデバッグログを確認し、問題の原因を特定することが重要です。
戻り値が「false」となった場合、適切なエラーハンドリングを行い、必要に応じて再度関数を実行するか、処理を中断してエラーを解決することが推奨されます。
OnnxRun関数を使った公式リファレンスのサンプルコード
//+------------------------------------------------------------------+
//| RunCastFloat16ToDouble関数 |
//| ONNXモデルを使用して、double型配列をFLOAT16型に変換し、 |
//| 再度double型に変換してエラーの差を確認するサンプル |
//+------------------------------------------------------------------+
bool RunCastFloat16ToDouble(long model_handle)
{
// 関数名をエキスパートログに出力
PrintFormat("test=%s",__FUNCTION__);
// double型の配列test_dataを作成し、初期化(1〜12の数値を格納)
double test_data[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
// ushort型の配列data_uint16を作成(FLOAT16型変換結果を格納するための配列)
ushort data_uint16[12];
// ArrayToFP16関数を使用して、double型配列をFLOAT16型に変換
if(!ArrayToFP16(data_uint16, test_data, FLOAT_FP16))
{
// エラーが発生した場合、エラーコードをエキスパートログに出力し、falseを返す
Print("error in ArrayToFP16. error code=", GetLastError());
return(false);
}
// 変換前のdouble型配列をエキスパートログに出力
Print("test array:");
ArrayPrint(test_data);
// 変換後のFLOAT16型のushort配列をエキスパートログに出力
Print("ArrayToFP16:");
ArrayPrint(data_uint16);
// FLOAT16型データを保持するための配列input_float16_valuesを作成(サイズは3x4=12)
U<ushort> input_float16_values[3*4];
// double型データを保持するための配列output_double_valuesを作成(同じく3x4=12)
U<double> output_double_values[3*4];
// 浮動小数点数のデータを格納する配列test_data_floatを作成(後で使用)
float test_data_float[];
// ArrayFromFP16関数を使用して、FLOAT16型からfloat型に再変換
if(!ArrayFromFP16(test_data_float, data_uint16, FLOAT_FP16))
{
// エラーが発生した場合、エラーコードをエキスパートログに出力し、falseを返す
Print("error in ArrayFromFP16. error code=", GetLastError());
return(false);
}
// 変換後のushort型配列をfloat型配列に変換し、それぞれの値をログ出力
for(int i = 0; i < 12; i++)
{
input_float16_values[i].value = data_uint16[i]; // ushort型の値を格納
// それぞれのインデックスでの値、16進数でのFLOAT16表現、ushort値をエキスパートログに出力
PrintFormat("%d input value =%f Hex float16 = %s ushort value=%d",
i, test_data_float[i], ArrayToString(input_float16_values[i].uc), input_float16_values[i].value);
}
// ONNXの入力配列をログ出力
Print("ONNX input array:");
ArrayPrint(input_float16_values);
// OnnxRun関数を使用してONNXモデルの実行を行い、結果を出力配列に格納
bool res = OnnxRun(model_handle, ONNX_NO_CONVERSION, input_float16_values, output_double_values);
// 実行に失敗した場合、エラーコードをログ出力し、falseを返す
if(!res)
{
PrintFormat("error in OnnxRun. error code=%d", GetLastError());
return(false);
}
// ONNXモデルの出力配列をエキスパートログに出力
Print("ONNX output array:");
ArrayPrint(output_double_values);
// 変換結果と元のデータの差を計算し、総誤差を表示
double sum_error = 0.0;
for(int i = 0; i < 12; i++)
{
double delta = test_data[i] - output_double_values[i].value; // 元の値と出力値の差
sum_error += MathAbs(delta); // 絶対誤差を積算
// 各インデックスごとの結果、差分をログ出力
PrintFormat("%d output double %f = %s difference=%f",
i, output_double_values[i].value, ArrayToString(output_double_values[i].uc), delta);
}
// 総誤差をエキスパートログに出力
PrintFormat("test=%s sum_error=%f", __FUNCTION__, sum_error);
// 関数の実行が成功したためtrueを返す
return(true);
}
コンパイルエラーに関する注意
上記のサンプルコードは、ONNXモデルを使用してdouble型配列をFLOAT16型に変換し、再度double型に戻して誤差を確認する内容となっています。
しかし、このコードには、MQL5標準のライブラリに存在しないデータ型や関数が使用されています。具体的には、U<ushort>やU<double>という未定義の型、およびArrayToString関数という関数が含まれており、これらはそのままではコンパイルエラーを引き起こします。
U
の文法的役割は、何らかのユーザー定義型またはテンプレートとして使用されることが意図されていた可能性があります。たとえば、浮動小数点のデータをushort型などの整数型に変換したり、それらを管理するためのカスタムクラスであることが考えられますが、このコード内では明示的な定義がないため、エラーが発生してしまいます。
このサンプルコードは、これらの型や関数が未定義であるため、適切に修正する必要があることを念頭に置いてください。
※テンプレート関数についての詳細は↓の記事をご覧ください。
サンプルコード解説1
//+------------------------------------------------------------------+
//| RunCastFloat16ToDouble関数 |
//| ONNXモデルを使用して、double型配列をFLOAT16型に変換し、 |
//| 再度double型に変換してエラーの差を確認するサンプル |
//+------------------------------------------------------------------+
bool RunCastFloat16ToDouble(long model_handle)
{
// 関数名をエキスパートログに出力
PrintFormat("test=%s",__FUNCTION__);
// double型の配列test_dataを作成し、初期化(1〜12の数値を格納)
double test_data[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
// ushort型の配列data_uint16を作成(FLOAT16型変換結果を格納するための配列)
ushort data_uint16[12];
このセクションでは、サンプルコードの冒頭部分を解説します。このコードは、RunCastFloat16ToDoubleという関数を使用して、double型のデータをFLOAT16型に変換し、再度double型に戻して誤差を確認するためのものです。。
関数の定義と目的
RunCastFloat16ToDouble関数は、ONNXモデルを操作する際に使用されるサンプルです。model_handleという引数を受け取り、ONNXモデルの実行に必要な操作を行います。この関数の目的は、double型の配列をFLOAT16型に変換し、最終的にdouble型に戻して変換による誤差を確認することです。
関数名のログ出力
最初に、PrintFormat関数を使用して、関数名をエキスパートログに出力しています。__FUNCTION__は、現在実行中の関数名を取得する特別な識別子です。これにより、関数が呼び出された際に、どの関数が実行されているかをログに記録することができます。
double型の配列の初期化
次に、double型の配列test_dataを作成し、1から12までの数値を初期化しています。この配列は、後にFLOAT16型に変換されるデータの元となります。ここでは12個の値を用意しており、これが変換の対象となります。
ushort型の配列の作成
ushort型の配列data_uint16は、FLOAT16型に変換されたデータを格納するために用意されています。FLOAT16形式は16ビットの浮動小数点形式であり、ushort(符号なし16ビット整数)に変換結果を格納します。この配列には、元のdouble型データをFLOAT16形式に変換した後のデータが格納されます。
この部分では、関数の初期設定を行い、double型データをFLOAT16型に変換するための準備をしています。
サンプルコード解説2
// ArrayToFP16関数を使用して、double型配列をFLOAT16型に変換
if(!ArrayToFP16(data_uint16, test_data, FLOAT_FP16))
{
// エラーが発生した場合、エラーコードをエキスパートログに出力し、falseを返す
Print("error in ArrayToFP16. error code=", GetLastError());
return(false);
}
// 変換前のdouble型配列をエキスパートログに出力
Print("test array:");
ArrayPrint(test_data);
// 変換後のFLOAT16型のushort配列をエキスパートログに出力
Print("ArrayToFP16:");
ArrayPrint(data_uint16);
このセクションでは、ArrayToFP16関数を使用して、double型配列をFLOAT16型に変換し、その結果をエキスパートログに出力する部分について解説します。
ArrayToFP16 関数を使用した変換
最初に、ArrayToFP16関数を使用して、double型配列であるtest_dataをFLOAT16型に変換し、その結果をushort型の配列data_uint16に格納しています。この関数は、double型またはfloat型の配列を受け取り、指定したフォーマット(ここではFLOAT_FP16)に変換する役割を持っています。変換が成功すれば次に進みますが、失敗した場合は、エラーコードをエキスパートログに出力し、関数はfalseを返して終了します。
エラー処理
もし変換に失敗した場合、Print関数を使用してエラーメッセージとGetLastError関数によるエラーコードをエキスパートログに出力します。このようにして、何らかの問題が発生した場合でも、原因をエラーログから確認できるようになっています。
配列の出力
変換が成功した場合、まず変換前のtest_data(double型配列)をエキスパートログに出力します。これにより、元のデータがどのような状態であったかを確認できます。その後、変換後のFLOAT16型データが格納されたdata_uint16(ushort型配列)を同様にログに出力します。ArrayPrint関数を使用して配列の内容全体を出力しているため、変換の前後のデータを比較することができます。
サンプルコード解説3
// FLOAT16型データを保持するための配列input_float16_valuesを作成(サイズは3x4=12)
U<ushort> input_float16_values[3*4];
// double型データを保持するための配列output_double_valuesを作成(同じく3x4=12)
U<double> output_double_values[3*4];
// 浮動小数点数のデータを格納する配列test_data_floatを作成(後で使用)
float test_data_float[];
// ArrayFromFP16関数を使用して、FLOAT16型からfloat型に再変換
if(!ArrayFromFP16(test_data_float, data_uint16, FLOAT_FP16))
{
// エラーが発生した場合、エラーコードをエキスパートログに出力し、falseを返す
Print("error in ArrayFromFP16. error code=", GetLastError());
return(false);
}
このセクションでは、FLOAT16型データを再びfloat型およびdouble型に変換するための準備と処理について解説します。
FLOAT16型データを保持する配列の作成
まず、FLOAT16型データを保持するために、ushort型を保持する配列input_float16_valuesを作成しています。この配列は、変換されたFLOAT16データを後で処理するために必要です。この例では、3×4=12というサイズで作成されており、3行4列のデータが格納されることを示しています。
double型データを保持する配列の作成
次に、double型のデータを保持するための配列output_double_valuesを同様に作成しています。この配列も同じく12個の要素を持ち、後でFLOAT16から再変換されたdouble型データが格納されます。
float型配列の作成
さらに、浮動小数点数(float型)のデータを格納するための配列test_data_floatも作成しています。この配列は、後でFLOAT16からfloat型に再変換されたデータを格納するために使用されますが、この時点では配列のサイズや内容はまだ指定されていません。
ArrayFromFP16関数による再変換
次に、ArrayFromFP16関数を使用して、FLOAT16型データを再びfloat型データに変換しています。この関数は、前に変換されたFLOAT16型のデータを使用して、元のfloat型データに戻す役割を果たします。もし再変換が成功すれば次に進みますが、失敗した場合には、Print関数を使用してエラーメッセージとGetLastError関数によるエラーコードをエキスパートログに出力し、falseを返して関数を終了します。
この処理により、再び使用可能なfloat型データを取り出すことが可能になり、次の処理に進む準備が整います。
サンプルコード解説4
// 変換後のushort型配列をfloat型配列に変換し、それぞれの値をログ出力
for(int i = 0; i < 12; i++)
{
input_float16_values[i].value = data_uint16[i]; // ushort型の値を格納
// それぞれのインデックスでの値、16進数でのFLOAT16表現、ushort値をエキスパートログに出力
PrintFormat("%d input value =%f Hex float16 = %s ushort value=%d",
i, test_data_float[i], ArrayToString(input_float16_values[i].uc), input_float16_values[i].value);
}
// ONNXの入力配列をログ出力
Print("ONNX input array:");
ArrayPrint(input_float16_values);
このセクションでは、FLOAT16型データをfloat型に変換した後、各値をログに出力する処理について解説します。
ushort型配列をfloat型配列に変換
まず、forループを使用して、ushort型の配列data_uint16をfloat型の配列に変換しています。ループは配列の全要素(ここでは12個)を対象にしており、各インデックスに対して、input_float16_values配列の要素にushort型の値を格納しています。この処理により、ushort型のFLOAT16データが順番に変換され、input_float16_values配列に保持されます。
各値をエキスパートログに出力
続いて、PrintFormat関数を使用して、各インデックスでのデータをエキスパートログに出力しています。具体的には、以下の情報を出力しています。
test_data_float[i]
: 変換されたfloat型の値。ArrayToString(input_float16_values[i].uc)
: 16進数で表現されたFLOAT16データ(ただし、この部分はMQL5には存在しないため、このままコンパイルするとエラーが発生します)。input_float16_values[i].value
: 元のushort型の値。
これにより、変換されたデータを詳細に確認することができ、変換が正しく行われたかどうかをエキスパートログで確認できるようになっています。
ONNXの入力配列をログ出力
最後に、Print関数を使用して、ONNXモデルの入力として使用されるFLOAT16データ(input_float16_values配列)をログに出力しています。ArrayPrint関数を使用することで、配列全体を一度にログに出力し、データ内容を確認できます。
これにより、データの変換後、ONNXモデルに渡される前の状態をログで確認でき、後続の処理で問題がないかどうかを調査するのに役立ちます。
サンプルコード解説5
// OnnxRun関数を使用してONNXモデルの実行を行い、結果を出力配列に格納
bool res = OnnxRun(model_handle, ONNX_NO_CONVERSION, input_float16_values, output_double_values);
// 実行に失敗した場合、エラーコードをログ出力し、falseを返す
if(!res)
{
PrintFormat("error in OnnxRun. error code=%d", GetLastError());
return(false);
}
// ONNXモデルの出力配列をエキスパートログに出力
Print("ONNX output array:");
ArrayPrint(output_double_values);
このセクションでは、ONNXモデルを実行し、その結果を処理する部分について解説します。
OnnxRun関数の呼び出し
OnnxRun関数を呼び出して、ONNXモデルの実行を行います。この関数には4つの引数が渡されます。
model_handle
: 実行するONNXモデルのハンドル(識別子)。ONNX_NO_CONVERSION
: モデル実行時のオプション。ここではデータ変換を行わない設定になっています。input_float16_values
: モデルに渡される入力データ(FLOAT16型のデータが格納された配列)。output_double_values
: 実行結果が格納される配列(ここではdouble型の配列)。
ONNXモデルが実行されると、結果はoutput_double_values配列に格納されます。この処理により、FLOAT16型の入力データに基づいてモデルが推論を行い、double型の結果を得ることができます。
実行結果のエラーハンドリング
OnnxRun関数の実行が失敗した場合、resがfalseを返すので、その場合にはPrintFormat関数を使用してエラーメッセージとエラーコードをログに出力します。エラーコードはGetLastError関数によって取得され、これを確認することで、何が原因で失敗したのかを特定できます。失敗した場合は、falseを返して関数を終了させます。
このようにして、ONNXモデルの実行が正常に行われたかどうかをチェックし、必要に応じてエラーログを残すことができます。
サンプルコード解説6
// ONNXモデルの出力配列をエキスパートログに出力
Print("ONNX output array:");
ArrayPrint(output_double_values);
// 変換結果と元のデータの差を計算し、総誤差を表示
double sum_error = 0.0;
このセクションでは、ONNXモデルからの出力結果をログに出力し、その結果と元のデータの誤差を計算する処理について解説します。
ONNXモデルの出力配列をログ出力
まず、ONNXモデルの実行後に得られた結果、つまりoutput_double_values
配列をエキスパートログに出力しています。Print関数を使用して、”ONNXoutput array”というメッセージをログに表示した後、ArrayPrint関数を使用して配列の内容全体をログに出力します。この操作によって、ONNXモデルが返した推論結果がどのようなデータであるかを確認できるようになっています。
変換結果と元データの誤差計算
次に、ONNXモデルの出力結果と元のデータ(test_data
)の間の差を計算する処理を行います。ここでは、sum_errorという変数を使用して、全てのデータの誤差を累積します。誤差を確認することにより、変換処理やモデルの推論がどれだけ正確に行われたかを測定できます。
この段階で、出力結果と元のデータの差異を視覚的に確認できるようになり、推論結果の精度を評価するための基礎が整います。
サンプルコード解説7
for(int i = 0; i < 12; i++)
{
double delta = test_data[i] - output_double_values[i].value; // 元の値と出力値の差
sum_error += MathAbs(delta); // 絶対誤差を積算
// 各インデックスごとの結果、差分をログ出力
PrintFormat("%d output double %f = %s difference=%f",
i, output_double_values[i].value, ArrayToString(output_double_values[i].uc), delta);
}
// 総誤差をエキスパートログに出力
PrintFormat("test=%s sum_error=%f", __FUNCTION__, sum_error);
// 関数の実行が成功したためtrueを返す
return(true);
}
このセクションでは、ONNXモデルの出力結果と元のデータの差を計算し、その誤差をログに出力する処理について解説します。
元のデータと出力結果の差の計算
forループを使用して、元のデータ(test_data)とONNXモデルの出力結果(output_double_values)の差を計算しています。各インデックスに対して以下の処理が行われます。
- delta変数に、元のデータと出力結果の差を格納しています。この差が、変換や推論によってどれだけの誤差が生じたかを示します。
- sum_error変数に、絶対誤差を累積しています。MathAbs関数を使用して絶対値を計算し、誤差が正負に関わらず正確に積算されるようにしています。これにより、全体の誤差の総和が得られます。
各インデックスの結果をログ出力
各インデックスでの出力結果、16進数でのFLOAT16表現(ただし、MQL5にはArrayToString関数は存在しないため、コンパイルエラーが発生します)、そして元のデータとの差分をログに出力しています。PrintFormat関数を使用して、インデックスごとの詳細な情報をエキスパートログに記録します。
総誤差の出力
すべてのインデックスでの誤差が計算された後、sum_errorに累積された総誤差をエキスパートログに出力します。これにより、全体としてどれだけの誤差が生じたかを確認できます。
関数の終了と戻り値
最後に、関数の実行が成功したことを示すためにtrueを返して関数を終了します。この戻り値によって、呼び出し元で処理が成功したかどうかを判断することができます。