Single-Head Attention
The one operation at the heart of every Transformer: scaled dot-product attention. Given queries, keys, and values, let each query pull a weighted blend of the values — weighted by how much its query matches each key. No numpy, just the math.
The problem
Implement scaled dot-product attention. You are given three matrices as lists of rows: Q (queries), K (keys) — each row length d — and V (values). For each query row, compute a score against every key as their dot product divided by √d, softmax those scores into weights that sum to 1, then return the weighted sum of the value rows. Return a matrix with one row per query and one column per value dimension: softmax(Q·Kᵀ / √d) · V.
Q=[[1]], K=[[1]], V=[[5, 6]][[5.0, 6.0]]Q=[[1,5]], K=[[0,0],[0,0]], V=[[1,2],[3,4]][[2.0, 3.0]]Q=[[10,0]], K=[[1,0],[0,1]], V=[[1,1],[2,2]]≈[[1.0, 1.0]]- Scale the scores by
1 / √d, whered = len(Q[0])is the key/query dimension. - Use a numerically stable softmax (subtract the row max before exp).
- Each output row is a convex combination of the value rows — the attention weights sum to 1.
- Pyodide has no numpy — use the
mathstdlib and plain lists only.
Your turn — write it
Edit the stub, hit Run (or ⌘/Ctrl + Enter), and watch the hidden tests. Stuck? the hints are right above and Reveal solution is one click away.
Implement attention(Q, K, V). For each query: score it against every key (dot product ÷ √d), softmax the scores, and return the weighted sum of the value rows.
- For query row q and key row k, the raw score is their dot product: sum(q[t] * k[t]).
- Divide every score by √d with d = len(Q[0]) — that is the "scaled" in scaled dot-product attention.
- Softmax each query row of scores into weights (subtract the max first for stability).
- Output row = Σ_j weight[j] * V[j] — a weighted blend of the value vectors, one entry per value column.
Explore the topic
See this challenge alongside everything else on the same subject — handbooks, system designs, algorithms and tools, in one place.