Sub-byte Quantification — Quantification sous l'octet#
TL;DR — La quantification sub-byte désigne toute méthode qui code les poids en moins de 8 bits (4-bit, 3-bit, 2-bit, voire 1.58-bit). Le défi technique central est le bit-packing : le hardware travaillant par octets, il faut empaqueter plusieurs valeurs sub-byte dans chaque mot mémoire. Les méthodes avancées (AQLM, QuIP#, HQQ) utilisent des codebooks et de la vector quantization non-uniforme pour rester viables en régime extrême.
Le concept : descendre sous l'octet#
Pourquoi le sub-byte ?#
Un octet (byte) = 8 bits. Le hardware (CPU, GPU, RAM) adresse la mémoire par octets. Mais 8-bit par poids, c'est encore beaucoup pour un LLM de 70B (70 GB). Descendre à 4-bit (35 GB), 3-bit (26 GB), ou 2-bit (17.5 GB) permet :
- Faire tourner de très grands modèles sur du hardware limité
- Servir plus de modèles en parallèle sur le même GPU
- Déployer sur edge devices (mobile, embarqué)
| Bits/poids | 70B (taille) | Viabilité & Méthodes |
|---|---|---|
| 16 (FP16) | 140 GB | Référence — pleine précision |
| 8 (INT8) | 70 GB | ✅ Trivial — LLM.int8(), FP8 |
| 4 (INT4) | 35 GB | ✅ Sweet spot — GPTQ, AWQ, GGUF |
| 3 (INT3) | 26 GB | ⚠️ Dégradé — HQQ, OmniQuant |
| 2 (INT2) | 17 GB | 🔴 Extrême — QuIP#, AQLM |
| 1.58 (tern.) | 14 GB | 🔴 Radical — BitNet b1.58 |
| 1 (binary) | 9 GB | 🔴🔴 Expérimental — BitNet b1 |
L'analogie pour néophyte#
Imagine que tu dois ranger des objets dans des boîtes qui font toutes la même taille (un octet). En 8-bit, chaque objet a sa propre boîte. En 4-bit, tu mets deux objets par boîte. En 2-bit, tu en mets quatre. Mais en 3-bit, ça ne tombe pas juste : tu ne peux pas mettre un nombre entier d'objets 3-bit dans une boîte 8-bit (8÷3 = 2.67). Il faut donc un astucieux système d'empilement (bit-packing) qui répartit les objets sur plusieurs boîtes. C'est ça, le défi sub-byte.
Le défi technique : le bit-packing#
Le problème fondamental#
Le hardware accède à la mémoire par mots de 8, 16, 32 ou 64 bits. Une valeur de 3 bits ne peut pas être adressée directement.
8 valeurs × 3 bits = 24 bits exactement"] WARN1["⚠️ Accès à une valeur = lire 2 octets"] end subgraph INT2["INT2 (2-bit) — 4 valeurs par octet"] B4["🟦 v1 | 🟩 v2 | 🟧 v3 | 🟥 v4 → 1 byte"] OK2["✅ Tombe juste : 8 / 2 = 4"] end
Le dé-empaquetage (unpacking) à la volée#
Pendant l'inférence, les valeurs packed doivent être dépaquetées avant le GEMM :
→ En streaming pendant le GEMM
→ Overhead si mal optimisé"]
Block-wise quantization#
Pour réduire l'overhead des scale factors, on partage les métadonnées par blocs :
= 0.125 bits/poids supplémentaires → négligeable"] BLOCK --> OVERHEAD
L'espace de quantification : uniforme vs non-uniforme#
(poids concentrés autour de 0)"] U5["Méthodes : GPTQ, AWQ, GGUF Q4_0"] U1 --> U2 --> U3 --> U4 --> U5 end subgraph NONUNIFORM["Quantification Non-Uniforme (codebook / vector quant)"] N1["Niveaux concentrés là où les poids sont denses"] N2["↑ densité autour de 0 ↑"] N3["✅ Plus efficace à bas bitrate"] N4["✅ Codebooks appris → adaptés au modèle"] N5["❌ Plus complexe, pas de support HW natif"] N6["Méthodes : QuIP# (E8 lattice), AQLM, SqueezeLLM"] N1 --> N2 --> N3 --> N4 --> N5 --> N6 end
Méthodes principales de quantification sub-byte#
Vue d'ensemble#
| Méthode | Bits | Approche | Calibration | SOTA à ce niveau ? | Page détaillée |
|---|---|---|---|---|---|
| GGUF K-quants | 2-4 | Block-wise uniforme | Non (legacy) / Oui (I-quants) | Non (pratique) | GGUF |
| GGUF I-quants | 2-4 | Importance matrix (non-uniforme) | Oui | Compétitif | GGUF |
| HQQ | 2-4 | Half-quadratic optimization | Non | Bon à 3-bit | HQQ |
| QuIP# | 2-3 | Hadamard + E8 lattice codebook | Oui | SOTA à 2-bit | QuIP# |
| AQLM | 2-3 | Multi-codebook additive vector quant | Oui | SOTA à 2-bit | AQLM |
| SqueezeLLM | 3 | Sensitivity-based non-uniform + dense-sparse | Oui | Bon à 3-bit | SqueezeLLM |
| SpQR | 3-4 | Sparse outlier isolation | Oui | Near-lossless | SpQR |
| BitNet b1.58 | 1.58 | QAT from scratch (ternaire) | N/A | Révolutionnaire | BitNet |
Détail des approches#
1. Bit-packing uniforme (GPTQ, AWQ, GGUF legacy)#
Le schéma le plus répandu : chaque poids est quantifié indépendamment avec une grille uniforme.
2. Codebook / Vector Quantization (QuIP#, AQLM)#
Au lieu de quantifier chaque poids indépendamment, on quantifie des groupes de poids conjointement via un codebook.
2-bit : 4 niveaux possibles par poids"] S1 --> S2 --> S3 --> S4 --> S5 end subgraph VQ["Vector Quantization (groupé)"] V1["w₁ w₂ w₃ w₄ w₅ w₆ w₇ w₈
(vecteur de 8 poids)"] V2["Codebook de 256 vecteurs (chacun dim 8)"] V3["→ Trouver le vecteur le plus proche"] V4["→ 1 index 8-bit pour 8 poids = 1 bit/poids !"] V5["✅ Exploite les corrélations entre poids"] V6["✅ Beaucoup plus expressif à bas bitrate"] V7["❌ Codebook doit être stocké + recherché"] V1 --> V2 --> V3 --> V4 --> V5 --> V6 --> V7 end
3. QuIP# : E8 lattice codebook#
QuIP# utilise un codebook basé sur le réseau E8 — le packing optimal de la sphère unité en 8 dimensions.
forment un ensemble symétrique optimal"] E3["QuIP# utilise ces points comme niveaux
de quantification pour 8 poids simultanément"] E4["→ Théoriquement optimal pour poids sub-Gaussiens"] E5["Après Hadamard preprocessing, les poids sont
effectivement sub-Gaussiens → E8 quasi-optimal"] E6["🏆 Résultat : SOTA à 2-bit (PPL ~7.2 sur Llama-2-7B)"] E1 --> E2 --> E3 --> E4 --> E5 --> E6
4. AQLM : multi-codebook additive#
AQLM approxime chaque vecteur de poids par une somme de vecteurs issus de plusieurs codebooks :
(256 entrées)"] CB2["Codebook 2
(256 entrées)"] SUM["w ≈ CB1[i₁] + CB2[i₂]"] W["Poids approximé"] CB1 -->|"index i₁ (8-bit)"| SUM CB2 -->|"index i₂ (8-bit)"| SUM SUM --> W INFO["2 × 8-bit pour 8 poids = 2 bits/poids
256 × 256 = 65 536 combinaisons additives
→ bien plus riche que 4 niveaux scalaires"]
5. HQQ : half-quadratic, sans calibration#
HQQ formule la quantification comme un problème d'optimisation data-free :
Diagramme : l'espace qualité/bits des méthodes sub-byte#
K-quants et I-quants de GGUF : le sub-byte grand public#
Le format GGUF de llama.cpp propose les schémas sub-byte les plus accessibles :
K-quants (block-wise, scales variables)#
| Type | ~bpw | Description |
|---|---|---|
| Q2_K | ~2.6 | 2-bit poids + 4-bit scales |
| Q3_K | ~3.4 | 3-bit poids + scales variables |
| Q4_K | ~4.6 | 4-bit poids + 6-bit scales (sweet) |
| Q5_K | ~5.7 | 5-bit + scales |
| Q6_K | ~6.6 | 6-bit + scales (quasi-lossless) |
I-quants (importance matrix)#
| Type | bpw | Description |
|---|---|---|
| IQ2_XXS | 2.0 | 2-bit extrême, matrice d'importance |
| IQ2_XS | 2.2 | 2-bit optimisé |
| IQ2_S | 2.4 | 2-bit amélioré |
| IQ3_XXS | 3.0 | 3-bit extrême |
| IQ3_S | 3.2 | 3-bit optimisé |
| IQ4_XS | 4.25 | 4-bit compact |
Les I-quants utilisent une matrice d'importance (issue de calibration) pour guider le placement des niveaux de quantification → meilleure qualité que les K-quants à bitrate égal en régime sub-4-bit.
Le state of the art en sub-byte#
Classement par régime de bitrate#
| Régime | SOTA | PPL (Llama-2-7B) | Notes |
|---|---|---|---|
| ~4 bit | AWQ / GPTQ | ~5.5 | Quasi-lossless, très supporté |
| ~3 bit | SqueezeLLM / HQQ | ~5.8-6.0 | Bonne qualité |
| ~2.5 bit | GGUF IQ2_S | ~8.0 | Utilisable sur CPU |
| ~2 bit | QuIP# | ~7.2 | SOTA PTQ à 2-bit |
| ~2 bit | AQLM | ~7.5 | Compétitif, kernels matures |
| 1.58 bit | BitNet b1.58 | ~10.5* | *Mais matche FP16 à taille équivalente (from scratch) |
⚠️ Les valeurs de PPL sont approximatives et dépendent du modèle, du dataset et des hyperparamètres. Elles sont indicatives pour situer les méthodes relatives les unes aux autres.
Exemple pratique#
Quantification sub-byte avec llama.cpp#
# Cloner llama.cpp
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp && mkdir build && cd build
cmake .. && make -j
# Quantifier en Q3_K (3-bit, ~3.4 bpw)
./bin/llama-quantize \
models/llama-7b-fp16.gguf \
models/llama-7b-q3_k.gguf \
Q3_K
# Quantifier en IQ3_XXS (3-bit importance matrix, ~3.0 bpw)
./bin/llama-quantize \
models/llama-7b-fp16.gguf \
models/llama-7b-iq3xxs.gguf \
IQ3_XXS
# Quantifier en Q2_K (2-bit, ~2.6 bpw)
./bin/llama-quantize \
models/llama-7b-fp16.gguf \
models/llama-7b-q2_k.gguf \
Q2_K
# Inférence avec le modèle quantifié
./bin/llama-cli \
-m models/llama-7b-q2_k.gguf \
-p "Explique la quantification sub-byte : " \
-n 200
Quantification 2-bit avec QuIP#
# Installer QuIP#
git clone https://github.com/Cornell-RelaxML/quip-sharp.git
cd quip-sharp
pip install -e .
# Quantification 2-bit avec codebook E8 lattice
python quantize.py \
--model meta-llama/Llama-2-7b-hf \
--output_dir ./llama-7b-quip-sharp-2bit \
--codebook E8P12 \
--bits 2 \
--calibration_dataset redpajama
Quantification 3-bit sans calibration avec HQQ#
from hqq import HQQModel, HQQLinear
# Quantification 3-bit sans aucune donnée de calibration
quant_config = {
"weight_quant_params": {
"nbits": 3,
"group_size": 64,
"quant_zero": True,
"quant_scale": True,
}
}
model = HQQModel.from_pretrained("meta-llama/Llama-2-7b-hf")
model.quantize_model(quant_config=quant_config)
# → 70B en 3-bit en moins de 5 minutes, sans calibration !
Vérifier le bitrate effectif#
import torch
def check_effective_bpw(model_path):
"""Calcule le bits-per-weight effectif d'un modèle quantifié."""
model = torch.load(model_path, map_location="cpu")
total_params = 0
total_bytes = 0
for name, param in model.named_parameters():
n_params = param.numel()
n_bytes = param.storage().nbytes()
total_params += n_params
total_bytes += n_bytes
bpw = (total_bytes * 8) / total_params
print(f"Paramètres : {total_params / 1e9:.1f}B")
print(f"Taille : {total_bytes / 1e9:.2f} GB")
print(f"Bits/poids effectif : {bpw:.2f}")
return bpw
Avantages et inconvénients du sub-byte#
| ✅ Avantages | ❌ Inconvénients |
|---|---|
| Compression maximale (4× à 8× vs INT8) | Overhead de dépaquetage (bit-packing/unpacking) |
| Déployer de très grands modèles sur hardware limité | Pas de support hardware natif sub-byte (sauf FP4 Blackwell) |
| Schémas par blocs → qualité adaptative | Qualité dégradée en deçà de 3-bit (sauf codebooks) |
| Codebooks non-uniformes → expressivité à bas bitrate | Vector quantization complexe à optimiser |
| I-quants GGUF : sub-byte accessible au grand public | Temps de quantification long (QuIP#, AQLM) |
| BitNet 1.58-bit : révolution potentielle | BitNet : from scratch + hardware non adapté |
L'avenir du sub-byte#
Références#
- QuIP# — Tseng et al., "QuIP#: Even Better LLM Quantization with Hadamard Incoherence and Lattice Codebooks", ICML 2024 — arXiv:2402.04396
- AQLM — Egiazarian et al., "Extreme Compression of Large Language Models via Additive Quantization", ICML 2024 — arXiv:2401.06118
- HQQ — Badri & Emad, "Half-Quadratic Quantization of Large Machine Learning Models", 2023 — Blog technique
- SqueezeLLM — Kim et al., "SqueezeLLM: Dense-and-Sparse Quantization", ICML 2024 — arXiv:2306.07629
- SpQR — Dettmers et al., "SpQR: A Sparse-Quantized Representation", ICLR 2024 — arXiv:2306.03078
- BitNet b1.58 — Ma et al., "The Era of 1-bit LLMs", 2024 — arXiv:2402.17764
- GGUF evaluation — "Which Quantization Should I Use?", 2026 — arXiv:2601.14277
- Code officiel llama.cpp — github.com/ggml-org/llama.cpp
- Voir aussi : BitNet b1.58 · AQLM · QuIP# · HQQ · SqueezeLLM · GGUF · Index quantification