← ContentsChapter 7 of 7
Chapter 07

Which one should you actually download?

The frontier from chapter 4 compresses into a fairly simple rule. Assume you want the best model that fits your memory.

Available memoryChooseWhy
Fits at Q8_0Q6_KEffectively indistinguishable from BF16; Q8_0 rarely earns its extra 2 bpw
Fits at ~4.5 bpwQ4_K_M / Q4_K_XLThe sweet spot. KLD ~0.02. Where almost everyone should sit
Slightly short at 4-bitIQ4_XS or Q3_K_XLIQ4_XS is ~4.25 bpw with codebook efficiency; Q3_K_XL is a real but acceptable step down
Only 2–3 bpw fitsIQ2_M / Q2_K_XLMeaningful degradation, but a bigger model at 2-bit usually beats a smaller model at 4-bit
DesperateIQ1_M over IQ1_SThe 0.19 bpw from IQ1_S to IQ1_M buys a large quality jump — see the KLD chart
GPU serving, high batchAWQ or FP8GGUF is optimised for single-stream and CPU; GPU-native formats batch better
Fine-tuningNF4 + QLoRAbitsandbytes format, integrates with the PEFT/Unsloth training path

Rules of thumb worth internalising

Reproduce any of this yourself

# 1 — convert to GGUF at full precision
python convert_hf_to_gguf.py ./model \
    --outfile model-f16.gguf --outtype f16

# 2 — collect the importance matrix from a calibration corpus
./llama-imatrix -m model-f16.gguf -f calibration.txt \
    -o imatrix.dat --chunks 200

# 3 — quantize, with per-tensor overrides (the "dynamic" part)
./llama-quantize --imatrix imatrix.dat \
    --tensor-type "attn_v=q6_K" \
    --tensor-type "ffn_down=q6_K" \
    --token-embedding-type q6_K \
    --output-tensor-type q6_K \
    model-f16.gguf model-IQ2_XXS.gguf IQ2_XXS

# 4 — measure what you did, against the f16 original
./llama-perplexity -m model-IQ2_XXS.gguf -f test.txt \
    --kl-divergence-base f16-logits.dat --kl-divergence

Step 4 is the one people skip and shouldn't. It's the only way to know whether step 3 helped. Use a held-out corpus that does not resemble your calibration data, or you're grading your own homework.

The whole series in one paragraph

Summary. Quantization works because trained weights occupy a tiny, predictable slice of the range their format can represent, and because inference is starved for memory bandwidth rather than arithmetic. You recover the wasted bits by factoring out a shared scale per small block, shaping the grid to match the weight distribution, and — the part that matters most below 4 bits — measuring which weights actually influence the output and spending your bit budget there. At that point "how many bits" stops being one number and becomes a per-tensor allocation problem, which is exactly what Unsloth's dynamic quants solve, and why a 131 GB build of a 720 GB model can still write working code while a naive 175 GB build cannot.
The seven things to remember
  1. Decoding is memory-bound. tokens/s ≈ bandwidth ÷ model bytes.
  2. Quantization = store integers plus a shared scale. Everything else is detail.
  3. One outlier ruins a per-tensor scale. Block-wise scaling costs ~0.5 bpw and fixes it.
  4. Grid shape matters: NF4 puts levels where the probability mass is.
  5. k-quants quantize the scales too, in a two-level hierarchy.
  6. Below 4 bits, importance-weighted objectives and codebooks are mandatory, not optional.
  7. Bit allocation across tensors dominates the size–quality frontier.

Where to go next

Unsloth — Dynamic 2.0 GGUFsMethodology, the Gemma 3 MMLU and KLD tables, QAT comparison Unsloth — DeepSeek-R1 Dynamic 1.58-bit720GB → 131GB, layer allocation, the Flappy Bird benchmark llama.cpp — Tensor Encoding SchemesExact bpw and block structure for every GGUF type QLoRA (Dettmers et al.)NF4, double quantization, paged optimisers Accuracy is Not All You NeedFlips, and why KL divergence is the right metric BitNet b1.58What happens when you never leave ternary