Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
GPT stands for Generative Pre-trained Transformer, the flagship family pioneered by OpenAI; large language model (LLM) is the broader technical category. Every decoder-only model runs on a single loop: predict the next token given prior tokens. Yet software teams rely on them to classify pull requests, extract identifiers from stack traces, draft root-cause explanations, and write bug fixes. How does one unidirectional prediction loop become an interactive general-purpose interface?
Picture continuous integration run RUN-842. It terminates abruptly with an unhandled exception at auth_fixture_test.py:41: expired auth fixture. Your team needs one developer assistant to tackle three distinct jobs:
- Label an incoming code review comment ("This docs page is stale") as
Docs. - Extract the exact run identifier
RUN-842from a noisy 2,000-line build log. - Answer an engineer who asks why the test suite failed on the release branch.
A pure next-token predictor sounds too narrow for all three tasks. Classical machine learning systems would train three separate pipelines: a text classifier, a token-span extractor, and a seq2seq summary model. Modern LLMs replace those bespoke architectures with a single text-to-text contract: feed the task in text, let the model generate the continuation.
The original Transformer debuted in 2017 as a dual-stack machine translation system with an encoder and a decoder.[1] Subsequent research split those components apart, scaled their parameters, aligned their behavior with human expectations, and overhauled their runtime mechanics. We'll track RUN-842 through every stage of this evolution, examining how each architectural breakthrough removed an engineering bottleneck while introducing fresh serving tradeoffs.
One generation loop, many tasks
Return to RUN-842. In a traditional supervised setup, a classification model predicts logits over a closed set of three labels: {"Bug", "Docs", "Security"}. An extraction model outputs character spans [start_idx, end_idx] pointing into the input log. A generative interface handles both by treating every job as prompt continuation.
A causal decoder reads text from left to right and samples the next token. The system prompt and demonstrations define the meaning of the continuation slot. When the model sees Label:, the next token should be Docs. When it sees Extracted Run ID:, the continuation should be RUN-842. When asked an open-ended question, it emits an explanatory sentence token by token.
A software assistant encounters three standard task shapes:
- Classification: A review note states, "This docs page is stale." You want the category label
Docs. - Extraction: A CI trace contains
RUN-842inside a stack frame. You want the exact identifier. - Generation: An on-call engineer asks, "Why did tests fail?" You want a concise diagnosis grounded in the trace.
An encoder model like BERT can handle the first two tasks with custom classification and span heads, but can't generate fluent explanations. A decoder-only model expresses all three through few-shot prompting:
1Classify the review note into Bug, Docs, or Security.
2
3Note: The auth test fails on main.
4Label: Bug
5
6Note: This docs page is stale.
7Label: Docs
8
9Note: The API key is printed in logs.
10Label: Security
11
12---
13
14Note: The README still mentions the old CLI flag.
15Label:The model ingests the prompt prefix, attends across the task rules and demonstrations, and predicts the continuation at the empty Label: slot. The prompt pattern sets up an inductive bias for Docs.
A unified text-to-text interface simplifies system architecture, but it introduces validation requirements. A classifier with a fixed output layer can only emit valid class labels; a generative model might emit Documentation, Doc, or a conversational preamble like Sure, the label is Docs. The calling application must validate and normalize the generated text against its schema.

Prompt framing turns classification into next-token scoring. The expected answer is Docs:
1def build_review_label_prompt(note: str) -> str:
2 examples = [
3 ("The auth test fails on main.", "Bug"),
4 ("This docs page is stale.", "Docs"),
5 ("The API key is printed in logs.", "Security"),
6 ]
7 header = "Classify the review note into Bug, Docs, or Security."
8 shots = "\n\n".join(
9 f"Note: {text}\nLabel: {label}" for text, label in examples
10 )
11 return f"{header}\n\n{shots}\n\n---\n\nNote: {note}\nLabel:"
12
13prompt = build_review_label_prompt("The README still mentions the old CLI flag.")
14print(prompt)
15print("pattern_target=Docs")
16print("why=the prompt pattern ends with a label slot")1Classify the review note into Bug, Docs, or Security.
2
3Note: The auth test fails on main.
4Label: Bug
5
6Note: This docs page is stale.
7Label: Docs
8
9Note: The API key is printed in logs.
10Label: Security
11
12---
13
14Note: The README still mentions the old CLI flag.
15Label:
16pattern_target=Docs
17why=the prompt pattern ends with a label slotWhy can a decoder-only model classify a review note even though its pretraining objective was next-token prediction?
Answer
The prompt establishes an in-context demonstration pattern ending at Label:. Next-token prediction places high probability mass on valid category words matching the demonstrations. The application still needs to parse the generated token and verify it belongs to the allowed label set.
The fork: reading and writing split apart
Why did the industry converge on decoder-only models for conversational assistants when BERT dominated natural language understanding in 2018? The answer lies in the attention mask.
The 2017 Transformer consisted of two stacks: an encoder that processed the full source sentence bidirectionally, and an autoregressive decoder that generated target tokens one by one while cross-attending to the encoder representations.[1] In 2018, researchers discovered that you didn't need both stacks for every task:
- The encoder-only branch (BERT). Google released BERT in 2018.[2] It uses an unmasked Transformer encoder. Every token attends to all tokens in the sequence simultaneously, both left and right. BERT pretrains on Masked Language Modeling (MLM), predicting randomly masked tokens from surrounding bidirectional context. It excels at producing fixed-size vector representations for classification, sentiment analysis, entity extraction, and passage retrieval.[3] It can't generate free-form text because its training objective never learned autoregressive generation.
- The decoder-only branch (GPT). OpenAI released GPT-1 in 2018.[4] It removes the encoder entirely, keeping only the decoder blocks. A lower-triangular causal mask prevents tokens from attending to future positions. It trains purely on causal language modeling: predict token given .
| Production requirement | Encoder-only (BERT-style) | Decoder-only (GPT-style) |
|---|---|---|
| Classify an incident report | Strong native fit; outputs class logits from [CLS] token. | Supported via prompt formatting; requires text parsing. |
| Generate a root-cause explanation | Requires adding a separate decoder. | Strong native fit; decodes tokens sequentially. |
| Dense semantic search | Strong fit; creates compact passage embeddings. | Capable, but computationally heavier for pure indexing. |
| Interactive assistant | Can't handle free-form conversational turns. | Universal interface for multi-turn dialogue. |
Causal decoders conquered general-purpose AI because generation subsumes understanding, but understanding doesn't subsume generation. If a model can generate text, you can cast classification, translation, summarization, and reasoning into text prompts. If a model only produces pooled vectors, generating open-ended sentences requires adding a decoder anyway.
A decoder writes left to right
In causal attention, token attends only to positions . When predicting the token after failed, the model attends to <bos>, tests, and failed. It can't peek at future tokens.
During training, causal masking enables teacher forcing: you pass the full training sequence through the model in one forward pass and compute the loss on all tokens in parallel using an upper-triangular mask of . During inference, generation runs sequentially: each newly emitted token is appended to the input prefix to predict the following token.
1tokens = ["<bos>", "tests", "failed", "<eos>"]
2n = len(tokens)
3causal = [[1 if col <= row else 0 for col in range(n)] for row in range(n)]
4bidirectional = [[1] * n for _ in range(n)]
5
6print("tokens:", tokens)
7print("causal row for 'failed': ", causal[2])
8print("bidirectional row for 'failed':", bidirectional[2])
9print("future visible in causal row:", causal[2][3])1tokens: ['<bos>', 'tests', 'failed', '<eos>']
2causal row for 'failed': [1, 1, 1, 0]
3bidirectional row for 'failed': [1, 1, 1, 1]
4future visible in causal row: 0The 0 at index 3 prevents the model from attending to the future <eos> token. Bidirectional encoders allow all-to-all connectivity (1, 1, 1, 1), making them great for full-sentence representation but incapable of causal autoregressive generation.
What mathematical property distinguishes a causal decoder attention matrix from an encoder attention matrix?
Answer
A causal decoder attention matrix is strictly lower-triangular: attention scores for positions are masked with (or zeroed out post-softmax), ensuring position can't attend to any future position . An encoder attention matrix has no triangular constraint, allowing every position to attend to all other positions.
The life of a token: prefill, KV cache, and decode
Trace the engineer's prompt through the decoder: "Why did tests fail?".
Text enters the tokenizer and breaks into a sequence of discrete token IDs. An embedding lookup table maps each ID to a vector of dimension . Position information is injected into each token's representation.
The tokens flow through stacked decoder layers. Inside each block:
- Normalization stabilizes activations across features.
- Self-attention projects hidden states into Queries (), Keys (), and Values ().
- Softmax-weighted attention scores combine values across all allowed causal positions.
- An output projection maps the attended values back to .
- A feed-forward network transforms each position independently through nonlinear activations.
After the final layer normalization, the hidden vector corresponding to the very last token position () projects through the unembedding matrix to produce raw vocabulary logits.[5] Applying softmax turns logits into probabilities. The sampling strategy (greedy argmax, temperature scaling, top-) selects the next token: Auth.
Inference splits into two distinct execution phases:
- The prefill phase (compute-bound): The model processes all prompt tokens in parallel. Because the full prompt is available upfront, matrix multiplications take the form . These large matrix-matrix multiplications (GEMM) saturate GPU tensor cores and run compute-bound. During prefill, the model computes and saves the Key and Value vectors for all prompt tokens into GPU memory: the KV cache.
- The decode phase (memory-bandwidth bound): To generate the next token, the model executes a forward pass for only a single new token (). Instead of re-running attention over all past tokens from scratch (which would take compute per sequence), the layer loads the cached Key and Value vectors from high-bandwidth memory (HBM), computes Query vectors for the single new token, appends the new Key and Value to the cache, and computes attention scores. Because batch size is small and sequence length is 1, decode steps run matrix-vector multiplications (GEMV). Tensor cores sit underutilized while the GPU waits for weights and cache tensors to stream across memory buses.

Inspect the output projection and cache dimensions with concrete Python calculations:
1import math
2
3prompt_tokens, d_model, vocab_size = 5, 4, 4
4last_hidden = [0.6, -0.2, 0.4, 0.1]
5output_rows = [
6 [0.8, 0.1, -0.2, 0.0],
7 [0.2, 0.5, 0.1, -0.1],
8 [-0.1, 0.2, 0.4, 0.3],
9 [0.0, -0.3, 0.2, 0.4],
10]
11labels = ["Auth", "The", "I", "Fix"]
12logits = [sum(h * w for h, w in zip(last_hidden, row)) for row in output_rows]
13peak = max(logits)
14exps = [math.exp(x - peak) for x in logits]
15z = sum(exps)
16probs = [e / z for e in exps]
17
18print("prompt tokens:", prompt_tokens, "d_model:", d_model, "vocab:", vocab_size)
19print("last-position logits:", [round(x, 2) for x in logits])
20print("argmax:", labels[logits.index(max(logits))])
21print("prob sum:", round(sum(probs), 3))
22layers, kv_heads, head_dim = 4, 2, 2
23print("cached K/V shape per layer:", (2, 1, kv_heads, prompt_tokens, head_dim))
24assert math.isclose(sum(probs), 1.0)
25assert logits.index(max(logits)) == 01prompt tokens: 5 d_model: 4 vocab: 4
2last-position logits: [0.38, 0.05, 0.09, 0.18]
3argmax: Auth
4prob sum: 1.0
5cached K/V shape per layer: (2, 1, 2, 5, 2)Notice the sequence length discrepancy right after sampling. Selecting Auth adds it to the text output, bringing the sequence to 6 tokens. The KV cache still contains only 5 positions. The next decode step takes Auth as its single input token, computes its Key and Value vectors, and expands the cache to length 6.
Why does the decode phase underutilize GPU tensor cores compared to the prefill phase?
Answer
Prefill processes all prompt tokens at once, executing large matrix-matrix multiplications (GEMM) with high arithmetic intensity. The decode phase passes only a single token at a time (), resulting in matrix-vector operations (GEMV). The GPU spends most of its clock cycles streaming model weights and accumulated KV cache tensors from High Bandwidth Memory into on-chip cache, running memory-bandwidth bound.
The evolution trajectory: GPT-1 to aligned assistants
Between 2018 and 2023, the decoder-only architecture moved through four major eras. Each stage addressed a key bottleneck in how language models acquire and apply knowledge.

1. GPT-1: Generative pretraining + task fine-tuning (2018)
Before GPT-1, natural language processing relied on task-specific models trained on small supervised datasets, or static word embeddings like Word2Vec and GloVe. Radford et al. (2018) showed that unsupervised next-token pretraining across a large text corpus (BooksCorpus, ~5 GB) produces general representations.[4]
GPT-1 consisted of a 12-layer decoder with 117 million parameters. Its core workflow was a two-stage process:
- Unsupervised pretraining: Train the decoder on raw text using standard autoregressive cross-entropy loss.
- Supervised fine-tuning: Discard the language modeling head, attach a task-specific linear projection layer, and update all model weights on labeled classification or entailment datasets.
Fine-tuning required separate checkpoints for every downstream task. It showed pretraining worked, but generation wasn't yet the operational interface.
2. GPT-2: Zero-shot transfer without task-specific weights (2019)
Radford et al. (2019) made a bold conceptual leap with GPT-2: language models are unsupervised multitask learners.[6] By expanding parameter scale to 1.5 billion parameters and training on WebText (40 GB of scraped internet text), they discovered that models can solve downstream tasks without updating weights or swapping linear heads.
Instead of training a dedicated sentiment head, you condition the model on a prompt:
"Input: The service was awful. Sentiment:"
The model predicts "negative" purely via zero-shot continuation. GPT-2 proved that task conditioning could be expressed entirely in natural language.
3. GPT-3: In-context few-shot emergence (2020)
Brown et al. (2020) expanded the architecture by two orders of magnitude, training GPT-3 with 175 billion parameters on 300 billion tokens.[7] At 175B parameters, in-context learning emerged as a reliable capability.
Instead of gradient updates, you provide 2 to 3 demonstrations directly in the prompt prefix. The model uses self-attention across the demonstration tokens to bind the task rules dynamically at inference time. Prompting became a new programming model: you configure the model by describing the task rather than collecting thousands of labeled examples to fine-tune weights.
4. InstructGPT & ChatGPT: Alignment via RLHF (2022)
GPT-3 unlocked in-context capability, but using base models was frustrating. Because base models optimize next-token prediction across raw web pages, they replicate internet text conventions rather than acting as helpful assistants. Prompting a base model with "Explain this test failure" frequently caused it to append more test errors, generate a fabricated forum discussion, or spit out spam.
To transform an autocomplete engine into a cooperative assistant, Ouyang et al. (2022) introduced InstructGPT, establishing the Reinforcement Learning from Human Feedback (RLHF) pipeline:[8]
- Supervised Fine-Tuning (SFT): Human annotators write thousands of high-quality demonstrations of instructions and ideal responses. The base model fine-tunes on this instruction dataset, learning the conversational assistant format.
- Reward Modeling (RM): The SFT model generates multiple candidate responses for various prompts. Human labelers rank these candidates from best to worst. A reward model trains on these pairwise comparisons to output a scalar score predicting human preference.
- Reinforcement Learning (PPO): The SFT model acts as an RL policy, updated via Proximal Policy Optimization (PPO) to maximize the reward model score while penalizing drift from the initial policy using a Kullback-Leibler (KL) divergence penalty.
On human evaluations, annotators consistently preferred outputs from a 1.3-billion parameter InstructGPT model over those from the 175-billion parameter raw GPT-3 base model.[8] Alignment demonstrated that raw parameter scale is incomplete without behavioral calibration.
Inspect how pairwise preference records evaluate whether chosen completions adhere to ground-truth evidence:
1comparisons = [
2 {
3 "prompt": "CI run RUN-842: auth_fixture_test.py:41 reports 'expired auth fixture'. Explain the failure.",
4 "chosen": "RUN-842 failed at auth_fixture_test.py:41: the log reports an expired auth fixture.",
5 "rejected": "The build failed for an unknown reason.",
6 "required_fact": "expired auth fixture",
7 },
8 {
9 "prompt": "Review note says the API key is printed in logs. Classify the issue.",
10 "chosen": "Security: redact the API key and rotate the exposed credential.",
11 "rejected": "Docs: update the README wording.",
12 "required_fact": "API key",
13 },
14]
15
16for row in comparisons:
17 keeps_required_fact = (
18 row["required_fact"] in row["prompt"]
19 and row["required_fact"] in row["chosen"]
20 )
21 print(f"prompt={row['prompt'][:22]}... chosen_keeps_fact={keeps_required_fact}")
22 print(" preferred:", row["chosen"])1prompt=CI run RUN-842: auth_f... chosen_keeps_fact=True
2 preferred: RUN-842 failed at auth_fixture_test.py:41: the log reports an expired auth fixture.
3prompt=Review note says the A... chosen_keeps_fact=True
4 preferred: Security: redact the API key and rotate the exposed credential.Understanding the distinction between base, instruct, and chat models helps teams select the right checkpoint:
| Checkpoint type | Training objective | Expected behavior | Production role |
|---|---|---|---|
| Base model | Pure causal language modeling on web-scale text. | Continues input text matching internet distribution; may emit transcripts or lists. | Foundation for domain continued pretraining and custom fine-tuning. |
| Instruct model | SFT on instruction-following datasets. | Answers direct single-turn commands (summarization, extraction). | Ideal for single-turn extraction and deterministic classification tasks. |
| Chat model | SFT + RLHF / DPO with multi-turn role formatting. | Follows system guidelines, maintains conversation context, and applies safety checks. | Default choice for interactive developer assistants and agents. |
A chat template serializes multi-turn conversations into a single token string using special control tokens (such as <|im_start|>system and <|im_end|>).[9] Applying a chat template formats the prompt correctly for an aligned model, but it won't give a raw base model instruction-following behavior that it was never trained to exhibit.
Why does human preference alignment make a 1.3B InstructGPT model more useful than a 175B raw GPT-3 base model for answering user questions?
Answer
A base model is an unsupervised autocomplete engine trained to predict web text: when asked a question, it may continue with related questions, alternative drafts, or internet fluff. InstructGPT's SFT and RLHF stages train the model specifically on the behavioral contract of answering requests directly, accurately, and concisely.
Scaling laws and compute optimality
As teams trained larger models, an engineering question arose: given a fixed computational budget (measured in floating-point operations, or FLOPs), how should you divide your budget between adding parameters and feeding more training tokens?
Kaplan et al. (2020) at OpenAI published early power-law scaling relationships for transformer language models.[10] Their empirical findings suggested that cross-entropy loss scales as a power law primarily with parameter count , and that when compute budgets increase, teams should invest the vast majority of compute into scaling model size rather than dataset size:
This drove the industry to build massive models trained on modest datasets. DeepMind built Gopher with 280 billion parameters trained on 300 billion tokens.[11] GPT-3 allocated 175 billion parameters to 300 billion tokens: a ratio of only ~1.7 tokens per parameter.
In 2022, Hoffmann et al. identified a flaw in Kaplan's analysis: the learning rate schedule had not been tuned independently for each training duration, causing longer runs on smaller models to underperform.[11] DeepMind trained over 400 models spanning 70M to 16B parameters across various token budgets to establish the Chinchilla scaling laws:
To train compute-optimally, for every doubling of model parameters, the number of training tokens should also double. Compute-optimal models require approximately 20 tokens per parameter:
| Model | Parameters | Training tokens | Tokens per parameter | Compute efficiency |
|---|---|---|---|---|
| Gopher | 280B | 300B | ~1.1 | Severely token-starved |
| GPT-3 | 175B | 300B | ~1.7 | Token-starved |
| Chinchilla | 70B | 1.4T | 20.0 | Compute-optimal frontier |
| LLaMA 1 | 65B | 1.4T | 21.5 | Inference-optimal design |
| LLaMA 3 | 8B | 15.0T | 1,875.0 | Over-trained serving specialist |
DeepMind built Chinchilla (70B parameters, 1.4T tokens) using the exact same compute budget as Gopher (280B parameters, 300B tokens). Chinchilla outperformed Gopher across downstream benchmarks including MMLU, Big-bench, and GSM8K.[11]
1runs = {
2 "Gopher": {"params_b": 280, "tokens_b": 300},
3 "GPT-3": {"params_b": 175, "tokens_b": 300},
4 "Chinchilla": {"params_b": 70, "tokens_b": 1400},
5 "LLaMA-3-8B": {"params_b": 8, "tokens_b": 15000},
6}
7
8for name, run in runs.items():
9 ratio = run["tokens_b"] / run["params_b"]
10 print(f"{name:12s} tokens/param={ratio:7.1f}")1Gopher tokens/param= 1.1
2GPT-3 tokens/param= 1.7
3Chinchilla tokens/param= 20.0
4LLaMA-3-8B tokens/param= 1875.0Training compute versus lifetime serving costs
The Chinchilla ratio represents training-compute optimality: minimizing the loss achieved for a given training budget. In real systems, total cost includes inference serving.
Meta's LLaMA paper pushed this insight further.[12] A 7B or 8B model trained on 15 trillion tokens (like LLaMA 3) costs significantly more training compute than Chinchilla's recommendation. But that 8B model fits onto a single GPU and serves millions of user queries at a fraction of the hardware cost required to host a 70B model. Over-training compact models on high-quality data is an intentional systems strategy to minimize lifetime inference cost.
Why do modern production labs intentionally train 8B models on 15 trillion tokens, defying Chinchilla's compute-optimal 20:1 ratio?
Answer
Chinchilla optimality minimizes training FLOPs to achieve a target loss. Once a model is deployed to production, inference costs quickly dominate training costs. An over-trained 8B model delivers the accuracy of a 70B model while fitting on single-GPU instances with dramatically lower latency and memory costs.
Modern frontier architectures: RMSNorm, SwiGLU, and RoPE
The vanilla Transformer architecture proposed by Vaswani et al. in 2017 is no longer what modern frontier models execute. Open-source foundations such as LLaMA, Mistral, Gemma, and DeepSeek converged on three core architectural upgrades:
1. Root Mean Square Normalization (RMSNorm)
Standard LayerNorm computes both the mean and variance across the feature dimension of hidden vector :[13]
Zhang & Sennrich (2019) demonstrated that the mean-centering step () contributes almost nothing to gradient stabilization during training.[14] What stabilizes training is scaling the input vector by the root mean square of its activations.
RMSNorm discards the mean calculation and the learned additive bias , scaling strictly by root mean square energy:[14][15]
RMSNorm reduces memory bandwidth traffic by avoiding a two-pass reduction across the vector. It achieves identical training stability while executing 10% to 50% faster on modern accelerators.
2. SwiGLU activations
Early transformers used standard ReLU or GELU activations inside a two-layer feed-forward network: .
Dauphin et al. and Shazeer (2020) demonstrated that Gated Linear Units (GLUs), which compute the elementwise product of two linear projections where one projection acts as a continuous gate, significantly improve model capacity.[16]
SwiGLU uses the Swish (or SiLU) activation function as the gating mechanism:
Because SwiGLU introduces three weight matrices ( and ) instead of two, models resize the intermediate hidden dimension from down to approximately (usually rounded to a multiple of 64 or 256). This keeps parameter count and training FLOPs identical to standard MLPs while lowering perplexity.
3. Rotary Position Embeddings (RoPE)
Original Transformers used absolute positional encodings: learned or sinusoidal vectors added directly to input token embeddings: . Absolute encodings don't explicitly encode relative distance, and they struggle to extrapolate when sequence lengths exceed training contexts.
Su et al. (2021) introduced Rotary Position Embeddings (RoPE).[17] Instead of adding positional vectors at the input layer, RoPE rotates 2D coordinate pairs in the Query and Key vectors inside each attention head by a position-dependent angle :
Because rotation matrices are orthogonal and multiplicative (), the resulting attention dot product depends strictly on relative position:
If token 14 attends to token 12, their dot product depends only on their relative distance . It produces the exact same relative geometric transformation as token 4 attending to token 2. RoPE equips models with natural attention decay across long sequences and supports context length extension via RoPE frequency interpolation.
Run the mechanics of RMSNorm and SwiGLU in Python:
1import math
2
3def rms_norm(x: list[float], gamma: list[float], eps: float = 1e-6) -> list[float]:
4 d = len(x)
5 rms = math.sqrt(sum(xi ** 2 for xi in x) / d + eps)
6 return [(xi / rms) * gi for xi, gi in zip(x, gamma)]
7
8def silu(z: float) -> float:
9 return z / (1.0 + math.exp(-z))
10
11x = [1.2, -0.8, 2.5, -1.5]
12gamma = [1.0, 1.0, 1.0, 1.0]
13normed = rms_norm(x, gamma)
14
15w_gate = [0.5, -0.3]
16w_up = [1.2, 0.8]
17gate_val = sum(xi * w for xi, w in zip(x[:2], w_gate))
18up_val = sum(xi * w for xi, w in zip(x[:2], w_up))
19swiglu_val = silu(gate_val) * up_val
20
21print("rms_norm output:", [round(v, 4) for v in normed])
22print("rms verification:", round(math.sqrt(sum(v**2 for v in normed) / len(normed)), 4))
23print("gate:", round(gate_val, 4), "up:", round(up_val, 4), "swiglu:", round(swiglu_val, 4))1rms_norm output: [0.7379, -0.4919, 1.5372, -0.9223]
2rms verification: 1.0
3gate: 0.84 up: 0.8 swiglu: 0.4694Why does RoPE rotate Query and Key representations instead of adding positional vectors to token embeddings?
Answer
Adding absolute position vectors mixes positional coordinates into semantic representations and forces the model to learn relative distances through layers of matrix transformations. RoPE rotates 2D coordinate pairs directly within attention heads, guaranteeing that the inner product between Query at position and Key at position depends purely on their relative offset .
Attention scaling and KV cache bandwidth reduction
During generation, every newly emitted token requires fetching the entire accumulated KV cache from GPU memory. For a 70B parameter model serving a batch of requests with 8,192 tokens of context, the KV cache alone can consume dozens of gigabytes of VRAM.
The exact memory footprint of the KV cache across all layers is:
Where is batch size, is sequence length, is layer count, is the number of Key/Value heads, is head dimension, is precision bytes (2 bytes for FP16/BF16), and the factor of 2 accounts for Keys and Values.
To tame this memory bottleneck, researchers developed three attention head topologies:
- Multi-Head Attention (MHA): Each query head has an independent key head and value head (). For 32 query heads, you cache 32 key heads and 32 value heads.
- Multi-Query Attention (MQA): Shazeer (2019) proposed sharing a single key head and a single value head across all query heads ().[18] This reduces the KV cache size by , but the severe compression can lead to quality degradation on complex reasoning and code retrieval tasks.
- Grouped-Query Attention (GQA): Ainslie et al. (2023) introduced the sweet spot: group query heads into partitions (e.g., 8 KV heads for 32 query heads, a 4:1 ratio).[19] GQA slashes the KV cache memory footprint and memory bandwidth demand by 75% while matching MHA quality across downstream tasks.
Compare cache sizes across architectures with concrete hardware numbers:
1batch, layers, tokens, head_dim, bytes_per_value = 8, 32, 8192, 128, 2
2query_heads = 32
3
4for label, kv_heads in [("MHA", 32), ("GQA", 8), ("MQA", 1)]:
5 cached_values = batch * layers * tokens * kv_heads * head_dim * 2
6 cache_gib = cached_values * bytes_per_value / (1024 ** 3)
7 relative = kv_heads / query_heads
8 print(f"{label}: kv_heads={kv_heads:2d} cache={cache_gib:5.2f} GiB relative={relative:.3f}")1MHA: kv_heads=32 cache=32.00 GiB relative=1.000
2GQA: kv_heads= 8 cache= 8.00 GiB relative=0.250
3MQA: kv_heads= 1 cache= 1.00 GiB relative=0.031RoPE preserves relative distance invariance across varying sequence positions:
1import math
2
3def rotate(vector: tuple[float, float], position: int, theta: float = 0.4) -> tuple[float, float]:
4 angle = position * theta
5 cosine, sine = math.cos(angle), math.sin(angle)
6 x, y = vector
7 return (cosine * x - sine * y, sine * x + cosine * y)
8
9def dot(left: tuple[float, float], right: tuple[float, float]) -> float:
10 return left[0] * right[0] + left[1] * right[1]
11
12query = (1.0, 0.2)
13key = (0.3, 0.9)
14same_gap_early = dot(rotate(query, 4), rotate(key, 2))
15same_gap_late = dot(rotate(query, 14), rotate(key, 12))
16different_gap = dot(rotate(query, 14), rotate(key, 2))
17print("same gap, early:", round(same_gap_early, 6))
18print("same gap, late: ", round(same_gap_late, 6))
19print("different gap: ", round(different_gap, 6))
20print("same gap equal:", math.isclose(same_gap_early, same_gap_late))1same gap, early: 0.936998
2same gap, late: 0.936998
3different gap: -0.794779
4same gap equal: TrueAlongside GQA, two runtime systems innovations make modern serving viable:
- FlashAttention: An exact attention implementation by Dao et al. (2022) that tiles matrix computations into GPU SRAM blocks, reducing high-bandwidth memory access from to .[20]
- PagedAttention: Developed by Kwon et al. (2023) for vLLM, it treats KV cache memory like an operating system's paged virtual memory.[21] By breaking the cache into non-contiguous physical memory blocks, it eliminates external fragmentation and boosts concurrent serving throughput.
How does Grouped-Query Attention (GQA) reduce decode latency compared to Multi-Head Attention (MHA)?
Answer
The decode phase is memory-bandwidth bound: the GPU spends most of its time streaming past Key and Value vectors from HBM into on-chip cache. In GQA, multiple query heads share a single KV head (e.g. 4:1 ratio), shrinking KV cache memory traffic by 75%. Less data moving across the memory bus directly speeds up token generation.
Sparse architectures: Mixture of Experts (MoE)
Dense models route every token through all parameters. A 70B dense model executes 70 billion parameter calculations for every token. As context lengths and batch sizes grow, the compute cost becomes prohibitive.
Shazeer et al. (2017) introduced the sparsely gated Mixture of Experts layer, modernised by Mixtral 8x7B (Jiang et al., 2024) and DeepSeek models.[22][23]
An MoE model replaces standard feed-forward layers with independent expert networks. A gating router computes a probability distribution over the experts and dispatches each token to only the top- experts (typically or ):
The fundamental MoE tradeoff: VRAM vs FLOPs
Mixtral 8x7B contains 47 billion total parameters across its 8 experts per layer. But for any given token, only 2 experts activate, touching approximately 13 billion parameters.
The decoupling is stark:
- Active parameters (~13B): Dictates compute FLOPs and execution latency per token.
- Total parameters (~47B): Dictates GPU VRAM capacity requirements. All 47B parameters must reside in VRAM simultaneously. You need enough GPUs to store 47B parameters (at least 90 GB in FP16), even though each token only touches 13B of them.
Without an auxiliary load-balancing loss, routers suffer from winner-take-all collapse: the gating network routes all tokens to 1 or 2 favored experts, leaving the remaining experts untrained and starved of gradients. Training with a load-balancing loss encourages an even distribution of tokens across all available experts.
Inspect top-2 expert routing in code:
1import math
2
3experts = ["E0", "E1", "E2", "E3"]
4tokens = ["import", "assertion"]
5router_logits = [
6 [2.4, 0.3, 0.5, -0.2],
7 [0.1, 2.2, 0.7, 0.0],
8]
9
10for token, logits in zip(tokens, router_logits):
11 ranked = sorted(range(len(logits)), key=lambda i: logits[i], reverse=True)[:2]
12 selected = [logits[i] for i in ranked]
13 peak = max(selected)
14 weights = [math.exp(value - peak) for value in selected]
15 total = sum(weights)
16 weights = [weight / total for weight in weights]
17 assert len(ranked) == 2 and math.isclose(sum(weights), 1.0)
18 routes = list(zip((experts[i] for i in ranked), (round(weight, 3) for weight in weights)))
19 print(f"{token:10s} -> {routes}")1import -> [('E0', 0.87), ('E2', 0.13)]
2assertion -> [('E1', 0.818), ('E2', 0.182)]Why can an engineer run a 47B MoE model faster than a 47B dense model, but still need the same number of GPUs to host it?
Answer
An MoE model only activates a subset of its parameters per token (e.g. 13B active out of 47B total), so it requires far fewer floating-point operations per decode step. However, because different tokens in a sequence route to different experts unpredictably, all 47B parameter weights must reside in GPU memory simultaneously.
Reasoning models and test-time compute
Standard autoregressive generation emits the final response immediately after ingesting the prompt. For complex code diagnosis, mathematical derivation, or multi-step logic, jumping directly to an answer frequently produces plausible-sounding hallucinations.
In 2024 and 2025, reasoning models like OpenAI o1 and DeepSeek-R1 introduced a shift: trading test-time inference compute for solution accuracy.[24][25]
Instead of answering instantly, a reasoning model generates an internal chain of thought before emitting its final answer. The model can:
- Formulate a hypothesis and test it against the evidence.
- Backtrack when a reasoning path leads to a contradiction.
- Compare stack frames, commit histories, and test configurations systematically.
OpenAI exposes this behavior via the reasoning.effort API parameter.[26] For our RUN-842 assistant, a standard model might immediately answer that the test failed due to a timeout. A reasoning model spends extra test-time compute verifying the stack trace, noting that line 41 explicitly reports an expired auth fixture, and producing a grounded root-cause analysis.
Reasoning tokens are billable tokens that consume time and context window space. Teams should evaluate reasoning effort against latency budgets on hard incident cases rather than defaulting to maximum effort for routine tasks.
How do reasoning models differ from standard chat models during inference?
Answer
Standard chat models output their final answer token by token immediately following the prompt. Reasoning models spend additional inference-time compute generating internal chain-of-thought tokens (deliberating, testing alternative hypotheses, and checking for errors) before producing the final response.
Open weights, licensing, and local serving
In early 2023, Meta released the original LLaMA family of models.[12] This release unlocked open-weight model deployment: teams could download checkpoint weights directly, run them on private clusters, quantize them to 4-bit precision, and fine-tune them using Low-Rank Adaptation (LoRA).
When evaluating downloadable models, software teams must distinguish between three terms:
| Category | Definition | What to inspect |
|---|---|---|
| Open weights | The model's learned weight checkpoints are publicly downloadable. | Commercial use restrictions, monthly active user thresholds, and acceptable use policies. |
| Open source | Code and architecture meet the OSI definition (e.g., Apache 2.0, MIT). | Dependencies, compiler compatibility, and integration libraries. |
| Open data | The underlying pretraining dataset is accessible and verifiable. | Data licensing, copyright status, filtering recipes, and safety boundaries. |
Two 2026 model families highlight why weight availability and serving practicalities require separate checks:
| Model release | Published claims | Operational realities |
|---|---|---|
| GLM-5.2 | 744B total parameters, ~40B active per token; 1M token context window under an MIT license.[27][28] | In BF16 precision, 744B parameters require roughly 1.49 TB of raw weight storage before accounting for KV cache memory. Serving requires multi-node GPU clusters with fast tensor parallelism. |
| DeepSeek V4 Flash 0731 | 284B total parameters, 13B active per token; MIT license with attached speculative decoding.[29][30] | The speculative decoding module requires specialized inference runtimes. Serving throughput depends heavily on whether your hardware configuration accommodates the sparse expert communication overhead. |
Hosting open weights ensures that proprietary CI logs never leave your VPC boundary. But your team assumes operational ownership of GPU procurement, cluster uptime, driver updates, and throughput optimization.
Why is calling an open-weight model "open source" often inaccurate?
Answer
Open source requires open code, permissive licensing without commercial discrimination, and reproducible build pipelines. Many open-weight releases provide downloadable neural network weights but restrict specific commercial uses, set user caps, or keep pretraining data, training code, and filtering algorithms proprietary.
Evaluating a released model on your workload
Don't select a model for your production assistant based on public leaderboard screenshots. Public benchmarks measure broad capabilities on standardized test sets, not your proprietary logs, company idioms, or latency constraints.
Construct a representative evaluation dataset:
- 50 failing CI logs requiring exact root-cause extraction.
- 50 code review notes requiring discrete triage categorization.
- 50 multi-turn debugging questions requiring cited file paths.
Define an explicit multi-criteria scoring objective. Model selection is a constrained optimization problem:
1candidates = [
2 {"name": "hosted-fast", "quality": 0.86, "p95_ms": 250, "cost": 0.50, "license_ok": True},
3 {"name": "hosted-deep", "quality": 0.94, "p95_ms": 1200, "cost": 1.50, "license_ok": True},
4 {"name": "local-restricted", "quality": 0.90, "p95_ms": 500, "cost": 0.20, "license_ok": False},
5]
6
7def score(model: dict, latency_penalty: float, cost_penalty: float) -> float:
8 if not model["license_ok"]:
9 return float("-inf")
10 return 100 * model["quality"] - latency_penalty * model["p95_ms"] - cost_penalty * model["cost"]
11
12scenarios = {
13 "live code help": (0.03, 8.0),
14 "nightly audit": (0.003, 4.0),
15}
16
17for scenario, penalties in scenarios.items():
18 eligible = [model for model in candidates if model["license_ok"]]
19 if not eligible:
20 raise ValueError("no candidate satisfies the license constraint")
21 ranked = sorted(eligible, key=lambda model: score(model, *penalties), reverse=True)
22 winner = ranked[0]
23 print(f"{scenario:13s} winner={winner['name']} score={score(winner, *penalties):.2f}")1live code help winner=hosted-fast score=74.50
2nightly audit winner=hosted-deep score=84.40For interactive code completion, latency is paramount: hosted-fast wins because an extra 950 ms delay destroys the developer flow. For a nightly test audit running asynchronously, accuracy dominates: hosted-deep wins.
Why can a model with 94% benchmark accuracy lose to a model with 86% accuracy in a production scorecard?
Answer
Production scorecards account for multi-dimensional constraints including p95 response latency, per-request serving costs, and licensing restrictions. A model with slightly higher accuracy that takes 1.2 seconds to respond or costs 3x more can be disqualified for real-time developer workflows.
Context window scaling and lost in the middle
Modern LLMs advertise context windows spanning 128k to over 1 million tokens. But having a million-token context window doesn't mean the model retrieves information uniformly across all positions.
Liu et al. (2023) documented "Lost in the Middle": language models recall information placed near the extreme beginning (primacy effect) or extreme end (recency effect) of long contexts with high fidelity, but accuracy drops sharply when key information is buried in the middle 20% to 80% of the prompt.[31]

Three factors drive this degradation:
- Primacy bias & attention sinks: The initial tokens in a prompt receive disproportionate attention mass across all transformer layers (acting as attention sinks).
- Recency bias: Tokens placed immediately before the generation slot sit fresh in local attention, allowing direct attention transfer without degradation across long causal chains.
- Attention diffusion: In the middle of a 20-document context, softmax weights diffuse across hundreds of distractor tokens, diluting the signal of the target passage.
Practical prompt engineering mitigations
When feeding large logs or multiple retrieved documents to your developer assistant:
- Place system instructions and governing policies at the very top of the prompt.
- Place the specific question or task instruction at the very end.
- If retrieval yields several candidate snippets, place the most relevant evidence passage either right at the start or directly above the final question.
1policy = "Rule: explain CI failures using cited log lines."
2older_notes = [
3 "History: dependency cache restored.",
4 "History: formatter completed.",
5 "History: unit tests started.",
6]
7case = "Evidence: RUN-842 failed at auth_fixture_test.py:41 with expired auth fixture."
8question = "Question: explain the failure and cite the evidence."
9
10prompt_lines = [policy, *older_notes, case, question]
11for position, line in enumerate(prompt_lines, start=1):
12 print(f"{position}: {line}")
13print("evidence_adjacent_to_question:", prompt_lines[-2] == case)11: Rule: explain CI failures using cited log lines.
22: History: dependency cache restored.
33: History: formatter completed.
44: History: unit tests started.
55: Evidence: RUN-842 failed at auth_fixture_test.py:41 with expired auth fixture.
66: Question: explain the failure and cite the evidence.
7evidence_adjacent_to_question: TruePlacing the decisive RUN-842 evidence adjacent to the question mitigates attention diffusion, ensuring the model grounds its response in the actual log error.
Why does retrieval-augmented generation (RAG) remain relevant even when models support 1-million-token context windows?
Answer
RAG filters out thousands of irrelevant distractor tokens, preventing the "Lost in the Middle" retrieval degradation. It also reduces KV cache memory consumption, lowers API billing costs, and improves generation latency by keeping prompt contexts focused on relevant passages.
Match the architecture to the job
Use this decision table when selecting models for engineering pipelines:
| Pipeline task | Recommended architecture | Primary selection rationale |
|---|---|---|
| Large-scale log embedding & clustering | Encoder-only (e.g. BERT / ModernBERT) | Fast bidirectional encoding; outputs compact fixed-size vectors for cosine similarity index. |
| Real-time code autocomplete | Compact dense decoder (e.g. 1B to 3B GQA) | Sub-50ms latency; minimal memory bandwidth footprint on local developer machines. |
| Multi-turn CI debugging assistant | Aligned chat decoder with GQA (8B to 70B) | Follows conversational instructions; robust in-context grounding and code generation. |
| High-throughput multi-task API gateway | Sparse Mixture of Experts (MoE) | High total knowledge capacity with low active FLOPs per token during concurrent serving. |
| Complex multi-file refactoring & math | Reasoning model (test-time compute) | Spends inference compute deliberating across dependencies before generating the diff. |
Common misconceptions
| Misconception | Observable symptom | Root cause | Engineering fix |
|---|---|---|---|
| "GPT means any LLM." | Engineers refer to open-weight LLaMA checkpoints as "GPTs". | GPT is OpenAI's brand name for its proprietary autoregressive model series. | Use "decoder-only model" or "LLM" for the general architectural category. |
| "BERT is completely obsolete." | Teams deploy 70B generative models to perform simple log classification. | Decoders dominate media coverage. | Use lightweight encoder models for embedding search, reranking, and discrete label classification. |
| "A bigger model always wins." | Selecting a 70B model trained on 300B tokens over a 7B model trained on 15T tokens. | Assuming parameter count equals intelligence. | Check dataset token volume and quality. Over-trained compact models frequently outperform starved massive models. |
| "Open-weight means free commercial use." | A startup builds a commercial product on a model with non-commercial license terms. | Confusing public model weights with open-source licensing. | Read the model card and license terms before integrating into commercial products. |
| "Context length guarantees recall." | Placing a stack trace in the middle of a 100k-token prompt and wondering why the model misses it. | Conflating context capacity with attention retrieval fidelity. | Place decisive evidence adjacent to the query or near prompt boundaries, and test recall empirically. |
Practice: diagnose the failing assistant
Your first prototype of the RUN-842 assistant exhibits three operational failures:
- Behavioral failure: When an engineer sends
"Explain why RUN-842 failed at line 41", the model responds by outputting:"Explain why RUN-843 failed at line 12\nExplain why RUN-844 failed at line 99". - Retrieval failure: When you provide a 20-line snippet, the model correctly identifies the expired auth fixture. When you provide the complete 50,000-line build log containing the exact same snippet in the middle, the model claims:
"The build succeeded without errors". - Memory failure: A newly proposed 47B MoE model advertises 13B active parameters per token. Your ops team attempts to deploy it on a single GPU with 24 GB of VRAM, and the container crashes immediately with an
OutOfMemoryCUDA error.
How do you diagnose and fix each of the three failures?
Answer
- The first issue indicates a raw base model executing text continuation instead of instruction following. Switch to an aligned instruct or chat checkpoint and verify your application wraps user input with the model's required chat template.
- The second issue is a classic "Lost in the Middle" attention degradation failure. Use a log parser or RAG pipeline to isolate the relevant stack trace snippet and place it immediately above the user's question.
- The third issue confuses active parameters with memory footprint. Even though the MoE model only computes 13B parameters per token, all 47B parameters must reside in GPU memory simultaneously. In FP16, 47B parameters require ~94 GB of VRAM. Deploy across multiple GPUs or use 4-bit quantization.