Meilleures pratiques pour les tests de charge des points de terminaison d'inférence en temps réel Amazon SageMaker

Meilleures pratiques pour les tests de charge des points de terminaison d'inférence en temps réel Amazon SageMaker

Nœud source: 1889926

Amazon Sage Maker est un service d'apprentissage automatique (ML) entièrement géré. Avec SageMaker, les data scientists et les développeurs peuvent créer et former rapidement et facilement des modèles ML, puis les déployer directement dans un environnement hébergé prêt pour la production. Il fournit une instance intégrée de bloc-notes de création Jupyter pour un accès facile à vos sources de données à des fins d'exploration et d'analyse, vous évitant ainsi de gérer des serveurs. Il fournit également des Algorithmes ML qui sont optimisés pour s'exécuter efficacement sur des données extrêmement volumineuses dans un environnement distribué.

L'inférence en temps réel SageMaker est idéale pour les charges de travail qui ont des exigences en temps réel, interactives et à faible latence. Avec l'inférence en temps réel SageMaker, vous pouvez déployer des points de terminaison REST qui sont soutenus par un type d'instance spécifique avec une certaine quantité de calcul et de mémoire. Le déploiement d'un point de terminaison en temps réel SageMaker n'est que la première étape du chemin vers la production pour de nombreux clients. Nous voulons être en mesure de maximiser les performances du point de terminaison pour atteindre un objectif de transactions par seconde (TPS) tout en respectant les exigences de latence. Une grande partie de l'optimisation des performances pour l'inférence consiste à s'assurer que vous sélectionnez le type d'instance approprié et que vous comptez pour sauvegarder un point de terminaison.

Cet article décrit les meilleures pratiques pour tester la charge d'un point de terminaison SageMaker afin de trouver la bonne configuration pour le nombre d'instances et la taille. Cela peut nous aider à comprendre les exigences minimales en matière d'instance provisionnée pour répondre à nos exigences de latence et de TPS. À partir de là, nous nous penchons sur la façon dont vous pouvez suivre et comprendre les mesures et les performances du point de terminaison SageMaker en utilisant Amazon Cloud Watch métrique.

Nous évaluons d'abord les performances de notre modèle sur une seule instance pour identifier le TPS qu'il peut gérer selon nos exigences de latence acceptables. Ensuite, nous extrapolons les résultats pour décider du nombre d'instances dont nous avons besoin pour gérer notre trafic de production. Enfin, nous simulons le trafic au niveau de la production et configurons des tests de charge pour un point de terminaison SageMaker en temps réel afin de confirmer que notre point de terminaison peut gérer la charge au niveau de la production. L'ensemble complet de code pour l'exemple est disponible dans ce qui suit GitHub référentiel.

Présentation de la solution

Pour ce poste, nous déployons un pré-formé Modèle DistilBERT câlinant le visage du Visage câlin. Ce modèle peut effectuer un certain nombre de tâches, mais nous envoyons une charge utile spécifiquement pour l'analyse des sentiments et la classification des textes. Avec cet exemple de charge utile, nous nous efforçons d'atteindre 1000 TPS.

Déployer un point de terminaison en temps réel

Cet article suppose que vous savez comment déployer un modèle. Faire référence à Créez votre endpoint et déployez votre modèle pour comprendre les éléments internes derrière l'hébergement d'un point de terminaison. Pour l'instant, nous pouvons pointer rapidement vers ce modèle dans le Hugging Face Hub et déployer un point de terminaison en temps réel avec l'extrait de code suivant :

# Hub Model configuration. https://huggingface.co/models
hub = { 'HF_MODEL_ID':'distilbert-base-uncased', 'HF_TASK':'text-classification'
} # create Hugging Face Model Class
huggingface_model = HuggingFaceModel(
transformers_version='4.17.0',
pytorch_version='1.10.2',
py_version='py38',
env=hub,
role=role,
) # deploy model to SageMaker Inference
predictor = huggingface_model.deploy(
initial_instance_count=1, # number of instances
instance_type='ml.m5.12xlarge' # ec2 instance type
)

Testons rapidement notre point de terminaison avec l'exemple de charge utile que nous souhaitons utiliser pour les tests de charge :


import boto3
import json
client = boto3.client('sagemaker-runtime')
content_type = "application/json"
request_body = {'inputs': "I am super happy right now."}
data = json.loads(json.dumps(request_body))
payload = json.dumps(data)
response = client.invoke_endpoint(
EndpointName=predictor.endpoint_name,
ContentType=content_type,
Body=payload)
result = response['Body'].read()
result

Notez que nous sauvegardons le point de terminaison à l'aide d'un seul Cloud de calcul élastique Amazon (Amazon EC2) instance de type ml.m5.12xlarge, qui contient 48 vCPU et 192 Gio de mémoire. Le nombre de vCPU est une bonne indication de la simultanéité que l'instance peut gérer. En général, il est recommandé de tester différents types d'instances pour s'assurer que nous avons une instance dont les ressources sont correctement utilisées. Pour voir une liste complète des instances SageMaker et leur puissance de calcul correspondante pour l'inférence en temps réel, reportez-vous à Tarification d'Amazon SageMaker.

Métriques à suivre

Avant de pouvoir passer aux tests de charge, il est essentiel de comprendre les mesures à suivre pour comprendre la répartition des performances de votre point de terminaison SageMaker. CloudWatch est le principal outil de journalisation utilisé par SageMaker pour vous aider à comprendre les différentes métriques qui décrivent les performances de votre point de terminaison. Vous pouvez utiliser les journaux CloudWatch pour déboguer vos appels de point de terminaison ; toutes les instructions de journalisation et d'impression que vous avez dans votre code d'inférence sont capturées ici. Pour plus d'informations, reportez-vous à Fonctionnement d'Amazon CloudWatch.

Il existe deux types de métriques différents que CloudWatch couvre pour SageMaker : les métriques au niveau de l'instance et les métriques d'appel.

Métriques au niveau de l'instance

Le premier ensemble de paramètres à prendre en compte concerne les métriques au niveau de l'instance : CPUUtilization ainsi que MemoryUtilization (pour les instances basées sur GPU, GPUUtilization). Pour L CPUUtilization, vous pouvez voir des pourcentages supérieurs à 100 % au début dans CloudWatch. Il est important de réaliser pour CPUUtilization, la somme de tous les cœurs de processeur est affichée. Par exemple, si l'instance derrière votre point de terminaison contient 4 vCPU, cela signifie que la plage d'utilisation peut atteindre 400 %. MemoryUtilization, en revanche, est compris entre 0 et 100 %.

Concrètement, vous pouvez utiliser CPUUtilization pour mieux comprendre si vous disposez d'une quantité suffisante ou même excessive de matériel. Si vous avez une instance sous-utilisée (moins de 30 %), vous pouvez potentiellement réduire votre type d'instance. À l'inverse, si votre utilisation est d'environ 80 à 90 %, il serait avantageux de choisir une instance avec une plus grande capacité de calcul/mémoire. D'après nos tests, nous suggérons une utilisation d'environ 60 à 70 % de votre matériel.

Métriques d'appel

Comme son nom l'indique, les métriques d'invocation permettent de suivre la latence de bout en bout de toute invocation à votre point de terminaison. Vous pouvez utiliser les métriques d'appel pour capturer le nombre d'erreurs et le type d'erreurs (5xx, 4xx, etc.) que votre point de terminaison peut rencontrer. Plus important encore, vous pouvez comprendre la répartition de la latence de vos appels aux terminaux. Une grande partie de cela peut être capturée avec ModelLatency ainsi que OverheadLatency métriques, comme illustré dans le schéma suivant.

Latences

La ModelLatency La métrique capture le temps que prend l'inférence dans le conteneur de modèle derrière un point de terminaison SageMaker. Notez que le conteneur de modèle inclut également tout code ou script d'inférence personnalisé que vous avez transmis pour l'inférence. Cette unité est capturée en microsecondes en tant que métrique d'appel, et généralement vous pouvez représenter graphiquement un centile sur CloudWatch (p99, p90, etc.) pour voir si vous atteignez votre latence cible. Notez que plusieurs facteurs peuvent avoir un impact sur la latence du modèle et du conteneur, tels que les suivants :

  • Script d'inférence personnalisé – Que vous ayez implémenté votre propre conteneur ou utilisé un conteneur basé sur SageMaker avec des gestionnaires d'inférence personnalisés, il est recommandé de profiler votre script pour intercepter toutes les opérations qui ajoutent spécifiquement beaucoup de temps à votre latence.
  • Protocole de communication – Considérez les connexions REST par rapport aux connexions gRPC au serveur de modèle dans le conteneur de modèle.
  • Optimisations du cadre du modèle – Ceci est spécifique au cadre, par exemple avec TensorFlow, il existe un certain nombre de variables d'environnement que vous pouvez régler et qui sont spécifiques à TF Serving. Assurez-vous de vérifier quel conteneur vous utilisez et s'il existe des optimisations spécifiques au framework que vous pouvez ajouter dans le script ou en tant que variables d'environnement à injecter dans le conteneur.

OverheadLatency est mesuré à partir du moment où SageMaker reçoit la demande jusqu'à ce qu'il renvoie une réponse au client, moins la latence du modèle. Cette partie est en grande partie hors de votre contrôle et relève du temps pris par les frais généraux de SageMaker.

La latence de bout en bout dans son ensemble dépend de divers facteurs et n'est pas nécessairement la somme de ModelLatency plus OverheadLatency. Par exemple, si votre client fait le InvokeEndpoint Appel API sur Internet, du point de vue du client, la latence de bout en bout serait Internet + ModelLatency + OverheadLatency. En tant que tel, lors du test de charge de votre point de terminaison afin de comparer avec précision le point de terminaison lui-même, il est recommandé de se concentrer sur les métriques du point de terminaison (ModelLatency, OverheadLatencyet InvocationsPerInstance) pour évaluer avec précision le point de terminaison SageMaker. Tous les problèmes liés à la latence de bout en bout peuvent ensuite être isolés séparément.

Quelques questions à prendre en compte pour la latence de bout en bout :

  • Où est le client qui appelle votre point de terminaison ?
  • Existe-t-il des couches intermédiaires entre votre client et l'environnement d'exécution SageMaker ?

Mise à l'échelle automatique

Nous ne couvrons pas spécifiquement la mise à l'échelle automatique dans cet article, mais il s'agit d'une considération importante afin de provisionner le nombre correct d'instances en fonction de la charge de travail. En fonction de vos modèles de trafic, vous pouvez joindre un stratégie de mise à l'échelle automatique à votre point de terminaison SageMaker. Il existe différentes options de mise à l'échelle, telles que TargetTrackingScaling, SimpleScalinget StepScaling. Cela permet à votre point de terminaison d'évoluer automatiquement en fonction de votre modèle de trafic.

Une option courante est le suivi des cibles, où vous pouvez spécifier une métrique CloudWatch ou une métrique personnalisée que vous avez définie et évoluer en fonction de celle-ci. Une utilisation fréquente de la mise à l'échelle automatique consiste à suivre InvocationsPerInstance métrique. Une fois que vous avez identifié un goulot d'étranglement à un certain TPS, vous pouvez souvent l'utiliser comme métrique pour évoluer vers un plus grand nombre d'instances afin de pouvoir gérer les pics de trafic. Pour obtenir une ventilation plus détaillée des points de terminaison SageMaker à mise à l'échelle automatique, reportez-vous à Configuration des points de terminaison d'inférence d'autoscaling dans Amazon SageMaker.

Test de charge

Bien que nous utilisions Locust pour montrer comment nous pouvons charger des tests à grande échelle, si vous essayez de dimensionner correctement l'instance derrière votre point de terminaison, Recommandateur d'inférence SageMaker est une option plus efficace. Avec des outils de test de charge tiers, vous devez déployer manuellement des points de terminaison sur différentes instances. Avec Inference Recommender, vous pouvez simplement passer un tableau des types d'instances que vous souhaitez tester, et SageMaker démarrera emplois pour chacune de ces instances.

Criquet

Pour cet exemple, nous utilisons Criquet, un outil de test de charge open source que vous pouvez implémenter à l'aide de Python. Locust est similaire à de nombreux autres outils de test de charge open source, mais présente quelques avantages spécifiques :

  • Facile à installer – Comme nous le démontrons dans cet article, nous allons passer un script Python simple qui peut facilement être refactorisé pour votre point de terminaison et votre charge utile spécifiques.
  • Distribué et évolutif – Locust est basé sur les événements et utilise événement sous la capuche. Ceci est très utile pour tester des charges de travail hautement simultanées et simuler des milliers d'utilisateurs simultanés. Vous pouvez obtenir un TPS élevé avec un seul processus exécutant Locust, mais il a également un génération de charge distribuée fonctionnalité qui vous permet d'évoluer vers plusieurs processus et machines clientes, comme nous l'explorerons dans cet article.
  • Métriques et interface utilisateur de Locust – Locust capture également la latence de bout en bout en tant que métrique. Cela peut aider à compléter vos métriques CloudWatch pour brosser un tableau complet de vos tests. Tout cela est capturé dans l'interface utilisateur Locust, où vous pouvez suivre les utilisateurs simultanés, les travailleurs, etc.

Pour mieux comprendre Locust, consultez leur Documentation.

Configuration d'Amazon EC2

Vous pouvez configurer Locust dans n'importe quel environnement compatible avec vous. Pour cet article, nous avons configuré une instance EC2 et y avons installé Locust pour effectuer nos tests. Nous utilisons une instance EC5.18 c2xlarge. La puissance de calcul côté client est également à prendre en compte. Parfois, lorsque vous manquez de puissance de calcul côté client, cela n'est souvent pas capturé et est confondu avec une erreur de point de terminaison SageMaker. Il est important de placer votre client dans un emplacement doté d'une puissance de calcul suffisante pour gérer la charge sur laquelle vous testez. Pour notre instance EC2, nous utilisons une AMI Ubuntu Deep Learning, mais vous pouvez utiliser n'importe quelle AMI tant que vous pouvez configurer correctement Locust sur la machine. Pour comprendre comment lancer et se connecter à votre instance EC2, reportez-vous au tutoriel Démarrez avec les instances Amazon EC2 Linux.

L'interface utilisateur Locust est accessible via le port 8089. Nous pouvons l'ouvrir en ajustant nos règles de groupe de sécurité entrantes pour l'instance EC2. Nous ouvrons également le port 22 afin de pouvoir accéder en SSH à l'instance EC2. Envisagez de limiter la source à l'adresse IP spécifique à partir de laquelle vous accédez à l'instance EC2.

Groupes de sécurité

Une fois que vous êtes connecté à votre instance EC2, nous configurons un environnement virtuel Python et installons l'API Locust open source via la CLI :

virtualenv venv #venv is the virtual environment name, you can change as you desire
source venv/bin/activate #activate virtual environment
pip install locust

Nous sommes maintenant prêts à travailler avec Locust pour tester la charge de notre point de terminaison.

Essais acridiens

Tous les tests de charge acridienne sont effectués sur la base d'un Fichier acridien que vous fournissez. Ce fichier Locust définit une tâche pour le test de charge ; c'est ici que nous définissons notre Boto3 appel d'API d'invoke_endpoint. Voir le code suivant:

config = Config(
retries = { 'max_attempts': 0, 'mode': 'standard'
}
) self.sagemaker_client = boto3.client('sagemaker-runtime',config=config)
self.endpoint_name = host.split('/')[-1]
self.region = region
self.content_type = content_type
self.payload = payload

Dans le code précédent, ajustez vos paramètres d'appel de point de terminaison d'appel en fonction de votre appel de modèle spécifique. Nous utilisons le InvokeEndpoint API utilisant le morceau de code suivant dans le fichier Locust ; c'est notre point d'exécution de test de charge. Le fichier Locust que nous utilisons est locust_script.py.

def send(self): request_meta = { "request_type": "InvokeEndpoint", "name": "SageMaker", "start_time": time.time(), "response_length": 0, "response": None, "context": {}, "exception": None,
}
start_perf_counter = time.perf_counter() try:
response = self.sagemaker_client.invoke_endpoint(
EndpointName=self.endpoint_name,
Body=self.payload,
ContentType=self.content_type
)
response_body = response["Body"].read()

Maintenant que notre script Locust est prêt, nous souhaitons exécuter des tests Locust distribués pour tester notre instance unique afin de déterminer la quantité de trafic que notre instance peut gérer.

Le mode distribué Locust est un peu plus nuancé qu'un test Locust à processus unique. En mode distribué, nous avons un nœud de calcul principal et plusieurs nœuds de calcul. Le travailleur principal indique aux travailleurs comment générer et contrôler les utilisateurs simultanés qui envoient une demande. Dans notre distribué.sh script, nous voyons par défaut que 240 utilisateurs seront répartis sur les 60 travailleurs. Notez que le --headless flag dans la CLI Locust supprime la fonctionnalité d'interface utilisateur de Locust.

#replace with your endpoint name in format https://<<endpoint-name>>
export ENDPOINT_NAME=https://$1 export REGION=us-east-1
export CONTENT_TYPE=application/json
export PAYLOAD='{"inputs": "I am super happy right now."}'
export USERS=240
export WORKERS=60
export RUN_TIME=1m
export LOCUST_UI=false # Use Locust UI .
.
. locust -f $SCRIPT -H $ENDPOINT_NAME --master --expect-workers $WORKERS -u $USERS -t $RUN_TIME --csv results &
.
.
. for (( c=1; c<=$WORKERS; c++ ))
do
locust -f $SCRIPT -H $ENDPOINT_NAME --worker --master-host=localhost &
done

./distributed.sh huggingface-pytorch-inference-2022-10-04-02-46-44-677 #to execute Distributed Locust test

Nous exécutons d'abord le test distribué sur une seule instance qui sauvegarde le point de terminaison. L'idée ici est que nous voulons maximiser pleinement une seule instance pour comprendre le nombre d'instances dont nous avons besoin pour atteindre notre objectif TPS tout en respectant nos exigences de latence. Notez que si vous souhaitez accéder à l'interface utilisateur, modifiez le Locust_UI variable d'environnement sur True et prenez l'adresse IP publique de votre instance EC2 et mappez le port 8089 à l'URL.

La capture d'écran suivante montre nos métriques CloudWatch.

Métriques CloudWatch

Finalement, nous remarquons que bien que nous ayons initialement atteint un TPS de 200, nous commençons à remarquer des erreurs 5xx dans nos journaux côté client EC2, comme illustré dans la capture d'écran suivante.

Nous pouvons également le vérifier en examinant nos métriques au niveau de l'instance, en particulier CPUUtilization.

Métriques CloudWatchIci on remarque CPUUtilization à près de 4,800 5.12 %. Notre instance ml.m48x.large a 48 vCPU (100 * 4800 = 5~). Cela sature toute l'instance, ce qui aide également à expliquer nos erreurs XNUMXxx. Nous constatons également une augmentation de ModelLatency.

Il semble que notre instance unique soit renversée et n'ait pas le calcul pour supporter une charge au-delà des 200 TPS que nous observons. Notre cible TPS est de 1000, alors essayons d'augmenter notre nombre d'instances à 5. Cela pourrait être encore plus dans un environnement de production, car nous observions des erreurs à 200 TPS après un certain point.

Paramètres du point de terminaison

Nous voyons à la fois dans les journaux de l'interface utilisateur Locust et de CloudWatch que nous avons un TPS de près de 1000 avec cinq instances soutenant le point de terminaison.

Criquet

Métriques CloudWatchSi vous commencez à rencontrer des erreurs même avec cette configuration matérielle, assurez-vous de surveiller CPUUtilization pour comprendre l'image complète derrière votre hébergement de terminaux. Il est essentiel de comprendre l'utilisation de votre matériel pour voir si vous avez besoin d'augmenter ou même de réduire. Parfois, des problèmes au niveau du conteneur entraînent des erreurs 5xx, mais si CPUUtilization est faible, cela indique que ce n'est pas votre matériel mais quelque chose au niveau du conteneur ou du modèle qui pourrait être à l'origine de ces problèmes (variable d'environnement appropriée pour le nombre de travailleurs non défini, par exemple). D'un autre côté, si vous remarquez que votre instance est complètement saturée, c'est un signe que vous devez soit augmenter le parc d'instances actuel, soit essayer une instance plus grande avec un parc plus petit.

Bien que nous ayons augmenté le nombre d'instances à 5 pour gérer 100 TPS, nous pouvons voir que le ModelLatency la métrique est encore élevée. Cela est dû au fait que les instances sont saturées. En général, nous suggérons de viser à utiliser les ressources de l'instance entre 60 et 70 %.

Nettoyer

Après le test de charge, assurez-vous de nettoyer toutes les ressources que vous n'utiliserez pas via la console SageMaker ou via le delete_endpoint Appel de l'API Boto3. De plus, assurez-vous d'arrêter votre instance EC2 ou toute autre configuration client que vous avez pour ne pas encourir de frais supplémentaires là-bas également.

Résumé

Dans cet article, nous avons décrit comment vous pouvez tester en charge votre point de terminaison en temps réel SageMaker. Nous avons également discuté des mesures que vous devriez évaluer lors du test de charge de votre point de terminaison pour comprendre la répartition de vos performances. Assurez-vous de vérifier Recommandateur d'inférence SageMaker pour mieux comprendre le dimensionnement des instances et les techniques d'optimisation des performances.


À propos des auteurs

Marc Karpe est un architecte ML au sein de l'équipe SageMaker Service. Il se concentre sur l'aide aux clients pour la conception, le déploiement et la gestion des charges de travail ML à grande échelle. Dans ses temps libres, il aime voyager et explorer de nouveaux endroits.

Ram Végiraju est un architecte ML au sein de l'équipe SageMaker Service. Il se concentre sur l'aide aux clients pour créer et optimiser leurs solutions d'IA/ML sur Amazon SageMaker. Dans ses temps libres, il aime voyager et écrire.

Horodatage:

Plus de Apprentissage automatique AWS