Standards architecturaux et meilleures pratiques pour le déploiement de LLM en production. Optimisation de l'inférence via quantization, dimensionnement VRAM sur GPU (H100, A100, RTX), choix des moteurs d'inférence (TRT-LLM, vLLM, llama.cpp), et arbre de décision pour le déploiement.
Statut : Validé | Cible : Ingénieurs MLOps, AI Architects, DevOps
Ce document documente les standards architecturaux et les meilleures pratiques pour le déploiement de Large Language Models (LLMs) en environnement de production. Il se concentre sur l'optimisation de l'inférence via la quantization, le dimensionnement de la VRAM sur GPU (NVIDIA H100, A100, RTX), et le choix des moteurs d'inférence (TRT-LLM, vLLM, llama.cpp).
L'arbre de décision ci-dessous est le cadre que j'applique en mission : il m'a mené au chemin "GPU haute densité" (vLLM sur 2×H100) pour un assistant 70B en production, au chemin "CPU Only" (llama.cpp GGUF) pour la V1 de MIRROR, puis au chemin "API-first" pour sa V2 - trois réponses différentes au même problème de dimensionnement.
Pour maitriser la quantization, il est impératif de comprendre la répartition de l'empreinte mémoire d'un modèle lors de l'inférence. L'occupation de la VRAM se divise en trois piliers :
L'estimation de la mémoire allouée au KV-cache se calcule selon :
KV_bytes ≈ batch_size × sequence_length × num_layers × (2 × hidden_size) × (bytes_per_dtype / gqa_factor)
Exemple concret : pour Llama 3 70B en FP16, avec un batch de 4 et 8192 tokens de contexte, le KV-cache consomme environ 20 Go de VRAM en plus des poids du modèle.
Indépendamment du format numérique, PagedAttention (introduit par vLLM) optimise l'usage du KV-cache en le découpant en pages non contiguës. Cela réduit le gaspillage mémoire lié à la fragmentation de 60-80% à moins de 4%, permettant de maximiser l'in-flight batching.
La règle d'or du dimensionnement repose sur la conversion du nombre de paramètres en octets :
Attention : toujours ajouter une marge de 15% à 20% pour le KV-cache et les activations.
| Modèle cible | Précision | VRAM Poids | VRAM Totale (avec contexte) | GPU minimum recommandé | Gain vs FP16 |
|---|---|---|---|---|---|
| 8B (Llama 3 8B) | FP16 (Base) | ~16 Go | ~18-20 Go | 1x RTX 3090/4090 (24 Go) | Ref. |
| 8B | FP8 / INT8 | ~8 Go | ~10-12 Go | 1x RTX 4060 Ti (16 Go) ou T4 | -50% |
| 8B | INT4 (AWQ/GGUF) | ~4 Go | ~6-8 Go | 1x RTX 3060 (12 Go) | -75% |
| 70B (Llama 3 70B) | FP16 (Base) | ~140 Go | ~160 Go | 2x H100 (80 Go) | Ref. |
| 70B | FP8 / INT8 | ~70 Go | ~78 Go | 1x H100 (80 Go) | -50% |
| 70B | INT4 (AWQ/GGUF) | ~35 Go | ~42 Go | 1x A6000 (48 Go) | -75% |
Plusieurs approches Post-Training Quantization (PTQ) dominent l'écosystème :
SmoothQuant (INT8 W8A8) : lisse les valeurs extrêmes (outliers) des activations en transférant une partie de leur amplitude vers les poids via un rescaling. Permet une inférence 8-bit stable sans ré-entrainement, idéale sur architectures Ampere/Hopper. (Xiao et al., ICML 2023)
AWQ (Activation-aware Weight Quantization, INT4) : identifie et protège le ~1% des poids les plus critiques (stockés en FP16), et quantifie les 99% restants en 4 bits. Offre le meilleur ratio qualité/compression pour des environnements contraints en VRAM. (Lin et al., MLSys 2024)
GPTQ (INT3/INT4) : méthode one-shot utilisant l'approximation Hessienne pour compenser l'erreur de quantization bloc par bloc. Très populaire dans la sphère open-source. (Frantar et al., ICLR 2023)
GGUF (llama.cpp) : format de quantization granulaire avec de nombreuses variantes (Q2_K, Q3_K_M, Q4_K_M, Q5_K_M, Q6_K, Q8_0). Chaque variante offre un compromis différent entre taille et qualité. Q4_K_M est le sweet spot pour la plupart des usages CPU.
Compilateur et runtime ultra-optimisé spécifique aux GPU NVIDIA. - Avantages : support FP8 natif sur H100, W8A8, AWQ, in-flight batching. Délivre le débit maximal et la latence minimale absolus (jusqu'a 4.6x plus de throughput sur H100 vs A100). - Inconvénients : verrouillage matériel (CUDA uniquement), nécessite une compilation (build engine) préalable.
Serveur Python/C++ développé par UC Berkeley, standard de l'industrie. - Avantages : PagedAttention native, support dynamique du FP8/INT8, chargement à la volée. API Python compatible OpenAI. - Inconvénients : latence pure sur une seule requête très légèrement supérieure à TRT-LLM, bien que le débit sous forte charge soit exceptionnel.
Implémentation minimaliste en C/C++ pour l'exécution locale. - Avantages : agnostique au matériel (CPU, Apple Silicon, petits GPU). Supporte des formats de quantization très granulaires (Q4_K_M, Q8_0). SIMD acceleration (AVX2/AVX-512). - Inconvénients : ne tire pas pleinement parti des Tensor Cores industriels. Déconseillé pour exploiter des serveurs data center à forte charge.
--quantization fp8)Bénéfice : permet de faire tenir un 70B sur un seul H100 avec une vitesse et une qualité > 99% préservée
Le Choix Universel (A100 / GPU standards)
--quantization int8)Bénéfice : idéal si le FP8 natif n'est pas supporté
Haute Densité / Budget Réduit
--quantization awq)Bénéfice : divise les coûts de VRAM par 4. Parfait pour les chatbots internes
CPU Only / Edge
| Métrique Cible | Seuil d'Alerte (Exemple) | Action Corrective |
|---|---|---|
| Latence (Time To First Token) | p99 > 500ms | Scale-out ou bascule vers profil INT4 |
| Gaspillage KV-Cache | > 8% | Vérifier configuration PagedAttention |
| Erreurs OOM | > 0.5% des requêtes | Réduire le contexte max ou KV en FP8 |
| Throughput | < SLA (tokens/s) | Ajouter des replicas ou passer en FP8 |
La quantization est une transformation technique, elle ne modifie en rien la licence source d'un modèle. Un modèle "mergé" hérite systématiquement des restrictions de toutes ses composantes d'origine. Si l'usage commercial ou la redistribution sont interdits sur le modèle de base, ils le restent sur le GGUF ou le moteur TensorRT.
# vLLM FP8 (H100)
vllm serve mistralai/Mistral-7B-Instruct \
--quantization fp8 \
--kv-cache-dtype fp8 \
--max-model-len 16384
# vLLM INT8 (A100 / GPU standards)
vllm serve meta-llama/Llama-3-70B-Instruct \
--quantization int8 \
--tensor-parallel-size 2
# vLLM AWQ INT4 (Budget)
vllm serve TheBloke/Llama-3-70B-Instruct-AWQ \
--quantization awq \
--max-model-len 8192
# TensorRT-LLM Build & Serve
trtllm-build \
--checkpoint_dir /path/to/ckpt \
--output_dir /path/to/engine_fp8 \
--use_fp8 --use_fp8_kv_cache \
--max_batch_size 16 --max_input_len 8192
# llama.cpp (CPU)
./llama-server -m model-Q4_K_M.gguf \
--host 0.0.0.0 --port 8080 \
-ngl 0 -c 4096 -t $(nproc)
Article complet disponible sur GitHub
Michail Berjaoui - Février 2025