75 minutes · Layer 4 (Export). Matrix · Trade-off · Workflows · Dynamic 2.0 · After-training
The format is determined by where the model will run. Pick the runtime, then the format. Default to four-bit.
Pillar 6 — Deploy · First module
You finished FT11. You have a fine-tuned checkpoint in FP16/BF16 — sixteen bits per parameter. That is far too large to deploy cheaply.
Not the same as QLoRA (FT08): QLoRA quantizes the base during training to fit VRAM (the adapter stays high-precision). That is quantize-to-train. FT19 is quantize-to-serve.
| Deployment target | Format |
|---|---|
| Local / CPU / mixed (Ollama, llama.cpp) | GGUF (Q4_K_M) |
| NVIDIA GPU prod serving (vLLM, TGI) | AWQ (AWQ-Marlin) |
| Max quality @ low bitrate (ExLlamaV2) | EXL2 (variable-rate) |
| Apple Silicon native | MLX (4-bit group) |
| VRAM-rich Hopper/Blackwell (high quality) | FP8 |
| Blackwell-native 4-bit (2025 frontier) | MXFP4 / NVFP4 |
GPTQ is the mature 4-bit alternative — slightly worse than AWQ at 4-bit, ubiquitous.
Consumed by llama.cpp and everything on it: Ollama, LM Studio, llamafile, KoboldCpp.
One self-contained .gguf — weights, tokenizer, config, chat template.
Runs on CPU, mixed CPU/GPU offload, Mac Metal.
The sweet spot
Q4_K_M
~4 bits/param · ~75% smaller than FP16 · minimal loss.
7B: 14 GB → ~4.4 GB
K-quant ladder: Q2_K → Q3_K_M → Q4_K_M → Q5_K_M → Q8_0
AWQ (preferred)
Activation-aware Weight Quantization. Calibration finds the ~1% of salient weights; those stay high-precision.
AWQ-Marlin = optimized vLLM kernel. Best speed/quality for server inference.
Lower perplexity than GPTQ at the same 4-bit size.
GPTQ (mature, ubiquitous)
One-shot post-training 4-bit. Slightly worse quality than AWQ at 4-bit. Showing its age.
Nearly every HF model has a GPTQ variant. Fine where it is what tooling supports.
GPTQ-Marlin kernel also exists for vLLM.
| Format | Superpower | The catch |
|---|---|---|
| EXL2 | Variable-rate per-layer quant → best perplexity at small sizes | ExLlamaV2-only (vLLM experimental) |
| MLX | Apple Silicon-native; exploits unified memory → best Mac perf | Mac only. 4-bit group quant default. |
| FP8 | 2x size/speed over FP16, negligible loss, Hopper/Blackwell HW accel | Needs VRAM headroom |
| MXFP4 / NVFP4 | 2025 Blackwell-native 4-bit (E2M1). Block: MXFP4=32, NVFP4=16 (finer → better accuracy) | Newest; Blackwell hardware |
All four answer "what is the right format?" with "what is your hardware and what are you optimizing for?"
| Quant | Size vs FP16 | Quality |
|---|---|---|
| Q2 | ~88% smaller | noticeable degradation — desperate only |
| Q3 | ~84% smaller | measurable degradation |
| Q4 | ~75% smaller | SWEET SPOT — minimal loss |
| Q5 | ~70% smaller | near-lossless |
| Q8 | ~58% smaller | effectively lossless |
# GGUF — llama.cpp
python convert_hf_to_gguf.py ./model --outfile model-f16.gguf
llama-quantize model-f16.gguf model-Q4_K_M.gguf Q4_K_M
# (or Unsloth: model.save_pretrained_gguf("out", quantization_method="q4_k_m"))
# AWQ — AutoAWQ
model = AutoAWQForCausalLM.from_pretrained("./model")
model.quantize(tokenizer, quant_config={"w_bit":4,"q_group_size":128,"version":"GEMM"})
model.save_quantized("./model-awq")
# MLX — mlx-lm
python -m mlx_lm.convert --hf-path ./model --quantize --q-bits 4 --mlx-path ./model-mlx
Uniform quant (standard Q4_K_M, AWQ 4-bit) gives every layer the same bitrate. But not all layers are equally sensitive.
| Layer type | Uniform Q4 | Dynamic 2.0 |
|---|---|---|
| Attention projections (sensitive) | Q4 — quality lost | Q6 — kept high |
| Tolerant MLP block | Q4 — bits wasted | Q3 — compressed |
| Average | Q4 | Q4 (same size, HIGHER quality) |
Three reasons quantization is Layer 4, not part of Layer 3:
Next: FT20 — Serving Stacks