Quantisation formats and what they cost
FP32 to INT4, NF4, GGUF k-quants, AWQ and GPTQ on one page, with bytes per parameter, memory for 7 B and 70 B models, reported accuracy impact and hardware support.
This page is for anyone deciding how to fit a model onto the hardware they have. Each row gives the storage cost, the memory for the weights of a 7 B and a 70 B dense model, what the format’s own paper or maintainers report about accuracy, and where the kernels exist. Weights are only part of the budget: the KV cache, activations and optimiser state come on top, and the KV cache dominates at long context. For the background see /topics/efficient-ml/ and /topics/hardware-and-acceleration/.
Number formats
Memory figures are weights only, computed as parameters times bytes per parameter, rounded. “Bits” counts the scales and zero points that block formats carry alongside the weights.
| Format | Bits per weight | Bytes per parameter | 7 B weights | 70 B weights | Reported accuracy impact | Hardware support |
|---|---|---|---|---|---|---|
| FP32 | 32 (1 sign, 8 exponent, 23 mantissa) | 4.0 | 28 GB | 280 GB | The reference | Everything |
| TF32 | 19 used (8 exponent, 10 mantissa), stored as FP32 | 4.0 | 28 GB | 280 GB | A compute format, not a storage one. NVIDIA reports matching FP32 training accuracy on its tested models | NVIDIA tensor cores from Ampere (2020), on by default for convolutions in PyTorch, off for matmul unless you set torch.backends.cuda.matmul.allow_tf32 = True |
| BF16 | 16 (8 exponent, 7 mantissa) | 2.0 | 14 GB | 140 GB | Kalamkar et al. (2019) report training matching FP32 across CNNs, RNNs and transformers without loss scaling | NVIDIA Ampere and later, AMD MI200 and later, TPU (its native format), Apple M2 and later, x86 CPUs with AVX-512 BF16 or AMX |
| FP16 | 16 (5 exponent, 10 mantissa) | 2.0 | 14 GB | 140 GB | Micikevicius et al. (2017) report matching FP32 with a master copy and loss scaling. Overflows above 65,504, so activations can blow up | Nearly every GPU since 2017 (NVIDIA Volta tensor cores, Pascal with reduced throughput), Apple Silicon, ARM CPUs with FP16 extensions |
| FP8 E4M3 | 8 (4 exponent, 3 mantissa) | 1.0 | 7 GB | 70 GB | Micikevicius et al. (2022) report training and inference results matching 16-bit on GPT-3 scale models with per-tensor scaling, E4M3 for weights and activations | NVIDIA Hopper and Ada (2022) and later, AMD MI300, via Transformer Engine or torch.float8_e4m3fn |
| FP8 E5M2 | 8 (5 exponent, 2 mantissa) | 1.0 | 7 GB | 70 GB | Same paper: the wider-range variant, used for gradients in the backward pass | Same hardware |
| INT8 (weights and activations) | 8 | 1.0 | 7 GB | 70 GB | Dettmers et al. (2022, LLM.int8) report no perplexity degradation up to 175 B once outlier features are kept in 16-bit. Xiao et al. (2022, SmoothQuant) report matching FP16 accuracy for W8A8 on OPT and BLOOM by migrating activation outliers into the weights | NVIDIA Turing and later tensor cores, Intel CPUs with VNNI or AMX, most mobile NPUs, AMD MI200 and later |
| INT4 (weight only, plain) | 4 plus a scale per group | 0.5 to 0.56 | 3.5 to 3.9 GB | 35 to 39 GB | Depends entirely on the rounding method. Round-to-nearest at 4-bit without calibration is where GPTQ and AWQ start from and both report it degrades noticeably on small models | No native INT4 tensor cores on Hopper (removed after Ampere). Weights are dequantised to 16-bit in the kernel. Blackwell adds FP4 |
| NF4 (bitsandbytes) | 4 plus a block scale (double-quantised) | about 0.53 | 3.7 GB | 37 GB | Dettmers et al. (2023, QLoRA) report that 4-bit NF4 fine-tuning with LoRA matches 16-bit full fine-tuning on their benchmarks, and that NF4 beats plain FP4 and INT4 at the same size | Software only: CUDA kernels in bitsandbytes, with ROCm and CPU backends newer and slower. Dequantised to BF16 for every matmul, so it is slower than 16-bit |
Model-file formats
These are packaging schemes that carry their own quantisation, used for inference rather than training.
| Format | Bits per weight | Bytes per parameter | 7 B file | 70 B file | Reported accuracy impact | Hardware support |
|---|---|---|---|---|---|---|
| GGUF Q8_0 | 8 plus one FP16 scale per 32 weights, 8.5 | 1.06 | 7.4 GB | 74 GB | The llama.cpp maintainers’ perplexity tables show it within noise of FP16 on Llama models | CPU (AVX2, AVX-512, NEON), Metal, CUDA, Vulkan, ROCm, SYCL, through llama.cpp and Ollama |
| GGUF Q6_K | about 6.6 | 0.82 | 5.7 GB | 57 GB | Maintainers’ tables: very small perplexity increase | Same |
| GGUF Q5_K_M | about 5.7 | 0.71 | 5.0 GB | 50 GB | Maintainers’ tables: small increase, usually the quality choice when it fits | Same |
| GGUF Q4_K_M | about 4.85 (4-bit weights, 6-bit block scales, some tensors at higher precision) | 0.61 | 4.2 GB | 42 GB | Maintainers’ tables: a noticeable but small perplexity increase, the default most tools pick | Same |
| GGUF Q4_0 | 4.5 | 0.56 | 3.9 GB | 39 GB | The legacy 4-bit scheme, worse than Q4_K_M at nearly the same size. ARM-optimised repacked variants exist | Same, fastest on some ARM CPUs |
| GGUF Q3_K_M and Q2_K | about 3.9 and 3.4 | 0.49 and 0.42 | 3.4 and 2.9 GB | 34 and 29 GB | Maintainers’ tables: clear degradation, worst on small models. Importance-matrix (imatrix) quantisation recovers part of it | Same |
| GPTQ (4-bit, group 128) | 4 plus FP16 scale and zero per 128, about 4.25 | 0.53 | 3.7 GB | 37 GB | Frantar et al. (2022) report negligible accuracy loss at 4-bit on OPT-175B and BLOOM-176B using second-order error correction, and usable 3-bit | CUDA and ROCm kernels (Marlin, ExLlama) in vLLM, GPTQModel and Transformers. Weight-only, dequantised in the kernel |
| AWQ (4-bit, group 128) | about 4.25 | 0.53 | 3.7 GB | 37 GB | Lin et al. (2023) report lower perplexity than GPTQ and round-to-nearest at the same bit width on Llama and OPT, by scaling the 1 % of channels that activations show to matter, with no backpropagation needed | CUDA kernels in vLLM, AutoAWQ and TensorRT-LLM. Weight-only |
The three 4-bit families land within 0.1 bytes per parameter of each other. Pick by where you run: GGUF for llama.cpp and Ollama, AWQ or GPTQ for vLLM on NVIDIA, NF4 for fine-tuning with QLoRA. Converting between them means re-quantising from the 16-bit original, not from each other.
What the numbers hide
- Weights are not the whole budget. A 7 B model at 4-bit is 4 GB of weights, but a 32 k context with a 32-layer, 8 KV-head model adds about 1 GB of KV cache per sequence at FP16. Serving engines quantise the cache too (FP8 KV cache in vLLM and TensorRT-LLM, Q8 in llama.cpp).
- Perplexity is the metric every paper reports and it understates damage on tasks with a single right answer, such as code and maths. Check a downstream benchmark on your task, with a confidence interval (see /reference/evaluation-metrics/).
- Small models lose more. The papers’ headline numbers are for 65 B to 175 B models. A 1 B or 3 B model at 4-bit can lose several points on reasoning benchmarks.
- Weight-only formats help memory and the memory-bound decode phase. They do not speed up prefill, which is compute-bound and runs in 16-bit after dequantisation. W8A8 and FP8 do speed up both.
- Quantised fine-tuning (QLoRA) trains adapters in 16-bit on top of frozen 4-bit weights. The base model’s rounding error is baked in.
- Speed depends on the kernel more than the format. The same AWQ checkpoint can run faster or slower than FP16 depending on batch size and GPU generation. Measure.
A worked int8 quantisation
Symmetric int8 maps the range [-max|w|, max|w|] onto [-127, 127] with one scale. Per-tensor scaling lets a single outlier waste most of the levels. Per-row (per-channel) scaling fixes that at the cost of one scale per output row, and is what every INT8 scheme in the table does.
import numpy as np
rng = np.random.default_rng(0)
w = rng.normal(0.0, 0.02, size=(256, 1024)).astype(np.float32) # a weight matrix like a small linear layer
w[3, 17] = 0.5 # one outlier, as real weights have
def quantise_int8_symmetric(x: np.ndarray, axis=None):
"""Symmetric int8: q = round(x / s), s = max|x| / 127. axis=None is per-tensor, axis=1 per output row."""
amax = np.abs(x).max() if axis is None else np.abs(x).max(axis=axis, keepdims=True)
scale = np.maximum(amax, 1e-12) / 127.0
q = np.clip(np.rint(x / scale), -128, 127).astype(np.int8)
return q, scale.astype(np.float32)
def dequantise(q, scale):
return q.astype(np.float32) * scale
for name, axis in [("per-tensor", None), ("per-row", 1)]:
q, s = quantise_int8_symmetric(w, axis)
w_hat = dequantise(q, s)
err = w_hat - w
rel = np.linalg.norm(err) / np.linalg.norm(w)
zeros = (q == 0).mean()
print(f"{name:11} bytes/param 1.0 relative error {rel:.4f} max abs error {np.abs(err).max():.5f} weights rounded to zero {zeros:.1%}")
print("fp32 bytes:", w.nbytes, " int8 bytes:", w.size, " ratio:", w.nbytes / w.size)
per-tensor bytes/param 1.0 relative error 0.0568 max abs error 0.00197 weights rounded to zero 7.8%
per-row bytes/param 1.0 relative error 0.0087 max abs error 0.00197 weights rounded to zero 1.1%
fp32 bytes: 1048576 int8 bytes: 262144 ratio: 4.0
One outlier at 0.5 in a matrix of weights around 0.02 makes the per-tensor scale 25 times coarser than the rows need, rounds 7.8 % of the weights to zero and multiplies the error by six. Per-row scaling leaves one bad row and 255 good ones. Group-wise scaling (every 32 or 128 weights, as in GGUF, GPTQ and AWQ) is the same idea taken further, which is why those formats carry scales and why their bits per weight are not round numbers.
Sources
- Kalamkar et al., A Study of BFLOAT16 for Deep Learning Training, 2019
- Micikevicius et al., Mixed Precision Training, 2017
- Micikevicius et al., FP8 Formats for Deep Learning, 2022
- Dettmers et al., LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale, 2022
- Xiao et al., SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models, 2022
- Dettmers et al., QLoRA: Efficient Finetuning of Quantized LLMs, 2023
- Frantar et al., GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers, 2022
- Lin et al., AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration, 2023
- GGUF specification and the llama.cpp quantisation tool
- NVIDIA TensorFloat-32 in the A100 (TF32)
- PyTorch numerical accuracy and TF32 notes
- bitsandbytes documentation and vLLM quantisation support