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.

xychart-beta title "Quantification 2-bit uniforme — erreur d'arrondi" x-axis ["poids original", "", "", "", "", "", "", ""] y-axis "Niveau quantifié" -4 --> 4 bar [0, 1, 1, 1, -1, -1, 1, 3]

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 :

flowchart TD A["Matrice W (FP16)
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 :

  1. Les valeurs extrêmes (outliers) sont diluées dans toutes les directions
  2. La magnitude des poids devient plus homogène
  3. 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 :

flowchart LR subgraph SCALAR["Quantification scalaire
(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
flowchart LR subgraph U["Grille uniforme (scalaire)"] U1["●─────●─────●─────●
│ │ │ │
●─────●─────●─────●
│ │ │ │
●─────●─────●─────●
↑ 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#

flowchart TD subgraph CLASSIC["QUANTIFICATION CLASSIQUE (ex: INT4)"] direction TB C1["Poids FP16"] --> C2["Scale + Round"] C2 --> C3["Poids INT4"] C2 --> C4["❌ Erreur d'arrondi élevée
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.

xychart-beta title "Perplexité WikiText2 — Llama-2 (plus bas = mieux)" x-axis ["FP16", "QuIP# 4b", "AWQ 4b", "GPTQ 4b", "QuIP# 3b", "QuIP# 2b", "AWQ 2b", "HQQ 2b", "GPTQ 2b"] y-axis "Perplexité" 5 --> 12 bar [5.47, 5.55, 5.63, 5.69, 6.01, 7.27, 8.48, 9.62, 11.36]

À 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# ?#

flowchart TD A["Quand utiliser QuIP# ?"] --> B{"Quantifier en 4 bits ou plus ?"} B -->|"OUI"| C["GPTQ ou AWQ suffisent
(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#

ia llm quantification quip hadamard lattice e8 2-bit vector-quantization