The Interview Edge Blog
← Back to all guides
LLM internals · Core

Attention Mechanism Explained: QKV, Softmax & the √dₖ Rule

Queries, keys, values, and the softmax that binds them: the attention mechanism worked by hand with numbers before a single line of PyTorch.

Listen to this guideNarrated audio · ~11 min

Explain it like I’m five

Imagine every word in a sentence can point to the other words that help it make sense. In “The cat sat,” the word “sat” points strongly to “cat” because it tells us who did the sitting.

Attention gives every word a question, lets the other words offer clues, and turns the best matches up louder. The model mixes those clues into a better understanding. It repeats this with several sets of pointers so it can notice different things at once.

Intuition: a relevance-weighted lookup

Your Transformer trains for two days, then the loss flatlines and the gradients vanish. The model isn’t too small — your attention scores blew up, the softmax went razor-sharp, and learning quietly died. Dividing by √dₖ is the fix: it keeps the dot products in a range the softmax can actually learn from. In interviews, nobody asks ‘what is attention?’ They ask ‘why divide by √dₖ, and what breaks without it?’ By the end of this guide you’ll derive the answer on a whiteboard without flinching.

For the token “sat” in “The cat sat,” the useful context may be “cat.” Attention gives “sat” a query, gives every token a key and a value, compares the query with all keys, and makes a weighted mixture of values.

Queries and keys decide where to look. Values decide what to take. The projection matrices are learned during training, so different heads can specialize in different relationships.

Attention flow from token embeddings to queries keys values and weighted outputToken vectors XThe cat satQ = XWqK = XWkV = XWvsoftmax(QKᵀ / √d)attention weightsweights × Vcontext vectorFor “sat”, a possible attention row.16.60 cat.24

A query asks “what matters to me?” Keys advertise what each token contains. Values carry the information that gets mixed.

One sentence worth memorizing

Scaled dot-product attention is softmax(QKᵀ / √dₖ)V: score relevance, normalize scores, then mix information.

Why every symbol exists

Linear projections

From token states X, learned matrices create Q = XWq, K = XWk, and V = XWv. Separate projections let the same token expose different features for matching and content.

Dot products

QKᵀ produces one score for every query–key pair. Large positive values mean alignment; negative values mean mismatch.

Scaling and softmax

When vector dimension grows, raw dot products tend to grow too. Dividing by √dₖ keeps logits in a range where softmax has useful gradients. Softmax turns each row into non-negative weights summing to one.

Masks and multiple heads

A causal mask sets future-token logits to negative infinity before softmax. Multi-head attention repeats the operation in smaller subspaces, concatenates the results, and applies an output projection.

Google Research: translation without recurrence

The original Transformer work applied attention to machine translation, showing that a model could connect source and target tokens without recurrent or convolutional layers.

In translation, the right source word for the next output is not always nearby. A decoder may need to connect a pronoun to a noun many positions earlier, preserve a name exactly, or reorder a phrase to fit the target language. Attention lets each output position build a different relevance-weighted view of the source.

01 · Represent

Source tokens become contextual vectors. Multiple attention heads can learn different relationships—syntax, phrase boundaries, entities, and long-range references.

02 · Align

For each output position, queries score source keys. Softmax turns those scores into weights, and the weighted values carry the relevant source information into the translation.

03 · Generate

The decoder combines that source context with the target tokens already produced. The alignment can change at every step rather than following a fixed word-for-word map.

On the WMT 2014 English-to-German task, the paper reported 28.4 BLEU, more than 2 BLEU above the previous best results cited by the authors. This is the foundational research demonstration, not a claim about a specific consumer translation deployment.

Source: “Attention Is All You Need,” Vaswani et al. ↗
Why the example transfers

Code completion can connect a call site to an earlier function; summarization can pull facts from distant paragraphs. The learned patterns differ, but the operation is still relevance-weighted lookup.

Work one attention row by hand

Let a single query be q = [1, 0]. Three keys are k₁=[1,0], k₂=[0,1], k₃=[1,1]. With dₖ=2:

ScoresDot products are [1, 0, 1]. Divide by √2 ≈ 1.414 → [0.707, 0, 0.707].
WeightsSoftmax gives approximately [0.401, 0.198, 0.401].
OutputIf values are [2,0], [0,2], [1,1], the weighted sum is [1.203, 0.797].

The output is not a selected token. It is a differentiable blend, weighted by relevance.

The complete core in eight lines

PyTorch · scaled dot-product attention
import math
import torch

def attention(x, W_q, W_k, W_v, mask=None):
    Q, K, V = x @ W_q, x @ W_k, x @ W_v
    logits = (Q @ K.transpose(-2, -1)) / math.sqrt(Q.size(-1))
    if mask is not None:
        logits = logits.masked_fill(mask == 0, float("-inf"))
    weights = torch.softmax(logits, dim=-1)
    return weights @ V, weights

# x: [batch, tokens, d_model]
# output: [batch, tokens, d_value]

Real implementations fuse operations, split heads, apply dropout, add residual connections, and use optimized kernels such as FlashAttention. The equation stays the same; memory movement changes.

What interviewers actually ask

Use these as reasoning drills rather than memorized answers.

OpenAI
Why divide by √dₖ, and what breaks without it?

What to say (≈90 sec): “Start from the dot product q·k. If each query and key component has mean zero and variance one, the dot product is a sum of dₖ independent terms, so its variance is dₖ and its standard deviation is √dₖ. With a typical head dimension like 64, the raw scores swing over roughly ±24 — three standard deviations — and the softmax turns razor-sharp: nearly all the weight lands on one token. That saturation is the killer, because the softmax gradient term pᵢ(1 − pᵢ) collapses to zero, gradients vanish, and training flatlines even though the loss looks like it just converged. Dividing by √dₖ restores unit variance, keeping the scores in the range where the softmax is sensitive and gradients can flow.”

Likely follow-up: “Why √dₖ specifically — why not divide by dₖ?” → √dₖ isn’t a magic constant; it’s the standard deviation of the dot product, so dividing by it normalizes the variance to exactly 1. Dividing by dₖ would over-shrink the scores to variance 1/dₖ, flattening attention toward uniform weights and killing the model’s ability to focus. You want the widest spread the softmax can still learn from — and that’s unit variance.

The answer that sinks you: “It keeps the numbers small so the softmax doesn’t overflow.” Softmax implementations already subtract the max for numerical stability, so overflow was never the problem. What actually breaks is learning: saturation drives the gradients to zero. The answer that shows depth is gradient quality, not numerical hygiene.

Anthropic
What does a causal mask guarantee—and what does it not?

What to say (≈90 sec): “The causal mask — a lower-triangular matrix with −∞ above the diagonal — guarantees exactly one thing: position i’s representation is computed only from positions 1 through i, so during teacher-forced training the model never directly reads future tokens. That’s what makes the autoregressive objective well-defined and keeps generation consistent with training. What it does not guarantee is everything people assume: not factuality, not safety, and not even the absence of leakage. Leakage can still happen indirectly — duplicated or overlapping training windows let a token in one window attend to text that appears later in another window of the same document, and if any upstream feature like a bidirectional embedding is concatenated into the input, future information walks in through the front door. The mask constrains positions within one sequence; it can’t fix a data pipeline that feeds the future into the past.”

Likely follow-up: “How would you detect that kind of indirect leakage?” → I’d look for suspiciously low loss on tokens that should be hard to predict — exact-duplicate spans across windows scoring near-zero perplexity — and audit the tokenization and windowing code for overlap. At eval time, I’d cut prefixes right before facts the model “shouldn’t” know and check whether it still completes them verbatim.

The answer that sinks you: “The causal mask guarantees the model can’t leak future information.” That confuses within-sequence position constraints with data hygiene. The mask only controls what position i can attend to in a given sequence — if the pipeline leaks future text into the input itself, the mask happily lets the model read it.

Meta
Compare multi-head attention with one wide head.

What to say (≈90 sec): “One wide head of dimension d does a single dot-product match in one big subspace — one similarity metric over the whole embedding. Multi-head attention splits the model dimension into h heads of dimension dₖ = d/h, each with its own learned Q, K, V projections, then concatenates their outputs and mixes them through W^O. Each head can specialize in a different matching subspace — the classic finding is heads tracking syntax in some heads and coreference or positional patterns in others. The parameter count is identical: h heads of size d/h cost the same as one head of size d in the projections, so the representational diversity is effectively free in FLOPs terms, and heads parallelize cleanly across the sequence. The honest caveats: heads are often redundant and many can be pruned after training with little loss, and the smaller per-head dimension changes per-head sharpness — which is exactly why the √dₖ scale matters more as heads multiply.”

Likely follow-up: “If heads are redundant, why not just train fewer, wider heads?” → Because redundancy is a trained outcome, not a starting guarantee — over-parameterized heads give optimization multiple paths during training, and you compress afterward by pruning or merging the heads that ablation shows are load-bearing versus dead weight. Some heads do turn out to be critical, like induction heads for in-context copying, so you prune selectively against a validation set instead of assuming which heads matter.

The answer that sinks you: “More heads means more parameters, so it’s strictly more expressive.” The projection parameter count is the same either way — the gain comes from diverse matching subspaces, not more weights. And since research shows many heads are prunable after training, “more heads” can mean more redundancy, not more capacity.

Google
Derive the time and memory complexity in sequence length.

What to say (≈90 sec): “Walk the forward pass. The Q, K, V projections are each O(nd²) — linear in n, so not the binding term in sequence length. QKᵀ builds the n×n score matrix: n² dot products, each over dₖ dimensions, giving O(n²dₖ). The softmax is O(n²) elementwise, and multiplying the scores by V is another O(n²dᵥ). The dominant term is O(n²d) compute. Memory is the tighter constraint: materializing the n×n score matrix costs O(n²) floats — at n = 32k that’s about a billion entries per head, roughly 4 GB at fp32 before you’ve stored anything else — plus the O(nd) KV cache that grows with every generated token. Tiled kernels avoid materializing the full matrix but don’t change the O(n²d) compute; they attack the memory constant.”

Likely follow-up: “At what sequence length does the n² term actually dominate?” → Compare O(n²d) against the O(nd²) projections: the attention term wins when n is comparable to or larger than d. With a model width around 4096, that crossover sits in the low thousands of tokens — below it the projections and the MLP dominate, above it the attention compute and score-matrix memory take over, which is why the memory wall shows up at tens of thousands of tokens.

The answer that sinks you: “It’s O(n²d) because the softmax is quadratic.” The softmax is O(n²) but cheap per element — the dominant cost is the QKᵀ matmul and the scores-times-V matmul, each carrying the factor of d. Naming the n×n matrix and the per-element dimension is the whole derivation.

Amazon
How would you serve much longer contexts under a latency SLO?

What to say (≈90 sec): “I’d split it into three levers and order them by measurement, not fashion. First, shrink attention cost per token: grouped-query attention to cut the KV cache, sliding-window or local attention to bound the effective sequence length, and fused kernels so the n×n score matrix never materializes. Second, cache and skip work: prompt caching for repeated prefixes, and KV-cache eviction policies for streaming sessions. Third, avoid feeding the whole context at all: chunk and retrieve, so attention only ever sees the top-k relevant chunks. The ordering comes from profiling — prefill-bound, where a huge prompt sets time-to-first-token, points at sparse attention or retrieval; decode-bound, where per-token time is the problem, points at GQA plus cache quantization. And every lever gets a quality guardrail, like a needle-in-a-haystack retrieval check, so I know the SLO win didn’t silently cost accuracy.”

Likely follow-up: “You only get to pick one lever. Which?” → It depends on which phase sets the p99: for a chatbot with long pasted documents, retrieval-based chunking wins because most of the context was never needed in the first place; for long-form generation, GQA plus KV-cache quantization attacks the decode bottleneck that actually drives per-token latency. So I’d profile time-to-first-token versus per-token time first, then spend my one lever on the binding constraint — never optimize blind.

The answer that sinks you: “I’d switch to a model with a longer context window.” A model that supports a million tokens doesn’t make the O(n²) cost disappear — latency SLOs are about wall-clock, and serving cost still scales with context. Longer support without a caching, eviction, or retrieval strategy just blows the same budget on a bigger model.

Apple
How would you reduce attention memory for an on-device model?

What to say (≈90 sec): “On-device, memory bandwidth is the bottleneck, so I’d attack peak memory in three places. First, the KV cache, which dominates at long contexts: multi-query or grouped-query attention to shrink the number of key/value heads, quantize the cache to int8 or int4, and cap the maximum context. Second, the attention computation itself: sliding-window attention to bound the effective sequence length, plus a fused kernel that never materializes the n×n score matrix. Third, the weights: quantize the Q, K, V, O projections. The key discipline is measuring peak memory at the target context length, not just parameter count — at long contexts the cache and activations dwarf the weights, so the attention structure is where the real savings are.”

Likely follow-up: “What breaks if you quantize the KV cache too aggressively?” → Quality degrades unevenly: outliers in the keys matter far more than outliers in the values, so naive low-bit quantization of keys hurts disproportionately. The fix is per-channel scaling or keeping the small set of outlier channels in higher precision, and validating against a real quality eval rather than just perplexity.

The answer that sinks you: “Halve the model size and the memory problem is solved.” Halving the weights only shrinks the weight footprint. At long contexts, peak memory is set by the KV cache and the score matrix, so shrinking parameters without changing the attention structure — MQA or GQA, windowing, cache quantization — leaves the real bottleneck untouched.

Key takeaways

  1. Q asks, K matches, V contributes.
  2. Softmax makes each query’s weights comparable and sum to one.
  3. The √dₖ scale protects gradient quality.
  4. Causal masking prevents a decoder from looking ahead.
  5. Quadratic sequence cost motivates optimized kernels and sparse variants.
Read nextRLHF →