Scaled dot-product attention with a mask
Lesson: Self-Attention from ScratchCheat sheetTrack: Transformers from scratch
Problem
Attention lets each query token collect information from a set of key and value tokens. Every transformer layer in the course uses this operation. A decoder adds a causal mask, so that a token cannot see the tokens that come after it.
Task
Write two functions.
causal_mask(T)returns the causal mask for a sequence ofTtokens.scaled_dot_product_attention(Q, K, V, mask=None)returns the attention output and the attention weights.
The scores are S = Q K^T / \sqrt{d_k}, where d_k is the size of the last axis of Q. The weights are the softmax of each row of S, over the key axis. The output is the weights times V.
The mask is a boolean array. True means blocked. A blocked query and key pair gets a weight of exactly 0. The other weights in the row are a softmax over the allowed keys only, so each row of weights sums to 1.
The causal mask has True at row i and column j when j > i. Token i can see token i and every token before it.
Input and output
| Name | Type | Shape | Meaning |
|---|---|---|---|
T | int | the number of tokens | |
Q | float array | (..., Tq, d_k) | the queries, with zero or more leading batch axes |
K | float array | (..., Tk, d_k) | the keys, with the same leading axes as Q |
V | float array | (..., Tk, d_v) | the values, with the same leading axes as Q |
mask | bool array or None | broadcasts to (..., Tq, Tk) | True blocks a query and key pair |
causal_maskreturns a bool array of shape(T, T).scaled_dot_product_attentionreturns a tuple(output, weights).outputis a float array of shape(..., Tq, d_v).weightsis a float array of shape(..., Tq, Tk).
Constraints
1 <= T, Tq, Tk <= 64and1 <= d_k, d_v <= 64.- Each row of the mask has at least one allowed key.
- Scores can be larger than 1000. The output must stay finite.
- Transpose only the last two axes of
K. The leading axes are batch axes. - Do not change the input arrays.
- Use numpy only.
Examples
pythonQ = np.array([[1.0, 0.0], [0.0, 1.0]])
K = np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
V = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
output, weights = scaled_dot_product_attention(Q, K, V)textoutput
[[3. 4. ]
[3.40667256 4.40667256]]
weights
[[0.40111209 0.19777581 0.40111209]
[0.19777581 0.40111209 0.40111209]]pythoncausal_mask(3)text[[False True True]
[False False True]
[False False False]]pythonX = np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
output, weights = scaled_dot_product_attention(X, X, V, mask=causal_mask(3))textoutput
[[1. 2. ]
[2.3395231 3.3395231 ]
[3.51046953 4.51046953]]
weights
[[1. 0. 0. ]
[0.33023845 0.66976155 0. ]
[0.24825508 0.24825508 0.50348984]]Hints
4 hintsSolution and notes
LockedPass every check with Submit to open the reference solution and the notes.
Run it on your computer
Clone the repository, install numpy and run the same checks in a terminal:
python3 scripts/practice/run.py scaled-dot-product-attention my_answer.py
python3 scripts/practice/run.py scaled-dot-product-attention my_answer.py --examplesStart my_answer.py from starter.py.
Your code is saved in this browser as you type. On a phone you can read and edit the code, but the checks are easier to use on a desktop.
Keyboard shortcuts
- Ctrl Enter
- Run the examples
- Ctrl Shift Enter
- Submit
- Tab / Shift Tab
- Indent or outdent the lines
- Esc then Tab
- Move the focus out of the editor
- Alt H
- Show the next hint
On a Mac, use Cmd in place of Ctrl.
Select Run to check the examples, or Submit to run every check.