ONNX機械学習モデル変換・推論
【概要】scikit-learnモデルをONNX形式に変換し推論性能を比較する。ONNXの相互運用性と効果を確認。RandomForestClassifierでの変換精度と速度変化を確認。


目次
概要
背景と課題
機械学習フレームワーク(TensorFlow、PyTorch、scikit-learn等)は独自のモデル形式を持つため、フレームワーク間でのモデル共有には互換性の課題が存在する。学習環境と推論環境が異なる場合、モデルの移行が困難になる。
ONNX(Open Neural Network Exchange)の役割
ONNXは異なる機械学習フレームワーク間でのモデル相互運用を可能にするオープンスタンダード形式である。モデルを計算グラフ(ノードとエッジで表現される処理の流れ図)として表現し、標準化されたオペレーター(数学的演算処理)と型システム(データ型の定義体系)を使用してモデル構造を定義する。
出典: Bai, J., Lu, F., Zhang, K., et al. (2019). ONNX: Open Neural Network Exchange. GitHub repository. https://github.com/onnx/onnx
技術的制約
全てのモデルタイプがサポートされるわけではなく、フレームワーク固有の機能は変換時に制限される場合がある。scikit-learnでは主要な分類・回帰アルゴリズムがサポートされている。
応用分野
クラウドで学習したモデルをエッジデバイスで実行、Webブラウザでの機械学習推論、異なる開発チーム間でのモデル共有、モバイルアプリケーションへのモデルデプロイメント、リアルタイム推論システムでの活用が可能である。
学習目標
scikit-learnで学習したモデルをONNX形式に変換し、推論速度と精度の変化を測定することで、モデル変換技術の特性を理解する。
事前準備
Python, Windsurfをインストールしていない場合の手順(インストール済みの場合は実行不要)。
- 管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。
- 以下のコマンドをそれぞれ実行する(winget コマンドは1つずつ実行)。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Windsurf をシステム領域にインストール
winget install --scope machine --id Codeium.Windsurf -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
REM Windsurf のパス設定
set "WINDSURF_PATH=C:\Program Files\Windsurf"
if exist "%WINDSURF_PATH%" (
echo "%PATH%" | find /i "%WINDSURF_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%WINDSURF_PATH%" /M >nul
)
必要なPythonパッケージのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。
pip install scikit-learn sklearn-onnx onnxruntime numpy
プログラムコード
# ONNX変換による機械学習モデル推論性能比較プログラム
# 特徴技術名: ONNX (Open Neural Network Exchange)
# 出典: Bai, J., Lu, F., Zhang, K., et al. (2019). ONNX: Open Neural Network Exchange. arXiv preprint arXiv:1712.03213.
# 特徴機能: クロスプラットフォーム・クロスフレームワークでのモデル相互運用性。異なる機械学習フレームワーク間でモデルを共有・実行可能にする標準フォーマット
# 学習済みモデル: なし(プログラム内でRandomForestClassifierを学習)
# 方式設計:
# - 関連利用技術: scikit-learn (機械学習ライブラリ、RandomForestClassifierを提供)、skl2onnx (scikit-learnモデルのONNX変換ツール)、ONNX Runtime (高性能推論エンジン)
# - 入力と出力: 入力: Irisデータセット(プログラム内で自動読み込み)、出力: 推論性能比較結果(精度、推論時間、速度比)
# - 処理手順: 1) Irisデータセットの読み込みと分割、2) RandomForestClassifierの学習、3) scikit-learnモデルでの推論と性能測定、4) ONNXフォーマットへの変換、5) ONNX Runtimeでの推論と性能測定、6) 両モデルの性能比較
# - 前処理、後処理: 前処理: データ型をfloat32に変換(ONNX推論時)、後処理: 推論結果の精度計算と実行時間の比較
# - 追加処理: なし
# - 調整を必要とする設定値: N_ESTIMATORS (RandomForestの決定木数、デフォルト10)、RANDOM_SEED (再現性のための乱数シード、デフォルト42)
# 将来方策: N_ESTIMATORSの最適値を決定するため、複数の値で精度と推論時間のトレードオフを評価する機能の実装
# その他の重要事項: Windows環境での動作確認済み、ONNX変換により推論速度が変化する可能性がある
# 前準備: pip install scikit-learn sklearn-onnx onnxruntime numpy
import numpy as np
import time
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
# 定数定義
RANDOM_SEED = 42 # 再現性のための乱数シード
N_ESTIMATORS = 10 # RandomForestの決定木数
TEST_SIZE = 0.25 # テストデータの割合
N_RUNS = 10 # 推論時間測定の実行回数
TARGET_OPSET = 12 # ONNX opsetバージョン
# プログラム開始時の概要表示
print('=== ONNX変換による機械学習モデル推論性能比較プログラム ===')
print('scikit-learnのRandomForestClassifierをONNX形式に変換し、')
print('推論性能(精度・実行時間)を比較します。')
print()
# データセット読み込みと分割
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED
)
print('=== データセット情報 ===')
print(f'訓練データサイズ: {X_train.shape}')
print(f'テストデータサイズ: {X_test.shape}')
print(f'特徴量数: {X.shape[1]}')
print(f'クラス数: {len(np.unique(y))}')
print()
# メイン処理
print('=== scikit-learnモデル学習 ===')
sklearn_model = RandomForestClassifier(
n_estimators=N_ESTIMATORS, random_state=RANDOM_SEED
)
sklearn_model.fit(X_train, y_train)
# scikit-learn推論と性能測定
sklearn_pred = sklearn_model.predict(X_test)
sklearn_accuracy = np.mean(sklearn_pred == y_test)
# 推論時間を複数回測定
sklearn_times = []
for _ in range(N_RUNS):
start_time = time.time()
sklearn_model.predict(X_test)
sklearn_times.append(time.time() - start_time)
sklearn_time = np.mean(sklearn_times)
print(f'scikit-learn精度: {sklearn_accuracy:.4f}')
print(f'scikit-learn推論時間: {sklearn_time:.6f}秒 (平均/{N_RUNS}回)')
print()
print('=== ONNX変換 ===')
initial_types = [('input', FloatTensorType([None, X.shape[1]]))]
onnx_model = convert_sklearn(
sklearn_model,
initial_types=initial_types,
target_opset=TARGET_OPSET
)
print('ONNX変換完了')
print(f'ONNXモデルサイズ: {len(onnx_model.SerializeToString())} bytes')
print(f'ONNX opsetバージョン: {TARGET_OPSET}')
print()
print('=== ONNX推論実行 ===')
# CPUプロバイダーを明示的に指定
onnx_session = ort.InferenceSession(
onnx_model.SerializeToString(),
providers=['CPUExecutionProvider']
)
input_name = onnx_session.get_inputs()[0].name
print(f'入力名: {input_name}')
print(f'入力形状: {onnx_session.get_inputs()[0].shape}')
print(f'出力形状: {onnx_session.get_outputs()[0].shape}')
print(f'実行プロバイダー: {onnx_session.get_providers()}')
print()
# ONNX推論と性能測定
onnx_pred = onnx_session.run(None, {input_name: X_test.astype(np.float32)})[0]
onnx_accuracy = np.mean(onnx_pred == y_test)
# 推論時間を複数回測定
onnx_times = []
for _ in range(N_RUNS):
start_time = time.time()
onnx_session.run(None, {input_name: X_test.astype(np.float32)})
onnx_times.append(time.time() - start_time)
onnx_time = np.mean(onnx_times)
print(f'ONNX精度: {onnx_accuracy:.4f}')
print(f'ONNX推論時間: {onnx_time:.6f}秒 (平均/{N_RUNS}回)')
print()
# 結果出力
print('=== 結果比較 ===')
print(f"精度一致: {'はい' if np.array_equal(sklearn_pred, onnx_pred) else 'いいえ'}")
if sklearn_time > 0:
speed_ratio = onnx_time / sklearn_time
print(f'速度比較 (ONNX/scikit-learn): {speed_ratio:.2f}')
if speed_ratio < 1.0:
print(f'→ ONNXモデルが{1/speed_ratio:.2f}倍高速')
elif speed_ratio > 1.0:
print(f'→ scikit-learnモデルが{speed_ratio:.2f}倍高速')
else:
print('→ 両モデルの処理時間は同等')
print()
print('=== 結果の解釈 ===')
print('・精度一致: 両モデルが同一の予測結果を出力するかを確認')
print('・速度比較: ONNX変換による推論速度の変化を測定')
print('・ONNXの特徴: 異なる環境やフレームワーク間でのモデル互換性')
print(f'・測定条件: {N_RUNS}回実行の平均時間で比較')
使用方法
- 上記のプログラムコードを実行する
実験・探求のアイデア
機械学習モデル選択の実験
- RandomForestClassifier: 決定木アンサンブル手法(複数の決定木を組み合わせた分類器)
- SVM: サポートベクターマシン(データを分離する最適な境界面を見つける分類器)
- LogisticRegression: 線形分類器(線形関数を用いた確率的分類器)
- MLPClassifier: 多層パーセプトロン(複数の層からなるニューラルネットワーク)
各モデルでONNX変換時の速度変化を比較し、モデルの種類による違いを観察する。
追加実験
- データセット変更: Iris以外のデータセット(Wine、Breast Cancer等)での性能比較
- モデルサイズ変更: n_estimatorsを10、50、100に変更し、モデル複雑度と変換効果の関係を調査
- 推論回数増加: 1000回の推論を実行し、累積時間での性能差を測定
研究のアイデア
- 変換精度の検証: 異なるデータセットでの精度保持率を調査し、ONNX変換の信頼性を確認
- モデル保存・読み込み: ONNXモデルをファイル保存し、別のプログラムで読み込む実験