Feed-Forward Networks
Expand every position to a wider hidden dimension, apply a nonlinearity, then project back down — the transformer's second sub-layer, working on each position entirely on its own.
Why Does This Exist?
Self-attention is, on its own, a surprisingly limited operation: it's fundamentally a weighted average of value vectors. Weighted averages, however cleverly weighted, are still linear combinations of existing information — attention can route information between positions, deciding which tokens' values to blend and by how much, but it can't transform a single position's content into something qualitatively new. A layer built purely from attention, stacked repeatedly, is still mostly rearranging and re-weighting the same information over and over.
The transformer block's second sub-layer exists to do the part attention structurally can't: take each position's representation, on its own, and pass it through a genuine nonlinear transformation. Attention decides what to gather from where; the feed-forward network decides what to do with what's been gathered.
Think of It Like This
A researcher who gathers sources, then thinks alone
Self-attention is a researcher walking around a room, listening to several colleagues, and building a weighted synthesis of what they said — a blend, however clever, of existing opinions. The feed-forward network is the same researcher then going back to their own desk, closing the door, and thinking hard about that synthesis alone, transforming it into a genuinely new conclusion nobody in the room said outright.
One step is about gathering the right inputs from the right people. The other is about doing real, private cognitive work on what was gathered. A transformer block needs both, in sequence, every single layer.
How It Actually Works
Up-project, activate, down-project
The feed-forward network is applied identically and independently at every position in the sequence — no position influences another inside this sub-layer, which is the exact opposite of what attention just did. For a token representation of dimension :
projects up from dimension to a wider hidden dimension — commonly , as shown in the diagram — is a nonlinear activation function applied elementwise, and projects back down to . The widening isn't incidental: a narrow bottleneck limits how much the nonlinearity can actually reshape the representation, so the hidden layer is deliberately given more room than the input or output need, purely as working space for the transformation.
Why "position-wise" is the load-bearing detail
The same , , , are reused at every position in the sequence — one token's feed-forward computation never reads another token's representation. That's a deliberate division of labor within the block: mixing information across positions is entirely attention's job; transforming information within a position is entirely the feed-forward network's job. Splitting those two responsibilities into two separate sub-layers, rather than one sub-layer doing both, is part of why the transformer block generalizes as well as it does — each sub-layer has one clear job.
A useful way to think about what it stores
One interpretation, borne out by later interpretability work, treats the feed-forward layer as a large key-value memory: rows of act like keys that detect specific patterns in the input, and the corresponding rows of act like values that get added to the output when that pattern fires. Under this view, a large fraction of what a transformer "knows" — factual associations, in particular — lives in the weights of its feed-forward layers rather than in attention, which is more about deciding what to look at than what to remember. This isn't the only lens, but it's a genuinely useful one for reasoning about where a transformer's raw parameter count actually goes.
The activation function detail worth knowing
Modern transformers largely favor gated variants like SwiGLU over a plain ReLU or GELU feed-forward network: instead of one up-projection followed by an activation, a gated FFN computes two up-projections and multiplies one, passed through an activation, elementwise against the other, before the down-projection. That's a separate design refinement with its own page's worth of detail, but the shape of the sub-layer — up, transform, down, applied per position — stays the same regardless of which specific activation sits inside it.
Show Me the Code
The up-project, activate, down-project sequence, applied identically to every row of a batch of token representations.
import numpy as np
def gelu(x: np.ndarray) -> np.ndarray: return 0.5 * x * (1.0 + np.tanh(np.sqrt(2.0 / np.pi) * (x + 0.044715 * x ** 3)))
def feed_forward(x: np.ndarray, w1: np.ndarray, b1: np.ndarray, w2: np.ndarray, b2: np.ndarray) -> np.ndarray: hidden = gelu(x @ w1 + b1) # (n, d) -> (n, 4d), same W1 applied to every row return hidden @ w2 + b2 # (n, 4d) -> (n, d), same W2 applied to every row
rng = np.random.default_rng(0)x = rng.normal(size=(4, 8)) # 4 tokens, dim 8w1, b1 = rng.normal(scale=0.3, size=(8, 32)), np.zeros(32) # up-project to 4xw2, b2 = rng.normal(scale=0.3, size=(32, 8)), np.zeros(8) # down-project back to 8out = feed_forward(x, w1, b1, w2, b2)print(out.shape) # -> (4, 8) — same shape in and out, one row transformed independently of the restChange row 2 of x and only row 2 of out changes — there's no matrix in this function that lets one row's computation see another row's input, which is exactly the "position-wise" claim made concrete.
Watch Out For
Assuming the feed-forward layer mixes information across positions
Because the feed-forward network sits right after attention in the block, it's easy to assume some cross-position mixing happens there too. It doesn't, by construction — and are applied identically to every row of the input matrix, with each row computed entirely independently of every other row. Any cross-position mixing in a transformer block comes exclusively from the attention sub-layer; conflating the two sub-layers' jobs leads to a wrong mental model of where information actually flows between tokens.
Underestimating how much of the parameter budget this sub-layer owns
Because attention gets most of the conceptual attention (deliberately), it's easy to underestimate that the feed-forward sub-layer typically holds the majority of a transformer's parameters — the and matrices are larger than the attention projections at the same hidden dimension. When budgeting compute or memory for a custom transformer, or when reasoning about where quantization or pruning will have the biggest effect, the feed-forward layers are usually the larger target, not attention.
The Quick Version
- Self-attention routes information between positions via weighted averaging; it can't transform a single position's content into something new.
- The feed-forward network up-projects each position to a wider hidden dimension, applies a nonlinearity, and projects back down.
- The same weights apply identically and independently to every position — no cross-position mixing happens in this sub-layer.
- One useful interpretation treats it as a key-value memory, where much of a transformer's factual knowledge is thought to live.
- It typically holds the majority of a transformer's total parameters, more than the attention sub-layer at the same dimension.
What to Read Next
- Transformer Architecture is where the feed-forward network sits as the second of two sub-layers in every block.
- Self-Attention is the first sub-layer, responsible for the cross-position mixing this page's layer doesn't do.
- Pre-Norm vs Post-Norm covers how this sub-layer is wrapped with normalization and a residual connection inside the block.
- Multi-Layer Perceptron is the general architecture this page's up-project-activate-down-project shape is a specific instance of.