← ATTENTION & TRANSFORMERS / COURSE MAP
LESSON 06 / THE REUSABLE BLOCK

The block around attention.

Residual addition, layer normalization, and the feed-forward network: understand the boxes that architecture diagrams often leave unexplained.

On this page Explore the sections +
Before you begin Prerequisites & learning goal +

Builds on: Lesson 5: multi-head attention preserves model width and returns one vector per query.

Your goal: Assemble a pre-norm block and distinguish token mixing, feature mixing, normalization, and residual addition.

An attention layer computes useful mixtures, but a transformer diagram also contains boxes named Add, Norm, and Feed Forward. These operations are not decoration. They determine how information and gradients pass through the stack and how each position transforms its newly gathered context.

We will build the block used in our final model. Its input and output both have shape (B, T, D), which makes it possible to stack blocks without changing the representation width.

1. Add means a residual update

Let F be a learned transformation that returns the same shape as its input. A residual update is:

$$ y=x+F(x). $$
READ THE EQUATION

Keep the current representation and add the sublayer’s proposed change.

TermWhat it is and doesWhat it controls
\(x,y\)Current and updated feature tensors of equal shape.The representation before and after the residual update.
\(F,F(x)\)A learned sublayer and its output at x.The change proposed by attention or a feed-forward operation.
\(+\)Elementwise addition, requiring matching output shape.Preserves an identity path from x while allowing an update.

Check: x=[1,2] and F(x)=[0.1,−0.2] give y=[1.1,1.8]. If F(x)=0, y=x. Addition here does not concatenate features.

Every coordinate of F’s output is added to the matching coordinate of x. There is no concatenation and no new feature axis. The block can learn an update while the original representation has a direct path forward.

If F returns zero, the block returns x. During differentiation, a change in x has a direct path to the output through the addition, as well as a path through F. This direct contribution is one reason residual connections can help optimization. It does not guarantee that every deep network will train well; normalization, initialization, and the rest of the computation still matter. The foundations learning lesson explains how local derivatives carry a loss signal backward through a computation.

Residual addition is different from adding positional encodings. Both are elementwise sums, but one introduces an input signal and the other combines a representation with a learned update.

2. Layer normalization acts within a token

For one D-dimensional token vector x, compute its mean and population variance across features:

$$ \mu=\frac{1}{D}\sum_{j=1}^{D}x_j, \qquad \sigma^2=\frac{1}{D}\sum_{j=1}^{D}(x_j-\mu)^2. $$
READ THE EQUATION

For one token, compute its feature mean and its average squared deviation from that mean.

TermWhat it is and doesWhat it controls
\(x_j,j,D\)Feature j, feature index, and feature count for one token.The population whose statistics are measured; other tokens are excluded.
\(\mu,\sum_j x_j,1/D\)Mean, sum of features, and division by feature count.The token’s feature center.
\(x_j-\mu,(\cdot)^2\)Deviation from the mean and squaring that deviation.Spread without cancellation between positive and negative deviations.
\(\sigma^2\)Population variance, using division by D.The squared feature scale; this is not an unbiased sample estimate divided by D−1.

Check: For [1,3], mean is 2 and variance is (1+1)/2=1. Each token receives its own mean and variance, even inside a batch.

Layer normalization then computes:

$$ \operatorname{LN}(x)_j=\gamma_j\frac{x_j-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta_j. $$
READ THE EQUATION

Center a token’s feature, divide by its stabilized spread, then apply that feature’s learned gain and bias.

TermWhat it is and doesWhat it controls
\(\operatorname{LN}(x)_j,x_j\)Normalized output and original value at feature j of one token.The coordinate being transformed.
\(\mu,\sigma^2\)The feature mean and variance computed above.Center and spread of this token’s feature vector.
\(\epsilon,\sqrt{\sigma^2+\epsilon}\)A small positive constant and stabilized standard deviation.Avoids division by zero and controls behavior near zero variance.
\(\gamma_j,\beta_j\)Learned feature gain and bias.Rescale and shift each normalized coordinate; they are shared across positions.
Subtract, divide, multiply, addThe ordered operations in the formula.Centering and scaling happen before the learned affine adjustment.

Check: For [1,3], gain [1,1], bias [0,0], and tiny epsilon, the result is approximately [−1,1]. Identical features have zero centered values, so the result is beta rather than a division-by-zero error.

Epsilon is a small positive constant that keeps the denominator well-defined. Gamma and beta are learned feature-wise scale and shift parameters. Every token uses its own mean and variance, while sharing the same learned gamma and beta for that normalization layer.

With (B, T, D) input and LayerNorm(D), the statistics do not mix batch items or token positions. Layer normalization therefore does not secretly let a causal position read later tokens through shared sequence-wide statistics. Its statistics are also computed from the current input during both training and inference; this is different from batch normalization’s usual running-statistics behavior. See Layer Normalization and PyTorch’s LayerNorm documentation .

For [1, 2, 3, 4], the mean is 2.5 and the variance is 1.25. Ignoring epsilon for a moment and taking gamma = 1, beta = 0 gives approximately [-1.342, -0.447, 0.447, 1.342]. With epsilon included, the normalized variance is slightly below one; learned gamma and beta can change the final mean and variance further.

3. The feed-forward network mixes features

The position-wise feed-forward network applies the same small neural network independently to every token:

$$ \operatorname{FFN}(x)=\operatorname{ReLU}(xW_1+b_1)W_2+b_2. $$
READ THE EQUATION

Project token features to an inner width, apply ReLU, then project back to the model width.

TermWhat it is and doesWhat it controls
\(x,\operatorname{FFN}(x)\)One token’s input and output features.A feature transformation applied independently at every position.
\(W_1,b_1\)First projection D×F and inner-width bias F.Creates F intermediate combinations of the D input features.
ReLUReplace each negative intermediate value with zero; retain positive values.Adds a nonlinearity so the two affine projections do more than one affine map.
\(W_2,b_2\)Second projection F×D and model-width bias D.Returns to width D so residual addition is possible.
Matrix products and additionsFeature mixing and shared offsets.No position axis is mixed by this sublayer.

Check: With identity weights, zero biases, and x=[−1,2], the result is [0,2]. A wider inner layer has more intermediate features, not more token positions.

The first projection expands D features to an inner width, such as 64 from a model width of 32. ReLU replaces negative intermediate values with zero. The second projection returns to D features so the result can be added to the residual stream.

Without the nonlinearity, the two affine transformations could be combined into one affine transformation. The activation allows a richer input-dependent transformation of each token’s feature vector.

Attention mixes information between positions. The FFN transforms features within a position, using the context that has already arrived there. Its weights are shared across positions in the same block, but different blocks generally have their own FFN parameters. The original transformer’s position-wise FFN is described in §3.3 of Attention Is All You Need .

PYTHON
NumPy / inspect the operations
def layer_norm(x: FloatArray, gamma: FloatArray | None = None, beta: FloatArray | None = None, eps: float = 1e-5) -> FloatArray:
    mean = x.mean(axis=-1, keepdims=True)
    variance = ((x - mean) ** 2).mean(axis=-1, keepdims=True)
    normalized = (x - mean) / np.sqrt(variance + eps)
    gamma = np.ones(x.shape[-1]) if gamma is None else gamma
    beta = np.zeros(x.shape[-1]) if beta is None else beta
    return normalized * gamma + beta


def feed_forward(x: FloatArray, w1: FloatArray, b1: FloatArray, w2: FloatArray, b2: FloatArray) -> FloatArray:
    hidden = np.maximum(x @ w1 + b1, 0.0)  # ReLU, separately per token.
    return hidden @ w2 + b2               # Return to the residual width.

Typed Python · Shapes are documented alongside the code. Open the full file for imports and dependencies, or step through a concrete example.

The NumPy function exposes the normalization arithmetic. The PyTorch function delegates to the differentiable library operation. In our trainable blocks below, nn.LayerNorm owns gamma and beta; they are initialized to one and zero, respectively, and can then change during training. The NumPy block is a forward demonstration using those default affine parameters.

4. The order of Add and Norm matters

Two common arrangements are:

$$ \text{Post-norm:}\quad y=\operatorname{LN}(x+F(x)) $$
READ THE EQUATION

First add the sublayer update to the residual stream, then normalize that combined result.

TermWhat it is and doesWhat it controls
\(x,F(x)\)Residual stream and sublayer output.The two equal-shaped tensors being combined.
\(+\)Residual addition before normalization.The direct x path enters the normalization operation too.
LNLayer normalization with its per-token statistics, gain, bias, and epsilon.Rescales the combined features.
\(y\)Post-norm output.The normalized result supplied to the next stage.

Check: If F(x)=0, this arrangement gives LN(x), which need not equal x. That is a meaningful difference from a pre-norm residual update.

$$ \text{Pre-norm:}\quad y=x+F(\operatorname{LN}(x)). $$
READ THE EQUATION

Normalize only the sublayer input, and add its result back to the original residual stream.

TermWhat it is and doesWhat it controls
\(x,\operatorname{LN}(x)\)Original representation and normalized copy supplied to F.Keeps the residual path separate from the normalization path.
\(F\)Learned sublayer operating on normalized features.Produces an update with the same shape as x.
\(+\), \(y\)Add the update to x to produce y.Preserves an unnormalized identity route for this sublayer.

Check: If F always returns zero, y=x exactly. The original 2017 paper uses post-norm; the course implementation deliberately uses pre-norm with final stack norms.

The 2017 transformer diagram places normalization after each residual addition. Our teaching model uses pre-norm: normalize the input to a sublayer, compute its update, and add the update to the unnormalized residual stream. We also apply a final normalization at the end of each stack.

These formulas are not interchangeable. The location of normalization changes the forward function and the gradient path. Xiong et al. analyze this distinction and its implications for optimization. We choose one ordering consistently so our equations, diagrams, and executable model agree.

5. Assemble the two-sublayer block

Our encoder block computes:

$$ u=x+\operatorname{MHA}(\operatorname{LN}_1(x)), \qquad y=u+\operatorname{FFN}(\operatorname{LN}_2(u)). $$
READ THE EQUATION

Make an attention residual update, then make a feed-forward residual update using the already-updated representation.

TermWhat it is and doesWhat it controls
\(x,u,y\)Block input, intermediate result after attention, and final output.The sequence of representations; the second sublayer reads u, not stale x.
\(\operatorname{LN}_1,\operatorname{LN}_2\)Two separately learned layer-normalization modules.Prepare the inputs to their respective sublayers.
MHAMulti-head attention, with the appropriate visibility mask.Mixes information across allowed positions.
FFNPositionwise nonlinear feature transformation.Mixes features within each position after attention.
Both plus signsEqual-shaped residual additions.Carry x into u, then carry u into y.

Check: Even though both sublayers preserve shape (B,T,D), they do different work. Attention changes which positions inform a token; FFN transforms that token’s updated features.

The two normalization modules have separate learned parameters. MHA here means self-attention using the normalized states for all three projected inputs, subject to the source-padding mask.

VISUAL WALKTHROUGHALL STEPS
Build a pre-norm transformer block
1 / Normalize the sublayer input
LAYER NORMMULTI-HEAD ATTENTIONLAYER NORMFEED-FORWARD NETWORKRESIDUAL PATH: CARRY X FORWARDRESIDUAL PATH: CARRY THE UPDATED X FORWARDPRE-NORM BLOCK / EVERY ADD COMBINES TWO [B,T,D] TENSORS

Statistics are computed across D features for each token separately. The original residual stream takes the bypass route; normalization prepares the input to the learned transformation.

2 / Gather context
LAYER NORMMULTI-HEAD ATTENTIONLAYER NORMFEED-FORWARD NETWORKRESIDUAL PATH: CARRY X FORWARDRESIDUAL PATH: CARRY THE UPDATED X FORWARDPRE-NORM BLOCK / EVERY ADD COMBINES TWO [B,T,D] TENSORS

Multi-head attention mixes information across visible positions. Its output has model width D, so it fits the first residual addition.

3 / Add the update
LAYER NORMMULTI-HEAD ATTENTIONLAYER NORMFEED-FORWARD NETWORKRESIDUAL PATH: CARRY X FORWARDRESIDUAL PATH: CARRY THE UPDATED X FORWARDPRE-NORM BLOCK / EVERY ADD COMBINES TWO [B,T,D] TENSORS

The circle adds matching features. The updated representation becomes the input and residual stream for the second sublayer. Nothing is concatenated here.

4 / Transform the contextual features
LAYER NORMMULTI-HEAD ATTENTIONLAYER NORMFEED-FORWARD NETWORKRESIDUAL PATH: CARRY X FORWARDRESIDUAL PATH: CARRY THE UPDATED X FORWARDPRE-NORM BLOCK / EVERY ADD COMBINES TWO [B,T,D] TENSORS

The FFN expands features, applies ReLU, and projects back to D. Another residual addition completes the block. The same tensor shape can now enter the next block.

One operation at a time. Use the arrows or step buttons; nothing advances automatically.

PYTHON
NumPy / inspect the operations
class EncoderBlock:
    """A pre-norm forward pass. Normalization gain=1 and bias=0 here."""
    def __init__(self, width: int = 32, heads: int = 4, inner: int = 64, seed: int = 10) -> None:
        self.attn = MultiHead(width, heads, seed)
        self.ffn = ffn_parameters(width, inner, seed + 1)

    def __call__(self, x: FloatArray, source_visible: BoolArray) -> FloatArray:
        normalized = layer_norm(x)
        x = x + self.attn(normalized, normalized, source_visible)[0]
        return x + feed_forward(layer_norm(x), *self.ffn)

Typed Python · Shapes are documented alongside the code. Open the full file for imports and dependencies, or step through a concrete example.

The complete files supply MultiHead, layer_norm, and the NumPy FFN parameters from earlier sections. PyTorch registers its layers as submodules, so model.parameters() will include all their learned weights.

6. Two experiments that isolate the roles

First, set the attention and FFN updates to zero. A pre-norm block’s residual route becomes an identity map. A final stack normalization, if applied after that block, can still change the representation. Keep those two boundaries separate when you test.

Second, hold all token vectors fixed except one, and apply only the FFN to each row. Only the changed row’s output can change. Perform the same intervention before unmasked self-attention, and other rows can change too. That is the difference between feature-wise transformation and communication across positions.

Dropout is another common component: during training it randomly removes selected contributions, usually with rescaling. Our tiny model omits dropout so repeated forward passes are deterministic and cross-library comparisons stay simple. Omitting it here is a teaching choice, not a claim that regularization is unnecessary for larger tasks.

CHECK YOUR UNDERSTANDINGWould LayerNorm over the whole T × D sequence be equivalent to LayerNorm(D)?Think first. Open to check your reasoning.

No. It would combine statistics across token positions. That changes the operation and can introduce future-position dependence in a causal model. Our LayerNorm(D) computes separate statistics for each token across its feature axis only.

Carry this forward

We now have a reusable contextual transformation. An encoder applies it with source visibility. A decoder needs causal target visibility and, when there is an encoder, another attention sublayer for source memory. The next lesson puts those pieces into complete architectures and explains when each is useful.

END OF LESSON 06