QuIP / QuIP# (Quantization with Incoherence Processing)#
TL;DR — QuIP et QuIP# sont les méthodes de quantification qui ont rendu la quantification 2-bit viable pour les LLMs. L'idée centrale : avant de quantifier, on applique une rotation aléatoire (transformée de Hadamard) aux matrices de poids pour rendre leur distribution uniforme — réduisant ainsi l'erreur de quantification. QuIP# y ajoute des codebooks en réseau E8 pour atteindre le state-of-the-art en ≤ 4 bits.
Le problème fondamental (expliqué pour un néophyte)#
Quand tu quantifies un poids à 2 bits, tu n'as que 4 niveaux possibles (par exemple : -3, -1, +1, +3). Le problème : les poids d'un LLM ne sont pas uniformément répartis. Certains sont proches de zéro, d'autres sont des outliers.
Avec 2 bits (4 niveaux : -3, -1, +1, +3), les valeurs proches de 0 souffrent énormément — elles sont toutes arrondies au même niveau.
Les valeurs proches de 0 (entre -1 et +1) souffrent le plus : elles sont toutes arrondies au même niveau, perdant toute information fine.
L'insight de QuIP : si on fait tourner la matrice de poids (multiplication par une matrice orthogonale), la distribution devient plus uniforme — et la quantification devient beaucoup plus précise.
Incoherence Processing : le cœur de QuIP#
Le concept d'incohérence#
Une matrice de poids est incohérente quand :
- Aucune direction n'est privilegiée (pas d'axe dominant)
- La magnitude est répartie uniformément dans toutes les directions
- Les directions importantes pour le rounding ne sont pas alignées avec les axes coordonnés
La plupart des matrices de poids de LLMs ne sont pas incohérentes : elles ont des structure, des outliers, des directions dominantes.
La solution : rotation aléatoire#
QuIP applique une multiplication par des matrices orthogonales aléatoires avant et après la quantification :
Poids originaux structurés
avec outliers et directions dominantes"] A --> B["W' = Uᵀ W V
Rotation par matrices orthogonales U, V
→ distribution uniformisée
W' = poids 'incohérents'"] B --> C["Q(W')
Quantification (2, 3, ou 4 bits)
erreur minimisée car distribution uniforme"] C --> D["Q(W') Vᵀ ≈ W
Rotation inverse (à l'inférence)
restitution proche de l'original"]
Pourquoi ça marche mathématiquement ?#
Quand on multiplie une matrice par une matrice orthogonale aléatoire, la théorie des matrices aléatoires (Johnson-Lindenstrauss) garantit que :
- Les valeurs extrêmes (outliers) sont diluées dans toutes les directions
- La magnitude des poids devient plus homogène
- La distribution approche une gaussienne i.i.d. → quantification optimale
C'est l'analogue de mélanger un jeu de cartes : avant le mélange, les cartes sont triées (structurées) ; après, elles sont uniformément réparties (incohérentes).
QuIP vs QuIP# : les différences#
| Aspect | QuIP (2023) | QuIP# (2024) |
|---|---|---|
| Rotation | Matrices orthogonales aléatoires (coûteux) | Transformée de Hadamard randomisée (rapide) |
| Codebook | Quantification scalaire | Vector quantization en réseau E8 |
| Rounding | Adaptive rounding | Adaptive rounding + lattice |
| Fine-tuning | Non | Oui (post-quantization) |
| Performance | Premier 2-bit viable | State-of-the-art ≤ 4 bits |
| Vitesse | Lente (matrices denses) | Rapide (Hadamard = O(n log n)) |
QuIP# : trois innovations clés#
1. Randomized Hadamard Transform (RHT)#
Au lieu de multiplier par une matrice orthogonale aléatoire dense (O(n²)), QuIP# utilise une transformée de Hadamard randomisée :
H_random = D × H_n × D'
où :
- H_n = matrice de Hadamard (n × n), entries ±1/√n
- D, D' = matrices diagonales avec ±1 aléatoires
Calcul : O(n log n) au lieu de O(n²)
Propriétés orthogonales : préservées ✅
La transformée de Hadamard est extrêmement rapide à calculer (algorithme FFT-like) tout en ayant les mêmes propriétés de randomisation qu'une matrice orthogonale dense.
2. Codebooks en réseau E8#
Après rotation, les poids suivent une distribution sub-Gaussienne (concentrés, sans outliers). QuIP# exploite cette propriété avec des codebooks basés sur le réseau E8.
Le réseau E8 est le packing de sphères optimal en 8 dimensions (prouvé mathématiquement). Au lieu de quantifier chaque poids indépendamment (quantification scalaire), QuIP# groupe les poids par vecteurs de dimension 8 et utilise le réseau E8 comme codebook :
(1 poids à la fois)"] S1["w₁ → q₁
w₂ → q₂
...
4 niveaux chacun (2-bit)"] end subgraph E8["Vector quantization E8
(8 poids à la fois)"] E1["[w₁,w₂,...,w₈]
→ point le plus proche
du réseau E8
256 points optimaux en 8D
= 2 bits/poids
avec bien moins d'erreur !"] end
│ │ │ │
●─────●─────●─────●
│ │ │ │
●─────●─────●─────●
↑ arrangement non-optimal
laisse des 'trous'"] end subgraph E["Réseau E8 (packing optimal)"] E1["●───●───●───●
│ ╲ ╱ │ │ ╲ │
●──●───●──●──●
│╱ ╲ │ │ ╲│
●───●───●───●──●
↑ packing dense
minimise l'erreur"] end
Le réseau E8 garantit que le point quantifié le plus proche est toujours aussi proche que possible du vecteur original → erreur de quantification minimisée.
3. Fine-tuning post-quantification#
QuIP# ajoute une étape de fine-tuning légère après la quantification pour corriger les erreurs résiduelles, améliorant encore la fidélité.
Diagramme comparatif : QuIP vs quantification classique#
aux outliers (distribution non-uniforme)"] C4 --> C5["Résultat 2-bit : ❌ dégradation importante"] end subgraph QUIPSHARP["QuIP# (incoherence + E8)"] direction TB Q1["Poids FP16"] --> Q2["Hadamard Transform
distribution uniformisée
(outliers dilués)"] Q2 --> Q3["Adaptive Rounding
optimisation par bloc"] Q3 --> Q4["Vector Quant. E8
codebook optimal en 8D"] Q4 --> Q5["Fine-tuning
correction des erreurs résiduelles"] Q5 --> Q6["Poids 2-bit
Erreur minimale ✅"] Q6 --> Q7["Résultat 2-bit : ✅ première méthode viable"] end
Résultats de perplexité#
QuIP# surpasse toutes les méthodes PTQ existantes en régime ≤ 4 bits.
Llama-2 (perplexité sur WikiText2 — plus bas = mieux)#
| Bits/poids | FP16 (ref) | QuIP# | GPTQ | AWQ | HQQ |
|---|---|---|---|---|---|
| 16 (ref) | 5.47 | — | — | — | — |
| 4 | — | 5.55 | 5.69 | 5.63 | 5.73 |
| 3 | — | 6.01 | 6.62 | 6.40 | 6.55 |
| 2 | — | 7.27 | 11.36 | 8.48 | 9.62 |
À 2 bits : QuIP# obtient 7.27 de perplexité là où GPTQ s'effondre à 11.36 — c'est la différence entre un modèle utilisable et un modèle incohérent.
À 2 bits : QuIP# obtient 7.27 de perplexité là où GPTQ s'effondre à 11.36 — c'est la différence entre un modèle utilisable et un modèle incohérent.
Llama-2-70B à 2 bits#
| Méthode | Bits/poids | PPL WikiText2 | Taille modèle |
|---|---|---|---|
| FP16 | 16.0 | 5.47 | ~140 GB |
| QuIP# | 2.0 | 7.27 | ~18 GB |
| QuIP# | 2.5 | 6.53 | ~22 GB |
| QuIP# | 3.0 | 6.01 | ~27 GB |
Llama-2-70B en QuIP# 2-bit tient dans 18 GB — jouable sur un GPU consumer ou même en RAM CPU.
Quand utiliser QuIP# ?#
(plus simple, plus rapide)"] B -->|"NON"| D{"Quantifier en 3 bits ?"} D -->|"OUI"| E["QuIP# est excellent
AWQ compétitif"] D -->|"NON"| F["Quantifier en 2 bits ou moins"] F --> G["★ QuIP# est le SEUL choix
vraiment viable ★
(AQLM est une alternative
dans le même régime)"]
Exemple pratique#
QuIP (version originale)#
git clone https://github.com/Cornell-RelaxML/QuIP.git
cd QuIP
# Installer les dépendances
pip install -e .
# Quantifier un modèle avec la Hessienne
python quantize_llama.py \
--model meta-llama/Llama-2-7b-hf \
--save_path ./llama-7b-quip \
--bits 2 \
--use_had True \
--had_K 3 \
--codebook hadamard
QuIP# (version améliorée)#
git clone https://github.com/Cornell-RelaxML/quip-sharp.git
cd quip-sharp
pip install -e .
# Quantifier avec QuIP# (Hadamard + E8 lattice)
python quantize.py \
--model meta-llama/Llama-2-7b-hf \
--save_path ./llama-7b-quipsharp \
--bits 2 \
--codebook e8 \
--had_perm True \
--use_had True
# Inférence
python inference.py \
--model_path ./llama-7b-quipsharp \
--prompt "Explique la transformée de Hadamard :"
Intégration Hugging Face#
from transformers import AutoModelForCausalLM
# Charger un modèle pré-quantifié QuIP#
model = AutoModelForCausalLM.from_pretrained(
"relaxml/Llama-2-7b-E8P-2Bit", # Modèle QuIP# pré-quantifié
device_map="auto",
trust_remote_code=True,
)
# Inférence
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("relaxml/Llama-2-7b-E8P-2Bit")
inputs = tokenizer("Qu'est-ce que l'incoherence processing ?", return_tensors="pt")
outputs = model.generate(**inputs.to(model.device), max_new_tokens=200)
print(tokenizer.decode(outputs[0]))
Avantages et inconvénients#
✅ Avantages#
- Première quantification 2-bit vraiment viable pour les LLMs
- Garanties théoriques — première analyse formelle à l'échelle LLM
- State-of-the-art en régime ≤ 4 bits
- Codebooks E8 lattice : packing optimal prouvé mathématiquement
- Inférence rapide supportée (Hadamard = O(n log n))
- QuIP# : surpasse GPTQ et AWQ à bas bitrate
⚠️ Inconvénients#
- Complexité de mise en œuvre (Hadamard transforms, lattice codebooks)
- Temps de quantification plus long que GPTQ/AWQ
- Moins adapté pour ≥ 4 bits (GPTQ/AWQ suffisent à ce niveau)
- Support hardware limité pour les codebooks vectoriels
- Code plus complexe, moins d'intégration native que bitsandbytes/GGUF
- L'inférence nécessite des kernels spécialisés
Comparaison avec les alternatives#
| Méthode | Optimal à | 2-bit viable ? | Complexité | Vitesse de quantif. |
|---|---|---|---|---|
| QuIP# | ≤ 3 bit | ✅ ✅ (SOTA) | Élevée | Lente |
| AQLM | ≤ 3 bit | ✅ (alternatif) | Élevée | Très lente |
| GPTQ | 4 bit | ❌ (cassé) | Moyenne | Rapide |
| AWQ | 3-4 bit | ⚠️ (dégradé) | Faible | Rapide |
| HQQ | 3-4 bit | ⚠️ (dégradé) | Faible | Très rapide |
| GGUF (IQ2) | 2-3 bit | ⚠️ (correct) | Faible | Rapide |
Références#
- Papier QuIP — Chee et al., "QuIP: 2-Bit Quantization of Large Language Models With Guarantees", 2023 — arXiv:2307.13304
- Papier QuIP# — Tseng et al., "QuIP#: Even Better LLM Quantization with Hadamard Incoherence and Lattice Codebooks", ICML 2024 — arXiv:2402.04396
- Code QuIP — github.com/Cornell-RelaxML/QuIP
- Code QuIP# — github.com/Cornell-RelaxML/quip-sharp
- Modèles pré-quantifiés — huggingface.co/relaxml
- Réseau E8 (mathématiques) — Wikipedia: E8 lattice — packing de sphères optimal en 8 dimensions (conjecture de Kepler, démontrée par Viazovska)
- Page de référence — Voir aussi Quantification LLM pour le panorama complet des méthodes