Chapitre 19 · 10 min
Quantification simple
Réduis ton checkpoint local et son coût d’inférence avec la quantification INT8, puis vois jusqu’où vont les méthodes de production.
Un transformer entraîné est une collection de matrices de poids remplies de nombres float32. Un modèle 1B paramètres en float32 pèse 4 GB. Le même en int8 pèse 1 GB. En int4, 500 MB. La différence de qualité entre float32 et int8 est souvent faible, et le gain d’inférence est réel.
C’est la quantification : une idée simple appliquée systématiquement aux matrices de poids pour rendre les grands modèles moins chers à exécuter. La échange une petite perte de qualité contre une réduction de taille et une accélération d'inférence.
1. Quantification uniforme symétrique
Le schéma le plus simple : choisir une échelle s, stocker chaque poids comme petit entier, puis multiplier par s pour reconstruire une approximation :
Pour INT8 symétrique, s = max(|w|) / 127. Chaque poids devient un entier entre [-127, 127].
Code · JavaScript
Fais varier le nombre de bits. À 8 bits, la reconstruction est presque identique. À 4 bits, elle reste reconnaissable mais en escaliers. À 2 bits, on perd presque toute la forme.
2. Pourquoi ça marche
La compression seule ne suffit pas : le modèle doit calculer avec ces poids. En pratique, la qualité ne s’effondre pas, pour deux raisons :
- Les réseaux sont robustes aux petites perturbations de poids.
- L’erreur est à moyenne proche de zéro et s’annule partiellement dans les multiplications.
En INT8, la perplexité augmente souvent de seulement 1-2 %.
3. Où la quantification naïve échoue
Un seul scale par matrice laisse de la performance sur la table. En production, on utilise :
- Scales par canal : une échelle par ligne de
W. - Quantification attentive aux outliers : LLM.int8, GPTQ, AWQ gèrent les poids très grands qui dominent
max(|w|).
Ces méthodes ajoutent de la complexité mais récupèrent beaucoup de qualité sur les grands modèles. Ici, le schéma symétrique simple suffit.
4. La version PyTorch
Sauvegarde scripts/quantize.py :
"""scripts/quantize.py — apply dynamic INT8 quantization to a trained model."""
import torch
from llm.model import GPT, GPTConfig
# [1]
cfg = GPTConfig()
model = GPT(cfg)
model.load_state_dict(torch.load("checkpoints/model.pt", map_location="cpu"))
model.eval()
# Dynamic quantization: weights become INT8 at load time;
# activations stay float and are quantized on-the-fly during the forward pass.
# [2]
qmodel = torch.quantization.quantize_dynamic(
model,
{torch.nn.Linear}, # which layer types to quantize
dtype=torch.qint8,
)
# Compare file sizes
# [3]
torch.save(model.state_dict(), "checkpoints/model_fp32.pt")
torch.save(qmodel.state_dict(), "checkpoints/model_int8.pt")
import os
# [4]
fp32_size = os.path.getsize("checkpoints/model_fp32.pt")
int8_size = os.path.getsize("checkpoints/model_int8.pt")
print(f"fp32: {fp32_size / 1024:.1f} KB")
print(f"int8: {int8_size / 1024:.1f} KB")
print(f"ratio: {fp32_size / int8_size:.2f}x")- [1] charge le checkpoint normal sur CPU.
- [2]
quantize_dynamicconvertit les poids desnn.Linearen INT8. - [3] sauvegarde les deux versions.
- [4] mesure le ratio.
Lance :
python -m scripts.quantizepython -m scripts.quantizepython -m scripts.quantizePour le modèle 14M paramètres, tu devrais voir environ 3× de réduction. L’inférence CPU devient aussi plus rapide.
Et INT4 ?
INT4 donne environ 8× de compression et domine les setups llama.cpp. INT2 marche rarement. Les méthodes modernes comme GPTQ et AWQ utilisent un petit dataset de calibration pour minimiser la perte après quantification.
Recap
- La quantification échange un peu de qualité contre 4-8× moins de taille et 2-4× plus de
vitesse. - La quantification uniforme symétrique utilise
q = round(w / s). - INT8 est presque gratuit en qualité ; INT4 demande de meilleures méthodes ; INT2 échoue souvent. -torch.quantization.quantize_dynamicsuffit pour un premier INT8. - Ton projet local a maintenantscripts/quantize.py.
Pour aller plus loin
Prochaine étape : parler à ton modèle — ton checkpoint est entraîné et moins cher à lancer. Il reste à l’emballer dans une interface de chat minimale.