
Un modèle de langage peut tenir dans la mémoire d'un GPU et pourtant saturer la VRAM dès que vous lui soumettez une invite longue.
La raison est souvent le cache KV.
Lors de l'inférence autorégressive, un LLM génère un jeton à la fois. À chaque couche d'attention, il crée des représentations de clés et de valeurs pour les jetons déjà traités. Conserver ces représentations en mémoire permet au modèle de les réutiliser au lieu de recalculer le même état d'attention à chaque génération de nouveau jeton. Hugging Face décrit ce cache comme un stockage des paires clé-valeur des jetons précédemment traités, auxquelles s'ajoutent les nouvelles au fur et à mesure de la génération.
Cela économise de la puissance de calcul.
Cela signifie également que chaque jeton actif consomme de la mémoire.
Pour un transformeur standard, la mémoire du cache KV croît de manière approximativement linéaire avec :
longueur du contexte × nombre de couches × têtes KV × dimension de la tête × précision × séquences simultanées
C'est pourquoi un modèle annoncé avec une fenêtre de contexte de 128K ou 256K ne rend pas automatiquement ce contexte exploitable sur n'importe quel GPU.
Une fenêtre de contexte est une capacité du modèle.
Le cache KV fait partie du prix à payer pour l'utiliser.
KV signifie clé-valeur.
Dans le mécanisme d'attention des transformers, chaque jeton est projeté en trois types de vecteurs :
Requête (Query)
Clé (Key)
Valeur (Value)
La requête représente ce que le jeton actuel recherche.
Les clés permettent de déterminer quels jetons précédents sont pertinents.
Les valeurs contiennent les informations que l'attention extrait de ces jetons.
Un calcul d'attention simplifié ressemble à ceci :
Attention(Q, K, V)
Lors de la génération de texte, le modèle a besoin de manière répétée des clés et des valeurs associées à tous les jetons qu'il a déjà traités.
Sans mise en cache, la génération du 1 001e jeton impliquerait de recalculer des informations que le modèle a déjà traitées lors de la production du 1 000e jeton.
Ensuite, le 1 002e jeton répéterait une grande partie de ce travail.
Un cache KV conserve les anciennes clés et valeurs.
La nouvelle étape de génération doit seulement calculer l'état du nouveau jeton et effectuer l'attention sur le cache stocké. La documentation actuelle de Transformers de Hugging Face décrit le cache comme un tenseur de clés et un tenseur de valeurs par couche d'attention, qui s'agrandissent à mesure que des jetons supplémentaires sont traités.
La requête n'est pas stockée pour le décodage futur de la même manière.
C'est pourquoi nous l'appelons cache KV plutôt que cache QKV.
Imaginez une conversation contenant 10 000 jetons.
Le modèle a besoin de ces 10 000 jetons comme contexte avant de prédire le suivant.
Sans cache, il devrait recalculer à chaque étape de décodage les états des clés et des valeurs pour le contexte précédent.
Avec un cache KV, ces états ont déjà été calculés.
Le modèle les conserve et ajoute un nouvel ensemble de clés et de valeurs à l'arrivée du jeton suivant.
Conceptuellement :
Invite
1 2 3 4 5 ... 10 000
↓
K,V en cache
Générer le jeton 10 001
↓
ajouter nouveaux K,V
Générer le jeton 10 002
↓
ajouter nouveaux K,V
C'est l'une des raisons pour lesquelles l'inférence autorégressive reste utilisable à mesure que la sortie s'allonge. Hugging Face souligne que le cache évite de recalculer les états K et V précédents, bien que la mémoire cache elle-même augmente avec la longueur de la séquence.
Le compromis est simple :
vous utilisez de la mémoire pour économiser du calcul.
Pour un transformeur décodeur classique avec attention complète, un calcul de premier ordre utile est :
Octets du cache KV =
jetons
× couches
× 2
× têtes KV
× dimension de tête
× octets par élément
Le 2 représente les deux tenseurs mis en cache :
K + V
Pour plusieurs séquences indépendantes, multipliez par le nombre de séquences actives si elles ont la même longueur.
Une expression plus complète est :
Octets du cache KV =
taille du lot
× longueur de séquence
× nombre de couches
× 2
× nombre de têtes KV
× dimension de tête
× octets par élément
Cela découle directement de la structure du tenseur de cache utilisée par les implémentations de transformeurs : lot, têtes, longueur de séquence et dimension de tête, avec des tenseurs distincts pour les clés et les valeurs.
Cette formule est extrêmement utile.
Elle est aussi volontairement simplifiée.
L'attention à fenêtre glissante, les architectures hybrides, l'attention latente multi-têtes, la compression de cache, le parallélisme de tenseurs et d'autres conceptions peuvent modifier la disposition réelle en mémoire.
Pour un modèle d'attention complète standard, cependant, elle vous donne la bonne intuition.
Pour le calcul simple :
Ainsi, le passage d'un cache compatible de BF16 à FP8 réduit environ de moitié ses besoins de stockage brut.
Notez qu'il s'agit de la précision du cache.
Elle ne doit pas nécessairement être identique à la précision des poids du modèle.
Vous pouvez avoir :
poids du modèle en 4 bits
+
cache KV en BF16
ou :
poids du modèle en 4 bits
+
cache KV en FP8
Il s'agit de décisions de mémoire distinctes.
Cette distinction sera à nouveau importante dans notre futur guide d'inférence NVFP4 et Blackwell.
Prenons un transformeur GQA représentatif avec :
Pour un jeton :
32
× 2
× 8
× 128
× 2 octets
=
131 072 octets
C'est-à-dire :
128 Kio par jeton
Cela semble peu.
Multipliez maintenant par la longueur du contexte :
C'est pour une séquence.
Si les poids du modèle occupent déjà une part substantielle d'un GPU de 32 Go, un cache BF16 de 128K peut rendre le contexte maximal annoncé totalement irréaliste pour cette configuration.
C'est pourquoi notre guide de la VRAM pour RTX 5090 distingue systématiquement le fait de faire tenir les poids du modèle et celui de conserver suffisamment de mémoire pour la charge de travail.
Le nombre de paramètres ne détermine pas directement la taille du cache KV.
C'est l'architecture qui le fait.
Imaginez un modèle GQA plus grand avec :
80 couches
8 têtes KV
128 dimensions par tête
Cache BF16
Son cache nécessite :
80
× 2
× 8
× 128
× 2
=
327 680 octets
soit :
320 Kio par jeton
Voici à quoi ressemblent désormais les mêmes contextes :
Encore une fois, il s'agit d'une configuration représentative en attention complète et non d'un tableau universel pour tous les modèles 70B.
Cela explique pourquoi l'affirmation :
un modèle 70B en 4 bits nécessite environ 35 Go
est un conseil d'infrastructure incomplet.
Ce chiffre décrit une estimation approximative du poids des paramètres.
Le point de terminaison en cours d'exécution nécessite également un cache.
Notre guide des exigences GPU pour Llama 3.3 70B couvre l'aspect multi-GPU de ce problème.
Le dimensionnement du cache KV est parfois abordé comme si seuls les jetons générés importaient.
Ce n'est pas le cas.
Le prompt passe par une phase de pré-remplissage avant que le décodage ne commence. Les clés et valeurs créées pour ces jetons de prompt deviennent le cache que le modèle utilise lors de la génération de la réponse.
Supposons que vous envoyiez :
24 000 jetons de prompt
et autorisiez :
8 000 jetons de sortie
La séquence peut finir par contenir :
32 000 jetons actifs
Le cache doit représenter l'état d'attention pertinent pour cette séquence active.
C'est pourquoi un document long peut consommer une quantité considérable de VRAM avant même que le modèle n'ait généré beaucoup de texte.
Cela explique également pourquoi la limite de contexte nominale est généralement un budget combiné pour l'entrée et la sortie, plutôt qu'une allocation indépendante pour chacune. Meta, par exemple, indique 128 000 jetons de contexte pour Llama 3.1 et Llama 3.3, avec l'utilisation du GQA pour améliorer l'évolutivité de l'inférence.
Supposons plutôt que votre invite soit courte :
Invite de 4 000 jetons
mais que vous exécutiez une charge de travail de raisonnement qui génère :
28 000 jetons supplémentaires
Vous aboutissez au même résultat global :
Séquence active de 32 000 jetons
Le cache ne fait aucune distinction entre les jetons provenant de la sortie et ceux provenant de l'entrée.
Cela fait également des limites de sortie un paramètre d'infrastructure.
Un serveur configuré pour des réponses de 2 000 jetons présente un profil de cache dans le pire des cas très différent de celui qui autorise des générations de 30 000 jetons.
Un raisonnement long n'est pas gratuit sous prétexte que l'invite était courte.
Une seule requête est rarement le problème en production.
Supposons que notre modèle représentatif à 32 couches utilise environ :
4 Gio
de cache KV en BF16 pour une séquence de 32 000 jetons.
Quatre requêtes indépendantes d'une longueur à peu près équivalente peuvent pousser les besoins bruts en cache vers :
16 Gio
avant même de prendre en compte les poids du modèle et les autres allocations GPU.
Le total exact dans un moteur de service dépend des longueurs de séquence actives, de l'allocation des blocs, du partage de préfixes, du format du cache et du planificateur.
Le principe reste le suivant :
La longueur du contexte définit la taille maximale d'une requête. La concurrence détermine combien de ces requêtes doivent coexister.
C'est pourquoi dimensionner un serveur d'inférence à partir d'un seul test interactif est risqué.
Votre modèle peut fonctionner parfaitement avec un seul utilisateur, mais saturer la capacité du cache dès l'arrivée de dix utilisateurs.
Meta annonce un contexte de 128 000 jetons pour Llama 3.1 et Llama 3.3. Certaines familles de modèles plus récentes affichent des fenêtres nettement plus larges.
Ce chiffre répond à la question :
Quelle longueur de séquence le modèle a-t-il été conçu pour supporter ?
Il ne répond pas à :
Quelle longueur de séquence dois-je configurer sur ce GPU ?
La seconde question dépend de :
poids du modèle
+
cache KV
+
mémoire d'exécution
+
concurrence
+
exigences de latence
+
votre distribution de requêtes réelle
C'est pourquoi nous avons délibérément lancé des modèles tels que Qwen3.6-27B avec une limite de contexte plus réduite dans notre guide de déploiement, plutôt que de configurer automatiquement le maximum supporté par le modèle.
Une limite plus basse permet de libérer davantage de mémoire GPU pour les requêtes réelles.
Revenons à la formule :
couches
× têtes KV
× dimension de tête
Le nombre de têtes KV a une importance capitale.
Dans l'attention multi-têtes classique, chaque tête de requête possède ses propres têtes de clé et de valeur.
Le Grouped-Query Attention, ou GQA, permet à plusieurs têtes de requête de partager un nombre réduit de têtes de clé/valeur.
En résumé :
32 têtes de requête
au lieu de
32 têtes K + 32 têtes V
vous pourriez avoir
8 têtes K + 8 têtes V
Cela réduit la quantité d'état K et V devant être stockée.
Meta utilise explicitement le GQA dans Llama 3.1 et Llama 3.3 et décrit ce choix comme une amélioration de la scalabilité de l'inférence.
C'est pourquoi vous devriez utiliser :
num_key_value_heads
plutôt que :
num_attention_heads
lors du calcul de la taille du cache pour un modèle GQA.
Utiliser le nombre de têtes de requête peut considérablement surestimer le cache.
À nombre de couches et dimension de tête identiques :
Cela ne rend pas une architecture automatiquement meilleure dans l'ensemble.
Cela montre pourquoi le nombre de paramètres seul est insuffisant pour dimensionner le cache KV.
Deux modèles ayant un nombre de paramètres et des fenêtres de contexte similaires peuvent avoir des besoins en cache très différents.
Consultez la configuration du modèle.
Pour de nombreux modèles transformer Hugging Face, les champs de configuration utiles sont :
num_hidden_layers
num_key_value_heads
head_dim
Si head_dim n'est pas stocké explicitement, il est souvent calculé comme suit :
hidden_size / num_attention_heads
Utilisez ensuite :
octets de cache par jeton =
num_hidden_layers
× 2
× num_key_value_heads
× head_dim
× bytes_per_element
Voici un calculateur simple :
def kv_cache_gib(
couches,
têtes_kv,
dim_tête,
jetons,
octets_par_élément=2,
séquences=1,
):
octets_totaux = (
couches
* 2
* têtes_kv
* dim_tête
* jetons
* octets_par_élément
* séquences
)
return octets_totaux / (1024 ** 3)
print(
cache_kv_gib(
couches=32,
kv_heads=8,
head_dim=128,
tokens=32768,
bytes_per_element=2,
)
)
Résultat :
4,0
Cette configuration utilise donc environ :
4 Gio
de stockage brut pour le cache KV en BF16 pour une séquence de 32 000 jetons.
Ne considérez pas ce résultat comme une estimation complète de la mémoire GPU.
Il s'agit uniquement du composant cache du budget mémoire.
Supposons que vous quantifiez un modèle de BF16 vers 4 bits.
Les poids du modèle deviennent beaucoup plus légers.
C'est une bonne chose.
Mais si le moteur de service stocke toujours les tenseurs K et V en BF16, le calcul du cache KV n'a pas diminué.
Vous pourriez donc passer de :
modèle BF16 volumineux
+
cache BF16
à :
modèle 4 bits beaucoup plus petit
+
le même cache BF16
Pour les contextes courts, la mémoire occupée par les poids du modèle peut rester prédominante.
Pour les contextes très longs ou en cas de forte concurrence, le cache peut représenter une part de plus en plus importante du total.
C'est pourquoi la quantification des poids et celle du cache doivent être abordées séparément.
Le prochain guide NVFP4 se concentrera principalement sur les poids du modèle et le calcul. Cet article traite de l'état d'inférence croissant qui les entoure.
vLLM prend actuellement en charge plusieurs types de données pour le cache KV, notamment les formats BF16, FP16 et FP8. Son mode auto utilise le type de données du modèle, tandis qu'un cache FP8 explicite permet de réduire le nombre d'octets utilisés par chaque valeur mise en cache.
Pour l'exemple représentatif ci-dessus :
cache 32K BF16 ≈ 4 Gio
devient environ :
Cache FP8 32K ≈ 2 Gio
Et :
Cache BF16 128K ≈ 16 Gio
devient environ :
Cache FP8 128K ≈ 8 Gio
C'est une différence importante.
Cela ne signifie pas que le cache FP8 est exempt de compromis sur la qualité. La quantification de l'état d'attention mis en cache modifie la précision numérique ; vous devez donc évaluer le modèle et la charge de travail que vous prévoyez d'exécuter.
Les économies de mémoire sont précieuses car elles permettent d'obtenir des avantages concrets :
plus de contexte
ou
plus de requêtes simultanées
ou
plus d'espace pour le modèle
Supposons qu'un modèle prenne en charge beaucoup plus de contexte que ce dont votre produit a besoin.
Vous pourriez délibérément l'utiliser à 32K avec un cache KV en FP8 :
vllm serve <model> \
--max-model-len 32768 \
--kv-cache-dtype fp8
Les versions actuelles de vLLM exposent le type de données du cache KV de manière indépendante et permettent également un contrôle direct du budget mémoire alloué au cache.
L'idée n'est pas de dire que ces deux paramètres constituent la configuration optimale universelle.
L'idée est que le contexte et la précision du cache sont des décisions liées au service.
Vous n'êtes pas obligé d'accepter chaque maximum indiqué dans la fiche technique du modèle comme configuration d'exécution.
Notre guide de configuration vLLM couvre le reste de l'environnement de service.
Il existe un autre problème de mémoire caché au sein du cache KV.
Même si vous savez exactement quelle quantité d'informations doit être stockée, le traitement de requêtes de longueurs différentes peut entraîner une utilisation inefficace de la mémoire GPU.
Un utilisateur peut avoir :
2 000 jetons
un autre :
19 000
un autre :
7 000
et leurs séquences continuent de croître et de se terminer à des moments différents.
L'allocation de grands blocs de mémoire contigus pour chaque requête peut gaspiller de la capacité en raison de la fragmentation et de la surréservation.
Les travaux originaux sur vLLM ont introduit PagedAttention pour résoudre ce problème.
Au lieu d'exiger une allocation de cache contiguë par séquence, vLLM divise le stockage du cache KV en blocs, en s'inspirant de la pagination de la mémoire virtuelle. Les recherches menées sur vLLM ont fait état d'un gaspillage quasi nul dû à la fragmentation du cache KV et d'un partage plus flexible de l'état mis en cache entre les requêtes.
Il s'agit d'une amélioration majeure du service.
Mais il convient de faire une distinction importante :
PagedAttention facilite la gestion de la mémoire du cache KV. Cela ne rend pas gratuites les informations K et V sous-jacentes pour chaque jeton.
Une séquence de 100 000 jetons contient toujours beaucoup plus d'état d'attention mis en cache qu'une séquence de 5 000 jetons.
Nous approfondirons ce sujet dans notre futur guide sur PagedAttention et le traitement par lots continu.
Imaginez un hôtel.
Un allocateur de cache classique pourrait réserver tout un long couloir parce qu'un client pourrait éventuellement avoir besoin de toutes les chambres.
L'allocation paginée vous permet d'attribuer des chambres par petits groupes au fur et à mesure que le client en a réellement besoin.
Cela gaspille moins d'espace.
Cela ne change pas le nombre de chambres qu'un client occupe une fois qu'il les a remplies.
Le même principe s'applique ici.
La pagination améliore :
l'allocation
réutilisation
partage
fragmentation
L'architecture du modèle et la précision du cache déterminent toujours la quantité de données requise par un jeton actif.
C'est pourquoi ces deux types d'optimisation sont importants.
Certaines requêtes partagent le même début.
Imaginez un assistant où chaque requête commence par :
un prompt système de 10 000 jetons
+
la même documentation
Si le serveur recalcule et stocke ce préfixe exact indépendamment pour chaque requête, il duplique le travail.
La mise en cache de préfixe permet à un système de service de reconnaître les blocs déjà calculés et de les réutiliser.
Le gestionnaire de cache de préfixe actuel de vLLM hache les blocs de jetons de prompt, recherche les blocs déjà calculés et permet aux nouvelles requêtes de référencer ces blocs mis en cache lorsque cela est applicable.
Cela peut réduire le travail de pré-remplissage répété et le stockage de cache dupliqué pour les préfixes partagés.
Cela ne signifie pas que des conversations arbitraires partagent désormais un seul cache KV.
Une fois que les requêtes divergent, leurs jetons uniques nécessitent leur propre état.
Ces termes peuvent sembler interchangeables.
Ils répondent à des niveaux différents du problème.
Le KV caching signifie :
Ne pas recalculer les états K et V
pour les jetons que cette séquence active
a déjà traités.
Le prefix caching signifie :
Si une autre requête possède exactement le même
préfixe éligible, réutiliser les blocs de cache
déjà calculés pour ce préfixe également.
Le premier est fondamental pour un décodage autorégressif efficace.
Le second est une optimisation de service entre les requêtes.
Cette distinction devient importante lors de l'évaluation des affirmations concernant le cache de prompt ou le contexte partagé.
Oui, dans certaines implémentations d'inférence.
Hugging Face Transformers prend en charge des stratégies de cache permettant de décharger l'état du cache hors du GPU, échangeant ainsi la pression sur la mémoire GPU contre un transfert de données supplémentaire. Sa documentation actuelle sur le cache inclut des stratégies de cache déchargé et quantifié.
Cela peut s'avérer utile lorsque la VRAM constitue la limite absolue.
Le compromis est similaire à celui du déchargement des poids du modèle :
moins de mémoire GPU
↔
plus de surcharge de transfert
Qu'une configuration devienne techniquement possible ne signifie pas qu'elle offre la même latence ou le même débit que le maintien du cache sur le GPU.
Évaluez la charge de travail résultante.
La formule simple suppose que chaque couche stocke les états K et V pour la séquence active complète.
Certaines architectures ne le font pas.
Avec l'attention à fenêtre glissante, une couche ne porte son attention que sur une fenêtre récente limitée. Une fois que le cache de cette couche atteint la taille de la fenêtre, il n'a plus besoin de croître indéfiniment.
La documentation actuelle de Hugging Face sur le cache précise que les caches pour les couches à attention par fenêtre glissante ou par blocs cessent de croître une fois que leur fenêtre d'attention configurée est atteinte.
Cela signifie que :
un contexte de modèle de 128K
ne signifie pas nécessairement :
chaque couche d'attention
stocke 128K jetons d'état KV
L'architecture compte à nouveau.
Les modèles modernes combinent de plus en plus les mécanismes d'attention.
Un modèle peut contenir :
des couches d'attention complète
+
des couches à fenêtre glissante
+
des composants récurrents ou d'espace d'état
Ces couches n'ont pas nécessairement un comportement de cache identique.
Le système de cache actuel de vLLM prend explicitement en charge les modèles contenant différents types d'attention et de cache, plutôt que de supposer que chaque couche utilise un cache d'attention complète uniforme.
C'est pourquoi cette formule simple doit être considérée comme :
un très bon outil de dimensionnement pour les transformeurs à attention complète standard
plutôt que comme :
une loi universelle pour toute nouvelle architecture.
Si vous déployez un modèle inhabituel, examinez sa configuration et la documentation du moteur de service.
C'est la distinction clé en production.
Un fournisseur de modèle peut vous dire :
contexte maximal = 128K
Il ne peut pas savoir si votre application dessert :
un utilisateur
ou :
100 utilisateurs simultanés
Supposons que chaque requête réelle nécessite en moyenne seulement 8 000 jetons actifs.
Cela semble modeste.
En cas de forte concurrence, le nombre total de jetons mis en cache actifs peut tout de même devenir énorme.
Le problème de service est donc moins :
Ce GPU peut-il gérer un contexte de 128 000 jetons ?
et davantage :
Combien de jetons actifs ce déploiement peut-il supporter avec la latence requise ?
C'est une bien meilleure question pour la planification de la capacité.
Supposons qu'un GPU dispose d'assez de mémoire cache libre pour environ :
128 000 jetons d'état KV
pour un modèle et une précision donnés.
Vous pourriez théoriquement utiliser cette capacité comme suit :
1 requête × 128 000
ou environ :
4 requêtes × 32 000
ou :
16 requêtes × 8 Ko
Les planificateurs et les charges de travail réels sont plus complexes que cette simple division, mais elle met en évidence le compromis à faire.
Le même budget de mémoire GPU peut être alloué à des contextes individuels plus longs ou à un plus grand nombre de contextes simultanés.
C'est pourquoi maximiser aveuglément max_model_len peut être une mauvaise décision en production.
La mémoire GPU est sollicitée par plusieurs éléments :
On ne peut pas allouer deux fois le même gigaoctet.
C'est ce qui rend la quantification des poids du modèle utile pour bien plus que le simple fait de faire tenir un modèle plus volumineux.
Réduire la mémoire occupée par les poids peut libérer de la capacité GPU pour le cache KV.
De même, réduire la précision du cache KV peut laisser de la place pour davantage de requêtes sans modifier les poids du modèle.
Cette interaction explique pourquoi une configuration d'inférence efficace doit prendre en compte l'ensemble du budget mémoire.
Je procéderais dans cet ordre :
Cette séquence est plus fiable que de choisir un modèle simplement parce que son fichier de poids correspond à la capacité.
Un GPU de 32 Go peut être un appareil d'inférence très performant.
Sa limite reste de 32 Go.
Si un modèle quantifié occupe :
16 Go
cela ne signifie pas que vous disposez de 16 Go supplémentaires uniquement pour le contexte.
Le moteur d'exécution a également besoin d'une partie de cet espace.
Mais cela signifie que la mémoire restante peut prendre en charge un cache significatif.
C'est pourquoi un modèle 27B quantifié peut constituer une charge de travail intéressante pour un seul GPU, tandis qu'un modèle 70B nécessite généralement une mise en service multi-GPU.
Notre guide Qwen3.6-27B illustre le premier cas.
Notre guide Llama 3.3 70B illustre le second.
Et notre prochain guide sur le parallélisme tensoriel expliquera ce qu'il advient du problème de mémoire une fois que ces poids et caches sont répartis sur plusieurs GPU.
Un contexte long est utile.
Il vous permet de fournir :
des documents plus volumineux
un historique de conversation plus riche
davantage de passages extraits
des bases de code plus étendues
plus d'exemples
Mais chaque jeton supplémentaire peut entraîner des coûts en termes de :
mémoire
temps de préremplissage
travail d'attention
latence
capacité de service
Si votre application fonctionne bien avec 16 000 jetons, la configurer pour 128 000 ne la rendra pas huit fois plus performante.
Cela pourrait simplement imposer au serveur un scénario bien plus coûteux.
Concevez votre système en fonction des besoins réels des utilisateurs.
Une grande fenêtre de contexte est tentante :
Nous avons 128 000 jetons, alors mettons tout dans le prompt.
C'est souvent une mauvaise conception système.
Si seuls cinq paragraphes d'une base de connaissances de 500 pages sont pertinents pour répondre à une question, extraire ces passages est plus efficace que de préremplir systématiquement d'énormes quantités de texte inutile.
C'est l'une des raisons pour lesquelles le RAG avec Hivenet et l'inférence à long contexte résolvent des problèmes connexes mais distincts.
Le long contexte offre de l'espace au modèle.
La recherche d'informations détermine ce qui mérite d'occuper cet espace.
Les deux approches peuvent fonctionner de concert.
Le cache KV est la mémoire de travail du modèle pour l'attention lors de la génération.
Cela vous permet de gagner en vitesse, car les états d'attention précédents n'ont pas besoin d'être recréés à chaque apparition d'un nouveau jeton.
Mais ces états doivent être stockés quelque part.
Chaque décision de service devient donc un compromis :
contexte plus long
↔
plus de mémoire cache
concurrence accrue
↔
plus de mémoire cache
précision de cache supérieure
↔
plus de mémoire cache
plus de têtes KV
↔
plus de mémoire cache
Et inversement :
GQA
cache FP8
réutilisation de préfixe
allocation paginée
attention glissante
peut rendre cette mémoire plus facile à utiliser.
L'important est de ne plus considérer la longueur du contexte comme un chiffre appartenant uniquement au modèle.
Une fois le modèle en cours d'exécution, la longueur du contexte devient également une décision d'infrastructure.
Un cache KV stocke les tenseurs de clé et de valeur produits par les couches d'attention pour les jetons que le modèle a déjà traités. Lors de la génération autorégressive, le modèle réutilise ces tenseurs au lieu de les recalculer pour chaque nouveau jeton.
KV signifie clé-valeur, en référence aux vecteurs de clé et de valeur utilisés par l'attention des transformeurs.
Sans mise en cache, le modèle devrait recalculer à plusieurs reprises les états de clé et de valeur des jetons précédents à chaque génération d'un nouveau jeton. La mise en cache permet d'échanger de la mémoire supplémentaire contre une réduction substantielle des calculs répétés lors de l'inférence.
Pour une inférence résidant sur GPU, le cache KV est normalement conservé dans la mémoire du GPU pour un accès rapide. Certains frameworks prennent également en charge le déchargement du cache ou d'autres stratégies de mémoire lorsque la VRAM est limitée.
Oui, pour les caches dynamiques à attention complète standard. Chaque jeton actif supplémentaire ajoute un état de clé et de valeur dans les couches d'attention pertinentes du modèle. Les architectures à fenêtre glissante et autres architectures spécialisées peuvent modifier ce comportement.
Pour un transformeur conventionnel à attention complète, une approximation utile est :
jetons
× couches
× 2
× têtes KV
× dimension de tête
× octets par élément
Multipliez par le nombre de séquences simultanées lors de l'estimation de plusieurs séquences indépendantes de même longueur.
Parce que chaque jeton mis en cache stocke à la fois un tenseur clé et un tenseur valeur à chaque couche d'attention pertinente.
Non. La taille du cache KV dépend plus directement de l'architecture d'attention, du nombre de couches, du nombre de têtes KV, de la dimension de tête, de la longueur de séquence, de la précision du cache et de la simultanéité.
Pas automatiquement. La précision des poids du modèle et celle du cache KV sont des paramètres distincts. Un modèle 4 bits peut toujours utiliser un cache KV en BF16 ou FP16.
Oui. Les versions actuelles de vLLM prennent en charge les formats de cache KV en FP8 en plus des formats de plus haute précision.
Par rapport au BF16 ou au FP16, le FP8 utilise environ deux fois moins d'octets bruts par valeur mise en cache. Les économies réelles de mémoire GPU totale dépendent du modèle et du reste de la pile de service.
Il améliore principalement la manière dont la mémoire cache est allouée et partagée, réduisant ainsi la fragmentation et le gaspillage. Il ne supprime pas l'état clé/valeur par jeton requis par l'architecture d'attention.
La mise en cache de préfixe permet à un moteur de service de réutiliser des blocs de cache KV précédemment calculés lorsqu'une autre requête commence par le même préfixe de jeton éligible. Cela permet d'éviter la duplication des calculs de pré-remplissage et de l'état du cache.
Non. Cela signifie que le modèle prend en charge des séquences allant jusqu'à cette limite selon ses spécifications. La limite d'exécution utile dépend de la VRAM, de la taille du cache KV, de la concurrence, de la latence et des besoins réels en contexte de votre application.
Oui. Les jetons de prompt sont traités lors du pré-remplissage, et leurs états clé/valeur sont ensuite utilisés lors du décodage ultérieur.
Oui. Chaque jeton généré ajoute ses propres états clé et valeur au cache actif pour les étapes de décodage suivantes.
Le chargement prouve que ses poids et ses allocations initiales à l'exécution tiennent en mémoire. Des contextes plus longs ou des requêtes simultanées supplémentaires peuvent ensuite consommer davantage de mémoire cache KV jusqu'à ce que le GPU n'ait plus assez de capacité.
Oui. Le Grouped-Query Attention utilise moins de têtes clé/valeur que de têtes de requête, ce qui réduit la quantité d'états clé/valeur à stocker. Meta utilise le GQA dans Llama 3.1 et Llama 3.3 spécifiquement pour améliorer l'évolutivité de l'inférence.
Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.