AQLM (Additive Quantization for LLMs)#

TL;DR — AQLM adapte l'additive quantization (issue de l'information retrieval) à la compression de LLMs. Au lieu de quantifier chaque poids indépendamment, AQLM approxime chaque vecteur de poids par une somme de vecteurs sélectionnés dans plusieurs codebooks appris. Cette approche par vector quantization exploite les corrélations entre poids et atteint une qualité Pareto-optimale en régime sub-3-bit, surpassant GPTQ et AWQ à 2 bits.


Le problème fondamental (expliqué pour un néophyte)#

Les méthodes de quantification classiques (GPTQ, AWQ) traitent chaque poids individuellement : on prend un nombre FP16 et on le remplace par son plus proche niveau dans une grille fixe. À 2 bits, on n'a que 4 niveaux — c'est trop peu pour représenter la diversité des poids d'un grand modèle.

L'idée d'AQLM : au lieu de quantifier chaque poids séparément, on groupe les poids par petits vecteurs et on approxime chaque vecteur comme une combinaison additive de motifs appris (les codebooks).

Quantification classique (scalaire) : Chaque poids individuellement → w = 0.037 → q = 0, w = 0.142 → q = 1, etc. ❌ 4 niveaux seulement en 2-bit → beaucoup d'erreur.

Additive Quantization (AQLM) : Un VECTEUR de 8 poids à la fois :

flowchart LR W["[w₁, w₂, w₃, w₄, w₅, w₆, w₇, w₈]
8 poids FP16"] --> APPROX CB1["codebook₁[42]
vecteur dim 8"] --> APPROX CB2["codebook₂[17]
vecteur dim 8"] --> APPROX CB3["codebook₃[5]
vecteur dim 8"] --> APPROX APPROX["≈ Somme additive
3 indices seulement"] --> OUT["✅ 65 536 combinaisons possibles
en 2-bit/poids"]

C'est comme la différence entre peindre pixel par pixel (quantification scalaire) et utiliser une palette de motifs pré-calculés qu'on superpose (additive quantization).


Papier source#

Élément Détail
Titre Extreme Compression of Large Language Models via Additive Quantization
Auteurs Vage Egiazarian, Andrei Panferov, Denis Kuznedelev, Elias Frantar, Artem Babenko, Dan Alistarh (IST Austria / Yandex Research)
Date Janvier 2024
Publication ICML 2024
arXiv 2401.06118
Code github.com/Vahe1994/AQLM

Mécanisme#

Niveau néophyte#

Imagine que tu veux compresser une longue liste de nombres. Au lieu de stocker chaque nombre individuellement (avec ses décimales), tu crées plusieurs petits « dictionnaires » de motifs typiques. Pour reconstituer un groupe de nombres, tu additionnes un motif de chaque dictionnaire. C'est comme décomposer une couleur complexe en mélangeant quelques couleurs de base (RGB) — sauf qu'ici on a plusieurs couches de « couleurs de base » (les codebooks) et on les additionne.

Niveau intermédiaire#

AQLM utilise la vector quantization additive. Pour chaque ligne de la matrice de poids, on approxime le vecteur original par :

w ≈ codebook₁[i₁] + codebook₂[i₂] + ... + codebook_K[iK]

Chaque codebook est une petite matrice de taille n_groups × group_size contenant des vecteurs de référence. Le nombre de codebooks K et leur taille n_groups contrôlent le bitrate effectif :

Bitrate par poids = K × log₂(n_groups) / group_size

Exemple : K=2 codebooks, n_groups=65536 (16-bit index), group_size=8
  → bitrate = 2 × 16 / 8 = 4 bits par poids

Exemple extrême : K=2, n_groups=256 (8-bit), group_size=8
  → bitrate = 2 × 8 / 8 = 2 bits par poids

Les codebooks sont appris pendant la quantification : un algorithme d'optimisation ajuste conjointement les vecteurs de codebook et les indices pour minimiser l'erreur d'approximation, en utilisant les données de calibration.

Niveau tech avancé#

AQLM formule la compression comme un problème d'optimisation conjointe :

Minimiser : Σ_layers || W - Σ_{k=1}^{K} C_k[i_k] ||²_H

  où :
  - W = matrice de poids originale (par couche)
  - C_k = k-ème codebook (matrice de vecteurs)
  - i_k = indice sélectionné dans le codebook k
  - ||·||²_H = norme pondérée par la Hessienne H = 2XᵀX
    (information de second ordre des activations de calibration)

Trois caractéristiques distinguent AQLM :

  1. Joint optimization across blocks : contrairement à GPTQ qui traite chaque couche indépendamment, AQLM optimise les codebooks conjointement à travers plusieurs blocs du transformer, capturant les dépendances inter-couches.

  2. Beam search pour l'index assignment : l'attribution des indices de codebook n'est pas greedy — AQLM utilise une beam search pour explorer l'espace des combinaisons d'indices et trouver la meilleure approximation additive.

  3. Input-adaptive codebooks : les codebooks sont optimisés en utilisant la Hessienne des activations réelles (pas seulement la magnitude des poids), ce qui rend la compression sensible au comportement fonctionnel du modèle.

Le coût : l'apprentissage des codebooks est itératif et coûteux — compter des heures à des dizaines d'heures pour un modèle de 7B-70B.


Illustration : AQLM vs quantification classique#

flowchart TD subgraph CLASSIC["Quantification classique (GPTQ, AWQ)"] W1["Poids FP16
[0.037, 0.142, 0.091, 0.218, ...]"] --> SQ["Quantification scalaire
(chaque poids séparément)"] SQ --> Q1["2-bit: [0, 1, 1, 2, ...]
Codebook scalaire: {-0.3, -0.1, +0.1, +0.3}
❌ Perte d'information massive"] end subgraph AQLM["AQLM (multi-codebook)"] W2["Poids FP16"] --> SPLIT["Découpage en vecteurs
group_size = 8 poids"] SPLIT --> OPT["Optimisation conjointe des codebooks
Codebook 1: 256 vecteurs dim 8
Codebook 2: 256 vecteurs dim 8
Sélection: w ≈ CB₁[42] + CB₂[17]
(beam search optimale)"] OPT --> STORE["Stockage: 2 indices 8-bit pour 8 poids
= 2 bits/poids
✅ 65 536 combinaisons additives possibles"] end

Détail du process AQLM#

flowchart TD M0["Modèle FP16 pré-entraîné
(ex: Llama-2-7B)"] --> S1 S1["1. Calibration
Passer 128-512 échantillons
→ Calculer Hessienne H = 2XᵀX par couche"] --> S2 S2["2. Initialisation des codebooks
K codebooks via K-means sur les vecteurs de poids"] --> S3 S3["3. Optimisation itérative"] --> S3A S3A["a) Beam search: trouver meilleurs indices
(i₁, i₂, ..., i_K) pour chaque vecteur"] --> S3B S3B["b) Mise à jour: ajuster vecteurs des codebooks
pour minimiser ||W - ΣCB||²_H"] --> S3C S3C["c) Block-wise: optimiser conjointement
à travers les blocs du transformer"] --> S3D{"Convergé ?"} S3D -- Non --> S3A S3D -- Oui --> S4 S4["4. Modèle quantifié AQLM
K codebooks + indices par vecteur
Taille: ~2 bits/poids
Kernels GPU/CPU optimisés"]

Bits / Formats supportés#

Configuration Codebooks Taille de codebook Bits/poids Supporté Notes
2-bit (1×16) 1 65 536 ~2.0 Configuration de base
2-bit (2×8) 2 256 ~2.0 Configuration recommandée
3-bit (2×12) 2 4 096 ~3.0 Bon ratio qualité/taille
3-bit (1×8) 1 256 ~1.0 ⚠️ Trop extrême, dégradation
4-bit ~4.0 ⚠️ GPTQ/AWQ plus adaptés à ce niveau

AQLM est conçu pour le régime sub-3-bit. À 4 bits et plus, les méthodes scalaires (GPTQ, AWQ) sont plus rapides et suffisent.


Résultats de perplexité#

AQLM est Pareto-optimal en accuracy-vs-model-size pour moins de 3 bits par poids.

Llama-2 (perplexité sur WikiText2 — plus bas = mieux)#

Bits/poids FP16 (ref) AQLM GPTQ AWQ QuIP#
16 (ref) 5.47
~2 ~7.5–8.0 11.36 8.48 7.27
xychart-beta title "Perplexité WikiText2 — Llama-2-7B à ~2 bits (plus bas = mieux)" x-axis ["FP16", "QuIP#", "AQLM~7.5", "AWQ", "GPTQ"] y-axis "Perplexité" 5 --> 12 bar [5.47, 7.27, 7.5, 8.48, 11.36]

QuIP# et AQLM sont les seules options vraiment viables en 2-bit.

À mémoire équivalente#

À empreinte mémoire IDENTIQUE (~2 GB pour un 7B) :

xychart-beta title "Perplexité à mémoire équivalente (~2 GB pour 7B)" x-axis ["AQLM 2-bit", "AWQ 2-bit", "GPTQ 2-bit"] y-axis "Perplexité" 5 --> 12 bar [7.5, 8.48, 11.36]

AQLM 2-bit offre le meilleur qualité/taille. GPTQ 2-bit est cassé.


Avantages et inconvénients#

✅ Avantages ❌ Inconvénients
Pareto-optimal en sub-3-bit (accuracy vs taille) Temps de quantification très long (heures → jours)
Surpasse significativement GPTQ/AWQ à 2 bits Complexité de mise en œuvre (apprentissage de codebooks)
Vitesse d'inférence ≥ FP16 optimisé Support limité dans les frameworks mainstream
Memory footprint très réduit (~2 bpw) Moins utile pour ≥ 4 bits (GPTQ/AWQ suffisent)
Kernels GPU et CPU optimisés Sensibilité aux hyperparamètres (K, group_size)
Joint optimization inter-blocs Calibration requise (données d'entrée)

Outils et implémentations#

auto-aqlm (outil principal)#

auto-aqlm est le pipeline automatisé pour quantifier n'importe quel modèle Hugging Face avec AQLM.

# Installation
pip install auto-aqlm

# Quantification 2-bit (configuration recommandée : 2 codebooks × 8-bit)
python -m auto_aqlm \
    --model meta-llama/Llama-2-7b-hf \
    --output_dir ./llama-7b-aqlm-2bit \
    --num_codebooks 2 \
    --num_indices = 256 \
    --in_group_size 8 \
    --calibration_dataset redpajama \
    --num_calibration_samples 128

# Résultat attendu :
# - Taille : 13.5 GB → ~2.5 GB (−81%)
# - Temps : 2–6 heures sur A100
# - PPL WikiText2 : ~7.5–8.0

Hugging Face Transformers#

from transformers import AutoModelForCausalLM, AutoTokenizer

# Charger un modèle AQLM pré-quantifié
model = AutoModelForCausalLM.from_pretrained(
    "BlackSamorez/Llama-2-7b-AQLM-2Bit-1x16-hf",
    device_map="auto",
    trust_remote_code=True,
)

tokenizer = AutoTokenizer.from_pretrained(
    "BlackSamorez/Llama-2-7b-AQLM-2Bit-1x16-hf"
)

# Inférence
inputs = tokenizer("Explique l'additive quantization :", return_tensors="pt")
outputs = model.generate(**inputs.to(model.device), max_new_tokens=200)
print(tokenizer.decode(outputs[0]))

Exemple pratique#

Quantifier Llama-2-7B avec auto-aqlm#

# 1. Installer auto-aqlm
pip install auto-aqlm transformers accelerate

# 2. Lancer la quantification (2-bit, 2 codebooks × 8-bit indices)
python -m auto_aqlm \
    --model meta-llama/Llama-2-7b-hf \
    --output_dir ./llama-2-7b-aqlm \
    --num_codebooks 2 \
    --in_group_size 8 \
    --num_indices 256 \
    --calibration_dataset c4 \
    --num_calibration_samples 128

# 3. Résultat attendu
# ┌──────────────────────────────────────────┐
# │  Llama-2-7b FP16  :  13.5 GB             │
# │  Llama-2-7b AQLM  :  ~2.5 GB  (−81%)     │
# │  Temps de quantif. :  2–6h sur A100      │
# │  PPL WikiText2     :  ~7.5               │
# └──────────────────────────────────────────┘

Inférence avec vLLM (support AQLM)#

# vLLM supporte les modèles AQLM
pip install vllm

# Servir un modèle AQLM
python -m vllm.entrypoints.openai.api_server \
    --model BlackSamorez/Llama-2-7b-AQLM-2Bit-1x16-hf \
    --trust-remote-code

Comparaison avec les alternatives#

Méthode Régime optimal 2-bit viable ? Temps de quantif. Calibration Support frameworks
AQLM ≤ 3 bit ✅ (compétitif) Très long (heures–jours) ✅ Requise Partiel (HF, vLLM)
QuIP# ≤ 3 bit ✅ ✅ (SOTA) Long ✅ Requise Partiel
GPTQ 4 bit ❌ (cassé) Rapide (minutes) ✅ Requise Excellent
AWQ 3–4 bit ⚠️ (dégradé) Rapide ✅ Requise Excellent
HQQ 3–4 bit ⚠️ (dégradé) Très rapide ❌ Non Bon

AQLM et QuIP# se disputent le SOTA en sub-3-bit. QuIP# est généralement légèrement plus précis ; AQLM offre des kernels d'inférence plus matures.


Références#

ia llm quantification aqlm additive-quantization codebook vector-quantization 2-bit 3-bit