電通総研 テックブログ

電通総研が運営する技術ブログ

Azure Machine Learning SDK v2の基本的な使い方紹介

本記事はMicrosoft Azure Tech Advent Calendar 2022の22日目の記事です

Xイノベーション本部 AITCの後藤です。最近、Azureが提供する機械学習サービスであるAzure Machine Learning(Azure ML)のSDK v2が新たにGAになりました。

Azure MLは先日TechPlayのイベントでもご紹介した、私たちのチームが背極的に活用している機械学習のさまざまな用途に活用可能なサービスです。

Azure ML SDK v2はv1と大きく使い方が変わりました。本記事ではまだまだ情報が少ないAzure ML SDK v2に関して公式ドキュメントをベースに使い方を紹介します。

そもそもAzure Machine Learning (Azure ML) とは

Azure ML は、Azure の機械学習サービスです。一言で機械学習サービスといってもAzure ML は非常に多くの機能を提供しており、機械学習に関連する多くのユースケースで活用可能です。

機械学習を実際の業務で運用していくいわゆるMLOpsを実現するには多くの工程が必要です。モデルを学習させるまでも大変ですが、学習後に実際の業務で使用できるように運用していくのはさらに大変です。Azure MLではそういった機械学習をビジネスで活用しようとする際に遭遇する多くの問題を解決してくれます。

参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/overview-what-is-azure-machine-learning

Azure ML SDK v1とv2の比較

Azure ML SDK v2はv1から大きく変更が加えられ、使い方もほとんど原型がないレベルで変わっています。また、互換性に関してもv1とv2で維持されないものもあります。こういった理由から、v1とv2を同じコードベースで使用することは推奨されていません。

以下はSDKで使用されるクラスを念頭にv1とv2の概念の比較をした表です。

AzureML v1 AzureML v2
Workspace Workspace
Datastore Datastore
Compute Compute
Webservice OnlineEndpoint、OnlineDeployment
ParallelRunStep for Batch Scoring BatchEndpoint、BatchDeployment
Experiment, Run, Pipeline Job
Dataset
  • FileDataset
  • TabularDataset
  • Data assets(v1との整合性が取れていない)
  • URI_FILE
  • URI_FOLDER
  • MLTABLE
  • Model Model
    Environment Environment

    学習ジョブや推論まわりは名称含め大きく変わっていることがわかります。また、上記の表で名前が一緒でも、与える引数が異なるものなども存在します。

    参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-migrate-from-v1#using-v1-and-v2-code-together

    Azure ML SDK v2の基本的な使い方

    それでは基本的な機械学習のフローとして以下の内容をAzure ML SDK v2で順番に実行し、使い方を紹介します。

    1. 環境準備
    2. データセット登録
    3. 学習の実行
    4. 学習済みモデルの登録
    5. モデルのデプロイ

    準備

    Pythonパッケージのazure-ai-mlをインストールします(ちなみにSDK v1はazureml-coreです)。次にAzure portalもしくは Azure CLIでAzure Machine Learning workspace(AMLワークスペース)を作成します。

    ワークスペースを作成後、必要な情報をメモし、以下のようにPythonでMLClientを定義します。

    from azure.ai.ml import MLClient
    from azure.identity import DefaultAzureCredential
    
    # AMLワークスペースの情報
    subscription_id = "<subscription_id>"
    resource_group = "<リソースグループ名>"
    workspace = "<ワークスペース名>"
    
    ml_client = MLClient(
        DefaultAzureCredential(), subscription_id, resource_group, workspace
    )

    SDK v2の主要な変更の一つとして、上記のMLClientが挙げられます。SDK v1では、ExperimentやRunなど様々なクラスを用途に応じて使い分ける必要がありましたが、v2におけるAMLの操作は全てこのMLClientから実行します。

    データセット登録

    Azure MLに学習に使用するデータを登録します。

    ちなみに細かいですが、v1ではデータセットと呼称されていたものがv2ではデータ資産という呼称に変更されています。本記事では一般的な機械学習用語として「データセット」という言葉を使用しています。 参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-migrate-from-v1#data-datasets-in-v1

    Azure MLに登録する際のデータソースには以下の選択肢があります。

    • ローカルファイル
    • Azure BLOB Storage
    • Azure Data Lake Storage Gen2(ADLS gen2)
      • ADLS gen2はAzure Blob Storage をベースに構築された、ビッグ データ分析用途のストレージサービスです
    • Datastore
      • Azure ML で指定するAzureのストレージサービスです
      • 上記のBlobやADSK gen2を指定することで安全にAzure MLからデータにアクセスが可能になります

    今回は単純にローカルファイルをAzure MLのデータ資産に登録します。データセット登録は先ほどのMLClientを使用して以下のように書きます。

    from azure.ai.ml.entities import Data
    from azure.ai.ml.constants import AssetTypes
    
    # データの場所によりpathの記述方法が異なる
    # local: './<path>'
    # blob:  'https://<account_name>.blob.core.windows.net/<container_name>/<path>'
    # ADLS gen2: 'abfss://<file_system>@<account_name>.dfs.core.windows.net/<path>/'
    # Datastore: 'azureml://datastores/<data_store_name>/paths/<path>'
    
    my_data = Data(
        path="./data/iris.csv", # ローカルファイルパス
        type=AssetTypes.URI_FILE,
        description="irisデータ",
        name="iris",
        version='1' # データセットのバージョン
    )
    
    # AMLにデータセットを登録
    ml_client.data.create_or_update(my_data)

    参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-create-data-assets?tabs=CLI#tabpanel_2_Python-SDK

    正常に処理が終わるとブラウザでAMLワークスペースのアセット⇨データより以下のように確認できます。

    学習の実行

    データセットの登録が終わったので、次に学習を実行します。

    Azure ML SDK v2における学習の実行場所はローカルかコンピュートクラスターの2つです。コンピュートクラスターはAMLにおける学習用の計算リソースです。コンピュートクラスターは事前に作成しておく必要があります。作成はSDKCLI、AMLワークスペースからも可能です。

    今回はAMLワークスペースからコンピュートクラスターを作成します。AMLワークスペースより左のブレードメニューからコンピューティングを選択し、新規ボタンから作成できます。作成すると以下のようにコンピューティングクラスタータブに作成したクラスターが表示されます。

    コンピュートクラスターはその名の通り設定した台数でVMが必要に応じてスケールしてくれます。最小のVM台数を0に設定できるので、学習を実行する時だけ計算リソースを立ち上げることができます。逆に最大の台数を増やせばその分同時に実行できる学習も増やせます。 参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/quickstart-create-resources

    続いて、コンピュートクラスター内で実行する学習スクリプトを用意します。今回はMSのexampleのコードを拝借しました。(引用元:https://github.com/Azure/azureml-examples

    # imports
    import os
    import mlflow
    import argparse
    
    import pandas as pd
    import matplotlib.pyplot as plt
    
    from sklearn.svm import SVC
    from sklearn.model_selection import train_test_split
    
    # define functions
    def main(args):
        # enable auto logging
        mlflow.autolog()
    
        # setup parameters
        params = {
            "C": args.C,
            "kernel": args.kernel,
            "degree": args.degree,
            "gamma": args.gamma,
            "coef0": args.coef0,
            "shrinking": args.shrinking,
            "probability": args.probability,
            "tol": args.tol,
            "cache_size": args.cache_size,
            "class_weight": args.class_weight,
            "verbose": args.verbose,
            "max_iter": args.max_iter,
            "decision_function_shape": args.decision_function_shape,
            "break_ties": args.break_ties,
            "random_state": args.random_state,
        }
    
        # read in data
        df = pd.read_csv(args.iris_csv)
    
        # process data
        X_train, X_test, y_train, y_test = process_data(df, args.random_state)
    
        # train model
        model = train_model(params, X_train, X_test, y_train, y_test)
    
    
    def process_data(df, random_state):
        # split dataframe into X and y
        X = df.drop(["species"], axis=1)
        y = df["species"]
    
        # train/test split
        X_train, X_test, y_train, y_test = train_test_split(
            X, y, test_size=0.2, random_state=random_state
        )
    
        # return splits and encoder
        return X_train, X_test, y_train, y_test
    
    
    def train_model(params, X_train, X_test, y_train, y_test):
        # train model
        model = SVC(**params)
        model = model.fit(X_train, y_train)
    
        # return model
        return model
    
    
    def parse_args():
        # setup arg parser
        parser = argparse.ArgumentParser()
    
        # add arguments
        parser.add_argument("--iris-csv", type=str)
        parser.add_argument("--C", type=float, default=1.0)
        parser.add_argument("--kernel", type=str, default="rbf")
        parser.add_argument("--degree", type=int, default=3)
        parser.add_argument("--gamma", type=str, default="scale")
        parser.add_argument("--coef0", type=float, default=0)
        parser.add_argument("--shrinking", type=bool, default=False)
        parser.add_argument("--probability", type=bool, default=False)
        parser.add_argument("--tol", type=float, default=1e-3)
        parser.add_argument("--cache_size", type=float, default=1024)
        parser.add_argument("--class_weight", type=dict, default=None)
        parser.add_argument("--verbose", type=bool, default=False)
        parser.add_argument("--max_iter", type=int, default=-1)
        parser.add_argument("--decision_function_shape", type=str, default="ovr")
        parser.add_argument("--break_ties", type=bool, default=False)
        parser.add_argument("--random_state", type=int, default=42)
    
        # parse args
        args = parser.parse_args()
    
        # return args
        return args
    
    
    # run script
    if __name__ == "__main__":
        # parse args
        args = parse_args()
    
        # run main function
        main(args)
    

    ポイントはmlflow.autologです。SDK v1の頃はAML独自の書き方でログを記録するのが主流でしたが、SDK v2からはmlflowで記録を取ることが可能です。これによりAMLのコンピュートクラスター以外でも動作する汎用的な機械学習スクリプトが利用可能です。

    そして、mlflow.autologが有効に使えるコードであれば、この1行でAMLに学習済みモデルの記録や主要なメトリックの登録が行われます。ただし、後述しますがモデルをAMLに登録するには別途操作が必要です。

    では、このスクリプトをコンピュートクラスターで実行させましょう。SDK v2から学習を実行するには先ほどのMLClientを利用し、以下のように書きます。

    from azure.ai.ml import command, Input
    
    job = command(    
        code="./src",  # ローカルの学習スクリプトがあるディレクトリパス
        command="python train.py --iris-csv ${{inputs.iris}} --C ${{inputs.C}} --kernel ${{inputs.kernel}} --coef0 ${{inputs.coef0}}",
        inputs={
            "iris": Input(
                type="uri_file",
                path=my_data.path,
            ),
            "C": 0.8,
            "kernel": "rbf",
            "coef0": 0.1,
        },
        environment="AzureML-sklearn-1.0-ubuntu20.04-py38-cpu@latest",   
        compute="cpu-cluster", # コンピュートクラスター名
        display_name="sklearn-iris-example",
        experiment_name="iris-experiment",
        description="AML実験"
    )
    
    returned_job = ml_client.jobs.create_or_update(job)
    returned_job

    ポイントは3つです。

    1つ目は、学習に使用する入力データセットをInputオブジェクトとして定義することです。定義方法は簡単で、先ほど登録したDataオブジェクトのpathとtypeを引数に与えます。 2つ目は、学習環境となるenvironmentです。今回は学習スクリプトに複雑な依存関係がなかったのでAMLで用意してくれている環境を指定し、使用しました。こちらは自作の環境も使用できます。 3つ目はcompute引数に先ほど作成したコンピュートクラスター名を指定しているところです。これにより、コンピュートクラスターで学習が実行されます。

    このコードを実行するとiris-experimentという実験の配下にsklearn-iris-exampleという名前のジョブで実行情報が以下のように記録されます。

    画像にあるように、学習済みモデル、タグ、メトリックなど学習スクリプトやジョブ実行時に指定していない内容まで自動で記録してくれます。

    学習済みモデルの登録

    モデルをバージョン管理するために学習済みモデルを登録します。

    先ほど実行した学習ジョブには学習済みモデルの情報が記録されています。記録したモデルはAMLワークスペースからも該当のジョブを参照すると確認できます。

    SDKから登録するにはジョブ名をコピーして以下のpath引数に与えてModelオブジェクトを作成し、ml_clientからモデルの登録を行います。

    from azure.ai.ml.entities import Model
    from azure.ai.ml.constants import AssetTypes
    
    model = Model(
        path="azureml://jobs/<job name>/outputs/artifacts/paths/model/",
        name="run-iris-example",
        description="Model created from run.",
        type=AssetTypes.CUSTOM_MODEL
    )
    
    ml_client.models.create_or_update(model)

    Modelオブジェクトを作成する際のパスのフォーマットはいくつか存在します。このフォーマットは保存したモデルのtypeによって変わります。例えばtypeがmlflowであれば以下のような書き方になります。

    model = Model(
        path="runs:/<job name>/model",
        name="run-iris-example",
        description="Model created from run.",
        type=AssetTypes.CUSTOM_MODEL
    )

    参考:https://learn.microsoft.com/en-us/azure/machine-learning/how-to-manage-models?tabs=use-job-output%2Ccli

    デプロイ

    SDK v2で導入されたマネージドオンライン推論を試してみます。今回はバッチ推論がmlflowモデルをまだサポートしていないので実施しませんが、バッチ推論用のエンドポイントを作成することも可能です。

    SDK v2で導入されたマネージドオンライン推論は、その名の通りインフラ管理が不要なマネージドな環境でオンライン推論が実現できます。エンドポイントの細かい管理・運用は必要なく、スケーリングやブルーグリーンデプロイを用いたロールアウトなどの実際の運用時に欲しい設定が簡単にできます。(エンドポイントを作成後削除しない限り料金が発生し続けるので注意です)

    では早速マネージドオンライン推論の準備を行います。

    まずはエンドポイントを作成します。

    from azure.ai.ml.entities import ManagedOnlineEndpoint
    
    endpoint_name = "iris-test-online-endpoint"
    endpoint = ManagedOnlineEndpoint(
        name=endpoint_name,
        description="this is a sample online endpoint",
        auth_mode="key",   
    )
    
    ml_client.begin_create_or_update(endpoint).result()

    次にデプロイの設定を作成します。推論時の依存関係などの環境や使用するモデル、推論スクリプトはこのデプロイで設定します。今回デプロイするMLflowモデルの場合、推論用スクリプトは自動生成されるためデプロイ時に指定する必要はありません。

    from azure.ai.ml.entities import ManagedOnlineDeployment
    
    blue_deployment = ManagedOnlineDeployment(
        name="blue",
        endpoint_name=endpoint_name, # 作成したエンドポイント名
        model=model, # モデルオブジェクト
        instance_type="Standard_DS2_v2",
        instance_count=1,
    )
    
    ml_client.online_deployments.begin_create_or_update(blue_deployment).result()

    参考:https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-mlflow-batch?tabs=sdk#using-mlflow-models-with-a-scoring-script

    作成したデプロイに全てのトラフィックを割り当てるようエンドポイントを更新します。

    endpoint.traffic = {"blue": 100}
    ml_client.begin_create_or_update(endpoint).result()

    これでリアルタイム推論用のエンドポイントの完成です。

    推論はSDKから以下のようにできます。

    ml_client.online_endpoints.invoke(
        endpoint_name=endpoint_name,
        deployment_name="blue",
        request_file="./data/request.json",
    )

    この時、登録したモデルによってリクエストのjsonスキーマが異なるので注意です。今回はMLflowモデルを使用しているので、request.jsonの中身は以下のようになります。input_dataというキーに対してcolumnsdataを入力します。indexはなくても大丈夫です。

    {
      "input_data": {
        "columns": [
          "sepal length (cm)",
          "sepal width (cm)",
          "petal length (cm)",
          "petal width (cm)"
        ],
        "index":  [0, 1],
        "data": [
                 [2, 3, 4, 5],
                 [6, 5, 4, 3]
                ]
      }
    }

    個人的にエンドポイントのテストはSDKから実行するよりも、AMLワークスペースから実行できるのでそちらの方が楽です。

    SDK v2の感想

    SDK v1の頃に比べると、操作が直感的になり使いやすくなったと感じています。一方で、SDK v1を使用していた身からすると、使い方が大きく変わりすぎて戸惑うことも多かったです。あとはGAになったばかりということで、ドキュメント通りに動かないものも一部あり、苦労しました。

    おわりに

    私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募をお待ちしています。

    執筆:@goto.yuki、レビュー:@yamada.yShodoで執筆されました