CP4DaaSでLightGBMモデルをONNX形式に変換してデプロイする

## 本記事の目的
本記事では、IBM Cloud上で提供される Cloud Pak for Data as a Service（以降CP4DaaSと表記）において、LightGBMモデルをデプロイする方法についてご紹介します。

## はじめに
2024年12月より、CP4DaaSにおいてONNX形式に変換された機械学習モデルをデプロイすることが可能となりました。

そのためLightGBMやCatBoostなど、Watson Machine Learningで直接サポートされていないフレームワークのモデルも、ONNX形式に変換することでデプロイすることができるようになりました。

本記事ではLightGBMモデルについて取り上げますが、以下のフレームワークも同様にONNX形式に変換して、CP4DaaSでデプロイすることが可能です。
* PyTorch
* TensorFlow
* CatBoost
* XGBoost
* Scikit-learn

参考：[ONNX形式に変換されたモデルの展開](https://jp-tok.dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/deploy-onnx-overview.html?locale=ja&context=cpdaas&audience=wdp)

## ノートブック実行
今回は、IBMが提供している[こちらのサンプルプロジェクト](https://dataplatform.cloud.ibm.com/exchange/public/entry/view/fa4204ef-a9bc-4869-9e5c-756e25fb3235?context=cpdaas)を実行して、LightGBM モデルをONNX形式に変換し、CP4DaaSでデプロイしていきます。

https://dataplatform.cloud.ibm.com/exchange/public/entry/view/fa4204ef-a9bc-4869-9e5c-756e25fb3235?context=cpdaas

#### １. 環境のセットアップ
サンプルノートブックを実行していきます。まずは、環境のセットアップです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/0bf27219-56b8-49e4-ba52-fae1e5468672.png)


最初のセルでは、必要なライブラリをインストールします。
```python
!pip install -U ibm-watsonx-ai | tail -n 1
!pip install scikit-learn==1.3.0 | tail -n 1
!pip install lightgbm==4.2.0 | tail -n 1
!pip install onnxruntime==1.16.3 | tail -n 1
!pip install onnx==1.16.2 | tail -n 1
!pip install scikit-learn==1.3 | tail -n 1
!pip install skl2onnx | tail -n 1
!pip install onnxmltools
```
次のセルでは、必要なツールをインポートします。
```python
import json
import getpass
import onnx
import onnxruntime as ort
from ibm_watsonx_ai import APIClient, Credentials
from lightgbm import LGBMClassifier
from onnxmltools import convert_lightgbm
from onnxmltools.utils import save_model
from skl2onnx.common.data_types import FloatTensorType
from sklearn import datasets
from sklearn.model_selection import train_test_split
```

続いて、IBM CloudのAPIキーを取得し、入力する必要があります。また、次のセルでは使用しているIBM Cloudのロケーションを記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/0d1c573c-db19-4be5-912a-78de5c1b8a6b.png)

```python
api_key = getpass.getpass("Please enter your api key (hit enter): ")
```

```python
location = "IBM Cloudのロケーション"
```

続いてノートブックの指示に従い、新規デプロイメントスペースを作成してスペースIDを記載する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/a890d7db-e61d-481c-8b03-60c216f5d004.png)


```python
space_id = "使用するデプロイメントスペースのスペースID"
```

#### ２. LightGBM モデルをトレーニングする
次に、サンプルのLightGBMモデルをトレーニングします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/09301685-c237-4d7f-aa05-5e65dfcd2931.png)


まずサンプルデータ（Irisデータセット）を読み込み、訓練用とテスト用に分割します。
```python
iris = datasets.load_iris()
x, y = iris.data, iris.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.20, random_state=42)
```

そしてIrisデータを使ってLightGBMモデルをトレーニングします。
```python
model = LGBMClassifier(objective='multiclass')
model.fit(x_train, y_train)
```
最後にトレーニング済みのモデルに対し、テストデータの一部を使って予測を実行します。
```python
eval_data = x_test[:3]
model.predict(eval_data)
```


#### ３. LightGBM モデルをONNX形式に変換する
続いて、モデルをONNX形式に変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/63b737b1-8bef-46b9-b3b3-3c69a91abc3c.png)


以下のセルで LightGBM モデルを ONNX 形式に変換しています。
```python
initial_types = [("float_input", FloatTensorType([None, model.n_features_]))]
onnx_model = convert_lightgbm(model=model, initial_types=initial_types, zipmap=False, target_opset=9)
onnx_model_filename = "lgbm_model.onnx"
save_model(onnx_model, onnx_model_filename)
```

続いてモデルが正しく機能するかを確認します。
ここでは、onnx checkerを使用して、エクスポートされたONNXモデルの検証を行っています。またonnxruntimeを使用してモデルをロードし、テストデータに対して推論を実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/b2a53fae-e035-4f3a-8bf6-0b2b11fa6829.png)


「does not match actual shape…」という警告メッセージが出てきますが、推論結果には影響がないため無視してOKです。

```python
onnx.checker.check_model(onnx_model)
```


```python
session = ort.InferenceSession(onnx_model_filename)

input_data = {session.get_inputs()[0].name: eval_data.tolist()}
session.run([], input_data)
```


#### ４. 変換したモデルを watsonx.ai ランタイムリポジトリに永続化する

次に、watsonx.aiランタイムにモデルを保管します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/90ac3b53-8d78-420b-92ac-d1e546b82ffd.png)


まずwatsonx.aiランタイムに保存するためにモデルをzip化します。
```python
sofware_spec_id = client.software_specifications.get_id_by_name("onnxruntime_opset_19")
onnx_model_zip = "lgbm_onnx.zip"
```

```python
!zip {onnx_model_zip} {onnx_model_filename}
```

そしてwatsonx.aiランタイムにモデルを保存します。

```python
metadata = {
    client.repository.ModelMetaNames.NAME: 'LGBM to ONNX converted model',
    client.repository.ModelMetaNames.TYPE: 'onnxruntime_1.16',
    client.repository.ModelMetaNames.SOFTWARE_SPEC_ID: sofware_spec_id
}

published_model = client.repository.store_model(
    model=onnx_model_zip,
    meta_props=metadata
)
```
現在（2025年6月時点）サポートされているモデルタイプはonnxruntime_1.16であるため、今回はモデルタイプにonnxruntime_1.16を指定しています。

サポートされるバージョンは随時更新されるため、都度確認が必要です。
[こちらの公式ドキュメント](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/pm_service_supported_frameworks.html?locale=ja&context=wx)よりご確認いただけます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/5f9f2415-e32e-442f-bae4-986459050a9c.png)

#### ５. クライアントライブラリを使用してオンラインスコアリング用にモデルをデプロイする

次に、モデルをデプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/8a303f00-b814-490b-b64d-c9637f7fb192.png)

```python
metadata = {
    client.deployments.ConfigurationMetaNames.NAME: "Deployment of LGBM to ONNX converted model",
    client.deployments.ConfigurationMetaNames.ONLINE: {}
}

created_deployment = client.deployments.create(published_model_id, meta_props=metadata)
```


デプロイしたモデルに対し、スコアリングを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/d29685aa-c7b4-4a2c-9982-98ce5e4c2c81.png)

```python
scoring_payload = {
    "input_data": [
        {
            "values": eval_data
        }
    ]
}
```

```python
predictions = client.deployments.score(deployment_id, scoring_payload)
```

予測結果をJSON形式で表示させます。
```python
print(json.dumps(predictions, indent=2))
```


#### ６.デプロイメントの確認
最後に、モデルが正しくデプロイされているかを確認します。
「１. 環境のセットアップ」にて指定したデプロイメントスペースに、オンラインデプロイメントが作成されていればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/6e43af22-d9de-490c-97ab-d913608bd2e1.png)


エンドポイントURLなどもこちらの画面から確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/f2517525-f77b-4750-bf26-2ed99dc3282c.png)


#### ７.デプロイしたモデルに対してスコアリングを実行する
オンラインデプロイメントの「テスト」タブから、スコアリングを実行することができます。
JSONの欄に手動でJSON形式のデータを入力し、右下の青い「予測」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/2a541730-3ede-4b77-acf4-be8f461542b6.png)

今回使用したデータは以下です。
```json
{
  "input_data": [
    {
      "fields": ["feature1", "feature2", "feature3", "feature4"],
      "values": [
        [5.1, 3.5, 1.4, 0.2],
        [6.2, 2.9, 4.3, 1.3]
      ]
    }
  ]
}
```
すると、予測結果がこのような画面で表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092447/9be61daa-7108-41fe-8042-829477916871.png)

今回の予測結果は以下の通りです。
```json
[
	{
		"id": "label",
		"values": [
			0,
			1
		]
	},
	{
		"id": "probabilities",
		"values": [
			[
				0.9999876022338867,
				0.000011821748557849787,
				6.486750976364419e-7
			],
			[
				0.00001045464159687981,
				0.9996751546859741,
				0.00031440649763680995
			]
		]
	}
]
```


## まとめ
以上、LightGBMモデルをONNX形式に変換し、CP4DaaSでデプロイする方法についてご紹介しました。
LightGBM以外のモデルをONNXに変換してデプロイするサンプルプロジェクトについては、以下の公式ドキュメントからご参照ください。


**「ONNX形式に変換されたモデルの展開」**[（https://jp-tok.dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/deploy-onnx-overview.html?locale=ja&context=cpdaas&audience=wdp）](https://jp-tok.dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/deploy-onnx-overview.html?locale=ja&context=cpdaas&audience=wdp)