La quantization consiste à représenter les poids (et parfois les activations) d'un réseau de neurones avec moins de bits : passer de 32 bits par paramètre à 8, 4, voire 2 bits. Pour un grand modèle de langage, l'enjeu n'est pas anecdotique — un modèle 7B en FP32 occupe 28 Go rien que pour ses poids, ce qui le rend impossible à charger sur la plupart des GPU grand public. Cet article explique les formats numériques en jeu, pourquoi l'inférence des LLM est limitée par la bande passante mémoire, les grandes familles de méthodes (GPTQ, AWQ, SmoothQuant, GGUF, NF4), et les techniques de service (batching continu, PagedAttention, décodage spéculatif) qui transforment un modèle quantifié en service à haut débit.
Pourquoi quantifier : mémoire et bande passante
Un poids stocké en virgule flottante 32 bits (FP32) coûte 4 octets. Un modèle de N paramètres pèse donc 4 × N octets en FP32 ; en demi-précision (16 bits) la moitié, en INT8 le quart, en INT4 le huitième. La règle mentale utile est simple : mémoire des poids ≈ N × (bits / 8).
| Format | Octets/poids | Mémoire d'un 7B |
|---|---|---|
| FP32 | 4 | 28 Go |
| FP16 / BF16 | 2 | 14 Go |
| FP8 / INT8 | 1 | 7 Go |
| INT4 | 0,5 | 3,5 Go |
| INT2 | 0,25 | 1,75 Go |
Figure : formats numériques et mémoire. Chaque ligne montre la répartition des bits et la mémoire correspondante pour un modèle de 7 milliards de paramètres.
Mais la mémoire n'est que la moitié de l'histoire. Le vrai gain à l'inférence vient de la bande passante. Pour produire un seul token de sortie, le GPU doit relire l'intégralité des poids du modèle depuis la VRAM vers les unités de calcul. À chaque pas de décodage, on transfère donc taille du modèle octets — diviser cette taille par 2 ou par 4 divise d'autant le trafic mémoire, qui est le goulot d'étranglement dominant. Quantifier, c'est avant tout lire moins d'octets par token.
Intensité arithmétique et roofline
Pour formaliser ce goulot, on raisonne en intensité arithmétique : le nombre d'opérations flottantes (FLOPs) effectuées par octet lu en mémoire. Le modèle roofline dit qu'une charge est limitée par le calcul si son intensité dépasse le ratio FLOPs crête / bande passante crête du GPU, et limitée par la mémoire sinon. Sur un H100, ce ratio « pivot » se situe autour de 300 FLOPs/octet.
Or, le décodage d'un LLM avec un lot de taille 1 a une intensité arithmétique proche de 2 FLOPs/octet (chaque poids lu sert à une multiplication-addition, soit 2 FLOPs, pour ~2 octets en FP16). On est donc très loin à gauche du pivot : le GPU est massivement sous-utilisé côté calcul et passe son temps à attendre la VRAM. Deux leviers déplacent le curseur vers la droite : quantifier (moins d'octets par poids) et batcher (réutiliser chaque poids lu pour plusieurs requêtes, ce qui augmente directement l'intensité).
Les formats numériques en détail
Un nombre flottant se décompose en trois champs : signe (1 bit), exposant (la plage) et mantisse (la précision). Les arbitrages diffèrent selon le format :
- FP32 — 1 signe + 8 exposant + 23 mantisse. La référence « pleine précision », utilisée à l'entraînement historique.
- FP16 — 1 + 5 + 10. Demi-précision « IEEE ». Bonne précision relative mais plage d'exposant étroite (5 bits) : risque d'overflow/underflow sur de grandes activations.
- BF16 (bfloat16) — 1 + 8 + 7. Même plage d'exposant que FP32 (8 bits) mais mantisse réduite. C'est devenu le format d'entraînement et d'inférence par défaut sur les LLM : il « plante » beaucoup moins que FP16 sur les valeurs extrêmes.
- FP8 (E4M3 ou E5M2) — 8 bits flottants, supportés nativement par les GPU récents (Hopper/Blackwell). Deux variantes coexistent : E4M3 (4 bits d'exposant, 3 de mantisse) privilégie la précision et sert aux poids et activations ; E5M2 (5 exposant, 2 mantisse) privilégie la plage dynamique et sert souvent aux gradients. Bon compromis quand le matériel a des unités dédiées : on garde un comportement « flottant » (pas de zero-point) tout en doublant le débit des tensor cores.
- INT8 / INT4 — entiers. Pas d'exposant : on encode un réel
xparx ≈ scale × qoùqest un entier etscaleun facteur d'échelle flottant. Le choix duscale(et d'un éventuelzero-point) est tout l'art de la quantization entière. - INT2 / ternaire — l'extrême de la compression. À 2 bits (voire 1,58 bit pour les schémas ternaires {−1, 0, +1}), la mémoire s'effondre mais la qualité aussi, sauf entraînement dédié (QAT). Réservé à la recherche et aux contraintes matérielles extrêmes.
L'équation de base de la quantization entière affine est :
q = round(x / scale) + zero_point (quantize)
x ≈ scale × (q − zero_point) (dequantize)
Deux variantes coexistent :
- Symétrique :
zero_point = 0, la plage est centrée sur 0 ([−s·2^{b−1}, s·(2^{b−1}−1)]). Plus simple et un peu plus rapide. - Asymétrique (affine) : un
zero_pointnon nul aligne le zéro réel sur un entier. Indispensable pour les distributions décentrées (sorties de ReLU, GELU…).
La granularité du scale est cruciale :
- Par tenseur : un seul scale pour toute la matrice. Compact, mais grossier.
- Par canal : un scale par ligne (par sortie). Bien meilleur, peu de surcoût.
- Par groupe : un scale par bloc de 32, 64 ou 128 poids contigus. C'est le réglage des méthodes 4 bits sérieuses ; le
group_size = 128est le standard de fait.
Plus la granularité est fine, mieux on capture les variations locales d'amplitude — au prix de quelques métadonnées supplémentaires (les scales et zero-points). Un petit calcul : pour des poids 4 bits avec group_size = 128 et un scale FP16 par groupe, le surcoût des scales est de 16 bits / 128 poids ≈ 0,125 bit/poids, soit ~3 % de plus que les 4 bits nominaux. C'est pourquoi on parle parfois de « 4,5 bits effectifs » pour ces formats : la taille réelle inclut toujours les métadonnées.
Erreur de quantization et clipping
Deux sources d'erreur cohabitent. L'erreur d'arrondi (chaque réel est arrondi au niveau le plus proche) vaut au pire scale / 2 ; elle diminue quand on raffine le scale. L'erreur de clipping (saturation) apparaît quand on borne la plage pour mieux résoudre le centre de la distribution, au prix de l'écrasement des extrêmes. Trouver le scale optimal, c'est arbitrer entre les deux : un scale trop grand augmente l'arrondi, un scale trop petit augmente le clipping. Les méthodes sérieuses cherchent ce point en minimisant une erreur de reconstruction (souvent l'erreur quadratique sur la sortie de la couche, pas sur les poids eux-mêmes).
Weight-only vs weight + activation
Deux grandes familles s'opposent selon ce qu'on quantifie :
- Weight-only (poids seuls) : on compresse les poids en INT4/INT8, mais les activations restent en FP16/BF16, et les calculs se font en flottant après déquantization à la volée. C'est la voie reine pour le décodage (limité par la bande passante) : on lit moins d'octets de poids, on garde la précision des activations. GPTQ et AWQ relèvent de cette famille (W4A16 : poids 4 bits, activations 16 bits).
- Weight + activation : on quantifie aussi les activations (par ex. W8A8 : poids et activations en INT8). On exploite alors les unités de calcul entières (tensor cores INT8), ce qui accélère les phases compute-bound comme le prefill et le service à fort débit. Mais c'est beaucoup plus dur, à cause des outliers d'activation.
La notation WxAy résume tout cela : x bits pour les poids, y pour les activations. W4A16 (poids 4 bits, activations 16 bits) est weight-only ; W8A8 et W4A8 sont des schémas weight + activation. Le choix dépend de la phase qu'on optimise et du régime de batch.
Les kernels mixtes : le coût caché de la déquantization
Le weight-only n'est gagnant que si la déquantization à la volée ne mange pas le gain de bande passante. C'est le rôle des kernels GEMM mixtes (Marlin pour W4A16/W8A16, Machete sur Hopper) : ils multiplient des activations 16 bits par des poids 4 ou 8 bits en recouvrant la latence de chargement et de déquantization avec le calcul flottant, de sorte que la lecture compressée se traduit réellement en accélération. À faible batch, un GPTQ packé pour Marlin peut dépasser AWQ de ~25 %, et de près de 50 % à plus gros batch ; mais à très grand batch, on redevient compute-bound et le FP16 finit par rattraper. Morale : un format n'a de valeur que par le kernel qui le sert.
Le problème des outliers d'activation
C'est l'obstacle central de la quantization d'activations.
Dans les grands transformeurs, une poignée de canaux (de l'ordre de 0,1 à 1 % des dimensions cachées) portent des activations d'amplitude 100 à 1000× supérieure au reste. Le dilemme est sans issue avec un scale naïf :
- Si l'on choisit un scale assez grand pour couvrir ces valeurs extrêmes, tous les canaux « normaux » s'effondrent sur quelques niveaux de quantization, et la précision s'écroule.
- Si l'on choisit un scale fin, les outliers saturent (clipping) et l'information est perdue.
Les poids, eux, sont beaucoup plus « sages » : leur distribution est proche d'une gaussienne, sans valeurs aberrantes massives. C'est pourquoi la quantization weight-only est nettement plus facile que la quantization d'activations. C'est aussi ce dilemme des outliers que les méthodes modernes (AWQ, SmoothQuant) résolvent, chacune à sa façon.
PTQ vs QAT
Deux régimes pour produire un modèle quantifié :
- PTQ (Post-Training Quantization) : on quantifie après l'entraînement, sans rétro-propagation. Rapide (minutes à heures), c'est l'option par défaut. La plupart des méthodes utilisent un petit jeu de calibration (quelques centaines de phrases) pour estimer les plages d'activation et minimiser l'erreur de reconstruction.
- QAT (Quantization-Aware Training) : on simule la quantization pendant l'entraînement (ou un fine-tuning), pour que le modèle apprenne à être robuste à la perte de précision. Meilleure qualité en très basse précision (2-3 bits), mais coûteux : il faut des données, du calcul et du temps.
En pratique, à 8 et 4 bits, la PTQ suffit dans l'écrasante majorité des cas ; la QAT se réserve aux régimes très agressifs ou aux contraintes matérielles strictes.
Les méthodes PTQ qui comptent
GPTQ (2022) — weight-only, INT3/INT4. Méthode one-shot par couche : elle quantifie les poids colonne par colonne en utilisant une approximation de la Hessienne inverse pour minimiser l'erreur de reconstruction, en ajustant les poids restants à chaque étape. Concrètement, après avoir figé une colonne à sa valeur quantifiée, l'erreur introduite est propagée sur les colonnes encore en flottant, qui « compensent » — d'où une dégradation bien moindre qu'un arrondi naïf. Résultat : on peut faire tenir un modèle 175B sur un seul A100 et obtenir ~3,25× d'accélération vs FP16, avec une perplexité quasi inchangée.
AWQ (Activation-aware Weight Quantization, 2023) — weight-only, INT4. Constat clé : tous les poids ne se valent pas. Une faible fraction de poids « saillants » (repérés via la magnitude des activations, pas des poids) domine la qualité. Au lieu de garder ces poids en pleine précision (ce qui casse l'efficacité matérielle des formats mixtes), AWQ met à l'échelle par canal : il multiplie les canaux importants par un facteur s > 1 avant quantization et divise l'activation correspondante par s, transformation mathématiquement neutre qui réduit l'erreur relative sur les poids saillants. Pas de rétro-propagation ni de Hessienne, juste une recherche du s optimal par grille sur le jeu de calibration. C'est devenu un standard de fait pour le déploiement INT4, notamment côté périphérie/edge.
SmoothQuant (2022) — weight + activation, W8A8. Plutôt que de subir les outliers d'activation, on les « lisse » : par une transformation mathématiquement équivalente, on déplace une partie de la difficulté des activations vers les poids (plus faciles à quantifier). Résultat annoncé : jusqu'à 2× de réduction mémoire et ~1,56× d'accélération, sans entraînement.
GGUF / k-quants (llama.cpp) — l'écosystème CPU/Apple Silicon. Les k-quants (Q2_K à Q6_K) organisent les poids en super-blocs de 256 valeurs subdivisés, avec une double quantization des échelles (on quantifie les scales eux-mêmes) pour réduire le surcoût de métadonnées. Repères pratiques sur Llama-3-8B : Q4_K_M (~4,5 Go, +0,18 de perplexité) est le choix « par défaut » qualité/taille, Q5_K_M (~5,3 Go, +0,06) quand la qualité prime.
bitsandbytes NF4 — le format de QLoRA. NF4 (4-bit NormalFloat) est un type de données information-théoriquement optimal pour des poids distribués normalement : ses 16 niveaux sont placés sur les quantiles d'une loi normale (chaque « casier » reçoit la même masse de probabilité) plutôt qu'uniformément. Couplé à la double quantization — on quantifie en 8 bits les constantes de quantization (un scale FP32 par bloc de 64), ce qui économise ~0,4 bit/poids — NF4 égale les performances de BF16 tout en permettant le QLoRA : fine-tuner un modèle gelé en 4 bits via des adaptateurs LoRA, avec une empreinte mémoire 20× à 60× plus faible que le fine-tuning complet. Le gradient ne traverse les poids 4 bits (déquantifiés à la volée) que pour atteindre les petits adaptateurs entraînables.
Un exemple chiffré de gain mémoire au fine-tuning
Sur un 7B, le fine-tuning complet en BF16 demande non seulement les poids (~14 Go) mais aussi les gradients (~14 Go) et les états de l'optimiseur Adam (deux moments, ~28 Go en FP32), soit ~56 Go avant même les activations — hors de portée d'un seul GPU 24 Go. QLoRA renverse l'équation : poids gelés en NF4 (~3,5 Go), aucun gradient ni état d'optimiseur sur les poids du modèle, et seuls les adaptateurs LoRA (souvent < 1 % des paramètres) portent gradients et moments. Le total tient confortablement sur un GPU 24 Go, voire moins.
En résumé, on peut classer les méthodes ainsi :
- Weight-only INT4 sur GPU : GPTQ, AWQ.
- Weight + activation sur GPU : SmoothQuant (W8A8), FP8.
- CPU / Apple Silicon / edge : GGUF (k-quants).
- Fine-tuning économe : NF4 (QLoRA).
Le rôle du jeu de calibration
La plupart des méthodes PTQ ont besoin de quelques centaines d'exemples pour estimer les plages d'activation. Ce jeu de calibration influence le résultat :
- Il doit être représentatif du domaine d'usage (du code si vous servez du code, du texte multilingue si vous servez plusieurs langues).
- Sa taille raisonnable est de l'ordre de 128 à 512 séquences ; au-delà, le gain est marginal.
- Un jeu inadapté peut dégrader silencieusement la qualité sur votre tâche réelle.
À noter : des travaux récents montrent que, sur les LLM modernes, l'influence des outliers et du choix du jeu de calibration tend à diminuer — mais la prudence reste de mise.
Quantifier aussi le KV cache
Sur des contextes longs, le KV cache (les clés/valeurs d'attention de tous les tokens) peut peser autant que le modèle lui-même, voire davantage. Sa taille suit une formule simple :
octets_KV = 2 × n_couches × n_têtes_kv × dim_tête × longueur × octets_par_élément
Le facteur 2 couvre les clés et les valeurs. Sur un modèle type 7B (32 couches, dimension cachée 4096) en FP16, cela représente environ 0,5 Mo par token, soit ~2 Go pour un contexte de 4096 tokens — et cela par requête. C'est pourquoi l'attention multi-requêtes (MQA) et l'attention par groupes (GQA), qui réduisent n_têtes_kv, sont devenues standards : elles divisent directement le poids du KV cache.
Quantifier le cache (KV cache en FP8 ou INT8) :
- libère de la mémoire pour des lots plus gros ou des contextes plus longs ;
- réduit la bande passante à relire à chaque pas de décodage.
C'est un levier distinct de la quantization des poids, souvent combiné avec elle dans les moteurs de service récents.
De la compression au débit : l'inférence en service
Compresser le modèle ne suffit pas : il faut un moteur de service qui sature le GPU. Première clé : comprendre que l'inférence a deux phases aux profils opposés.
- Prefill : on traite tout le prompt d'un coup. Beaucoup de FLOPs en parallèle → limité par le calcul (compute-bound).
- Decode : on génère les tokens un par un, en relisant tous les poids (et le KV cache) à chaque pas pour produire un seul token → limité par la bande passante mémoire (memory-bandwidth-bound).
Cette dissymétrie explique tout le reste. En decode, le GPU passe son temps à attendre la mémoire, pas à calculer. La parade : mettre plusieurs requêtes en lot pour amortir chaque lecture de poids sur de nombreux tokens à la fois.
Batching continu et PagedAttention
Le batching continu (continuous / in-flight batching) entrelace dynamiquement les requêtes : dès qu'une génération se termine, une nouvelle requête entre dans le lot, sans attendre que tout le lot finisse. Le planificateur mêle dans un même pas du prefill (requêtes en attente) et du decode (requêtes en cours). Gain typique : 3 à 10× de débit sur le même matériel.
Pour batcher efficacement, il faut gérer le KV cache (les clés/valeurs d'attention de tous les tokens déjà vus) sans le gaspiller. PagedAttention s'inspire de la mémoire virtuelle des OS : le KV cache est découpé en blocs de taille fixe (par défaut 16 tokens), alloués à la demande et non contigus. On élimine ainsi la fragmentation (gain de ~19-27 % de mémoire), on autorise des lots plus gros, et on partage facilement des préfixes communs entre requêtes.
Le prefill segmenté (chunked prefill) complète l'ensemble : un très long prompt est découpé en morceaux sur plusieurs pas du moteur, pour qu'il ne monopolise pas le GPU au détriment des requêtes en cours de décodage. Sans lui, un prompt de 100 000 tokens bloquerait toutes les autres générations le temps de son prefill ; avec lui, le moteur entrelace des morceaux de prefill et des pas de decode, lissant la latence inter-tokens.
Caching de préfixes
Quand plusieurs requêtes partagent un même début — un long system prompt, un few-shot commun, un document en RAG — recalculer leur KV cache à chaque fois est du gaspillage pur. Le caching de préfixes (prefix caching) conserve et réutilise le KV cache du préfixe commun : seule la partie variable est recalculée. PagedAttention rend ce partage trivial (les blocs du préfixe sont simplement référencés par plusieurs séquences), et SGLang en fait un art avec RadixAttention, qui organise les préfixes en arbre radix pour maximiser les recouvrements. Sur des charges à fort partage, le gain de latence du time-to-first-token est spectaculaire.
Décodage spéculatif : plusieurs tokens par passage
Le decode est séquentiel par nature : token t+1 dépend de t. Le décodage spéculatif brise cette barrière sans changer la sortie. L'idée : un petit modèle brouillon (rapide) propose k tokens d'avance ; le grand modèle cible les vérifie tous en une seule passe parallèle, accepte le plus long préfixe correct et corrige le premier token rejeté. Comme la vérification est exacte, la sortie est strictement identique à un décodage classique — on ne paie aucune perte de qualité, on gagne juste plusieurs tokens par passage du grand modèle.
Figure : flux draft + verify du décodage spéculatif. Le débit dépend du taux d'acceptation des tokens proposés.
Plusieurs variantes évitent même d'avoir un modèle brouillon séparé :
- n-gram / prompt lookup : on cherche dans le texte déjà généré une occurrence antérieure du dernier n-gramme et on propose les tokens qui suivaient. Quasi gratuit, très efficace sur du texte répétitif (code, JSON, RAG).
- Medusa : on greffe sur le grand modèle plusieurs têtes légères qui prédisent les tokens
+1, +2, +3…en parallèle, vérifiés via un arbre de candidats. - EAGLE : on prédit au niveau des features (états cachés) plutôt que des tokens, pour un taux d'acceptation plus élevé. EAGLE-2 y ajoute un arbre de brouillon dynamique : plutôt qu'une structure fixe, il étend l'arbre en fonction des scores de confiance (le taux d'acceptation dépend du contexte, pas seulement de la position), gagnant 20-40 % sur EAGLE-1. EAGLE-3 exploite plusieurs couches cachées intermédiaires et relâche la contrainte de prédiction de features, repoussant encore les taux d'acceptation (accélérations annoncées de l'ordre de 3-5×).
Pourquoi le taux d'acceptation est tout
Le gain dépend entièrement de la fraction de tokens proposés que le modèle cible accepte. Si le brouillon propose k tokens et que le taux d'acceptation moyen est α, le nombre espéré de tokens validés par passage du grand modèle vaut environ (1 − α^{k+1}) / (1 − α). À α = 0,8 et k = 4, on valide ~3,4 tokens par passage au lieu d'un seul — d'où l'accélération. Mais chaque passage du grand modèle reste plus coûteux (vérification de k+1 positions) et le brouillon a son propre coût : si α est faible, le décodage spéculatif peut ralentir le service. D'où l'importance d'un brouillon bien aligné sur la cible.
Le décodage spéculatif brille surtout à faible concurrence (quand la latence par requête prime) ; à fort débit, le batching continu remplit déjà le GPU et le gain marginal diminue.
Passer à l'échelle : tensor parallelism
Quand un modèle ne tient pas sur un GPU (ou pour réduire la latence), on le découpe sur plusieurs GPU. Le tensor parallelism (TP) répartit chaque matrice de poids entre les rangs : avec TP=8, chaque GPU détient 1/8 des poids et calcule sa part, puis les rangs se synchronisent (all-reduce) à chaque couche. C'est complémentaire de la quantization : on réduit d'abord la taille par GPU, puis on parallélise ce qui reste.
Les moteurs de service
Ces techniques sont packagées dans des moteurs prêts à l'emploi :
- vLLM — référence open-source, PagedAttention « maison », batching continu, large support de quantization (GPTQ, AWQ, FP8…), décodage spéculatif.
- TensorRT-LLM (NVIDIA) — performances de pointe sur GPU NVIDIA, kernels optimisés, FP8 natif.
- TGI (Text Generation Inference, Hugging Face) — service prêt à l'emploi, bonne intégration de l'écosystème.
- SGLang — orienté programmes LLM structurés, caching de préfixes (RadixAttention) très efficace.
- llama.cpp — l'incontournable côté CPU / Apple Silicon / edge, moteur des GGUF.
Mesurer le compromis précision / taille
On ne quantifie jamais à l'aveugle. Deux familles de mesures :
- Perplexité (PPL) — mesure intrinsèque sur un corpus de référence (WikiText, C4) : à quel point le modèle est « surpris » par le texte réel. Rapide à calculer, utile pour repérer une dégradation grossière, mais une faible hausse de PPL ne garantit pas que les capacités sont préservées.
- Évaluations de tâches — benchmarks réels (MMLU, GSM8K, HumanEval, etc.) qui mesurent ce qui compte vraiment : raisonnement, code, connaissances. Plus lents, mais c'est la vérité terrain.
Quelques repères empiriques robustes : INT8 et W4A16 (GPTQ/AWQ, group 128) dégradent en général très peu (souvent < 1 point sur les évals) ; sous 4 bits la qualité chute plus vite ; et toujours mesurer sur sa propre tâche et sa propre langue — un modèle quantifié peut bien tenir en anglais et se dégrader davantage sur une langue moins représentée.
Un calcul de budget mémoire
Avant de choisir un format, un calcul de coin de table évite les mauvaises surprises. Voici une estimation grossière de la mémoire des poids :
def weight_memory_gb(params_billions: float, bits: int) -> float:
bytes_per_param = bits / 8
total_bytes = params_billions * 1e9 * bytes_per_param
return total_bytes / (1024 ** 3)
for bits in (16, 8, 4):
gb = weight_memory_gb(7, bits)
print(f"7B @ {bits} bits : {gb:.1f} Go")
# 7B @ 16 bits : 13.0 Go
# 7B @ 8 bits : 6.5 Go
# 7B @ 4 bits : 3.3 Go
À cela s'ajoutent le KV cache (proportionnel à batch × contexte) et un peu d'overhead. Un GPU de 24 Go accueille confortablement un 7B en 4 bits avec un long contexte, ou un 13B serré.
Pièges fréquents
- Quantifier puis ne pas mesurer : la perplexité seule ne suffit pas, vérifiez les évals de tâches.
- Jeu de calibration non représentatif : il fausse l'estimation des plages d'activation.
- Mauvaise langue de test : valider uniquement en anglais masque une dégradation sur d'autres langues.
- Granularité trop grossière : un scale par tenseur en 4 bits écrase souvent la qualité ; préférez
group_size = 128. - Oublier le KV cache : sur contexte long, il peut saturer la VRAM même avec des poids compressés.
- Croire le décodage spéculatif gratuit à fort débit : son gain s'estompe quand le batching continu remplit déjà le GPU.
Recette pratique
- Démarrer en BF16 pour valider le comportement, puis quantifier.
- GPU, latence faible : weight-only INT4 (AWQ ou GPTQ,
group_size=128) + vLLM/TensorRT-LLM ; activer le décodage spéculatif si la concurrence est faible. - GPU, fort débit : envisager W8A8 (SmoothQuant) ou FP8 pour exploiter les tensor cores entiers, avec batching continu.
- CPU / Mac / edge : GGUF, viser Q4_K_M par défaut, Q5_K_M si la qualité prime.
- Fine-tuning à petit budget : QLoRA (NF4 + double quantization + adaptateurs LoRA).
- Toujours : valider avec perplexité et évals de tâches, sur vos données et vos langues, avant de figer le choix.
En somme, la quantization s'attaque au goulot mémoire (moins d'octets par token), tandis que les techniques de service (batching continu, PagedAttention, décodage spéculatif, parallélisme) saturent le GPU et amortissent chaque lecture de poids. C'est la combinaison des deux — modèle compressé et moteur efficace — qui fait passer un LLM d'une démo coûteuse à un service viable à grande échelle.