ONNX モデルおよび SQL 機械学習を使用したデプロイと予測
重要
Azure SQL Edge の提供は、2025 年 9 月 30 日に終了する予定です。 詳細な情報と移行オプションについては、「提供終了に関する通知」を参照してください。
Note
Azure SQL Edge では ARM64 プラットフォームがサポートされなくなりました。
このクイックスタートでは、モデルをトレーニングし、ONNX に変換して、Azure SQL Edge にデプロイした後、アップロードされた ONNX モデルを使用してデータに対してネイティブ PREDICT を実行する方法について説明します。
このクイックスタートは scikit-learn に基づいており、Boston Housing データセットを使用します。
開始する前に
Azure SQL Edge を使用していて、Azure SQL Edge モジュールをデプロイしていない場合は、Azure portal を使用して SQL Edge をデプロイする手順に従います。
Azure Data Studio をインストールします。
このクイックスタートに必要な Python パッケージをインストールします。
- Python 3 カーネルに接続された新しいノートブックを開きます。
- [パッケージの管理] を選択します。
- [インストール済み] タブの、インストール済みパッケージの一覧で、以下の Python パッケージを探します。 これらのパッケージのいずれかがインストールされていない場合は、[新規追加] タブを選び、そのパッケージを検索して、[インストール] を選びます。
- scikit-learn
- numpy
- onnxmltools
- onnxruntime
- pyodbc
- setuptools
- skl2onnx
- sqlalchemy
以下のセクションの各スクリプト部分を Azure Data Studio ノートブックのセルに入力して、セルを実行します。
パイプラインをトレーニングする
データセットを分割し、特徴量を使用して住宅の中央値を予測します。
import numpy as np
import onnxmltools
import onnxruntime as rt
import pandas as pd
import skl2onnx
import sklearn
import sklearn.datasets
from sklearn.datasets import load_boston
boston = load_boston()
boston
df = pd.DataFrame(data=np.c_[boston['data'], boston['target']], columns=boston['feature_names'].tolist() + ['MEDV'])
target_column = 'MEDV'
# Split the data frame into features and target
x_train = pd.DataFrame(df.drop([target_column], axis = 1))
y_train = pd.DataFrame(df.iloc[:,df.columns.tolist().index(target_column)])
print("\n*** Training dataset x\n")
print(x_train.head())
print("\n*** Training dataset y\n")
print(y_train.head())
出力:
*** Training dataset x
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX \
0 0.00632 18.0 2.31 0.0 0.538 6.575 65.2 4.0900 1.0 296.0
1 0.02731 0.0 7.07 0.0 0.469 6.421 78.9 4.9671 2.0 242.0
2 0.02729 0.0 7.07 0.0 0.469 7.185 61.1 4.9671 2.0 242.0
3 0.03237 0.0 2.18 0.0 0.458 6.998 45.8 6.0622 3.0 222.0
4 0.06905 0.0 2.18 0.0 0.458 7.147 54.2 6.0622 3.0 222.0
PTRATIO B LSTAT
0 15.3 396.90 4.98
1 17.8 396.90 9.14
2 17.8 392.83 4.03
3 18.7 394.63 2.94
4 18.7 396.90 5.33
*** Training dataset y
0 24.0
1 21.6
2 34.7
3 33.4
4 36.2
Name: MEDV, dtype: float64
LinearRegression モデルをトレーニングするパイプラインを作成します。 他の回帰モデルを使用することもできます。
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import RobustScaler
continuous_transformer = Pipeline(steps=[('scaler', RobustScaler())])
# All columns are numeric - normalize them
preprocessor = ColumnTransformer(
transformers=[
('continuous', continuous_transformer, [i for i in range(len(x_train.columns))])])
model = Pipeline(
steps=[
('preprocessor', preprocessor),
('regressor', LinearRegression())])
# Train the model
model.fit(x_train, y_train)
モデルの精度を確認し、R2 スコアと平均二乗誤差を計算します。
# Score the model
from sklearn.metrics import r2_score, mean_squared_error
y_pred = model.predict(x_train)
sklearn_r2_score = r2_score(y_train, y_pred)
sklearn_mse = mean_squared_error(y_train, y_pred)
print('*** Scikit-learn r2 score: {}'.format(sklearn_r2_score))
print('*** Scikit-learn MSE: {}'.format(sklearn_mse))
出力:
*** Scikit-learn r2 score: 0.7406426641094094
*** Scikit-learn MSE: 21.894831181729206
モデルを ONNX に変換する
データ型をサポートされている SQL データ型に変換します。 この変換は他のデータフレームでも必要です。
from skl2onnx.common.data_types import FloatTensorType, Int64TensorType, DoubleTensorType
def convert_dataframe_schema(df, drop=None, batch_axis=False):
inputs = []
nrows = None if batch_axis else 1
for k, v in zip(df.columns, df.dtypes):
if drop is not None and k in drop:
continue
if v == 'int64':
t = Int64TensorType([nrows, 1])
elif v == 'float32':
t = FloatTensorType([nrows, 1])
elif v == 'float64':
t = DoubleTensorType([nrows, 1])
else:
raise Exception("Bad type")
inputs.append((k, t))
return inputs
skl2onnx
を使用して、LinearRegression モデルを ONNX 形式に変換し、ローカルに保存します。
# Convert the scikit model to onnx format
onnx_model = skl2onnx.convert_sklearn(model, 'Boston Data', convert_dataframe_schema(x_train), final_types=[('variable1',FloatTensorType([1,1]))])
# Save the onnx model locally
onnx_model_path = 'boston1.model.onnx'
onnxmltools.utils.save_model(onnx_model, onnx_model_path)
Note
SQL Edge の ONNX ランタイム バージョンと skl2onnx パッケージの間に不一致がある場合は、skl2onnx.convert_sklearn 関数の target_opset
パラメーターの設定が必要になる場合があります。 詳細について「Azure SQL Edge のリリース ノート」を参照し、リリースに対応する ONNX ランタイム バージョンを取得して、ONNX 下位互換性マトリックスに基づいて ONNX ランタイム用の target_opset
を選択してください。
ONNX モデルをテストする
モデルを ONNX 形式に変換したら、パフォーマンスの低下がほとんどまたはまったくないことを示すためにモデルにスコアを付けます。
Note
ONNX ランタイムは倍精度浮動小数点数ではなく浮動小数点数を使用するため、わずかな差異が生じる可能性があります。
import onnxruntime as rt
sess = rt.InferenceSession(onnx_model_path)
y_pred = np.full(shape=(len(x_train)), fill_value=np.nan)
for i in range(len(x_train)):
inputs = {}
for j in range(len(x_train.columns)):
inputs[x_train.columns[j]] = np.full(shape=(1,1), fill_value=x_train.iloc[i,j])
sess_pred = sess.run(None, inputs)
y_pred[i] = sess_pred[0][0][0]
onnx_r2_score = r2_score(y_train, y_pred)
onnx_mse = mean_squared_error(y_train, y_pred)
print()
print('*** Onnx r2 score: {}'.format(onnx_r2_score))
print('*** Onnx MSE: {}\n'.format(onnx_mse))
print('R2 Scores are equal' if sklearn_r2_score == onnx_r2_score else 'Difference in R2 scores: {}'.format(abs(sklearn_r2_score - onnx_r2_score)))
print('MSE are equal' if sklearn_mse == onnx_mse else 'Difference in MSE scores: {}'.format(abs(sklearn_mse - onnx_mse)))
print()
出力:
*** Onnx r2 score: 0.7406426691136831
*** Onnx MSE: 21.894830759270633
R2 Scores are equal
MSE are equal
ONNX モデルを挿入する
Azure SQL Edge の onnx
データベースの models
テーブルにモデルを格納します。 接続文字列に、サーバー アドレス、ユーザー名、パスワードを指定します。
import pyodbc
server = '' # SQL Server IP address
username = '' # SQL Server username
password = '' # SQL Server password
# Connect to the master DB to create the new onnx database
connection_string = "Driver={ODBC Driver 17 for SQL Server};Server=" + server + ";Database=master;UID=" + username + ";PWD=" + password + ";"
conn = pyodbc.connect(connection_string, autocommit=True)
cursor = conn.cursor()
database = 'onnx'
query = 'DROP DATABASE IF EXISTS ' + database
cursor.execute(query)
conn.commit()
# Create onnx database
query = 'CREATE DATABASE ' + database
cursor.execute(query)
conn.commit()
# Connect to onnx database
db_connection_string = "Driver={ODBC Driver 17 for SQL Server};Server=" + server + ";Database=" + database + ";UID=" + username + ";PWD=" + password + ";"
conn = pyodbc.connect(db_connection_string, autocommit=True)
cursor = conn.cursor()
table_name = 'models'
# Drop the table if it exists
query = f'drop table if exists {table_name}'
cursor.execute(query)
conn.commit()
# Create the model table
query = f'create table {table_name} ( ' \
f'[id] [int] IDENTITY(1,1) NOT NULL, ' \
f'[data] [varbinary](max) NULL, ' \
f'[description] varchar(1000))'
cursor.execute(query)
conn.commit()
# Insert the ONNX model into the models table
query = f"insert into {table_name} ([description], [data]) values ('Onnx Model',?)"
model_bits = onnx_model.SerializeToString()
insert_params = (pyodbc.Binary(model_bits))
cursor.execute(query, insert_params)
conn.commit()
データを読み込む
SQL にデータを読み込みます。
まず、features と target の 2 つのテーブルを作成して、Boston Housing データセットのサブセットを格納します。
- features には、ターゲットである中央値の予測に使用されるすべてのデータが格納されます。
- target には、データセット内の各レコードの中央値が格納されます。
import sqlalchemy
from sqlalchemy import create_engine
import urllib
db_connection_string = "Driver={ODBC Driver 17 for SQL Server};Server=" + server + ";Database=" + database + ";UID=" + username + ";PWD=" + password + ";"
conn = pyodbc.connect(db_connection_string)
cursor = conn.cursor()
features_table_name = 'features'
# Drop the table if it exists
query = f'drop table if exists {features_table_name}'
cursor.execute(query)
conn.commit()
# Create the features table
query = \
f'create table {features_table_name} ( ' \
f' [CRIM] float, ' \
f' [ZN] float, ' \
f' [INDUS] float, ' \
f' [CHAS] float, ' \
f' [NOX] float, ' \
f' [RM] float, ' \
f' [AGE] float, ' \
f' [DIS] float, ' \
f' [RAD] float, ' \
f' [TAX] float, ' \
f' [PTRATIO] float, ' \
f' [B] float, ' \
f' [LSTAT] float, ' \
f' [id] int)'
cursor.execute(query)
conn.commit()
target_table_name = 'target'
# Create the target table
query = \
f'create table {target_table_name} ( ' \
f' [MEDV] float, ' \
f' [id] int)'
x_train['id'] = range(1, len(x_train)+1)
y_train['id'] = range(1, len(y_train)+1)
print(x_train.head())
print(y_train.head())
最後に、sqlalchemy
を使用して、x_train
および y_train
pandas DataFrame をそれぞれ features
テーブルと target
テーブルに挿入します。
db_connection_string = 'mssql+pyodbc://' + username + ':' + password + '@' + server + '/' + database + '?driver=ODBC+Driver+17+for+SQL+Server'
sql_engine = sqlalchemy.create_engine(db_connection_string)
x_train.to_sql(features_table_name, sql_engine, if_exists='append', index=False)
y_train.to_sql(target_table_name, sql_engine, if_exists='append', index=False)
これで、データベース内のデータを表示できるようになりました。
ONNX モデルを使用して PREDICT を実行する
SQL 内のモデルで、アップロードされた ONNX モデルを使用して、データに対してネイティブ PREDICT を実行します。
Note
ノートブックのカーネルを SQL に変更して、残りのセルを実行します。
USE onnx
DECLARE @model VARBINARY(max) = (
SELECT DATA
FROM dbo.models
WHERE id = 1
);
WITH predict_input
AS (
SELECT TOP (1000) [id],
CRIM,
ZN,
INDUS,
CHAS,
NOX,
RM,
AGE,
DIS,
RAD,
TAX,
PTRATIO,
B,
LSTAT
FROM [dbo].[features]
)
SELECT predict_input.id,
p.variable1 AS MEDV
FROM PREDICT(MODEL = @model, DATA = predict_input, RUNTIME = ONNX) WITH (variable1 FLOAT) AS p;