使用 Azure Machine Learning 大規模定型 PyTorch 模型

適用於Python SDK azure-ai-ml v2 (目前)

在本文中,了解如何使用 Azure Machine Learning Python SDK v2 來執行 scikit-learn 定型指令碼。

本文中的範例指令碼用於分類鳶尾花影像,以根據 scikit-learn 的鳶尾花資料集來建立機器學習模型。

無論您是從頭開始定型機器學習 scikit-learn 模型,或是將現有的模型帶到雲端,您都可以使用 Azure Machine Learning,利用彈性的雲端計算資源來擴增開放原始碼定型作業。 您可以使用 Azure Machine Learning 建立、部署、版本設定和監視生產等級的模型。

必要條件

您可以使用 Azure Machine Learning 計算執行個體或您自己的 Jupyter Notebook 來執行本文的程式碼。

  • Azure Machine Learning 計算執行個體

    • 完成建立資源以開始使用以建立計算執行個體。 每個計算實例都包含預先載入 SDK 和 Notebooks 範例存放庫的專用筆記本伺服器。
    • 在 Azure Machine Learning 工作室中選取筆記本索引標籤。 在範例定型資料夾中,導覽至下列目錄來尋找已完整和擴充的筆記本:v2 > sdk > jobs > single-step > scikit-learn > train-hyperparameter-tune-deploy-with-sklearn
    • 您可以使用範例訓練資料夾中預先填入的程式代碼來完成本教學課程。
  • 您的 Jupyter 筆記本伺服器。

設定作業

本節透過載入必要的 Python 套件、連線至工作區、建立計算資源來執行命令作業,以及建立環境來執行作業,來設定定型作業。

連線到工作區

首先,您需要連線至您的 Azure Machine Learning 工作區。 Azure Machine Learning 工作區是服務的最上層資源。 其可以在您使用 Azure Machine Learning 時,提供集中式位置以處理您建立的所有成品。

我們正在使用 DefaultAzureCredential 來存取工作區。 此認證應該能夠處理大部分的 Azure SDK 驗證案例。

如果 DefaultAzureCredential 不適用於您,請參閱 azure-identity reference documentationSet up authentication 來取得更多可用的認證。

# Handle to the workspace
from azure.ai.ml import MLClient

# Authentication package
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()

如果您想要使用瀏覽器來登入並驗證,則應該移除下列程式碼中的註解,並改為使用瀏覽器。

# Handle to the workspace
# from azure.ai.ml import MLClient

# Authentication package
# from azure.identity import InteractiveBrowserCredential
# credential = InteractiveBrowserCredential()

接下來,提供您的訂用帳戶識別碼、資源群組名稱和工作區名稱,以取得工作區的控制代碼。 若要尋找這些參數:

  1. 在 Azure Machine Learning 工作室工具列的右上角,尋找您的工作區名稱。
  2. 選取您的工作區名稱以顯示您的資源群組和訂用帳戶識別碼。
  3. 將資源群組和訂用帳戶識別碼的值複製到程式碼中。
# Get a handle to the workspace
ml_client = MLClient(
    credential=credential,
    subscription_id="<SUBSCRIPTION_ID>",
    resource_group_name="<RESOURCE_GROUP>",
    workspace_name="<AML_WORKSPACE_NAME>",
)

執行此腳本的結果是您用來管理其他資源和作業的工作區句柄。

注意

建立 MLClient 時不會將用戶端連線至工作區。 用戶端初始化作業是緩慢的,會等第一次它所需要的時間到時才會進行呼叫。 在此文章中,這會在計算建立期間發生。

建立計算資源

Azure Machine Learning 需要計算資源才能執行工作。 此資源可以是採用 Linux 或 Windows OS 的單一或多節點機器,或 Spark 之類的特定計算網狀架構。

在下列範例指令碼中,我們會佈建 Linux compute cluster。 您可以查看 Azure Machine Learning pricing 頁面以取得 VM 大小和價格的完整清單。 我們只需要此範例的基本叢集;因此,我們會挑選具有 2 個 vCPU 核心和 7 GB RAM 的Standard_DS3_v2模型,以建立 Azure 機器學習 計算。

from azure.ai.ml.entities import AmlCompute

# Name assigned to the compute cluster
cpu_compute_target = "cpu-cluster"

try:
    # let's see if the compute target already exists
    cpu_cluster = ml_client.compute.get(cpu_compute_target)
    print(
        f"You already have a cluster named {cpu_compute_target}, we'll reuse it as is."
    )

except Exception:
    print("Creating a new cpu compute target...")

    # Let's create the Azure ML compute object with the intended parameters
    cpu_cluster = AmlCompute(
        name=cpu_compute_target,
        # Azure ML Compute is the on-demand VM service
        type="amlcompute",
        # VM Family
        size="STANDARD_DS3_V2",
        # Minimum running nodes when there is no job running
        min_instances=0,
        # Nodes in cluster
        max_instances=4,
        # How many seconds will the node running after the job termination
        idle_time_before_scale_down=180,
        # Dedicated or LowPriority. The latter is cheaper but there is a chance of job termination
        tier="Dedicated",
    )

    # Now, we pass the object to MLClient's create_or_update method
    cpu_cluster = ml_client.compute.begin_create_or_update(cpu_cluster).result()

print(
    f"AMLCompute with name {cpu_cluster.name} is created, the compute size is {cpu_cluster.size}"
)

建立作業環境

若要執行 Azure Machine Learning 作業,您需要環境。 Azure Machine Learning 環境會封裝相依性 (例如軟體執行時間和程式庫) 在您的計算資源上執行您的機器學習訓練指令碼。 此環境類似於您本機電腦上的 Python 環境。

Azure Machine Learning 可讓您使用策劃的 (或現成的) 環境,或是使用 Docker 映像或 Conda 設定來建立自訂環境。 在本文中,您會使用 Conda YAML 檔案為作業建立自定義環境。

建立自訂環境

若要建立自定義環境,您可以在 YAML 檔案中定義 Conda 相依性。 首先,建立一個用來儲存檔案的目錄。 在此範例中,我們已將目錄命名為 env

import os

dependencies_dir = "./env"
os.makedirs(dependencies_dir, exist_ok=True)

然後,在相依性目錄中建立檔案。 在此範例中,我們已將檔案命名為 conda.yml

%%writefile {dependencies_dir}/conda.yaml
name: sklearn-env
channels:
  - conda-forge
dependencies:
  - python=3.8
  - pip=21.2.4
  - scikit-learn=0.24.2
  - scipy=1.7.1
  - pip:  
    - azureml-mlflow==1.42.0
    - mlflow-skinny==2.3.2

規格包含您在作業中使用的一些一般套件(例如 numpy 和 pip)。

接下來,使用 YAML 檔案,在您的工作區中建立及登錄此自訂環境。 環境會在運行時間封裝成 Docker 容器。

from azure.ai.ml.entities import Environment

custom_env_name = "sklearn-env"

job_env = Environment(
    name=custom_env_name,
    description="Custom environment for sklearn image classification",
    conda_file=os.path.join(dependencies_dir, "conda.yaml"),
    image="mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04:latest",
)
job_env = ml_client.environments.create_or_update(job_env)

print(
    f"Environment with name {job_env.name} is registered to workspace, the environment version is {job_env.version}"
)

如需有關建立和使用環境的詳細資訊,請參閱在 Azure Machine Learning 中建立和使用軟體環境

[選擇性] 使用 Intel® Extension for Scikit-Learn 建立自訂環境

想要在 Intel 硬體上讓 scikit-learn 指令碼加速嗎? 請嘗試將 Intel® Extension for Scikit-Learn 新增至 conda yaml 檔案,並遵循上面詳述的後續步驟。 我們稍後會示範如何啟用這些優化,在此範例中:

%%writefile {dependencies_dir}/conda.yaml
name: sklearn-env
channels:
  - conda-forge
dependencies:
  - python=3.8
  - pip=21.2.4
  - scikit-learn=0.24.2
  - scikit-learn-intelex
  - scipy=1.7.1
  - pip:  
    - azureml-mlflow==1.42.0
    - mlflow-skinny==2.3.2

設定並提交您的定型作業

在本節中,我們將說明如何使用我們提供的定型腳本來執行定型作業。 若要開始,您可以藉由設定執行定型腳本的命令來建置定型作業。 然後,您會提交定型作業,以在 Azure 機器學習 中執行。

建立定型指令碼

在本文中,我們已提供定型指令碼 train_iris.py。 實務上,您應該能夠按原樣採用任何的自訂定型指令碼,並在不需修改程式碼的情況下,使用 Azure Machine Learning 來執行。

注意

所提供的定型指令碼會執行下列動作:

  • 顯示如何將某些計量記錄到您的 Azure Machine Learning 執行;
  • 使用 iris = datasets.load_iris() 下載並擷取定型資料;以及
  • 定型模型,然後儲存並註冊模型。

若要使用和存取您自己的資料,請參閱如何在作業中讀取和寫入資料,以在定型期間提供資料。

若要使用定型腳本,請先建立您要儲存盤案的目錄。

import os

src_dir = "./src"
os.makedirs(src_dir, exist_ok=True)

接下來,在來源目錄中建立指令碼檔案。

%%writefile {src_dir}/train_iris.py
# Modified from https://www.geeksforgeeks.org/multiclass-classification-using-scikit-learn/

import argparse
import os

# importing necessary libraries
import numpy as np

from sklearn import datasets
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split

import joblib

import mlflow
import mlflow.sklearn

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('--kernel', type=str, default='linear',
                        help='Kernel type to be used in the algorithm')
    parser.add_argument('--penalty', type=float, default=1.0,
                        help='Penalty parameter of the error term')

    # Start Logging
    mlflow.start_run()

    # enable autologging
    mlflow.sklearn.autolog()

    args = parser.parse_args()
    mlflow.log_param('Kernel type', str(args.kernel))
    mlflow.log_metric('Penalty', float(args.penalty))

    # loading the iris dataset
    iris = datasets.load_iris()

    # X -> features, y -> label
    X = iris.data
    y = iris.target

    # dividing X, y into train and test data
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

    # training a linear SVM classifier
    from sklearn.svm import SVC
    svm_model_linear = SVC(kernel=args.kernel, C=args.penalty)
    svm_model_linear = svm_model_linear.fit(X_train, y_train)
    svm_predictions = svm_model_linear.predict(X_test)

    # model accuracy for X_test
    accuracy = svm_model_linear.score(X_test, y_test)
    print('Accuracy of SVM classifier on test set: {:.2f}'.format(accuracy))
    mlflow.log_metric('Accuracy', float(accuracy))
    # creating a confusion matrix
    cm = confusion_matrix(y_test, svm_predictions)
    print(cm)

    registered_model_name="sklearn-iris-flower-classify-model"

    ##########################
    #<save and register model>
    ##########################
    # Registering the model to the workspace
    print("Registering the model via MLFlow")
    mlflow.sklearn.log_model(
        sk_model=svm_model_linear,
        registered_model_name=registered_model_name,
        artifact_path=registered_model_name
    )

    # # Saving the model to a file
    print("Saving the model via MLFlow")
    mlflow.sklearn.save_model(
        sk_model=svm_model_linear,
        path=os.path.join(registered_model_name, "trained_model"),
    )
    ###########################
    #</save and register model>
    ###########################
    mlflow.end_run()

if __name__ == '__main__':
    main()

[選擇性] 啟用 Intel® Extension for Scikit-Learn 最佳化,以在 Intel 硬體上獲得更多效能

如果您已安裝 Intel® Extension for Scikit-Learn (如上一節所示範),便可以在指令檔頂端新增兩行程式碼,以啟用效能最佳化,如下所示。

若要深入了解 Intel® Extension for Scikit-Learn,請瀏覽套件的文件

%%writefile {src_dir}/train_iris.py
# Modified from https://www.geeksforgeeks.org/multiclass-classification-using-scikit-learn/

import argparse
import os

# Import and enable Intel Extension for Scikit-learn optimizations
# where possible

from sklearnex import patch_sklearn
patch_sklearn()

# importing necessary libraries
import numpy as np


from sklearn import datasets
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split

import joblib

import mlflow
import mlflow.sklearn

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('--kernel', type=str, default='linear',
                        help='Kernel type to be used in the algorithm')
    parser.add_argument('--penalty', type=float, default=1.0,
                        help='Penalty parameter of the error term')

    # Start Logging
    mlflow.start_run()

    # enable autologging
    mlflow.sklearn.autolog()

    args = parser.parse_args()
    mlflow.log_param('Kernel type', str(args.kernel))
    mlflow.log_metric('Penalty', float(args.penalty))

    # loading the iris dataset
    iris = datasets.load_iris()

    # X -> features, y -> label
    X = iris.data
    y = iris.target

    # dividing X, y into train and test data
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

    # training a linear SVM classifier
    from sklearn.svm import SVC
    svm_model_linear = SVC(kernel=args.kernel, C=args.penalty)
    svm_model_linear = svm_model_linear.fit(X_train, y_train)
    svm_predictions = svm_model_linear.predict(X_test)

    # model accuracy for X_test
    accuracy = svm_model_linear.score(X_test, y_test)
    print('Accuracy of SVM classifier on test set: {:.2f}'.format(accuracy))
    mlflow.log_metric('Accuracy', float(accuracy))
    # creating a confusion matrix
    cm = confusion_matrix(y_test, svm_predictions)
    print(cm)

    registered_model_name="sklearn-iris-flower-classify-model"

    ##########################
    #<save and register model>
    ##########################
    # Registering the model to the workspace
    print("Registering the model via MLFlow")
    mlflow.sklearn.log_model(
        sk_model=svm_model_linear,
        registered_model_name=registered_model_name,
        artifact_path=registered_model_name
    )

    # # Saving the model to a file
    print("Saving the model via MLFlow")
    mlflow.sklearn.save_model(
        sk_model=svm_model_linear,
        path=os.path.join(registered_model_name, "trained_model"),
    )
    ###########################
    #</save and register model>
    ###########################
    mlflow.end_run()

if __name__ == '__main__':
    main()

建置定型作業

現在您已有執行工作所需的所有資產,接下來即可使用 Azure Machine Learning Python SDK 第 2 版來組建作業。 若要執行作業,我們會建立 command

Azure Machine Learning command 是一項資源,指定在雲端中執行定型程式碼所需的所有詳細資料。 這些詳細資料包括輸入和輸出、要使用的硬體類型、要安裝的軟體,以及如何執行程式碼。 command 包含執行單一命令的資訊。

設定命令

您可以使用一般用途 command 來執行定型腳本,並執行所需的工作。 建立 Command 物件以指定定型作業的設定詳細資料。

  • 此命令的輸入包括 epoch 數目、學習速率、訊號和輸出目錄。
  • 對於參數值:
    • 提供您為執行此命令所建立的計算叢集 cpu_compute_target = "cpu-cluster"
    • 提供您為執行 Azure Machine Learning 工作所建立的自訂環境 sklearn-env
    • 設定命令列動作本身,在此案例中命令為 python train_iris.py。 您可以透過 ${{ ... }} 標記法在命令中存取輸入和輸出;以及
    • 設定中繼資料 (例如顯示名稱和實驗名稱),其中實驗是在特定專案上執行之所有反覆項目的容器。 所有以相同實驗名稱提交的作業,都會在 Azure Machine Learning 工作室中相鄰列出。
from azure.ai.ml import command
from azure.ai.ml import Input

job = command(
    inputs=dict(kernel="linear", penalty=1.0),
    compute=cpu_compute_target,
    environment=f"{job_env.name}:{job_env.version}",
    code="./src/",
    command="python train_iris.py --kernel ${{inputs.kernel}} --penalty ${{inputs.penalty}}",
    experiment_name="sklearn-iris-flowers",
    display_name="sklearn-classify-iris-flower-images",
)

提交作業

接著即可提交作業,以在 Azure Machine Learning 中執行。 這次您會在 ml_client.jobs 上使用 create_or_update

ml_client.jobs.create_or_update(job)

完成後,作業會在工作區中註冊模型(因為定型的結果),並輸出連結以在 Azure Machine Learning 工作室 檢視作業。

警告

Azure Machine Learning 藉由複製整個來源目錄來執行定型指令碼。 如果您不想上傳敏感性資料,請使用 .ignore 檔案,或不要將敏感性資料放入來源目錄中。

作業執行期間發生的情況

作業執行時,會經歷下列階段:

  • 準備:根據定義的環境來建置 Docker 映像。 映像上傳至工作區的容器登錄,並快取以供稍後執行。 記錄也會串流至執行歷程記錄,並可檢視以監視進度。 如果指定策展環境,則會使用支援該策展環境的快取映像。

  • 縮放:如果叢集需要更多節點來執行該執行比目前可用的節點,則叢集會嘗試擴大規模。

  • 執行中:系統會將指令碼資料夾 [src] 中的所有指令碼上傳至計算目標,掛接或複製資料存放區,然後執行指令碼。 stdout./logs 資料夾的輸出都會串流到執行歷程記錄,並且可用來監視執行。

微調模型超參數

既然您已了解如何使用 SDK 執行簡單 Scikit-learn 定型執行,讓我們看看您是否可以進一步改善模型的正確性。 您可以使用 Azure Machine Learning 的sweep功能來微調和最佳化模型的超參數。

若要微調模型的超參數,請定義於定型期間要在其中搜尋的參數空間。 您可以藉由以套件的特殊輸入azure.ml.sweep取代傳遞至定型作業的一些參數來kernelpenalty執行此操作。

from azure.ai.ml.sweep import Choice

# we will reuse the command_job created before. we call it as a function so that we can apply inputs
# we do not apply the 'iris_csv' input again -- we will just use what was already defined earlier
job_for_sweep = job(
    kernel=Choice(values=["linear", "rbf", "poly", "sigmoid"]),
    penalty=Choice(values=[0.5, 1, 1.5]),
)

然後,您可以使用某些掃掠特定參數,在命令作業上設定掃掠,例如要監看的主要計量,以及要使用的取樣演算法。

在下列程式碼中,我們會使用隨機取樣來嘗試不同的超參數設定組,以嘗試將主要計量 Accuracy 最大化。

sweep_job = job_for_sweep.sweep(
    compute="cpu-cluster",
    sampling_algorithm="random",
    primary_metric="Accuracy",
    goal="Maximize",
    max_total_trials=12,
    max_concurrent_trials=4,
)

現在,您可以如先前一樣提交此作業。 這一次,您正在執行掃掠作業,以掃掠您的訓練作業。

returned_sweep_job = ml_client.create_or_update(sweep_job)

# stream the output and wait until the job is finished
ml_client.jobs.stream(returned_sweep_job.name)

# refresh the latest status of the job after streaming
returned_sweep_job = ml_client.jobs.get(name=returned_sweep_job.name)

您可以使用在作業執行期間呈現的工作室使用者介面連結來監視作業。

尋找並註冊最佳的模型

一旦完成所有執行,您就可以找到產生模型且精確度最高的回合。

from azure.ai.ml.entities import Model

if returned_sweep_job.status == "Completed":

    # First let us get the run which gave us the best result
    best_run = returned_sweep_job.properties["best_child_run_id"]

    # lets get the model from this run
    model = Model(
        # the script stores the model as "sklearn-iris-flower-classify-model"
        path="azureml://jobs/{}/outputs/artifacts/paths/sklearn-iris-flower-classify-model/".format(
            best_run
        ),
        name="run-model-example",
        description="Model created from run.",
        type="custom_model",
    )

else:
    print(
        "Sweep job status: {}. Please wait until it completes".format(
            returned_sweep_job.status
        )
    )

然後,您可以註冊此模型。

registered_model = ml_client.models.create_or_update(model=model)

部署模型

註冊模型之後,其部署方式會與 Azure Machine Learning 中任何其他已註冊的模型相同。 如需部署的詳細資訊,請參閱使用 Python SDK 第 2 版,搭配受控線上端點部署和評分機器學習模型

下一步

在本文中,您已定型並註冊 scikit-learn 模型,並了解部署選項。 若要深入了解 Azure Machine Learning,請參閱下列其他文章。