ONNX (Open Neural Network Exchange) は、機械学習モデル用の移植可能なハードウェア最適化ランタイムを提供します。 モデルを ONNX 形式に変換することで、予測時に元のトレーニング フレームワークに依存することなく、待機時間が短い Spark でバッチ推論を実行できます。
この記事では、SynapseML を使用して LightGBM モデルをトレーニングし、それを ONNX 形式に変換した後、ONNX モデルを使用して、Microsoft Fabricで Spark で推論を実行します。
[前提条件]
Microsoft Fabric サブスクリプションを取得します。 または、無料の Microsoft Fabric 試用版にサインアップします。
Microsoft Fabric にサインインします。
ホーム ページの左下にあるエクスペリエンス スイッチャーを使用して Fabric に切り替えます。
- ノートブックをレイクハウスにアタッチします。 ノートブックの左側で、[ 追加 ] を選択して既存のレイクハウスを追加するか、作成します。
- Fabric Runtime 1.2 以降。
必要なパッケージをインストールする
ノートブックで次のセルを実行して、必要なパッケージをインストールします。
onnxmltools パッケージは、Fabric ランタイムにプレインストールされていません。
%pip install onnxmltools --quiet
インストールが完了したら、パッケージが使用可能であることを確認します。
import onnxmltools
import lightgbm
print(f"onnxmltools version: {onnxmltools.__version__}")
print(f"lightgbm version: {lightgbm.__version__}")
Note
lightgbm パッケージは、Fabric Runtime 1.2 以降にプレインストールされています。 必要なのは、 onnxmltoolsをインストールすることだけです。
サンプル データを読み込む
パブリック Azure Blob Storageから破産予測データセットを読み込みます。
df = (
spark.read.format("csv")
.option("header", True)
.option("inferSchema", True)
.load(
"wasbs://publicwasb@mmlspark.blob.core.windows.net/company_bankruptcy_prediction_data.csv"
)
)
print(f"Rows: {df.count()}, Columns: {len(df.columns)}")
display(df.limit(5))
表示されるテーブルには、次のような列が含まれています。
| 破産。 | 純利益フラグ | 負債に対する自己資本比率 |
|---|---|---|
| 0 | 1.0 | 0.0165 |
| 0 | 1.0 | 0.0208 |
LightGBM モデルをトレーニングする
VectorAssemblerを使用して特徴列を結合し、LightGBMClassifierをトレーニングします。
from pyspark.ml.feature import VectorAssembler
from synapse.ml.lightgbm import LightGBMClassifier
feature_cols = df.columns[1:]
featurizer = VectorAssembler(inputCols=feature_cols, outputCol="features")
train_data = featurizer.transform(df)["Bankrupt?", "features"]
model = (
LightGBMClassifier(featuresCol="features", labelCol="Bankrupt?")
.setDataTransferMode("bulk")
.setEarlyStoppingRound(300)
.setLambdaL1(0.5)
.setNumIterations(1000)
.setNumThreads(-1)
.setMaxDeltaStep(0.5)
.setNumLeaves(31)
.setMaxDepth(-1)
.setBaggingFraction(0.7)
.setFeatureFraction(0.7)
.setBaggingFreq(2)
.setObjective("binary")
.setIsUnbalance(True)
.setMinSumHessianInLeaf(20)
.setMinGainToSplit(0.01)
)
model = model.fit(train_data)
正常にトレーニングされたモデルを確認します。
print(f"Model type: {type(model).__name__}")
print(f"Number of features: {len(feature_cols)}")
モデルを ONNX 形式に変換する
トレーニング済みのモデルを LightGBM ブースターにエクスポートし、ONNX に変換します。
import lightgbm as lgb
from typing import Union
from lightgbm import Booster, LGBMClassifier
from onnxmltools.convert import convert_lightgbm
from onnxmltools.convert.common.data_types import FloatTensorType
def convert_to_onnx(lgbm_model: Union[LGBMClassifier, Booster], input_size: int) -> bytes:
initial_types = [("input", FloatTensorType([-1, input_size]))]
onnx_model = convert_lightgbm(
lgbm_model, initial_types=initial_types, target_opset=13
)
return onnx_model.SerializeToString()
booster_model_str = model.getLightGBMBooster().modelStr().get()
booster = lgb.Booster(model_str=booster_model_str)
model_payload_ml = convert_to_onnx(booster, len(feature_cols))
ONNX 変換が成功したことを確認します。
print(f"ONNX model payload size: {len(model_payload_ml)} bytes")
assert len(model_payload_ml) > 0, "ONNX conversion failed: empty payload"
出力には、ONNX モデルのペイロード サイズがバイト単位で表示されます (通常は約 800,000 バイト)。
Important
型定義には from onnxmltools.convert.common.data_types import FloatTensorType を使用します。 以前のインポート パス from onnxconverter_common.data_types import FloatTensorType は、現在のバージョンの onnxmltoolsと互換性がありません。
ONNX モデルを読み込んで構成する
ONNX ペイロードを SynapseML ONNXModel に読み込み、モデルの入力と出力を調べます。
from synapse.ml.onnx import ONNXModel
onnx_ml = ONNXModel().setModelPayload(model_payload_ml)
print("Model inputs:" + str(onnx_ml.getModelInputs()))
print("Model outputs:" + str(onnx_ml.getModelOutputs()))
出力には、モデルの入力ノードと出力ノードが一覧表示されます。
入力列と出力列をマッピングしてモデルを構成します。
FeedDictは、ONNX モデルの入力名を DataFrame 列名にマップします。
FetchDictは、必要な出力列名を ONNX モデルの出力名にマップします。
onnx_ml = (
onnx_ml.setDeviceType("CPU")
.setFeedDict({"input": "features"})
.setFetchDict({"probability": "probabilities", "prediction": "label"})
.setMiniBatchSize(5000)
)
推論を実行する
テスト データを作成し、ONNX モデルを使用して変換します。
from pyspark.ml.feature import VectorAssembler
import pandas as pd
import numpy as np
n = 10000
m = 95
test = np.random.rand(n, m)
testPdf = pd.DataFrame(test)
cols = list(map(str, testPdf.columns))
testDf = spark.createDataFrame(testPdf)
testDf = testDf.repartition(4)
testDf = (
VectorAssembler()
.setInputCols(cols)
.setOutputCol("features")
.transform(testDf)
.drop(*cols)
.cache()
)
display(onnx_ml.transform(testDf))
Note
テスト データはランダムに生成されるため、予測値は実際の結果を表しません。 このセクションでは、ONNX モデルが Spark で正しく実行されることを示します。
出力には、 features、 prediction、および probabilityの列が含まれている必要があります。
| 特徴 | 予測 | 確率 |
|---|---|---|
{"type":1,"values":[0.105... |
0 | {"0":0.835... |
{"type":1,"values":[0.814... |
0 | {"0":0.658... |
推論によって生成された結果を確認します。
results = onnx_ml.transform(testDf)
print(f"Result count: {results.count()}")
print(f"Output columns: {results.columns}")
assert "prediction" in results.columns, "Missing prediction column"
assert "probability" in results.columns, "Missing probability column"
出力は、すべてのテスト行がスコア付けされ、結果 DataFrame に features、 prediction、および probability 列が含まれていることを確認します。
Troubleshooting
| 問題 | 原因 | Resolution |
|---|---|---|
ModuleNotFoundError: No module named 'onnxmltools' |
パッケージは、Fabricランタイムにプレインストールされていません。 |
%pip install onnxmltools --quiet を実行し、Python カーネルを再起動します。 |
RuntimeError: Operator LgbmClassifier got an input with a wrong type |
FloatTensorTypeのインポート パスが正しくありません。 |
from onnxmltools.convert.common.data_types import FloatTensorTypeからインポートする代わりに、onnxconverter_common.data_typesを使用します。 |
ModuleNotFoundError: No module named 'onnx.mapping' |
onnxmltools バージョン 1.7.0 以前は、現在の onnx パッケージと互換性がありません。 |
%pip install onnxmltools --upgrade --quietを実行して、互換性のあるバージョンをインストールします。 |
ONNX conversion returns empty payload |
ブースター モデルの文字列の抽出に失敗しました。 | 変換前 model.getLightGBMBooster().modelStr().get() 空でない文字列が返されることを確認します。 |
Feature (Column_) appears more than one time中にmodel.fit() |
特殊文字を含むデータセット列では、LightGBM のサニタイズ後に重複する名前が生成されます。 |
.setDataTransferMode("bulk")構成にLightGBMClassifierを追加します。 一括モードでは Apache Arrow を使用し、列名のサニタイズの問題を回避します。 |
AssertionError の SparkContext 上の ONNXModel() |
Spark セッションが初期化されていません。 | このコードは、lakehouse がアタッチされたFabric ノートブックで実行します。
spark変数は、ランタイムによって事前に初期化されます。 |
リソースをクリーンアップする
キャッシュされたテスト DataFrame が不要になった場合は、クラスター メモリを解放するためにデータフレームの永続性を解除します。
testDf.unpersist()