G14: Attention, embeddings, and transformer reasoning
On this page
- Prerequisites and observable goals
- The central question: which values should this position combine?
- A numerical example before the general architecture
- Mask before normalization
- Differentiate the mixing operation
- What a transformer adds, and what it does not establish
- Count computation and memory before claiming acceleration
- Independent practice
- Smallest useful hints
- Complete solutions: G14
- Readiness and continuation
Prerequisites and observable goals#
G05 matrix products; G12 probability and stable loss; G13 chain-rule sensitivities.
Compute and differentiate a small attention operation, enforce causal masking, account for memory costs, and distinguish a forward attention lab from a trained language model.
The central question: which values should this position combine?#
Attention constructs a data-dependent weighted combination of value vectors. It does not look up a guaranteed fact or produce a proof that a token is important. The original transformer paper defines scaled dot-product attention and multi-head composition. This chapter develops the operation on small original examples; the lab implements forward attention, not a pretrained model or a complete transformer training system. [G-ATTENTION]
Let contain query rows, key rows, and value rows. The number of queries need not equal the number of keys . Key and value row counts must agree because every score column weights the corresponding value row. Compute
contains scores, contains nonnegative row-normalized weights, and contains output vectors. A row of need not sum to one. Its coordinates are values, not probabilities. The square root is a scale on dot-product scores, not a normalization of the final output.
A numerical example before the general architecture#
Take one query and two keys with , , , and . The scores are , so softmax gives . The output is . The computation did not choose one value; it mixed both.
If scores are , exponentials are , weights are , and the same values produce . Do not substitute the unnormalized scores directly into the weighted sum. Adding any constant to all scores in one row leaves softmax unchanged because the common exponential factor cancels. Subtract the largest allowed score before exponentiating to reduce overflow:
Here is the set of allowed columns. Set disallowed weights to zero. If is empty, there is no probability distribution to normalize; the reference rejects the row rather than silently returning NaNs or arbitrary zeros.
The scale has an intuitive variance motivation: under independent zero-mean unit-variance query/key coordinates with suitable independence between vectors, the dot product has variance . Dividing by keeps that variance near one. Learned vectors need not satisfy those assumptions; this is a motivating model, not a theorem that all network scores have unit variance.
Mask before normalization#
In causal self-attention, query position may use key positions , not future positions. For two zero-score positions with scalar values , the masked weight matrix and output are
The first position cannot see value 8. If you instead normalize the first row over both columns and then zero the future entry, the row becomes and outputs 1. The remaining weights no longer sum to one. Renormalization after masking could repair this simple case, but masking before softmax expresses the intended contract directly and avoids accidentally allowing future scores to influence the denominator.
The diagram uses zero-based positions increasing downward and rightward. A useful test changes only a future value or key and checks that an earlier causal output is unchanged. Such tests check noninterference for selected inputs; the indexing argument explains why the implementation should preserve it generally.
Differentiate the mixing operation#
For a single softmax row, direct differentiation of the quotient gives
The Jacobian is . You rarely need to construct that full matrix. Given upstream sensitivities ,
For and , the weighted mean is , so the score gradient is . Its sum is zero. This matches softmax invariance to a common score shift: that direction should have zero first-order effect.
For matrices, let be the output sensitivity with the same shape as . Then
Apply the row formula to to obtain . For the unmasked score product,
Masked score coordinates have zero sensitivity under a fixed mask. Check dimensions: is -by-, so is -by-. A transposition error may survive square test cases; use unequal query/key counts and unequal in tests. These derivative formulas are taught here; the supplied lab intentionally implements only the forward pass. A backward implementation is an unassigned continuation, not an implied completed feature.
What a transformer adds, and what it does not establish#
An embedding table maps an integer token ID to a learned vector. The lookup is not an ordinal statement that token 200 is twice token 100. Positional information distinguishes sequence order; self-attention without position information has permutation symmetries that may be inappropriate for a sequence task. Learned projections construct queries, keys, and values from hidden states.
Multiple heads use separate projections, perform several attention operations, concatenate outputs, and project again. Residual paths add compatible-shaped representations; normalization and a position-wise feed-forward subnetwork provide further transformations. A decoder additionally enforces causal information flow. Exact normalization placement, positional schemes, and other details vary by architecture; the original paper is one specific design, not a specification of every current model.
Next-token training compares a model’s distribution at one position with a target token using a classification loss. Training on many such examples does not turn a high-probability continuation into established truth. Evaluation needs held-out data, contamination controls, clear task definitions, and failure analysis. Attention weights alone are not a causal explanation of the final answer: value vectors and later transformations can alter the consequence of a weight, and alternative parameterizations may change apparent importance.
Count computation and memory before claiming acceleration#
A straightforward self-attention score array for batch , heads , and sequence length has entries. At with four-byte entries, that is bytes, or 64 MiB. Doubling makes this one array 256 MiB. This excludes queries, keys, values, gradients, activations, optimizer state, and temporary buffers. Efficient attention algorithms can avoid materializing some full intermediates; the quadratic storage statement describes the straightforward implementation, not an unavoidable lower bound for every exact implementation.
The dot products and weighted sums require work on the order of per head in the direct method. During autoregressive inference, a key/value cache reuses earlier projected states rather than recomputing them for every new token. It trades memory for work; it does not remove the need for the new query to interact with the allowed history under ordinary dense attention. This CPU lab is for correctness and dimensions, not GPU throughput or an inference benchmark.
Independent practice#
F foundation C standard course work T transfer S optional stretch L runnable lab or project. Attempt the complete set before opening its solutions.
For of shape , of shape , and of shape , give the shapes of scores, weights, and outputs. Explain why replacing by a array violates this contract.
Compute the attention output for zero scores and values , then for scores with the same values. Explain why the output is not itself a probability distribution.
For two zero-score positions and values , derive the causal outputs. Identify the wrong first output obtained by applying softmax over both positions and only then zeroing the future weight.
With and upstream , derive the gradient with respect to the two scores. Verify that the entries sum to zero and interpret that identity.
Calculate the bytes and MiB for one float32 score array at , then at . State three excluded memory costs and why this is not a measured GPU-memory requirement for every implementation.
Implement softmax and forward attention in starters/attention.py. Enforce shapes, finite inputs, a fixed causal mask, and rejection of entirely masked rows. Test row sums, unequal query/key counts, extreme scores, and invariance of earlier causal outputs to a changed future value.
Smallest useful hints#
For .01, write the inner dimensions of every matrix product. For .03, the first causal row has one allowed element, so its allowed weight must be one. For .05, MiB uses bytes, not .
Complete solutions: G14#
Compare the first step where your work differs. A corrected answer is not yet an independent success.
G14.01Show solution
Shapes follow from the summation indices
is . Row softmax preserves this shape. Multiplication by gives . A replacement supplies only four value rows for five keys and fails the inner-dimension requirement. Square-only examples would conceal this category of transpose error.
Back to G14.01G14.02Show solution
Normalize scores before mixing values
Zero scores give weights and output 5. Scores give weights and output . The output is a scalar in the value space and can exceed one or be negative if values permit; only the weights have the normalized probability-vector structure. Shifting both scores by the same finite constant gives the same exact weights.
Back to G14.02G14.03Show solution
Causality constrains the denominator too
The first position sees only value 2 and outputs 2. The second sees both and outputs 5. Normalizing the first row over both positions before zeroing produces and output 1, violating the intended normalized mixing rule. A test that changes the future value alone would not catch every denominator bug; also check allowed row sums and the one-allowed-key case.
Back to G14.03G14.04Show solution
The constant-shift direction has zero sensitivity
The weighted upstream mean is . Therefore the derivatives are and . Their sum is zero, as required because adding the same perturbation to both scores leaves softmax unchanged. This check catches some, not all, derivative errors.
Back to G14.04G14.05Show solution
One tensor is not the entire memory budget
At 1024, bytes, or 64 MiB. At 2048, it is bytes, or 256 MiB. Gradients, projected states, optimizer buffers, and allocator overhead are examples of excluded costs. An algorithm that streams blocks may not store this entire tensor, and a measured device peak also depends on implementation and allocation behavior. The calculation is a transparent model of one array.
Back to G14.05G14.06Show solution
A bounded forward reference
The code validates row widths, key/value counts, and finite arithmetic. It normalizes only allowed entries using the maximum allowed score. The causal option requires aligned self-attention lengths rather than silently guessing how unequal sequence positions correspond. Tests compare hand-computed outputs and a future-value change. This is the full reference for the assigned forward lab; it does not claim to train a language model or implement optimized GPU attention.
"""G14: single-head scaled dot-product attention, not a trained transformer."""
import math
def softmax(scores, allowed=None):
s = list(scores)
mask = [True]*len(s) if allowed is None else list(allowed)
if not s or len(mask) != len(s) or any(type(x) is not bool for x in mask) or not any(mask):
raise ValueError("nonempty row with at least one allowed position required")
if any(not math.isfinite(x) for x in s):
raise ValueError("finite pre-mask scores required")
top = max(v for v,ok in zip(s,mask) if ok)
e = [math.exp(v-top) if ok else 0. for v,ok in zip(s,mask)]
total = math.fsum(e)
return [v/total for v in e]
def attention(Q,K,V,causal=False):
n,m = len(Q),len(K)
d = len(Q[0]) if n else 0
dv = len(V[0]) if V else 0
if (type(causal) is not bool or not 1 <= n <= 256 or not 1 <= m <= 256
or not 1 <= d <= 128 or not 1 <= dv <= 128 or len(V) != m):
raise ValueError("bounded nonempty matching matrices required")
if causal and n != m:
raise ValueError("this causal lab requires equal query/key lengths")
for matrix,width in ((Q,d),(K,d),(V,dv)):
if any(len(row) != width or any(not math.isfinite(x) for x in row) for row in matrix):
raise ValueError("ragged or nonfinite matrix")
out, weights = [],[]
for i,q in enumerate(Q):
scores = [math.fsum(a*b for a,b in zip(q,k))/math.sqrt(d) for k in K]
p = softmax(scores,[j <= i for j in range(m)] if causal else None)
weights.append(p)
out.append([math.fsum(p[j]*V[j][v] for j in range(m)) for v in range(dv)])
return out,weights
if __name__ == "__main__":
print(attention([[0.],[0.]],[[1.],[2.]],[[2.],[8.]],causal=True))Readiness and continuation#
Explain , , , scores, weights, and outputs as six distinct objects. Derive a backward shape on unequal lengths and demonstrate a masking counterexample. A next project can add the taught backward formulas and finite-difference tests, then a complete tiny decoder with leakage-free next-token evaluation. That additional project is not counted as completed work here.