HelloAI
L7 Chapter 4 🐥 🕒 12 min

Quantization Deep Dive: GPTQ / AWQ / FP8 / GGUF

Fitting 70B models in 24GB VRAM — quantization is the key to running big models on consumer hardware. Full coverage.

A
Alai
7/30/2026

A 70B model in FP16 = 140GB. A single H100 = 80GB. A 4090 = 24GB.

Without quantization, you can’t run 70B on consumer hardware. This piece explains how it actually works.

The Big Picture

Model weights are typically stored as FP16 (16-bit floats). Quantization reduces precision:

FormatBitsRangeUse
FP3232±3.4 × 10³⁸Training (legacy)
FP1616±65kTraining (default)
BF1616±3.4 × 10³⁸Training (preferred)
FP88±448H100+ training, inference
INT88-128 to 127Inference
INT44-8 to 7Inference
INT22-2 to 1Inference (extreme)

Lower bits = less memory + faster + (usually) lower quality.

Why It Works

Surprising insight: LLM weights are highly redundant.

Most weights are near zero. The model uses fine-grained values, but the gradient is dominated by extremes. You can quantize most weights to coarse buckets without much quality loss.

Original FP16 weight: 0.0234567
INT4 quantized:       0  (rounded — close enough)

Original FP16 weight: -2.7
INT4 quantized:       -3  (rounded)

For 175B parameters, billions of small errors mostly cancel out.

Static vs Dynamic

Static quantization

Pre-compute scales / zero-points offline:

quantized_w, scale, zero_point = quantize(weight)
# scale = (max - min) / 2^bits
# zero_point = offset for asymmetric quantization

Fast at inference, but needs calibration data.

Dynamic quantization

Compute scales on-the-fly per layer / per token:

def forward(x):
    scale = compute_scale(x)
    x_quant = (x / scale).round().clamp(-127, 127)
    ...

Slightly slower, but adapts to input distribution.

Major Methods

GPTQ (Frantar et al. 2022)

Quantize one layer at a time, optimizing the rest to compensate.

for layer in model:
    # Quantize this layer's weights
    quantized = quantize_to_int4(layer.weight)
    # Compute error: original_output - quantized_output
    error = compute_error(...)
    # Adjust REMAINING layers to compensate
    next_layer.weight += correction

Key insight: layer-by-layer compensation prevents error accumulation.

Pros: high quality at INT4 (within 1-2% of FP16) Cons: slow to quantize (1-3 hours for 70B)

AWQ (Lin et al. 2023)

Activation-aware weight quantization.

Key insight: not all weights matter equally — the ones that interact with high-magnitude activations matter most. Keep those at higher precision, quantize the rest aggressively.

# Find important weight channels by activation magnitudes
important_channels = top_k(|activation|, k=1%)

# Quantize:
weights[important_channels] @ INT8
weights[other_channels] @ INT4

Pros: better quality than GPTQ Cons: needs calibration data

Most popular method for serving in 2024-2025.

FP8 (H100 native)

Hardware-level support on H100+ GPUs:

# H100 supports FP8 matrix multiplies natively
# 2× throughput of FP16, half memory

import torch
model.to(torch.float8_e4m3fn)  # FP8 format

E4M3: 4-bit exponent + 3-bit mantissa (range ±448, ~10⁻⁴ precision) E5M2: 5-bit exponent + 2-bit mantissa (wider range, less precision)

Used heavily in: DeepSeek V3 training, all modern frontier training.

GGUF (llama.cpp ecosystem)

A format, not a method. GGUF files contain quantized weights in various schemes:

  • Q4_K_M — 4-bit with mixed scheme
  • Q5_K_M — 5-bit
  • Q8_0 — 8-bit
  • IQ2_XXS — 2-bit experimental

Run on CPU or GPU, optimized for consumer hardware.

# Install llama.cpp
brew install llama.cpp  # macOS

# Download a GGUF model
huggingface-cli download bartowski/Llama-3.3-70B-Instruct-GGUF \
    --include "*Q4_K_M*"

# Run
llama-cli -m model.gguf -p "Hello"

Used by: Ollama, LM Studio, GPT4All, KoboldCpp.

NF4 (QLoRA, 2023)

Normal Float 4-bit — non-uniform quantization optimized for normally-distributed weights.

Used in QLoRA (fine-tuning quantized models).

Mixed Precision

Best of both worlds — quantize most, keep some at higher precision:

# Example: 4-bit base + 16-bit LoRA adapters
base_model = load_model_4bit("Llama-3-70B")
lora_adapter = load_lora_fp16("./my_lora")  # Adapter stays high precision

# Inference combines them

Heavy quantization for memory, high precision for the learning parts.

Quality / Speed Trade-offs

Real benchmarks (Llama-3-70B):

SetupVRAMTokens/secQuality (MMLU)
FP16140GB2579.5
INT8 (BitsAndBytes)70GB3079.0
GPTQ INT435GB4578.3
AWQ INT435GB5078.7
GGUF Q4_K_M40GB35 (CPU)78.5
FP8 (H100)70GB5079.3

Sweet spot: AWQ INT4 — 4× memory savings, ~1% quality loss, 2× throughput.

When Quality Drops

Quantization can hurt:

1. Long-context tasks

Tasks needing 32k+ context attention sometimes degrade more.

2. Math / code

Numerical precision matters more — INT4 loses ~3-5% on these.

3. Multi-step reasoning

Errors compound across steps.

Rule of thumb: simpler the task, more quantization you can get away with.

Practical Recommendations

Inference, max VRAM efficiency

AWQ INT4 — best quality-per-bit.

Personal use, any hardware

GGUF (via Ollama / LM Studio) — runs on Mac, PC, anywhere.

Training a quantized model

QLoRA (NF4 + LoRA) — fine-tune 70B on 1× H100.

Production high-throughput

FP8 on H100+ — fastest, hardware-supported.

Tools

ToolUse
BitsAndBytesEasy INT8 / INT4 in HuggingFace
AutoGPTQGPTQ quantization
AutoAWQAWQ quantization
llama.cppGGUF + CPU inference
OllamaEasiest local deploy
TensorRT-LLMNVIDIA production inference
vLLMOpen-source serving with AWQ

Quantization Recipes

Quantize your own model

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

model_path = 'meta-llama/Llama-3-70B-Instruct'
quant_path = 'Llama-3-70B-Instruct-awq'
quant_config = {"zero_point": True, "q_group_size": 128, "w_bit": 4}

# Load and quantize
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(quant_path)

Takes 1-3 hours, fits 70B in 35GB.

Load and run

from awq import AutoAWQForCausalLM

model = AutoAWQForCausalLM.from_quantized("./Llama-3-70B-Instruct-awq")
output = model.generate(input_ids, max_new_tokens=200)

Future Directions

  • Lower bits (1.58-bit, 1-bit models) — research shows it’s possible
  • Better calibration — automated, no manual tuning
  • Hardware — purpose-built AI chips with native low-bit support
  • Mixed: per-channel, per-layer, per-token quantization
💡 A perspective

Quantization is one of those engineering breakthroughs that changed who can use AI.

In 2020: training and inference both needed datacenter compute. In 2024: thanks to quantization, a Mac Mini can run 70B models locally.

The economics of AI flipped: Closed source had compute advantage. Open source + quantization made it accessible everywhere.

This is why Llama / DeepSeek / Qwen / Mistral matter so much — not just the model, but the ecosystem of running it cheap.

Next recommended: L7-05 Model Deployment or L7-03 Inference Optimization.