Reference Videos: Here These are rough short notes, attuned to my needs w.r.t to the topic.
Index
- [[#Some Terms]]
- [[#Tokenization]]
- [[#Positional Embeddings & RoPE]]
- [[#The Elephant in the Room - Attention]]
- [[#Refining the Attention Pattern]]
- [[#Multi-Headed Attention & Scale]]
- [[#Training vs. Inference]]
- [[#Context Windows & Output Limits]]
- [[#KV Caching]]
- [[#FlashAttention: Defeating the $O(N^2)$ VRAM Monster]]
- [[#Related]]
- [[#To be added..]]
Some Terms
-
Pretraining - is the initial, computationally intensive phase of training where a neural network learns general language patterns, syntax, and world knowledge by analyzing massive, unstructured datasets of text or code to predict the next token. Pretraining establishes the foundational model that later undergoes fine-tuning and alignment for specific applications.
-
Supervised Fine-Tuning (SFT) - is the essential training phase that transforms a generalized, pre-trained base model into a functional, instruction-following assistant. It adapts the model using a curated dataset of specific input-output (instruction-response) pairs, teaching it how to behave, format responses, and operate within specific domains.
-
RLHF (Reinforcement Learning from Human Feedback) is a training technique that aligns Large Language Models (LLMs) with human values. It bridges the gap between raw text prediction and actual helpfulness by using human preferences to guide the model’s tone, factual accuracy, and safety. See PPO, GRPO, DPO, RLAIF etc.
Tokenization
Every token maps to an embedding (trainable):
-
Character level - For e.g. if we use unicode, there’s 297,334 assigned code points(chars) out of maximum possible 1,114,112 in the standard. so vocabulary = 297,334 tokens. Since, each token is a char, the resulting token consumption fills up context window really fast. Extra work for the network to capture gazillion ways in which characters will be arranged into words, their meaning, their meaning w.r.t the others. Versus, obvious words/roots/prefixes/suffixes forming a single token making the process easier on the network.
-
Word level - vocab is dictionary. Resulting model can’t tolerate misspellings, also doesn’t understand roots/prefxies/suffixes etc. Also, bad for multiple languages, vocab explodes.
-
UTF-8 + BPE: one of the better methods ; in UTF-8, the first bit(s) have some metadata signifying type of current byte (starting byte or continuation byte) and the length of the char (1/2/3/4 byte). Every token ends up being 1 byte. In good old ascii text, we have a simple bytestream with all tokens(and accordingly characters) being 1 byte. If we have foreign lang/emojis or something, then that 1 character will be expressed using multiple bytes, and consequently multiple tokens. BPE recursively replaces most frequent character pair, while also forming “new characters” in the vocab. You start with vocab size 256, then with BPE, add to it the new characters. You not only get the flexibility of character level encoding but also the representative power of words (roots/prefixes/suffixes etc).
Positional Embeddings & RoPE
-
The Need for Order: Transformers process all tokens simultaneously and inherently have no concept of sequence order.
-
Positional Math: A positional vector is added to the word vector to provide a “clock” or index. This operation itself is mathematically trivial and is not a bottleneck.
-
Absolute vs. Relative: Older models used fixed positional slots, creating hard boundaries.
-
Rotary Position Embedding (RoPE): Modern models use relative trigonometric rotations (angles) instead of fixed slots. This mathematical framework allows the context window to be stretched to millions of tokens without requiring total retraining.
The Math Behind RoPE: The Relative Distance Function
When we talked about Rotary Position Embeddings (RoPE) turning absolute positions into relative distances, the “Function” in that relationship relies on the geometry of complex numbers.
To see how the absolute positions (let’s call them $m$ for the query token and $n$ for the key token) collapse into a relative distance $(m-n)$, we can map the token vectors onto a 2D complex plane.
Instead of adding a position vector, RoPE multiplies the query and key vectors by a complex rotation. If $\theta$ is the base angle frequency, we rotate the query vector $q$ by angle $m\theta$ and the key vector $k$ by angle $n\theta$:
\[q_m = q e^{im\theta}\] \[k_n = k e^{in\theta}\]The attention score is calculated by taking the dot product (the inner product) of these two vectors. In the complex plane, this looks like:
\[\langle q_m, k_n \rangle = \text{Re}(q_m k_n^*)\]Substitute our rotated vectors into the equation:
\[\langle q_m, k_n \rangle = \text{Re}(q e^{im\theta} k^* e^{-in\theta})\] \[\langle q_m, k_n \rangle = \text{Re}(q k^* e^{i(m-n)\theta})\]That right there is the magic. The absolute positions $m$ and $n$ vanish as independent variables. The attention score is now strictly a function of the original word vectors ($q$ and $k$) and the relative distance between them $(m-n)$. This elegant mathematical property is exactly what allows modern models to extrapolate beyond their trained context windows.
The Elephant in the Room - Attention
When text enters the model, each word is initially assigned a high-dimensional vector - an embedding, where directions in space correspond to semantic meaning. However, these initial embeddings are just a basic lookup table — the word “mole” gets the exact same vector whether the text says “true mole,” “mole of carbon dioxide,” or “biopsy of the mole.”
The attention mechanism exists to fix this. It allows the embeddings to pass information to each other, updating their vectors so they bake in the surrounding context.
The Three Pillars: Queries, Keys, and Values
To figure out how words should update one another, a single “head” of attention relies on three matrices full of tunable parameters that act on the embeddings.
Think of an example phrase like “a fluffy blue creature”, where adjectives need to update the meaning of a noun:
-
Queries (Q): What a word is “looking for.” The model multiplies the embedding by a Query Matrix. You can imagine the noun “creature” broadcasting a query that asks: “Are there any adjectives sitting in front of me?”
-
Keys (K): What a word “is.” The embeddings are multiplied by a Key Matrix. The adjectives “fluffy” and “blue” produce key vectors that essentially answer: “Yes, I am an adjective.”
-
The Dot Product (Relevance): To measure how well a Key matches a Query, the model computes their dot product. High positive scores mean the words align closely and should “attend” to each other.
-
Values (V): Once the model knows a word is relevant, the Value Matrix determines what information should actually be passed. These value vectors are multiplied by the relevance score and added to the original word’s embedding (e.g., shifting “creature” into a more specific, fluffy-blue direction in the embedding space).
Key insight: The attention mechanism calculates a complete grid of every possible Key-Query combination, producing a massive matrix of relevance scores between every single word in the sequence.
Refining the Attention Pattern
Before those relevance scores can be used to update the embeddings, two critical mathematical steps happen to the grid:
-
Masking: During training, the model tries to predict the next word for every subsequence simultaneously. To prevent later words from influencing earlier words (which would be “cheating” by giving away the answer), the model sets the relevance scores of all future tokens to negative infinity.
-
Softmax: The model applies a softmax function to the columns of the grid. This normalizes the dot product scores into usable weights between 0 and 1 that sum up to exactly 1, acting like a probability distribution for how much influence to pull from surrounding words.
Because this grid maps every word against every other word, its size scales quadratically — $O(N^2)$ with the context size. This is why larger context windows are historically so computationally expensive.
Multi-Headed Attention & Scale
Everything above describes a single head of attention, which might learn just one contextual rule (like adjectives updating nouns). To capture the true complexity of language, transformers use multi-headed attention:
-
Parallel Processing: Models run many heads simultaneously. For example, GPT-3 uses 96 distinct heads per block. One head might track grammar, another tracks pronoun references, and another tracks emotional tone.
-
The Final Update: For each word, the proposed changes from all 96 heads are summed together and added to the original embedding, creating a vastly richer mathematical representation of the word.
-
Parameter Count: The scale is staggering. A single multi-headed block contains roughly 600 million parameters. Across all 96 layers, GPT-3 dedicates nearly 58 billion parameters just to the attention mechanism alone.
-
A single attention head isn’t to be mistook for a single question (e.g. looking for adjective). It’s a matrix of a decent number of weights. It most likely is asking multiple superposed questions, and for the exact same query depending on the input token itself, the question(s) that the product of query weights and the input token’s embedding yield may very well be asking entirely different things.
Training vs. Inference
-
Training is parallel: The model processes the entire sequence simultaneously. A single forward pass calculates the loss for every position at once (each token predicts the one immediately following it).
-
Inference is autoregressive: Text generation is a sequential loop. The model predicts one token, appends it to the input sequence, and feeds the entire updated sequence back into the network to predict the next token.
How Training Works in Practice
Because the self-attention mechanism processes the entire context window simultaneously, a single forward pass over a sequence (e.g., 2,048 tokens) yields 2,048 distinct output vectors at the final layer. Instead of just calculating the loss on the very last token, the training pipeline calculates the cross-entropy loss for every position at the same time.
If the training sequence is ["The", "cat", "sat", "on", "the", "mat"], the model outputs a vector for each word simultaneously.
-
The vector at position 1 (context:
"The") is optimized to predict"cat". -
The vector at position 2 (context:
"The", "cat") is optimized to predict"sat". -
The vector at position 3 (context:
"The", "cat", "sat") is optimized to predict"on". -
…and so on.
Why this matters: This is the core reason why Transformers are so efficient to train on GPUs compared to older architectures. By using every single column to predict the token that comes immediately after it, a single batched operation provides 2,048 different training signals rather than just one.
How Inference Works in Practice
The GPU allocates a matrix sized to the model’s maximum context window, filling unused space with padding tokens. An attention mask ensures the model only calculates attention for the active tokens.
During generation, we only look at the output vector of the last active token in the sequence (the current “final column”) to predict the new unknown token. That final vector contains the context of the entire sequence and is passed through the unembedding matrix to predict the $N+1$ token. Once predicted, that new token is appended to the input, and the process repeats.
The <EOS> Token: The autoregressive loop does not run indefinitely. During training, documents are terminated with an End of Sequence (<EOS>) token. When the final column outputs <EOS> with the highest probability, the software wrapper breaks the loop and finalizes the output.
Context Windows & Output Limits
-
The Shared Bucket: The total context window is a hard architectural limit shared between the prompt and the response (Total Context = Input Tokens + Output Tokens).
-
Prefill vs. Decode: Reading the input prompt (Prefill) is fast because it is processed in parallel. Generating the output (Decode) is slow because it is sequential.
-
Asymmetric Limits: Output limits are heavily capped compared to input limits to prevent KV cache VRAM exhaustion and to mitigate “hallucination drift” (where the model loses the original context by overly conditioning on its own prolonged output).
Input vs. Output Caps: The KV Cache Dynamic
From a pure storage perspective, 1 token = 1 token. An input token and an output token occupy the exact same amount of VRAM in the KV cache.
However, output limits are capped much more heavily due to system dynamics:
-
Deterministic vs. Speculative Allocation:
-
Input (Prefill): The system knows the exact memory footprint upfront and can reject the request immediately if it doesn’t fit.
-
Output (Decode): Non-deterministic. Without a cap, a runaway generation can unexpectedly bloat, causing memory fragmentation or crashing the batch.
-
-
Multi-Tenant Scheduling (Continuous Batching): Output caps act as a resource governor. They guarantee a predictable maximum footprint per request, allowing the scheduler to safely pack multiple users into the same VRAM pool without risking mid-generation evictions.
-
Quadratic Scaling Costs:
-
Total compute/memory access complexity scales as:
\[\mathcal{O}(N_{\text{out}} \times (N_{\text{in}} + N_{\text{out}}))\] -
Because every new output token requires reading the entire historical cache from VRAM, longer outputs exponentially degrade system throughput and latency. Input is a one-time “sunk cost”; output is a compounding bandwidth tax.
-
-
Hallucinations and the “Drift” Effect The longer an LLM generates text without being re-grounded by human input, the more likely it is to degrade in quality.
-
If you ask a model to write a 100,000-word novel in a single output, by word 20,000, it is heavily conditioning its next predictions on its own previously generated (and potentially flawed) text.
-
This causes “drift,” where the model starts looping in repetitive patterns, forgetting the original prompt, or hallucinating wildly. Capping the output forces the user to interact, course-correct, and feed new, clean input into the sequence.
-
KV Caching
Re-multiplying the entire matrix for historical tokens every time a new word is added wastes massive compute. The system caches the intermediate mathematical representations (Keys and Values) of past tokens in GPU memory (VRAM). For a new token, the GPU only calculates its specific values and pulls the rest from the cache.
To walk through a concrete example: if you feed the model a 3-token input ["The", "cat", "sat"], the model processes 3 columns simultaneously:
-
Column 1 (only has context of
"The"): Its output vector will give high probabilities for words like"cat","dog","quick", etc. -
Column 2 (has context of
"The", "cat"): Its output vector will give high probabilities for words like"sat","ran","is". -
Column 3 (has context of
"The", "cat", "sat"): Its output vector predicts the 4th token, giving high probabilities for words like"on","down","quietly".
If Column 3 predicts "on", you take "on", append it to the input array so it becomes ["The", "cat", "sat", "on"], and send the whole sequence back into the model to predict the 5th token.
Because the Keys and Values of earlier tokens like "The", "cat", and "sat" do not change when "on" is added to the end of the sequence, the system caches those intermediate calculations in GPU memory. The server doesn’t actually recalculate the entire sequence — it only runs the matrix multiplications for the newly added token and pulls the historical context directly from the memory cache.
The VRAM Wall: Storing the KV cache for large context windows (or thousands of concurrent users) consumes massive amounts of VRAM, making memory capacity and memory bandwidth the primary hardware bottlenecks. Managing this cache efficiently across thousands of concurrent users is currently one of the hardest infrastructure challenges in deploying high-throughput AI systems.
FlashAttention: Defeating the $O(N^2)$ VRAM Monster
VRAM and memory bandwidth are the true physical bottlenecks of the Transformer. FlashAttention is currently the most important engineering breakthrough in the industry because it directly attacks how the GPU routes that memory.
To understand FlashAttention, you have to look at the physical architecture of a GPU, which has a strict memory hierarchy:
-
SRAM (Compute Cache): Incredibly fast, located right next to the compute cores, but incredibly tiny (e.g., 20MB per GPU).
-
HBM (VRAM): The main memory pool (e.g., 80GB on an H100). It is huge, but reading/writing to it is remarkably slow compared to the cores’ calculation speed.
The Problem with Standard Attention
In standard attention, the GPU calculates the massive $N \times N$ attention matrix (Query matrix multiplied by Key matrix). Because it is so huge, it cannot fit in the SRAM. The GPU is forced to write this massive $N \times N$ matrix to the slow HBM, read it back to apply the Softmax function, write it back to HBM, and read it again to multiply it by the Value matrix.
The GPU cores spend most of their time sitting idle, waiting for the massive matrix to travel back and forth from the HBM. This is known as the “Memory Wall.”
The FlashAttention Solution
-
Tiling: The large N × N attention score matrix is never fully created in the main memory (HBM). Instead, the inputs (Q, K, V) are divided into small blocks (tiles) that fit into the GPU’s ultra-fast on-chip memory (SRAM).
-
Kernel Fusion: Operations like matrix multiplication, softmax, and scaling are fused into a single CUDA kernel. The GPU processes each block entirely within SRAM, completely bypassing the need to write intermediate steps back to HBM.
-
Online Softmax: Because softmax typically requires looking at an entire row of data at once, FlashAttention tracks incremental, running scaling statistics across tiles. This allows it to compute mathematically exact softmax results piece-by-piece.
-
Recomputation: To save massive amounts of memory during training backward passes, FlashAttention does not store the intermediate attention matrices. It simply recomputes them on-the-fly using the cached fast statistics.
Needs further reading- https://gordicaleksa.medium.com/eli5-flash-attention-5c44017022ad
Related
- A single attention head isn’t to be mistook for a single question (e.g. looking for adjective). It’s a matrix of a decent number of weights. It most likely is asking multiple superposed questions, and for the exact same query depending on the input token itself, the question(s) that the product of query weights and the input token’s embedding yield may very well be asking entirely different things.
To be added..
- Grouped attention, sparse attention.