The mechanism that lets each token look at every other token and decide how much each contributes.
x (token embeddings, shape [L, d])
W_Q W_K W_V
\ | /
x -------> Q K V (each [L, d_k])
scores = Q . K^T / sqrt(d_k) (shape [L, L])
weights = softmax(scores) (rows sum to 1)
output = weights @ V (shape [L, d_v])
For each token, take its Query vector and dot-product with every other token’s Key to get a similarity score. Normalise, then take a weighted sum of all Values.
sqrt(d_k)?Inner products grow with d_k, pushing softmax into saturated regions where gradients vanish. Dividing by sqrt(d_k) normalises the variance back to ~1.
import torch, torch.nn.functional as F
def self_attention(Q, K, V):
d_k = Q.size(-1)
scores = (Q @ K.transpose(-2, -1)) / (d_k ** 0.5)
weights = F.softmax(scores, dim=-1)
return weights @ V
In a meeting room, every speaker writes down how relevant everyone else’s comment is to their own, then drafts their reply as a weighted mix.
Interview tip: Memorise
softmax(QK^T / sqrt(d_k)) V. Half of all “explain transformers” questions end here.