ONNX modeli ve SQL makine öğrenmesi ile dağıtma ve tahminde bulunma

Önemli

Azure SQL Edge artık ARM64 platformunu desteklememektedir.

Bu hızlı başlangıçta bir modeli eğitmeyi, ONNX'e dönüştürmeyi, Azure SQL Edge'e dağıtmayı ve ardından karşıya yüklenen ONNX modelini kullanarak veriler üzerinde yerel PREDICT çalıştırmayı öğreneceksiniz.

Bu hızlı başlangıç scikit-learn tabanlıdır ve Boston Housing veri kümesini kullanır.

Başlamadan önce

  • Azure SQL Edge kullanıyorsanız ve bir Azure SQL Edge modülü dağıtmadıysanız Azure portalını kullanarak SQL Edge'i dağıtma adımlarını izleyin.

  • Azure Data Studio'yu yükleyin.

  • Bu hızlı başlangıç için gereken Python paketlerini yükleyin:

    1. Python 3 Çekirdeğine bağlı Yeni Not Defteri'ni açın.
    2. Paketleri Yönet'i seçin
    3. Yüklü sekmesinde, yüklü paketler listesinde aşağıdaki Python paketlerini arayın. Bu paketlerden herhangi biri yüklü değilse Yeni Ekle sekmesini seçin, paketi arayın ve Yükle'yi seçin.
      • scikit-learn
      • numpy
      • onnxmltools
      • onnxruntime
      • pyodbc
      • kurulum araçları
      • skl2onnx
      • sqlalchemy
  • Aşağıdaki bölümlerde yer alan her betik bölümü için, bunu Azure Data Studio not defterindeki bir hücreye girin ve hücreyi çalıştırın.

İşlem hattını eğitin

Bir evin ortanca değerini tahmin etmek için özellikleri kullanmak için veri kümesini bölün.

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

Çıkış:

*** 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 modelini eğitmek için bir işlem hattı oluşturun. Diğer regresyon modellerini de kullanabilirsiniz.

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)

Modelin doğruluğunu denetleyin ve R2 puanını ve ortalama hata karesini hesaplayın.

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

Çıkış:

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

Modeli ONNX'e dönüştürme

Veri türlerini desteklenen SQL veri türlerine dönüştürün. Bu dönüştürme diğer veri çerçeveleri için de gereklidir.

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

kullanarak skl2onnxLinearRegression modelini ONNX biçimine dönüştürün ve yerel olarak kaydedin.

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

Dekont

SQL Edge'de ONNX çalışma zamanı sürümü ile skl2onnx packge arasında uyuşmazlık varsa, skl2onnx.convert_sklearn işlevinin parametresini ayarlamanız target_opset gerekebilir. Daha fazla bilgi için, sürüme karşılık gelen ONNX çalışma zamanı sürümünü almak için SQL Edge Sürüm notlarına bakın ve ONNX geriye dönük uyumluluk matrisini temel alan ONNX çalışma zamanı için öğesini seçintarget_opset.

ONNX modelini test etme

Modeli ONNX biçimine dönüştürdükten sonra, performansta çok az veya hiç düşüş göstermemek için modeli puanlar.

Dekont

ONNX Çalışma Zamanı, küçük tutarsızlıkların mümkün olması için çift yerine float kullanır.

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

Çıkış:

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

R2 Scores are equal
MSE are equal

ONNX modelini ekleme

Modeli Azure SQL Edge'de, veritabanındaki bir models tabloda depolayın onnx. bağlantı dizesi sunucu adresini, kullanıcı adını ve parolayı belirtin.

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

Verileri yükleme

Verileri SQL'e yükleyin.

İlk olarak, Boston konut veri kümesinin alt kümelerini depolamak için iki tablo (özellikler ve hedef) oluşturun.

  • Özellikler , hedef, ortanca değeri tahmin etmek için kullanılan tüm verileri içerir.
  • Hedef , veri kümesindeki her kaydın ortanca değerini içerir.
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())

Son olarak, ve y_train pandas veri çerçevelerini sırasıyla ve targettablolarına features eklemek x_train için kullanınsqlalchemy.

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)

Artık veritabanındaki verileri görüntüleyebilirsiniz.

ONNX modelini kullanarak PREDICT çalıştırma

SQL'deki modelle, karşıya yüklenen ONNX modelini kullanarak verilerde yerel PREDICT komutunu çalıştırın.

Dekont

Kalan hücreyi çalıştırmak için not defteri çekirdeğini SQL olarak değiştirin.

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;

Sonraki adımlar