Quantiser Qwen3-32B à la main – un modèle 32B sur 16 Go de VRAM
Comment j'ai quantisé Qwen3-32B avec llama.cpp et une matrice d'importance pour le faire tourner sur un GPU de 16 Go : conversion GGUF, imatrix, niveaux de quantisation comparés, budget VRAM et offload de couches.

Faire tourner un modèle dense de 32 milliards de paramètres sur un GPU de 16 Go de VRAM semble une pointure de trop – et en pleine précision, ça l’est. Mais avec une quantisation manuelle via llama.cpp et une matrice d’importance, Qwen3-32B tient très bien sur la carte. Voici le chemin complet que j’ai suivi – y compris le calcul qui explique pourquoi ça marche.
Le problème de la VRAM
Qwen3-32B compte ~32,8 milliards de paramètres répartis sur 64 couches transformer. La taille brute des poids varie linéairement avec les bits par poids :
| Précision | Bits/poids | Poids (≈) | Tient dans 16 Go ? |
|---|---|---|---|
| bf16 (original) | 16 | ~66 Go | aucune chance |
| Q8_0 | 8 | ~35 Go | non |
| Q6_K | ~6,6 | ~27 Go | non |
| Q5_K_M | ~5,7 | ~23 Go | non |
| Q4_K_M | ~4,8 | ~20 Go | pas tout à fait |
| IQ4_XS | ~4,3 | ~18 Go | presque |
| Q3_K_M | ~3,9 | ~16 Go | limite |
| IQ3_M | ~3,7 | ~15 Go | oui (avec contexte) |
| IQ2_M | ~2,7 | ~11 Go | oui, mais qualité ↓ |
(Chiffres arrondis – la taille réelle des fichiers varie légèrement.)
D’où la vraie décision : Q4_K_M (~20 Go) ne tourne qu’avec offload partiel (une partie des couches sur le CPU), tandis que IQ3_M (~15 Go) tient presque entièrement sur le GPU. Et c’est précisément là que la matrice d’importance entre en jeu.
Étape 1 : récupérer le modèle & convertir en GGUF
# Poids originaux depuis Hugging Face
huggingface-cli download Qwen/Qwen3-32B --local-dir ./Qwen3-32B
# Conversion en GGUF pleine précision (point de départ de la quantisation)
python llama.cpp/convert_hf_to_gguf.py ./Qwen3-32B \
--outfile qwen3-32b-bf16.gguf \
--outtype bf16Le bf16.gguf est l’intermédiaire de ~66 Go – il ne réside que sur le disque, pas en VRAM. Toutes les variantes quantisées en dérivent.
Étape 2 : calculer la matrice d’importance (imatrix)
La quantisation naïve traite chaque poids à l’identique. La imatrix mesure au contraire, sur du texte réel, quels poids comptent pour les sorties et les protège lors de la quantisation. Aux faibles largeurs de bits (tout ce qui est sous 4 bits), c’est la différence entre « utilisable » et « raconte n’importe quoi ».
# Texte de calibration : quelques centaines de Ko, mélange représentatif
# – prose, code et une part de votre propre domaine
./llama-imatrix \
-m qwen3-32b-bf16.gguf \
-f calibration.txt \
-o qwen3-32b.imatrix \
--chunks 200 \
-ngl 20Ce qui compte, c’est la composition de calibration.txt : si vous utilisez ensuite le modèle pour du code, mettez-y du code ; si vous travaillez en français, du texte français. La imatrix optimise exactement pour ce qu’elle voit.
Étape 3 : quantiser
# Entièrement sur GPU – le plus petit niveau utilisable, grâce à la imatrix
./llama-quantize \
--imatrix qwen3-32b.imatrix \
qwen3-32b-bf16.gguf \
qwen3-32b-IQ3_M.gguf \
IQ3_M
# Qualité supérieure, mais offload partiel nécessaire
./llama-quantize \
--imatrix qwen3-32b.imatrix \
qwen3-32b-bf16.gguf \
qwen3-32b-Q4_K_M.gguf \
Q4_K_MBudgéter la VRAM & choisir -ngl
Les poids ne sont pas la seule chose à loger en VRAM. Le budget réel :
VRAM utilisable ≈ 16 Go
− cache KV (croît avec la longueur de contexte)
− buffer de calcul (~0,5–1 Go)
− OS/pilote (~0,5–1 Go)Le cache KV est le poste variable le plus lourd. Qwen3-32B utilise une grouped-query attention à 8 têtes KV – ce qui garde le cache petit, mais à 8k de contexte en fp16 cela fait tout de même ~1–2 Go. Deux leviers le réduisent :
--flash-attn # attention plus efficace, buffer plus petit
-ctk q8_0 -ctv q8_0 # quantiser le cache KV lui-même en 8 bits → ~moitié du besoinIl reste ainsi ~13,5–14 Go pour les poids. Le calcul des couches pour Q4_K_M :
poids ≈ 20 Go / 64 couches ≈ 0,31 Go par couche
13,5 Go utilisables / 0,31 Go ≈ 43–44 couches sur le GPUDonc -ngl 44 : 44 couches tournent sur le GPU, le reste sur le CPU. Avec IQ3_M (~15 Go) en revanche, les 64 couches tiennent pratiquement toutes – -ngl 99 (llama.cpp plafonne au maximum) – et ça tourne entièrement sans offload CPU.
Étape 4 : lancer
# Variante A – IQ3_M, entièrement sur le GPU (rapide)
./llama-server \
-m qwen3-32b-IQ3_M.gguf \
-ngl 99 \
-c 8192 \
--flash-attn \
-ctk q8_0 -ctv q8_0 \
--host 0.0.0.0 --port 8080
# Variante B – Q4_K_M, offload partiel (meilleure qualité, plus lent)
./llama-server \
-m qwen3-32b-Q4_K_M.gguf \
-ngl 44 \
-c 8192 \
--flash-attn \
-ctk q8_0 -ctv q8_0Optionnel : intégrer dans Ollama
Comme le reste de mon installation (le wiki LLM à la Karpathy) tourne sur Ollama, le GGUF fini y passe via un Modelfile :
# Modelfile
FROM ./qwen3-32b-IQ3_M.gguf
PARAMETER num_ctx 8192
PARAMETER num_gpu 99ollama create qwen3-32b-local -f Modelfile
ollama run qwen3-32b-localRésultat & enseignements
Qwen3-32B tourne sur 16 Go – et de façon utile. Ce que j’en retiens :
- Tout sur le GPU l’emporte sur l’offload partiel. Une quantisation plus basse mais entièrement chargée (IQ3_M) est nettement plus rapide qu’une quantisation plus haute dont la moitié calcule sur le CPU – le transfert PCIe par token coûte plus que ce que la meilleure quantisation rapporte.
- La imatrix n’est pas optionnelle sous 4 bits. Sans elle, IQ3_M est sensiblement plus bête ; avec une bonne calibration, elle se rapproche étonnamment de Q4.
- Quantisation du cache KV + flash attention sont le gain discret : elles créent la marge pour loger soit plus de contexte, soit quelques couches de plus sur le GPU.
- Les données de calibration sont une affaire de domaine. La imatrix ne vaut que le texte qu’on lui montre – pour les tâches de code, il faut y mettre du code.
La quantisation manuelle est au fond un problème de budgétisation, pas de magie : arbitrer entre bits par poids, cache KV et offload de couches jusqu’à ce que le grand modèle tienne dans la petite carte.