We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Attention Variants step 2 of 14
Causal Attention Mask
Causal attention lets each sequence position use its own value and earlier values, while excluding later positions. This is the masking rule used for autoregressive self-attention. This task computes the full attention output, not just the mask.
Read the score matrix by row
For each sequence in the batch, first calculate
scores = q @ transpose_last_two_axes(k) / sqrt(d). Its shape is (T, T):
row i is the query position, and column j is the key position.
Keep a pair when j <= i; mask it when j > i.
For three positions, the allowed pairs are:
key 0 key 1 key 2
query 0 yes no no
query 1 yes yes no
query 2 yes yes yes
The diagonal stays allowed. With only one position, the output therefore equals its value vector. If all allowed scores are equal, each query returns the mean of the value vectors in its allowed prefix.
Mask before softmax
Replace future-position scores with -1e9, then apply softmax over the last
axis and multiply by v. Merely setting those scores to zero would still
give them positive softmax weights. Masking after softmax without renormalizing
would leave the retained weights summing to less than one.
This exercise uses float32 scores and moderate input magnitudes, for which
-1e9 makes masked weights underflow to zero. The sentinel is not universal:
it lies outside float16's finite range and must be sufficiently below the
allowed scores. An ideal additive mask uses negative infinity, provided each
row has at least one finite allowed score. An entirely masked row needs
separate handling; ordinary softmax does not define a useful distribution there.
Your task
Implement causal_attention(q, k, v) for three float32 tensors of the same
shape (N, T, d), with positive dimensions. Use batched matrix products,
a lower-triangular mask including the diagonal, and a stable row-wise softmax.
For this exercise, use the finite sentinel -1e9. Do not call a built-in
attention function or add padding masks, dropout or projection layers.
In PyTorch, transpose the last two axes of k, not the batch axis, and
create the mask on the input device. In JAX, use jnp.swapaxes(k, -2, -1).
One (T, T) mask broadcasts across all N sequences.
Output
Return the attended values with shape (N, T, d). Leave the inputs unchanged.
For example, zero queries and keys with values [2, 4, 9] in a single
width-one sequence produce [2, 3, 5], the successive prefix means.
The grid below shows allowed pairs rather than softmax weights. Select Causal to see this exercise's lower-triangular pattern.
Loading visualization…
Understand the ideas behind this exercise. The Problem tab contains the exact implementation requirements.
Attention combines values using learned weights
Attention lets each output position combine information from source positions. It uses three sets of vectors:
- A query for each output position.
- A key for each source position, compared with the query to get a score.
- A value for each source position, containing the information to combine.
Scaled dot-product attention has three steps: compute query-key scores, turn those scores into weights with softmax, then take a weighted sum of the values.
$$A=\operatorname{softmax}\left(\frac{QK^T}{\sqrt{d_k}}+M\right),\qquad O=AV.$$
Here, d_k is the width of each query and key vector. The mask M is zero for
allowed positions and negative infinity for blocked positions.
For a single head, Q has shape (query_length, key_width), K has shape
(source_length, key_width), and V has shape (source_length, value_width).
The weights A have shape (query_length, source_length). Softmax runs over
source positions, so each query gets its own set of weights that sum to one.
The output has shape (query_length, value_width).
For weights [0.25, 0.75] and scalar values [2, 6], the output is
0.25 * 2 + 0.75 * 6 = 5. Both values contribute; attention does not simply
choose the value with the largest score.
Mask before softmax
A causal mask blocks future positions. A padding mask blocks positions added to
make sequences the same length. Setting a blocked score to negative infinity
makes its exponential zero, so it receives zero weight. Setting it to zero does
not work: exp(0) = 1.
Each row needs at least one allowed, finite score. Applying ordinary softmax to an all-negative-infinity row produces undefined values. If you instead zero weights after softmax, the remaining weights no longer sum to one unless you normalise them again.
Why divide by the square root of the key width?
If query and key components are independent, with mean zero and variance one,
the variance of their dot product is d_k. Dividing by sqrt(d_k) brings it
back to one. This helps keep softmax from concentrating nearly all the weight on
one source position, where its derivatives become small. Scaling helps under
these assumptions; it does not prevent extreme weights for every possible input.
In self-attention, queries, keys, and values come from the same sequence. In cross-attention, queries come from one sequence and keys and values come from another.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
PyTorch reference solution
Sign in to track your attempts toward unlocking the reference solution.