learning-ai.cafe learning-ai.cafe / Attention / 10

10 · Attention

The Transformer Block

The residual stream, layer normalisation, and the feed-forward layer that surrounds attention.

Two rounds of attention collapse into one

Everything asked for at the start is built. The sentence arrives as with its positions folded in, each head turns it into , and , the mask settles which positions a query may read, and folds the heads back into one vector per word. chased leaves holding 0.60 of mouse, 0.18 of cat, and a little of the rest.

The masking chapter set the model a task: read The cat chased the and give the word that follows. Answering mouse means using two facts that now sit in the same vector — the subject is a cat, the verb is chased. Attention put both there by averaging, and averaging is not deriving a third fact from two. Gathering information and computing with it are different operations, and the model so far does only the first.

Why not simply run attention again? Its output has the shape of its input, so it can be fed straight back in. Follow a single head and write the weight table as , so one round is

where mixes the rows and reshapes each row on its own. Freeze the weights for a moment and put two rounds one after the other:

is one table and is one projection, so two rounds have the form of one. In a real stack is computed from what the first round produced, so the two are not literally identical — but that dependence lives in the proportions of the blend, never in the vectors being blended. Every value crosses the mechanism without passing through a nonlinear function. The softmax decides how much of each word to take, never what any word is.

Geometrically: every row of is positive and sums to one, so a head's answer is an average of the value vectors, and a head can move a word anywhere inside the region those vectors span and nowhere else.

Five value vectors as points with their convex hull shaded, one blended answer inside it, and a second panel where an arrow labelled FFN leaves the hull.

A softmax row is positive and sums to one, so one head's answer is always a blend of the value vectors and can never leave the region they span. The feed-forward network is what lets a position land somewhere new.

Give every word its own network

Attention has already done all the sideways looking, and it is the only part of the model that needs to. So whatever comes next should read one word's vector and nothing else. That step is two matrix multiplications with a function in between:

is and is , so the vector widens and comes back. The usual choice is , which is 36 for the nine-wide example here. is applied to each number on its own, most often ReLU, which replaces negatives with zero.

That makes each column of a small test. It compresses the word's vector to one number, and ReLU zeroes it unless a specific combination of features is present — something carrying both a cat and a chase direction, say. takes whichever tests fired and writes their conclusions back into the vector. Widening to is room to run thousands of tests at once; narrowing back is packaging the result so the rest of the model can read it.

Two panels: five rows with arrows between every pair, and the same five rows each with its own small network and no arrows between rows.

The two sublayers have complementary jobs. Attention is the only place positions talk to each other; the feed-forward network is the only place a position is transformed. A block is one of each.

Adding instead of replacing

The natural move is to repeat the pair: attention, feed-forward, attention again, as many times as the budget allows. Stacking that way has a cost. Each sublayer hands back a different vector than it was given, so a dozen blocks in a row means the word has been overwritten a dozen times, and anything about it still needed has to be rebuilt at every step.

The fix is to add rather than replace:

or, over the whole sentence, and then . All three matrices are , so both additions line up row by row. This is a residual connection. Nothing is thrown away: the vector that arrived is still in the sum, and a sublayer with nothing useful to add can return values near zero and let the vector through unchanged.

A thick unbroken line running through three blocks, with each sublayer branching off it and returning through a plus sign.

The sublayers sit off the main stream rather than blocking it: each takes a copy, does its work, and adds the result back. The stream is a running total that is never overwritten, and because the result must be added to it, every sublayer has to return the width it was given.

The second advantage shows up only during training. A correction travels backward from the loss to every earlier block, and in a plain stack it is reshaped by every sublayer on the way. With the addition, the derivative of is . That is an express lane straight past the sublayer's arithmetic.

Where the identity comes from

Write the step one component at a time, , and differentiate against . The second term is the sublayer's own Jacobian. The first is , which is 1 when and 0 otherwise, because two components of the input are free to move independently. Ones down the diagonal is the identity, so .

A correction arriving at the output becomes : one copy untouched, one through the sublayer. Over blocks the product expands into every way of picking or from each bracket, and one of those terms picks every time. The correction reaching the first block always contains an exact copy of the one that left the loss, whatever the are doing. Without the residual the product is , which shrinks to nothing or grows without bound.

The same stack read right to left, once with the correction forced through every sublayer and once with it forking at each plus sign.

Without the additions a correction can only reach block 1 by passing through everything in between. With them it always has a route that passes through nothing at all.

Keeping the numbers in range

The stream solves one problem and creates another. Crossing blocks, the vector accumulates contributions, so its numbers tend to grow with depth. Queries and keys are built from that stream, and , so if doubles then both double and the score quadruples. Stretched gaps are exactly what took mouse from 0.60 to 0.96 with everything else rounding away, and collapses with it. Dividing by does not help: it answers growth from the dimension, not growth from depth.

So put the numbers back in range before a sublayer reads them. Take one word's vector, compute the mean of its components and their spread, subtract the mean and divide by the spread:

guards against a division by zero when every component is identical. and are learned vectors of width , applied element-wise, so the model is not pinned to zero mean and unit variance — it can learn its own scale and shift, independent of how much depth has accumulated.

One detail carries the whole idea: every sum runs over the features of a single word, to . Not over the words in the sentence, not over the batch. chased is normalised from its own nine numbers and would come out identical if the rest of the batch were thrown away.

That matters because the common alternative, batch normalisation, takes its statistics across the examples in the batch, which breaks here three times over. Sentences are padded, so statistics at a late position would be computed partly over [PAD] slots. Sentences differ in length, so how many real words reach a position depends on what else is in the batch. And at generation time there is a single sequence and no batch at all.

One row of nine values with its mean and spread before and after normalisation, beside the full five by nine matrix with a mean and spread per row.

Layer normalisation reads nine numbers and writes nine numbers, never once looking at a different row. Whatever scale a word arrives at, it leaves at the same one as every other word.

Where the norm goes is a second decision. The original Transformer normalised the sum, , which is post-norm. Almost everything since normalises only the copy the sublayer reads, , which is pre-norm. The two differ by one box moved, and that box decides how the gradient flows: in post-norm the norm sits on the stream and rescales every correction travelling back; in pre-norm the stream is untouched. The price is one extra at the very end, since the stream now reaches the output unnormalised.

Two identical three-block columns, one with the norm boxes on the main line and one with them on the branches.

One box moved off the main line. That is the whole difference, and it is what keeps the unbroken path of the previous figures alive through a deep stack.

The block

Attention to move information between words, a feed-forward network to compute with it, an addition so neither overwrites what came before, a normalisation so the numbers stay in range. A transformer block is those four things:

Two sublayers, two norms, two additions. The norms are separate, each with its own and , which is why they carry different subscripts. All of , and are , so a block hands back exactly what it was given: five words in, five words out, nine numbers each.

One block opened up, with shapes on every edge: LN, multi-head, plus, LN, feed-forward, plus.

The block in full: two sublayers, two norms, two additions, and a width that never changes. This is the residual stream with the norms placed where the previous figure said to put them.
def layer_norm(X, gamma, beta, eps=1e-5):
    """Statistics from the last axis alone, so every position is on its own."""
    mu = X.mean(dim=-1, keepdim=True)
    var = X.var(dim=-1, unbiased=False, keepdim=True)
    return (X - mu) / torch.sqrt(var + eps) * gamma + beta

def block(X, attn, ffn, ln_1, ln_2, mask=None):
    """One pre-norm block. X is (n, d_model), and so is the result."""
    Z = X + multi_head_attention(layer_norm(X, *ln_1), *attn, mask)
    return Z + feed_forward(layer_norm(Z, *ln_2), *ffn)

Each claim above is a line of that function. The sublayer reads layer_norm(X, ...) and never the stream, so the norm sits on the branch. What it returns is added rather than substituted. And the result is shaped like X, which is what lets the last line of one block be the first argument of the next.

Counting parameters for the nine-wide example: attention holds , , and , which across all heads come to weights, or 324. The feed-forward holds and , which at come to , or 648. Two thirds of a block is the part that does not look sideways at all.

Stacking these is the next chapter, along with the two pieces still missing between a stack of blocks and a word.

learning-ai.cafe

Attention

Is it really all we need? The routing mechanism that helps the model to focus.

  1. 01 Why Attention?
  2. 02 The Dictionary Analogy
  3. 03 Measuring Similarity
  4. 04 From Scores to Weights
  5. 05 Scaled Dot-Product Attention
  6. 06 Self-Attention
  7. 07 Multi-Head Attention
  8. 08 Masking and Causality
  9. 09 Positional Information
  10. 10 The Transformer Block
    1. Two rounds of attention collapse into one
    2. Give every word its own network
    3. Adding instead of replacing
    4. Keeping the numbers in range
    5. The block
  11. 11 The Whole Model
  12. 12 The Cost of Looking Everywhere
  13. 13 References

Who built this page

Every merged contribution is typed and scored at review. Chips next to a heading show who touched that section. Scores feed the contributor share of project support.

History

    Propose a change

    Settings

    Language
    Appearance
    Accent
    Font
    Text size
    Column width
    Book