22 — Inference

Quantization#

INT8 · INT4 · GPTQ · AWQMathematical 1 min read
◆ The PatternShrink models 2–4× with minimal quality loss

Quantization reduces the precision of model weights from 16-bit floats to 8-bit or 4-bit integers. This halves (or quarters) memory usage and speeds up memory-bound inference. The challenge: preserving output quality.

Linear quantization: q = round((x − zero) / scale) x ≈ q · scale + zero
Map continuous weights to discrete integer grid. Scale and zero-point define the mapping.
Model sizes: FP16=2B/param → INT8=1B/param → INT4=0.5B/param
A 70B model: 140GB (FP16) → 70GB (INT8) → 35GB (INT4). In INT4 it fits on a single 80 GB GPU; FP16 needs at least two.

Methods: (1) GPTQ — weight-only, layer-by-layer with Hessian info, (2) AWQ — activation-aware, protects salient weights, (3) GGUF — CPU-friendly mixed-precision, (4) bitsandbytes — NF4 datatype for QLoRA. Weight-only quantization (activations stay in fp16) is most common for LLMs.

INT8 quantization has virtually no quality loss for most tasks. INT4 shows small degradation but is the sweet spot for serving — 4× memory savings are too compelling to ignore.
Interactive — precision comparison

Python — quantize with bitsandbytes#

Pattern bridge: Reducing precision from float32 to int8/int4 is binning applied to weights — discrete approximation of continuous values. The production side — calibration, accuracy checks and serving — is in quantization for MLOps.
Performance in practice
  • Good 4-bit methods (GPTQ, AWQ) usually lose little on standard benchmarks; maths, code and long-context tasks tend to degrade first. llama.cpp’s GGUF format runs 4-bit 70B models on machines with 64 GB of memory
  • Memory: FP32→FP16 = 2×, FP16→INT8 = 2×, INT8→INT4 = 2×. A 70B model goes from 280 GB → 35 GB
  • Speed: weight-only INT4 can be several times faster than FP16 at small batch sizes, where memory bandwidth is the bottleneck (AWQ reports over 3×; Lin et al. 2024)
  • AWQ protects the most important weights using activation statistics — better quality than naive rounding
When to use this
✓ Use when: Deploying models locally or on limited hardware. Reducing inference costs in production. Running large models (30B+) on consumer GPUs. Edge deployment on phones/laptops.
✗ Skip when: Using cloud APIs (already optimized). Tasks requiring maximum precision (scientific computation). Small models (<1B) that already fit in memory. Training — quantize for inference only.
Worked example

A 70B model needs 140 GB in fp16, 70 GB in int8 and 35 GB in int4. How it is quantized matters as much as the bit width: with a few outlier weights, one int4 scale for the whole tensor gives an average error of 87% of a weight’s size; a separate scale per group of 128 weights cuts it to 30%. Group-wise scales are why 4-bit models work.

Where it misleads
  • Average reconstruction error is not task quality; evaluate on the tasks you care about, including long-context and maths, which degrade first.
  • Weight-only quantization saves memory and bandwidth; it speeds up compute-bound batches much less.
  • Below 4 bits quality usually falls quickly (Dettmers & Zettlemoyer 2023).
In code
sizes = {bits: 70e9 * bits / 8 / 1e9 for bits in (16, 8, 4)}   # a 70B model, GB
rng = np.random.default_rng(1)
w = rng.normal(0, 0.02, 4_096)
w[::512] = 0.5                                    # a few outlier weights

def int4_error(w, group):
    g = w.reshape(-1, group)
    scale = np.abs(g).max(axis=1, keepdims=True) / 7
    return np.abs(np.clip(np.round(g / scale), -7, 7) * scale - g).mean() / np.abs(w).mean()

per_tensor, per_group = int4_error(w, 4_096), int4_error(w, 128)
Python, in your browser — edit the code and run it again

Assumes import numpy as np. Configurations named after real models use their published shapes; other numbers are made up or simulated, as the comments say.

The same idea with a library
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

# 4-bit quantization (NF4 for QLoRA)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,  # quantize the quantization constants
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-70B",
    quantization_config=bnb_config,
    device_map="auto"
)
# 70B model now fits in ~35GB VRAM
Sources
  • E. Frantar, S. Ashkboos, T. Hoefler & D. Alistarh, “GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers”, ICLR, 2023
  • J. Lin et al., “AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration”, Proceedings of Machine Learning and Systems 6, 2024
  • T. Dettmers & L. Zettlemoyer, “The Case for 4-bit Precision: k-bit Inference Scaling Laws”, ICML, 2023

Last reviewed October 2026 · How this site checks its content · Report a mistake

Check yourself

1. Going from 16-bit to 4-bit weights shrinks weight memory to about…

Show answer

A. Four bits instead of sixteen. Group-wise schemes add a scale (and sometimes a zero point) per small block of weights.

2. Why do outlier weights make quantization harder?

Show answer

C. Per-group or per-channel scales, and keeping outliers in higher precision, limit the damage.

3. Why does weight-only quantization speed up token-by-token generation?

Show answer

C. At batch size 1 the GPU mostly waits for weights to arrive from memory; smaller weights arrive sooner.

Linked from 1
← Previous
Speculative Decoding
Open in the full reader, with the topic sidebar