Condividi tramite


Distribuire modelli di linguaggio negli endpoint batch

SI APPLICA A:Estensione ML dell'interfaccia della riga di comando di Azure v2 (corrente)Python SDK azure-ai-ml v2 (corrente)

Gli endpoint batch possono essere usati per distribuire modelli costosi, ad esempio modelli linguistici, sui dati di testo. In questa esercitazione, viene illustrato come distribuire un modello in grado di eseguire il riepilogo di testo di lunghe sequenze di testo usando un modello di HuggingFace. Illustra anche come eseguire l'ottimizzazione dell'inferenza usando librerie HuggingFace optimum e accelerate.

Informazioni sull'esempio

Il modello con cui lavoreremo è stato ottenuto usando gli strumenti di trasformazione della libreria più diffusi di HuggingFace insieme a un modello con training preliminare di Facebook con l'architettura BART. È stato introdotto nel documento BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation. Questo modello presenta i vincoli seguenti che sono importanti da tenere presenti per la distribuzione:

  • Può funzionare con sequenze fino a 1024 token.
  • È stato formato per l'esecuzione del riepilogo del testo in inglese.
  • Verrà utilizzato Torch come back-end.

L'esempio contenuto in questo articolo si basa sugli esempi di codice contenuti nel repository azureml-examples. Per eseguire i comandi in locale senza dover copiare o incollare il file YAML e altri file, innanzitutto clonare il repository quindi cambiare le directory nella cartella:

git clone https://github.com/Azure/azureml-examples --depth 1
cd azureml-examples/cli

I file per questo esempio si trovano in:

cd endpoints/batch/deploy-models/huggingface-text-summarization

Seguire la procedura nei Jupyter Notebook

È possibile seguire questo esempio in un Jupyter Notebook. Nel repository clonato, aprire il notebook: text-summarization-batch.ipynb.

Prerequisiti

  • Una sottoscrizione di Azure. Se non si ha una sottoscrizione di Azure, creare un account gratuito prima di iniziare. Provare la versione gratuita o a pagamento di Azure Machine Learning.

  • Un'area di lavoro di Azure Machine Learning. Per creare un'area di lavoro, vedere Gestire le aree di lavoro di Azure Machine Learning.

  • Assicurarsi di disporre delle autorizzazioni seguenti nell'area di lavoro di Azure Machine Learning:

    • Creare o gestire endpoint e distribuzioni batch: usare un ruolo di proprietario, collaboratore o personalizzato che consenta Microsoft.MachineLearningServices/workspaces/batchEndpoints/*.
    • Creare distribuzioni di Azure Resource Manager nel gruppo di risorse dell'area di lavoro: usare un ruolo di Proprietario, Contributore o Personalizzato che consenta Microsoft.Resources/deployments/write nel gruppo di risorse in cui viene distribuita l'area di lavoro.
  • Installare il software seguente per usare Azure Machine Learning:

    Eseguire il comando seguente per installare l'interfaccia della riga di comando di Azure e l'mlestensione per Azure Machine Learning:

    az extension add -n ml
    

    Le distribuzioni dei componenti della pipeline per gli endpoint batch sono state introdotte nella versione 2.7 dell'estensione ml per l'interfaccia della riga di comando di Azure. Usare il comando az extension update --name ml per ottenere la versione più recente.


Connettersi all'area di lavoro

L'area di lavoro è la risorsa di primo livello per Machine Learning. Fornisce una posizione centralizzata per lavorare con tutti gli artefatti creati durante l'uso di Machine Learning. In questa sezione ci si connette all'area di lavoro in cui verranno eseguite le attività di distribuzione.

Immettere i valori per l'ID sottoscrizione, l'area di lavoro, la posizione e il gruppo di risorse nel codice seguente:

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

Registrazione del modello

A causa delle dimensioni del modello, non è stato incluso in questo repository. È invece possibile scaricare una copia dall'hub del modello HuggingFace. Sono necessari i pacchetti transformers e torch installati nell'ambiente in uso.

%pip install transformers torch

Usare il codice seguente per scaricare il modello in una cartella model:

from transformers import pipeline

model = pipeline("summarization", model="facebook/bart-large-cnn")
model_local_path = 'model'
summarizer.save_pretrained(model_local_path)

È ora possibile registrare questo modello nel registro di Azure Machine Learning:

MODEL_NAME='bart-text-summarization'
az ml model create --name $MODEL_NAME --path "model"

Creazione dell'endpoint

Verrà creato un endpoint batch denominato text-summarization-batch in cui distribuire il modello HuggingFace per eseguire il riepilogo del testo nei file di testo in inglese.

  1. Decidere il nome dell'endpoint. Il nome dell'endpoint finisce nell'URI associato all'endpoint. Per questo motivo, i nomi degli endpoint batch devono essere univoci all'interno di un'area di Azure. Ad esempio, può essere presente un solo endpoint batch con il nome mybatchendpoint in westus2.

    In questo caso, inserire il nome dell'endpoint in una variabile in modo da potervi fare facilmente riferimento in un secondo momento.

    ENDPOINT_NAME="text-summarization-batch"
    
  2. Configurare l'endpoint batch

    Il seguente file YAML definisce un endpoint batch:

    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. Creare l'endpoint:

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

Creazione della distribuzione

Verrà ora creata la distribuzione che ospita il modello:

  1. È necessario creare uno script di assegnazione dei punteggi in grado di leggere i file CSV forniti dalla distribuzione batch e restituire i punteggi del modello con il riepilogo. Lo script seguente esegue queste azioni:

    • Indica una funzione init che rileva la configurazione hardware (CPU e GPU) e carica il modello di conseguenza. Sia il modello che il tokenizer vengono caricati in variabili globali. Non viene usato un oggetto pipeline da HuggingFace per tenere conto della limitazione nella sequenza lenghs del modello attualmente in uso.
    • Si noti che vengono eseguite ottimizzazioni del modello per migliorare le prestazioni usando librerie optimum e accelerate. Se il modello o l'hardware non lo supporta, verrà eseguita la distribuzione senza tali ottimizzazioni.
    • Indica una funzione run eseguita per ogni mini batch fornito dalla distribuzione batch.
    • La funzione run legge l'intero batch usando la libreria datasets. Il testo da riepilogare si trova nella colonna text.
    • Il metodo run esegue l'iterazione su ognuna delle righe del testo ed esegue la stima. Poiché si tratta di un modello molto costoso, l'esecuzione della stima su interi file comporterà un'eccezione di memoria insufficiente. Si noti che il modello non viene eseguito con l'oggetto pipeline da transformers. Questa operazione viene eseguita per tenere conto di sequenze lunghe di testo e la limitazione di 1024 token nel modello sottostante in uso.
    • Restituisce il riepilogo del testo fornito.

    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
    

    Suggerimento

    Anche se i file vengono forniti in mini batch dalla distribuzione, questo script di assegnazione dei punteggi elabora una riga per volta. Si tratta di un modello comune quando si gestiscono modelli costosi (come gli strumenti di trasformazione) poiché il tentativo di caricare l'intero batch e inviarlo al modello contemporaneamente può comportare un utilizzo elevato della memoria sull'executor batch (eccezioni OOM).

  2. È necessario indicare l'ambiente in cui verrà eseguita la distribuzione. In questo caso, il modello viene eseguito in Torch e richiede le librerie transformers, accelerate e optimum da HuggingFace. Azure Machine Learning ha già un ambiente con il supporto Torch e GPU disponibile. Si aggiungeranno solo un paio di dipendenze in un file di 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. È possibile usare il file conda indicato in precedenza come indicato di seguito:

    La definizione dell'ambiente è inclusa nel file di distribuzione.

    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
    

    Importante

    L'ambiente torch200-transformers-gpu creato richiede un dispositivo hardware compatibile con CUDA 11.8 per eseguire Torch 2.0 e Ubuntu 20.04. Se il dispositivo GPU non supporta questa versione di CUDA, è possibile controllare l'ambiente conda alternativo torch113-conda.yaml (disponibile anche nel repository), che esegue Torch 1.3 su Ubuntu 18.04 con CUDA 10.1. Tuttavia, l'accelerazione che usa le librerie optimum e accelerate non sarà supportata in questa configurazione.

  4. Ogni distribuzione viene eseguita nei cluster di calcolo. Supportano sia i cluster dell’ambiente di calcolo di Machine Learning (AmlCompute) sia i cluster Kubernetes. In questo esempio il modello può trarre vantaggio dall'accelerazione GPU, motivo per cui si usa un cluster GPU.

    az ml compute create -n gpu-cluster --type amlcompute --size STANDARD_NV6 --min-instances 0 --max-instances 2
    

    Nota

    A questo punto non viene addebitato alcun costo per l'ambiente di calcolo perché il cluster rimane a 0 nodi fino a quando non viene richiamato un endpoint batch e non viene inviato un processo di assegnazione dei punteggi batch. Altre informazioni su come gestire e ottimizzare i costi per AmlCompute.

  5. A questo punto, è possibile creare la distribuzione.

    Per creare una nuova distribuzione nell'endpoint creato, creare una YAMLconfigurazione simile alla seguente. È possibile controllare lo schema YAML dell'endpoint batch completo per ottenere proprietà aggiuntive.

    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
    

    Creare quindi la distribuzione con il comando seguente:

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

    Importante

    Si noterà in questa distribuzione un valore elevato di timeout nel parametro retry_settings. Il motivo è dovuto alla natura del modello in esecuzione. Si tratta di un modello molto costoso e l'inferenza su una singola riga può richiedere fino a 60 secondi. I parametri timeout controllano il tempo di attesa della distribuzione batch per completare l'elaborazione di ogni mini batch. Poiché il modello esegue stime riga per riga, l'elaborazione di un file di grandi dimensioni può richiedere tempo. Si noti anche che il numero di file per batch è impostato su 1 (mini_batch_size=1). Ancora una volta, questo è dovuto alla natura del lavoro che stiamo facendo. L'elaborazione di un file alla volta per batch è abbastanza costosa da giustificare tale scelta. Si noterà che si tratta di un criterio nell'elaborazione NLP.

  6. Anche se è possibile richiamare una distribuzione specifica all'interno di un endpoint, in genere si desidera richiamare l'endpoint stesso e consentire all'endpoint di decidere quale distribuzione usare. Tale distribuzione è denominata distribuzione "predefinita". In questo modo è possibile modificare la distribuzione predefinita e quindi modificare il modello che gestisce la distribuzione senza modificare il contratto con l'utente che richiama l'endpoint. Usare l'istruzione seguente per aggiornare la distribuzione predefinita:

    DEPLOYMENT_NAME="text-summarization-hfbart"
    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  7. A questo punto, l'endpoint batch è pronto per essere usato.

Test della distribuzione

Per testare l'endpoint, si userà un campione di set di dati BillSum: un corpus per la sintesi automatica della legislazione statunitense. Questo esempio è incluso nel repository nella cartella data. Si noti che il formato dei dati è CSV e il contenuto da riepilogare si trova nella colonna text, come previsto dal modello.

  1. Richiamare l'endpoint:

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

    Nota

    L'utilità jq potrebbe non essere installata in ogni installazione. È possibile ottenere istruzioni in questo collegamento.

    Suggerimento

    Si noti che indicando un percorso locale come input, i dati vengono caricati nell'account di archiviazione predefinito di Azure Machine Learning.

  2. Un processo batch viene avviato non appena viene restituito il comando. È possibile monitorare lo stato del processo fino al termine dell'operazione:

    az ml job show -n $JOB_NAME --web
    
  3. Al termine della distribuzione, è possibile scaricare le stime:

    Per scaricare le previsioni, usare il comando seguente:

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

Considerazioni sulla distribuzione di modelli che elaborano il testo

Come accennato in alcune delle note di questa esercitazione, l'elaborazione del testo può avere alcune peculiarità che richiedono una configurazione specifica per le distribuzioni batch. Quando si progetta la distribuzione batch, tenere presente quanto segue:

  • Alcuni modelli NLP possono essere molto costosi in termini di memoria e tempo di calcolo. In questo caso, è consigliabile ridurre il numero di file inclusi in ogni mini batch. Nell'esempio precedente, il numero è stato portato al minimo, ovvero 1 file per batch. Anche se questo potrebbe non essere il caso, prendere in considerazione il numero di file a cui il modello può assegnare un punteggio ogni volta. Tenere presente che la relazione tra le dimensioni dell'input e il footprint di memoria del modello potrebbe non essere lineare per i modelli di Deep Learning.
  • Se il modello non riesce nemmeno a gestire un file alla volta (come in questo esempio), è consigliabile leggere i dati di input in righe/blocchi. Implementare l'invio in batch a livello di riga se è necessario ottenere una velocità effettiva o un utilizzo hardware superiore.
  • Impostare il valore timeout della distribuzione in modo che corrisponda al costo del modello e alla quantità di dati che si prevede di elaborare. Tenere presente che timeout indica il tempo di attesa della distribuzione batch per l'esecuzione dello script di assegnazione dei punteggi per un determinato batch. Se il batch include molti file o file con molte righe, questo influisce sul valore corretto di questo parametro.

Considerazioni per i modelli MLflow che elaborano il testo

Le stesse considerazioni indicate in precedenza si applicano ai modelli MLflow. Tuttavia, poiché non è necessario fornire uno script di assegnazione dei punteggi per la distribuzione del modello MLflow, alcune delle raccomandazioni indicate potrebbero richiedere un approccio diverso.

  • I modelli MLflow negli endpoint batch supportano la lettura di dati tabulari come dati di input, che possono contenere sequenze lunghe di testo. Per informazioni dettagliate sui tipi di file supportati, vedere Supporto dei tipi di file.
  • Le distribuzioni batch chiamano la funzione di stima del modello MLflow con il contenuto di un intero file in come frame di dati Pandas. Se i dati di input contengono molte righe, è probabile che l'esecuzione di un modello complesso (come quello presentato in questa esercitazione) comporti un'eccezione di memoria insufficiente. In questo caso, è possibile considerare quanto segue: