Skip to content
ginsberg.si

Transformers from the Weights Up/Part 3 of 20

Transformers from the Weights Up, Part 3: Activation functions and nonlinearity

Why a stack of linear maps collapses, and how sigmoid, ReLU, GELU, and SwiGLU keep depth.

2 Nov 2026

Composition of affine maps is affine. Worse than useless at scale: Llama-3-8B's up_proj [14336, 4096] followed by down_proj [4096, 14336] with nothing in between would be 117.4M weights computing a single 4096 × 4096 matrix (16.8M weights). Insert an elementwise nonlinearity between them and the collapse is gone.

Image 3 is the picture this lesson starts from: a weighted sum, then a sigmoid.

IMAGE 03z = Σ wᵢxᵢ + b0.88σy = σ(z)0.7070.88zσ(z)
Image 3. The same weighted sum, passed through a sigmoid: σ(0.88) ≈ 0.707.
(l) Two layers without a nonlinearity are one layer
input grid x
W₁
[[1.1, 0.7], [−0.6, 0.9]], b₁ = (0.2, −0.3)
W₂
[[0.9, −0.8], [0.6, 0.7]]
W₂W₁ (the collapse)
[[1.47, -0.09], [0.24, 1.05]]
W₂b₁
(0.42, -0.09)
Probe point (1, 1) now at
(1.00, 1.00)

Linear mode: the final grid is exactly W₂W₁x + W₂b₁. Two 2×2 layers bought nothing a single 2×2 layer could not do.

Toy 2-D weights. Lines of the input grid are colored by direction. With no activation, every stage keeps lines straight and parallel, and the end result equals the single matrix shown. Switch on ReLU: the half-planes where a coordinate goes negative are flattened onto the axes, the grid folds, and no single matrix can reproduce the result.

The activation zoo

FunctionDefinitionShape factsWhere you meet it
sigmoid σ1 / (1 + e^−z)Range (0, 1); σ′ = σ(1 − σ) ≤ 0.25, maximal at z = 01980s–2000s MLPs, LSTM gates, the original GLU gate; today mainly inside SiLU
tanh(e^z − e^−z) / (e^z + e^−z)Range (−1, 1), zero-centred; tanh′ ≤ 1; tanh(z) = 2σ(2z) − 1RNNs; the fast approximation of GELU
ReLUmax(0, z)Gradient exactly 0 or 1; outputs are sparse; a unit whose input is always negative never updates again ("dead")The original Transformer FFN (2017), most CNNs
GELUz · Φ(z), Φ = standard normal CDFSmooth, non-monotonic; minimum ≈ −0.170 at z ≈ −0.752BERT, GPT-2
SiLU / Swishz · σ(z)Smooth, non-monotonic; minimum ≈ −0.278 at z ≈ −1.278; → z for large zThe gate in SwiGLU: Llama, Mistral, Qwen (hidden_act "silu")

GELU and SiLU are both "the input times a soft on/off probability": Φ(z) and σ(z) are similar S-curves, and σ(1.702z) is a classic close approximation to Φ(z). Many implementations of GELU use the tanh form below, which differs from the exact z·Φ(z) by at most about 4.7 × 10⁻⁴ (checked numerically over [−6, 6]). That gap is why checkpoints record which variant they were trained with (gelu vs gelu_new / gelu_pytorch_tanh in Hugging Face configs).

(m) The activation zoo, value and slope
-10123-4-2024
z
0.00
sigmoid
f = 0.500, f′ = 0.250
ReLU
f = 0.000, f′ = 0.000
GELU
f = 0.000, f′ = 0.500
SiLU (Swish)
f = 0.000, f′ = 0.500

All curves computed live from their definitions (GELU uses the exact z·Φ(z)). The marker sweeps z; the table reads each function and its derivative there. In slope view, note sigmoid's ceiling of 0.25, ReLU's hard 0/1 step, and that GELU and SiLU overshoot 1 slightly and stay nonzero for negative z.

Saturation and the vanishing gradient

Backprop (Lesson 6) multiplies local derivatives along the path from the loss to each weight. Every elementwise activation contributes a diagonal Jacobian, diag(f′(z)), at every layer it passes through. For sigmoid that factor is at most 0.25 and far smaller once |z| grows, so the signal reaching early layers shrinks geometrically. Ten sigmoid layers at the very best cap the gradient at 0.25¹⁰ ≈ 9.5 × 10⁻⁷ of what it was; in practice the weights' own factors and saturated units make it worse or erratic.

(n) Why sigmoid stacks stopped training: gradients through depth
11e-41e-81e-121e-161e-201e-24loss end24 layers back
sigmoid, best case (σ′ = 0.25 every layer)
after 24 layers: 3.55e-15
sigmoid at |z| = 2 (σ′ ≈ 0.105)
after 24 layers: 3.22e-24
tanh at |z| = 1 (tanh′ ≈ 0.420)
after 24 layers: 9.06e-10
ReLU on an active path, or the residual identity path
after 24 layers: 1.00e+0

The activation's slope multiplies the backward signal once per layer, so these curves are g^k on a log scale. This deliberately ignores the weight matrices, which contribute their own factors; careful initialization can offset some shrinkage, but no init fixes sigmoid's hard 0.25 ceiling when units saturate. Residual connections (Lesson 11) add an identity path whose factor is exactly 1.

What nonlinearity buys: folding space

A ReLU layer partitions input space into regions by the sign pattern of W₁x + b₁ and is affine inside each region. More units mean more kinks, and enough kinks fit any continuous function on a bounded domain to any tolerance (the universal approximation theorem, Cybenko 1989 for sigmoids, Hornik 1991 more generally). The theorem says nothing about how many units you need or whether gradient descent will find them; depth helps because each layer can fold the regions created by the previous one.

(o) What nonlinearity buys: a sum of ReLUs bends to fit any curve
Hidden units (kinks)
6
Parameters (W₁, b₁, W₂, b₂)
19
Max |error| on [−3, 3]
0.2325

Constructed, not trained: unit i is ReLU(x − kᵢ) (a row of W₁ with weight 1 and bias −kᵢ), scaled by cᵢ (a column of W₂). Each unit adds one kink, so one hidden layer of N units on a scalar input gives at most N + 1 linear pieces. Target is sin(1.6x) + 0.25x on [−3, 3].

Gating: from GLU to SwiGLU

A gated linear unit (Dauphin et al., 2016) computes two projections of the same input and multiplies them, using a squashed version of one as a per-unit valve on the other. Shazeer (2020) swapped in different gate functions inside the transformer FFN and found the gated variants beat the ReLU and GELU baselines at equal parameter count. The Swish (SiLU) version became the default. His paper's explanation, verbatim: "We offer no explanation as to why these architectures seem to work; we attribute their success, as all else, to divine benevolence."

Classic FFN (Vaswani et al., 2017)

Two matrices: [d, d_ff] and [d_ff, d], with d_ff = 4d.

SwiGLU FFN (Shazeer, 2020; used by Llama)

Three matrices, no biases. d_ff shrinks by 2/3 to hold parameters constant.

(p) SwiGLU, one position, step by step
x [4]0.800-0.5000.3001.000W_gateW_upgate [6]······up [6]······SiLUSiLU(gate)······⊙ uph [6]······W_downout [4]····

Toy weights (made up for illustration, d = 4, d_ff = 6); the arithmetic is real and computed live. Column order matches Llama's code: w1 = gate_proj, w3 = up_proj, w2 = down_proj, and forward is w2(silu(w1(x)) * w3(x)).

Why a gate helps, as intuition rather than proof: the output of hidden unit i is now a product of two learned linear features of x, so the FFN computes quadratic-style interactions that a ReLU unit (one feature, clipped) cannot. The gate also gives a smooth, nonzero gradient for slightly negative inputs, so units are much less prone to dying.

(q) Three matrices instead of two: what SwiGLU costs
SwiGLU as shipped in Llama-3-8Bgateupdown
d_ff
14,336
FFN weights per layer
3 × 4096 × 14,336 = 176,160,768
Forward FLOPs per token per layer
352.3 MFLOP

Per layer, d = 4096. The 2/3 factor keeps SwiGLU's parameters equal to a 4d ReLU FFN (3 · d · 8d/3 = 8d² = 2 · d · 4d). Llama 3 then multiplies by ffn_dim_multiplier 1.3 and rounds up to a multiple of multiple_of, which lands on 14336.

That is the code path in Meta's reference model.py: hidden_dim = int(2 · hidden_dim / 3), then multiplied by ffn_dim_multiplier, then rounded up to a multiple of multiple_of. The result: 3 × 4096 × 14336 = 176,160,768 FFN weights per layer, 80.8% of the block's 218.1M from Lesson 2.

Engineering consequences you will see later

  1. 1

    FLOP-free, not byte-free

    SiLU and ⊙ are a few operations per element of a [T, 14336] tensor, negligible next to the matmuls. But a naive implementation writes gate and up to memory and reads them back, so fast kernels fuse SiLU(gate) ⊙ up into one pass, or into the matmul epilogue.

  2. 2

    Gate and up can be one matmul

    They read the same input, so many engines concatenate gate_proj and up_proj into a single [2 · 14336, 4096] weight and split the output. llama.cpp's GGUF keeps them as separate ffn_gate and ffn_up tensors; vLLM merges them at load time.

  3. 3

    Products make outliers

    Multiplying two projections can produce occasional very large values in down_proj's 14336-wide input. Fishman et al. (2024) traced FP8 training instabilities on trillion-token runs to this SwiGLU amplification. It is one reason quantization recipes handle ffn_down carefully (the Q4_K_M mix in Lesson 1 promotes some ffn_down tensors to Q6_K; Lessons 16–17 revisit).

  4. 4

    Activations are where sparsity lives

    After a ReLU, many hidden units are exactly zero for a given token; after SiLU they are merely small. That difference matters for interpretability (Lesson 5's key–value view) and for sparsity-exploiting inference tricks.

Lesson 3: key numbers

0.25
max slope of sigmoidσ′(0) = σ(0)(1 − σ(0))
9.5e-7
0.25¹⁰best-case gradient through 10 sigmoids
−0.278
SiLU minimumat z ≈ −1.278
−0.170
GELU minimumat z ≈ −0.752
3
FFN matrices with SwiGLUgate, up, down; no biases
⅔
d_ff shrink for equal params3 · d · 8d/3 = 2 · d · 4d
14336
Llama-3-8B d_ff⌊⅔·16384⌋ × 1.3, rounded up
176.2M
FFN weights per layer80.8% of the 218.1M block

Lesson 3: self-check

1. Remove the SiLU and the gate from a Llama-3-8B FFN, leaving up_proj then down_proj. What does it compute, and how many weights would it really need?

Answer. A single linear map W_down·W_up, a 4096 × 4096 matrix (rank at most 4096): 16,777,216 weights instead of 117,440,512. The hidden width buys nothing without a nonlinearity.

2. Ignoring weights, what is the largest factor a gradient can keep after passing back through 20 sigmoid activations?

Answer. 0.25²⁰ ≈ 9.1 × 10⁻¹³. Saturated units (|z| of a few) make it orders of magnitude smaller still.

3. Why does SwiGLU use d_ff = ⅔ · 4d in the parameter-matched comparison?

Answer. It has three d × d_ff matrices instead of two. Equal parameters means 3 · d · d_ff = 2 · d · 4d, so d_ff = 8d/3 = ⅔ · 4d.

4. Can a SiLU unit die the way a ReLU unit can? Why or why not?

Answer. Not in the same way. SiLU′(z) = σ(z)(1 + z(1 − σ(z))) is nonzero for all finite negative z (it is exactly zero only near z ≈ −1.278, the minimum), so a unit with negative inputs still receives gradient, though it decays for very negative z.

5. A one-hidden-layer ReLU net on a scalar input has 10 hidden units. At most how many linear pieces does its output have?

Answer. 11. Each unit contributes at most one kink, at x = −bᵢ/wᵢ.

6. In Llama's code, which of w1, w2, w3 goes through SiLU, and which Hugging Face tensor names are they?

Answer. w1 goes through SiLU: w2(silu(w1(x)) * w3(x)). w1 = gate_proj, w3 = up_proj, w2 = down_proj.

Lesson 3: resources