Share via


バッチ エンドポイントに言語モデルをデプロイする

適用対象: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 ワークスペース。 準備できていない場合は、Microsoft Azure Machine Learning ワークスペースの管理に関する記事の手順を使用して作成します。

  • ワークスペースに次のアクセス許可があることを確認します。

    • バッチ エンドポイントとバッチ デプロイを作成または管理する: 所有者または共同作成者のロール、あるいは Microsoft.MachineLearningServices/workspaces/batchEndpoints/* を許可するカスタム ロールを使用します。

    • ワークスペース リソース グループに ARM デプロイを作成する: 所有者または共同作成者のロール、あるいはワークスペースがデプロイされているリソース グループで Microsoft.Resources/deployments/write を許可するカスタム ロールを使用します。

  • Azure Machine Learning を使用するには、次のソフトウェアをインストールする必要があります。

    Azure CLImlAzure Machine Learning 用の 拡張機能

    az extension add -n ml
    

    注意

    Batch エンドポイントのパイプライン コンポーネント デプロイは、Azure CLI 用 ml 拡張機能のバージョン 2.7 で導入されました。 az extension update --name ml を使用して、最新バージョンを取得します。

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

ワークスペースは、Azure Machine Learning の最上位のリソースで、Azure 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 モデルをデプロイし、英語のテキスト ファイルに対してテキストの概要作成を実行します。

  1. エンドポイントの名前を決めます。 このエンドポイントの名前は、エンドポイントに関連付けられている URI に記載されます。 そのため、バッチ エンドポイント名は Azure リージョン内で一意である必要があります。 たとえば、westus2 に存在できる mybatchendpoint という名前のバッチ エンドポイントは 1 つだけです。

    今回は、後で簡単に参照できるように、エンドポイント名を変数に配置しておきましょう。

    ENDPOINT_NAME="text-summarization-batch"
    
  2. バッチ エンドポイントを構成する

    次の YAML ファイルは、バッチ エンドポイントを定義します。

    endpoint.yml

    $schema: https://azuremlschemas.azureedge.net/latest/batchEndpoint.schema.json
    name: text-summarization-batch
    description: A batch endpoint for summarizing text using a HuggingFace transformer model.
    auth_mode: aad_token
    
  3. エンドポイントを作成します。

    az ml batch-endpoint create --file endpoint.yml  --name $ENDPOINT_NAME
    

デプロイを作成する

モデルをホストするデプロイを作成しましょう。

  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 の実行) にメモリ不足が発生する可能性があるため、高価なモデル (トランスフォーマーなど) を扱う際に一般的なパターンです。

  2. デプロイを実行する環境を示す必要があります。 この場合、モデルは Torch で実行され、HuggingFace のライブラリ transformersaccelerate、および 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]
    
  3. 先ほど説明した 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 ライブラリを使用したアクセラレーションは、この構成ではサポートされません。

  4. 各デプロイはコンピューティング クラスターで実行されます。 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 のコストの管理および最適化について確認してください。

  5. 次に、デプロイを作成しましょう。

    作成されたエンドポイントの下に新しいデプロイを作成するには、次のような 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 処理のパターンであることがわかります。

  6. エンドポイント内で特定のデプロイを呼び出すこともできますが、通常はエンドポイント自体を呼び出し、使用するデプロイはエンドポイントで決定されるようにします。 このようなデプロイは、"既定" のデプロイと呼ばれます。 これにより、エンドポイントを呼び出すユーザーとのコントラクトを変更せずに、既定のデプロイを変更し、デプロイを提供するモデルを変更することができます。 既定のデプロイを更新するには、次の手順に従います。

    DEPLOYMENT_NAME="text-summarization-hfbart"
    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  7. この時点で、バッチ エンドポイントを使用する準備は完了です。

デプロイをテストする

エンドポイントをテストするために、データセット BillSum: 米国法律の自動概要作成のためのコーパスのサンプルを使用します。 このサンプルは、フォルダー data のリポジトリに含まれています。 データの形式は CSV であり、概要を作成するコンテンツは、モデルで想定される列 text の下にあります。

  1. エンドポイントを呼び出しましょう。

    JOB_NAME=$(az ml batch-endpoint invoke --name $ENDPOINT_NAME --input data --input-type uri_folder --query name -o tsv)
    

    注意

    ユーティリティ jq は、すべてのインストールでインストールされるとは限りません。 手順はこのリンクから確認できます。

    ヒント

    ローカル パスを入力として示すと、データが Azure Machine Learning の既定のストレージ アカウントにアップロードされることに注意してください。

  2. コマンドが戻ると、すぐにバッチ ジョブが開始されます。 ジョブの状態は、完了するまで監視できます。

    az ml job show -n $JOB_NAME --web
    
  3. デプロイが完了したら、予測をダウンロードできます。

    予測をダウンロードするには、次のコマンドを使用します。

    az ml job download --name $JOB_NAME --output-name score --download-path .
    

テキストを処理するモデルをデプロイするときの考慮事項

このチュートリアルの注意事項で説明したように、テキストの処理には、バッチ デプロイ用の特定の構成を必要とするいくつかの特徴がある場合があります。 バッチ デプロイを設計するときは、次の考慮事項に配慮してください。

  • 一部の NLP モデルは、メモリとコンピューティング時間の観点から非常に高価な場合があります。 その場合は、各ミニバッチに含まれるファイルの数を減らすことを検討してください。 上記の例では、この数はバッチごとに最小 1 ファイルに設定されています。 このケースは当てはまらないかも知れませんが、モデルが一度にスコアリングできるファイルの数を考慮してください。 ディープ ラーニング モデルでは、入力のサイズとモデルの占有領域の関係が線形ではない可能性があることに注意してください。
  • モデルが (この例のように) 一度に 1 つのファイルを処理できない場合は、入力データを行/チャンクで読み取る方法を検討してください。 高いスループットまたはハードウェア使用率を実現する必要がある場合は、行レベルでバッチ処理を実装します。
  • デプロイの timeout 値を、モデルのコストと処理するデータ量に応じて設定します。 timeout は、バッチ デプロイが、指定されたバッチのスコアリング スクリプトの実行を待機する時間を示しています。 バッチに多数のファイルまたは多数の行があるファイルがある場合、このパラメーターの適切な値に影響します。

テキストを処理する MLflow モデルに関する考慮事項

上記と同じ考慮事項が MLflow モデルに適用されます。 ただし、MLflow モデルのデプロイにはスコアリング スクリプトを提供する必要がないため、記載されている推奨事項のいくつかには異なるアプローチが必要です。

  • バッチ エンドポイントの MLflow モデルでは、長いシーケンスのテキストを含む可能性がある入力データとしての表形式データの読み取りがサポートされます。 サポートされているファイルの種類の詳細については、「ファイルの種類のサポート」を参照してください。
  • バッチ デプロイでは、ファイル全体の内容を Pandas データフレームで使用して、MLflow モデルの予測関数を呼び出します。 入力データに多数の行が含まれている場合は、複雑なモデル (このチュートリアルで説明したモデルなど) を実行すると、メモリ不足の例外が発生する可能性があります。 このような場合は、次の点を考慮してください。
    • モデルで予測を実行する方法をカスタマイズし、バッチ処理を実装します。 MLflow モデルの推論をカスタマイズする方法については、「カスタム モデルのログ記録」を参照してください。
    • スコアリング スクリプトを作成し、mlflow.<flavor>.load_model() を使用してモデルを読み込みます。 詳細については、「MLflow モデルとスコアリング スクリプトの使用」を参照してください。