PR

【MQL5】OnnxSetInputShape関数-入力形状を調べて動的に設定する方法

初心者向け

MetaTrader 5(MQL5)には、ONNX形式の学習済みモデルを読み込んで推論を実行するための関数群が用意されています。今回はその中でも 入力データの形状を設定する関数 である OnnxSetInputShape を紹介します。


OnnxSetInputShapeとは?

OnnxSetInputShape は、モデルの入力データの形状(シェイプ)をインデックス指定で設定する関数です。
ONNXモデルでは入力次元が固定されていますが、推論前にモデルの バッチサイズやシーケンス長、特徴量数を調整 する必要があります。

例えば「バッチサイズ 1 × 時系列長 10 × 特徴量 4」といった入力形式を指定することで、MQL5上でモデルに正しいデータを渡すことができます。


関数仕様

bool  OnnxSetInputShape( 
   long         onnx_handle, // ONNXセッションハンドル 
   long         input_index, // 入力パラメータインデックス 
   const ulong& shape[]      // 入力データの形状を記述する配列 
);

パラメータ

  • onnx_handle
    OnnxCreate または OnnxCreateFromBuffer で作成されたONNXセッションオブジェクトのハンドル
  • input_index
    0から始まる入力パラメータのインデックス
  • shape
    入力データ形状を記述する配列(例:{1,10,4} など)

戻り値

  • 成功した場合: true
  • 失敗した場合: false(詳細は GetLastError() で取得可能)

使用例

以下は、入力シェイプ [1,10,4]、出力シェイプ [1,1] を設定する例です。

// 入出力データの形状を指定
const ulong ExtInputShape [] = {1,10,4};
const ulong ExtOutputShape[] = {1,1};

// モデルを作成
long handle = OnnxCreateFromBuffer(model, ONNX_DEBUG_LOGS);

// 入力データの形状を指定
if(!OnnxSetInputShape(handle, 0, ExtInputShape))
  {
   Print("failed, OnnxSetInputShape error ", GetLastError());
   OnnxRelease(handle);
   return(-1);
  }

// 出力データの形状を指定
if(!OnnxSetOutputShape(handle, 0, ExtOutputShape))
  {
   Print("failed, OnnxSetOutputShape error ", GetLastError());
   OnnxRelease(handle);
   return(-1);
  }

上記のように、入力と出力のシェイプを明示的に設定することになります。

ONNXモデルによる形状の違い

ONNXモデルはアルゴリズムの種類によって入力形状が異なります。代表的なケースを整理すると以下のようになります。

1. 決定木系モデル(CatBoost、ランダムフォレストなど)

  • インデックス:0
  • 形状{バッチサイズ, 特徴量サイズ}
  • {1, 50}(特徴量が50個)
  • 時系列長の概念はなく、単純に特徴量の数を指定します。

2. ディープラーニングモデル(時系列でない分類・回帰)

  • インデックス:0
  • 形状{バッチサイズ, 特徴量サイズ}
  • {1, 128}(128次元の特徴量入力)
  • 多クラス分類やロジスティック回帰などで用いられます。

3. ディープラーニングの時系列モデル(CNN, LSTMなど)

  • インデックス:0
  • 形状{バッチサイズ, シーケンス長, 特徴量サイズ}
  • {1, 12, 18}(シーケンス長12、特徴量18)
  • FXの時系列予測などはこちらの形式になります。
WAN
WAN

形状はシーケンス長、特徴量の順番であることを忘れずに!!
入力するデータも
double data[シーケンス][特徴量];
の形状になるよ(‘ω’)
間違って
double data[特徴量][シーケンス];
にしないように(‘Д’)


4. 複数入力を持つディープラーニングモデル(サブネットあり)

  • インデックス:複数(0, 1, …)
  • 形状例
    • インデックス0:{バッチ, シーケンス①, 特徴量①}
    • インデックス1:{バッチ, シーケンス②, 特徴量②}
    • 入力1(価格系列):{1, 12, 18}
    • 入力2(外部特徴量):{1, 12, 8}
  • 異なる系列や特徴量を同時に学習させたモデルでは、このようにインデックスごとに形状を指定します。

入力形状を調べて動的に設定する方法

実際の運用では「このモデルがどんな入力形状を期待しているか」をコードで調べたいケースがよくあります。
その際に使うのが OnnxGetInputTypeInfo 関数です。

以下の例では、モデルの0番目の入力から シーケンス長や特徴量数を動的に取得 しています。

// 複数入力に対応したONNX入力形状設定関数
bool SetupMultipleInputShapes(long handle)
{
   // 入力数を取得
   long input_count = OnnxGetInputCount(handle);
   if(input_count <= 0)
   {
      Print("Failed to get input count or no inputs found");
      return false;
   }
   
   Print("Total inputs found: ", input_count);
   
   // 各入力に対して処理
   for(int input_index = 0; input_index < input_count; input_index++)
   {
      Print("Processing input ", input_index);
      
      // 入力型情報を格納する構造体
      OnnxTypeInfo input_typeinfo;
      
      // 入力の型情報を取得
      if(!OnnxGetInputTypeInfo(handle, input_index, input_typeinfo))
      {
         Print("Failed to get input type info for input ", input_index);
         return false;
      }
      
      // 入力がテンソル型か確認
      if(input_typeinfo.type != ONNX_TYPE_TENSOR)
      {
         Print("Input ", input_index, " is not a tensor type");
         return false;
      }
      
      // 入力テンソルの次元を取得
      long input_dimensions[];
      ArrayCopy(input_dimensions, input_typeinfo.tensor.dimensions);
      
      // ここで使う変数を先に宣言
      int  size             = ArraySize(input_dimensions);
      long batch_size       = 0;
      long sequence_length  = 0;   // 非時系列では 0 のまま
      long dynamic_feature  = 0;
      ulong ExtInputShape[];
      
      Print("Input ", input_index, " dimensions: ", size);
      
      // 分岐:2D=非時系列、3D=時系列、それ以外はエラー
      if(size == 2)
      {
         Print("Input ", input_index, ": 非時系列データ");
         batch_size      = input_dimensions[0];
         dynamic_feature = input_dimensions[1];
         
         // 推論ではバッチサイズは基本1(動的/0/-1なら1に補正)
         if(batch_size <= 0 || batch_size == -1)
            batch_size = 1;
         
         ArrayResize(ExtInputShape, 2);
         ExtInputShape[0] = batch_size;
         ExtInputShape[1] = dynamic_feature;
         
         Print("Input ", input_index, " shape: [", batch_size, ", ", dynamic_feature, "]");
      }
      else if(size == 3)
      {
         Print("Input ", input_index, ": 時系列データ");
         batch_size       = input_dimensions[0];
         sequence_length  = input_dimensions[1];
         dynamic_feature  = input_dimensions[2];
         
         if(batch_size <= 0 || batch_size == -1)
            batch_size = 1;
         
         ArrayResize(ExtInputShape, 3);
         ExtInputShape[0] = batch_size;
         ExtInputShape[1] = sequence_length;
         ExtInputShape[2] = dynamic_feature;
         
         Print("Input ", input_index, " shape: [", batch_size, ", ", sequence_length, ", ", dynamic_feature, "]");
      }
      else
      {
         Print("Invalid input dimensions for input ", input_index, ": size=", size);
         return false;
      }
      
      // 形状を適用(各入力に対して個別に設定)
      if(!OnnxSetInputShape(handle, input_index, ExtInputShape))
      {
         Print("Failed OnnxSetInputShape for input ", input_index, ", error ", GetLastError());
         return false;
      }
      
      Print("Successfully set shape for input ", input_index);
   }
   
   Print("All input shapes configured successfully");
   return true;
}

// 使用例
bool InitializeOnnxModel()
{
   // ONNXモデルをロード
   long handle = OnnxCreateFromBuffer(model_buffer, model_size);
   if(handle == INVALID_HANDLE)
   {
      Print("Failed to create ONNX model");
      return false;
   }
   
   // 複数入力の形状を設定
   if(!SetupMultipleInputShapes(handle))
   {
      Print("Failed to setup input shapes");
      OnnxRelease(handle);
      return false;
   }
   
   return true;
}

ポイント

  • OnnxGetInputTypeInfo を使うことで、モデルごとの入力形状を自動的に確認可能
  • コード内で shape をハードコーディングせず、モデルに合わせて動的に処理できる
  • 特に CatBoostなどの決定木系LSTMなどの時系列モデル の違いを吸収するのに便利

ポイントまとめ

  • OnnxSetInputShapeモデル入力の形状を設定する関数
  • 形状は [バッチサイズ, 時系列長, 特徴量数] のような配列で指定
  • 入力と出力のシェイプは 必ず対応するように設定
  • エラー発生時は GetLastError() で原因を確認

まとめ

MQL5でONNXモデルを動かす場合、モデルの入出力シェイプを正しく設定しないと推論は失敗します。
OnnxSetInputShape を使えば、モデルに適切なデータ構造を渡す準備ができ、安定したAIトレード実装に役立ちます。

タイトルとURLをコピーしました