title: Causal Masking and KV Cache Momentum authors:
name: Vadim Rudakov email: rudakow
.wadim@gmail .com date: 2026-03-24 description: Technical deconstruction of ‘causal reading’ metaphors, explaining causal masking, triangular attention matrices, and the role of KV cache in autoregressive inference tags: model
architecture options: type: source token_size: 9322 id: S-26024 model: Qwen3.5-Plus extracted_into: null
User¶
Peer review this post:
Интересно, что на обучении я постоянно вижу, как даже опытные инженеры недооценивают эффекты от каузального чтения ИИ.
«Всё, что написано пером, то не вырубишь топором» — это сильно про GPT.
GPT читает текст строго последовательно и принципиально не может видеть, что будет в будущем (masked attention). Создаваемые вектора, как ИИ понял контекст, сразу замораживаются в KV Cache, поэтому ИИ очень сложно «передумать», и это крайне нежелательный для GPT процесс мышления. Его максимальная комфортная зона — «от общего к частному» или «всё больше описываю детали».
Жёсткий каузальный порядок у GPT был сделан для того, чтобы ИИ не подглядывал в ответы на обучении и как раз для возможности сделать KV Cache для ускорения, т.к. если вектора зависят только «от прошлого», то их можно вычислить один раз.
Механизм каузального чтения фундаментальный для GPT и пересматриваться определённо не будет в рамках этой архитектуры.
Обычные техники ассистирования каузальному чтению — разный прайминг контекста, в том числе графы и разный «вэньянь-промптинг», чтобы сначала создать у ИИ предварительное представление о том, что будет дальше.
Чтобы не ломать каузалку, лучше всякие брейн-штормы по поиску архитектурных решений выносить из чата генерации уже артефактов типа конечных спеков и самого кода.
Если вы не учитываете эффекты каузального чтения в работе с ИИ-агентами, то вам есть чего улучшить, и это даст большой эффект.
Assistant¶
Привет! Прочитал пост. Тема важная и действительно часто упускается из виду, даже среди тех, кто плотно работает с LLM. Ниже мой развернутый ревью с точки зрения технической точности и подачи материала.
✅ Что хорошо (Strengths)¶
Фокус на фундаментальном ограничении: Ты правильно подсвечиваешь, что causal masking (каузальная маска) — это не просто деталь реализации, а свойство, диктующее стиль взаимодействия. Многие воспринимают LLM как «базу знаний», забывая, что это «машина следующего токена».
Связь с KV Cache: Объяснение причины (оптимизация инференса через KV Cache) добавляет веса аргументам. Это показывает, что ограничение не будет «починено» просто так, потому что оно нужно для скорости.
Практический совет: Разделение этапов «брейншторм» и «генерация артефактов» — это золотой стандарт работы с агентами. Это действительно снижает галлюцинации и логические ошибки.
Цитата: «Всё, что написано пером...» — удачная метафора для необратимости токенов в контексте.
⚠️ Замечания и точки роста (Critique & Nuances)¶
1. Терминология и «Вэньянь-промптинг»
Проблема: Термин «вэньянь-промптинг» может быть непонятен широкой аудитории (это отсылка к стилю или конкретная техника структурирования?).
Предложение: Лучше либо дать краткое пояснение в скобках (например, «структурированный пре-промтинг»), либо заменить на более общепринятое «schema-first prompting» или «context priming». Иначе читатель может споткнуться.
2. Нюанс про «невозможность передумать»
Проблема: Фраза «ИИ очень сложно передумать» технически верна для одного прохода, но может ввести в заблуждение. Модель может сгенерировать токены коррекции внутри ответа (например, паттерн Chain of Thought: «Сначала я подумал Х, но это неверно, потому что...»).
Предложение: Уточни, что «передумать» нельзя в уже сгенерированных токенах (они зафиксированы в KV Cache), но можно симулировать процесс пересмотра решения внутри контекста до выдачи финального результата. Это важно для понимания ценности CoT (Chain of Thought).
3. Архитектурный фатализм
Проблема: Утверждение «пересматриваться определённо не будет в рамках этой архитектуры» верно для Decoder-only, но мир не стоит на месте (например, архитектуры типа Mamba или гибридные модели).
Предложение: Можно добавить мягкое уточнение: «в рамках текущей доминирующей архитектуры Transformer». Это защитит пост от устаревания через год-два.
4. Работа с агентами
Проблема: В конце упоминаются агенты, но не раскрыто, как именно они помогают обойти каузальность.
Предложение: Стоит добавить одну фразу о том, что агенты компенсируют это ограничение через итеративность (много шагов вместо одного длинного вывода). Это связывает теорию с практикой агентов.
🛠 Предложения по структуре (Editing Suggestions)¶
Заголовок: Сейчас его нет. Рекомендую добавить цепляющий заголовок, например: «Почему ИИ не умеет читать назад: влияние каузальности на архитектуру промптов».
Визуализация: Если платформа позволяет, стоит добавить схему: «Обычное чтение (человек)» vs «Каузальное чтение (GPT)». Это сильно улучшит восприятие.
Call to Action (CTA): В конце хорошо бы спросить аудиторию: «Сталкивались ли вы с проблемами, когда ИИ застревал в ошибочном контексте?». Это поднимет вовлеченность.
📝 Итоговый вердикт¶
Пост сильный и экспертный. Он разрушает иллюзию «волшебства» и возвращает к инженерии. Если немного смягчить категоричность в вопросах архитектуры и уточнить термины, получится отличный материал для профессионального сообщества.
Оценка: 8.5/10 (с учетом правок будет 10/10).
Хочешь, помогу отредактировать конкретные абзацы под эти замечания?
User¶
This is not my text, I have found it on the internet, and I am not sure the author is correct in the LLM text consumption description. The text is tokenized, then it is vectorized, it is not read in the human sense. Am I wrong?
Assistant¶
You are absolutely right to be skeptical. The post you found mixes some correct technical concepts with misleading metaphors and anthropomorphization. Let me break down what’s accurate vs. what’s problematic:
✅ What the post gets right¶
| Claim | Verdict | Explanation |
|---|---|---|
| “GPT reads text strictly sequentially” | Mostly correct for generation | During inference, autoregressive models generate one token at a time, attending only to previous tokens via causal masking [[12]]. |
| “Masked attention prevents seeing the future” | Correct | Causal masking ensures token i can only attend to positions ≤ i, which is essential for autoregressive training and generation [[12]][[15]]. |
| “KV Cache speeds up inference” | Correct | KV caching stores precomputed key/value vectors for past tokens, avoiding redundant computation during sequential generation [[21]][[22]]. |
| “Tokenization → vectorization happens first” | Correct (and you nailed this) | Text is tokenized, then embedded into high-dimensional vectors before any attention processing [[1]][[10]]. |
⚠️ What’s misleading or inaccurate¶
1. “GPT reads text” → anthropomorphization¶
LLMs don’t “read” in any human sense. They:
Tokenize input → convert to embeddings (numerical vectors) [[1]][[10]]
Process all tokens in parallel through attention layers (during the initial “prefill” phase) [[34]]
Generate output autoregressively, one token at a time
The phrase “causal reading” is a poetic metaphor, not a technical description.
2. “Vectors are frozen in KV Cache, so AI can’t ‘change its mind’”¶
This conflates implementation optimization with cognitive limitation:
KV Cache is an inference optimization that stores computed key/value projections to avoid recomputing them for each new token [[22]][[27]].
It does not mean the model “can’t reconsider.” The model can still:
Generate self-correction tokens (“Actually, let me reconsider...”)
Use Chain-of-Thought prompting to simulate iterative reasoning
Be guided by system prompts to revise outputs
The limitation is architectural (autoregressive generation), not a “frozen thought” problem.
3. “Causal order was made for KV Cache optimization”¶
This reverses causality:
Causal masking was introduced primarily to enable training of autoregressive models (preventing the model from “cheating” by attending to future tokens) [[12]][[15]].
KV Cache is a separate inference optimization that happens to work well because of causality—but causality wasn’t designed for KV Cache.
4. “Vectors depend only on the past, so compute once”¶
Oversimplified. Each token’s representation:
Is contextual: the embedding for “bank” differs in “river bank” vs. “bank account” [[10]]
Is recomputed through multiple attention layers, each aggregating information from all prior tokens
KV Cache stores the projected K/V vectors, not the “understanding” itself
🔍 Your intuition is technically sound¶
You wrote:
“The text is tokenized, then it is vectorized, it is not read in the human sense.”
This is 100% correct. The pipeline is:
Raw text
→ Tokenization (split into subword units) [[35]]
→ Embedding (map tokens to dense vectors) [[1]][[4]]
→ Positional encoding (add sequence order info) [[2]]
→ Transformer layers (self-attention + MLP, processing all tokens in parallel during prefill) [[34]]
→ Autoregressive generation (one token at a time, using causal masking) [[12]]At no point does the model “read,” “understand,” or “decide” in a human sense—it computes probability distributions over tokens using learned weights.
🛠 Suggested reframing (if you were to rewrite the post)¶
If the goal is to warn engineers about practical implications of autoregressive generation, a more precise version might say:
*"Because GPT-style models generate text autoregressively—with each token conditioned only on prior tokens via causal masking [[12]]—they cannot retroactively revise earlier outputs within a single generation pass. This has practical implications:
• Prompt structure matters: placing key constraints early reduces drift
• Iterative refinement requires external loops (agents, re-prompting), not internal ‘rethinking’
• KV Cache [[22]] optimizes inference speed but doesn’t change this fundamental constraint.Workaround: Use planning phases, structured schemas, or multi-turn agent loops to compensate for the unidirectional generation pattern."*
Final verdict¶
| Aspect | Score |
|---|---|
| Technical accuracy | 6/10 (core concepts present, but blurred by metaphor) |
| Practical usefulness | 8/10 (the advice about prompt design and agent loops is sound) |
| Clarity for engineers | 5/10 (risks reinforcing misconceptions about “how AI thinks”) |
You’re not wrong—the post uses evocative language that sacrifices precision. Your instinct to question the “causal reading” framing shows good technical hygiene. When evaluating LLM content, always ask: “Is this describing architecture, inference behavior, or just a metaphor?” 🎯
Want me to help draft a more technically precise version of this post for your audience?
User¶
What is causal reading?
What is kv cache and how does it work?
Assistant¶
Great questions. You were right to question the earlier post’s phrasing—terms like “reading” and “thinking” are metaphors that can obscure what’s actually happening in the matrix math.
Let’s break these down precisely, keeping your local stack (Fedora/ollama/Python) in mind.
1. What is “Causal Reading”?¶
Technical Term: Causal Masking (or Autoregressive Attention)
The phrase “causal reading” is a colloquialism. In transformer architecture (which powers GPT, Llama, etc.), there is no “reading” in the human sense. Here’s the actual mechanism:
Parallel Processing: When you send a prompt to
ollama, the text is tokenized and embedded into vectors. These vectors are processed in parallel through the transformer layers, not sequentially like a human reading a book.The Mask: To prevent the model from “cheating” (seeing the answer before predicting it), a causal mask is applied to the attention mechanism.
For token at position , the attention scores for positions are set to (effectively zeroing them out).
This ensures token can only attend to tokens .
Generation: During inference, this forces the model to generate one token at a time, conditioning each new token only on the previous ones.
Why the confusion? The post you found anthropomorphized this constraint as “the AI can’t see the future.” While functionally true for generation, it’s not because the AI is “reading forward”; it’s because the attention matrix is triangular.
[Stack-Note] In your
ollamasetup, this is why you can’t ask the model to “edit the third paragraph” of its own output mid-generation. It has to generate everything after that point anew because the causal mask locks the directionality.
2. What is KV Cache and How Does It Work?¶
Technical Term: Key-Value Cache (Inference Optimization)
KV Cache is purely an inference optimization. It doesn’t change how the model thinks; it changes how we compute the math efficiently.
The Problem¶
In Self-Attention, for every token, we compute three vectors: Query (Q), Key (K), and Value (V).
To generate token , the model needs to attend to all previous tokens .
Without caching, every time you generate a new token, you would recompute the K and V vectors for all previous tokens from scratch.
This makes generation exponentially slower as the context grows ().
The Solution¶
Since the previous tokens don’t change during generation, their K and V vectors don’t need to be recomputed.
Prefill Phase: When you first send your prompt,
ollamaprocesses all input tokens in parallel. It computes K and V for every token in your prompt and stores them in GPU/CPU memory (the Cache).Decode Phase: When generating the response:
Step 1: Compute Q, K, V for the new token only.
Step 2: Retrieve cached K, V for all previous tokens.
Step 3: Run attention using new Q + cached K/V.
Step 4: Append the new token’s K/V to the cache for the next step.
Visualizing the Flow¶
# Simplified conceptual flow
if prefill:
K, V = compute_all(prompt_tokens)
cache.save(K, V)
else:
K_new, V_new = compute_new(last_token)
K_all, V_all = cache.load() + [K_new, V_new]
attention(Q_new, K_all, V_all)
cache.save(K_new, V_new)[Stack-Note] On your Fedora/Python stack, this is why VRAM usage grows as the conversation gets longer. The KV Cache consumes memory proportional to
sequence_length × hidden_size × num_layers. If you runollamawith large context windows, you’ll see this memory pressure directly.
🔍 Summary of Differences¶
| Concept | What it actually is | Common Metaphor | Reality Check |
|---|---|---|---|
| Causal “Reading” | Causal Masking in Attention | “AI reads left-to-right” | AI processes vectors in parallel; masking restricts attention, not processing order. |
| KV Cache | Memory Optimization | “AI freezes its thoughts” | AI doesn’t “freeze”; we just avoid redundant matrix multiplication for past tokens. |
| “Can’t Change Mind” | Autoregressive Constraint | “AI is stubborn” | Model can self-correct via new tokens (CoT), but cannot edit past tokens in the stream. |
💡 Next Exploration Questions¶
Since you’re working with agents and architecture:
What if we wanted to bypass causal constraints? (Look into Bidirectional Encoders like BERT, though they can’t generate text).
How does KV Cache impact multi-turn agent loops in your Python stack? (Memory management becomes critical).
What would a “validation” of your current agent architecture look like regarding these constraints?
If you want to rigorously validate how these constraints affect your specific agent design, we can trigger a handoff to your ai_systems_consultant_hybrid persona. Otherwise, shall we dig deeper into how this affects prompt engineering strategies?
User¶
I still don’t understand the meaning of the word “causal” - is it cause and effect, precedentant and descendant?
What is key and value in kv cache? I know only python dictionary where a key is used to extract the value
Assistant¶
Great questions. These terms borrow from mathematics and database theory, which can make them feel abstract when applied to neural networks. Let’s unpack them using your stack (Python/PostgreSQL/ollama) as a reference point.
1. What does “Causal” actually mean here?¶
You’re right to pause on this. In statistics, “causal” usually means cause-and-effect (A causes B). In LLM architecture, it’s slightly different—it refers to directionality of information flow.
The Mental Model¶
Think of “Causal” in LLMs as “Past influences Future, but Future cannot influence Past.”
Non-Causal (Bidirectional): Like reading a whole book chapter before answering a question about it. You can look ahead and behind. (Example: BERT models).
Causal (Unidirectional): Like writing a story sentence-by-sentence. Once you write a word, you can’t change it based on words you haven’t written yet. (Example: GPT, Llama).
Why “Causal”?¶
It’s called “Causal Language Modeling” because the prediction for token is caused only by tokens . There is no “leakage” from the future.
[Stack-Note] Python & SQL Analogies¶
Python Generators: Think of a
yieldgenerator. You can only get the next value by iterating forward. You can’t ask the generator for the 10th value without consuming the first 9.# Causal-like flow def stream_tokens(): for token in sequence: yield process(token) # Can't see next token yetSQL Window Functions: When you use
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, you are enforcing a causal window. The calculation for the current row cannot see rows that come after it.
[Assumption] Surface Check¶
The blog post you found implied “causal” means the AI “reads” like a human. That’s the metaphor breaking down. It’s not reading; it’s masking. The model sees all tokens during the initial “prefill” phase, but the attention mechanism is mathematically forced to ignore future positions when calculating probabilities for the current position.
2. What are Key and Value in KV Cache?¶
Your intuition about Python dictionaries is actually a very strong starting point. The concept is similar, but instead of strings or integers, we are dealing with vectors (lists of numbers).
The Attention Mechanism (Q, K, V)¶
To understand the cache, we need to briefly touch on Self-Attention. Every token passing through the model generates three vectors:
Query (Q): “What am I looking for right now?” (The current token’s request).
Key (K): “What is my identity?” (A label that helps other tokens find me).
Value (V): “What is my actual content?” (The information I hold).
The Dictionary Analogy¶
| Concept | Python Dict Analogy | LLM Reality |
|---|---|---|
| Key (K) | The key in my_dict[key] | A vector representing the token’s “address” or semantic identity. |
| Value (V) | The value in my_dict[key] | A vector representing the token’s actual meaning/embedding. |
| Query (Q) | The lookup operation my_dict.get(query) | A vector representing what the current token wants to attend to. |
How the Cache Works¶
When ollama generates text:
Step 1 (Prefill): You send a prompt. The model computes K and V vectors for every token in your prompt.
Step 2 (Cache): It stores these K/V vectors in GPU memory (the KV Cache).
Step 3 (Generation): To generate the next token:
It computes a new Query for the current position.
It compares this Query against all stored Keys in the cache to find relevance (Attention Scores).
It retrieves the corresponding Values to build the context.
Crucially: It does not recompute the K/V for the old tokens. It just reads them from the cache.
[Stack-Note] PostgreSQL & Python Context¶
PostgreSQL Indexing: Think of the Key as an index entry (
CREATE INDEX ON table(column)) and the Value as the row data. The KV Cache lets the model skip scanning the whole “table” (recomputing embeddings) every time it needs to look up context.Python Memoization: If you’ve used
functools.lru_cache, this is similar.from functools import lru_cache @lru_cache(maxsize=None) def compute_kv(token_id): return heavy_matrix_computation(token_id) # Next time we need this token's context, we pull from cacheThe KV Cache is essentially a massive, specialized
lru_cachefor vector projections.
🔍 Synthesis: How they work together in ollama¶
Causal Masking ensures that when the model computes attention, it only looks at past Keys (indices ).
KV Cache ensures that those past Keys and Values are already computed and sitting in VRAM, ready to be queried instantly.
Without Causal Masking, the model would cheat during training.
Without KV Cache, the model would be too slow to use (recomputing the entire prompt history for every single new word).
💡 Next Exploration Questions¶
Since we’re mapping this to your local stack:
What if you wanted to inspect the KV Cache size during an
ollamarun? (We could look atnvidia-smiorrocm-smimemory usage vs. context length).How does this impact your PostgreSQL design? (If you’re storing conversation history, do you need to store the raw text or the computed vectors?).
What would a “validation” of your current agent architecture look like regarding these constraints? (If you’re building agents, do you need to account for context window limits imposed by KV Cache memory?).
If you want to rigorously validate how these constraints affect your specific agent design, we can trigger a handoff to your ai_systems_consultant_hybrid persona. Otherwise, shall we dig deeper into how this affects prompt engineering strategies?
[Handoff-Candidate] If you’re ready to evaluate how KV Cache limits impact your agent’s memory architecture, we should switch to the validator persona.
User¶
What is “attention matrix is triangular”?
Assistant¶
This is a linear algebra concept that describes exactly how the causal constraint is enforced mathematically.
When the model calculates attention, it builds a grid (matrix) showing how much every token should “pay attention” to every other token. In a causal model, this grid is lower triangular.
1. The Visual: The Attention Grid¶
Imagine a prompt with 4 tokens: ["The", "cat", "sat", "down"].
The model computes an attention score for every pair.
Row = The token currently being processed (Query).
Column = The token being looked at (Key).
Non-Causal (Bidirectional - e.g., BERT): Every token can see every other token. The matrix is full.
The cat sat down
The [1] [1] [1] [1]
cat [1] [1] [1] [1]
sat [1] [1] [1] [1]
down [1] [1] [1] [1]Causal (Unidirectional - e.g., GPT/Llama):
Token i can only see tokens 0 to i. The upper-right triangle is masked out (set to before softmax, becoming 0 probability).
The cat sat down
The [1] [0] [0] [0] ← "The" sees only itself
cat [1] [1] [0] [0] ← "cat" sees "The", "cat"
sat [1] [1] [1] [0] ← "sat" sees "The", "cat", "sat"
down [1] [1] [1] [1] ← "down" sees all previousNotice the shape of the [1] area? It forms a lower triangle.
2. The Python Implementation (NumPy)¶
Since you work with Python, this is exactly how libraries like PyTorch or Transformers create this mask internally.
import numpy as np
seq_length = 4
# Create a matrix of zeros
mask = np.zeros((seq_length, seq_length))
# Create the causal mask (lower triangular)
# np.tril returns the lower triangle of a matrix
causal_mask = np.tril(np.ones((seq_length, seq_length)))
print(causal_mask)Output:
[[1. 0. 0. 0.]
[1. 1. 0. 0.]
[1. 1. 1. 0.]
[1. 1. 1. 1.]]In the actual model:
Compute raw attention scores ().
Add the mask (where 0 becomes ).
Apply
softmax.The upper triangle becomes exactly 0.0 probability.
3. The SQL Analogy (PostgreSQL)¶
Since you use PostgreSQL, think of this as a self-join with a condition.
Imagine a table tokens with columns id and content.
Non-Causal (Full Attention):
SELECT t1.id, t2.id
FROM tokens t1
CROSS JOIN tokens t2;
-- Every row compares with every other rowCausal (Triangular Attention):
SELECT t1.id, t2.id
FROM tokens t1
JOIN tokens t2 ON t2.id <= t1.id;
-- Row 1 can only join with Row 1
-- Row 2 can join with Row 1, 2
-- Row 3 can join with Row 1, 2, 3The result set of this query forms a triangular shape if you plot t1.id vs t2.id. This is exactly what the attention matrix represents: valid connections between tokens.
4. Why “Triangular” Matters for You¶
Memory Structure: In
ollama, when you see VRAM usage grow linearly with context size, it’s partly because this triangular history (the KV Cache) is being stored.Parallel vs. Sequential:
Prefill (Prompt Processing): The model computes this whole triangular matrix in parallel on the GPU. It knows all tokens in your prompt exist, but the mask ensures token 4 doesn’t “learn” from token 5 during training.
Decode (Generation): The matrix becomes a single row vector (the new token attending to all past cached keys). The triangular logic remains, but computationally it’s just appending to the history.
No “Lookahead”: Because the upper triangle is mathematically zeroed out, there is no gradient path from future tokens to past tokens. The model physically cannot optimize based on information it hasn’t generated yet.
Summary¶
Matrix: A grid of attention scores between all tokens.
Triangular: The upper half is deleted (masked) so tokens can only attend to themselves and predecessors.
Causal: This shape enforces the “cause (past) → effect (future)” directionality.
Does this clarify the geometric meaning behind the term?
User¶
The matrix becomes a single row vector - explain this in more details please, no SQL examples, please, only math and torch/tf
Assistant¶
This transition from a full matrix to a single row vector is the core computational optimization that makes autoregressive generation feasible. It stems directly from the change in input dimensions between the Prefill Phase (processing the prompt) and the Decode Phase (generating tokens one-by-one).
Here is the rigorous breakdown using linear algebra and PyTorch semantics.
1. The Mathematical Formulation¶
The core operation in Self-Attention is:
Where:
(Query): Represents the token asking for information.
(Key): Represents the tokens offering information.
(Value): Represents the actual content of the tokens.
: Dimension of the key vectors.
Phase 1: Prefill (Processing the Prompt)¶
When you first send a prompt of length to the model:
Input: Sequence of tokens.
Shapes:
Attention Scores ():
Shape:
This is the full attention matrix. With causal masking, it is lower-triangular.
Phase 2: Decode (Generating Token )¶
When generating the next token (step ), you are not processing the whole sequence again. You are processing only the new token.
Input: 1 new token (the one just generated or the end-of-prompt token).
Context: All previous tokens are stored in the KV Cache.
Shapes:
(Only 1 query for the new token)
(All past keys retrieved from cache)
(All past values retrieved from cache)
Attention Scores ():
Shape:
This is the “single row vector”.
2. PyTorch Demonstration¶
Here is a concrete implementation showing how the shapes collapse from a matrix to a row vector during inference.
import torch
import torch.nn.functional as F
# Dimensions
d_k = 64 # Key dimension
prompt_len = 5 # N (Prefill length)
current_step = 5 # t (Current context length during decode)
# --- PHASE 1: PREFILL (Matrix) ---
# Simulate processing a prompt of 5 tokens
Q_prefill = torch.randn(prompt_len, d_k)
K_prefill = torch.randn(prompt_len, d_k)
# Attention Scores Shape: (5, 64) @ (64, 5) -> (5, 5)
scores_prefill = Q_prefill @ K_prefill.T
print(f"Prefill Scores Shape: {scores_prefill.shape}")
# Output: torch.Size([5, 5]) -> A Square Matrix
# --- PHASE 2: DECODE (Row Vector) ---
# Simulate generating the 6th token.
# We only compute Q for the NEW token (shape 1 x d_k)
Q_new = torch.randn(1, d_k)
# We retrieve ALL past Keys from the KV Cache (shape 5 x d_k)
# In reality, this grows: step 6 would be 6 x d_k, step 7 would be 7 x d_k
K_cache = torch.randn(current_step, d_k)
# Attention Scores Shape: (1, 64) @ (64, 5) -> (1, 5)
scores_decode = Q_new @ K_cache.T
print(f"Decode Scores Shape: {scores_decode.shape}")
# Output: torch.Size([1, 5]) -> A Single Row Vector
# --- SOFTMAX & OUTPUT ---
# The row vector is normalized to get attention weights
weights = F.softmax(scores_decode / (d_k ** 0.5), dim=-1)
# Shape: (1, 5)
# We multiply weights by Values from cache
V_cache = torch.randn(current_step, d_k)
output = weights @ V_cache
# Shape: (1, 5) @ (5, 64) -> (1, 64)
print(f"Output Shape: {output.shape}")3. Why This Matters Computationally¶
1. Complexity Reduction¶
Prefill: Computing the matrix is . This is why processing a long prompt takes longer than generating a single token.
Decode: Computing the row vector is . Since grows linearly, each generation step is constant time relative to the attention calculation (though memory bandwidth for fetching KV cache becomes the bottleneck).
2. The “Triangular” Mask in Decode¶
In the Prefill phase, you need an explicit causal mask (the triangular matrix) to prevent token from seeing token where .
In the Decode phase, the mask is implicitly handled by the cache:
represents token .
contains only tokens .
There are no future tokens in the cache to attend to.
Therefore, the attention score vector naturally only contains weights for past tokens. You do not need to apply a triangular mask during decode because the “future” simply does not exist in the tensor yet.
3. Memory Bandwidth Bound¶
Because the operation reduces to a matrix-vector multiplication (specifically a vector-matrix multiplication here):
The computational cost is low. The bottleneck becomes loading the and from GPU VRAM into the compute units. This is why LLM inference is often “memory-bound” rather than “compute-bound” during generation.
Summary¶
Prefill: has length . Attention scores are an Matrix.
Decode: has length 1. Attention scores are a Row Vector.
Meaning: This row vector represents the probability distribution of where the new token should “look” in the entire history of previous tokens.