What specifically goes wrong if you remove the √d_k scaling from attention?
Without √d_k, dot product magnitudes grow with d_k, softmax saturates onto one position, the softmax Jacobian collapses off the peak, and gradients can't redistribute attention. Training stalls.
Imagine a class voting on which movie to watch. If everyone scores movies between 1 and 5, the vote is balanced and every movie has a real shot. But suppose the scores get amplified to 100 to 500. Now the top movie wins with essentially 100% of the weight and the rest get zero. Worse, if you wanted the class to change its mind, nudging a near-zero option a little still leaves it near zero: there's no mechanism for the runners-up to gain ground. The √d_k division is the rule that keeps scores in the 1-to-5 range no matter how big the input vectors get, so the vote stays competitive and the model can keep learning.
Detailed answer & concept explanation~6 min readEverything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
Derive Var(Q · K) = d_k from independence, show why standard deviation scales as √d_k, walk the softmax Jacobian to show off-peak gradients vanish under saturation, and rule out alternative normalizations (d_k, log d_k) as over- or under-correcting.
# Demonstrating softmax saturation when scaling is omitted
import torch
import torch.nn.functional as F
d_k = 128
q = torch.randn(1, d_k)
k = torch.randn(64, d_k) # 64 candidate positions
scores_unscaled = q @ k.T # std ~= sqrt(d_k) ~= 11
scores_scaled = scores_unscaled / (d_k ** 0.5)
attn_unscaled = F.softmax(scores_unscaled, dim=-1).squeeze()
attn_scaled = F.softmax(scores_scaled, dim=-1).squeeze()
print(f'unscaled max prob: {attn_unscaled.max().item():.4f}')
print(f'scaled max prob: {attn_scaled.max().item():.4f}')
# unscaled max ~= 0.99 (saturated); scaled max ~= 0.05 (soft)Real products, models, and research that use this idea.
- Vaswani et al. 2017 footnote 4 explicitly motivates the scaling: 'We suspect that for large values of d_k, the dot products grow large in magnitude, pushing the softmax function into regions where it has extremely small gradients.'
- Modern decoder LLMs like Llama 4 Maverick and Gemma 4 use d_head = 64 to 128 per head, exactly the regime where unscaled attention would saturate immediately.
- PyTorch's torch.nn.functional.scaled_dot_product_attention applies the √d_k scaling internally; users almost never implement it by hand anymore.
- Some 2024-2025 architectures use QK-Norm (layer-norm applied to Q and K before the dot product) as a complementary variance-control mechanism for very large head dimensions.
What an interviewer would ask next. Try answering before peeking at the approach.
QDoes removing √d_k still hurt at inference, where there's no gradient to compute?
QWhy √d_k specifically and not d_k or log(d_k)?
Don't say thisRed flags and common mistakes that signal junior thinking. Click to expand.
Red flags and common mistakes that signal junior thinking. Click to expand.
Choosing the FP overflow answer. Numerical overflow can happen for extreme values, but the primary failure is the gradient signal, and training will stall well before any FP16 NaN appears.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.
Same topic, related formats. Practice these next.