Why we divide by √dk.
A short detour through variance, before we earn the right to softmax.
The dot product of two random vectors is, on average, larger when the vectors are larger. That sentence is obvious and also important. If our queries and keys live in ℝ⁵¹², then on average q · k grows with the dimension — and softmax, reading those numbers, will collapse into a one-hot distribution before the network has had time to learn anything.
The fix is small and clarifying. We assume each component of q and k is roughly unit-variance. Then their inner product has variance dk, and dividing by √dk restores unit variance — softmax operates in a regime where it can still see gradients.
Intuition first.
Think of it geometrically. As we add dimensions, two random unit-vectors become, in expectation, nearly orthogonal. Their dot product should be near zero — but its variance grows with dk because we're summing dk independent random variables. We're not making the mean bigger; we're making the spread bigger.
Dividing by √dk is the classic trick for normalising the variance of a sum of independent unit-variance terms. Nothing more. It's the same reason you divide by √n when computing a sample mean.
A quick variance sketch.
Let qi, ki ~ N(0,1) independently. Then q·k = Σ qiki. Each term has mean 0 and variance 1, so Var(q·k) = dk. Scaling by 1/√dk gives variance 1. Softmax now operates over values that don't explode with model width.
What this changes (and what it doesn't).
It changes gradient flow early in training. Without scaling, softmax saturates — gradients vanish, and the attention heads learn slowly. With scaling, the attention distribution stays diffuse enough to update.
It doesn't change the expressiveness of the model. The scaled and unscaled versions can represent the same functions — scaling just makes optimisation tractable.
It's not magic. It's variance.