W4A8 / W8A8 — Quantification en précision mixte#

TL;DR — La notation WxAy décrit une quantification en précision mixte où les poids (Weights) sont codés sur x bits et les activations sur y bits. Les poids étant statiques et réguliers, on peut les compresser agressivement (4-bit). Les activations, sujettes à des outliers dynamiques, nécessitent plus de précision (8 ou 16-bit). W8A8 est la configuration la plus robuste (support natif Tensor Cores INT8/FP8). W4A8 est plus agressive et optimale pour le cloud serving à grand batch.


Le concept : pourquoi quantifier différemment poids et activations ?#

Dans un Transformer, une couche linéaire calcule Y = X × W où :
- W (poids) — fixe après entraînement, distributions lisses et régulières
- X (activations) — changent à chaque token, contiennent des outliers (valeurs extrêmes)

block-beta columns 2 h["POURQUOI LA PRÉCISION MIXTE ?"]:2 p["POIDS (W)"]:1 a["ACTIVATIONS (X)"]:1 p1["• Statiques\n• Distribution lisse, gaussienne\n• Pas d'outliers\n• Compressibles à 4-bit ou moins\n• Mesurables offline"]:1 a1["• Dynamiques (changent chaque token)\n• Outliers systématiques (jusqu'à 20× la moyenne)\n• Sensibles à la quantification\n• Nécessitent plus de précision (INT8 min.)\n• Imprévisibles sans calibration"]:1 h --> p & a p --> p1 a --> a1

L'analogie pour néophyte#

Imagine un restaurant. Les poids sont le livre de recettes : il ne change jamais, tu peux le photocopier en version condensée (4 mots-clés par recette). Les activations, ce sont les ingrédients qui arrivent en temps réel — chaque plat est différent, parfois un ingrédient sort du lot (une dose 20× trop forte). Tu ne peux pas condenser les ingrédients autant que les recettes, sinon tu ruines le plat. Donc : recettes en 4-bit, ingrédients en 8-bit.


Les trois configurations principales#

Schéma Poids Activ. Cas d'usage optimal
W4A16 INT4 FP16 Edge / single-user : préserve la qualité, réduit la mémoire poids → GPTQ, AWQ, GGUF
W4A8 INT4 INT8 Cloud serving (large batch) : compression poids + accél. compute via INT8 Tensor Cores → QoQ/QServe, OmniQuant, QQQ
W8A8 INT8 INT8 Production robuste : perte d'accuracy minimale, support hardware natif maximal → SmoothQuant, LLM.int8()
W4A4 INT4 INT4 Régime extrême (QAT souvent nécessaire) → LLM-QAT
W6A6 INT6 INT6 Compromis doux (peu commun)

W8A8 : la configuration de référence#

Principe#

Poids et activations tous deux en 8-bit. C'est le standard de l'industrie pour la quantification production-ready.

flowchart TD M["Modèle FP16"] --> SQ["**SmoothQuant** (calibration)
Migration des outliers :
X' = X · diag(1/s)
W' = diag(s) · W
→ Activations « lissées »"] SQ --> Q["**Quantification INT8**
W → INT8 (per-channel)
X → INT8 (per-token)"] Q --> GEMM["**GEMM INT8 × INT8**
Tensor Cores INT8 natifs
Débit 2× supérieur au FP16 sur Ampere/Hopper"]

Pourquoi W8A8 est le plus stable#

  1. Pas de déquantification coûteuse : tout le pipeline reste en INT8
  2. Support hardware natif : Tensor Cores INT8 depuis NVIDIA Volta (V100)
  3. Outliers gérables : SmoothQuant + INT8 suffit à contenir les valeurs extrêmes
  4. Perte d'accuracy < 1% sur la plupart des benchmarks

Outils principaux pour W8A8#

Outil Mécanisme Notes
SmoothQuant Migration outliers activation → poids PTQ pur, α ≈ 0.5, ICML 2023
LLM.int8() Décomposition mixed-precision pour outliers Zéro dégradation, léger overhead
TensorRT-LLM Quantification INT8/FP8 native NVIDIA Standard de production
FP8 (Hopper) W8A8 en floating-point (E4M3) Meilleure dynamique que INT8

W4A8 : le meilleur des deux mondes pour le cloud serving#

Principe#

Poids en INT4 (compression agressive), activations en INT8 (stabilité + accélération compute). C'est la configuration qui a émergé comme optimale pour le large-batch cloud serving.

flowchart TD M["Modèle FP16"] --> S1["**Étape 1 : SmoothQuant + calibration**
Lisser les activations
Migration outliers → poids"] S1 --> S2["**Étape 2 : Quantification différenciée**
W → INT4 (group quant, g=128)
X → INT8 (per-token)"] S2 --> S3["**Étape 3 : GEMM en précision mixte**"] S3 --> DQ["W_int4 → [déquant 4→8 bit] → W_int8"] DQ --> GEMM["GEMM INT8×INT8 (Tensor Cores)"] S3 --> X8["X_int8"] X8 --> GEMM

W4A8 vs W4A16 : quand utiliser quoi ?#

Aspect W4A16 (poids INT4 + act FP16) W4A8 (poids INT4 + act INT8)
Bottleneck Mémoire poids (weight loading) Compute + mémoire poids
Compute FP16 × déquant (pas accéléré) INT8 GEMM (Tensor Cores, 2× plus rapide)
Qualité Maximale pour l'INT4 Légèrement inférieure (act quantifiées)
Optimal pour Edge, single-user, CPU inference Cloud serving, large batch, datacenter
Outils GPTQ, AWQ, GGUF, HQQ QServe/QoQ, QQQ, OmniQuant

La nuance critique : la déquantification progressive#

Le défi du W4A8 est le GEMM : comment multiplier du INT4 par du INT8 ? Il faut déquantifier les poids. Deux approches :

flowchart LR subgraph NAIVE["⚠️ Approche naïve (lente)"] direction TB W4N["W_int4"] --> DQN["[déquant INT4 → FP16]"] --> WFN["W_fp16"] X8N["X_int8"] --> DXN["[déquant INT8 → FP16]"] --> XFN["X_fp16"] WFN --> GN["GEMM FP16 × FP16
⚠️ Pas d'accélération hardware
⚠️ Overhead de déquant lourd"] XFN --> GN end subgraph OPT["✅ Approche W4A8 optimisée (rapide)"] direction TB W4O["W_int4"] --> DQO["[déquant INT4 → INT8]"] --> W8O["W_int8"] X8O["X_int8"] W8O --> GO["GEMM INT8 × INT8
✅ Tensor Cores INT8 (2× FP16)
✅ Déquant peu coûteuse"] X8O --> GO end

C'est exactement l'innovation du progressive quantization dans QoQ/QServe.


Diagramme unifié : W8A8 vs W4A8 vs W4A16#

flowchart TD M["Modèle FP16 Original"] --> W8A8 & W4A8 & W4A16 subgraph W8A8_box["W8A8"] W8A8D["W: INT8 · A: INT8
GEMM INT8×INT8 Tensor Cores
Accuracy: ★★★★ · Mémoire: ★★ · Vitesse: ★★★★
(large batch)"] end subgraph W4A8_box["W4A8"] W4A8D["W: INT4 · A: INT8
Déquant 4→8 · GEMM INT8×INT8 Tensor Cores
Accuracy: ★★★ · Mémoire: ★★★★ · Vitesse: ★★★★★
(large batch)"] end subgraph W4A16_box["W4A16"] W4A16D["W: INT4 · A: FP16
Déquant 4→16 · GEMM FP16
Accuracy: ★★★★ · Mémoire: ★★★★ · Vitesse: ★★★
(single user)"] end

Le rôle de SmoothQuant dans W8A8 et W4A8#

SmoothQuant est le prérequis pour toute quantification W8A8 ou W4A8 réussie. Sans lui, les outliers d'activation rendent l'INT8 catastrophique.

flowchart LR subgraph SANS["Sans SmoothQuant"] direction TB SA1["Activations brutes :
Outlier (20×)
INT8 = saturé ou clip
→ erreurs massives"] SA2["Poids inchangés
→ INT8 propre"] end subgraph AVEC["Avec SmoothQuant"] direction TB AV1["Activations lissées :
Toutes les valeurs
dans une plage gérable
→ INT8 propre"] AV2["Poids légèrement modifiés
(multipliés par s)
→ INT8/INT4 propre"] end

La transformation est mathématiquement équivalente (même sortie) :

  Y = X · W                           (original)
  Y = (X · diag(1/s)) · (diag(s) · W) (équivalent, mais X lissé)
  Y = X' · W'                          (W' = poids modifiés)

Méthodes et outils de quantification W4A8 / W8A8#

Vue d'ensemble#

Méthode Schéma Type Outlier Handling Source
SmoothQuant W8A8 PTQ Migration α=0.5 arXiv:2211.10438
LLM.int8() W8A8 (mixed) PTQ Décomposition FP16 pour outliers arXiv:2208.07339
QoQ / QServe W4A8KV4 PTQ+ SmoothQuant + SmoothAttention arXiv:2405.04532
QQQ W4A8 PTQ INT4 poids + INT8 act optimisés arXiv:2406.09904
OmniQuant W4A8, W8A8, W6A6, W4A4 PTQ+ Learnable clipping + transform arXiv:2308.13137
TensorRT-LLM W8A8, FP8 PTQ Native NVIDIA INT8/FP8

Le combo SmoothQuant + GPTQ (W4A8 pratique)#

Une approche pratique de W4A8 combine SmoothQuant (pour les activations INT8) avec GPTQ (pour les poids INT4) :

flowchart TD S1["**1. SmoothQuant sur le modèle FP16**
→ Activations lissées, quantifiables en INT8
→ Poids légèrement modifiés"] S1 --> S2["**2. GPTQ sur les poids lissés**
→ Poids compressés en INT4 (group_size=128)"] S2 --> S3["**3. Inférence**
→ Déquant INT4 → INT8 à la volée
→ GEMM INT8 × INT8 sur Tensor Cores"]

Exemple pratique#

W8A8 avec TensorRT-LLM#

# 1. Installer TensorRT-LLM
pip install tensorrt-llm

# 2. Quantification W8A8 (SmoothQuant + INT8)
python tensorrt_llm/examples/llama/quantize.py \
    --model_dir ./llama-3-8b \
    --dtype float16 \
    --qformat int8_smoothquant \  # W8A8 avec SmoothQuant
    --calib_size 512 \            # 512 échantillons de calibration
    --output_dir ./llama-3-8b-w8a8

# 3. Build du moteur TensorRT
trtllm-build \
    --checkpoint_dir ./llama-3-8b-w8a8 \
    --output_dir ./trt-engine-w8a8 \
    --gemm_plugin int8  # Utiliser les Tensor Cores INT8

# 4. Inférence
python -m tensorrt_llm.run \
    --engine_dir ./trt-engine-w8a8 \
    --tokenizer_dir ./llama-3-8b

W4A8 avec QServe/QoQ#

# Cloner QServe
git clone https://github.com/mit-han-lab/omniserve.git
cd omniserve
pip install -e .

# Serving W4A8KV4
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

W8A8 avec SmoothQuant (standalone)#

from smoothquant import SmoothQuant, Calibration

# 1. Calibration
calib = Calibration(model, dataset="redpajama", n_samples=512)
activation_stats = calib.collect_stats()

# 2. Migration des outliers
sq = SmoothQuant(alpha=0.5)  # α contrôle la migration
model_smoothed = sq.migrate(model, activation_stats)

# 3. Quantification INT8 (W8A8)
model_int8 = quantize_int8(model_smoothed)
# → GEMM INT8 × INT8 sur Tensor Cores

Tableau de décision#

Ton scénario Schéma recommandé Outil
Edge / mobile / single-user W4A16 GPTQ, AWQ, GGUF
Cloud serving petit batch (< 8) W4A16 + Marlin GPTQ-Marlin (vLLM)
Cloud serving large batch (32+) W4A8 QServe/QoQ
Production robuste, accuracy max W8A8 TensorRT-LLM, SmoothQuant
Hardware Hopper+ disponible FP8 (W8A8) vLLM FP8, TensorRT FP8
Fine-tuning basse précision W4A4 LLM-QAT

Avantages et inconvénients#

✅ Avantages ❌ Inconvénients
Flexibilité : choisir le meilleur compromis accuracy/perf Complexité accrue (gérer multiples précisions)
W4A8 optimal pour cloud serving (compute + mémoire) Nécessite des kernels spécialisés (Marlin, QServe)
W8A8 : support hardware natif maximal Pas toujours supporté par tous les frameworks
W4A16 : qualité maximale pour l'edge W4A16 : pas d'accélération du compute
SmoothQuant rend W8A8/W4A8 viables SmoothQuant requiert calibration + tuning de α
FP8 : meilleur que INT8 sur Hopper+ FP8 : hardware récent uniquement

Références#

  • SmoothQuant — Xiao et al., "SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models", ICML 2023 — arXiv:2211.10438
  • LLM.int8() — Dettmers et al., "LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale", NeurIPS 2022 — arXiv:2208.07339
  • QoQ / QServe — Lin et al., "QServe: W4A8KV4 Quantization and System Co-design for Efficient LLM Serving", 2024 — arXiv:2405.04532
  • QQQ"QQQ: Quality Quattuor-Bit Quantization for Large Language Models", 2024 — arXiv:2406.09904
  • OmniQuant — Shao et al., "OmniQuant: Omnidirectionally Calibrated Quantization for Large Language Models", 2023 — arXiv:2308.13137
  • FP8 Formats — Micikevicius et al., "FP8 Formats for Deep Learning", 2022 — arXiv:2209.05433
  • Voir aussi : SmoothQuant · QoQ / QServe · GPTQ · LLM.int8() · Marlin · Index quantification
ia llm quantification w4a8 w8a8 mixed-precision tensorrt smoothquant qserve omniquant qqq