← Back to list

LLM Quantization Under the Hood: Fitting LLaMA 3.1

A senior systems perspective on squeezing 16GB models into 5GB of VRAM without breaking accuracy.

Shariful Islam Sharif · 2026-06-28 12:24 · 0 claps · 6.8 min read
#llm #model-quantization #ai #llama-3
Open on Medium ↗
Wiki topics: LLM · Large Language Models OPS · LLMOps & Inference AI · AI · General 🎮 · Gaming

LLM Quantization Under the Hood: Fitting LLaMA 3.1 8B on Consumer Hardware (with Hugging Face & bitsandbytes)

A senior systems perspective on squeezing 16GB models into 5GB of VRAM without breaking accuracy.

As backend engineers, we’ve spent years obsessing over memory footprints, connection pools, and caching layers to optimize system throughput. When transitioning into the Generative AI space, we encounter a completely different class of resource constraints: GPU Memory (VRAM).

To run a modern, capable Large Language Model (LLM) like Llama-3.1–8B-Instruct at native precision, you need serious enterprise hardware. But what if you want to deploy it on a budget-friendly cloud instance, a single consumer GPU (like an RTX 4090/3090), or even a free-tier Google Colab T4 instance?

This is where Quantization comes in. In this deep dive, we will break down what LLM Quantization is, why it is a critical system design pattern, and how it works under the hood by analyzing a real-world PyTorch model architecture.

Why Are LLMs So Large?

During training, most models use FP32 or FP16 precision. Let’s assume FP16.

Each weight requires, For an 8B model:

16 bits = 2 bytes

8,000,000,000 × 2 bytes ≈ 16 GB

That’s just the model weights. During inference, you also need memory for:

  • KV Cache
  • Activations
  • Temporary tensors
  • CUDA buffers

Which means the actual GPU memory requirement is significantly higher. This is why many consumer GPUs cannot load large models.

What is Quantization?

At its core, a neural network is a massive collections of weights (matrices filled with floating-point numbers). By default, these weights are stored in either:

  • FP32 (32-bit Floating Point): Single-precision, consuming 4 bytes per parameter.
  • FP16 / BF16 (16-bit Floating Point/Brain Float): Half-precision, consuming 2 bytes per parameter.

Quantization is the process of storing model weights using fewer bits while preserving as much model quality as possible.

High-Precision Range (e.g., -3.14 to 3.14 in FP16)
[ -3.14,  -2.11,  -0.5,   0.25,   1.89,   3.14 ]  <-- Consumes 16-bits per value
                     │
                     ▼  Quantization Function (Scaling & Rounding)
Low-Precision Discrete Bins (e.g., NF4 / 4-bit)
[  -7,     -5,     -1,     0,      4,      7   ]  <-- Consumes only 4-bits per value!

By transitioning from a 16-bit space to a 4-bit space, we reduce the memory needed to store each parameter by ~75%.D 4EOL

Why is Quantization Needed? (The System Math)

Let’s do some quick system calculations for Llama 3.1 8B (which has approximately 8 billion parameters):

During training, most models use FP32 or FP16 precision. Let’s assume FP16. Each weight requires:

16 bits = 2 bytes

For an 8B model:

8,000,000,000 × 2 bytes ≈ 16 GB

At 16GB, just loading the model weights completely saturates standard 16GB VRAM GPUs (like the Nvidia T4). If you factor in the KV-Cache (needed to remember conversational context during generation) and the Activation Tensors during a forward pass, the system will instantly throw an Out-Of-Memory (OOM) exception.

To run this model safely with some headroom, you would need a 24GB or 40GB GPU. Quantization democratizes this process by compressing the model’s footprint:

However, Memory at 4 Bit precision

8,000,000,000 * 0.5 bytes = ~4 GB

Adding about 1.5 GB of unquantized system overhead and KV-cache, the entire pipeline fits comfortably inside a 5.5 GB VRAM footprint, allowing it to run smoothly on budget hardware.

A Backend Engineer Analogy

Suppose your database contains a column:

price DOUBLE PRECISION

After analyzing your data, you realize every value is between:

0 and 255

Using DOUBLE PRECISION is wasteful.

You change it to:

SMALLINT

Nothing changes from a business perspective. But you’ve reduced storage considerably.

Quantization applies exactly the same idea to neural network weights.Instead of storing every value with unnecessarily high precision, we store a more compact representation.

FP32 vs FP16 vs INT8 vs INT4

Let’s compare the memory requirements.

Format

Bits per WeightApproximate Memory (8B Model)FP323232 GBFP161616 GBINT888 GBINT444 GB

Format      Bits per Weight      Approximate Memory (8B Model)

FP32        32                   32 GB

FP16        16                   16 GB

INT8        8                    8 GB

INT4        4                    4 GB

This explains why you often see model names like: Llama-3–8b-Q4

The Q4 simply means the model has been quantized to roughly 4 bits per weight.

Doesn’t This Destroy Accuracy?

This was my next question. Suppose a weight is ***0.238764 and After quantization it might become `0.24`***

It’s not identical. But neural networks are surprisingly tolerant to these tiny approximation errors.Changing one weight slightly among billions of parameters usually has very little effect on the final prediction.

That’s why 4-bit models often perform remarkably close to their FP16 counterparts.

The Hybrid Runtime Flow: How Quantized Inference Works

A common point of confusion is: If the weights are compressed to 4-bit, how can the model calculate complex attention matrices without losing extreme accuracy?

The solution is a hybrid runtime execution loop. The weights are stored at-rest in 4-bit, but when an activation pass (computation) happens, they are dynamically dequantized on-the-fly to 16-bit to perform the matrix multiplication.

[ 4-bit Weights stored in GPU VRAM ] 
                   │
                   ▼ 
Dynamically dequantize to BF16 (16-bit)
                   │
                   ▼
[ MatMul: BF16 Weights  x  BF16 Input Activations ]
                   │
                   ▼
Produce output activation tensor (BF16)
                   │
                   ▼
Discard the temporary BF16 weights

Hands-on: Hugging Face & bitsandbytes Integration

Using Hugging Face’s transformers coupled with the bitsandbytes library, we can configure and load Llama-3.1-8B-Instruct in 4-bit with just a few lines of code.

import os
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig

LLAMA = "meta-llama/Llama-3.1-8B-Instruct"

# 1. Configure the Quantization parameters
quant_config = BitsAndBytesConfig(
    load_in_4bit=True,                  # Compress weights to 4-bit
    bnb_4bit_use_double_quant=True,     # Quantize the quantization constants (saves ~0.4 bits/param)
    bnb_4bit_compute_dtype=torch.bfloat16, # Dynamic computation precision
    bnb_4bit_quant_type="nf4"           # Optimal data distribution format (Normal Float 4)
)

# 2. Initialize the Tokenizer
tokenizer = AutoTokenizer.from_pretrained(LLAMA)
tokenizer.pad_token = tokenizer.eos_token

# 3. Load the model with our Quantization Configuration
model = AutoModelForCausalLM.from_pretrained(
    LLAMA, 
    device_map="auto",                  # Intelligently allocates layers across available GPUs
    quantization_config=quant_config
)

# 4. Measure the System Impact
memory = model.get_memory_footprint() / 1e6
print(f"Memory Footprint: {memory:,.1f} MB")

Explaining the Magic Parameters:

  1. **load_in_4bit=True:** Tells the Hugging Face loader to convert compatible linear layers into 4-bit during execution setup.
  2. **bnb_4bit_use_double_quant=True:** Quantization relies on scaling factors (constants) to map values. This option quantizes those scaling factors themselves from 32-bit to 8-bit, saving roughly $0.4$ bits per parameter without any impact on accuracy.
  3. **bnb_4bit_compute_dtype=torch.bfloat16:** This defines the target precision for actual GPU computation during a forward pass. Using bfloat16 (Brain Float 16) is highly recommended over float16 for modern architectures (like Llama 3) to prevent underflow/overflow issues.
  4. **bnb_4bit_quant_type="nf4" (Normal Float 4):** Unlike standard integers, NF4 is an information-theoretically optimal quantization type designed specifically for normally distributed data. Since neural network weights naturally follow a normal Gaussian distribution, NF4 drastically reduces perplexity degradation compared to standard 4-bit integers (fp4).

Under the Hood: Analyzing the Quantized Architecture

When we inspect the internal PyTorch layer representation of the loaded LlamaForCausalLM model, we get a fascinating view of exactly which components have been altered:

LlamaForCausalLM(
  (model): LlamaModel(
    (embed_tokens): Embedding(128256, 4096)
    (layers): ModuleList(
      (0-31): 32 x LlamaDecoderLayer(
        (self_attn): LlamaAttention(
          (q_proj): Linear4bit(in_features=4096, out_features=4096, bias=False)
          (k_proj): Linear4bit(in_features=4096, out_features=1024, bias=False)
          (v_proj): Linear4bit(in_features=4096, out_features=1024, bias=False)
          (o_proj): Linear4bit(in_features=4096, out_features=4096, bias=False)
        )
        (mlp): LlamaMLP(
          (gate_proj): Linear4bit(in_features=4096, out_features=14336, bias=False)
          (up_proj): Linear4bit(in_features=4096, out_features=14336, bias=False)
          (down_proj): Linear4bit(in_features=14336, out_features=4096, bias=False)
          (act_fn): SiLUActivation()
        )
        (input_layernorm): LlamaRMSNorm((4096,), eps=1e-05)
        (post_attention_layernorm): LlamaRMSNorm((4096,), eps=1e-05)
      )
    )
    (norm): LlamaRMSNorm((4096,), eps=1e-05)
    (rotary_emb): LlamaRotaryEmbedding()
  )
  (lm_head): Linear(in_features=4096, out_features=128256, bias=False)
)

As system designers, we must observe which modules were successfully quantized and which were left at native precision:

✅ Converted to 4-bit (Linear4bit)

  • Attention Heads (q_proj, k_proj, v_proj, o_proj): These linear layers manage the multi-head self-attention logic. Because they represent the majority of parameters in the attention block, compressing them to Linear4bit yields massive memory savings.
  • Multi-Layer Perceptron / Feed-Forward Network (gate_proj, up_proj, down_proj): In Llama models, the MLP block handles key-value mapping and contains huge matrices (mapping dimensions up to 14,336. Converting these to Linear4bit represents the largest single reduction in the model's overall footprint.

❌ Left Unquantized (Standard Precision)

  • **LlamaRMSNorm (input_layernorm & post_attention_layernorm):** Normalization layers scale representations to maintain training stability. Because the numerical variance in these layers is highly sensitive, quantizing them to 4-bit would cause extreme degradation (loss of coherence and infinite repeating outputs).
  • **embed_tokens (Embedding Layer):** This converts token IDs into continuous vector space. It is kept at higher precision to preserve semantic rich embeddings.
  • **lm_head (Language Modeling Head):** The final classification layer that calculates probabilities over the vocabulary of $128,256$ tokens to predict the next word. It remains a standard Linear layer to ensure output probability distributions are calculated with high mathematical precision.

The Bottom Line: Benefits & Engineering Trade-offs

The Benefits

  1. Dramatic VRAM Savings: We successfully brought the memory footprint of LLaMA 3.1 8B down from 16 GB to under 5 GB.
  2. Infrastructure Cost Reduction: You can run production endpoints on cheaper, readily available GPUs (such as Nvidia T4 or L4) instead of hunting for expensive, hard-to-source A100/H100 instances.
  3. PEFT Compatibility: Quantization acts as the baseline for QLoRA (Quantized Low-Rank Adaptation). It allows you to freeze the 4-bit base model and train lightweight “adapters” on top of it, making custom fine-tuning possible on a single mid-range GPU.

The Engineering Trade-offs

  • Perplexity Overhead: There is a minor (typically 1-2% loss in output accuracy or coherence compared to the native 16-bit model. However, for most business use cases (like classification, RAG pipelines, or extraction), this difference is imperceptible.
  • Inference Latency: Because the weights are dynamically cast from 4-bit to 16-bit and back during every forward pass, there is a minor computational bottleneck. This can slightly reduce your Token-Per-Second generation speed compared to running the native model on high-end hardware.

Conclusion

Quantization is the ultimate bridges between advanced AI research and cost-efficient backend engineering. By mastering the configuration of frameworks like bitsandbytes and understanding how model architecture changes under the hood, you can design highly resilient, cost-effective LLM backends capable of running state-of-the-art models on commodity hardware.

Are you planning to deploy quantized models in your infrastructure? How are you optimizing your latency vs. resource budgets? Let’s talk in the comments below!


메타데이터
post_id
b6bb7f2cf5fe
slug
llm-quantization-under-the-hood-fitting-llama-3-1-b6bb7f2cf5fe
url
https://medium.com/@sharif-42/llm-quantization-under-the-hood-fitting-llama-3-1-b6bb7f2cf5fe
canonical_url
https://medium.com/@sharif-42/llm-quantization-under-the-hood-fitting-llama-3-1-b6bb7f2cf5fe
author_url
https://medium.com/@sharif-42
status
ok
fetched_at
2026-07-07 17:16:07