PR

【ONNX】CatBoostRegressorのONNXモデルをMQL5で使用する方法

機械学習

はじめに

MQL5でCatBoostRegressorで作成したONNXモデルを使用する際に起きる問題と解決方法について解説します。CatBoostは優れた決定木ベースのアルゴリズムですが、ONNXへの出力形状とMQL5の期待する出力形状に不一致があり、そのままでは読み込めないケースがあります。この記事では、その原因と対処法を詳しく説明します。

CatBoostとONNXの出力形状の問題

CatBoostからONNXモデルに変換する際、以下のような出力形状の違いがあります:

  • 分類モデル: ラベルとMapという2つの形式で出力されます
  • 回帰モデル: テンソルのみで出力されます

一方、MQL5では決定木のONNXファイルは暗黙的に出力形状が[1,N]という形状で受け取ることを期待しています。CatBoostの回帰モデルの出力(テンソルのみ)とMQL5の期待する出力形状のルールがマッチしていなかったことが問題で回帰モデルについてはそのままでは使用できないという問題がありました。

CatBoostのONNXに関するその他のルール

  1. 入力はテンソルのみ:ラベルは使用できません。ただし、ラベルにしたところで予測精度は変わらないという報告があるので、この点は気にする必要はありません。
  2. 分類のMap出力:出力形状の確認はラベルのみで行い、Map側に関しては出力形状の確認をしなくても自動変換で取得が可能です。取得する場合は以下のようにstructで受け取ります:
struct Map {
    ulong key[];
    float value[];
};

long label[1];
Map output_data_map[1];

if(!OnnxRun(ModelHandle, ONNX_DEFAULT, data, label, output_data_map))
{
    // エラー処理
}

解決方法:ONNXモデルの出力形状を変更する

回帰モデルの問題を解決するには、PythonでCatBoostのONNXを作成した後に、ONNXの出力形状を強制的に[1,1]に設定することが必要です。

Pythonでの実装方法

# ONNXモデルをロード
import onnx
from onnx import helper, numpy_helper
import numpy as np

# モデルをロード
model = onnx.load("original_catboost.onnx")

# 1x1にreshapeするノードを追加
shape_tensor = numpy_helper.from_array(np.array([1, 1], dtype=np.int64), name="reshape_to_1x1")

# [1, 1]に変更するための初期化子を追加
model.graph.initializer.append(shape_tensor)

# Reshapeノードを作成して追加
new_node = helper.make_node(
    "Reshape", 
    inputs=[model.graph.output[0].name, "reshape_to_1x1"], 
    outputs=["pred_reshaped"]
)
model.graph.node.append(new_node)

# 出力定義を差し替え
model.graph.output[0].name = "pred_reshaped"

# 出力形状を明示的に定義
output_shape = model.graph.output[0].type.tensor_type.shape

# 出力形状が未定義または次元が不足している場合に次元を追加
while len(output_shape.dim) < 2:
    output_shape.dim.add()

# 出力形状を[1, 1]に設定
output_shape.dim[0].dim_value = 1  # バッチサイズを1に設定
output_shape.dim[1].dim_value = 1  # 出力次元を1に設定

# リシェイプ後のONNXモデルを保存
onnx_model_path = "catboost.onnx"
onnx.save(model, onnx_model_path)

MQL5での実装方法

ONNXモデルの形状を修正した後、MQL5側では以下のように出力形状を設定します:

//---- 出力形状を設定
const long output_shape[] = {1,1};
if(!OnnxSetOutputShape(ModelHandle, 0, output_shape))
{
    // エラー発生時の処理
}

そして、以下のように予測値を取得できます:

// 予測実行
float output[1];
if(!OnnxRun(ModelHandle, ONNX_DEFAULT, features, output))
{
    // エラー発生時の処理
}

まとめ

CatBoostRegressorのONNXモデルをMQL5で使用する際の主な問題点は出力形状の不一致にあります。PythonでONNXモデルの出力形状を[1,1]に変更し、MQL5側でも適切に出力形状を設定することで、この問題を解決できます。

この方法を使えば、CatBoostの優れた予測性能をMQL5で活用することができるようになります。ぜひ皆さんも試してみてください。

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