Connect with us

Intelligence artificielle

SGLang: Exécution efficace de programmes de modèles de langage structurés

mm
SGLang: Efficient Execution of Structured Language Model Programs

Les grands modèles de langage (LLM) sont de plus en plus utilisés pour des tâches complexes nécessitant plusieurs appels de génération, des techniques d’incitation avancées, un contrôle de flux et des entrées/sorties structurées. Cependant, les systèmes efficaces pour la programmation et l’exécution de ces applications font défaut. SGLang, un système nouvellement introduit, vise à remédier à cela en fournissant une exécution efficace de programmes de modèles de langage complexes. SGLang se compose d’un langage frontal et d’un runtime. Le frontal simplifie la programmation avec des primitives pour la génération et le contrôle de la parallélisme, tandis que le runtime accélère l’exécution grâce à des optimisations nouvelles comme RadixAttention pour la réutilisation du cache KV et des machines à états finis compressées pour un décodage de sortie structurée plus rapide. Les expériences démontrent que SGLang atteint un débit jusqu’à 6,4 fois supérieur par rapport aux systèmes d’inférence de pointe sur divers grands modèles de langage et multimodaux, traitant des tâches telles que le contrôle d’agent, le raisonnement logique, les benchmarks d’apprentissage à quelques exemples, le décodage JSON, les pipelines de génération augmentée de récupération et le chat à plusieurs tours.

Les progrès récents dans les capacités des LLM ont élargi leur utilité, leur permettant de gérer une gamme plus large de tâches générales et de fonctionner comme des agents autonomes. Dans ces applications, les LLM s’engagent dans une planification multi-ronde, un raisonnement et une interaction avec des environnements externes. Cela est facilité par l’utilisation d’outils, de multiples modalités d’entrée et de diverses techniques d’incitation, telles que l’apprentissage à quelques exemples, l’autocoordination, le squelette de pensée et l’arbre de pensée. Ces nouveaux cas d’utilisation nécessitent plusieurs appels de génération de LLM, souvent dépendants, indiquant une tendance à utiliser des structures multi-appels pour compléter des tâches complexes.

Ce changement marque une transition d’un simple chat à une utilisation plus sophistiquée et programmée des LLM, où les programmes planifient et contrôlent les processus de génération des LLM. Ces programmes sont appelés “Programmes de modèles de langage” (LM Programs). Les techniques d’incitation avancées et les flux de travail d’agent font partie de la portée des programmes LM. Il existe deux propriétés courantes des programmes LM : (1) Les programmes LM impliquent généralement plusieurs appels de LLM entrecoupés de contrôle de flux pour compléter des tâches complexes et améliorer la qualité globale. (2) Les programmes LM reçoivent des entrées structurées et produisent des sorties structurées, permettant la composition de programmes LM et l’intégration dans des systèmes logiciels existants.

Dans cet article, nous allons plonger plus profondément dans le cadre SGLang, en explorant son architecture, en analysant ses performances et en le comparant aux cadres de pointe. Alors, commençons.

Introduction à SGLang

Malgré l’utilisation généralisée des programmes LM, les systèmes actuels pour les exprimer et les exécuter restent inefficaces. SGLang identifie deux défis principaux associés à l’utilisation efficace des programmes LM :

  • Complexité de programmation : Développer des programmes LM est fastidieux et difficile en raison de la nature non déterministe des LLM. Cela implique une manipulation de chaînes de caractères extensive, un réglage expérimental des incitations, un parsing de sortie fragile, la gestion de multiples modalités d’entrée et la mise en œuvre de mécanismes de parallélisme. Cette complexité réduit considérablement la lisibilité même des programmes les plus simples.
  • Inefficacité d’exécution : L’exécution des programmes LM est inefficace en raison de calculs et d’utilisation de mémoire redondants. Les moteurs d’inférence de pointe, optimisés pour réduire la latence et améliorer le débit, manquent de connaissances directes sur la charge de travail, ce qui entraîne des inefficacités importantes. Un exemple notable est la réutilisation du cache KV, qui consiste en des tenseurs intermédiaires réutilisables essentiels pour l’inférence générative. Les systèmes actuels manquent de mécanismes efficaces pour faciliter la réutilisation du cache KV entre plusieurs appels de LLM partageant un préfixe commun, ce qui entraîne des calculs inutiles et une perte de mémoire. De plus, le décodage contraint pour les sorties structurées, telles que le mode JSON, est sous-optimal car les systèmes existants ne décodent qu’un jeton à la fois.

Pour répondre à ces défis, SGLang introduit un langage de génération structuré pour les LLM. L’idée principale est d’exploiter systématiquement la structure multi-appel dans les programmes LM pour une exécution efficace. Comme le montre la figure suivante, SGLang comporte deux parties : un langage frontal et un runtime.

Le frontal simplifie la programmation des programmes LM, et le runtime accélère leur exécution. Ces parties peuvent fonctionner ensemble pour une meilleure performance ou fonctionner de manière indépendante.

SGLang est un langage spécifique à un domaine intégré à Python, fournissant des primitives pour la génération (par exemple, étendre, générer, sélectionner) et le contrôle de la parallélisme (par exemple, fork, join). Il est compatible avec le contrôle de flux et les bibliothèques Python, permettant aux utilisateurs de développer facilement des flux de travail d’incitation avancés avec la syntaxe Python native. SGLang inclut un interpréteur et un compilateur. L’interpréteur gère l’état d’incitation comme un flux et soumet des opérations primitives au flux pour une exécution asynchrone, assurant un contrôle approprié de la synchronisation et du parallélisme intra-programme. De plus, les programmes SGLang peuvent être tracés et compilés pour des optimisations supplémentaires. Le runtime de SGLang propose plusieurs optimisations nouvelles pour accélérer l’exécution des programmes LM :

  • RadixAttention : Cette technique permet la réutilisation automatique du cache KV entre plusieurs appels de génération. Dans les moteurs d’inférence existants, le cache KV d’une demande est supprimé après le traitement, empêchant la réutilisation entre plusieurs appels et ralentissant l’exécution. SGLang maintient un cache LRU du cache KV dans un arbre radix, gérant le cache KV comme un cache traditionnel et utilisant l’arbre radix pour un appariement, une insertion et une expulsion efficaces. Cela permet au runtime de gérer divers modèles de réutilisation de manière efficace.
  • Machine à états finis compressée : Cette technique permet un décodage contraint plus rapide pour les sorties structurées. Les systèmes existants suivent les contraintes uniquement pour le jeton suivant, ce qui les rend capables de décoder un jeton à la fois. Au lieu de cela, SGLang analyse les contraintes et construit une machine à états finis compressée pour les représenter, compressant un chemin multi-jeton en un chemin à une étape chaque fois que possible, permettant le décodage de plusieurs jetons à la fois pour une vitesse plus rapide.
  • Exécution spéculative d’API : Pour les modèles d’API uniquement, tels que OpenAI’s GPT-4, SGLang introduit l’exécution spéculative d’API pour optimiser les programmes multi-appels.

En utilisant SGLang, diverses applications de LLM ont été mises en œuvre, notamment le contrôle d’agent, le raisonnement logique, les benchmarks d’apprentissage à quelques exemples, le décodage JSON, les pipelines de génération augmentée de récupération et le chat à plusieurs tours et le traitement multi-modal. Les performances ont été testées sur des modèles tels que Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (image) et LLaVA-NeXT-34B (vidéo) sur les GPU NVIDIA A10G et A100. Les résultats expérimentaux montrent que SGLang atteint un débit jusqu’à 6,4 fois supérieur sur une large gamme de charges de travail, de modèles et de configurations matérielles, par rapport aux systèmes de programmation et d’inférence existants, notamment Guidance, vLLM et LMQL.

SGLang : Modèle de programmation et méthodologie

Le modèle de programmation SGLang est introduit à travers un exemple en cours d’exécution, décrivant ses primitives de langage et ses modes d’exécution, et détaillant les opportunités d’optimisation du runtime. Ce modèle simplifie les opérations fastidieuses dans les flux de travail multi-appels (par exemple, manipulation de chaînes, appel d’API, spécification de contraintes, parallélisme) en fournissant des primitives flexibles et composables. SGLang est un langage spécifique à un domaine intégré à Python. La figure suivante montre un programme qui évalue un essai sur une image à l’aide de la méthode d’incitation de résolution de branche.

La fonction multi_dimensional_judge prend trois arguments : `s`, `path` et `essay`. s gère l’état d’incitation, path est le chemin du fichier d’image et essay est le texte de l’essai. De nouvelles chaînes et des primitives SGLang peuvent être ajoutées à l’état s pour l’exécution à l’aide de l’opérateur +=. Tout d’abord, la fonction ajoute l’image et l’essai à l’incitation. Elle vérifie ensuite si l’essai est lié à l’image à l’aide de la sélection, en stockant le résultat dans s[“related”]. Si lié, l’incitation est divisée en trois copies pour une évaluation parallèle à partir de différentes dimensions, en utilisant la génération pour stocker les résultats dans f[“judgment”]. Ensuite, elle fusionne les jugements, génère un résumé et attribue une note de lettre. Enfin, elle retourne les résultats au format JSON, suivant un schéma défini par une contrainte d’expression régulière regex. SGLang simplifie considérablement ce programme, car un programme équivalent utilisant une interface similaire à celle d’OpenAI nécessiterait 2,1 fois plus de lignes de code en raison de la manipulation manuelle de chaînes et du contrôle de parallélisme.

SGLang fournit des primitives pour contrôler l’état d’incitation, la génération et le parallélisme, qui peuvent être utilisées avec la syntaxe et les bibliothèques Python. Voici les primitives :

gen: Appelle un modèle pour générer et stocke les résultats dans une variable avec le nom spécifié dans son premier argument. Il prend en charge un argument `regex` pour contraindre la sortie à suivre une grammaire définie par une expression régulière (par exemple, un schéma JSON).

  • select: Appelle un modèle pour choisir l’option la plus probable parmi une liste.
  • += ou extend: Ajoute une chaîne à l’incitation.
  • [variable_name]: Récupère les résultats d’une génération.
  • fork: Crée des fourches parallèles de l’état d’incitation.
  • join: Réunit l’état d’incitation.
  • image et video: Prend en charge les entrées d’image et de vidéo.

La façon la plus simple d’exécuter un programme SGLang est via un interpréteur, où une incitation est traitée comme un flux asynchrone. Des primitives comme extend, gen et select sont soumises au flux pour une exécution asynchrone. Ces appels non bloquants permettent au code Python de continuer à s’exécuter sans attendre la fin de la génération, semblable au lancement de noyaux CUDA de manière asynchrone. Chaque incitation est gérée par un exécuteur de flux dans un thread en arrière-plan, permettant un parallélisme intra-programme. La récupération des résultats de la génération bloquera jusqu’à ce qu’ils soient prêts, assurant une synchronisation correcte. Alternativement, les programmes SGLang peuvent être compilés en graphiques de calcul et exécutés avec un exécuteur de graphique, permettant davantage d’optimisations. Cet article utilise le mode interpréteur par défaut et discute des résultats du mode compilé dans l’appendice D. SGLang prend en charge les modèles à poids ouvert avec son propre runtime SGLang (SRT), ainsi que les modèles d’API tels que OpenAI et les modèles Anthropic.

Les systèmes de programmation pour les LLM peuvent être classés en systèmes de haut niveau (par exemple, LangChain, DSPy) et en systèmes de bas niveau (par exemple, LMQL, Guidance, SGLang). Les systèmes de haut niveau fournissent des incitations prédéfinies ou auto-générées, telles que l’optimiseur d’incitation de DSPy. Les systèmes de bas niveau ne modifient généralement pas les incitations mais permettent la manipulation directe des incitations et des primitives. SGLang est un système de bas niveau similaire à LMQL et Guidance. Le tableau suivant compare leurs fonctionnalités.

SGLang se concentre davantage sur l’efficacité du runtime et est livré avec son propre runtime conçu, permettant des optimisations nouvelles. Les langages de haut niveau (par exemple, DSPy) peuvent être compilés en langages de bas niveau (par exemple, SGLang). L’intégration de SGLang en tant que backend dans DSPy pour une meilleure efficacité du runtime est démontrée plus tard.

L’exemple ci-dessus illustre les opérations RadixAttention avec une politique d’éviction LRU à travers neuf points dans le temps, montrant l’évolution dynamique de l’arbre radix en réponse à diverses demandes. Ces demandes incluent deux sessions de chat, un lot d’interrogations d’apprentissage à quelques exemples et des échantillonnages d’autocoordination. Chaque arête de l’arbre porte une étiquette indiquant une sous-chaîne ou une séquence de jetons. Les nœuds sont codés par couleur pour refléter différents états : vert pour les nœuds nouvellement ajoutés, bleu pour les nœuds mis en cache accédés pendant le point dans le temps et rouge pour les nœuds qui ont été supprimés.

Étape 1 : L’arbre radix est initialement vide.

Étape 2 : Le serveur traite un message d’utilisateur entrant “Bonjour” et répond avec la sortie LLM “Salut”. L’incitation système “Vous êtes un assistant utile”, le message d’utilisateur “Bonjour !” et la réponse LLM “Salut !” sont consolidés dans l’arbre comme une arête unique liée à un nouveau nœud.

Étape 3 : Une nouvelle incitation arrive, et le serveur trouve le préfixe de l’incitation (c’est-à-dire le premier tour de la conversation) dans l’arbre radix et réutilise son cache KV. Le nouveau tour est ajouté à l’arbre comme un nouveau nœud.

Étape 4 : Une nouvelle session de chat commence. Le nœud de l’étape 3 est divisé en deux nœuds pour permettre aux deux sessions de chat de partager l’incitation système.

Étape 5 : La deuxième session de chat se poursuit. Cependant, en raison des limites de mémoire, un nœud de l’étape 4 doit être supprimé. Le nouveau tour est ajouté après le nœud restant de l’étape 4.

Étape 6 : Le serveur reçoit une interrogation d’apprentissage à quelques exemples, la traite et l’insère dans l’arbre. Le nœud racine est divisé car la nouvelle interrogation ne partage aucun préfixe avec les nœuds existants.

Étape 7 : Le serveur reçoit un lot d’interrogations d’apprentissage à quelques exemples supplémentaires. Ces interrogations partagent les mêmes exemples, donc un nœud de l’étape 6 est divisé pour permettre le partage.

Étape 8 : Le serveur reçoit un nouveau message de la première session de chat. Il supprime tous les nœuds de la deuxième session de chat car ils sont les moins récemment utilisés.

Étape 9 : Le serveur reçoit une demande pour échantillonner davantage de réponses aux questions d’un nœud de l’étape 8, probablement pour une incitation d’autocoordination. Pour faire de la place pour ces demandes, plusieurs nœuds sont supprimés.

Cet exemple démontre comment RadixAttention gère l’allocation dynamique et la suppression de nœuds en réponse à différents types de demandes, assurant une réutilisation efficace du cache KV et une gestion de la mémoire.

SGLang : Évaluation et résultats

Résultats sur les modèles à poids ouvert

Les résultats de latence et de débit sont présentés dans les figures suivantes. SGLang améliore le débit de jusqu’à 6,4 fois et réduit la latence de jusqu’à 3,7 fois. Ces améliorations résultent de la réutilisation du cache KV, de l’exploitation du parallélisme au sein d’un seul programme et d’un décodage contraint plus rapide.

Sur ces benchmarks, le taux de cache atteint entre 50 % et 99 %. La figure 13 (appendice) liste les taux de cache atteints et optimaux pour tous, montrant que l’approche de planification sensible au cache de SGLang approche 96 % du taux de cache optimal en moyenne.

Résultats sur les plus grands modèles avec parallélisme de tenseurs

Les plus grands modèles, Mixtral-8x7B et Llama-70B, ont été testés avec un parallélisme de tenseurs sur le même ensemble de benchmarks, et les résultats sont présentés dans la figure suivante. L’accélération sur les plus grands modèles montre une tendance similaire à celle observée sur les plus petits modèles, indiquant que l’optimisation de SGLang se généralise bien aux plus grands modèles. Guidance et LMQL ont été omis en raison du manque d’implémentations efficaces de parallélisme de tenseurs.

Résultats sur les modèles multimodaux

SGLang a une prise en charge native des modèles multimodaux avec les primitives d’image et de vidéo. Les optimisations de cet article sont compatibles avec les modèles multimodaux. Pour RadixAttention, le hachage des images d’entrée est calculé et utilisé comme clé dans l’arbre radix, permettant la réutilisation du cache KV des jetons d’image de la même image. LLaVA-v1.5-7B (image) a été exécuté sur llava-bench-in-the-wild et LLaVA-NeXT-34B (vidéo) sur ActivityNet. Puisque ces modèles ne sont pas bien pris en charge par les systèmes de référence, la mise en œuvre originale des auteurs de modèles dans Hugging Face Transformers a été utilisée comme référence. Comme le montre le tableau suivant, SGLang offre un débit jusqu’à 6 fois supérieur sur ces benchmarks. Dans llava-bench-in-the-wild, plusieurs questions sur la même image ont été traitées, et SGLang a réutilisé le cache KV dans ce cas.

Déploiement de production

SGLang a été déployé dans Chatbot Arena pour servir les modèles à poids ouvert. En raison d’un faible trafic pour certains modèles, un seul travailleur SGLang sert chacun. Après un mois, un taux de cache RadixAttention de 52,4 % pour LLaVA-Next-34B et de 74,1 % pour Vicuna-33B a été observé. Les cache hits provenaient de messages système courants, d’exemples d’images réutilisés fréquemment et d’historiques de chat à plusieurs tours. Cela a réduit la latence du premier jeton d’un facteur moyen de 1,7 pour Vicuna-33B.

Pensées finales

Dans cet article, nous avons parlé de SGLang, un système nouvellement introduit, qui vise à remédier à cela en fournissant une exécution efficace de programmes de modèles de langage complexes. SGLang se compose d’un langage frontal et d’un runtime. Le frontal simplifie la programmation avec des primitives pour la génération et le contrôle de la parallélisme, tandis que le runtime accélère l’exécution grâce à des optimisations nouvelles comme RadixAttention pour la réutilisation du cache KV et des machines à états finis compressées pour un décodage de sortie structurée plus rapide. Les expériences démontrent que SGLang atteint un débit jusqu’à 6,4 fois supérieur par rapport aux systèmes d’inférence de pointe sur divers grands modèles de langage et multimodaux, traitant des tâches telles que le contrôle d’agent, le raisonnement logique, les benchmarks d’apprentissage à quelques exemples, le décodage JSON, les pipelines de génération augmentée de récupération et le chat à plusieurs tours.

Un ingénieur de profession, un écrivain de cœur. Kunal est un rédacteur technique avec une profonde affection et une compréhension de l'IA et du ML, dédié à simplifier les concepts complexes dans ces domaines grâce à sa documentation engageante et informative.