添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Azure SQL Edge 不再支援 ARM64 平台。

在本快速入門中,您將了解如何將模型定型、將其轉換為 ONNX、將其部署至 Azure SQL Edge ,然後使用上傳的 ONNX 模型對資料執行原生 PREDICT。

本快速入門以 scikit-learn 為基礎,並使用 Boston Housing 資料集

  • 如果您正在使用 Azure SQL Edge,並且尚未部署 Azure SQL Edge 模組,請依照 使用 Azure 入口網站部署 SQL Edge 中的步驟操作。

  • 安裝 Azure Data Studio

  • 安裝此快速入門所需的 Python 套件。

  • 開啟連線至 Python 3 核心的 新筆記本
  • 選取 [管理套件]
  • [已安裝] 索引標籤中,在已安裝的套件清單中尋找下列 Python 套件。 如果未安裝上述任何套件,請選取 [新增新的] 索引標籤、搜尋套件,然後選取 [安裝]
  • scikit-learn
  • numpy
  • onnxmltools
  • onnxruntime
  • pyodbc
  • setuptools
  • skl2onnx
  • sqlalchemy
  • 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)
    

    如果 SQL Edge 和 skl2onnx 套件中的 ONNX 執行時間版本不符,您可能需要設定 target_opset skl2onnx.convert_sklearn 函式的參數。 如需詳細資訊,請參閱 SQL Edge 發行備註 取得版本對應的 ONNX 執行階段版本,並根據 ONNX 回溯相容性矩陣來挑選 ONNX 執行階段的 target_opset

    測試 ONNX 模型

    將模型轉換成 ONNX 格式之後,請對模型進行評分,以顯示效能略為降低或不受影響的情況。

    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 中的模型儲存在資料庫 onnxmodels 資料表中。 在連接字串中,指定伺服器位址使用者名稱密碼

    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。

    首先,建立 featurestarget 這兩個資料表,以儲存 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())
    

    最後,使用 sqlalchemyx_trainy_train 的 pandas 資料框架分別插入資料表 featurestarget 中。

    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。

    將筆記本核心變更為 SQL,以執行其餘的資料格。

    USE onnx
    DECLARE @model VARBINARY(max) = (
            SELECT DATA
            FROM dbo.models
            WHERE id = 1
    WITH predict_input
        SELECT TOP (1000) [id],
            CRIM,
            INDUS,
            CHAS,
            PTRATIO,
            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;
    
  • SQL Edge 中採用 ONNX 的機器學習和 AI
  •