Améliorez les performances des modèles Falcon avec Amazon SageMaker | Services Web Amazon

Améliorez les performances des modèles Falcon avec Amazon SageMaker | Services Web Amazon

Quel est le cadre et la configuration optimaux pour héberger des modèles de langage étendus (LLM) pour les applications d'IA générative génératrices de texte ? Malgré l'abondance d'options pour servir les LLM, il est difficile de répondre à cette question en raison de la taille des modèles, des différentes architectures de modèles, des exigences de performances des applications, etc. Le Amazon Sage Maker Conteneur d'inférence de grand modèle (LMI) facilite le service des LLM en réunissant une multitude de cadres et de techniques différents qui optimisent le déploiement des LLM. Le conteneur LMI dispose d'une puissante pile de services appelée Service DJL qui est indépendant du LLM sous-jacent. Il fournit des paramètres de configuration au niveau du système qui peuvent être ajustés pour extraire les meilleures performances de l'infrastructure d'hébergement pour un LLM donné. Il prend également en charge des optimisations récentes telles que le traitement par lots continu, également appelé traitement par lots itératif ou traitement par lots roulant, qui offre des améliorations significatives du débit.

Plus tôt poster, nous avons montré comment utiliser le conteneur LMI pour déployer la famille de modèles Falcon sur SageMaker. Dans cet article, nous montrons comment améliorer le débit et la latence du service Falcon-40B avec des techniques telles que le traitement par lots continu. Nous fournissons également une compréhension intuitive des paramètres de configuration fournis par le conteneur SageMaker LMI qui peut vous aider à trouver la meilleure configuration pour votre application réelle.

Fondamentaux de l'inférence générative de texte pour les LLM

Examinons d'abord quelques principes fondamentaux sur la manière d'effectuer une inférence pour les LLM pour la génération de texte.

Passage direct, activations et cache KV

Étant donné une séquence d'entrée de jetons, ils sont exécutés dans un forward pass à travers toutes les couches du LLM (comme Falcon) pour générer le jeton suivant. UN forward pass fait référence au processus de transmission des données d'entrée via un réseau neuronal pour produire une sortie. Dans le cas de la génération de texte, la transmission directe consiste à introduire une graine ou un contexte initial dans le modèle de langage et à générer le caractère ou le jeton suivant dans la séquence. Pour générer une séquence de texte, le processus est souvent effectué de manière itérative, ce qui signifie qu'il est répété pour chaque étape ou position de la séquence de sortie. À chaque itération, le modèle génère le caractère ou le jeton suivant, qui fait partie du texte généré, et ce processus se poursuit jusqu'à ce que la longueur de texte souhaitée soit générée.

La génération de texte avec des modèles de langage comme Falcon ou GPT est autoregressive. Cela signifie que le modèle génère un jeton à la fois tout en conditionnant les jetons générés précédemment. En d’autres termes, à chaque itération, le modèle prend le texte généré précédemment comme entrée et prédit le prochain jeton en fonction de ce contexte. Comme mentionné dans vLLM : service LLM facile, rapide et bon marché avec PagedAttention, dans ce processus de décodage autorégressif, tous les jetons d'entrée du LLM produisent leurs tenseurs de clé d'attention et de valeur, et ces tenseurs sont conservés dans la mémoire GPU pour générer les jetons suivants. Ces tenseurs de clé et de valeur mis en cache sont souvent appelés les KV cache.

Phases de pré-remplissage et de décodage

Dans un processus de décodage autorégressif, comme celui utilisé dans la génération de texte avec des modèles de langage tels que Falcon, il y a généralement deux phases principales : prefill phase et le decode phase. Ces phases sont cruciales pour générer un texte cohérent et contextuellement pertinent.

La phase de pré-remplissage comprend les éléments suivants :

  • Contexte initial – La phase de pré-remplissage commence par un contexte initial ou un texte de départ fourni par l'utilisateur. Ce contexte initial peut être une phrase, une expression ou même juste un seul mot. Il définit le point de départ de la génération de texte et fournit un contexte pour la suite.
  • Conditionnement du modèle – Le contexte fourni est utilisé pour conditionner le modèle de langage. Le modèle prend ce contexte en entrée et génère le jeton suivant (mot ou caractère) dans la séquence en fonction de sa compréhension du contexte.
  • Génération de jetons – Le modèle génère un jeton à la fois, prédisant ce qui devrait suivre dans le texte. Ce jeton est ajouté au contexte, l’étendant ainsi.
  • Processus itératif – Le processus de génération de jetons est répété de manière itérative. A chaque étape, le modèle génère un jeton en considérant le contexte mis à jour, qui inclut désormais les jetons générés lors des étapes précédentes.

La phase de pré-remplissage se poursuit jusqu'à ce qu'une condition d'arrêt prédéterminée soit remplie. Cette condition peut être une longueur maximale du texte généré, un jeton spécifique qui signale la fin du texte, ou tout autre critère défini par l'utilisateur ou l'application.

La phase de décodage comprend les éléments suivants :

  • Aboutissement – Après la phase de pré-remplissage, vous disposez d'un texte partiellement généré qui peut être incomplet ou coupé à un moment donné. La phase de décodage est chargée de compléter le texte pour le rendre cohérent et grammaticalement correct.
  • Suite du dernier jeton – Dans la phase de décodage, le modèle démarre à partir du dernier jeton généré lors de la phase de pré-remplissage. Il utilise ce jeton comme contexte initial et génère le jeton suivant pour continuer le texte.
  • Achèvement itératif – Comme lors de la phase de pré-remplissage, le processus de génération de jetons est à nouveau itératif. Le modèle génère un jeton à la fois, en fonction des jetons précédents dans la séquence.
  • Condition d'arrêt – La phase de décodage comporte également une condition d'arrêt, qui peut être la même que celle de la phase de pré-remplissage, comme l'atteinte d'une longueur maximale ou la rencontre d'un jeton de fin de texte. Lorsque cette condition est remplie, le processus de génération s'arrête.

La combinaison des phases de pré-remplissage et de décodage permet aux modèles autorégressifs de générer un texte qui s'appuie sur un contexte initial et produit des séquences de texte cohérentes, contextuellement pertinentes et contextuellement cohérentes.

Reportez-vous à Un système de service distribué pour les modèles génératifs basés sur des transformateurs pour une explication détaillée du processus.

Optimisation du débit grâce au traitement par lots dynamique

Jusqu'à présent, nous n'avons parlé que d'une seule entrée. En pratique, nous nous attendons à traiter plusieurs requêtes provenant de manière aléatoire des clients de l'application pour une inférence simultanément ou de manière échelonnée. De manière traditionnelle, le traitement par lots de base peut être utilisé pour augmenter le débit et l'utilisation des ressources informatiques du GPU. Le traitement par lots consiste à combiner efficacement les représentations numériques de plusieurs requêtes dans un lot et à effectuer des exécutions parallèles des passes avant autorégressives. Ce dosage intelligent se fait côté service. Le serveur DJLServing de SageMaker LMI peut être configuré pour regrouper plusieurs requêtes afin de les traiter en parallèle en définissant les paramètres suivants dans servant.propriétés:

  • max_batch_delay = 100 – Le délai maximum pour l'agrégation par lots en millisecondes. La valeur par défaut est de 100 millisecondes.
  • taille du lot = 32 – La taille du lot dynamique. La valeur par défaut est 1.

Cela montre essentiellement que DJLServing mettra en file d'attente les requêtes pendant 100 millisecondes à la fois ou si le nombre de requêtes mises en file d'attente atteint la taille de lot spécifiée, le lot sera programmé pour être exécuté vers le backend pour inférence. C'est ce qu'on appelle dynamic batching. C'est dynamique car la taille du lot peut changer d'un lot à l'autre en fonction du nombre de demandes ajoutées au cours de cette période. Cependant, comme les requêtes peuvent avoir des caractéristiques différentes (par exemple, certaines requêtes peuvent avoir la forme de 20 jetons d'entrée et 500 jetons de sortie, alors que d'autres peuvent être inversées, avec 500 jetons d'entrée mais seulement 20 pour la sortie), certaines requêtes peuvent terminer le traitement plus rapidement que les autres dans le même lot. Cela pourrait entraîner une sous-utilisation du GPU en attendant que toutes les requêtes en cours du lot terminent leur étape de décodage, même s'il y a des requêtes supplémentaires en attente de traitement dans la file d'attente. Le diagramme suivant illustre ce processus.

Visuel de mise en lots dynamique simple

Visuel de traitement par lot dynamique – remarquez les fenêtres inactives à la fin des requêtes 2 et 3

Optimisation du débit grâce au traitement par lots continu

Avec continuous batching, aussi connu sous le nom iterative or rolling par lots, nous profitons des différences entre les étapes de pré-remplissage et de décodage. Pour activer le traitement par lots continu, DJServing fournit les configurations supplémentaires suivantes selon serveing.properties :

  • moteur=MPI – Nous vous encourageons à utiliser le moteur MPI pour le traitement par lots continu.
  • option.rolling_batch=auto ou lmi-dist – Nous vous recommandons d'utiliser auto car il sélectionnera automatiquement l'algorithme de lot glissant le plus approprié ainsi que d'autres optimisations à l'avenir.
  • option.max_rolling_batch_size=32 – Cela limite le nombre de requêtes simultanées. La valeur par défaut est 32.

Avec le traitement par lots continu, la pile de diffusion (DJLServing) n'attend pas toutes les requêtes en cours d'un lot pour terminer son étape de décodage. Au lieu de cela, lors des interruptions logiques (à la fin d'une itération dans l'étape de décodage), il récupère des requêtes supplémentaires qui sont en attente dans la file d'attente pendant que le lot actuel est toujours en cours de traitement (d'où le nom lot roulant). Il effectue cette vérification des demandes en attente à la fin de chaque itération de l'étape de décodage. N'oubliez pas que pour chaque requête, nous devons exécuter l'étape de pré-remplissage suivie de l'étape de décodage séquentiel. Étant donné que nous pouvons traiter tous les jetons de l'invite initiale d'une requête en parallèle pour son étape de pré-remplissage, chaque fois qu'une nouvelle requête est extraite, nous suspendons temporairement l'étape de décodage des requêtes en cours du lot - nous sauvegardons temporairement son cache KV. et activations en mémoire et exécutez l'étape de pré-remplissage des nouvelles requêtes.

La taille de ce cache peut être configurée avec l'option suivante :

Une fois le pré-remplissage terminé, nous combinons les nouvelles requêtes et les anciennes requêtes en pause dans un nouveau lot roulant, qui peut procéder à leur étape de décodage en parallèle. Notez que les anciennes requêtes mises en pause peuvent continuer leur étape de décodage là où elles s'étaient arrêtées et les nouvelles requêtes démarreront à partir de leur premier nouveau jeton.

Visuel de mise en lots continue ou itérative

Visuel de traitement par lots continu ou itératif : notez que les temps d'inactivité sont remplacés par des demandes de suivi

Vous avez peut-être déjà réalisé que le batching continu est une approche presque similaire avec laquelle nous parallélisons naturellement les tâches de notre vie quotidienne. Nous recevons des messages, des e-mails, des notifications téléphoniques (potentiellement de nouvelles demandes) arrivant à des moments aléatoires (analogue à plusieurs demandes arrivant de manière aléatoire et échelonnée pour les GPU). Tout cela se produit pendant que nous accomplissons nos tâches en vol : rédiger des e-mails, coder, participer à des réunions (analogues aux tâches de traitement actuelles dans les GPU). Lors des pauses logiques, nous mettons nos tâches en vol en pause et vérifions nos notifications pour décider si une action est requise de notre part, et si c'est le cas, nous l'ajoutons à nos tâches en vol (lot roulant réel), ou mettez-le sur une liste de choses à faire (la file d'attente).

Rassembler tout cela : comment réfléchir à l'utilisation de la mémoire des GPU

Il est recommandé de tester en charge votre modèle pour déterminer quelle configuration est la plus rentable pour votre cas d'utilisation professionnel. Pour mieux comprendre, visualisons l'empreinte mémoire des GPU au fur et à mesure que le modèle est chargé et que les requêtes successives sont traitées dans un lot glissant. Pour cet article, supposons que nous chargions le modèle Falcon-40B sur l'une des instances de type G5 installées avec des GPU NVIDIA A10G, chacun avec 24 Go de mémoire. Notez qu'une compréhension similaire s'applique aux types d'instances p3, p4 et p5, fournis avec les séries de GPU V100, A100 et H100.

Voici un aperçu de la manière d'obtenir une valeur approximative de la mémoire totale requise pour servir le Falcon-40B :

  • Taille du modèle = Nombre de paramètres du modèle (40 milliards pour Falcon-40B) x 4 octets par paramètre (pour FP32) = 160 Go
  • Mémoire totale approximative requise pour charger le Falcon-40B à des fins d'inférence = Taille du modèle (= 160 Go) + Cache KV (Attention Cache) (=* 20 Go) + Surcharge de mémoire supplémentaire par ML Frameworks (environ 2 Go)
MémoireVisuel

Memory Visual – Comprendre l'empreinte mémoire d'un modèle Falcon-40B chargé

Pour Falcon-40B, si nous compressons le modèle en quantifiant le modèle au type de données bfloat16 (2 octets), la taille du modèle devient d'environ 80 Go. Comme vous pouvez le voir, cela reste plus grand que la mémoire prise en charge par un périphérique accélérateur, nous devons donc adopter une technique de partitionnement de modèle (sharding) avec un paramètre spécial. parallélisme tenseur (TP) et partagez le modèle sur plusieurs dispositifs accélérateurs. Supposons que nous ayons choisi g5.24xlarge, qui dispose de 4 périphériques GPU A10G. Si nous configurons DJLServing (serving.properties) avec ce qui suit, nous pouvons nous attendre à ce que les 80 Go de poids de modèle soient répartis également sur les 4 GPU :

Avec tensor_parallel_degree réglé sur 4, environ 20 Go de la mémoire GPU de 24 Go (environ 84 %) sont déjà utilisés avant même qu'une seule requête ne soit traitée. Les 16 % restants du GPU seront utilisés pour le cache KV pour les requêtes entrantes. Il est possible que pour votre scénario d'entreprise et ses exigences en matière de latence et de débit, 2 à 3 Go de mémoire restante soient plus que suffisants. Sinon, vous pouvez augmenter la taille de l'instance à g5.48xlarge, qui dispose de 8 GPU et utilise tensor_parallel_degree défini sur 8. Dans un tel cas, seulement environ 10 Go de la mémoire disponible de 24 Go de chaque GPU sont utilisés pour les poids de modèle et nous disposer d'environ 60 % du GPU restant pour les activations et le cache KV. Intuitivement, nous pouvons voir que cette configuration peut nous permettre d’avoir un débit plus élevé. De plus, comme nous disposons désormais d’un tampon plus grand, nous pouvons augmenter le max_rolling_batch_prefill_tokens ainsi que max_rolling_batch_size paramètres pour optimiser davantage le débit. Ensemble, ces deux paramètres contrôleront les préallocations des pré-remplissages d'activation et du cache KV pour le modèle. Un nombre plus grand pour ces deux paramètres sera corrélé à un débit plus important, en supposant que vous disposiez de suffisamment de tampon pour le cache KV dans la mémoire GPU.

Traitement par lots continu avec PagedAttention

PagedAttention est un nouvel algorithme d'optimisation développé par l'UC Berkeley qui améliore le processus de traitement par lots continu en permettant au cache d'attention (cache KV) d'être non contigu en allouant de la mémoire en pages ou blocs de taille fixe. Ceci s'inspire des concepts de mémoire virtuelle et de pagination utilisés par les systèmes d'exploitation.

Selon le vLLM papier, le cache d'attention de chaque séquence de jetons est partitionné en blocs et mappé aux blocs physiques via une table de blocs. Pendant le calcul de l'attention, un noyau PagedAttention peut utiliser la table des blocs pour récupérer efficacement les blocs de la mémoire physique. Cela entraîne une réduction significative du gaspillage de mémoire et permet une taille de lot plus grande, une utilisation accrue du GPU et un débit plus élevé.

Comparaison des performances

Pour garantir des tests de charge efficaces de votre configuration de déploiement, il est recommandé de commencer par considérer le scénario commercial et de définir clairement les caractéristiques de l'entrée et de la sortie de l'application basée sur LLM. Par exemple, si vous travaillez sur un cas d'utilisation de résumé de centre d'appels, l'entrée peut consister en un texte plus volumineux, comme une transcription de conversation de 500 jetons entre un agent du service client et un client, mais la sortie peut être relativement plus petite, environ 100 jetons. jetons, représentant un résumé de la transcription. D'un autre côté, si vous travaillez sur un scénario de génération de code, l'entrée peut être aussi courte que 15 jetons, comme « écrire une implémentation efficace en Python pour décrire toutes les ressources EC2, y compris la pagination », mais le résultat pourrait être beaucoup plus long. plus grand, atteignant 500 jetons. Il est également important de déterminer si l'obtention d'une latence plus faible ou l'optimisation du débit est la priorité absolue pour votre scénario spécifique.

Après avoir acquis une compréhension complète du scénario commercial, vous pouvez analyser et déterminer la configuration optimale pour votre environnement d'hébergement. Dans ce contexte, l'environnement d'hébergement englobe divers éléments clés, notamment le type d'instance et d'autres paramètres de configuration tels que tensor_parallel_degree, max_rolling_batch_size, max_rolling_batch_prefill_tokens, et plus. Notre objectif est d'identifier la configuration la plus efficace pour répondre à nos exigences en matière de temps de réponse, de débit et de qualité de sortie du modèle.

Dans notre analyse, nous avons comparé les performances pour illustrer les avantages du traitement par lots continu par rapport au traitement par lots dynamique traditionnel. Nous avons utilisé les configurations détaillées dans le tableau suivant dans serve.properties pour le traitement par lots dynamique et le traitement par lots itératif, à l'aide d'un conteneur LMI sur SageMaker.

Traitement par lots dynamique Dosage continu Traitement par lots continu avec PagedAttention

moteur = Python

option.model_id=tiiuae/falcon-40b

option.tensor_parallel_degree=8

option.dtype=fp16

batch_size = 4

max_batch_delay = 100

option.trust_remote_code = vrai

moteur = MPI

option.model_id = {{s3_url}}

option.trust_remote_code = vrai

option.tensor_parallel_degree = 8

option.max_rolling_batch_size = 32

option.rolling_batch = automatique

option.dtype = fp16

option.max_rolling_batch_prefill_tokens = 1024

option.paged_attention = Faux

moteur = MPI

option.model_id = {{s3_url}}

option.trust_remote_code = vrai

option.tensor_parallel_degree = 8

option.max_rolling_batch_size = 32

option.rolling_batch = automatique

option.dtype = fp16

option.max_rolling_batch_prefill_tokens = 1024

option.paged_attention = Vrai

Les deux configurations ont été comparées pour le Falcon-40B avec le type de données FP16 déployé sur ml.g5.48xlarge dans plusieurs scénarios différents qui représentent des applications du monde réel :

  • Un petit nombre de jetons d'entrée avec un grand nombre de jetons générés – Dans ce scénario, le nombre de jetons d'entrée a été fixé à 32 et 128 nouveaux jetons ont été générés
Stratégie de traitement par lots Débit (jetons/s) Latence p90 (sec)
Traitement par lots dynamique 5.53 58.34
Dosage continu 56.04 4.74
Traitement par lots continu avec PagedAttention 59.18 4.76
  • Une grande entrée avec un petit nombre de jetons générés – Ici, nous fixons le nombre de jetons d’entrée à 256 et demandons au LLM de résumer l’entrée à 32 jetons
Stratégie de traitement par lots Débit (jetons/s) Latence p90 (sec)
Traitement par lots dynamique 19.96 59.31
Dosage continu 46.69 3.88
Traitement par lots continu avec PagedAttention 44.75 2.67

Nous pouvons constater que le traitement par lots continu avec PagedAttention offre une amélioration du débit 10 fois supérieure dans le scénario 1 et 2.3 fois dans le scénario 2 par rapport à l'utilisation du traitement par lots dynamique sur SageMaker lors de l'utilisation du conteneur LMI.

Conclusion

Dans cet article, nous avons examiné comment les LLM utilisent la mémoire et expliqué comment le traitement par lots continu améliore le débit à l'aide d'un conteneur LMI sur SageMaker. Nous avons démontré les avantages du traitement par lots continu pour le Falcon-40B à l'aide d'un conteneur LMI sur SageMaker en affichant les résultats de référence. Vous pouvez trouver le code sur le GitHub repo.


À propos des auteurs

Abhigyan ShivadityaAbhi Shivaditya est architecte de solutions senior chez AWS, travaillant avec des organisations d'entreprises mondiales stratégiques pour faciliter l'adoption des services AWS dans des domaines tels que l'intelligence artificielle, l'informatique distribuée, la mise en réseau et le stockage. Son expertise réside dans l'apprentissage profond dans les domaines du traitement du langage naturel (TAL) et de la vision par ordinateur. Abhi aide les clients à déployer efficacement des modèles d'apprentissage automatique hautes performances au sein de l'écosystème AWS.

Améliorez les performances des modèles Falcon avec Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Recherche verticale. Aï.Dhawal Patel est architecte principal en apprentissage machine chez AWS. Il a travaillé avec des organisations allant des grandes entreprises aux startups de taille moyenne sur des problèmes liés à l'informatique distribuée et à l'intelligence artificielle. Il se concentre sur l'apprentissage en profondeur, y compris les domaines de la PNL et de la vision par ordinateur. Il aide les clients à obtenir une inférence de modèle haute performance sur SageMaker.

Améliorez les performances des modèles Falcon avec Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Recherche verticale. Aï.Pinak Panigrahi travaille avec les clients pour créer des solutions basées sur l'apprentissage automatique afin de résoudre les problèmes commerciaux stratégiques sur AWS. Lorsqu’il n’est pas occupé par l’apprentissage automatique, on peut le trouver en train de faire une randonnée, de lire un livre ou de regarder du sport.

Améliorez les performances des modèles Falcon avec Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Recherche verticale. Aï.Abhi Sodhani occupe le poste d'architecte senior de solutions IA/ML chez AWS, où il se spécialise dans l'offre d'une expertise technique et de conseils sur les solutions d'IA générative et de ML aux clients. Son objectif principal est d'aider les entreprises numériques natives à réaliser tout le potentiel des technologies d'IA générative et de ML, leur permettant d'atteindre efficacement leurs objectifs commerciaux. Au-delà de ses efforts professionnels, Abhi fait preuve d'une forte passion pour les activités intellectuelles telles que la lecture, ainsi que pour la participation à des activités qui favorisent le bien-être physique et mental, comme le yoga et la méditation.

Améliorez les performances des modèles Falcon avec Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Recherche verticale. Aï.Lan Qing est ingénieur en développement logiciel chez AWS. Il a travaillé sur plusieurs produits stimulants chez Amazon, notamment des solutions d'inférence ML hautes performances et un système de journalisation hautes performances. L'équipe de Qing a lancé avec succès le premier modèle de milliards de paramètres dans Amazon Advertising avec une très faible latence requise. Qing possède une connaissance approfondie de l'optimisation de l'infrastructure et de l'accélération du Deep Learning.

Horodatage:

Plus de Apprentissage automatique AWS