OmniQuant (Omnidirectionally Calibrated Quantization)#
OmniQuant améliore les méthodes PTQ existantes en rendant apprenables (learnable) les paramètres qui étaient auparavant fixés manuellement : le clipping threshold des poids (LWC) et les paramètres de transformation équivalente (LET). Cette approche différenciable couvre toutes les configurations — de W2A16 (2-bit weight-only) à W6A6 — avec une qualité supérieure aux heuristiques fixes.
Le problème : les paramètres fixes des PTQ classiques#
Pour un néophyte#
Les méthodes de quantification PTQ classiques (GPTQ, AWQ, SmoothQuant) utilisent des règles empiriques pour fixer leurs paramètres :
paramètres fixes)) SmoothQuant α = 0.5 fixé à la main GPTQ group-size = 128 fixé AWQ Grid search du scale
→ Que se passe-t-il si on LAISSE le modèle CHERCHER les meilleurs paramètres ? C'est l'idée d'OmniQuant ! ✅
Analogie : régler une radio. SmoothQuant (manuel) → Tu tournes le bouton à mi-chemin (α=0.5) parce que « ça marche en général ». OmniQuant (apprenable) → Le bouton s'ajoute TOUT SEUL pour chaque station (chaque couche du modèle). Trouve le réglage OPTIMAL, pas juste « correct ».
Le problème technique en détail#
Quantification PTQ standard : Y = X · W (multiplication matricielle). Quantifier W : Q(W) = Clip(W, -τ, τ) / s puis Round, où τ = clipping threshold (seuil) et s = scale = τ / (2^(b-1) - 1).
Le seuil τ contrôle TOUT :
- τ petit → haute résolution mais écrête les valeurs extrêmes (clipping error)
- τ grand → pas d'écrêtage mais faible résolution (rounding error)Les PTQ classiques fixent τ par heuristique → sous-optimal !
Les deux piliers d'OmniQuant#
1. Learnable Weight Clipping (LWC)#
Au lieu d'un seuil de clipping fixe, LWC optimise le seuil de clipping des poids de manière différentiable :
→ petits outliers écrêtés"] end subgraph LWC["Clipping LWC (apprenable)"] L1["Distribution des poids"] --> L2["Seuil OPTIMAL τ* (appris)
→ meilleur trade-off trouvé"] end
Implémentation différentiable : Au lieu de
Clip(W, τ), on utiliseQ(W; γ) = tanh(γ · τ) · Round(W / tanh(γ · τ))où γ est un paramètre APPRENABLE (gradient descent). γ = 1 : pas de clipping (= max|W|). γ < 1 : clipping agressif (réduit la range). Optimisé par block-wise error minimization.
2. Learnable Equivalent Transformation (LET)#
LET étend l'idée de SmoothQuant en rendant apprenables les paramètres de transformation équivalente :
SmoothQuant (transformation fixe) :
Y = (X · diag(1/s)) · (diag(s) · W)
où s_j = max(|X_j|)^α / max(|W_j|)^(1-α)
α fixé (typiquement 0.5)
OmniQuant LET (transformation apprenable) :
Y = (X · diag(1/s*)) · (diag(s*) · W + b*)
où s* = paramètre de scaling APPRENABLE
b* = paramètre de shifting APPRENABLE
→ Le modèle APPREND les meilleures transformations
pour chaque couche, au lieu d'utiliser une heuristique
→ sous-optimal"] end subgraph OQ["OmniQuant LET (transformation apprenable)"] OQX["X"] --> OQX2["÷s₁* + b₁*"] --> OQX3["X'"] OQW["W"] --> OQW2["×s₁* - b₁*"] --> OQW3["W' → Quant(W')"] OQN["Couche 1: s₁*, b₁* optimisés
Couche 2: s₂*, b₂* optimisés
Couche 3: s₃*, b₃* optimisés
→ chaque couche a SA transformation optimale"] end
Diagramme du process OmniQuant#
LWC: γ₀ (clipping init)
LET: s₀, b₀ (smooth init)"] --> BLOCK subgraph BLOCK["3. Block-wise Error Minimization (par bloc transformer)"] FW["Forward pass (bloc)
X → LET(X, s*, b*) → X'
W → LET(W, s*, b*) → W'
W' → LWC_Quant(W', γ*) → Q
Y' = X' · Q"] --> LOSS LOSS["Loss = MSE(Y', Y_FP16)
Backprop → update γ*, s*, b*
(modèle FP16 reste GELÉ)"] LOSS --> FW end BLOCK --> FINAL FINAL["4. Quantification finale
Appliquer γ*, s*, b* optimaux"] --> RESULT RESULT["5. Modèle quantifié final
W2A16, W3A16, W4A16, W4A4, W6A6...
avec paramètres optimaux par couche"]
Caractéristiques#
| Propriété | Valeur |
|---|---|
| Type | Post-Training Quantization (PTQ) différenciable |
| Configurations | W2A16, W3A16, W4A16, W4A4, W6A6 |
| Paramètres apprenables | Clipping (LWC) + Transformation (LET) |
| Optimisation | Block-wise error minimization |
| Calibration | 128 samples |
| Temps de quantif. | 1-16h sur A100-40G (LLaMA-2 7-70B) |
| Backprop requise | ✅ Oui (mais modèle gelé) |
| Activation quantif. | ✅ Oui (W4A4, W6A6) |
Comparaison avec GPTQ / AWQ#
| Critère | OmniQuant | GPTQ | AWQ | SmoothQuant |
|---|---|---|---|---|
| Approche | Paramètres apprenables | Compensation Hessienne | Scaling saillant | Migration fixe (α) |
| Différentiable | ⭐ Oui | Non | Non (grid search) | Non |
| Configurations | ⭐ W2-W6, A4-A16 | W2-W4 (weight-only) | W3-W4 (weight-only) | W8A8 |
| Weight + Activation | ⭐ W4A4, W6A6 | ❌ Non | ❌ Non | ✅ W8A8 |
| Paramètres par couche | ⭐ Optimisés individuellement | Uniformes | Grid search | α fixe |
| 2-bit viable | ⭐ W2A16 compétitif | Dégradé | Dégradé | N/A |
| Temps de quantif. | 1-16h (GPU) | ⭐ Rapide (~4 GPU-h) | ⭐ Rapide | ⭐ Minutes |
| Complexité | Élevée | Moyenne | Faible | Faible |
| Support écosystème | Limité | ⭐ Très large | ⭐ Très large | Bon |
OmniQuant vs SmoothQuant : la même idée, mais apprenable#
SmoothQuant :
Y = (X · diag(1/s)) · (diag(s) · W)— s fixé par heuristique.OmniQuant :
Y = (X · diag(1/s*) + b*) · (diag(s*) · W - b*)— s, b APPRIS par couche.La différence : SmoothQuant a 1 paramètre α global. OmniQuant a s + b par couche (optimisés). → OmniQuant trouve TOUJOURS un meilleur ou égal réglage que SmoothQuant. Mais ça coûte une optimisation (gradient).
Niveau néophyte : l'analogie#
Imagine que tu cuisines un gâteau. Les recettes classiques (GPTQ, AWQ) te disent : "mets 200g de sucre" — un montant fixe pour tous les gâteaux. OmniQuant dit : "apprends la quantité parfaite de sucre pour chaque gâteau". Au début, tu fais plusieurs essais (optimisation par gradient), mais au final, chaque gâteau est parfait car la quantité a été ajustée individuellement. C'est plus long que de suivre une recette fixe, mais le résultat est meilleur.
Résultats de performance#
Perplexité sur LLaMA-2#
Tableau récapitulatif#
| Configuration | OmniQuant PPL | Concurrent PPL | Avantage |
|---|---|---|---|
| W4A16 (LLaMA-2-7B) | 5.58 | 5.63 (AWQ) | ⭐ |
| W3A16 (LLaMA-2-7B) | 6.10 | 6.40 (AWQ) | ⭐ |
| W2A16 (LLaMA-2-7B) | 7.75 | 8.48 (AWQ) | ⭐ |
| W4A4 (LLaMA-2-7B) | 5.91 | 8.71 (SmoothQuant) | ⭐⭐ |
| W6A6 (LLaMA-2-7B) | 5.52 | 5.55 (SmoothQuant) | ⭐ |
Points forts : OmniQuant surpasse systématiquement les PTQ classiques sur toutes les configurations, avec un avantage particulièrement marqué en W4A4 (quantification poids + activation) et en W2A16 (2-bit extrême).
Exemple pratique#
Installation#
git clone https://github.com/OpenGVLab/OmniQuant.git
cd OmniQuant
pip install -e .
Quantification W4A16 (weight-only 4-bit)#
# Quantifier LLaMA-2-7B en W4A16
python omniquant.py \
--model meta-llama/Llama-2-7B-hf \
--output_dir ./llama-7b-omniquant-w4a16 \
--wbits 4 \
--abits 16 \
--let --lwc \
--calib_dataset pile \
--nsamples 128 \
--epochs 5 \
--gpu_id 0
Quantification W4A4 (weight + activation)#
python omniquant.py \
--model meta-llama/Llama-2-7B-hf \
--output_dir ./llama-7b-omniquant-w4a4 \
--wbits 4 \
--abits 4 \
--let --lwc \
--calib_dataset pile \
--nsamples 128 \
--epochs 10 \
--gpu_id 0
Quantification W2A16 (2-bit extrême)#
python omniquant.py \
--model meta-llama/Llama-2-7B-hf \
--output_dir ./llama-7b-omniquant-w2a16 \
--wbits 2 \
--abits 16 \
--let --lwc \
--calib_dataset pile \
--nsamples 128 \
--epochs 20 \
--gpu_id 0
Inférence#
import torch
from transformers import AutoTokenizer
from omniquant import OmniQuantModelForCausalLM
# Charger le modèle quantifié
model = OmniQuantModelForCausalLM.from_quantized(
"./llama-7b-omniquant-w4a16",
device="cuda",
)
tokenizer = AutoTokenizer.from_pretrained("./llama-7b-omniquant-w4a16")
# Génération
inputs = tokenizer("Qu'est-ce que la quantification apprenable ?", return_tensors="pt").to("cuda")
output = model.generate(**inputs, max_new_tokens=200)
print(tokenizer.decode(output[0], skip_special_tokens=True))
Avantages et inconvénients#
✅ Avantages#
- Différentiable : optimise les paramètres plutôt que de les fixer manuellement
- Très flexible : couvre W2A16 → W6A6 (weight-only ET weight+activation)
- Best-in-class : surpasse GPTQ, AWQ, SmoothQuant sur toutes les configurations testées
- W4A4 compétitif : une des rares PTQ viables en quantification poids + activation simultanée
- W2A16 viable : meilleure PTQ 2-bit weight-only avant QuIP#/AQLM
- Block-wise : optimisation par bloc, pas de ré-entraînement complet
⚠️ Inconvénients#
- Nécessite un GPU pour l'optimisation (gradient descent sur 128 samples)
- Plus complexe que GPTQ/AWQ (training-based PTQ, pas purement one-shot)
- Plus lent à exécuter que la PTQ pure (1-16h vs minutes pour GPTQ)
- Support limité dans les frameworks mainstream (vs GPTQ/AWQ très intégrés)
- Moins d'écosystème d'inférence (kernels custom nécessaires)
Quand choisir OmniQuant ?#
(poids + activation 4-bit)"} -- OUI --> R1["★ OmniQuant est le meilleur choix PTQ ★"] Q1 -- NON --> Q2 Q2{"Tu veux W2A16 ?
(2-bit weight-only)
sans QuIP#/AQLM ?"} -- OUI --> R2["OmniQuant est compétitif en PTQ simple"] Q2 -- NON --> Q3 Q3{"Tu veux la meilleure
qualité W4A16 ?"} -- OUI --> R3["OmniQuant bat AWQ et GPTQ"] Q3 -- NON --> R4["→ GPTQ ou AWQ sont plus rapides"]
Références#
- Papier arXiv : OmniQuant: Omnidirectionally Calibrated Quantization for Large Language Models — Shao et al., 2023
- Code officiel : github.com/OpenGVLab/OmniQuant
- Voir aussi : SmoothQuant · GPTQ · AWQ · Index quantification