Quantizing LLMs: INT4, GPTQ, and GGUF Tradeoffs

LLMInferencePerformanceOn-Device

LLM quantization is how you fit a model that was trained in 16-bit floats onto hardware that doesn't have that much VRAM — by storing weights in INT8 or INT4 and accepting a measured quality trade. If you've ever wanted a 70B-class model on a single consumer GPU, or a 7B model on a phone, quantization is the lever you're actually pulling. The alphabet soup (GPTQ, AWQ, GGUF, bitsandbytes) matters less than understanding what each method preserves and where it fails.

I treat quantization as an engineering decision with a budget: memory, latency, and eval score. Here's how I pick among the common options in 2026.

What you're actually compressing

Transformers spend most of their footprint on weight matrices. Activations still matter at runtime, but for deployment the first win is shrinking the checkpoint. Rough memory for a dense model:

Precision Approx. bytes/param 7B model 70B model
FP16 / BF16 2 ~14 GB ~140 GB
INT8 1 ~7 GB ~70 GB
INT4 0.5 ~3.5 GB ~35 GB

Those numbers ignore KV cache and activations — KV cache optimization still bites you at long context — but they explain why INT4 is the default conversation for "run this locally."

Quantization can be weight-only (weights INT4, activations higher precision during matmul) or full (weights and activations). Weight-only is what most production stacks ship today because it's simpler and usually accurate enough.

GPTQ vs AWQ vs bitsandbytes

Three post-training approaches dominate GPU serving:

A minimal load path with Hugging Face Transformers + AWQ looks like:

from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "TheBloke/Mistral-7B-Instruct-v0.2-AWQ"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",
    trust_remote_code=True,
)

prompt = "Summarize OCPP 1.6 in two sentences."
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=128)
print(tokenizer.decode(out[0], skip_special_tokens=True))

For serving, prefer a dedicated engine (vLLM, TGI, TensorRT-LLM) that fused the quantized kernels — loading AWQ into a research notebook is not the same as sustained tokens/sec under load.

GGUF and the llama.cpp world

GGUF is the format the local/on-device crowd standardized on. One file holds metadata, tokenizer pieces, and quantized tensors. Quant variants are named by scheme and bits — Q4_K_M, Q5_K_S, Q8_0, and so on. The K-quants (k-means style block quantization) are usually the sweet spot for quality vs size.

Why GGUF wins on a laptop or on-device stack:

  1. Portable — same file runs via llama.cpp, Ollama, LM Studio, and many mobile wrappers.
  2. CPU-friendly — not everything has a CUDA card; GGUF kernels are tuned for CPU and Apple Silicon Metal.
  3. Simple ops — pull a file, run. No separate calibration pipeline on the user's machine.

The tradeoff: GPU server fleets that already run vLLM usually prefer GPTQ/AWQ/FP8 paths with continuous batching. GGUF is not wrong on a GPU — it's just not where the high-throughput ecosystem invested first.

Quality cliffs I actually measure

Don't trust a blog's "negligible loss" claim. I run three checks before shipping a quantized checkpoint:

  1. Task eval — your golden set, not only MMLU. For agents, tool-call JSON validity rates matter more than trivia.
  2. Long-context sanity — quantization can amplify attention noise; test the context lengths you sell.
  3. Domain jargon — rare tokens (protocol names, product SKUs) are where 4-bit often slips first.

Rules of thumb from shipping work:

Choosing a path

Goal Prefer
Max tokens/sec on A100/H100 fleet AWQ or GPTQ (or FP8) in vLLM / TensorRT-LLM
Laptop / offline desktop app GGUF via llama.cpp or Ollama
Mobile / edge prototype GGUF or vendor NPU formats; start with a small model
Fine-tune on one GPU QLoRA / NF4, then export a serving quant
Absolute quality, money available BF16 / FP8, skip aggressive INT4

Also decide who quantizes. Community GGUF repos are convenient and sometimes wrong (mismatched chat templates, silent corruption). Prefer official or well-known publishers, verify SHA hashes, and smoke-test generation before you wire the file into a product.

Operational gotchas

Quantization is not magic compression — it's a controlled precision trade. Pick INT4 when the memory math demands it, pick the method that matches your runtime, and prove quality on your prompts. Everything else is branding on the Hugging Face card.

Resources

Frequently asked questions

What is LLM quantization?

LLM quantization reduces the numeric precision of model weights — typically from 16-bit floats down to 8-bit or 4-bit integers — so the model uses less memory and can run faster on the same hardware. Done well, quality stays close to the full-precision model; done poorly, you get nonsense outputs that look fine until you measure them.

What's the difference between GPTQ, AWQ, and GGUF?

GPTQ and AWQ are post-training quantization methods that produce weight-only INT4 (or INT8) models, usually consumed by GPU runtimes like vLLM or Hugging Face Transformers. GGUF is a file format (and tooling ecosystem around llama.cpp) that packs quantized weights for CPU/GPU inference on local machines. You pick GPTQ/AWQ for server GPUs and GGUF when you want one file that runs well on laptops and phones.

How much quality do you lose with INT4?

On capable 7B–70B models, well-tuned INT4 (especially AWQ or GPTQ with calibration) often stays within a few points of FP16 on standard benchmarks. Smaller models and math/coding tasks degrade faster. Always eval your own prompts — a 'good' MMLU score can still break your product-specific edge cases.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →