Navigation
πŸ“ FoundationsΒ· 14 min read

Feed-Forward Networks & Normalization

FFN architectures (ReLU, GELU, SwiGLU), LayerNorm vs RMSNorm, residual connections, and Pre-Norm vs Post-Norm.

ffnswiglugelulayernormrmsnormresidualpre-norm

Every Transformer block has two sub-layers: attention and FFN. This chapter covers the FFN side β€” the β€œexpand-then-compress” network that processes each token independently β€” plus the normalization and residual connections that make deep networks trainable.


1. The FFN: Expand and Compress

Standard FFN

The original Transformer FFN:

FFN(x)=ReLU(xW1+b1)W2+b2\text{FFN}(\mathbf{x}) = \text{ReLU}(\mathbf{x}\mathbf{W}_1 + \mathbf{b}_1)\mathbf{W}_2 + \mathbf{b}_2

Shape flow: (B,S,d)β†’(B,S,dff)β†’(B,S,d)(B, S, d) \to (B, S, d_{ff}) \to (B, S, d)

The β€œexpand-then-compress” design: up-project to dff=4dd_{ff} = 4d (expand the representational space), apply non-linearity, then down-project back to dd (compress). This bottleneck structure gives each token a chance to process information in a higher-dimensional space.

Activation Functions

FunctionFormulaProperties
ReLUReLU(x)=max⁑(0,x)\text{ReLU}(x) = \max(0, x)Simple, but β€œdead neurons” (zero gradient for x<0x < 0)
GELUGELU(x)=xβ‹…Ξ¦(x)β‰ˆxβ‹…Οƒ(1.702x)\text{GELU}(x) = x \cdot \Phi(x) \approx x \cdot \sigma(1.702x)Smooth approximation of ReLU, used in BERT/GPT-2
Swish/SiLUSwish(x)=xβ‹…Οƒ(x)=x1+eβˆ’x\text{Swish}(x) = x \cdot \sigma(x) = \frac{x}{1 + e^{-x}}Smooth, non-monotonic, used in SwiGLU

Where Ξ¦(x)\Phi(x) is the Gaussian CDF and Οƒ(x)=1/(1+eβˆ’x)\sigma(x) = 1/(1+e^{-x}) is the sigmoid function.

SwiGLU: The Modern Standard

SwiGLU Data Flow

Most modern LLMs (LLaMA, Qwen, Mistral, DeepSeek) use SwiGLU:

SwiGLU(x)=(Swish(xWgate))βŠ™(xWup)\text{SwiGLU}(\mathbf{x}) = \left(\text{Swish}(\mathbf{x}\mathbf{W}_{\text{gate}})\right) \odot \left(\mathbf{x}\mathbf{W}_{\text{up}}\right) FFN(x)=SwiGLU(x)β‹…Wdown\text{FFN}(\mathbf{x}) = \text{SwiGLU}(\mathbf{x}) \cdot \mathbf{W}_{\text{down}}

The gating mechanism: instead of applying the same activation to all dimensions, the model learns a per-dimension gate that controls which information passes through. Wgate\mathbf{W}_{\text{gate}} produces gate signals, Wup\mathbf{W}_{\text{up}} produces candidate values, element-wise multiply selects what to keep.

Impact on parameter count: SwiGLU has 3 matrices (gate, up, down) instead of 2. To maintain similar total parameters, dffd_{ff} is reduced from 4d4d to approximately (8/3)d(8/3)d.

FFN TypeMatricesdffd_{ff} (for 7B, d=4096d=4096)Params per block
Standard (ReLU/GELU)24d=163844d = 163842Γ—dΓ—dff=134M2 \times d \times d_{ff} = 134M
SwiGLU3(8/3)dβ‰ˆ11008(8/3)d \approx 110083Γ—dΓ—dff=135M3 \times d \times d_{ff} = 135M

Activation Memory Cost

SwiGLU produces large intermediate tensors that must be saved for the backward pass:

  • Gate output: (B,S,dff)(B, S, d_{ff})
  • Up output: (B,S,dff)(B, S, d_{ff})
  • Gate activation output: (B,S,dff)(B, S, d_{ff})

Total activation memory per block FFN: ∼3imesBimesSimesdffimes2\sim 3 imes B imes S imes d_{ff} imes 2 bytes (BF16).

For LLaMA-2-7B (dff=11008d_{ff}=11008, B=1,S=4096B=1, S=4096): 3 imes 4096 imes 11008 imes 2 pprox 258 MB per block, Γ— 32 blocks = 8.3 GB of activation memory. This is why activation checkpointing is essential for training.

FFN as a Knowledge Store

Recent research (β€œTransformer Feed-Forward Layers Are Key-Value Memories”, Geva et al. 2021) shows that FFN layers function as learned key-value memories: the up-projection keys match input patterns, and the down-projection values store associated information. This is why:

  • MoE experts can specialize in different domains (each expert stores different knowledge)
  • Larger FFN dimensions improve factual recall
  • Knowledge editing techniques target FFN weights specifically

2. Residual Connections

Every sub-layer in a Transformer is wrapped with a residual (skip) connection:

hout=hin+SubLayer(hin)\mathbf{h}_{out} = \mathbf{h}_{in} + \text{SubLayer}(\mathbf{h}_{in})

Why? In a deep network, gradients must flow through many layers. Without residuals, the gradient of layer 1 is a product of many Jacobians β€” likely to vanish or explode. The residual creates a gradient highway:

βˆ‚houtβˆ‚hin=I+βˆ‚SubLayerβˆ‚hin\frac{\partial \mathbf{h}_{out}}{\partial \mathbf{h}_{in}} = \mathbf{I} + \frac{\partial \text{SubLayer}}{\partial \mathbf{h}_{in}}

The identity matrix I\mathbf{I} ensures gradients always have a direct path, even if the sub-layer’s gradient is small. This is why Transformers can be 100+ layers deep.


3. Layer Normalization

LayerNorm

Normalizes across the hidden dimension for each token independently:

LN(x)=Ξ³βŠ™xβˆ’ΞΌΟƒ2+Ο΅+Ξ²\text{LN}(\mathbf{x}) = \gamma \odot \frac{\mathbf{x} - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta

Where ΞΌ=1dβˆ‘ixi\mu = \frac{1}{d}\sum_i x_i and Οƒ2=1dβˆ‘i(xiβˆ’ΞΌ)2\sigma^2 = \frac{1}{d}\sum_i (x_i - \mu)^2. Learnable parameters: scale Ξ³\gamma and shift Ξ²\beta (both size dd).

RMSNorm

Used by LLaMA, Qwen, Mistral β€” skips mean centering:

RMSNorm(x)=Ξ³βŠ™x1dβˆ‘ixi2+Ο΅\text{RMSNorm}(\mathbf{x}) = \gamma \odot \frac{\mathbf{x}}{\sqrt{\frac{1}{d}\sum_i x_i^2 + \epsilon}}

Only dd learnable parameters (just the scale Ξ³\gamma, no shift Ξ²\beta).

Why RMSNorm won: ~10-15% faster than LayerNorm (fewer operations β€” no mean computation, no shift) with comparable model quality. The mean centering in LayerNorm turns out to be unnecessary for most LLM architectures.

Pre-Norm vs Post-Norm

Two ways to arrange normalization in a Transformer block:

Post-Norm (original Transformer):

x β†’ Attention β†’ Add(x, Β·) β†’ LayerNorm β†’ FFN β†’ Add(Β·, Β·) β†’ LayerNorm β†’ out

Pre-Norm (modern LLMs):

x β†’ LayerNorm β†’ Attention β†’ Add(x, Β·) β†’ LayerNorm β†’ FFN β†’ Add(Β·, Β·) β†’ out

Pre-Norm is now standard because:

  • More stable training β€” gradients are normalized before each sub-layer
  • Enables training deeper models without careful learning rate tuning
  • Used by GPT-2/3, LLaMA, Qwen, Mistral, and virtually all modern LLMs

The tradeoff: Post-Norm can achieve slightly better final quality with careful training, but Pre-Norm is much easier to train at scale.


4. Putting It Together: One Decoder Block

class DecoderBlock(nn.Module):
    def __init__(self, d_model, num_heads, d_ff):
        super().__init__()
        self.norm1 = RMSNorm(d_model)
        self.attn = MultiHeadSelfAttention(d_model, num_heads)
        self.norm2 = RMSNorm(d_model)
        self.ffn = SwiGLU_FFN(d_model, d_ff)

    def forward(self, x, mask=None):
        # Pre-Norm + Attention + Residual
        x = x + self.attn(self.norm1(x), mask)
        # Pre-Norm + FFN + Residual
        x = x + self.ffn(self.norm2(x))
        return x

Data flow with shapes (d=4096,dff=11008d=4096, d_{ff}=11008):

Input x: (B, S, 4096)
  β†’ RMSNorm β†’ (B, S, 4096)
  β†’ Attention β†’ (B, S, 4096)
  β†’ + x (residual) β†’ (B, S, 4096)
  β†’ RMSNorm β†’ (B, S, 4096)
  β†’ SwiGLU FFN:
      β†’ W_gate: (B, S, 4096) Γ— (4096, 11008) β†’ (B, S, 11008)
      β†’ W_up:   (B, S, 4096) Γ— (4096, 11008) β†’ (B, S, 11008)
      β†’ Swish(gate) βŠ™ up β†’ (B, S, 11008)
      β†’ W_down: (B, S, 11008) Γ— (11008, 4096) β†’ (B, S, 4096)
  β†’ + x (residual) β†’ (B, S, 4096)
Output: (B, S, 4096)

Self-Check

  • Explain the β€œexpand-then-compress” design of FFN and why dff>dd_{ff} > d
  • Explain how SwiGLU gating works and why it has 3 weight matrices
  • Explain why dffβ‰ˆ(8/3)dd_{ff} \approx (8/3)d with SwiGLU instead of 4d4d
  • Explain why residual connections prevent gradient vanishing
  • Explain the difference between LayerNorm and RMSNorm
  • Explain Pre-Norm vs Post-Norm and why Pre-Norm is standard

References