Partage via


Déployer des modèles de langage dans des points de terminaison par lots

S’APPLIQUE À :Extension Azure ML CLI v2 (actuelle)Kit de développement logiciel (SDK) Python azure-ai-ml v2 (préversion)

Les points de terminaison par lots peuvent être utilisés pour déployer des modèles coûteux, tels que des modèles de langage, sur des données textuelles. Dans ce didacticiel, vous apprendrez à déployer un modèle qui peut effectuer un résumé textuel de longues séquences de texte à l’aide d’un modèle depuis HuggingFace. Il montre également comment effectuer une optimisation de l’inférence à l’aide des bibliothèques HuggingFace optimum et accelerate.

À propos de cet exemple

Le modèle avec lequel vous allez travailler a été créé à l’aide des transformateurs de bibliothèque populaires de HuggingFace, ainsi qu’un modèle préentraîné de Facebook avec l’architecture BART. Il a été introduit dans le document BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation. Ce modèle présente les contraintes suivantes qu’il faut garder à l’esprit pour le déploiement :

  • Il peut fonctionner avec des séquences allant jusqu’à 1 024 jetons.
  • Il est entraîné pour résumer du texte en anglais.
  • Vous allez utiliser Torch comme back-end.

L’exemple de cet article est basé sur des extraits de code contenus dans le référentiel azureml-examples. Pour exécuter les commandes localement sans avoir à copier/coller le fichier YAML et d’autres fichiers, clonez d’abord le référentiel, puis modifiez les répertoires dans le dossier :

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

Les fichiers de cet exemple se situent dans :

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

Suivre dans les notebooks Jupyter

Vous pouvez suivre cet exemple dans un notebook Jupyter. Dans le dépôt cloné, ouvrez le notebook : text-summarization-batch.ipynb.

Prérequis

Avant de suivre les étapes décrites dans cet article, vérifiez que vous disposez des composants requis suivants :

  • Un abonnement Azure. Si vous n’avez pas d’abonnement Azure, créez un compte gratuit avant de commencer. Essayez la version gratuite ou payante d’Azure Machine Learning.

  • Un espace de travail Azure Machine Learning. Si vous n’en avez pas, suivez les étapes décrites dans l’article Gérer des espaces de travail Azure Machine Learning pour en créer un.

  • Vérifiez que vous disposez des autorisations suivantes dans l’espace de travail :

    • Créez ou gérez des points de terminaison et des déploiements par lots : utilisez un rôle propriétaire, contributeur ou personnalisé autorisant Microsoft.MachineLearningServices/workspaces/batchEndpoints/*.

    • Créez des déploiements ARM dans le groupe de ressources de l’espace de travail : utilisez un rôle propriétaire, contributeur ou personnalisé autorisant Microsoft.Resources/deployments/write dans le groupe de ressources où l’espace de travail est déployé.

  • Vous devez installer le logiciel suivant pour utiliser Azure Machine Learning :

    L’interface Azure CLI et l’mlextension pour Azure Machine Learning.

    az extension add -n ml
    

    Notes

    Les déploiements de composants de pipeline pour des points de terminaison Batch ont été introduits dans la version 2.7 de l’extension ml pour Azure CLI. Utilisez az extension update --name ml pour en obtenir la dernière version.

Se connecter à un espace de travail

L’espace de travail est la ressource de niveau supérieur pour Azure Machine Learning. Il fournit un emplacement centralisé dans lequel exploiter tous les artefacts que vous créez lorsque vous utilisez Azure Machine Learning. Dans cette section, nous allons nous connecter à l’espace de travail dans lequel vous allez effectuer des tâches de déploiement.

Transmettez les valeurs de votre ID d’abonnement, de votre espace de travail, de votre emplacement et de votre groupe de ressources dans le code suivant :

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

Inscription du modèle

En raison de la taille du modèle, il n’a pas été inclus dans ce référentiel. Vous pouvez plutôt télécharger une copie depuis le hub du modèle HuggingFace. Les packages transformers et torch doivent être installés dans l’environnement que vous utilisez.

%pip install transformers torch

Utilisez le code suivant pour télécharger le modèle dans un dossier model :

from transformers import pipeline

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

Nous pouvons maintenant inscrire ce modèle dans le registre Azure Machine Learning :

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

Création du point de terminaison

Nous allons créer un point de terminaison de lot nommé text-summarization-batch où nous déploierons le modèle HuggingFace pour faire une synthèse de fichiers texte en anglais.

  1. Choisissez le nom du point de terminaison. Le nom du point de terminaison figure dans l’URI associé à celui-ci. De ce fait, les noms de point de terminaison de traitement par lots doivent être uniques au sein d’une région Azure. Par exemple, il ne peut y avoir qu’un seul point de terminaison de traitement par lots avec le nom mybatchendpoint dans westus2.

    En l’occurrence, nous allons placer le nom du point de terminaison dans une variable afin que nous puissions facilement le référencer ultérieurement.

    ENDPOINT_NAME="text-summarization-batch"
    
  2. Configurer votre point de terminaison de traitement par lots

    Le fichier YAML suivant définit un point de terminaison de lot :

    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. Créez le point de terminaison :

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

Création du déploiement

Créons le déploiement qui héberge le modèle :

  1. Vous devez créer un script de scoring qui peut lire les fichiers CSV fournis par le déploiement par lots et retourner les scores du modèle avec le résumé. Le script suivant exécute ces actions :

    • Indique une fonction init qui détecte la configuration matérielle (CPU ou GPU) et charge le modèle en conséquence. Le modèle et le générateur de jetons sont chargés dans des variables globales. Nous n’utilisons pas d’objet pipeline de HuggingFace pour tenir compte de la limitation des longueurs de séquence du modèle que nous utilisons actuellement.
    • Notez que nous effectuons des optimisations de modèles pour améliorer les performances à l’aide des bibliothèques optimum et accelerate. Si le modèle ou le matériel ne le prend pas en charge, nous exécutons le déploiement sans ces optimisations.
    • Indique une fonction run exécutée pour chaque mini-lot fourni par le déploiement par lot.
    • La fonction run lit l’intégralité du lot à l’aide de la bibliothèque datasets. Le texte que vous devez résumer est situé sur la colonne text.
    • La méthode run itère sur chacune des lignes du texte et exécute la prédiction. Étant donné qu’il s’agit d’un modèle très coûteux, l’exécution de la prédiction sur des fichiers entiers entraîne une exception de type mémoire insuffisante. Notez que le modèle n’est pas exécuté avec l’objet pipeline à partir de transformers. Cela permet de tenir compte de longues séquences de texte et de la limitation de 1 024 jetons dans le modèle sous-jacent que vous utilisez.
    • Il retourne le résumé du texte fourni.

    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
    

    Conseil

    Bien que les fichiers soient fournis dans des mini-lots par le déploiement, ce script de scoring traite un fichier à la fois. Il s’agit d’un modèle courant lors de l’utilisation de modèles coûteux (tels que des transformateurs), car une tentative de charger l’intégralité du lot et de l’envoyer au modèle en une fois peut entraîner une pression de mémoire élevée sur l’exécuteur de lot (exceptions de mémoire insuffisante).

  2. Nous devons indiquer sur quel environnement nous allons exécuter le déploiement. Dans notre cas, notre modèle s’exécute sur Torch et nécessite les bibliothèques transformers, accelerate et optimum de HuggingFace. Azure Machine Learning a déjà un environnement qui prend en charge Torch et les GPU. Nous allons simplement ajouter quelques dépendances dans un fichier 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. Nous pouvons utiliser le fichier conda mentionné précédemment comme suit :

    La définition d’environnement est incluse dans le fichier de déploiement.

    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
    

    Important

    L’environnement torch200-transformers-gpu que nous avons créé nécessite un matériel compatible CUDA 11.8 pour exécuter Torch 2.0 et Ubuntu 20.04. Si votre appareil GPU ne prend pas en charge cette version de CUDA, vous pouvez vérifier l’autre environnement conda torch113-conda.yaml (également disponible sur le dépôt), qui exécute Torch 1.3 sur Ubuntu 18.04 avec CUDA 10.1. Toutefois, l’accélération à l’aide des bibliothèques optimum et accelerate n’est pas prise en charge dans cette configuration.

  4. Chaque déploiement s’exécute sur des clusters de calcul. Ils prennent en charge les clusters de calcul Azure Machine Learning (AmlCompute) ou les clusters Kubernetes. Dans cet exemple, étant donné que notre modèle peut tirer parti de l’accélération GPU, nous utilisons un cluster GPU.

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

    Notes

    Vous n’êtes pas facturé pour le calcul à ce stade, car le cluster reste à 0 nœud jusqu’à ce qu’un point de terminaison de lot soit appelé et qu’un travail de scoring par lots soit envoyé. Apprenez-en davantage sur la gestion et l’optimisation des coûts pour AmlCompute.

  5. Créons à présent le déploiement.

    Pour créer un nouveau déploiement sous le point de terminaison créé, créez une configuration YAML comme suit. Pour obtenir des propriétés supplémentaires, vous pouvez vérifier le schéma YAML du point de terminaison de lot complet.

    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
    

    Ensuite, créez le déploiement avec la commande suivante :

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

    Important

    Vous remarquerez dans ce déploiement une valeur élevée dans timeout dans le paramètre retry_settings. Cela est dû à la nature du modèle que vous exécutez. Il s’agit d’un modèle très coûteux et une inférence sur une seule ligne peut prendre jusqu’à 60 secondes. Les paramètres timeout contrôlent la durée pendant laquelle le déploiement par lots doit attendre que le script de scoring termine le traitement de chaque mini-lot. Étant donné que votre modèle exécute des prédictions ligne par ligne, le traitement d’un fichier long peut prendre du temps. Notez également que le nombre de fichiers par lot est défini sur 1 (mini_batch_size=1). C’est là encore lié à la nature du travail que vous effectuez. Le traitement d’un fichier à la fois par lot est assez coûteux pour le justifier. Vous remarquerez qu’il s’agit d’un pattern dans le traitement NLP.

  6. Bien que vous puissiez appeler un déploiement spécifique à l’intérieur d’un point de terminaison, vous voulez en général appeler le point de terminaison lui-même et le laisser décider du déploiement à utiliser. Ce déploiement s’appelle le déploiement « par défaut ». Cela vous donne la possibilité de modifier le déploiement par défaut et donc de modifier le modèle qui sert le déploiement sans modifier le contrat avec l’utilisateur appelant le point de terminaison. Utilisez l’instruction suivante pour mettre à jour le déploiement par défaut :

    DEPLOYMENT_NAME="text-summarization-hfbart"
    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  7. À ce stade, notre point de terminaison batch est prêt à être utilisé.

Test du déploiement

Pour tester notre point de terminaison, nous allons utiliser un exemple du jeu de données BillSum: A Corpus for Automatic Summarization of US Legislation. Cet exemple est inclus dans le dépôt dans le dossier data. Notez que les données sont au format CSV et que le contenu à résumer se trouve sous la colonne text (ce qui est attendu par le modèle).

  1. Appelons le point de terminaison :

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

    Notes

    L’utilitaire jq peut ne pas être installé sur chaque installation. Vous pouvez obtenir plus d’instructions sur ce lien.

    Conseil

    Notez qu’en indiquant un chemin local comme entrée, les données sont chargées dans le compte de stockage par défaut d’Azure Machine Learning.

  2. Une tâche de lot est démarrée dès que la commande retourne son résultat. Vous pouvez surveiller l’état du travail jusqu’à ce qu’il se termine :

    az ml job show -n $JOB_NAME --web
    
  3. Une fois le déploiement terminé, nous pouvons télécharger les prédictions :

    Utilisez les commandes suivantes pour télécharger les prédictions :

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

Considérations relatives au déploiement de modèles qui traitent du texte

Comme mentionné dans certaines des notes de ce tutoriel, le traitement du texte peut avoir certaines particularités qui nécessitent une configuration spécifique pour les déploiements par lots. Prenez en compte les points suivants lors de la conception du déploiement par lots :

  • Certains modèles NLP peuvent être très coûteux en termes de mémoire et de temps de calcul. Si c’est le cas, envisagez de réduire le nombre de fichiers inclus sur chaque mini-lot. Dans l’exemple ci-dessus, le nombre a été pris au minimum, 1 fichier par lot. Bien que ce ne soit peut-être pas votre cas, prenez en compte le nombre de fichiers que votre modèle peut noter à chaque fois. N’oubliez pas que la relation entre la taille de l’entrée et l’empreinte mémoire de votre modèle peut ne pas être linéaire pour les modèles Deep Learning.
  • Si votre modèle ne peut même pas gérer un fichier à la fois (comme dans cet exemple), envisagez de lire les données d’entrée dans des lignes/blocs. Implémentez le traitement par lots au niveau des lignes si vous devez obtenir un débit ou une utilisation matérielle plus élevée.
  • Définissez la valeur timeout de votre déploiement en accord avec le coût de votre modèle et la quantité de données que vous prévoyez de traiter. N’oubliez pas que timeout indique le délai d’attente pendant lequel le déploiement par lots attend que votre script de scoring s’exécute pour un lot donné. Si votre lot comporte de nombreux fichiers, ou des fichiers avec de nombreuses lignes, cela aura un impact sur la valeur appropriée de ce paramètre.

Considérations relatives aux modèles MLflow qui traitent du texte

Les mêmes considérations mentionnées ci-dessus s’appliquent aux modèles MLflow. Toutefois, étant donné que vous n’êtes pas obligé de fournir un script de scoring pour votre modèle de déploiement MLflow, une partie des recommandations mentionnées peut nécessiter une approche différente.

  • Les modèles MLflow dans les points de terminaison de lot prennent en charge la lecture des données tabulaires en tant que données d’entrée, qui peuvent contenir de longues séquences de texte. Pour plus d’informations, consultez les types de fichiers pris en charge.
  • Les déploiements par lots appellent la fonction de prédiction de votre modèle MLflow avec le contenu d’un fichier entier en tant que trame de données Pandas. Si vos données d’entrée contiennent de nombreuses lignes, il est probable que l’exécution d’un modèle complexe (comme celui présenté dans ce tutoriel) entraîne une exception de mémoire insuffisante. Si c’est votre cas, vous pouvez envisager ce qui suit :