Pronoun Looks Back

Mediumlinear algebraAlso known as Scaled Dot-Product Attention Weights

A translation model reads: "The parcel did not fit in the locker because it was too big." To translate it correctly, the model has to work out which earlier word it is about. In a transformer, each word sends out a query vector ("what am I looking for?") and every word offers a key vector ("what do I contain?").

The model scores each key against the query with a dot product, shrinks the scores by the square root of the vector size so they do not get too extreme, and turns them into weights with softmax. Those weights say how much it pays attention to each word. This is the core step inside every transformer layer.

Your task

Write attentionWeights(query, keys). Let d be the length of query. For each key, compute the score (query · keys[i]) / √d. Return the softmax of the scores: e^scoreᵢ / Σ e^scoreⱼ, one weight per key, in the same order.

Scores can be large, so your answer must not overflow. The weights must add up to 1.

attentionWeights(query: float[], keys: float[][]) → float[] Numbers are accepted within 1e-6.

Examples

Example 1
Input
query = [1, 0, 1, 0]
keys = [[2, 0, 2, 0], [0, 1, 0, 1], [1, 1, 1, 1]]
Output
[0.6652409557748218, 0.09003057317038046, 0.24472847105479764]
Why

d = 4, so divide by √4 = 2. Dot products: 4, 0, 2. Scores: 2, 0, 1. Softmax: e² ≈ 7.389, e⁰ = 1, e¹ ≈ 2.718, total 11.107, so the weights are about 0.665, 0.090 and 0.245. The first key matches the query best and gets most of the attention.

Example 2
Input
query = [0, 0]
keys = [[3, 1], [-2, 5]]
Output
[0.5, 0.5]
Why

A zero query has dot product 0 with every key. All scores are 0, so attention is spread evenly: 0.5 and 0.5.

Example 3
Input
query = [30, 40]
keys = [[30, 40], [40, 30]]
Output
[1, 1.9531818567219233e-31]
Why

Dot products are 2500 and 2400, scores 2500 / √2 ≈ 1767.8 and 2400 / √2 ≈ 1697.1. e^1767.8 overflows, so shift by the largest score first. The gap is about 70.7, so the second weight is e^-70.7, tiny but not zero, and the first is almost exactly 1.

Constraints

  • 1 ≤ keys.length ≤ 1,000
  • 1 ≤ d = query.length = keys[i].length ≤ 256
  • -1,000 ≤ query[j], keys[i][j] ≤ 1,000
  • Answers within 0.000001 of the expected values are accepted.
Start with brute force

There is no faster way for one query: you must look at every number of every key once, which is O(n · d). Real models do this for every word at once as a matrix multiplication, so a sentence of n words costs O(n² · d). That quadratic cost is why long inputs are expensive.

The mistakes to avoid are leaving out the √d scaling, dividing by d instead of √d, and exponentiating raw scores, which overflows for large vectors.

Hints

0 of 4 shown

Walkthrough and solution

Unlocks when you pass every test. Stuck for a while? Looking is fine too, as long as you then close it and write the code yourself.

Ctrl+Enter runs

Run your code to see results here. Run checks the examples. Submit checks every test, including hidden edge cases.

Write code