Déploiement de grands modèles à des fins d’inférence avec TorchServe - Amazon SageMaker AI

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Déploiement de grands modèles à des fins d’inférence avec TorchServe

Ce didacticiel explique comment déployer de grands modèles et servir les demandes d’inférence dans Amazon SageMaker AI avec TorchServe sur des GPU. Cet exemple déploie le modèle OPT-30b sur une instance ml.g5. Vous pouvez le modifier pour l'adapter à d'autres modèles et types d'instance. Dans les exemples, remplacez italicized placeholder text par vos propres informations.

TorchServe est une puissante plateforme ouverte pour l’inférence de grands modèles distribués. En prenant en charge des bibliothèques populaires telles que PyTorch, PiPPy native, DeepSpeed et HuggingFace Accelerate, elle propose des API de gestion uniformes qui restent cohérentes entre les scénarios d’inférence de grands modèles distribués et non distribués. Pour plus d’informations, consultez la documentation TorchServe sur l’inférence de grands modèles.

Conteneurs de deep learning avec TorchServe

Pour déployer un grand modèle avec TorchServe sur SageMaker AI, vous pouvez utiliser l’un des conteneurs de deep learning (DLC) SageMaker AI. Par défaut, TorchServe est installé dans tous les DLC AWS PyTorch. Pendant le chargement des modèles, TorchServe peut installer des bibliothèques spécialisées adaptées aux grands modèles, comme PiPPy, Deepspeed et Accelerate.

Le tableau suivant répertorie tous les DLC SageMaker AI avec TorchServe.

Catégorie de DLC Cadre Matériel Exemple d'URL

Conteneurs de cadre SageMaker AI

PyTorch 2.0.0+

CPU, GPU

763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-inference:2.0.1-gpu-py310-cu118-ubuntu20.04-sagemaker

Conteneurs Graviton de cadre SageMaker AI

PyTorch 2.0.0+

CPU

763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-inference-graviton:2.0.1-cpu-py310-ubuntu20.04-sagemaker

Conteneurs d’inférence StabilityAI

PyTorch 2.0.0+

GPU

763104351884.dkr.ecr.us-east-1.amazonaws.com/stabilityai-pytorch-inference:2.0.1-sgm0.1.0-gpu-py310-cu118-ubuntu20.04-sagemaker

Conteneurs Neuron

PyTorch 1.13.1

Neuronx

763104351884.dkr.ecr.us-west-2.amazonaws.com/pytorch-inference-neuron:1.13.1-neuron-py310-sdk2.12.0-ubuntu20.04

Premiers pas

Avant de déployer votre modèle, respectez les prérequis. Vous pouvez également configurer les paramètres de votre modèle et personnaliser le code du gestionnaire.

Prérequis

Avant de démarrer, vérifiez que les conditions préalables suivantes sont respectées :

  1. Assurez-vous d’avoir accès à un compte AWS. Configurez votre environnement afin que l’AWS CLI puisse accéder à votre compte via un utilisateur IAM ou un rôle IAM AWS. Nous vous recommandons d'utiliser un rôle IAM. À des fins de test dans votre compte personnel, vous pouvez associer les politiques d'autorisations gérées suivantes au rôle IAM :

    Pour plus d’informations sur la façon d’attacher des politiques IAM à un rôle, consultez Ajout et suppression d’autorisations basées sur l’identité IAM dans le Guide de l’utilisateur IAM AWS.

  2. Configurez vos dépendances de façon locale, comme indiqué dans les exemples suivants.

    1. Installez la version 2 de l’AWS CLI :

      # Install the latest AWS CLI v2 if it is not installed !curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" !unzip awscliv2.zip #Follow the instructions to install v2 on the terminal !cat aws/README.md
    2. Installez SageMaker AI et le client Boto3 :

      # If already installed, update your client #%pip install sagemaker pip --upgrade --quiet !pip install -U sagemaker !pip install -U boto !pip install -U botocore !pip install -U boto3

Configuration des paramètres et des réglages du modèle

TorchServe utilise torchrun afin de configurer l’environnement distribué pour le traitement du parallélisme des modèles. TorchServe a la capacité de prendre en charge plusieurs applications de travail pour un grand modèle. Par défaut, TorchServe utilise un algorithme de type tourniquet (round-robin) pour attribuer des GPU à une application de travail sur un hôte. Dans le cas d’une inférence de grands modèles, le nombre de GPU attribué à chaque application de travail est automatiquement calculé en fonction du nombre de GPU spécifié dans le fichier model_config.yaml. La variable d’environnement CUDA_VISIBLE_DEVICES, qui spécifie les ID des dispositifs GPU visibles à un moment donné, est définie en fonction de ce nombre.

Par exemple, supposons qu’il y ait 8 GPU sur un nœud et qu’une application de travail requiert 4 GPU sur un nœud (nproc_per_node=4). Dans ce cas, TorchServe attribue quatre GPU à la première application de travail (CUDA_VISIBLE_DEVICES="0,1,2,3") et quatre GPU à la seconde (CUDA_VISIBLE_DEVICES="4,5,6,7”).

Outre ce comportement par défaut, TorchServe offre aux utilisateurs la flexibilité de spécifier des GPU pour une application de travail. Par exemple, si vous définissez la variable deviceIds: [2,3,4,5] dans le fichier YAML de configuration du modèle, et que vous définissez nproc_per_node=2, TorchServe attribue CUDA_VISIBLE_DEVICES=”2,3” à la première application de travail et CUDA_VISIBLE_DEVICES="4,5” à la seconde.

Dans l’exemple model_config.yaml suivant, nous configurons les paramètres frontaux et dorsaux pour le modèle OPT-30b. Les paramètres frontaux configurés sont parallelType, deviceType, deviceIds et torchrun. Pour des informations plus détaillées sur les paramètres frontaux que vous pouvez configurer, consultez la documentation PyTorch sur GitHub. La configuration dorsale est basée sur une carte YAML qui permet une personnalisation libre. Pour les paramètres dorsaux, nous définissons la configuration DeepSpeed et les paramètres supplémentaires utilisés par le code du gestionnaire personnalisé.

# TorchServe front-end parameters minWorkers: 1 maxWorkers: 1 maxBatchDelay: 100 responseTimeout: 1200 parallelType: "tp" deviceType: "gpu" # example of user specified GPU deviceIds deviceIds: [0,1,2,3] # sets CUDA_VISIBLE_DEVICES torchrun: nproc-per-node: 4 # TorchServe back-end parameters deepspeed: config: ds-config.json checkpoint: checkpoints.json handler: # parameters for custom handler code model_name: "facebook/opt-30b" model_path: "model/models--facebook--opt-30b/snapshots/ceea0a90ac0f6fae7c2c34bcb40477438c152546" max_length: 50 max_new_tokens: 10 manual_seed: 40

Personnalisation des gestionnaires

TorchServe propose des gestionnaires de base et des utilitaires de gestionnaire pour l’inférence de grands modèles créés avec des bibliothèques populaires. L’exemple suivant montre comment la classe TransformersSeqClassifierHandler du gestionnaire personnalisé étend BaseDeepSpeedHandler et utilise les utilitaires du gestionnaire. Pour un exemple de code complet, consultez le code custom_handler.py dans la documentation PyTorch sur GitHub.

class TransformersSeqClassifierHandler(BaseDeepSpeedHandler, ABC): """ Transformers handler class for sequence, token classification and question answering. """ def __init__(self): super(TransformersSeqClassifierHandler, self).__init__() self.max_length = None self.max_new_tokens = None self.tokenizer = None self.initialized = False def initialize(self, ctx: Context): """In this initialize function, the HF large model is loaded and partitioned using DeepSpeed. Args: ctx (context): It is a JSON Object containing information pertaining to the model artifacts parameters. """ super().initialize(ctx) model_dir = ctx.system_properties.get("model_dir") self.max_length = int(ctx.model_yaml_config["handler"]["max_length"]) self.max_new_tokens = int(ctx.model_yaml_config["handler"]["max_new_tokens"]) model_name = ctx.model_yaml_config["handler"]["model_name"] model_path = ctx.model_yaml_config["handler"]["model_path"] seed = int(ctx.model_yaml_config["handler"]["manual_seed"]) torch.manual_seed(seed) logger.info("Model %s loading tokenizer", ctx.model_name) self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.tokenizer.pad_token = self.tokenizer.eos_token config = AutoConfig.from_pretrained(model_name) with torch.device("meta"): self.model = AutoModelForCausalLM.from_config( config, torch_dtype=torch.float16 ) self.model = self.model.eval() ds_engine = get_ds_engine(self.model, ctx) self.model = ds_engine.module logger.info("Model %s loaded successfully", ctx.model_name) self.initialized = True def preprocess(self, requests): """ Basic text preprocessing, based on the user's choice of application mode. Args: requests (list): A list of dictionaries with a "data" or "body" field, each containing the input text to be processed. Returns: tuple: A tuple with two tensors: the batch of input ids and the batch of attention masks. """ def inference(self, input_batch): """ Predicts the class (or classes) of the received text using the serialized transformers checkpoint. Args: input_batch (tuple): A tuple with two tensors: the batch of input ids and the batch of attention masks, as returned by the preprocess function. Returns: list: A list of strings with the predicted values for each input text in the batch. """ def postprocess(self, inference_output): """Post Process Function converts the predicted response into Torchserve readable format. Args: inference_output (list): It contains the predicted response of the input text. Returns: (list): Returns a list of the Predictions and Explanations. """

Préparation des artefacts de votre modèle

Avant de déployer votre modèle sur SageMaker AI, vous devez empaqueter ses artefacts dans un fichier. Pour les grands modèles, nous vous recommandons d’utiliser l’outil PyTorch torch-model-archiver avec l’argument --archive-format no-archive, qui ignore l’étape de compression des artefacts du modèle. L’exemple suivant enregistre tous les artefacts du modèle dans un nouveau dossier nommé opt/.

torch-model-archiver --model-name opt --version 1.0 --handler custom_handler.py --extra-files ds-config.json -r requirements.txt --config-file opt/model-config.yaml --archive-format no-archive

Une fois le dossier opt/ créé, téléchargez le modèle OPT-30b dans le dossier à l’aide de l’outil PyTorch Download_model.

cd opt python path_to/Download_model.py --model_path model --model_name facebook/opt-30b --revision main

Enfin, chargez les artefacts du modèle sur un compartiment Amazon S3.

aws s3 cp opt {your_s3_bucket}/opt --recursive

Vous devriez maintenant avoir des artefacts de modèle stockés dans Amazon S3 prêts à être déployés sur un point de terminaison SageMaker AI.

Déploiement du modèle à l’aide du kit SageMaker Python SDK

Après avoir préparé les artefacts de votre modèle, vous pouvez déployer ce dernier sur un point de terminaison d’hébergement SageMaker AI. Cette section explique comment déployer un grand modèle sur un point de terminaison et effectuer des prédictions de réponse en streaming. Pour plus d’informations sur le streaming des réponses provenant des points de terminaison, consultez Invoquer des points de terminaison en temps réel.

Pour déployer votre modèle, procédez comme suit :

  1. Créez une session SageMaker AI, comme illustré dans l’exemple suivant.

    import boto3 import sagemaker from sagemaker import Model, image_uris, serializers, deserializers boto3_session=boto3.session.Session(region_name="us-west-2") smr = boto3.client('sagemaker-runtime-demo') sm = boto3.client('sagemaker') role = sagemaker.get_execution_role() # execution role for the endpoint sess= sagemaker.session.Session(boto3_session, sagemaker_client=sm, sagemaker_runtime_client=smr) # SageMaker AI session for interacting with different AWS APIs region = sess._region_name # region name of the current SageMaker Studio Classic environment account = sess.account_id() # account_id of the current SageMaker Studio Classic environment # Configuration: bucket_name = sess.default_bucket() prefix = "torchserve" output_path = f"s3://{bucket_name}/{prefix}" print(f'account={account}, region={region}, role={role}, output_path={output_path}')
  2. Créez un modèle non compressé dans SageMaker AI, comme illustré dans l’exemple suivant.

    from datetime import datetime instance_type = "ml.g5.24xlarge" endpoint_name = sagemaker.utils.name_from_base("ts-opt-30b") s3_uri = {your_s3_bucket}/opt model = Model( name="torchserve-opt-30b" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S"), # Enable SageMaker uncompressed model artifacts model_data={ "S3DataSource": { "S3Uri": s3_uri, "S3DataType": "S3Prefix", "CompressionType": "None", } }, image_uri=container, role=role, sagemaker_session=sess, env={"TS_INSTALL_PY_DEP_PER_MODEL": "true"}, ) print(model)
  3. Déployez le modèle sur une instance Amazon EC2, comme illustré dans l’exemple suivant.

    model.deploy( initial_instance_count=1, instance_type=instance_type, endpoint_name=endpoint_name, volume_size=512, # increase the size to store large model model_data_download_timeout=3600, # increase the timeout to download large model container_startup_health_check_timeout=600, # increase the timeout to load large model )
  4. Initialisez une classe pour traiter la réponse en streaming, comme illustré dans l’exemple suivant.

    import io class Parser: """ A helper class for parsing the byte stream input. The output of the model will be in the following format: ``` b'{"outputs": [" a"]}\n' b'{"outputs": [" challenging"]}\n' b'{"outputs": [" problem"]}\n' ... ``` While usually each PayloadPart event from the event stream will contain a byte array with a full json, this is not guaranteed and some of the json objects may be split across PayloadPart events. For example: ``` {'PayloadPart': {'Bytes': b'{"outputs": '}} {'PayloadPart': {'Bytes': b'[" problem"]}\n'}} ``` This class accounts for this by concatenating bytes written via the 'write' function and then exposing a method which will return lines (ending with a '\n' character) within the buffer via the 'scan_lines' function. It maintains the position of the last read position to ensure that previous bytes are not exposed again. """ def __init__(self): self.buff = io.BytesIO() self.read_pos = 0 def write(self, content): self.buff.seek(0, io.SEEK_END) self.buff.write(content) data = self.buff.getvalue() def scan_lines(self): self.buff.seek(self.read_pos) for line in self.buff.readlines(): if line[-1] != b'\n': self.read_pos += len(line) yield line[:-1] def reset(self): self.read_pos = 0
  5. Testez une prédiction de réponse en streaming, comme illustré dans l’exemple suivant.

    import json body = "Today the weather is really nice and I am planning on".encode('utf-8') resp = smr.invoke_endpoint_with_response_stream(EndpointName=endpoint_name, Body=body, ContentType="application/json") event_stream = resp['Body'] parser = Parser() for event in event_stream: parser.write(event['PayloadPart']['Bytes']) for line in parser.scan_lines(): print(line.decode("utf-8"), end=' ')

Vous avez maintenant déployé votre modèle sur un point de terminaison SageMaker AI et vous devriez pouvoir l’invoquer pour obtenir des réponses. Pour plus d’informations sur les points de terminaison en temps réel SageMaker AI, consultez Points de terminaison à modèle unique.