QoQ / QServe — Quantification W4A8KV4#
QoQ (quattuor-octo-quattuor = 4-8-4 en latin) est un schéma de quantification W4A8KV4 : poids en 4-bit, activations en 8-bit, KV cache en 4-bit. Couplé au système QServe (kernels GPU optimisés), c'est la première solution INT4 qui accélère le large-batch cloud serving, avec un throughput jusqu'à 3.5× supérieur à TensorRT-LLM.
Le problème : INT4 ne suffit pas pour le cloud serving#
Les méthodes de quantification comme GPTQ et AWQ compressent les poids en INT4 — ce qui réduit la mémoire et accélère l'inférence pour des batch sizes petits. Mais en cloud serving (servir des dizaines de requêtes simultanées), le bottleneck change :
(weight loading)"] S2["Solution: poids INT4
(GPTQ, AWQ) ✅ Résolu"] end subgraph BATCH["Large Batch (batch 32+)"] B1["Bottleneck = COMPUTE
(activations × poids)
+ KV cache mémoire"] B2["⚠️ Non adressé par GPTQ/AWQ"] end
En large batch, les activations (FP16) dominent le compute. Quantifier seulement les poids ne suffit pas — il faut aussi quantifier les activations. Mais les activations ont des outliers qui rendent l'INT4 catastrophique. D'où la solution intermédiaire : W4A8KV4.
Niveau néophyte : l'analogie#
Imaginez une cuisine industrielle qui prépare 100 repas à la fois. Les recettes (poids) peuvent être stockées en version super-condensée (4 mots-clés au lieu de 16). Mais les ingrédients frais (activations) arrivent en continu et en grande quantité — les condenser autant que les recettes rendrait les plats immangeables. QoQ utilise donc une compression modérée pour les ingrédients (8 niveaux au lieu de 16), suffisante pour accélérer la production sans ruiner la qualité.
De plus, les notes de commande (KV cache) s'accumulent pendant le service — les compresser aussi (4 niveaux) évite d'encombrer le plan de travail.
Niveau technique : la quantification W4A8KV4#
Décomposition des trois précisions#
| Composant | Bits | Raison |
|---|---|---|
| Poids (W) | INT4 | Compression max, stable (comme GPTQ/AWQ), réduit la mémoire |
| Activations (A) | INT8 | INT4 trop agressif (outliers), INT8 = bon compromis compute/qualité |
| KV Cache (KV) | INT4 | Énorme footprint mémoire, quantifiable à 4-bit sans trop de perte (avec SmoothAttention) |
Diagramme du pipeline QServe#
via scaling par-canal
X' = X · diag(1/s), W' = diag(s) · W
→ Activations « lissées » quantifiables en INT8"] end S1 --> S2 subgraph S2["Étape 2 : Quantification"] S2A["W' → INT4 (group quant)
X' → INT8 (per-token)
KV → INT4 (per-channel) + SmoothAttention"] end S2 --> S3 subgraph S3["Étape 3 : Inférence QServe"] S3A["Progressive dequant : INT4 → INT8 → GEMM INT8
(pas de déquant 4→16 !)
Kernels GPU optimisés :
• Compute-aware reordering
• Register-level parallelism
• INT8 Tensor Cores"] end
Les 3 innovations clés de QoQ#
1. Progressive quantization (INT4 → INT8 → GEMM)#
Le problème classique : pour multiplier du INT4 (poids) par du INT8 (activations), il faut déquantifier les poids en INT8 ou FP16. QoQ fait une transition progressive sans passer par FP16 :
⚠️ LENT"] N2 --> N3["W_fp16"] N3 --> N4["GEMM FP16 × INT8
⚠️ LENT"] end subgraph QOQ["Méthode QoQ"] Q1["W_int4"] --> Q2["déquant 4→8 bit
(zéro coût FP16 !)"] Q2 --> Q3["W_int8"] Q3 --> Q4["GEMM INT8 × INT8
✅ RAPIDE
(Tensor Cores INT8 !)"] end
Cette transition INT4→INT8 est beaucoup moins coûteuse que INT4→FP16 car elle opère uniquement sur des entiers, et le GEMM INT8 utilise les Tensor Cores INT8 natifs du GPU (débit 2× supérieur au FP16 sur Ampere).
2. SmoothAttention pour le KV cache 4-bit#
La quantification du KV cache en INT4 dégrade l'attention. QoQ introduit SmoothAttention : appliquer une technique similaire à SmoothQuant, mais spécifiquement aux channels du KV cache pour lisser leurs distributions avant quantification.
(distribution avec outliers)"] --> SA["SmoothAttention
Scaling par-canal des K/V
(migration des outliers)"] SA --> KV2["KV Cache INT4
(distribution lissée,
quantifiable sans dégradation)"]
3. Optimisations système (compute-aware reordering + register parallelism)#
Le système QServe implémente des kernels GPU qui réorganisent les poids au niveau des registers pour minimiser les conflits de banques et maximiser le parallélisme au niveau des registres :
Caractéristiques#
| Propriété | Valeur |
|---|---|
| Type | PTQ + co-design système |
| Précision | W4A8KV4 (poids INT4, activations INT8, KV cache INT4) |
| Bottleneck ciblé | Large-batch cloud serving (compute + KV mémoire) |
| GPU requis | NVIDIA Ampere+ (A100, L40S, H100) |
| Throughput vs TensorRT-LLM | 1.2×–3.5× supérieur |
| Coût de serving | Réduit de 3× (L40S bat A100) |
| Outils | QServe (MIT HAN Lab) |
Résultats throughput#
Comparaison vs TensorRT-LLM (FP16)#
| Modèle | GPU | TRT-LLM FP16 (tok/s) | QServe W4A8KV4 (tok/s) | Speedup |
|---|---|---|---|---|
| Llama-3-8B | A100 | ~6 000 | ~7 200 | 1.2× |
| Llama-3-8B | L40S | ~4 000 | ~5 600 | 1.4× |
| Qwen1.5-72B | A100 | ~800 | ~1 900 | 2.4× |
| Qwen1.5-72B | L40S | ~500 | ~1 750 | 3.5× |
| Llama-2-70B | A100 | ~850 | ~1 900 | 2.2× |
Le résultat clé : L40S bat A100#
Avec QServe, L40S (~8K$) égale A100 (~20K$) en throughput → coût de serving réduit de ~3×.
Le L40S (carte data-center à ~8 000 $) avec QServe surpasse un A100 (~20 000 $) en FP16. C'est une réduction de coût de serving de 3×.
Exemple pratique#
Installation de QServe#
# Cloner le repository QServe (MIT HAN Lab)
git clone https://github.com/mit-han-lab/omniserve.git
cd omniserve
# Installer les dépendances
pip install -e .
Quantification W4A8KV4 d'un modèle#
from qserve import QoQQuantizer, QServeEngine
# 1. Configuration de la quantification QoQ
quant_config = {
"w_bits": 4, # Poids INT4
"a_bits": 8, # Activations INT8
"kv_bits": 4, # KV cache INT4
"w_group_size": 128, # Group quantization pour les poids
"smoothquant_alpha": 0.5, # Migration outliers (SmoothQuant)
"smoothattention": True, # SmoothAttention pour KV 4-bit
}
# 2. Quantifier le modèle
quantizer = QoQQuantizer(quant_config)
model = quantizer.quantize("meta-llama/Meta-Llama-3-8B")
# 3. Servir avec le moteur QServe
engine = QServeEngine(model, tensor_parallel_size=1)
Serving avec QServe#
# Lancer le serveur QServe
python -m qserve.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-8B \
--quantization qoq \
--w-bits 4 \
--a-bits 8 \
--kv-bits 4 \
--port 8000
Benchmark throughput#
# Benchmark QServe
python -m qserve.benchmark.benchmark_serving \
--model meta-llama/Meta-Llama-3-8B \
--quantization qoq \
--num-prompts 256 \
--request-rate 32
# Comparer avec TensorRT-LLM FP16
python -m qserve.benchmark.benchmark_serving \
--model meta-llama/Meta-Llama-3-8B \
--quantization fp16 \
--backend tensorrt-llm \
--num-prompts 256 \
--request-rate 32
Comparaison : QoQ vs autres schémas#
| Schéma | Poids | Activations | KV Cache | Batch serving | Accuracy |
|---|---|---|---|---|---|
| GPTQ / AWQ | INT4 | FP16 | FP16 | ⚠️ Faible (batch < 8) | Excellente |
| SmoothQuant | INT8 | INT8 | FP16 | ✅ Bon (batch 16+) | Excellente |
| GPTQ + Marlin | INT4 | FP16 | FP16 | ✅ Bon (batch ≤ 64) | Excellente |
| QoQ / QServe | INT4 | INT8 | INT4 | ✅✅ Excellent (batch 64+) | Très bonne |
| FP8 (H100) | FP8 | FP8 | FP8 | ✅ Bon (H100 uniquement) | Excellente |
Le positionnement unique de QoQ : c'est le seul schéma qui quantifie simultanément poids (4-bit), activations (8-bit) et KV cache (4-bit) avec des kernels GPU optimisés pour le large-batch serving.
Limitations#
- Complexité de mise en œuvre — le co-design algorithme + système nécessite les kernels QServe custom (pas de drop-in dans n'importe quel framework).
- Configuration très spécifique — W4A8KV4 est figée ; pas de flexibilité comme GPTQ (3/4/8-bit au choix).
- SmoothQuant requis — la quantification INT8 des activations nécessite une étape de calibration SmoothQuant préalable.
- GPU Ampere+ obligatoire — les Tensor Cores INT8 sont essentiels pour le throughput promis.
- Support framework limité — principalement via le repository QServe officiel, intégration partielle dans vLLM/TensorRT-LLM.
Références#
- Papier arXiv : QServe: W4A8KV4 Quantization and System Co-design for Efficient LLM Serving (Lin et al., MIT, 2024)
- Code source : github.com/mit-han-lab/omniserve (QServe)
- SmoothQuant : arXiv:2211.10438 — base théorique de la migration d'outliers
- Voir aussi : SmoothQuant · GPTQ · KV Cache Quantization (KIVI) · Index quantification