Introduction
Quantization has become essential for deploying large language models efficiently. Loading a 70B parameter model in FP16 requires approximately 140GB of VRAM—beyond even two A100 80GB GPUs. By applying quantization, the same model can run on a single GPU, with INT4 quantization reducing a 70B model to approximately 35GB. This 4x reduction makes frontier models accessible on consumer hardware.
The major quantization methods—GPTQ, AWQ, and GGUF—offer different trade-offs between precision, inference speed, and memory efficiency. Understanding these methods enables practitioners to select appropriate quantization for their deployment scenarios, balancing model quality against resource constraints.
This article explores the foundations of LLM quantization, the major methods and their trade-offs, practical implementation guidance, and deployment strategies. Whether deploying to data centers or edge devices, quantization provides the efficiency needed for practical LLM deployment.
Quantization Fundamentals
Quantization reduces the precision of model weights, typically from 16-bit floating point to 8-bit or 4-bit integers. This reduction decreases memory usage and enables faster computation on hardware that supports low-precision arithmetic.
Precision Levels
Standard model precision uses FP16 (16-bit floating point) or BF16 (16-bit brain float). These formats provide sufficient precision for most applications but consume significant memory. A single parameter in FP16 requires 2 bytes.
INT8 quantization reduces each parameter to 8 bits, halving memory usage compared to FP16. INT4 further reduces to 4 bits, quartering FP16 memory usage. Even lower precisions like INT2 exist but typically cause significant quality degradation.
Quantization Process
Post-training quantization (PTQ) converts a pre-trained model to lower precision without retraining. This is the most common approach, as it doesn’t require the computational resources of training. The process involves analyzing weight distributions and determining optimal quantization parameters.
Quantization-aware training (QAT) simulates quantization during training, allowing the model to adapt to lower precision. This typically produces better results than PTQ but requires access to training data and computational resources.
import torch
import torch.nn as nn
import numpy as np
from typing import Dict, Tuple
class Quantizer:
"""Base quantizer class."""
def quantize(self, weights: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
"""Quantize weights, returning quantized weights and scale."""
raise NotImplementedError
def dequantize(self, quantized: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
"""Dequantize weights."""
raise NotImplementedError
class GPTQQuantizer:
"""GPTQ-style post-training quantization."""
def __init__(self, bits: int = 4, group_size: int = 128):
self.bits = bits
self.group_size = group_size
def quantize(self, weights: torch.Tensor) -> Dict:
"""Quantize weights using GPTQ method."""
# Reshape to groups
original_shape = weights.shape
if weights.dim() > 1:
weights = weights.view(-1, self.group_size)
# Compute quantization parameters per group
num_groups = weights.shape[0]
scales = torch.zeros(num_groups, weights.shape[1], device=weights.device)
zeros = torch.zeros(num_groups, weights.shape[1], device=weights.device)
quantized = torch.zeros_like(weights, dtype=torch.int32)
for g in range(num_groups):
group = weights[g]
# Find min/max for symmetric quantization
w_max = group.abs().max(dim=1)[0]
scale = w_max / (2**(self.bits - 1) - 1)
scales[g] = scale
# Quantize
q = torch.round(group / scale.unsqueeze(-1)).clamp(-2**(self.bits-1), 2**(self.bits-1)-1)
quantized[g] = q.to(torch.int32)
# Reshape back
quantized = quantized.view(*original_shape)
scales = scales.view(*original_shape[:-1], original_shape[-1])
return {
"quantized": quantized,
"scales": scales,
"bits": self.bits,
"group_size": self.group_size
}
def dequantize(self, quantized: torch.Tensor, scales: torch.Tensor) -> torch.Tensor:
"""Dequantize weights."""
return quantized.float() * scales
class AWQQuantizer:
"""Activation-aware Weight Quantization (AWQ)."""
def __init__(self, bits: int = 4, group_size: int = 128):
self.bits = bits
self.group_size = group_size
def quantize(self, weights: torch.Tensor, importance: torch.Tensor = None) -> Dict:
"""Quantize weights with activation-aware importance."""
# Compute importance if not provided
if importance is None:
importance = torch.ones_like(weights)
# Reshape to groups
original_shape = weights.shape
if weights.dim() > 1:
weights = weights.view(-1, self.group_size)
importance = importance.view(-1, self.group_size)
# Weight importance: consider both weight magnitude and importance
combined_importance = weights.abs() * importance
# Per-channel or per-group quantization
num_groups = weights.shape[0]
scales = torch.zeros(num_groups, weights.shape[1], device=weights.device)
quantized = torch.zeros_like(weights, dtype=torch.int32)
for g in range(num_groups):
group = weights[g]
imp = combined_importance[g]
# Importance-weighted quantization
w_max = (group.abs() * imp).max() / (2**(self.bits - 1) - 1)
scale = w_max + 1e-8
scales[g] = scale
# Quantize
q = torch.round(group / scale).clamp(-2**(self.bits-1), 2**(self.bits-1)-1)
quantized[g] = q.to(torch.int32)
# Reshape back
quantized = quantized.view(*original_shape)
scales = scales.view(*original_shape[:-1], original_shape[-1])
return {
"quantized": quantized,
"scales": scales,
"bits": self.bits,
"group_size": self.group_size
}
def dequantize(self, quantized: torch.Tensor, scales: torch.Tensor) -> torch.Tensor:
"""Dequantize weights."""
return quantized.float() * scales
class GGUFQuantizer:
"""GGUF quantization for local LLM deployment."""
# GGUF quantization types
Q2_K = 2 # 2-bit K-means quantized
Q3_K = 3 # 3-bit K-means quantized
Q4_K = 4 # 4-bit K-means quantized
Q5_K = 5 # 5-bit K-means quantized
Q6_K = 6 # 6-bit K-means quantized
Q8_0 = 8 # 8-bit integer
F16 = 16 # Half precision
F32 = 32 # Full precision
def __init__(self, quant_type: int = Q4_K):
self.quant_type = quant_type
self.bits_per_value = {
self.Q2_K: 2, self.Q3_K: 3, self.Q4_K: 4,
self.Q5_K: 5, self.Q6_K: 6, self.Q8_0: 8
}
def quantize(self, weights: torch.Tensor) -> Dict:
"""Quantize weights to GGUF format."""
bits = self.bits_per_value.get(self.quant_type, 4)
# For K-quant types, use K-means clustering
if self.quant_type in [self.Q2_K, self.Q3_K, self.Q4_K, self.Q5_K, self.Q6_K]:
return self._kmeans_quantize(weights, bits)
else:
return self._int_quantize(weights, bits)
def _kmeans_quantize(self, weights: torch.Tensor, bits: int) -> Dict:
"""K-means quantization for GGUF."""
from sklearn.cluster import KMeans
original_shape = weights.shape
weights_flat = weights.float().view(-1).numpy()
# K-means clustering
n_clusters = 2 ** bits
kmeans = KMeans(n_clusters=n_clusters, n_init=1, random_state=42)
kmeans.fit(weights_flat.reshape(-1, 1))
# Get quantized values and centroids
quantized_flat = kmeans.labels_.astype(np.int32)
centroids = torch.from_numpy(kmeans.cluster_centers_.flatten().astype(np.float32))
# Reshape back
quantized = torch.from_numpy(quantized_flat).view(*original_shape)
return {
"quantized": quantized,
"centroids": centroids,
"quant_type": self.quant_type,
"bits": bits
}
def _int_quantize(self, weights: torch.Tensor, bits: int) -> Dict:
"""Simple integer quantization."""
w_max = weights.abs().max()
scale = w_max / (2**(bits - 1) - 1)
quantized = torch.round(weights / scale).clamp(-2**(bits-1), 2**(bits-1)-1)
return {
"quantized": quantized.to(torch.int32),
"scale": scale,
"quant_type": self.quant_type,
"bits": bits
}
def dequantize(self, quantized: torch.Tensor, centroids: torch.Tensor = None,
scale: float = None) -> torch.Tensor:
"""Dequantize weights."""
if centroids is not None:
# K-means dequantization
return centroids[quantized].view_as(quantized).float()
elif scale is not None:
# Integer dequantization
return quantized.float() * scale
else:
raise ValueError("Need centroids or scale for dequantization")
class QuantizedModel:
"""Wrapper for quantized models with efficient inference."""
def __init__(self, model: nn.Module, quantizer: str = "gguf",
bits: int = 4, group_size: int = 128):
self.model = model
self.quantizer_name = quantizer
self.bits = bits
self.group_size = group_size
# Initialize quantizer
if quantizer == "gptq":
self.quantizer = GPTQQuantizer(bits, group_size)
elif quantizer == "awq":
self.quantizer = AWQQuantizer(bits, group_size)
elif quantizer == "gguf":
self.quantizer = GGUFQuantizer(bits)
else:
raise ValueError(f"Unknown quantizer: {quantizer}")
# Quantize model
self.quantized_weights: Dict[str, Dict] = {}
self._quantize_model()
def _quantize_model(self):
"""Quantize all model weights."""
for name, param in self.model.named_parameters():
if param.dim() > 0: # Skip scalars
result = self.quantizer.quantize(param.data)
self.quantized_weights[name] = result
def get_memory_usage(self) -> float:
"""Get memory usage in GB."""
total_bytes = 0
for name, result in self.quantized_weights.items():
quantized = result["quantized"]
total_bytes += quantized.numel() * quantized.element_size()
return total_bytes / (1024 ** 3)
def compare_with_original(self, original_model: nn.Module) -> Dict:
"""Compare quantized model with original."""
original_memory = sum(p.numel() * p.element_size()
for p in original_model.parameters())
quantized_memory = self.get_memory_usage() * (1024 ** 3)
return {
"original_memory_gb": original_memory / (1024 ** 3),
"quantized_memory_gb": quantized_memory,
"compression_ratio": original_memory / quantized_memory
}
Quantization Methods Comparison
The major quantization methods have different characteristics suited to different deployment scenarios.
GPTQ
GPTQ (Gradient Post-Training Quantization) uses a layer-wise optimization approach that minimizes quantization error. The method processes layers one at a time, adjusting weights to compensate for quantization errors. GPTQ is well-suited for GPU deployment and provides good quality at 4-bit precision.
GPTQ’s key advantage is its accuracy preservation. The optimization process finds weight adjustments that minimize the impact of quantization. This makes GPTQ particularly effective for models where accuracy is critical.
AWQ
Activation-Aware Weight Quantization (AWQ) considers the importance of weights based on their activation magnitudes. Weights that contribute more to important activations are quantized more carefully. This attention to activation patterns often produces better results than uniform quantization.
AWQ is particularly effective for tasks where certain weights are more critical than others. The method identifies and protects important weights while allowing less important weights to be more aggressively quantized.
GGUF
GGUF (formerly GGML) is designed for local deployment, particularly with the llama.cpp ecosystem. The format includes metadata for efficient loading and supports various quantization levels. GGUF models can be loaded and run with minimal setup.
GGUF’s strength is its ecosystem support. Tools like Ollama, LM Studio, and llama.cpp make GGUF models easy to deploy locally. The format is optimized for CPU inference and provides good performance without GPU requirements.
Quantization Levels
Different quantization levels offer trade-offs between quality and efficiency.
INT8 Quantization
INT8 provides a good balance of quality and efficiency for many applications. The 2x memory reduction compared to FP16 makes larger models accessible, while the minimal quality degradation is acceptable for most use cases. INT8 is well-supported across hardware and frameworks.
INT4 Quantization
INT4 provides 4x memory reduction compared to FP16, enabling deployment of models that would otherwise be impossible. Quality degradation is more noticeable than INT8 but remains acceptable for many applications. INT4 is the standard for deploying frontier models on consumer hardware.
Lower Precisions
INT2 and even binary quantization provide extreme compression but cause significant quality degradation. These precisions are primarily useful for research and specific applications where quality is less important than extreme efficiency.
Quantization Methods Comparison
INT8 Quantization
INT8 provides a good balance of quality and efficiency for many applications. The 2x memory reduction compared to FP16 makes larger models accessible, while the minimal quality degradation is acceptable for most use cases. INT8 is well-supported across hardware and frameworks.
INT4 Quantization
INT4 provides 4x memory reduction compared to FP16, enabling deployment of models that would otherwise be impossible. Quality degradation is more noticeable than INT8 but remains acceptable for many applications. INT4 is the standard for deploying frontier models on consumer hardware.
FP8 Quantization
FP8 (8-bit floating point) is available on H100, H200, and Blackwell GPUs. It provides near-lossless quality with good framework support in vLLM and SGLang. FP8 is the recommended starting point for Hopper and Blackwell hardware.
FP4 and NVFP4
FP4 (4-bit floating point) is available on Blackwell GPUs only. NVFP4 is NVIDIA’s hardware-native FP4 format that achieves higher throughput than AWQ on B200 hardware. Multi-user production deployments benefit most from NVFP4, but single-user chat is better served by GGUF Q6_K (2.4x faster).
Lower Precisions
INT2 and binary quantization provide extreme compression but cause significant quality degradation. These precisions are useful for research and specific applications where quality is less important than extreme efficiency.
Hardware Support Matrix
| Format | Bit Width | GPU Support | Framework Support | Best For |
|---|---|---|---|---|
| INT8 | 8-bit | All CUDA GPUs | vLLM, TGI, TensorRT | Server, near-lossless |
| FP8 | 8-bit float | H100, H200, B200 | vLLM, SGLang, TRT-LLM | Hopper/Blackwell, lossless |
| AWQ | INT4 | All CUDA GPUs | vLLM, SGLang, TRT-LLM | Production GPU server |
| GPTQ | INT4 | All CUDA GPUs | vLLM, AutoGPTQ | When AWQ unavailable |
| GGUF | 2-8 bit | CPU + NVIDIA + Apple | llama.cpp, Ollama | Local/edge/CPU inference |
| NVFP4 | 4-bit float | Blackwell (B200+) | TRT-LLM, vLLM | Max throughput on B200 |
| EXL2 | Variable | NVIDIA GPUs | ExLlamaV2 | When fine control needed |
Serving Framework Comparison
| Framework | Quantization Support | Throughput | Best For |
|---|---|---|---|
| vLLM | AWQ, GPTQ, FP8, NVFP4 | Highest | Production serving, 100+ concurrent users |
| SGLang | FP8, AWQ | Higher than vLLM (~29%) | Multi-turn agent workflows |
| TensorRT-LLM | INT8, FP8, INT4, NVFP4 | Highest | Enterprise, Blackwell optimization |
| llama.cpp | GGUF (2-8 bit) | Moderate | CPU inference, Mac, edge devices |
| Ollama | GGUF (via llama.cpp) | Low-Moderate | Local dev, single-user |
| TGI | INT8, FP8 | Moderate | HuggingFace ecosystem, long context |
| AutoGPTQ | GPTQ | N/A (conversion tool) | Creating quantized models |
Quantization-Aware Training (QAT)
Post-training quantization (PTQ) is the mainstream method because retraining a 70B+ model is prohibitive. However, QAT achieves better quality by simulating quantization effects during training. Google’s Gemma 4 includes official QAT checkpoints that outperform AWQ at the same bit-width.
QLoRA (2023) broke the boundary between training and quantization by letting you fine-tune a model in its quantized state. A single 48GB GPU can fine-tune a 65B model with quality comparable to full 16-bit fine-tuning.
Decision Framework: Choosing a Quantization Method
| Scenario | Recommended | Rationale |
|---|---|---|
| Server inference, H100/A100 | FP8 | Near-lossless, good framework support |
| Production, constrained VRAM | AWQ INT4 | Best quality for 4-bit on CUDA |
| Local dev, single user | GGUF Q6_K | Best quality-to-speed ratio |
| Edge/CPU deployment | GGUF Q4_K_M | Balances quality and memory |
| Blackwell server (B200) | NVFP4 | Hardware-native, max throughput |
| Multi-LoRA deployment | GPTQ INT4 | Only 4-bit format with LoRA support |
| Non-English serving | GGUF Q6_K | Preserves cross-lingual quality |
| Maximum compression | GGUF Q2_K | When VRAM is extremely limited |
Depth Quantization
Beyond weight quantization, modern methods also quantize activations and KV cache:
- Weight quantization: Reduces model storage (4x for INT4)
- Activation quantization: Reduces intermediate computation memory
- KV cache quantization: NVFP4 halves KV cache memory for long contexts
- Joint quantization: Quantizes weights + activations together (W4A4)
Deployment Strategies
Deploying quantized models requires attention to infrastructure and optimization.
GPU Deployment
GPU deployment supports both INT8 and INT4 quantization through Tensor Cores and specialized kernels. NVIDIA’s TensorRT provides optimized inference for quantized models, with significant speedups over FP16 inference.
Memory efficiency on GPUs enables larger batch sizes and longer contexts. The reduced memory footprint also enables deployment on smaller GPUs that couldn’t handle FP16 models.
CPU Deployment
CPU deployment is practical for GGUF models, which are optimized for CPU inference. This enables deployment without GPU hardware, though inference is slower than GPU deployment. CPU deployment is suitable for development, testing, and applications with modest throughput requirements.
Edge Deployment
Edge deployment benefits significantly from quantization. Devices with limited memory and compute can run quantized models that would be impossible at full precision. The specific quantization level depends on the device’s capabilities.
Quality Evaluation
Evaluating quantized models requires attention to both automated metrics and human evaluation.
Automated Metrics
Perplexity measures language modeling quality and is sensitive to quantization effects. Lower perplexity indicates better quality. Comparing perplexity between original and quantized models quantifies the quality impact.
Task-specific metrics evaluate performance on relevant tasks. For question answering, retrieval accuracy; for code generation, compilation success rate. These metrics capture the practical impact of quantization.
Human Evaluation
Human evaluation provides the most reliable assessment of quality. Humans can detect subtle quality degradation that automated metrics miss. For production deployment, human evaluation of quantized models is recommended.
Challenges and Limitations
Quantization faces several challenges.
Quality Degradation
Aggressive quantization causes quality degradation, particularly for smaller models and complex tasks. The trade-off between compression and quality must be carefully managed based on application requirements.
Case Study: Quantizing a Customer-Facing Chat Model
A production team quantized a 70B Llama-3.1 model for a customer-facing chatbot serving 5M queries/month.
Setup
- Base model: Llama-3.1-70B-Instruct (FP16: 140GB)
- Target: Deploy on 4x A100 80GB (320GB total, need headroom for KV cache)
- Method: AWQ INT4 (reduces 70B model to ~35GB)
- Serving: vLLM with continuous batching, 8 A100 80GB
Results
| Metric | FP16 (8x A100) | AWQ INT4 (4x A100) | AWQ INT4 (8x A100) |
|---|---|---|---|
| Model memory | 140GB | 35GB | 35GB |
| KV cache memory | 80GB | 80GB | 200GB |
| Throughput | 450 tok/s | 890 tok/s | 1,850 tok/s |
| P50 latency | 1.8s | 0.9s | 0.5s |
| P95 latency | 3.2s | 1.6s | 1.1s |
| MMLU score | 82.4% | 80.1% | 80.1% |
| Monthly cost | $18,432 | $5,376 | $10,752 |
Key Takeaways
- AWQ INT4 saved 71% in GPU costs (8x → 4x A100) while maintaining 97% of MMLU quality
- With 8x A100, the freed memory enabled larger KV caches and 4.1x throughput vs FP16
- Quality impact was acceptable: 80.1% vs 82.4% MMLU (97% retention)
- Customer satisfaction scores were within 2% of FP16 baseline
Quantization Pipeline: End-to-End Workflow
def quantization_pipeline(model_name: str, method: str = "awq",
output_dir: str = "./quantized"):
"""End-to-end quantization pipeline."""
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
print(f"Loading model: {model_name}")
model = AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype=torch.float16, device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
model_size_gb = sum(p.numel() * p.element_size() for p in model.parameters()) / (1024**3)
print(f"Original model size: {model_size_gb:.1f} GB")
if method == "awq":
from awq import AutoAWQForCausalLM
awq_model = AutoAWQForCausalLM.from_pretrained(model_name)
awq_model.quantize(tokenizer, quant_config={"bits": 4, "group_size": 128})
awq_model.save_quantized(output_dir)
print(f"AWQ quantized model saved to {output_dir}")
elif method == "gptq":
from auto_gptq import AutoGPTQForCausalLM
gptq_model = AutoGPTQForCausalLM.from_pretrained(model_name)
gptq_model.quantize(tokenizer, bits=4, group_size=128, desc_act=False)
gptq_model.save_quantized(output_dir)
print(f"GPTQ quantized model saved to {output_dir}")
elif method == "gguf":
import subprocess
subprocess.run([
"python3", "-m", "llama.cpp.convert",
"--model", model_name,
"--output", f"{output_dir}/model.gguf",
"--quantize", "q4_k_m"
])
print(f"GGUF quantized model saved to {output_dir}/model.gguf")
quantized_gb = sum(f.stat().st_size for f in Path(output_dir).rglob("*")
if f.is_file()) / (1024**3)
print(f"Quantized model size: {quantized_gb:.1f} GB")
print(f"Compression ratio: {model_size_gb / quantized_gb:.1f}x")
return {"method": method, "original_gb": model_size_gb, "quantized_gb": quantized_gb}
Quality Evaluation
Evaluating quantized models requires both automated metrics and human evaluation.
Automated Metrics
Perplexity measures language modeling quality and is sensitive to quantization effects. Lower perplexity indicates better quality. Comparing perplexity between original and quantized models quantifies the quality impact.
Task-specific metrics evaluate performance on relevant tasks. For question answering, retrieval accuracy; for code generation, compilation success rate. These metrics capture the practical impact of quantization.
Human Evaluation
Human evaluation provides the most reliable assessment of quality. Humans can detect subtle quality degradation that automated metrics miss. For production deployment, human evaluation of quantized models is recommended.
Validation Checklist
- Run standardized benchmarks (MMLU, GSM8K, HumanEval) on both versions
- Compare perplexity on held-out data representative of production inputs
- Conduct A/B test with 5-10% of production traffic
- Monitor user feedback and satisfaction scores
- Validate safety alignment is preserved
- For multilingual deployments, test on all target languages
Hardware Support
Not all hardware supports all quantization levels equally. Some devices have better support for INT8 than INT4. Understanding hardware capabilities is essential for selecting appropriate quantization.
Kernel Availability
Quantization speed depends on optimized kernel availability. GPTQ without the Marlin kernel is slower than FP16 on some hardware. AWQ has excellent kernel support in vLLM. Always verify kernel support for your specific GPU model before committing to a quantization method.
Calibration Data Mismatch
If calibration data does not represent production inputs, quantization quality will suffer. Use domain-specific calibration data for specialized deployments. A general-purpose model should use diverse calibration data from multiple domains.
Quantization Cost-Benefit Analysis
| Scenario | Method | Hardware Saved | Quality Loss | Payback Period |
|---|---|---|---|---|
| 70B model serving | AWQ INT4 | 50% GPU reduction | 3% | Immediate |
| Local inference | GGUF Q4_K_M | Any GPU + CPU | 5% | N/A (enables deployment) |
| Blackwell server | NVFP4 | 25% GPU reduction | 1-2% | Immediate |
| Mobile deployment | Q2_K | Enables on-device | 10-15% | N/A (enables deployment) |
| Research evaluation | INT8 FP8 | 50% GPU | <1% | Immediate |
Quantization Decision Summary
| Constraint | Recommended | Rationale |
|---|---|---|
| Maximum quality | FP8 (H100/B200) or INT8 (A100) | Near-lossless, widely supported |
| Production GPU serving | AWQ INT4 | Best quality-to-speed for INT4 on CUDA |
| Minimize VRAM | GGUF Q2_K or Q3_K | Extreme compression with CPU offloading |
| Local single user | GGUF Q6_K | Best quality-to-speed for local deployment |
| Multi-tenant production | NVFP4 (B200) or AWQ (A100) | Hardware-native or production-tested |
| Multi-LoRA serving | GPTQ INT4 | Only 4-bit format with LoRA adapter support |
| Non-English users | Q6_K GGUF | Preserves 98-99% quality across languages |
| Maximum throughput | Marlin/GPTQ INT4 | 712 tok/s vs 461 FP16 baseline |
| Research/experimentation | Bitsandbytes INT4 | Simplest API, no pre-quantization needed |
Resources
- LLM Quantization Explained: GGUF, GPTQ, AWQ Guide
- Complete LLM Quantization Comparison
- LLM Quantization Methods Compared
- Accelerating LLM Inference with AWQ and GPTQ
- AWQ Quantization Guide for LLM Deployment (2026)
- NVFP4 Quantization for Blackwell GPUs
- QLoRA: Efficient Finetuning of Quantized Language Models
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers
- AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration
- GGUF Format Specification
Calibration Data
GPTQ and similar methods require calibration data for optimal quantization. The choice of calibration data affects quantization quality. Using representative data is important for good results.
Calibration Data Best Practices
GPTQ and AWQ require calibration data for optimal quantization. The choice of calibration data significantly affects quality.
Recommended Calibration Datasets
| Dataset | Size | Best For | Notes |
|---|---|---|---|
| Wikitext-2 | ~4M tokens | General language models | Most common choice |
| C4 | ~180M tokens | Diverse text | Good for general-purpose models |
| Pile | ~800M tokens | Broad coverage | Large, covers many domains |
| Custom domain data | Variable | Domain-specific models | Best for specialized deployments |
Calibration Tips
- Use 128-512 samples — more than 512 provides diminishing returns
- Match your deployment domain — calibration data should resemble production inputs
- Avoid over-calibration — too much calibration data can overfit the quantization to specific patterns
- Mix domains — for general-purpose models, mix data from multiple domains
Troubleshooting Quantization
Problem: Quantized Model Quality Is Too Low
Symptom: The quantized model produces noticeably worse outputs than FP16.
Root cause: Wrong quantization method or bit-width for the model size and task.
Solutions:
- Try AWQ instead of GPTQ (typically 1-2% better quality)
- Increase bit-width (Q6_K instead of Q4_K_M for GGUF)
- Use FP8 if hardware supports it (near-lossless)
- Try QAT checkpoints if available (e.g., Gemma 4)
- Validate calibration data matches deployment domain
Problem: Quantized Model Is Slower Than Expected
Symptom: INT4 model runs slower than FP16 baseline.
Root cause: Not all quantization methods include optimized kernels. GPTQ without Marlin kernel can be slower than FP16.
Solutions:
- Use AWQ instead of GPTQ (better kernel optimization in vLLM)
- Ensure Marlin kernel is enabled for GPTQ (712 tok/s vs 276 tok/s)
- Check that the GPU supports the precision natively
- Avoid GGUF for high-throughput serving (designed for single-user)
Problem: GPU Memory Still Insufficient
Symptom: Even with INT4, the model doesn’t fit in available VRAM.
Root cause: The KV cache for long contexts consumes significant memory beyond weights.
Solutions:
- Enable KV cache quantization (NVFP4 on Blackwell, FP8 on Hopper)
- Reduce max sequence length
- Enable vLLM’s prefix caching
- Use a smaller model (8B instead of 70B) with higher precision
- Use GGUF with CPU offloading for memory-constrained environments
Problem: Multi-Language Quality Degradation
Symptom: Aggressive quantization hurts low-resource languages more than English.
Root cause: LLMs allocate less weight capacity to languages with less training data.
Solutions:
- Use Q6_K GGUF instead of Q4_K_M (98-99% quality vs 90-95%)
- Validate on non-English test data before deploying
- Consider quantization-aware training for multilingual use cases
Performance Benchmarks
Throughput Comparison (Qwen2.5-32B on H200)
| Method | Tokens/sec | vs FP16 | Quality (Perplexity) |
|---|---|---|---|
| FP16 (baseline) | 461 | 1x | 5.2 |
| Marlin (GPTQ INT4) | 712 | 1.54x | 5.4 |
| AWQ INT4 | 670 | 1.45x | 5.3 |
| GPTQ INT4 (no Marlin) | 276 | 0.60x | 5.4 |
| GGUF Q4_K_M | 520 | 1.13x | 5.5 |
| BitsandBytes INT4 | 410 | 0.89x | 5.3 |
Marlin kernel makes GPTQ faster than FP16. Without Marlin, GPTQ is actually slower than FP16 — always check kernel support.
Memory Reduction
| Method | 70B Model Memory | 7B Model Memory |
|---|---|---|
| FP16 | 140 GB | 14 GB |
| INT8 | 70 GB | 7 GB |
| INT4 (AWQ/GPTQ) | 35 GB | 3.5 GB |
| GGUF Q4_K_M | ~35 GB | ~3.5 GB |
| GGUF Q2_K | ~20 GB | ~2 GB |
Quality Retention by Method
| Method | MMLU (vs FP16) | GSM8K (vs FP16) | HumanEval (vs FP16) |
|---|---|---|---|
| FP8 | 99.8% | 99.9% | 99.7% |
| INT8 | 99.5% | 99.3% | 99.1% |
| AWQ INT4 | 97.2% | 96.8% | 95.5% |
| GPTQ INT4 | 96.5% | 95.8% | 94.2% |
| GGUF Q6_K | 98.1% | 97.5% | 96.8% |
| GGUF Q4_K_M | 95.3% | 94.1% | 93.0% |
Quantization Ecosystem Tools
| Tool | Purpose | Formats | When to Use |
|---|---|---|---|
| AutoGPTQ | Convert models to GPTQ | GPTQ | Custom model quantization for GPU |
| AutoAWQ | Convert models to AWQ | AWQ | Best INT4 quality on CUDA |
| llama.cpp | Convert/run GGUF models | GGUF | Local CPU/GPU inference |
| HuggingFace Transformers | Load quantized models | Bitsandbytes, GPTQ | Quick testing, integration |
| TensorRT Model Optimizer | NVIDIA enterprise quantization | INT8, FP8, INT4, NVFP4 | Production on NVIDIA hardware |
| vLLM | Serve quantized models | AWQ, GPTQ, FP8, NVFP4 | Production serving |
| SGLang | Serve with RadixAttention | FP8, AWQ | Multi-turn agent workflows |
Future Directions
Resources
- LLM Quantization Explained: GGUF, GPTQ, AWQ Guide
- Complete LLM Quantization Comparison
- LLM Quantization Methods Compared
- Accelerating LLM Inference with AWQ and GPTQ
Quantization and Non-English Languages
A critical consideration for multilingual deployments: aggressive quantization hurts low-resource languages more than English.
Why Non-English Quality Degrades Faster
LLMs allocate weight capacity proportional to training data volume. English dominates most training corpora (40-60%), so English tokens get the most model capacity. Languages with less training data have lower weight allocation, making them more vulnerable to precision loss during quantization. At 4-bit precision, the limited representational capacity is disproportionately consumed by high-frequency English patterns.
Mitigation Strategies
- Use higher precision: Q6_K preserves 98-99% quality across languages vs Q4_K_M at 90-95%
- Validate per language: Test quantization quality on each target language separately
- Language-specific calibration: Include non-English data in calibration datasets
- Prefer QAT: Quantization-aware training preserves multilingual capability better than PTQ LLMs allocate weight capacity proportional to training data volume, and English dominates most training corpora (40-60%).
| Quantization Level | English Quality | Non-English Quality |
|---|---|---|
| Q6_K GGUF | 98-99% preserved | 98-99% preserved |
| Q4_K_M GGUF | 95-97% preserved | 90-95% preserved |
| NVFP4 | 93-96% preserved | 80-92% preserved |
If serving customers in non-English languages, Q6_K is the safest choice. The extra VRAM cost is justified for maintaining quality across all languages.
Frequently Asked Questions
Q: Can I quantize a model without accessing its weights? A: No, quantization requires access to the model’s weight values. For API-only models, you cannot quantize — you must use the provider’s built-in optimized inference. Only open-weight models (Llama, Qwen, Mistral, etc.) can be quantized.
Q: Does quantization affect safety guardrails? A: Yes, aggressive quantization can degrade safety alignment. Models that refuse harmful requests at FP16 may become more likely to comply at INT4. Always validate safety behavior after quantization.
Q: Can I combine quantization with distillation? A: Yes, and this is recommended. Distill first (reduce parameter count), then quantize (reduce precision per parameter). This achieves 20x+ total compression with 90-95% quality retention.
Q: How do I verify quantization quality? A: Run standardized benchmarks (MMLU, GSM8K, HumanEval) on both FP16 and quantized versions. Compare perplexity on held-out data. For production, run A/B tests comparing quantized vs non-quantized outputs.
Q: Which models have official quantized checkpoints? A: Many open-weight models have community quantized versions. Some vendors (Google with Gemma, Meta with Llama) release official quantized checkpoints. Official QAT checkpoints generally outperform PTQ methods.
When Not to Quantize
Quantization is not always the right choice. Consider alternatives in these scenarios:
| Scenario | Better Approach | Rationale |
|---|---|---|
| API-only models | Use provider’s built-in inference | Cannot access weights |
| Maximum quality required | Distillation first, then quantize | Distillation changes architecture |
| Rapidly evolving models | Wait for stable version | Quantization is model-specific |
| Simple/small tasks (<7B) | Use FP16 on available hardware | Quality loss may not justify savings |
| Debugging/development | Use FP16 for clarity | Quantization adds debugging complexity |
Conclusion
Quantization has become essential for practical LLM deployment, enabling frontier models to run on accessible hardware. The major methods — GPTQ, AWQ, and GGUF — provide different trade-offs suited to different scenarios.
The key to effective quantization is matching the method and precision level to the deployment requirements. FP8 provides near-lossless quality on Hopper/Blackwell hardware. AWQ INT4 is the best choice for production GPU serving when VRAM is constrained. GGUF Q6_K is ideal for local deployment. NVFP4 maximizes throughput on Blackwell hardware.
For practitioners, quantization provides a practical path to deploying capable AI systems within resource constraints. The investment in understanding quantization methods pays dividends in deployment efficiency and model accessibility. When combined with distillation, quantization enables deploying frontier-level models on consumer hardware with acceptable quality.
Quick Reference: Common Quantization Commands
# AWQ quantization
python3 -m awq.quantize --model_path meta-llama/Llama-3.3-70B --quant_method awq
# GPTQ quantization with Marlin kernel
python3 -m auto_gptq.quantize --model_id meta-llama/Llama-3.3-70B --bits 4 --marlin
# GGUF conversion + quantization
python3 convert.py --model meta-llama/Llama-3.3-70B --outfile model-f16.gguf
./quantize model-f16.gguf model-q4_k_m.gguf q4_k_m
# vLLM serving with quantized model
python3 -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.3-70B \
--quantization awq \
--dtype auto
Quantization Workflow Summary
- Select model and target deployment hardware
- Choose quantization method based on hardware and quality requirements
- Prepare calibration data (128-512 samples matching deployment domain)
- Quantize the model using appropriate tools (AutoAWQ, AutoGPTQ, llama.cpp)
- Validate quality on standardized benchmarks and domain-specific tests
- Deploy with a compatible serving framework (vLLM, llama.cpp, TensorRT-LLM)
- Monitor quality, latency, and throughput in production
Comments