AI Intermediate: Infrastructure & Agentic SystemsDay 4 of 30 · Week 1: Bare-Metal Infrastructure & Advanced Containerization
Course outline▾

Week 1 · Bare-Metal Infrastructure & Advanced Containerization

  1. Day 1The AI Infrastructure Shift — Why the Model is Only 10% of the Production System
  2. Day 2Advanced Docker for AI — Multi-Stage Builds for Heavy Python Runtimes
  3. Day 3Hardware Acceleration in Docker — GPU Passthrough and Limits for Constrained Hardware
  4. Day 4Optimizing Model Weights — Quantization Basics (GGUF, AWQ) for Efficient Inference
  5. Day 5Local AI Runtimes: Deploying Ollama and vLLM for Bare-Metal Execution
  6. Day 6Containerizing AI Microservices: Building and Packaging APIs with FastAPI
  7. Day 7Docker Compose: Architecting a Multi-Container Database and Inference Stack Locally

Week 2 · Kubernetes Foundation for MLOps

  1. Day 8K3d & Lightweight Kubernetes: Running Multi-Node Clusters on 16GB of RAM
  2. Day 9Kubernetes Core for Machine Learning: Pods, Deployments, and Headless Services
  3. Day 10Persistent Volumes (PV/PVC): Managing Heavy Model Weights and Vector Stores
  4. Day 11StatefulSets for Vector Databases: Deploying Milvus or Qdrant in Kubernetes
  5. Day 12Auto-Scaling AI Inference: Using Horizontal Pod Autoscalers (HPA) for Spiky API Traffic
  6. Day 13Helm Charts: Packaging, Versioning, and Updating AI Deployments
  7. Day 14Week 2 Capstone: Deploying a Highly Available Local AI Application on K3d

Week 3 · High-Performance Networking & Security Mesh

  1. Day 15AI Network Bottlenecks: Transitioning from iptables to eBPF
  2. Day 16Cilium & eBPF: Sub-Millisecond Routing for Heavy AI Microservices
  3. Day 17Service Mesh 101: Installing Istio over Your Local Cluster
  4. Day 18Envoy Proxy as an AI Gateway: Load Balancing and Terminating LLM Prompts
  5. Day 19Zero-Trust mTLS: Encrypting Traffic Between Autonomous Agents
  6. Day 20API Rate Limiting & Egress Control: Preventing Agents from Exfiltrating Data
  7. Day 21Distributed Tracing: Debugging Agent Workflows with Jaeger and OpenTelemetry

Week 4 · Enterprise Agents & CI/CD Workflows

  1. Day 22Introduction to MLOps: Bridging AI Development with Continuous Integration
  2. Day 23GitOps with ArgoCD: Automated, Self-Healing AI Deployments
  3. Day 24Building Ephemeral AI Sandboxes: Secure Execution Environments for Agent Code
  4. Day 25Multi-Agent Orchestration Frameworks: Exploring Mastra, LangGraph, and CrewAI
  5. Day 26Tool-Calling in Production: Connecting Agents Securely to Internal Databases
  6. Day 27MLOps Guardrails: Evaluating Agents and Preventing Hallucinations in Real-Time
  7. Day 28Continuous Training Pipelines: Connecting Data Ingestion to Automated Model Retraining
  8. Day 29Managing Technical Debt and Infrastructure Drift in Enterprise AI
  9. Day 30The AI-Native Platform: Scaling from a Sandbox Environment to Global Cloud Architecture
Open the course page →

Day 4: Optimizing Model Weights — Quantization Basics (GGUF, AWQ) for Efficient Inference

2026-11-10 · 9 min read

Production AI runs into the memory wall. LLMs are bound by memory bandwidth, because every generated token moves billions of parameters from memory to the compute cores. A standard 8-billion-parameter model in FP16 needs roughly 16 GB of VRAM for the weights alone, before the KV-cache and context. Quantization compresses the weights so these models fit on commodity enterprise hardware without a collapse in quality.

Bytes per parameter

FP16 stores each weight in 16 bits: 1 sign bit, 5 exponent bits and 10 mantissa bits, which is 2 bytes. INT4 uses 4 bits, which is 0.5 bytes, a 75% saving.

THE MEMORY WALL: BYTES PER PARAMETER, 8-BILLION-PARAMETER MODELFP16sign 1 · exponent 5 · mantissa 1016 GB2 B/paramINT88 bits8 GB1 B/paramINT44 bits4 GB0.5 B/paramEvery generated token streams all the weights through the memory bus.Fewer bytes per weight means less to move, and more room for the KV-cache.
FIG 1 · Weights only, before KV-cache and runtime overhead. INT4 cuts weight memory by 75% compared with FP16.

The mathematics

Quantization maps a float weight to a small integer with a scale and an offset.

q = round(w / S) + Z
w ≈ S × (q − Z)        (de-quantize)

w is the original weight, S a scale factor, Z a zero-point offset and q the stored integer. At run time the weights are de-quantized on the fly, or used directly by low-bit kernels, which cuts memory by up to 75%.

QUANTIZATION: q = round(w / S) + Z051015float weights (amber) snap to the nearest of 16 levels (4-bit)woriginal weightSscale factorZzero point offsetqstored integerAt run time weights are de-quantized on the fly, or used by low-bit kernels.
FIG 2 · Quantization maps many float values onto a small set of integer levels. The rounding error is the price paid for memory savings.

Here is a tiny runnable demonstration of the rounding error you trade for that saving. It uses simple 4-bit quantization of a random weight vector, so it shows the principle rather than what production formats do (they quantize in small groups and keep extra scales to reduce the error).

import numpy as np

rng = np.random.default_rng(0)
w = rng.normal(0, 0.05, size=4096).astype(np.float32)      # pretend these are weights

bits = 4
levels = 2**bits - 1                                       # 15 steps for 4-bit
S = (w.max() - w.min()) / levels                           # scale factor
Z = round(-w.min() / S)                                    # zero point

q = np.clip(np.round(w / S) + Z, 0, levels).astype(np.uint8)   # quantize: stored as small integers
w_hat = S * (q.astype(np.float32) - Z)                         # de-quantize

print("max level:", q.max(), " memory: 16 bits ->", bits, "bits per weight")
print("mean absolute error:", float(np.abs(w - w_hat).mean()))

Choosing a format

PICK A FORMAT BY WHERE IT RUNSFORMATBEST FORRUNS INGGUFCPU, Apple Silicon, mixed CPU/GPUllama.cpp · Ollamalayer-by-layer offload, one portable fileAWQdatacentre GPUsvLLM · TensorRT-LLMprotects the few weights that matter mostGPTQdedicated GPUsGPU inference stackspost-training, calibrated on sample dataMatch the format to your engine: the format and the runtime must agree.
FIG 3 · GGUF for portable CPU and mixed deployments, AWQ for high-throughput GPU serving, GPTQ for calibrated 4-bit GPU models.
  • GGUF: the format used by llama.cpp and Ollama. It targets CPUs and Apple Silicon, supports offloading layers between CPU and GPU, and ships as one portable file.
  • AWQ (Activation-aware Weight Quantization): suits high-throughput GPU serving through engines such as vLLM and TensorRT-LLM. It protects the small set of weights that matter most, and holds up well at 4-bit.
  • GPTQ: a post-training method for GPUs that calibrates 4-bit weights against a sample dataset.

The format has to match the runtime you plan to use, so decide the engine first.

The quality trade-off

SIZE AGAINST QUALITY, 8B MODEL (APPROXIMATE)FILE SIZEQUALITY (ILLUSTRATIVE)FP1616 GBreferenceQ8_08.5 GBvery close to FP16Q4_K_M4.8 GBcommon local defaultQ2_K3.2 GBvisible quality lossQ4_K_M usually fits a 16 GB machine with small quality loss.Always test on your own prompts: the effect varies by model and task.
FIG 4 · Lower bit widths shrink the file but cost quality. Sizes are approximate for an 8B model. Quality bars are illustrative, since real loss depends on the model and the task.
  • Q8_0 is very close to FP16 and halves the memory.
  • Q4_K_M is a common default for local deployment. An 8B model drops to about 4.8 GB and runs on a 16 GB workstation, usually with a small quality loss.
  • Q2_K compresses hardest and can show clear degradation, such as weaker reasoning and malformed JSON output.

How much quality you lose depends on the model and the task, so run your own prompts and a few edge cases before you commit. For example, with Ollama you can pull a specific quantization by tag: ollama pull llama3.1:8b-instruct-q4_K_M.

Run it yourself

This is the exact example from the video. A real script with a real result.

import numpy as np

rng = np.random.default_rng(0)
w = rng.normal(0, 0.05, 4096).astype(np.float32)

bits = 4
levels = 2**bits - 1
S = (w.max() - w.min()) / levels
Z = round(-w.min() / S)

q = np.clip(np.round(w / S) + Z, 0, levels).astype(np.uint8)
w_hat = S * (q.astype(np.float32) - Z)

print("levels used:", int(q.max()) + 1)
print("mean abs error:", round(float(np.abs(w - w_hat).mean()), 5))
print("size in bytes:", w.size * 2, "->", w.size * bits // 8)

Run it:

python quantize.py

Output I got while recording the video:

levels used: 16
mean abs error: 0.00604
size in bytes: 8192 -> 2048

Coming Up Next

Day 5: Local AI runtimes: deploying Ollama and vLLM for bare-metal execution.

#Quantization #MachineLearning #Optimization #LLMs #GGUF #vLLM #DataScience #AIEngineering