Azure SQL Edge (プレビュー) での ONNX モデルを使用したデプロイと予測Deploy and make predictions with an ONNX model in Azure SQL Edge (Preview)

このクイックスタートでは、モデルをトレーニングし、ONNX に変換して、Azure SQL Edge (プレビュー) にデプロイした後、アップロードされた ONNX モデルを使用してデータに対してネイティブ PREDICT を実行する方法について説明します。In this quickstart, you'll learn how to train a model, convert it to ONNX, deploy it to Azure SQL Edge (Preview), and then run native PREDICT on data using the uploaded ONNX model. 詳細については、「SQL Edge (プレビュー) での ONNX を使用した機械学習と AI」を参照してください。For more information, see Machine learning and AI with ONNX in SQL Edge (Preview).

このクイックスタートは scikit-learn に基づいており、Boston Housing データセットを使用します。This quickstart is based on scikit-learn and uses the Boston Housing dataset.

開始する前にBefore you begin

  • Azure SQL Edge モジュールをデプロイしていない場合は、Azure portal を使用して SQL Edge (プレビュー) をデプロイする手順に従います。If you haven't deployed an Azure SQL Edge module, follow the steps of deploy SQL Edge (Preview) using the Azure portal.

  • Azure Data Studio をインストールします。Install Azure Data Studio.

  • Azure Data Studio を開き、次の手順に従って、このクイックスタートに必要なパッケージをインストールします。Open Azure Data Studio and follow these steps to install the packages needed for this quickstart:

    1. Python 3 カーネルに接続された新しいノートブックを開きます。Open New Notebook connected to the Python 3 Kernel.
    2. [パッケージの管理] をクリックし、 [新規追加]scikit-learn を検索して、scikit-learn パッケージをインストールします。Click Manage Packages and under Add New, search for scikit-learn, and install the scikit-learn package.
    3. また、setuptoolsnumpyonnxmltoolsonnxruntimeskl2onnxpyodbc、および sqlalchemy パッケージもインストールします。Also, install the setuptools, numpy, onnxmltools, onnxruntime, skl2onnx, pyodbc, and sqlalchemy packages.
  • Azure Data Studio ノートブックのセルに以下の各スクリプト部分を入力し、セルを実行します。For each script part below, enter it in a cell in the Azure Data Studio notebook and run the cell.

パイプラインをトレーニングするTrain a pipeline

データセットを分割し、特徴量を使用して住宅の中央値を予測します。Split the dataset to use features to predict the median value of a house.

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())

出力:Output:

*** 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 モデルをトレーニングするパイプラインを作成します。Create a pipeline to train the LinearRegression model. 他の回帰モデルを使用することもできます。You can also use other regression models.

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 スコアと平均二乗誤差を計算します。Check the accuracy of the model and then calculate the R2 score and mean squared error.

# 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))

出力:Output:

*** Scikit-learn r2 score: 0.7406426641094094
*** Scikit-learn MSE: 21.894831181729206

モデルを ONNX に変換するConvert the model to ONNX

データ型をサポートされている SQL データ型に変換します。Convert the data types to the supported SQL data types. この変換は他のデータフレームにも必要になります。This conversion will be required for other dataframes as well.

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 形式に変換し、ローカルに保存します。Using skl2onnx, convert the LinearRegression model to the ONNX format and save it locally.

# Convert the scikit model to onnx format
onnx_model = skl2onnx.convert_sklearn(model, 'Boston Data', convert_dataframe_schema(x_train))
# Save the onnx model locally
onnx_model_path = 'boston1.model.onnx'
onnxmltools.utils.save_model(onnx_model, onnx_model_path)

ONNX モデルをテストするTest the ONNX model

モデルを ONNX 形式に変換したら、パフォーマンスの低下がほとんどまたはまったくないことを示すためにモデルにスコアを付けます。After converting the model to ONNX format, score the model to show little to no degradation in performance.

注意

ONNX ランタイムは倍精度浮動小数点数ではなく浮動小数点数を使用するため、わずかな差異が生じる可能性があります。ONNX Runtime uses floats instead of doubles so small discrepancies are possible.

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()

出力:Output:

*** Onnx r2 score: 0.7406426691136831
*** Onnx MSE: 21.894830759270633

R2 Scores are equal
MSE are equal

ONNX モデルを挿入するInsert the ONNX model

Azure SQL Edge の onnx データベースの models テーブルにモデルを格納します。Store the model in Azure SQL Edge, in a models table in a database onnx. 接続文字列に、サーバー アドレスユーザー名パスワードを指定します。In the connection string, specify the server address, username, and password.

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()

データを読み込むLoad the data

Azure SQL Edge にデータを読み込みます。Load the data into Azure SQL Edge.

まず、featurestarget の 2 つのテーブルを作成して、Boston Housing データセットのサブセットを格納します。First, create two tables, features and target, to store subsets of the Boston housing dataset.

  • features には、ターゲットである中央値の予測に使用されるすべてのデータが格納されます。Features contains all data being used to predict the target, median value.
  • target には、データセット内の各レコードの中央値が格納されます。Target contains the median value for each record in the dataset.
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 テーブルに挿入します。Finally, use sqlalchemy to insert the x_train and y_train pandas dataframes into the tables features and target, respectively.

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)

これで、データベース内のデータを表示できるようになりました。Now you can view the data in the database.

ONNX モデルを使用して PREDICT を実行するRun PREDICT using the ONNX model

Azure SQL Edge のモデルで、アップロードされた ONNX モデルを使用して、データに対してネイティブ PREDICT を実行します。With the model in Azure SQL Edge, run native PREDICT on the data using the uploaded ONNX model.

注意

ノートブックのカーネルを SQL に変更して、残りのセルを実行します。Change the notebook kernel to SQL to run the remaining cell.

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 [onnx].[dbo].[features]
    )
SELECT predict_input.id
    , p.variable1 AS MEDV
FROM PREDICT(MODEL = @model, DATA = predict_input, RUNTIME=ONNX) WITH (variable1 FLOAT) AS p

次の手順Next Steps