TL;DR
vLLM est un moteur d'inférence et de serving pour les grands modèles de langage. Il ne sert pas à entraîner le modèle : il charge ses poids, reçoit des requêtes et produit des tokens. Son intérêt vient notamment de sa gestion efficace du KV cache, de PagedAttention et du continuous batching.
Le modèle mental minimal tient en quelques étapes :
texte utilisateur
-> tokenizer
-> identifiants de tokens
-> tenseurs
-> modèle
-> prédiction d'un token
-> ajout du token au contexte
-> nouvelle prédiction
-> réponse finale
Pendant le prefill, le moteur traite le prompt et construit le KV cache. Pendant le decode, il génère les tokens un par un en réutilisant ce cache. PagedAttention organise le KV cache en blocs, à la manière de la mémoire virtuelle d'un système d'exploitation. Le scheduler peut alors faire entrer et sortir des séquences d'un batch entre les tours de calcul afin de mieux occuper le GPU.
La vidéo de référence
La vidéo vLLM – 03. Notions & Définitions est le troisième épisode de la série. Elle adopte volontairement une approche de vulgarisation : apprendre d'abord le vocabulaire, puis revenir plus en profondeur sur chaque notion dans les épisodes suivants.
Son fil conducteur est utile pour débuter : partir du prompt et suivre les transformations jusqu'à la réponse. La vidéo insiste ensuite sur les liens entre QKV, KV cache, prefill, decode, PagedAttention, scheduler et continuous batching. Elle élargit enfin le panorama au routage, au parallélisme, à la quantification, au décodage spéculatif, aux sorties structurées et aux adaptateurs LoRA.
Le support écrit de référence est le fichier 03-notions-definitions/slides.md. Les slides fournissent les schémas, les tableaux et les définitions synthétiques sur lesquels s'appuie cet article.
vLLM, c'est quoi exactement ?
vLLM est une bibliothèque open source destinée à l'inférence et au serving de modèles de langage. La documentation officielle le présente comme un moteur capable d'exécuter des modèles et de les exposer notamment derrière une API compatible avec le format OpenAI.
Trois éléments doivent être distingués :
| Élément | Rôle |
|---|---|
| Modèle | Réseau de neurones utilisé pour calculer le prochain token |
| Poids ou paramètres | Valeurs apprises pendant l'entraînement et chargées en mémoire pour l'inférence |
| Moteur d'inférence | Logiciel qui exécute le modèle, planifie les requêtes et gère les ressources |
Une image de conteneur ne devient pas un processus toute seule. De la même façon, un fichier de poids reste inerte tant qu'un moteur ne le charge pas et ne l'exécute pas. vLLM joue ce rôle de moteur. Il ne réentraîne pas automatiquement le modèle et ne modifie pas ses connaissances lors de chaque requête.
Du prompt à la réponse : le modèle mental minimal
Le tokenizer transforme le texte en tokens
Un LLM ne travaille pas directement avec des phrases. Le tokenizer découpe le texte en unités appelées tokens, puis associe chaque token à un identifiant numérique. Un token peut représenter un mot, une partie de mot, un signe de ponctuation ou un autre fragment défini par le vocabulaire du modèle.
texte : "Hello world"
tokens : ["Hello", " world"]
identifiants: [15496, 995]
Cet exemple illustre le principe, mais le découpage et les identifiants réels dépendent du tokenizer. Il faut utiliser le tokenizer et le chat template attendus par le modèle servi. Deux modèles peuvent découper une même phrase différemment.
Les tenseurs portent les calculs
Les identifiants sont regroupés dans des tenseurs, c'est-à-dire des tableaux multidimensionnels de nombres. Le modèle transforme notamment chaque identifiant en une représentation vectorielle, puis applique une succession d'opérations matricielles.
Le GPU est adapté à cette charge parce qu'il peut exécuter de nombreuses opérations numériques en parallèle. Cela ne signifie pas que toute étape d'une application LLM doit nécessairement tourner sur GPU, mais les calculs principaux d'un grand modèle y sont généralement bien plus efficaces.
L'inférence est autorégressive
Pour un modèle de génération de texte classique, la réponse n'est pas calculée d'un seul bloc. Le modèle prédit le prochain token, l'ajoute au contexte, puis recommence :
prompt -> token D
prompt + D -> token E
prompt + D + E -> token F
prompt + D + E + F -> ...
Cette boucle explique plusieurs métriques importantes :
| Métrique | Question posée |
|---|---|
| Time to First Token, ou TTFT | Combien de temps avant le premier token ? |
| Inter-Token Latency, ou ITL | Quel délai entre deux tokens générés ? |
| Débit en tokens par seconde | Combien de tokens le système produit-il par unité de temps ? |
| Latence totale | Combien de temps avant la fin de la réponse ? |
Le streaming affiche les tokens au fur et à mesure. Il améliore la perception de réactivité, sans changer le principe autorégressif de la génération.
QKV et attention : la base du KV cache
Dans un mécanisme d'attention, les représentations des tokens sont projetées en trois familles de tenseurs : Query, Key et Value.
Une intuition simple consiste à les lire ainsi :
| Terme | Intuition |
|---|---|
| Query, Q | Ce que la représentation courante cherche |
| Key, K | Ce avec quoi elle peut être comparée |
| Value, V | L'information à agréger selon le résultat de la comparaison |
Le calcul compare une query aux keys pertinentes, transforme ces scores en poids d'attention, puis combine les values correspondantes. Cette formulation est volontairement simplifiée, mais elle permet de comprendre pourquoi les tenseurs K et V déjà calculés peuvent être réutilisés pendant la génération.
La query du nouveau token sert au calcul courant. Les keys et values des tokens précédents restent nécessaires aux étapes suivantes. C'est précisément ce que conserve le KV cache.
Prefill et decode : deux phases très différentes
Le serving d'un LLM se décompose en deux phases principales.
Le prefill traite le prompt
Pendant le prefill, le moteur traite les tokens du prompt et construit les tenseurs Key et Value qui alimentent le KV cache.
prompt : [A] [B] [C]
KV cache après prefill :
A -> key_A, value_A
B -> key_B, value_B
C -> key_C, value_C
Cette phase traite plusieurs tokens du prompt et mobilise fortement les unités de calcul du GPU. Plus le prompt est long, plus le travail initial et le délai avant le premier token peuvent augmenter.
Le decode génère la réponse
Pendant le decode, le moteur produit généralement un nouveau token par séquence et par itération. Il relit le KV cache existant, calcule le token suivant, puis ajoute les nouveaux K et V au cache.
decode 1 : réutiliser KV(A, B, C) -> générer D
decode 2 : réutiliser KV(A, B, C, D) -> générer E
Le decode effectue moins de travail parallèle par séquence, mais lit fréquemment les poids et le KV cache. Il est donc souvent décrit comme davantage limité par la bande passante mémoire que le prefill.
| Phase | Entrée principale | Résultat | Profil simplifié |
|---|---|---|---|
| Prefill | Tous les tokens du prompt | KV cache initial et premier calcul de sortie | Davantage orienté calcul |
| Decode | Contexte mis en cache et dernier token | Un token supplémentaire | Davantage orienté mémoire |
Cette distinction est essentielle pour interpréter les performances. Optimiser uniquement le nombre moyen de tokens par seconde peut masquer un mauvais TTFT sur les prompts longs ou une mauvaise ITL pendant la génération.
KV cache : éviter les recalculs inutiles
Sans KV cache, le moteur devrait recalculer les keys et values des tokens précédents à chaque nouveau token. Avec le cache, il les calcule une fois et les réutilise :
sans KV cache : recalculer encore les anciens tokens
avec KV cache : relire les anciens K/V et calculer seulement la suite
Le gain de calcul a un coût : le KV cache occupe de la mémoire, souvent de la VRAM lorsque l'inférence s'exécute sur GPU. Sa taille augmente avec plusieurs dimensions du workload, notamment :
- le nombre de séquences actives ;
- la longueur du contexte de chaque séquence ;
- le nombre de couches et de têtes KV du modèle ;
- la taille des têtes ;
- le type numérique utilisé pour le cache.
Il ne suffit donc pas de vérifier que les poids tiennent dans la VRAM. Il faut aussi réserver de la place au KV cache, aux buffers d'exécution et aux autres allocations du moteur. Le calcul rapide « nombre de paramètres multiplié par octets par poids » n'est qu'une estimation des poids, pas de toute la mémoire nécessaire au serving.
Prefix caching : réutiliser un début de prompt
Le KV cache d'une requête évite les recalculs pendant son propre decode. Le prefix caching va plus loin : il permet de réutiliser des blocs déjà calculés lorsque plusieurs requêtes partagent exactement un même préfixe tokenisé.
Les cas typiques cités dans les slides et la vidéo sont :
- un long prompt système commun à tous les utilisateurs ;
- des règles ou politiques d'entreprise répétées ;
- un document RAG injecté dans plusieurs requêtes ;
- une partie stable d'un historique de conversation ;
- des instructions applicatives identiques.
préfixe commun : système + règles + document
requête 1 : préfixe commun + question A
requête 2 : préfixe commun + question B
requête 3 : préfixe commun + question C
Cette technique est surtout intéressante lorsque le préfixe est suffisamment long, strictement identique au niveau des tokens et réellement réutilisé. Elle ne doit pas être confondue avec un cache de réponse : le moteur réutilise des calculs intermédiaires, pas nécessairement la réponse finale.
PagedAttention : organiser le KV cache en blocs
Le KV cache pose un problème d'allocation. Les séquences n'ont pas toutes la même longueur, ne commencent pas au même moment et ne se terminent pas ensemble. Réserver à chacune une grande zone mémoire continue peut créer de la fragmentation et gaspiller de la VRAM.
PagedAttention applique une idée proche de la mémoire virtuelle : la vue logique d'une séquence est continue, tandis que ses blocs physiques peuvent être dispersés.
KV logique de la requête A : A0 -> A1 -> A2 -> A3
blocs physiques GPU :
slot 0 : C0
slot 1 : A2
slot 2 : libre
slot 3 : B0
slot 4 : A0
slot 5 : A3
slot 6 : A1
block table de A :
A0 -> slot 4
A1 -> slot 6
A2 -> slot 1
A3 -> slot 5
Lorsqu'une séquence grandit, le moteur peut lui attribuer un nouveau bloc libre sans déplacer tout son cache. Lorsqu'elle se termine, ses blocs redeviennent disponibles.
| Mémoire virtuelle | PagedAttention |
|---|---|
| Espace logique d'un processus | KV cache logique d'une requête |
| Page virtuelle | Bloc KV logique |
| Cadre de page physique | Bloc KV physique en mémoire GPU |
| Table de pages | Block table |
Cette analogie aide à comprendre l'abstraction, mais PagedAttention reste un mécanisme spécialisé pour les tenseurs K/V et les kernels d'attention. La documentation de conception de PagedAttention précise d'ailleurs que son document décrit historiquement le kernel issu du travail original et ne reflète pas tous les détails du code actuel.
Scheduler et continuous batching : garder le GPU occupé
Le scheduler décide quelles séquences seront exécutées lors du prochain tour de calcul. Il doit concilier plusieurs contraintes : requêtes en attente, séquences déjà actives, budget de tokens, espace disponible dans le KV cache et équité entre requêtes.
Le chapitre introduit ce vocabulaire :
| Terme | Définition pratique |
|---|---|
| Waiting | Requêtes reçues mais pas encore planifiées |
| Running | Séquences actuellement prises en charge par le moteur |
| Swapped | Séquences dont les données ont été déplacées hors de la mémoire GPU |
max-num-seqs |
Limite du nombre de séquences actives |
max-num-batched-tokens |
Budget de tokens traité dans une itération |
| KV budget | Blocs mémoire disponibles pour les séquences |
Dans un batch statique, les requêtes rapides laissent des emplacements inutilisés jusqu'à ce que la plus lente termine :
tour 1 : A B C
tour 2 : A B C
tour 3 : A B -
tour 4 : A - -
Avec le continuous batching, une requête terminée peut être remplacée entre deux itérations :
tour 1 : A B C
tour 2 : A B C
tour 3 : A B D
tour 4 : A E D
Le batch reste un ensemble de travail exécuté conjointement, mais sa composition évolue. Cette planification à l'échelle des itérations est une raison majeure pour laquelle un moteur de serving spécialisé peut atteindre un meilleur débit qu'un script d'inférence conçu pour une requête isolée.
Data, tensor, pipeline et expert parallelism
Ajouter des GPU peut répondre à deux besoins différents : servir davantage de requêtes ou faire tenir et exécuter une instance de modèle plus lourde.
| Technique | Ce qui est réparti | Cas d'usage simplifié |
|---|---|---|
| Data parallelism | Des copies complètes du modèle servent des requêtes différentes | Le modèle tient sur un GPU ou un groupe de GPU, mais le trafic augmente |
| Tensor parallelism | Les opérations et poids d'une même couche sont découpés entre GPU | Une instance de modèle nécessite plusieurs GPU fortement interconnectés |
| Pipeline parallelism | Des groupes de couches sont placés sur différents GPU | Le modèle est découpé en étapes successives |
| Expert parallelism | Les experts d'un modèle Mixture of Experts sont distribués | Le modèle contient des experts activés sélectivement |
Le tensor parallelism n'est donc pas simplement le découpage du prompt. Il répartit les calculs et les paramètres au sein des couches, puis nécessite des communications entre GPU. Le pipeline parallelism répartit plutôt la pile de couches en plusieurs étapes.
Deux règles donnent une première orientation :
Plus d'utilisateurs : ajouter des réplicas, donc scale out.
Une instance plus grande : ajouter des GPU au groupe, donc scale up.
Le choix réel dépend toutefois de la taille du modèle, du trafic, des objectifs de latence, de la mémoire et de l'interconnexion. La documentation vLLM propose un guide dédié au parallélisme et au scaling.
Quantification, décodage et LoRA : où placer les autres notions ?
La fin du chapitre présente plusieurs techniques qui seront approfondies plus tard dans la série.
Quantification
La quantification réduit la précision numérique des poids, par exemple de FP16 vers FP8 ou INT4. Pour un modèle de 7 milliards de paramètres, l'ordre de grandeur brut des seuls poids est approximativement :
| Format simplifié | Octets par paramètre | Taille brute pour 7 milliards de paramètres |
|---|---|---|
| FP16 | 2 | 14 Go |
| FP8 | 1 | 7 Go |
| INT4 | 0,5 | 3,5 Go |
Ces nombres ignorent les métadonnées, les buffers, le KV cache et les particularités du format. Une précision plus faible peut aussi modifier la qualité, la compatibilité et les performances. Il faut donc mesurer le modèle quantifié sur le cas d'usage plutôt que choisir uniquement la plus petite taille.
Décodage spéculatif
Le speculative decoding emploie un mécanisme de proposition moins coûteux, par exemple un petit modèle draft, pour suggérer plusieurs tokens. Le modèle principal les vérifie. L'accélération dépend notamment du coût du draft, du taux d'acceptation et du matériel ; elle n'est pas automatique.
Guided decoding et sorties structurées
Le guided decoding contraint les sorties autorisées, par exemple avec un schéma JSON, une grammaire ou une expression régulière. L'objectif est d'obtenir un format exploitable par une application plutôt qu'un texte libre qu'il faudrait parser de manière fragile.
LoRA et multi-LoRA
Un adaptateur LoRA ajoute un petit ensemble de paramètres entraînables à un modèle de base gelé. Il permet d'adapter le comportement sans réentraîner tous les poids. Le multi-LoRA consiste à servir plusieurs adaptateurs au-dessus d'un même modèle de base, selon les capacités et la configuration du moteur.
Quand utiliser vLLM et quand choisir une autre approche ?
vLLM cible principalement le serving efficace de modèles, avec gestion de requêtes concurrentes et API. Le bon choix dépend du besoin réel.
| Besoin | Orientation possible |
|---|---|
| Développer localement avec une expérience très simple | Un outil local intégré peut suffire |
| Servir un modèle à plusieurs clients avec une API | vLLM correspond directement à ce besoin |
| Contrôler le batching, le KV cache et le parallélisme | Un moteur spécialisé comme vLLM devient pertinent |
| Exécuter principalement sur CPU ou matériel très contraint | Comparer avec des runtimes conçus pour ce contexte |
| Ne pas exploiter d'infrastructure GPU | Une API managée peut réduire l'exploitation nécessaire |
| Garder modèles et données dans son infrastructure | Le self-hosting avec vLLM peut répondre à cette contrainte |
La décision doit être guidée par les SLO : TTFT, ITL, débit, concurrence, coût par token, qualité du modèle et exigences de sécurité. Une comparaison sans benchmark représentatif donne peu d'informations utiles.
Lire les slides et démarrer la suite du tutoriel
Ce chapitre est conceptuel et n'impose pas de lancer un serveur. Le dépôt utilise mdp pour afficher les slides dans le terminal :
# macOS
brew install mdp
# Debian ou Ubuntu
sudo apt-get install mdp
# depuis la racine du dépôt
mdp 03-notions-definitions/slides.md
Le README du dépôt décrit ensuite le parcours complet. Le chapitre 04 prépare l'instance GPU chez Infomaniak, puis le chapitre 05 lance un premier modèle avec vllm serve.
Liens utiles
- Dépôt GitLab tutorials-vllm
- Slides du chapitre 03 : Notions and Definitions
- README et sommaire de la formation
- Vidéo vLLM – 03. Notions & Définitions
- Playlist YouTube vLLM – Tutorials
- Documentation officielle vLLM
- Documentation PagedAttention
- Documentation Automatic Prefix Caching
- Guide vLLM sur le parallélisme et le scaling
- Code source vLLM sur GitHub
- Article fondateur sur vLLM et PagedAttention
- Article scientifique vLLM
- Forum de la communauté vLLM
- Slack de la communauté vLLM
FAQ vLLM, KV cache et PagedAttention
vLLM entraîne-t-il les modèles ?
Non. vLLM est d'abord un moteur d'inférence et de serving. Il charge des poids déjà entraînés et exécute le modèle pour répondre aux requêtes. Un entraînement ou un fine-tuning se fait avec une chaîne d'outils distincte.
Quelle différence entre un token et un mot ?
Un token est une unité définie par le tokenizer. Il peut correspondre à un mot complet, une partie de mot, un espace, un signe ou un autre fragment. Le nombre de tokens n'est donc pas égal au nombre de mots.
À quoi sert le KV cache ?
Le KV cache conserve les tenseurs Key et Value des tokens déjà traités. Pendant le decode, le moteur les réutilise au lieu de recalculer toute l'attention sur l'historique à chaque nouveau token.
Le KV cache contient-il le texte du prompt ?
Pas sous sa forme textuelle. Il contient des tenseurs numériques dérivés des tokens par les couches d'attention. L'application peut conserver le texte par ailleurs, mais ce n'est pas ce que désigne le KV cache.
Quelle différence entre KV cache et prefix caching ?
Le KV cache accélère la suite d'une séquence en cours. Le prefix caching permet de réutiliser des blocs KV associés à un préfixe identique entre plusieurs traitements compatibles.
PagedAttention est-il un système de swap GPU ?
Non. PagedAttention organise le KV cache en blocs logiques et physiques afin de mieux utiliser la mémoire. Le déplacement de données vers la mémoire CPU ou un stockage externe est une problématique d'offloading ou de swapping distincte.
Pourquoi le continuous batching améliore-t-il le débit ?
Parce que les séquences terminées peuvent quitter le batch et être remplacées par des requêtes en attente entre les itérations. Le GPU passe ainsi moins de temps avec des emplacements inutilisés à cause de requêtes de longueurs différentes.
Quelle différence entre prefill et decode ?
Le prefill traite les tokens du prompt et construit le KV cache initial. Le decode génère ensuite les tokens un par un en réutilisant ce cache. Les deux phases n'ont pas le même profil de calcul ni les mêmes métriques de latence.
La quantification réduit-elle aussi automatiquement le KV cache ?
Pas nécessairement. La précision des poids et le type numérique du KV cache sont des réglages distincts. Un modèle peut avoir des poids quantifiés tout en utilisant un autre format pour son cache.
Faut-il utiliser tensor parallelism pour servir plus d'utilisateurs ?
Pas toujours. Si le modèle tient déjà dans un GPU ou un petit groupe de GPU, plusieurs réplicas complets peuvent être plus simples pour augmenter le débit. Le tensor parallelism sert surtout à répartir une même instance de modèle et implique des communications entre GPU.
Conclusion
Comprendre vLLM commence moins par une commande que par une chaîne de concepts. Le texte devient des tokens, les tokens deviennent des tenseurs, puis le modèle génère la réponse de manière autorégressive. Le prefill construit le contexte de calcul, le decode l'étend token par token et le KV cache évite de recommencer inutilement le travail déjà effectué.
PagedAttention rend ce cache plus simple à allouer sous forme de blocs. Le scheduler et le continuous batching organisent ensuite plusieurs séquences concurrentes pour utiliser efficacement les ressources. Quantification, prefix caching, parallélisme, speculative decoding et LoRA sont des leviers supplémentaires, mais ils prennent tout leur sens une fois ce socle compris.
La suite logique du parcours est le chapitre 04-infomaniak-cloud-setup, consacré à la préparation d'une machine GPU, avant le premier lancement réel de vllm serve au chapitre 05.