When you build AI agents or applications on top of LLMs, you’re making decisions at every step: which model to use, how much context you can fit, how to retrieve relevant knowledge, and how to deploy affordably. These concepts are the ones that drive those decisions. Understanding them turns you from someone who uses LLMs into someone who architects with them.
What goes on behind the scenes when you interact with an LLM:
Raw Text
│
▼
╔═════════════════════════════════════╗
║ Tokenizer/Preprocessor ║
║ ║
║ ▼ ║
║ TOKENIZATION (BPE) ║
║ ▼ ║
║ Token IDs [5765, 2065, 374, ...] ║
║ ║
╚═════════════════════════════════════╝
│
▼
╔═════════════════════════════════════╗
║ LLM Model ║
║ ║
║ ▼ ║
║ EMBEDDING → Dense Vectors ║
║ ▼ ║
║ TRANSFORMER LAYERS (attention, ║
║ feed-forward, billions of params) ║
║ ▼ ║
║ Output Token Probabilities ║
║ ║
╚═════════════════════════════════════╝
│
▼
Generated Text
An LLM is not a single monolithic thing — it’s a pipeline of distinct components, each with a specific job:
| Component | What it does | Inside the model? |
|---|---|---|
| Tokenizer | Splits raw text into subword tokens and maps them to integer IDs | No — deterministic algorithm, shipped alongside the model |
| Embedding Layer | Converts token IDs into dense vectors (high-dimensional representations) | Yes — first layer of the neural network |
| Transformer Layers | Process vectors through self-attention and feed-forward networks to build contextual understanding | Yes — the bulk of the model’s parameters live here |
| Output Head | Projects final vectors into vocabulary-sized probabilities for next-token prediction | Yes — last layer of the neural network |
When someone says “a 70B model,” they mean 70 billion parameters spread across the embedding layer, transformer layers, and output head. The tokenizer adds negligible overhead.
When you’re building an AI agent or application, here’s how these concepts show up at each stage:
Modelling (creating the base model):
Customizing & optimizing for deployment:
At inference (every request):
Tokenization and embeddings also run at inference time — the same flow (text → tokens → vectors → transformer layers → output) executes on every request your agent handles. The difference is that during training the model learns from this flow, while during inference it applies what it learned.
┌─────────────────────────────────────────────────────────────┐
│ MODEL CREATION │
│ │
│ Pre-trained Data (trillions of tokens) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PRE-TRAINING │ │
│ │ │ │
│ │ Text ──→ TOKENIZATION ──→ Token IDs │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ EMBEDDING ──→ Vectors │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ TRANSFORMER LAYERS │ │
│ │ (learn parameters by predicting │ │
│ │ next token, billions of iterations) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Base Model (parameters/weights, e.g. 7B, 70B, 405B) │
│ │ │
│ ▼ │
│ (Optional) FINE-TUNING / LoRA ──→ Specialized Model │
│ │ │
│ ▼ │
│ (Optional) QUANTIZATION ──→ Compressed for deployment │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ INFERENCE (using the model) │
│ │
│ Raw Text (user prompt, system prompt, RAG context) │
│ │ │
│ ▼ │
│ TOKENIZATION ──→ Token IDs [5765, 2065, 374, ...] │
│ │ │
│ ▼ │
│ EMBEDDING ──→ Dense vectors (features learned in training) │
│ │ │
│ ▼ │
│ TRANSFORMER LAYERS (parameters detect features, │
│ │ build understanding layer by layer) │
│ │ │
│ ├── + LoRA adapters (if kept separate, applied here │
│ │ alongside transformer weights per layer) │
│ │ │
│ │ OR merged into weights (if pre-merged after │
│ │ fine-tuning — no extra step visible here) │
│ │ │
│ ▼ │
│ Output Token Probabilities ──→ Generated Text │
│ │
└─────────────────────────────────────────────────────────────┘
The top box happens once (or occasionally, when you fine-tune). The bottom box happens every time your agent processes a request. Understanding both helps you make informed decisions about which model to pick, whether to fine-tune, and how to deploy.
Pre-trained data size is the volume of text the model was trained on during pre-training. It’s measured in tokens and determines the breadth of knowledge.
Note: This is the total count of tokens processed during training, not unique tokens. The same document can appear multiple times across training epochs, so “15 trillion tokens” means the model read 15 trillion tokens in sequence as training examples — including repetitions. The actual unique token vocabulary is tiny by comparison (32K–128K entries). Think of it as “total tokens read” rather than “distinct tokens in the dataset.”
| Model | Training Tokens | Approximate Raw Text |
|---|---|---|
| LLaMA-2 | 2 trillion | ~1.5 TB of text |
| LLaMA-3 | 15+ trillion | ~11 TB of text |
| GPT-4 (estimated) | 13+ trillion | ~10 TB of text |
GPT-4’s training data size is not officially disclosed by OpenAI. The figure above is a widely cited estimate.
The optimal balance between model size and training data follows a power law. For a fixed compute budget, there’s an ideal ratio — you get the best performance by scaling parameters and training tokens roughly together, not by making one huge and the other small:
Tokenization is the process of converting raw text into a sequence of integers (token IDs) that the model can process. The model doesn’t see characters or words — it sees tokens.
Raw text is whatever input string gets sent to the model before any processing. It can originate from:
It’s simply the human-readable string that hasn’t yet been split into tokens.
When does the model “see” tokens? At inference time, before any neural network computation begins:
"token" → 5765)From step 3 onward, the model only operates on high-dimensional vectors — it never processes raw characters or words directly.
Input: "Tokenization is interesting!"
Tokens: ["Token", "ization", " is", " interesting", "!"]
IDs: [5765, 2065, 374, 7185, 0] (illustrative — actual IDs vary by tokenizer)
An embedding is a dense vector representation of a token (or sequence) in continuous high-dimensional space. It’s how meaning gets encoded as numbers.
"king" → [0.23, -0.45, 0.89, ..., 0.12] (dimension: 4096)
"queen" → [0.25, -0.43, 0.87, ..., 0.14] (similar vector!)
"car" → [-0.67, 0.31, -0.22, ..., 0.88] (very different)
Each embedding is a fixed-length array of floating-point numbers, each entry in this vector is a dimension. The length of that array is the dimensionality of the embedding.
What is a dimension? A dimension is an axis — a direction in the vector space. Think of 3D space: you have height, width, and depth — three axes, three dimensions. A point in 3D space needs three numbers (one per axis) to describe its position. Now scale that up: a 4096-dimension vector is a point in a space with 4096 axes.
Before training, only the number of dimensions is decided (e.g., “this model will have 4096”). What each axis means is not defined by anyone — they start as blank slots.
During training, the model learns what each axis should represent by adjusting weights to minimize prediction error. The meaning of each dimension emerges from the training process itself. No human labels them. The model fills each axis/dimension with whatever statistical pattern helps it predict text better.
After training, each axis has settled into capturing something — but it’s not human-labeled. Dimension #742 doesn’t explicitly mean “formality” or “topic.” It’s a direction that, combined with all other directions, helps the model separate different meanings. The number stored in each dimension is how far along that axis the word sits. Individually, one axis tells you almost nothing. But the combination of all 4096 positions places the word at a unique point where semantically similar words cluster together.
Common dimension sizes:
| Model / Context | Dimensions | Notes |
|---|---|---|
| BERT (base) | 768 | Older, smaller embedding models |
| OpenAI text-embedding-3-small | 1536 | Good balance of quality and cost |
| OpenAI text-embedding-3-large | 3072 | Higher quality, more expensive to store/search |
| LLaMA-3 8B (internal) | 4096 | Hidden dimension inside the LLM |
| LLaMA-3 70B (internal) | 8192 | Larger model = wider vectors |
Practical impact:
Parameters (or weights) are the learned numerical values inside the model. They are what the model “knows” — the compressed knowledge from training.
Neuron output = activation(weight₁·input₁ + weight₂·input₂ + ... + bias)
Scale reference:
| Model | Parameters | Approximate Size (FP16) |
|---|---|---|
| LLaMA-3 8B | 8 billion | ~16 GB |
| LLaMA-3 70B | 70 billion | ~140 GB |
More parameters = more capacity to store knowledge and patterns, but also more resources needed — both during training and when using the model:
Model size refers to the parameter count and directly determines memory requirements and capability:
Parameters RAM (FP16) Capability Level
─────────────────────────────────────────────────
1-3B 2-6 GB Simple tasks, classification
7-8B 14-16 GB General chat, code, reasoning
13-14B 26-28 GB Strong all-around performance
30-34B 60-68 GB Near frontier quality
65-70B 130-140 GB Frontier-class reasoning
180B+ 360+ GB Largest open models
Some models (Mixtral, GPT-4) use MoE — they have many parameters but only activate a subset per token. A router network decides which “expert” sub-networks handle each token:
A feature is a pattern the model detects in the input to help it decide what to output next. Features are not defined by anyone — the model discovers them on its own during training.
Features aren’t a separate data structure the model looks up — they are the computation happening inside the transformer layers. The model builds features and uses them simultaneously, layer by layer:
Each layer reads features from the previous layer’s output, builds higher-level features on top, and passes them forward. The model doesn’t “detect a question and then use that fact” as separate steps. Instead, by some middle layer, the vector has been shaped in a way that implicitly encodes “this is a question” — and subsequent layers build on that shape to produce an appropriate answer.
Features in training vs. inference:
Features play a role in both phases, but differently:
Think of it like learning to drive vs. driving:
The features exist in the parameters. Training shapes the parameters so they detect useful patterns. Inference runs input through those shaped parameters to produce predictions.
Each transformer layer builds increasingly abstract features on top of the previous layer’s output.
Features and dimensions are related but not the same thing:
Why they’re not one-to-one:
This is called a distributed representation. It’s what makes embeddings powerful (and hard to interpret) — meaning is spread out, not neatly boxed into individual slots.
Analogy: Think of a painting. The dimensions are individual pixels. A feature is “there’s a face in this image.” No single pixel is the face — the face emerges from the combination of many pixels. And each pixel contributes to multiple things (face, background, lighting) at once.
In older ML (like spam filters), a human would manually decide what features to look for:
You’d hand-engineer a list of signals and feed them to the model. In LLMs, nobody defines the features. The model discovers what patterns matter by itself during training. You can’t easily inspect or name them — they emerge from the data.
Fine-tuning is the process of taking a pre-trained model and training it further on a smaller, domain-specific dataset to specialize its behavior.
A pre-trained model is a generalist — it knows a lot about everything but isn’t optimized for your specific task. Fine-tuning narrows its focus:
Full fine-tuning updates all parameters in the model:
LoRA (Low-Rank Adaptation) is a fine-tuning technique that only trains a small set of adapter parameters, making it much cheaper in GPU memory and compute. The training dataset can be any size — LoRA’s benefit is about compute efficiency, not data efficiency.
How it works:
In a transformer, the attention layers have large weight matrices (e.g., 4096 × 4096 = ~16 million numbers in one matrix). During full fine-tuning, you’d update all 16 million numbers. LoRA takes a different approach:
output = original_matrix × input + (A × B) × inputWhy does this work? When you fine-tune for a specific task, you don’t need to change all 16 million numbers. The adjustment needed is much simpler — it can be captured with far fewer numbers. Think of it like: the original matrix is a detailed painting, but the edit you need is just “shift everything slightly warmer in tone” — that’s a simple adjustment, not a full repaint. The two small matrices capture that simple adjustment.
The “16” in those dimensions is the rank — a knob you control:
Deployment options after training:
Once A and B are trained, you have two choices for how to use them:
output = original_matrix × input + (A × B) × input. Slightly more compute per request, but you can swap adapters at runtime — same base model, different LoRA adapters for different tasks, without reloading the full model.merged = original + (A × B). Zero extra computation at runtime, but now it’s a single specialized model — you can’t swap behaviors anymore.Which parameters get updated?
You (the practitioner) choose which layers to attach LoRA adapters to. Typically:
The original model weights are completely frozen. Only the small adapter matrices receive gradient updates during training.
How does training know when to stop?
Same as any neural network training — you monitor a validation loss:
There’s no magic “done” signal — it’s the practitioner’s job to monitor metrics and decide when the adapters have learned enough.
Analogy: Imagine you have a textbook (the pre-trained model). Full fine-tuning takes a red pen to every page — the original content is the starting point, but every part is allowed to change. LoRA adds a small set of sticky notes to specific pages — the original pages stay untouched, and the sticky notes are cheap to create.
QLoRA = LoRA applied on top of a quantized model:
When to fine-tune vs. use RAG:
| Approach | Best for | Tradeoff |
|---|---|---|
| RAG | Factual recall, current information, large knowledge bases | No training needed, but adds latency and retrieval complexity |
| Fine-tuning | Behavior change, output format, tone, domain-specific reasoning | Requires training data and compute, but faster at inference |
| Both | Production agents that need specialized behavior AND current knowledge | Most robust but most complex to maintain |
Quantization reduces the numerical precision of model weights to make models smaller and faster, with minimal quality loss.
Remember: every parameter in a model is a number (a weight). By default, each weight is stored as a 16-bit floating-point number (FP16). A 70B model has 70 billion of these numbers, each taking 16 bits — that’s ~140 GB just to load the model into memory. For many teams, that’s too expensive or simply doesn’t fit on available hardware.
Quantization solves this by asking: “Do we really need 16 bits of precision for each weight?” In most cases, the answer is no. You can round each weight to a less precise representation (8-bit, 4-bit, even 2-bit) and the model still produces nearly the same output. It’s like reducing an image from 24-bit color to 8-bit color — you lose some subtle gradients, but the picture is still recognizable.
Quantization is typically applied after training, as a separate step before deployment:
Who does it:
Original (FP16): 1.234375 → stored in 16 bits
Quantized (INT4): 1.25 → stored in 4 bits (approximation)
| Method | Approach |
|---|---|
| GPTQ | Post-training quantization using calibration data |
| AWQ | Activation-aware quantization (protects important weights) |
| GGUF | Format used by llama.cpp for CPU inference |
| bitsandbytes | Dynamic quantization during loading (NF4, INT8) |
Q-bits refers to the number of bits used to represent each weight after quantization. Lower bits = smaller model = faster but potentially less accurate.
FP32 (32-bit): Full precision, baseline quality
FP16 (16-bit): Half precision, standard for training/inference
INT8 (8-bit): Good quality, 2x smaller than FP16
INT4 (4-bit): Acceptable quality, 4x smaller than FP16
INT2 (2-bit): Experimental, significant quality loss
| Q-bits | Size Reduction | Quality Impact | Use Case |
|---|---|---|---|
| FP16 | 1x (baseline) | None | Cloud inference, training |
| INT8 | 2x smaller | Minimal (~1%) | Production serving |
| Q6_K | 2.7x smaller | Very small | Local with quality priority |
| Q4_K_M | 4x smaller | Small (~2-3%) | Local inference on laptop |
| Q2_K | 8x smaller | Noticeable | Experimentation only |