Share via


在批次端點中部署語言模型

適用於:Azure CLI ml 延伸模組 v2 (目前)Python SDK azure-ai-ml v2 (目前)

批次端點可用來對文字資料部署昂貴的模型,例如語言模型。 在本教學課程中,您會了解如何部署模型,使用來自 HuggingFace 的模型來執行長篇文字的文字摘要。 它也會示範如何使用 HuggingFace optimumaccelerate 程式庫進行推斷最佳化。

關於此範例

我們將使用的模型是使用 HuggingFace 的熱門程式庫轉換程式以及 Facebook 搭配 BART 架構的預先定型模型建置而成。 這在論文 BART:去除序列至序列預先定型的雜訊以產生自然語言 (BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation) 中已有介紹。 此模型有下列限制,針對部署,請務必牢記:

  • 可以使用最多 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 Notebook 中跟著做

您可以在 Jupyter Notebook 中遵循此範例。 在複製的存放庫中,開啟 Notebook:text-summarization-batch.ipynb

必要條件

遵循本文中的步驟之前,請確定您已滿足下列必要條件:

  • Azure 訂用帳戶。 如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶。 試用免費或付費版本的 Azure Machine Learning

  • Azure Machine Learning 工作區。 如果您沒有工作區,請使用管理 Azure 機器學習 工作區一文中的步驟來建立一個工作區。

  • 請確定您在工作區中具有下列權限:

    • 建立或管理批次端點和部署:使用允許 Microsoft.MachineLearningServices/workspaces/batchEndpoints/*的擁有者、參與者或自定義角色。

    • 在工作區資源群組中建立ARM部署:使用可在部署工作區的資源群組中使用擁有 Microsoft.Resources/deployments/write 者、參與者或自定義角色。

  • 您必須安裝下列軟體,才能使用 Azure 機器學習:

    Azure CLI適用於 Azure Machine Learningml 擴充功能。

    az extension add -n ml
    

    注意

    批次端點的管線元件部署是在 Azure CLI 的 ml 擴充功能 2.7 版中引進。 使用 az extension update --name ml 來取得其最後一個版本。

連線到您的工作區

工作區是 Azure Machine Learning 的最上層資源,其提供一個集中位置來處理您在使用 Azure Machine Learning 時建立的所有成品。 在本節中,我們將連線到您將執行部署工作的工作區。

在下列程式碼中傳入訂用帳戶識別碼、工作區、位置和資源群組的值:

az account set --subscription <subscription>
az configure --defaults workspace=<workspace> group=<resource-group> location=<location>

註冊模型

由於模型的大小,此存放庫中並未包含模型。 相反地,您可以從 HuggingFace 模型的中樞下載複本。 您需要在您使用的環境中安裝套件 transformerstorch

%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 的批次端點。

    在此情況下,讓我們將端點的名稱放在變數中,方便稍後參考。

    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 物件來考慮我們目前使用的模型序列長度的限制。
    • 請注意,我們正在執行模型最佳化,以改善使用 optimumaccelerate 程式庫的效能。 如果模型或硬體不支援,我們將執行部署,而不進行這類最佳化。
    • 指定 run 函式執行批次部署所提供的每個迷你批次。
    • run 函式使用 datasets 程式庫讀取整個批次。 我們需要摘要的文字位於資料行 text
    • run 方法會逐一查看文字的每一個資料列,然後執行預測。 由於這是會佔用大量資源的模型,針對整體檔案執行預測將會導致記憶體不足的例外狀況。 請注意,模型不會使用來自 transformerspipeline 物件執行。 這是為了顧及長序列的文字,以及我們所使用的基礎模型中 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
    

    提示

    雖然部署是以迷你批次提供檔案,但此評分指令碼會一次處理一個資料列。 這是處理佔用大量資源的模型 (例如轉換程式) 時常見的模式,因為嘗試載入整個批次並一次傳送至模型,可能會導致批次執行程式面臨高記憶體壓力 (OOM 例外狀況)。

  2. 我們必須指定要執行部署的環境。 在我們的案例中,我們的模型會在 Torch 上執行,而且它需要來自 HuggingFace 的程式庫 transformersaccelerateoptimum。 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 需要 CUDA 11.8 相容硬體裝置才能執行 Torch 2.0 和 Ubuntu 20.04。 如果您的 GPU 裝置不支援此版本的 CUDA,您可以檢查替代的 torch113-conda.yaml conda 環境(也可在存放庫上取得),其會透過 Ubuntu 18.04 搭配 CUDA 10.1 執行 Torch 1.3。 不過,此設定不支援使用 optimumaccelerate 程式庫的加速。

  4. 每個部署都會在計算叢集上執行。 其支援 Azure Machine Learning Compute 叢集 (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
    

    重要

    您會在此部署中注意到參數 timeout 中的 retry_settings 有很高的值。 這是因為我們執行的模型本質所造成。 這是佔用大量資源的模型,單一資料列的推斷可能就需要 60 秒的時間。 參數 timeout 會控制批次部署應該等候評分指令碼完成處理每個迷你批次的時間。 由於我們的模型會逐列執行預測,因此處理較長的檔案可能需要一段時間。 另請注意,每個批次的檔案數目會設定為 1 (mini_batch_size=1)。 這同樣與我們執行的工作本質相關。 每個批次一次處理一個檔案所佔用的大量資源便足以證明。 您會注意到這是 NLP 處理的模式。

  6. 雖然您可以在端點內叫用特定部署,但是您通常會想要叫用端點本身,讓端點決定要使用的部署。 這類部署名為「預設」部署。 這可讓您變更預設部署,進而變更提供部署的模型,而不需變更與端點叫用者之間的合約。 使用下列指示來更新預設部署:

    DEPLOYMENT_NAME="text-summarization-hfbart"
    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  7. 此時,我們的批次端點已可供使用。

測試部署

為了測試我們的端點,我們將使用資料集 BillSum: A Corpus for Automatic Summarization of US Legislation。 此範例包含在資料夾 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 個檔案。 雖然這可能不符合您的案例,但請考慮您的模型每次可以評分的檔案數目。 請記住,輸入大小與模型記憶體使用量之間的關聯性可能不是深度學習模型的線性。
  • 如果您的模型一次甚至無法處理一個檔案 (類似此範例中的情況),請考慮以資料列/區塊為單位讀取輸入資料。 如果您必須達到更高的輸送量或硬體使用率,請在資料列層級實作批次處理。
  • 請根據模型佔用的資源以及您期待處理的資料量來設定部署的 timeout 值。 請記住,timeout 會指定批次部署等候評分指令碼執行指定批次的時間。 如果您的批次有許多檔案或檔案具有許多資料列,這會影響此參數的適當值。

處理文字的 MLflow 模型考量

先前提及的考量同樣適用於 MLflow 模型。 不過,由於您不需要為 MLflow 模型部署提供評分指令碼,因此所提及的一些建議可能需要不同的方法。

  • 批次端點中的 MLflow 模型支援將表格式資料讀取為輸入資料,其中可以包含長序列的文字。 如需支援哪些檔案類型的詳細資料,請參閱檔案的類型支援
  • 批次部署會呼叫 MLflow 模型的預測函式,將整個檔案的內容作為 Pandas 資料框架。 如果您的輸入資料包含許多資料列,則有可能發生執行複雜模型 (如本教學課程中所展示的模型) 導致記憶體不足的例外狀況。 如果這符合您的案例,您可以考慮: