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 ou à coller des fichiers YAML et autres, utilisez les commandes suivantes pour cloner le référentiel et accéder au dossier de votre langage de programmation :
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
Un abonnement Azure. Si vous n’avez pas d’abonnement Azure, créez un compte gratuit avant de commencer.
Un espace de travail Azure Machine Learning. Pour créer un espace de travail, consultez Gérer les espaces de travail Azure Machine Learning.
Autorisations suivantes dans l’espace de travail Azure Machine Learning :
- Pour la création ou la gestion des déploiements et des points de terminaison de lot : utilisez un rôle Propriétaire, Contributeur, ou un rôle personnalisé auquel les autorisations
Microsoft.MachineLearningServices/workspaces/batchEndpoints/*
ont été accordées. - Pour la création de déploiements Azure Resource Manager dans le groupe de ressources de l’espace de travail : utilisez un rôle Propriétaire, Contributeur, ou un rôle personnalisé auquel l’autorisation
Microsoft.Resources/deployments/write
a été accordée dans le groupe de ressources où l’espace de travail est déployé.
- Pour la création ou la gestion des déploiements et des points de terminaison de lot : utilisez un rôle Propriétaire, Contributeur, ou un rôle personnalisé auquel les autorisations
L’interface CLI Azure Machine Learning ou le SDK Azure Machine Learning pour Python :
Exécutez la commande suivante pour installer l’interface de ligne de commande Azure et l’extension pour Azure Machine Learning
ml
:az extension add -n ml
Les déploiements de composants de pipeline pour les points de terminaison de lot sont introduits dans la version 2.7 de l’extension
ml
pour Azure CLI. Utilisez la commandeaz extension update --name ml
pour obtenir la dernière version.
Se connecter à un espace de travail
L’espace de travail est la ressource de niveau supérieur du service Azure Machine Learning. Il fournit un emplacement centralisé dans lequel utiliser tous les artefacts que vous créez quand vous utilisez Azure Machine Learning. Dans cette section, vous vous connectez à l’espace de travail dans lequel vous effectuez vos tâches de déploiement.
Dans la commande suivante, entrez votre ID d’abonnement, le nom de l’espace de travail, le nom du groupe de ressources et l’emplacement :
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.
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
danswestus2
.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
Créez le point de terminaison :
Création du déploiement
Créons le déploiement qui héberge le modèle :
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’objetpipeline
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
etaccelerate
. 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èquedatasets
. Le texte que vous devez résumer est situé sur la colonnetext
. - 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’objetpipeline
à partir detransformers
. 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).
- Indique une fonction
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èquestransformers
,accelerate
etoptimum
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 fichierconda.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]
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 condatorch113-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èquesoptimum
etaccelerate
n’est pas prise en charge dans cette configuration.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.
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ètreretry_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ètrestimeout
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.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 :
À 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).
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.
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 :
Une fois le déploiement terminé, nous pouvons télécharger les prédictions :
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 quetimeout
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 :
- Personnalisez la façon dont votre modèle exécute des prédictions et implémentez le traitement par lots. Pour savoir comment personnaliser l’inférence du modèle MLflow, consultez Journalisation des modèles personnalisés.
- Créez un script de scoring et chargez votre modèle à l’aide de
mlflow.<flavor>.load_model()
. Pour plus d’informations, consultez Utilisation de modèles MLflow avec un script de scoring.