Transformer Block Assembly › Stacking Blocks: Building a Complete Model

Michael BrenndoerferUpdated June 13, 20253 min read
Language AI HandbookAll chapters
Part 15 of 67Transformer Blocks
Chapter 110Transformer Block Assembly › Stacking Blocks: Building a Complete Model
Reading section 8 of 16 · Stacking Blocks: Building a Complete Model · 3 minSection 8 of 16 · 3 min

Book contents

Section 8 of 16 · 3 min read

Transformer Block Assembly › Stacking Blocks: Building a Complete ModelOne section · Sections in this chapter8 / 16 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

A transformer model stacks multiple blocks in sequence. The output of one block becomes the input to the next, with each block refining the representations:

In[26]:
Code
class TransformerStack:
    """
    Stack of transformer blocks forming a complete encoder or decoder.
    """

    def __init__(
        self,
        n_layers,
        d_model,
        n_heads,
        d_ff,
        use_swiglu=True,
        is_decoder=False,
    ):
        """
        Args:
            n_layers: Number of transformer blocks
            d_model: Model dimension
            n_heads: Number of attention heads
            d_ff: Feed-forward hidden dimension
            use_swiglu: Whether to use SwiGLU FFN
            is_decoder: Whether to apply causal masking
        """
        self.n_layers = n_layers
        self.is_decoder = is_decoder

        # Create blocks
        if is_decoder:
            self.blocks = [
                DecoderBlock(d_model, n_heads, d_ff, use_swiglu)
                for _ in range(n_layers)
            ]
        else:
            self.blocks = [
                TransformerBlock(d_model, n_heads, d_ff, use_swiglu)
                for _ in range(n_layers)
            ]

        # Final normalization (after all blocks)
        self.final_norm = RMSNorm(d_model)

    def __call__(self, x):
        """
        Process input through all transformer blocks.

        Args:
            x: Input tensor of shape (seq_len, d_model)

        Returns:
            Output tensor of shape (seq_len, d_model)
        """
        # Pass through each block sequentially
        for block in self.blocks:
            x = block(x)

        # Final normalization
        x = self.final_norm(x)

        return x

    def num_parameters(self):
        """Total parameters in the stack."""
        block_params = sum(block.num_parameters() for block in self.blocks)
        # Final norm adds d_model parameters
        return block_params + self.blocks[0].d_model


# Create a small transformer stack
transformer = TransformerStack(
    n_layers=6,
    d_model=256,
    n_heads=4,
    d_ff=688,
    use_swiglu=True,
    is_decoder=True,
)
Out[27]:
Console
Transformer Stack Configuration
==================================================
  Layers: 6
  d_model: 256
  n_heads: 4
  d_ff: 688
  FFN type: SwiGLU
  Type: Decoder (causal)
  Total parameters: 4,746,496

Let's trace how representations evolve through the stack:

In[28]:
Code
# Track representations through layers
def trace_through_stack(stack, x):
    """Collect representations after each layer."""
    representations = [x.copy()]

    current = x
    for block in stack.blocks:
        current = block(current)
        representations.append(current.copy())

    # After final norm
    final = stack.final_norm(current)
    representations.append(final.copy())

    return representations


# Trace through the stack
x_trace = np.random.randn(8, 256) * 0.02
layer_outputs = trace_through_stack(transformer, x_trace)
Out[29]:
Visualization
Line plot showing representation magnitude increasing slightly through 6 transformer layers plus final norm.
How representations evolve through a 6-layer transformer stack. The plot shows the mean L2 norm of token representations after each layer. The gradual increase reflects the accumulation of information via residual connections.

The visualization shows how pre-norm architectures allow representation magnitude to grow through the network. Each residual connection adds to the running sum. The final normalization (RMSNorm after all blocks) brings the representations back to a controlled scale before the output layer.