SwiGLU Feed-Forward Block (LLaMA MLP) - LLM Internals
Implement SwiGLU Feed-Forward Block (LLaMA MLP) in Python with NumPy. medium exercise with examples, hints, and instant feedback. No setup or signup required.
SwiGLU Feed-Forward Block (LLaMA MLP)
What you'll build
The MLP half of a LLaMA decoder block: RMSNorm, a SwiGLU gated feed-forward network, and the residual connection around them. Three functions: rms_norm, swiglu_ffn and llama_mlp_block.
Background
The original Transformer MLP is ReLU(x W_1) W_2. LLaMA, Mistral, Qwen and PaLM use a gated MLP instead, SwiGLU (Shazeer, 2020): one projection goes through SiLU and gates a second, linear projection elementwise. At equal parameter count it gives lower loss. LLaMA also replaces LayerNorm with RMSNorm, which skips the mean subtraction and the bias: cheaper, and empirically just as good. About two thirds of a dense LLM's parameters live in these MLPs.
Formula
rms_norm(x) = x / sqrt(mean(x^2, axis=-1) + eps) * weight
silu(z) = z * sigmoid(z) = z / (1 + exp(-z))
gate = silu(x @ W_gate) # (..., d_ff)
up = x @ W_up # (..., d_ff)
swiglu_ffn(x) = (gate * up) @ W_down # (..., d_model)
llama_mlp_block(x) = x + swiglu_ffn(rms_norm(x))
No biases anywhere (LLaMA uses none). With three weight matrices instead of two, LLaMA sets d_ff ≈ (2/3) · 4 · d_model to match the parameter count of a 4 · d_model ReLU MLP.
Inputs
x: float array(..., d_model)(any number of leading axes, e.g.(batch, seq_len, d_model))weight/norm_weight:(d_model,)W_gate,W_up:(d_model, d_ff);W_down:(d_ff, d_model)eps: float, default1e-6
Expected Return Format
rms_norm(x, weight, eps=1e-6): same shape asxswiglu_ffn(x, W_gate, W_up, W_down): shape(..., d_model)llama_mlp_block(x, norm_weight, W_gate, W_up, W_down, eps=1e-6): same shape asx
Edge cases and conventions
- RMSNorm normalizes over the last axis only;
epsgoes inside the square root. - The residual adds the un-normalized
x(pre-norm):x + ffn(norm(x)), notnorm(x) + ffn(norm(x)). - The gate is SiLU, not sigmoid (that would be GLU) and not ReLU (ReGLU).
Constraints
NumPy only, vectorised.
Follow-up
- Count the parameters of the SwiGLU MLP and of a ReLU MLP with
d_ff = 4 d_model. Whatd_ffmakes them equal? - Write the attention half of the LLaMA block (RMSNorm, RoPE, GQA, causal mask). Which earlier problems give you each piece?
Examples
Input: rms_norm(np.array([[3.0, 4.0]]), np.ones(2)) Output: [[0.8485, 1.1314]]
mean(x^2) = 12.5, so the RMS is 3.5355 and x / 3.5355 = [0.8485, 1.1314]. The mean is not subtracted.
Input: swiglu_ffn(np.array([[1.0, -1.0]]), np.array([[1.0], [0.0]]), np.array([[2.0], [1.0]]), np.array([[1.0, 0.5]])) Output: [[0.7311, 0.3655]]
gate = silu(1) = 0.7311, up = 2 - 1 = 1, then (0.7311 * 1) @ [[1, 0.5]].
Practice in Python
import numpy as np
def rms_norm(x, weight, eps=1e-6):
"""
RMSNorm over the last axis: x / sqrt(mean(x**2) + eps) * weight.
Args:
x: (..., d_model)
weight: (d_model,) learned scale
Returns:
Array with the same shape as x
"""
# Your code here
pass
def swiglu_ffn(x, W_gate, W_up, W_down):
"""
SwiGLU feed-forward network: (silu(x @ W_gate) * (x @ W_up)) @ W_down
Args:
x: (..., d_model)
W_gate, W_up: (d_model, d_ff)
W_down: (d_ff, d_model)
Returns:
Array of shape (..., d_model)
"""
# Your code here
pass
def llama_mlp_block(x, norm_weight, W_gate, W_up, W_down, eps=1e-6):
"""
Pre-norm MLP sub-block with a residual: x + swiglu_ffn(rms_norm(x)).
Returns:
Array with the same shape as x
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 8 tests, or use the progressive hints.