Skip to content
AI360Xpert
Core ML

SwiGLU

SwiGLU swaps a plain feed-forward activation for a gated one: one projection decides how much of a second projections output actually gets through it.

The input is projected twice — one branch runs through Swish and gates, elementwise, the other branch's output — before a final projection produces the result
The input is projected twice — one branch runs through Swish and gates, elementwise, the other branch's output — before a final projection produces the result

Why Does This Exist?

A feed-forward network inside a transformer block is small on purpose: expand a token's representation, run it through a nonlinearity, project it back down. For years that nonlinearity was ReLU, then GELU — a single activation function applied elementwise to one projection, no exceptions. It worked, but "one projection, one squashing function" is a narrow way to transform information. The network gets to decide how much to squash a value, but it never gets to decide, per dimension, whether that value should pass at all based on some other signal computed from the same input.

Gating fixes that narrowness. Instead of one projection running through an activation, you compute two projections from the same input and multiply them together, elementwise, so one acts as a learned, per-dimension valve on the other. That idea predates transformers by years — LSTMs are built entirely out of gates — but applying it inside a feed-forward block was a smaller, later step. SwiGLU is that step: keep the expand-then-contract shape of a normal FFN, but make the expansion gated instead of plain. Empirically, it beats plain ReLU and GELU FFNs at the same parameter count by a small but consistent margin, and "small but consistent margin, at no real extra cost" is exactly the kind of change that spreads through the field fast. Most large language models built after roughly 2022 use it.

Think of It Like This

A valve on a pipe, controlled by a second pipe

Picture two parallel pipes carrying the same fluid from the same source, one running through a valve that a second pipe's flow rate controls. The second pipe doesn't add anything to what comes out — it decides how much of the first pipe's flow is allowed through, moment to moment, dimension by dimension.

That's the gate in SwiGLU. One projection of the input runs through Swish and becomes the valve setting; a second, separate projection of the same input is the fluid. Multiply them elementwise and you get a flow that's shaped by two different views of the same input, rather than one view squashed by a fixed function.

How It Actually Works

The two branches

SwiGLU projects the same input xx through three separate learned weight matrices, W1W_1, W2W_2, W3W_3, and combines them as:

SwiGLU(x)=(Swish(xW1)xW3)W2\text{SwiGLU}(x) = \big(\text{Swish}(xW_1) \odot xW_3\big)W_2

xW1xW_1 and xW3xW_3 are both linear projections of the identical input xx, into the same expanded dimension — the diagram's two parallel boxes. Swish(xW1)\text{Swish}(xW_1) applies the Swish activation, swish(z)=zσ(z)\text{swish}(z) = z \cdot \sigma(z), to the first branch only; the second branch, xW3xW_3, stays a plain linear projection with no nonlinearity of its own.

The gate

\odot is elementwise multiplication. Each dimension of Swish(xW1)\text{Swish}(xW_1) multiplies the matching dimension of xW3xW_3 — the "gate", output by the Swish branch, scales the "value" carried by the linear branch, dimension by dimension. A dimension where the gate is near zero effectively shuts that value off; a dimension where the gate is near one lets it through mostly unchanged. Crucially, the gate itself is learned and input-dependent — it isn't a fixed function like plain ReLU, it's a whole separate projection of xx that the network trains to decide what matters.

Back down to model width

The gated product still lives in the expanded dimension, so W2W_2 projects it back down to the model's working width — exactly the contraction step an ordinary FFN ends with. Nothing about the surrounding transformer architecture changes: SwiGLU drops into the same slot a ReLU or GELU FFN occupied, same input shape in, same output shape out. Only the internal computation between those two shapes gets richer.

Why Swish, specifically

Swish is smooth and non-monotonic near zero — unlike ReLU, which is flat at exactly zero for any negative input, Swish dips slightly negative before rising, which keeps a small, non-zero gradient flowing even for inputs a plain ReLU would kill outright. That smoothness is why the gate uses Swish rather than a harder function: a gate that can vary continuously, rather than snapping fully open or fully shut, gives training a smoother surface to optimize over.

Show Me the Code

A minimal SwiGLU feed-forward block: two parallel projections, one gated, then a contraction back to the input width.

import numpy as np

def swish(x: np.ndarray) -> np.ndarray:    return x / (1.0 + np.exp(-x))

def swiglu_ffn(    x: np.ndarray, w1: np.ndarray, w2: np.ndarray, w3: np.ndarray) -> np.ndarray:    gate = swish(x @ w1)      # (n, d_ff) — the Swish-activated branch    value = x @ w3             # (n, d_ff) — the plain linear branch    return (gate * value) @ w2  # elementwise gate, then project back to d_model

rng = np.random.default_rng(0)x = rng.normal(size=(2, 8))                        # 2 tokens, model width 8w1 = rng.normal(scale=0.3, size=(8, 16))            # expand to d_ff = 16w3 = rng.normal(scale=0.3, size=(8, 16))w2 = rng.normal(scale=0.3, size=(16, 8))            # contract back to d_model = 8out = swiglu_ffn(x, w1, w2, w3)print(out.shape)              # -> (2, 8) — same shape as the input, as any FFN preservesprint(round(float(out[0, 0]), 4))  # -> -0.1005

Two independent projections go in at width 16, multiply elementwise, then collapse back to width 8 — the shape never leaves the ordinary FFN contract.

Watch Out For

Forgetting SwiGLU needs three weight matrices, not two

A plain FFN has two weight matrices: one to expand, one to contract. SwiGLU needs three — W1W_1 and W3W_3 both expand the same input, and only then does W2W_2 contract. Porting a plain-FFN checkpoint's shapes directly, or budgeting parameters as if SwiGLU only added an activation function, undercounts the actual parameter cost. In practice, models compensate by shrinking dffd_\text{ff} somewhat so total FFN parameters land close to a comparable plain-FFN model, rather than paying for a third full-width matrix on top of the usual two.

Assuming the gate branch and the value branch are interchangeable

xW1xW_1 (through Swish) and xW3xW_3 (linear) play genuinely different roles even though both start as a plain matrix multiply on the same input. Swap which one gets the activation and the computation is no longer the same function — the network learns different weights for "decide how much passes" versus "carry the content that passes." If you're implementing from scratch, keep the two branches labeled distinctly rather than treating them as symmetric.

The Quick Version

  • SwiGLU replaces a plain FFN activation with a gated one: two projections of the same input, one run through Swish, multiplied elementwise.
  • The formula is (Swish(xW1)xW3)W2(\text{Swish}(xW_1) \odot xW_3)W_2 — expand twice, gate, contract once.
  • The gate is learned and input-dependent, unlike a fixed activation like ReLU or GELU.
  • SwiGLU needs three weight matrices instead of a plain FFN's two, which models usually offset with a smaller expansion width.
  • It drops into the same input-shape-in, output-shape-out slot as any other FFN variant, so nothing else in the block changes.

Related concepts