本記事は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 SDK v1とv2の比較
- Azure ML SDK v2の基本的な使い方
- 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 |
Data assets(v1との整合性が取れていない) |
Model | Model |
Environment | Environment |
学習ジョブや推論まわりは名称含め大きく変わっていることがわかります。また、上記の表で名前が一緒でも、与える引数が異なるものなども存在します。
Azure ML SDK v2の基本的な使い方
それでは基本的な機械学習のフローとして以下の内容をAzure ML SDK v2で順番に実行し、使い方を紹介します。
- 環境準備
- データセット登録
- 学習の実行
- 学習済みモデルの登録
- モデルのデプロイ
準備
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 Blob Storage はAzure のオブジェクトストレージサービスです
- 参考:https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-blobs-overview
- 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)
正常に処理が終わるとブラウザでAMLワークスペースのアセット⇨データより以下のように確認できます。
学習の実行
データセットの登録が終わったので、次に学習を実行します。
Azure ML SDK v2における学習の実行場所はローカルかコンピュートクラスターの2つです。コンピュートクラスターはAMLにおける学習用の計算リソースです。コンピュートクラスターは事前に作成しておく必要があります。作成はSDKやCLI、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 )
デプロイ
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()
作成したデプロイに全てのトラフィックを割り当てるようエンドポイントを更新します。
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
というキーに対してcolumns
とdata
を入力します。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.y (Shodoで執筆されました)