AI Grimoire
Sheet
statusstandard
difficultyintroductory
timeO(n·d²)
described2020
revised12d ago

SwiGLU

The feed-forward block, with its single ReLU replaced by a multiplicative gate. Three matrices instead of two, and the width shrunk by ⅔ to pay for it.

[ffn][core]Current standard

Standing

Current standardPart of the default recipe. Present in most frontier models trained today, and the thing a new design departs from rather than argues for.

The default feed-forward block. Gating is settled; which activation gates is not, and barely matters.

judged as of 2026-09 · what the labels mean

Theory

The original transformer’s feed-forward block is two matrices with a pointwise nonlinearity between them. A gated linear unit splits the first projection in two and multiplies one branch, passed through an activation, into the other.

SwiGLU(x)=(Swishβ(xW1)xW3)W2,Swishβ(z)=zσ(βz)\mathrm{SwiGLU}(x) = \bigl( \mathrm{Swish}_\beta(x W_1) \odot x W_3 \bigr) W_2, \qquad \mathrm{Swish}_\beta(z) = z\,\sigma(\beta z)
eq. 1 — Swish/SiLU gate, elementwise product

At β=1\beta = 1 the activation is SiLU, which is what every released implementation uses; β\beta is almost never learned.

Paying for the third matrix

A gated block has three weight matrices where the classic block has two, so holding the parameter count fixed against a 4d4d classic block means shrinking the hidden width by a factor of 23\tfrac{2}{3}.

2ddff  =  3ddffdff=23dff=83d2 \cdot d \cdot d_{\text{ff}} \;=\; 3 \cdot d \cdot d_{\text{ff}}' \quad\Longrightarrow\quad d_{\text{ff}}' = \tfrac{2}{3} d_{\text{ff}} = \tfrac{8}{3} d
eq. 2 — parameters held equal

In practice dffd_{\text{ff}}' is rounded to a multiple of 128 or 256 so the matmul tiles evenly; the ratio you see in a config file is rarely exactly 8/38/3 for that reason alone.

Shazeer’s own verdict on why gating helps is worth quoting for its honesty: the variants are offered “divine benevolence” as the explanation. The empirical result has held up across a decade of models regardless.

Implementation

python · torch ≥ 2.1
import torch.nn.functional as F
from torch import Tensor, nn


class SwiGLU(nn.Module):
    def __init__(self, d_model: int, multiple_of: int = 256):
        super().__init__()
        hidden = int(2 / 3 * 4 * d_model)
        # round up so the matmul tiles evenly
        hidden = multiple_of * ((hidden + multiple_of - 1) // multiple_of)

        self.gate = nn.Linear(d_model, hidden, bias=False)   # W1
        self.up = nn.Linear(d_model, hidden, bias=False)     # W3
        self.down = nn.Linear(hidden, d_model, bias=False)   # W2

    def forward(self, x: Tensor) -> Tensor:
        return self.down(F.silu(self.gate(x)) * self.up(x))

Fusing gate and up into one nn.Linear(d_model, 2 * hidden) and splitting the result is worth a few percent of step time, at the cost of a checkpoint layout that no longer matches anyone else’s. Biases are omitted throughout, as they are in every modern decoder.

Related

References

[1]Shazeer — GLU Variants Improve Transformer (2020)arXiv:2002.05202
[2]Dauphin et al. — Language Modeling with Gated Convolutional Networks (2016)arXiv:1612.08083
[3]Ramachandran et al. — Searching for Activation Functions (2017)arXiv:1710.05941