The MLP

After attention has moved information between tokens, the MLP processes each token on its own: widen the vector four times, bend it with an activation function, narrow it back. Its neurons act like key–value memories, and it holds most of the model’s parameters.

Key idea
MLP(x) = W_down · act(W_up · x); SwiGLU adds a gate; neurons are keys (patterns) and values (what to write)
Real world
A reference librarian. You hand over one question; they check it against everything they know, the few matching entries light up, and they hand back a note built from those entries. They never talk to the other people in the queue.
In an LLM
At the word “ France”, neurons whose keys match “a country” fire, and their values add what the model knows about it, such as its capital. Later attention carries that to the end of “The capital of France is”, where “ Paris” is pushed up.

Widen, bend, narrow

The second half of every transformer block is a small neural network called the (multi-layer perceptron, also called the feed-forward layer). It works on one token’s vector at a time. In GPT-2:

  1. Up-project. Multiply the 768-number vector by a matrix WupW_{\text{up}} (768 × 3072). Now there are 3072 numbers, four times as many. Each one is a neuron: the dot product of the input with one column of WupW_{\text{up}}.
  2. Activate. Apply an to each of the 3072 numbers separately.
  3. Down-project. Multiply by WdownW_{\text{down}} (3072 × 768) to get back to 768 numbers, which are added to the residual stream.
MLP⁡(x)=GELU⁡(xWup+b1) Wdown+b2\operatorname{MLP}(x) = \operatorname{GELU}(x W_{\text{up}} + b_1)\, W_{\text{down}} + b_2

The activation is essential. Two matrix multiplications in a row are just one matrix multiplication (xW1W2=x(W1W2)x W_1 W_2 = x (W_1 W_2)), so without the bend in the middle the MLP could only compute a linear map. The activation lets each neuron act as a switch: on for some inputs, off for others.

ReLU0.000
GELU−0.170
SiLU−0.248
GELU is used in GPT-2, BERT. A smooth ReLU: it multiplies the input by the probability that a standard normal is below it. Small negative inputs leak through slightly (at −0.8 it gives −0.170), so the gradient is never exactly zero there.
Three activation functions. Each one is applied to every hidden number separately. Drag the slider (or the chart) to probe a value; pick a curve to read about it.
  • ReLU (max⁡(0,x)\max(0, x)) was the default for years. It is exactly zero for every negative input, so a neuron that is always negative gets no gradient and can stop learning.
  • GELU (Hendrycks and Gimpel, 2016) is a smooth ReLU used by GPT-2 and BERT: slightly negative inputs leak through a little.
  • SiLU (x⋅σ(x)x \cdot \sigma(x), also called Swish) looks almost the same and is what Llama-family models use, inside SwiGLU.

Step through one token

token:
input x
x1 × 4
thingact.smallfirstcat1.00.10.00.0
One token’s vector, here “cat” from the toy model: 4 numbers. The MLP works on each token on its own; it never looks at other tokens.
A toy MLP with model width 4 and hidden width 16 (the usual 4× expansion), on one token of “The cat sat on the”. Weights are random but fixed. Press play, or switch to SwiGLU.

Notice that the MLP never looks at other tokens. Change the token and only that token’s row is computed. All the movement of information between positions happens in attention; the MLP’s job is to process what has arrived.

SwiGLU: a gate

Most models since 2023 use (Shazeer, 2020) instead of a plain activation. It makes two projections of the input: a gate and an up vector. The gate goes through SiLU and is multiplied, number by number, with the up vector:

SwiGLU⁡(x)=(SiLU⁡(xWgate)⊙xWup) Wdown\operatorname{SwiGLU}(x) = \big(\operatorname{SiLU}(x W_{\text{gate}}) \odot x W_{\text{up}}\big)\, W_{\text{down}}

Switch the widget above to SwiGLU to see it. The gate decides how open each neuron is; the up vector decides what flows through. It costs a third matrix, so models shrink the hidden width from 4d to about 8/3·d to keep the parameter count the same (Llama 3 8B: d = 4096, hidden 14,336 ≈ 3.5d). In Shazeer’s experiments it gave a consistently lower loss for the same compute, and nobody has fully explained why.

Neurons as memories

Geva et al., 2021 suggested a clean way to read an MLP. Look at one neuron ii:

  • its column of WupW_{\text{up}} is a key: the neuron fires when the input points in that direction;
  • its row of WdownW_{\text{down}} is a value: when the neuron fires, that vector is added to the residual stream, scaled by how strongly it fired.

So the MLP is a big soft lookup table: thousands of “if the input looks like this, add that” rules, all checked at once. They found real neurons, in a trained language model, whose keys match human-readable patterns (text ending in a particular word, a topic like TV shows) and whose values push up the words that tend to come next. Here is a toy version with five readable neurons:

input features: a countryhome or furniturecountinga person’s nameends in “the”
neurons: key match → activation
capital cities1.0
places in a room0.0
next number0.0
people0.0
a noun comes next0.4
values added → next-word probability
·Paris65%
·France16%
·floor3%
·mat3%
·four3%
·Mary3%
·her3%
·a5%
“capital cities” fires the most (1.0): its key matches the input. Its value adds to the scores of the words it stores, so “Paris” comes out on top at 65%. Switch it off and see what is left.
activation = max(0, key · input)probability = softmax(Σ activation × value)
A toy MLP layer with five readable neurons. Each neuron’s key is a pattern it looks for in the input; its value is the set of words it pushes up when it fires. Pick a sentence, then switch neurons off to see what they were contributing.

Real neurons are rarely this tidy. Most respond to several unrelated patterns at once (they are polysemantic), because the model has many more facts to store than it has neurons; see interpretability. But the key → value picture is a good first model, and it explains why editing a single MLP layer can change a fact the model “knows” (Meng et al., 2022).

example · a reference librarian

You ask a librarian “who wrote the book with the white whale?”. They don’t consult the other people in the queue. Your question matches a few things they know (whales, novels, the 1850s), those memories light up, and they hand back a note that says “Melville”. That is the MLP: one token’s vector in, a few matching memories activated, their stored answers added together and handed back.

example · capitals in GPT-2

Research on factual recall (Meng et al., 2022) found that the MLPs in the early-to-middle layers, working at the last token of the subject (here “ France”), do much of the work of recalling facts about it: neurons whose keys match “a country” fire, and their values add what the model knows about it, such as its capital. Later, attention moves that information to the final position, “ is”, where the output is read. (The neuron names here are an illustration; real neurons are messier.) In the logit lens you can watch “ France” and “ Paris” rise into GPT-2’s top guesses at the last position by layers 10 and 11.

Most of the parameters

The MLP is also where most of the weights are. Per block, attention has four d×dd \times d matrices (4d24d^2 weights) and a GPT-2 MLP has two d×4dd \times 4d ones (8d28d^2). So the MLP holds two thirds of every block:

46%23%32%
MLPs: 56.7Mattention: 28.3Membeddings (tied): 39.4MLayerNorms: 38.4K
total124.4M
MLP share of the blocks67%
MLP share of everything46%
Inside each GPT-2 block the MLP has 8d² weights (two 768 × 3072 matrices) and attention has 4d² (four 768 × 768), so the MLP holds two thirds of every block. The big embedding table pulls its share of the whole model down to 46%.
Where the parameters are, counted exactly from each model’s configuration.
Attention per blockMLP per blockMLP share of blocksMLP share of whole model
GPT-2 small2.36M4.72M67%46%
Llama 3 8B41.9M176.2M81%70%

Llama’s share is higher because it uses grouped-query attention (only 8 key–value heads, which shrinks WKW_K and WVW_V; see efficient attention) and a wide SwiGLU. This is also why mixture-of-experts models split the MLP, not attention: it is the biggest part, and each token only needs some of its memories.

going deeper

In code

class MLP(nn.Module):                  # GPT-2
    def __init__(self, d):
        super().__init__()
        self.up = nn.Linear(d, 4 * d)
        self.down = nn.Linear(4 * d, d)
    def forward(self, x):
        return self.down(F.gelu(self.up(x), approximate="tanh"))
 
class SwiGLU(nn.Module):               # Llama
    def __init__(self, d, hidden):
        super().__init__()
        self.gate = nn.Linear(d, hidden, bias=False)
        self.up = nn.Linear(d, hidden, bias=False)
        self.down = nn.Linear(hidden, d, bias=False)
    def forward(self, x):
        return self.down(F.silu(self.gate(x)) * self.up(x))

Formulas

  • GELU⁡(x)=x Φ(x)≈0.5x(1+tanh⁡(2/π (x+0.044715x3)))\operatorname{GELU}(x) = x\,\Phi(x) \approx 0.5x\big(1 + \tanh(\sqrt{2/\pi}\,(x + 0.044715x^3))\big), where Φ\Phi is the standard normal CDF.
  • SiLU⁡(x)=x σ(x)=x/(1+e−x)\operatorname{SiLU}(x) = x\,\sigma(x) = x / (1 + e^{-x}).

Parameter counts

GPT-2 small MLP per block: 2×768×3072+3072+768=4,722,4322 \times 768 \times 3072 + 3072 + 768 = 4{,}722{,}432. Llama 3 8B per block: 3×4096×14,336=176,160,7683 \times 4096 \times 14{,}336 = 176{,}160{,}768.

In papers

Related

  • residual-stream - Every token carries one vector up through the model. Each attention layer and each MLP reads that vector, computes something, and adds the result back. Nothing is overwritten. This running sum is the residual stream, and it is why deep transformers can be trained at all.
  • the-block - Put the pieces together: LayerNorm, attention, add; LayerNorm, MLP, add. That is one transformer block, and a model is a stack of identical blocks between an embedding table and an output layer. Here is one block computed number by number, and an exact parameter count for real models.
  • normalization - Before each attention layer and each MLP, the model rescales the token’s vector to a standard size. LayerNorm subtracts the mean and divides by the standard deviation; RMSNorm just divides by the root mean square. Where the norm sits, before the layer or after it, decides whether a deep model trains.
  • unembedding - At the top of the stack, the last token’s vector is normalised and multiplied by one big matrix that gives a score to every token in the vocabulary. Softmax turns the scores into probabilities. Applying the same read-out to the middle layers, the logit lens, shows the prediction taking shape.