Azure Machine Learning を使用して PyTorch モデルを大規模にトレーニングする

適用対象: Python SDK azure-ai-ml v2 (現行)

この記事では、Azure Machine Learning Python SDK v2 を使って PyTorch モデルのトレーニング、ハイパーパラメーター チューニング、デプロイを行う方法について説明します。

ニワトリと七面鳥の画像を分類するサンプル スクリプトを使い、PyTorch の転移学習チュートリアルに基づいて、ディープ ラーニング ニューラル ネットワーク (DNN) を構築します。 転移学習は、ある問題を解決することで得られた知識を、異なるが関連している問題に適用する手法です。 転移学習では、最初からトレーニングするよりも必要なデータ、時間、コンピューティング リソースが少なくなるので、トレーニング プロセスを短縮できます。 転移学習の詳細については、ディープ ラーニングと機械学習に関する情報を参照してください。

PyTorch のディープ ラーニング モデルを一からトレーニングする場合でも、既存のモデルをクラウドに持ち込む場合でも、Azure Machine Learning のエラスティック クラウド コンピューティング リソースを使用して、オープンソースのトレーニング ジョブをスケールアウトできます。 Azure Machine Learning を使用して、運用レベルのモデルをビルド、デプロイ、バージョン管理、および監視することができます。

前提条件

  • Azure サブスクリプション。 アカウントがまだない場合は、無料のアカウントを作成してください。
  • この記事のコードは、Azure Machine Learning コンピューティング インスタンスまたは独自の Jupyter Notebook のいずれかで実行します。
    • Azure Machine Learning コンピューティング インスタンス (ダウンロードやインストールは必要なし):
      • クイック スタート: Azure Machine Learning の利用の開始を完了して、SDK およびサンプル リポジトリが事前に読み込まれた専用のノートブック サーバーを作成します。
      • ワークスペースの [ノートブック] セクションにある [サンプル] タブで、SDK v2/sdk/python/jobs/single-step/pytorch/train-hyperparameter-tune-deploy-with-pytorch ディレクトリに移動して、完成した展開済みのノートブックを見つけます
    • お使いの Jupyter Notebook サーバー:

このガイドの完成した Jupyter Notebook バージョンは、GitHub サンプル ページにもあります。

この記事のコードを実行して GPU クラスターを作成する前に、ワークスペースのクォータの引き上げを要求する必要があります。

ジョブをセットアップする

このセクションでは、必要な Python パッケージを読み込み、ワークスペースに接続し、コマンド ジョブを実行するコンピューティング リソースを作成し、ジョブを実行する環境を作成することで、トレーニング用のジョブを設定します。

ワークスペースに接続する

まず、Azure Machine Learning ワークスペースに接続する必要があります。 ワークスペースは、サービス用の最上位のリソースです。 Azure Machine Learning を使用するときに作成する、すべての成果物を操作するための一元的な場所が提供されます。

ここでは、DefaultAzureCredential を使ってワークスペースへのアクセスを取得しています。 この資格情報を使うと、ほとんどの Azure SDK 認証シナリオに対応できます。

DefaultAzureCredential でうまくいかない場合は、他に使用可能な資格情報について、azure.identity パッケージまたは「認証の設定」を参照してください。

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

次に、サブスクリプション ID、リソース グループ名、ワークスペース名を指定して、ワークスペースへのハンドルを取得します。 これらのパラメーターを見つけるには、次の手順を実行します。

  1. Azure Machine Learning スタジオ ツール バーの右上隅にあるワークスペース名を探します。
  2. ワークスペース名を選択して、リソース グループとサブスクリプション ID を表示します。
  3. リソース グループとサブスクリプション ID の値をコードにコピーします。
# 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>",
)

このスクリプトの実行結果は、他のリソースとジョブを管理するために使用できるワークスペース ハンドルになります。

Note

MLClient を作成しても、クライアントはワークスペースに接続されません。 クライアントの初期化は遅延型なので、初めて呼び出す必要が生じるときまで待機します。 この記事では、これはコンピューティングの作成時に発生します。

ジョブを実行するコンピューティング リソースを作成する

Azure Machine Learning には、ジョブを実行するためのコンピューティング リソースが必要です。 このリソースには、Linux または Windows OS の単一または複数ノードのマシン、または Spark などの特定のコンピューティング ファブリックを指定できます。

次のスクリプト例では、Linux コンピューティング クラスターをプロビジョニングします。 VM のサイズと価格の完全な一覧については、「Azure Machine Learning の価格」ページをご覧ください。 この例には GPU クラスターが必要なので、STANDARD_NC6 モデルを選択して Azure Machine Learning コンピューティングを作成してみましょう。

from azure.ai.ml.entities import AmlCompute

gpu_compute_target = "gpu-cluster"

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

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

    # Let's create the Azure ML compute object with the intended parameters
    gpu_cluster = AmlCompute(
        # Name assigned to the compute cluster
        name="gpu-cluster",
        # Azure ML Compute is the on-demand VM service
        type="amlcompute",
        # VM Family
        size="STANDARD_NC6s_v3",
        # 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
    gpu_cluster = ml_client.begin_create_or_update(gpu_cluster).result()

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

ジョブ環境を作成する

Azure Machine Learning ジョブを実行するには、環境が必要です。 Azure Machine Learning 環境では、コンピューティング リソースで機械学習トレーニング スクリプトを実行するために必要な依存関係 (ソフトウェア ランタイムやライブラリなど) がカプセル化されます。 この環境は、ローカル コンピューター上の Python 環境に似ています。

Azure Machine Learning では、キュレーションされた (または既製の) 環境を使うか、Docker イメージまたは Conda 構成を使ってカスタム環境を作成できます。 この記事では、キュレーションされた Azure Machine Learning 環境 AzureML-pytorch-1.9-ubuntu18.04-py37-cuda11-gpu を再利用します。 @latest ディレクティブを使って、この環境の最新バージョンを使用します。

curated_env_name = "AzureML-pytorch-1.9-ubuntu18.04-py37-cuda11-gpu@latest"

トレーニング ジョブを構成して送信する

このセクションでは、まずトレーニング用のデータを紹介します。 次に、提供したトレーニング スクリプトを使用して、トレーニング ジョブを実行する方法について説明します。 トレーニング スクリプトを実行するためのコマンドを構成して、トレーニング ジョブをビルドする方法について説明します。 次に、トレーニング ジョブを送信して Azure Machine Learning で実行します。

トレーニング データを取得する

この ZIP ファイルのデータセットを使用できます。 このデータセットは、2 つのクラス (七面鳥と鶏) のそれぞれについて約 120 個のトレーニング画像と、クラスごとに 100 個の検証用画像で構成されています。 画像は Open Images v5 Dataset のサブセットです。 トレーニング スクリプト pytorch_train.py でデータセットをダウンロードして抽出します。

トレーニング スクリプトを準備する

前提条件のセクションで、トレーニング スクリプト pytorch_train.py を提供しました。 実際には、コードを変更しなくても、あらゆるカスタム トレーニング スクリプトを "そのまま" Azure Machine Learning で実行できるはずです。

提供されているトレーニング スクリプトによって、データのダウンロード、モデルのトレーニング、モデルの登録が行われます。

トレーニング ジョブを作成する

ジョブの実行に必要なすべての資産が揃ったので、次は Azure Machine Learning Python SDK v2 を使ってジョブを作成します。 この例では、command を使用します。

Azure Machine Learning command は、クラウドでトレーニング コードを実行するために必要なすべての詳細を指定するリソースです。 これらの詳細には、入力と出力、使用するハードウェアの種類、インストールするソフトウェア、コードの実行方法が含まれます。 command には、1 つのコマンドを実行するための情報が含まれています。

コマンドを構成する

汎用 command を使用してトレーニング スクリプトを実行し、目的のタスクを実行します。 トレーニング ジョブの構成の詳細を指定する command オブジェクトを作成します。

from azure.ai.ml import command
from azure.ai.ml import Input

job = command(
    inputs=dict(
        num_epochs=30, learning_rate=0.001, momentum=0.9, output_dir="./outputs"
    ),
    compute=gpu_compute_target,
    environment=curated_env_name,
    code="./src/",  # location of source code
    command="python pytorch_train.py --num_epochs ${{inputs.num_epochs}} --output_dir ${{inputs.output_dir}}",
    experiment_name="pytorch-birds",
    display_name="pytorch-birds-image",
)
  • このコマンドの入力には、エポックの数、学習速度、モメンタム、出力ディレクトリが含まれます。
  • パラメーター値については次のとおりです。
    1. このコマンドを実行するために作成したコンピューティング クラスター gpu_compute_target = "gpu-cluster" を指定します。
    2. 前に初期化したキュレーション環境 AzureML-pytorch-1.9-ubuntu18.04-py37-cuda11-gpu を指定します。
    3. Samples フォルダーで完成したノートブックを使用していない場合は、pytorch_train.py ファイルの場所を指定します。
    4. コマンド ライン アクション自体を構成します。この場合、コマンドは python pytorch_train.py です。 コマンドの入力と出力には ${{ ... }} 表記法を使用してアクセスできます。
    5. 表示名や実験名などのメタデータを構成します。ここで、実験は特定のプロジェクトで行われるすべてのイテレーションのコンテナーです。 同じ実験名で送信されたすべてのジョブは、Azure Machine Learning スタジオ上ではすべて隣り合わせに表示されます。

ジョブを送信する

次に、Azure Machine Learning で実行するジョブを送信します。 今回は、ml_client.jobscreate_or_update を使用します。

ml_client.jobs.create_or_update(job)

完了すると、ジョブは (トレーニングの結果として) モデルをワークスペースに登録し、Azure Machine Learning スタジオでジョブを表示するためのリンクを出力します。

警告

Azure Machine Learning では、ソース ディレクトリ全体をコピーすることで、トレーニング スクリプトが実行されます。 アップロードしたくない機密データがある場合は、.ignore ファイルを使用するか、ソース ディレクトリに含めないようにします。

ジョブ実行中の動作

ジョブの実行は、以下の段階を経て実施されます。

  • 準備:Docker イメージは、定義されている環境に従って作成されます。 イメージはワークスペースのコンテナー レジストリにアップロードされ、後で実行するためにキャッシュされます。 ログもジョブ履歴にストリーミングされ、表示して進行状況を監視できます。 キュレーションされた環境が指定されている場合は、そのキュレーションされた環境を補足するキャッシュ済みのイメージが使用されます。

  • スケーリング: 実行に現在使用可能な数より多くのノードが必要な場合、クラスターはスケールアップを試みます。

  • 実行中: スクリプト フォルダー src 内のすべてのスクリプトがコンピューティング先にアップロードされ、データ ストアがマウントまたはコピーされて、スクリプトが実行されます。 stdout からの出力と ./logs フォルダーがジョブ履歴にストリーミングされるので、ジョブの監視のために使用できます。

モデルのハイパーパラメーターの調整

1 組のパラメーターを使用してモデルをトレーニングしたので、モデルの精度をさらに上げることができるかどうかを見てみましょう。 Azure Machine Learning の sweep 機能を使用して、モデルのハイパーパラメーターを調整して最適化できます。

モデルのハイパーパラメーターを調整するには、トレーニング中に検索するパラメーター空間を定義します。 これを行うには、トレーニング ジョブに渡されるパラメーターの一部を azure.ml.sweep パッケージからの特別な入力に置き換えます。

トレーニング スクリプトでは、学習率スケジュールを使って数エポックごとに学習率を減衰させるので、初期学習率とモメンタム パラメーターをチューニングできます。

from azure.ai.ml.sweep import Uniform

# we will reuse the command_job created before. we call it as a function so that we can apply inputs
job_for_sweep = job(
    learning_rate=Uniform(min_value=0.0005, max_value=0.005),
    momentum=Uniform(min_value=0.9, max_value=0.99),
)

次に、監視する主要メトリックや使用するサンプリング アルゴリズムなど、いくつかのスイープ固有のパラメーターを使用して、コマンド ジョブのスイープを構成できます。

次のコードでは、ランダム サンプリングを使用して、主要メトリック best_val_acc を最大化するために、さまざまな構成セットのハイパーパラメーターを試します。

また、パフォーマンスの低い実行を早期に終了するために早期終了ポリシー BanditPolicy を定義します。 BanditPolicy は、プライマリ評価メトリックの Slack 係数内にならないすべての実行を終了します。 このポリシーをすべてのエポックに適用します (エポックごとに best_val_acc メトリックを報告し、evaluation_interval=1 であるため)。 最初の 10 エポック (delay_evaluation=10) の後まで最初のポリシー評価を延期することに注意してください。

from azure.ai.ml.sweep import BanditPolicy

sweep_job = job_for_sweep.sweep(
    compute="gpu-cluster",
    sampling_algorithm="random",
    primary_metric="best_val_acc",
    goal="Maximize",
    max_total_trials=8,
    max_concurrent_trials=4,
    early_termination_policy=BanditPolicy(
        slack_factor=0.15, evaluation_interval=1, delay_evaluation=10
    ),
)

これで、以前と同様にこのジョブを送信できます。 今回は、トレーニング ジョブをスイープするスイープ ジョブを実行します。

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 "outputs"
        path="azureml://jobs/{}/outputs/artifacts/paths/outputs/".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
        )
    )

モデルをオンライン エンドポイントとしてデプロイする

これで、オンライン エンドポイント (つまり、Azure クラウドの Web サービス) としてモデルをデプロイできます。

通常、機械学習サービスをデプロイするには、以下のものが必要です。

  • デプロイするモデル資産。 これらの資産には、トレーニング ジョブに既に登録したモデルのファイルとメタデータが含まれます。
  • サービスとして実行するコード。 コードは、特定の入力要求 (エントリ スクリプト) でモデルを実行します。 このエントリ スクリプトは、デプロイされた Web サービスに送信されたデータを受け取り、それをモデルに渡します。 モデルがデータを処理した後、スクリプトはモデルの応答をクライアントに返します。 スクリプトはこのモデルに固有で、モデルが受け入れ、返すデータを理解する必要があります。 MLFlow モデルを使用すると、Azure Machine Learning によってこのスクリプトが自動的に作成されます。

デプロイの詳細については、「Python SDK v2 を使用して、マネージド オンライン エンドポイントに機械学習モデルをデプロイしてスコアリングする」を参照してください。

新しいオンライン エンドポイントを作成する

モデルをデプロイする最初の手順として、オンライン エンドポイントを作成する必要があります。 エンドポイント名は、Azure リージョン全体で一意である必要があります。 この記事では、汎用一意識別子 (UUID) を使用して一意の名前を作成します。

import uuid

# Creating a unique name for the endpoint
online_endpoint_name = "aci-birds-endpoint-" + str(uuid.uuid4())[:8]
from azure.ai.ml.entities import ManagedOnlineEndpoint

# create an online endpoint
endpoint = ManagedOnlineEndpoint(
    name=online_endpoint_name,
    description="Classify turkey/chickens using transfer learning with PyTorch",
    auth_mode="key",
    tags={"data": "birds", "method": "transfer learning", "framework": "pytorch"},
)

endpoint = ml_client.begin_create_or_update(endpoint).result()

print(f"Endpoint {endpoint.name} provisioning state: {endpoint.provisioning_state}")

エンドポイントを作成したら、次のようにして取得できます。

endpoint = ml_client.online_endpoints.get(name=online_endpoint_name)

print(
    f'Endpint "{endpoint.name}" with provisioning state "{endpoint.provisioning_state}" is retrieved'
)

モデルをエンドポイントにデプロイする

これで、エントリ スクリプトを使用してモデルをデプロイできるようになりました。 1 つのエンドポイントに複数のデプロイを設定できます。 ルールを使用すると、エンドポイントはトラフィックをこれらのデプロイに転送できます。

次のコードでは、受信トラフィックの 100% を処理する 1 つのデプロイを作成します。 デプロイに任意の色名 (aci-blue) を指定しました。 デプロイには、aci-greenaci-red などの他の名前を使用することもできます。

モデルをエンドポイントにデプロイするコードで次のことが行われます。

  • 前に登録したモデルの最適なバージョンをデプロイする。
  • score.py ファイルを使用してモデルをスコア付けする。
  • 推論を実行するために (前に指定した) 同じキュレーション環境を使用する。
from azure.ai.ml.entities import (
    ManagedOnlineDeployment,
    Model,
    Environment,
    CodeConfiguration,
)

online_deployment_name = "aci-blue"

# create an online deployment.
blue_deployment = ManagedOnlineDeployment(
    name=online_deployment_name,
    endpoint_name=online_endpoint_name,
    model=model,
    environment=curated_env_name,
    code_configuration=CodeConfiguration(code="./score/", scoring_script="score.py"),
    instance_type="Standard_NC6s_v3",
    instance_count=1,
)

blue_deployment = ml_client.begin_create_or_update(blue_deployment).result()

Note

このデプロイが完了するまでに少し時間がかかると想定してください。

デプロイしたモデルをテストする

エンドポイントにモデルをデプロイしたので、エンドポイントの invoke メソッドを使用して、デプロイされたモデルの出力を予測できます。

エンドポイントをテストするため、予測にサンプル画像を使ってみましょう。 まず、画像を表示してみましょう。

# install pillow if PIL cannot imported
%pip install pillow
import json
from PIL import Image
import matplotlib.pyplot as plt

%matplotlib inline
plt.imshow(Image.open("test_img.jpg"))

画像のフォーマットとサイズ変更を行う関数を作成します。

# install torch and torchvision if needed
%pip install torch
%pip install torchvision

import torch
from torchvision import transforms


def preprocess(image_file):
    """Preprocess the input image."""
    data_transforms = transforms.Compose(
        [
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
        ]
    )

    image = Image.open(image_file)
    image = data_transforms(image).float()
    image = torch.tensor(image)
    image = image.unsqueeze(0)
    return image.numpy()

画像をフォーマットし、JSON ファイルに変換します。

image_data = preprocess("test_img.jpg")
input_data = json.dumps({"data": image_data.tolist()})
with open("request.json", "w") as outfile:
    outfile.write(input_data)

その後、この JSON を使ってエンドポイントを呼び出し、結果を出力できます。

# test the blue deployment
result = ml_client.online_endpoints.invoke(
    endpoint_name=online_endpoint_name,
    request_file="request.json",
    deployment_name=online_deployment_name,
)

print(result)

リソースをクリーンアップする

今後エンドポイントが不要な場合は、削除してリソースの使用を停止します。 他のデプロイがエンドポイントを使っていないことを確認してから削除してください。

ml_client.online_endpoints.begin_delete(name=online_endpoint_name)

Note

このクリーンアップが完了するまでに少し時間がかかると想定してください。

次の手順

この記事では、Azure Machine Learning で PyTorch を使用して、ディープ ラーニング ニューラル ネットワークをトレーニングして登録しました。 また、モデルをオンライン エンドポイントにデプロイしました。 Azure Machine Learning の詳細については、以下の他の記事をご覧ください。