バッチ エンドポイントに言語モデルをデプロイする
適用対象:Azure CLI ml extension v2 (現行)Python SDK azure-ai-ml v2 (現行)
バッチ エンドポイントを使用して、言語モデルのような、テキスト データに対するコストの高いモデルをデプロイできます。 このチュートリアルでは、HuggingFace のモデルを使用して、長いシーケンスのテキストに対するテキストの概要作成を実行できるモデルをデプロイする方法について説明します。 また、HuggingFace の optimum
ライブラリと accelerate
ライブラリを使用して推論の最適化を行う方法も示します。
このサンプルについて
使用するモデルは、HuggingFace の一般的なライブラリ トランスフォーマーと、BART アーキテクチャを使用した Facebook の事前学習済みモデルを使用して構築されました。 これは、論文「BART: 自然言語生成のためのシーケンスからシーケンスへのノイズ除去の事前学習」で紹介されました。 このモデルには、デプロイに関して注意すべき次の制約があります。
- 最大 1024 個のトークンをシーケンスで処理できます。
- 英語でテキストの概要作成を行うために学習されています。
- Torch をバックエンドとして使用します。
この記事の例は、azureml-examples リポジトリに含まれているコード サンプルを基にしています。 YAML などのファイルをコピーして貼り付けることなくコマンドをローカルで実行するには、最初にリポジトリを複製してから、ディレクトリをそのフォルダーに変更します。
git clone https://github.com/Azure/azureml-examples --depth 1
cd azureml-examples/cli
この例のファイルは、次の場所にあります。
cd endpoints/batch/deploy-models/huggingface-text-summarization
Jupyter ノートブックで経過をたどる
Jupyter Notebook で、このサンプルに従って実行できます。 複製されたリポジトリで、ノートブック text-summarization-batch.ipynb を開きます。
前提条件
Azure サブスクリプション。 Azure サブスクリプションをお持ちでない場合は、開始する前に無料アカウントを作成してください。 無料版または有料版の Azure Machine Learning をお試しください。
Azure Machine Learning ワークスペース。 ワークスペースを作成するには、「Azure Machine Learning ワークスペースの管理」を参照してください。
Machine Learning ワークスペースでの以下のアクセス許可があることを確認してください。
- バッチ エンドポイントとバッチ デプロイを作成または管理する: 所有者または共同作成者のロール、あるいは
Microsoft.MachineLearningServices/workspaces/batchEndpoints/*
を許可するカスタム役割を使用します。 - ワークスペース リソース グループ内での Azure Resource Manager デプロイの作成: 所有者、共同作成者、またはワークスペースがデプロイされるリソース グループ での
Microsoft.Resources/deployments/write
を許可するカスタム ロールを使用します。
- バッチ エンドポイントとバッチ デプロイを作成または管理する: 所有者または共同作成者のロール、あるいは
Machine Learning を使用するには、以下のソフトウェアをインストールします。
次のコマンドを実行して、Azure CLI と
ml
という Azure Machine Learning 用の拡張機能をインストールします。az extension add -n ml
バッチ エンドポイントのパイプライン コンポーネント デプロイは、Azure CLI 用拡張機能
ml
のバージョン 2.7 で導入されました。az extension update --name ml
コマンドを使用して、最新バージョンを取得します。
ワークスペースに接続する
ワークスペースは、Machine Learning の最上位のリソースです。 これは Machine Learning を使用する際に作成するすべての成果物を操作するための一元的な場所を提供します。 このセクションでは、デプロイ タスクを実行するワークスペースに接続します。
次のコマンドで、サブスクリプション ID、ワークスペース、場所、リソース グループの値を入力します。
az account set --subscription <subscription>
az configure --defaults workspace=<workspace> group=<resource-group> location=<location>
モデルを登録する
モデルのサイズのため、このリポジトリには含まれていません。 代わりに、HuggingFace モデルのハブからコピーをダウンロードできます。 使用している環境に、パッケージ transformers
および torch
がインストールされている必要があります。
%pip install transformers torch
次のコードを使用して、モデルをフォルダー model
にダウンロードします:
from transformers import pipeline
model = pipeline("summarization", model="facebook/bart-large-cnn")
model_local_path = 'model'
summarizer.save_pretrained(model_local_path)
これで、このモデルを Azure Machine Learning レジストリに登録できるようになりました。
MODEL_NAME='bart-text-summarization'
az ml model create --name $MODEL_NAME --path "model"
エンドポイントの作成
text-summarization-batch
という名前のバッチ エンドポイントを作成し、そこに HuggingFace モデルをデプロイし、英語のテキスト ファイルに対してテキストの概要作成を実行します。
エンドポイントの名前を決めます。 このエンドポイントの名前は、エンドポイントに関連付けられている URI に記載されます。 そのため、バッチ エンドポイント名は Azure リージョン内で一意である必要があります。 たとえば、
westus2
に存在できるmybatchendpoint
という名前のバッチ エンドポイントは 1 つだけです。バッチ エンドポイントを構成する
エンドポイントを作成します。
デプロイを作成する
モデルをホストするデプロイを作成しましょう。
バッチ デプロイによって指定された CSV ファイルを読み取り、概要でモデルのスコアを返すことができるスコアリング スクリプトを作成する必要があります。 次のスクリプトでは、これらのアクションが実行されます。
- ハードウェア構成 (CPU と GPU) を検出し、それに応じてモデルを読み込む
init
関数を示します。 モデルとトークナイザーの両方がグローバル変数に読み込まれます。 現在使用しているモデルのシーケンス長さの制限を考慮して、HuggingFace のpipeline
オブジェクトは使用しません。 - パフォーマンスを改善するために、
optimum
ライブラリとaccelerate
ライブラリを使用して、パフォーマンスのよいモデルの最適化を実行していることに注意してください。 モデルまたはハードウェアでサポートされていない場合は、このような最適化を行わずにデプロイを実行します。 - バッチデプロイによって提供されるミニバッチごとに実行される
run
関数を示す。 run
関数はdatasets
ライブラリを使用してバッチ全体を読み取る。 概要作成を行う必要があるテキストはtext
列にあります。- この
run
メソッドは、テキストの各行を反復処理し、予測を実行する。 これは非常に高価なモデルであるため、ファイル全体に対して予測を実行すると、メモリ不足の例外が発生します。 モデルはtransformers
からのpipeline
オブジェクトで実行されないことに注意してください。 これは、長いシーケンスのテキストと、使用している基になるモデルの 1024 個のトークンの制限を考慮するために行われます。 - 指定されたテキストの概要を返します。
code/batch_driver.py
import os import time import torch import subprocess import mlflow from pprint import pprint from transformers import AutoTokenizer, BartForConditionalGeneration from optimum.bettertransformer import BetterTransformer from datasets import load_dataset def init(): global model global tokenizer global device cuda_available = torch.cuda.is_available() device = "cuda" if cuda_available else "cpu" if cuda_available: print(f"[INFO] CUDA version: {torch.version.cuda}") print(f"[INFO] ID of current CUDA device: {torch.cuda.current_device()}") print("[INFO] nvidia-smi output:") pprint( subprocess.run(["nvidia-smi"], stdout=subprocess.PIPE).stdout.decode( "utf-8" ) ) else: print( "[WARN] CUDA acceleration is not available. This model takes hours to run on medium size data." ) # AZUREML_MODEL_DIR is an environment variable created during deployment model_path = os.path.join(os.environ["AZUREML_MODEL_DIR"], "model") # load the tokenizer tokenizer = AutoTokenizer.from_pretrained( model_path, truncation=True, max_length=1024 ) # Load the model try: model = BartForConditionalGeneration.from_pretrained( model_path, device_map="auto" ) except Exception as e: print( f"[ERROR] Error happened when loading the model on GPU or the default device. Error: {e}" ) print("[INFO] Trying on CPU.") model = BartForConditionalGeneration.from_pretrained(model_path) device = "cpu" # Optimize the model if device != "cpu": try: model = BetterTransformer.transform(model, keep_original_model=False) print("[INFO] BetterTransformer loaded.") except Exception as e: print( f"[ERROR] Error when converting to BetterTransformer. An unoptimized version of the model will be used.\n\t> {e}" ) mlflow.log_param("device", device) mlflow.log_param("model", type(model).__name__) def run(mini_batch): resultList = [] print(f"[INFO] Reading new mini-batch of {len(mini_batch)} file(s).") ds = load_dataset("csv", data_files={"score": mini_batch}) start_time = time.perf_counter() for idx, text in enumerate(ds["score"]["text"]): # perform inference inputs = tokenizer.batch_encode_plus( [text], truncation=True, padding=True, max_length=1024, return_tensors="pt" ) input_ids = inputs["input_ids"].to(device) summary_ids = model.generate( input_ids, max_length=130, min_length=30, do_sample=False ) summaries = tokenizer.batch_decode( summary_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False ) # Get results: resultList.append(summaries[0]) rps = idx / (time.perf_counter() - start_time + 00000.1) print("Rows per second:", rps) mlflow.log_metric("rows_per_second", rps) return resultList
ヒント
ファイルはデプロイによってミニバッチで提供されますが、このスコアリング スクリプトは一度に 1 つの行を処理します。 これは、バッチ全体を読み込んで一度にモデルに送信しようとすると、バッチ Executor (OOM の実行) にメモリ不足が発生する可能性があるため、高価なモデル (トランスフォーマーなど) を扱う際に一般的なパターンです。
- ハードウェア構成 (CPU と GPU) を検出し、それに応じてモデルを読み込む
デプロイを実行する環境を示す必要があります。 この場合、モデルは
Torch
で実行され、HuggingFace のライブラリtransformers
、accelerate
、およびoptimum
が必要です。 Azure Machine Learning には、Torch と GPU がサポートされている環境が既に用意されています。conda.yaml
ファイルにいくつかの依存関係を追加するだけです。environment/torch200-conda.yaml
name: huggingface-env channels: - conda-forge dependencies: - python=3.8.5 - pip - pip: - torch==2.0 - transformers - accelerate - optimum - datasets - mlflow - azureml-mlflow - azureml-core - azureml-dataset-runtime[fuse]
先ほど説明した conda ファイルを次のように使用できます。
環境定義はデプロイ ファイルに含まれています。
deployment.yml
compute: azureml:gpu-cluster environment: name: torch200-transformers-gpu image: mcr.microsoft.com/azureml/openmpi4.1.0-cuda11.8-cudnn8-ubuntu22.04:latest
重要
作成した環境
torch200-transformers-gpu
では、Torch 2.0 と Ubuntu 20.04 を実行するために CUDA 11.8 互換のハードウェア デバイスが必要です。 GPU デバイスでこのバージョンの CUDA がサポートされていない場合は、代替のtorch113-conda.yaml
conda 環境 (リポジトリでも利用可能) を確認できます。この環境では、CUDA 10.1 で Ubuntu 18.04 を使用して Torch 1.3 が実行されます。 ただし、optimum
およびaccelerate
ライブラリを使用したアクセラレーションは、この構成ではサポートされません。各デプロイはコンピューティング クラスターで実行されます。 Azure Machine Learning コンピューティング クラスター(AmlCompute) または Kubernetes クラスターの両方がサポートされています。 この例では、モデルは GPU アクセラレーションの恩恵を受けることができるため、GPU クラスターを使用します。
az ml compute create -n gpu-cluster --type amlcompute --size STANDARD_NV6 --min-instances 0 --max-instances 2
注意
この時点では、コンピューティングに課金はされません。バッチ エンドポイントが呼び出されてバッチ スコアリング ジョブが送信されるまで、クラスターは 0 ノードのままだからです。 AmlCompute のコストの管理および最適化について確認してください。
次に、デプロイを作成しましょう。
作成されたエンドポイントの下に新しいデプロイを作成するには、次のような
YAML
構成を作成します。 追加のプロパティについては、完全なバッチ エンドポイント YAML スキーマを確認してください。deployment.yml
$schema: https://azuremlschemas.azureedge.net/latest/modelBatchDeployment.schema.json endpoint_name: text-summarization-batch name: text-summarization-optimum description: A text summarization deployment implemented with HuggingFace and BART architecture with GPU optimization using Optimum. type: model model: azureml:bart-text-summarization@latest compute: azureml:gpu-cluster environment: name: torch200-transformers-gpu image: mcr.microsoft.com/azureml/openmpi4.1.0-cuda11.8-cudnn8-ubuntu22.04:latest conda_file: environment/torch200-conda.yaml code_configuration: code: code scoring_script: batch_driver.py resources: instance_count: 2 settings: max_concurrency_per_instance: 1 mini_batch_size: 1 output_action: append_row output_file_name: predictions.csv retry_settings: max_retries: 1 timeout: 3000 error_threshold: -1 logging_level: info
次に、次のコマンドを使ってデプロイを作成します。
az ml batch-deployment create --file deployment.yml --endpoint-name $ENDPOINT_NAME --set-default
重要
このデプロイでは、
retry_settings
パラメータのtimeout
に高い値が設定されています。 その理由は、実行しているモデルの性質によるものです。 これは非常に高価なモデルであり、1 つの行での推論には最大 60 秒かかる場合があります。timeout
パラメーターは、Batch Deployment が各ミニバッチの処理を完了するまでのスコアリング スクリプトの待機時間を制御します。 このモデルでは予測が行ごとに実行されるため、長いファイルの処理には時間がかかる場合があります。 また、バッチあたりのファイル数が 1 (mini_batch_size=1
) に設定されていることにも注意してください。 これも、現在の作業の性質に関連しています。 バッチごとに1ファイルずつ処理すると、それだけで十分なコストがかかります。 これは NLP 処理のパターンであることがわかります。エンドポイント内で特定のデプロイを呼び出すこともできますが、通常はエンドポイント自体を呼び出し、使用するデプロイはエンドポイントで決定されるようにします。 このようなデプロイは、"既定" のデプロイと呼ばれます。 これにより、エンドポイントを呼び出すユーザーとのコントラクトを変更せずに、既定のデプロイを変更し、デプロイを提供するモデルを変更することができます。 既定のデプロイを更新するには、次の手順に従います。
この時点で、バッチ エンドポイントを使用する準備は完了です。
デプロイをテストする
エンドポイントをテストするために、データセット BillSum: 米国法律の自動概要作成のためのコーパスのサンプルを使用します。 このサンプルは、フォルダー data
のリポジトリに含まれています。 データの形式は CSV であり、概要を作成するコンテンツは、モデルで想定される列 text
の下にあります。
エンドポイントを呼び出しましょう。
JOB_NAME=$(az ml batch-endpoint invoke --name $ENDPOINT_NAME --input data --input-type uri_folder --query name -o tsv)
注意
ユーティリティ
jq
は、すべてのインストールでインストールされるとは限りません。 手順はこのリンクから確認できます。ヒント
ローカル パスを入力として示すと、データが Azure Machine Learning の既定のストレージ アカウントにアップロードされることに注意してください。
コマンドが戻ると、すぐにバッチ ジョブが開始されます。 ジョブの状態は、完了するまで監視できます。
デプロイが完了したら、予測をダウンロードできます。
テキストを処理するモデルをデプロイするときの考慮事項
このチュートリアルの注意事項で説明したように、テキストの処理には、バッチ デプロイ用の特定の構成を必要とするいくつかの特徴がある場合があります。 バッチ デプロイを設計するときは、次の考慮事項に配慮してください。
- 一部の NLP モデルは、メモリとコンピューティング時間の観点から非常に高価な場合があります。 その場合は、各ミニバッチに含まれるファイルの数を減らすことを検討してください。 上記の例では、この数はバッチごとに最小 1 ファイルに設定されています。 このケースは当てはまらないかも知れませんが、モデルが一度にスコアリングできるファイルの数を考慮してください。 ディープ ラーニング モデルでは、入力のサイズとモデルの占有領域の関係が線形ではない可能性があることに注意してください。
- モデルが (この例のように) 一度に 1 つのファイルを処理できない場合は、入力データを行/チャンクで読み取る方法を検討してください。 高いスループットまたはハードウェア使用率を実現する必要がある場合は、行レベルでバッチ処理を実装します。
- デプロイの
timeout
値を、モデルのコストと処理するデータ量に応じて設定します。timeout
は、バッチ デプロイが、指定されたバッチのスコアリング スクリプトの実行を待機する時間を示しています。 バッチに多数のファイルまたは多数の行があるファイルがある場合、このパラメーターの適切な値に影響します。
テキストを処理する MLflow モデルに関する考慮事項
上記と同じ考慮事項が MLflow モデルに適用されます。 ただし、MLflow モデルのデプロイにはスコアリング スクリプトを提供する必要がないため、記載されている推奨事項のいくつかには異なるアプローチが必要です。
- バッチ エンドポイントの MLflow モデルでは、長いシーケンスのテキストを含む可能性がある入力データとしての表形式データの読み取りがサポートされます。 サポートされているファイルの種類の詳細については、「ファイルの種類のサポート」を参照してください。
- バッチ デプロイでは、ファイル全体の内容を Pandas データフレームで使用して、MLflow モデルの予測関数を呼び出します。 入力データに多数の行が含まれている場合は、複雑なモデル (このチュートリアルで説明したモデルなど) を実行すると、メモリ不足の例外が発生する可能性があります。 このような場合は、次の点を考慮してください。
- モデルで予測を実行する方法をカスタマイズし、バッチ処理を実装します。 MLflow モデルの推論をカスタマイズする方法については、「カスタム モデルのログ記録」を参照してください。
- スコアリング スクリプトを作成し、
mlflow.<flavor>.load_model()
を使用してモデルを読み込みます。 詳細については、「MLflow モデルとスコアリング スクリプトの使用」を参照してください。