Gated Linear Units › GLU in the Feed-Forward Network

Michael BrenndoerferUpdated June 14, 20253 min read
Language AI HandbookAll chapters
Part 15 of 67Transformer Blocks
Chapter 109Gated Linear Units › GLU in the Feed-Forward Network
Reading section 4 of 16 · GLU in the Feed-Forward Network · 3 minSection 4 of 16 · 3 min

Book contents

Section 4 of 16 · 3 min read

Gated Linear Units › GLU in the Feed-Forward NetworkOne section · Sections in this chapter4 / 16 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

With the GLU mechanism understood, our next step is integrating it into the transformer's feed-forward network. This requires adapting the standard two-layer FFN architecture, and understanding this integration reveals why GLU is such a natural fit for transformers.

Recalling the Standard FFN

The standard FFN applies a simple pattern: expand the dimension, apply nonlinearity, contract back:

FFN(x)=σ(xW1+b1)W2+b2\text{FFN}(x) = \sigma(xW_1 + b_1)W_2 + b_2

where xx is the input, W1W_1 and W2W_2 are weight matrices, b1b_1 and b2b_2 are bias vectors, and σ\sigma is a nonlinear activation function (e.g., ReLU or GELU).

The key insight is that the nonlinearity σ\sigma is applied after the expansion. This creates a high-dimensional feature space where the nonlinearity can carve out complex decision boundaries, before projecting back to the original dimension.

Replacing Activation with Gating

GLU replaces the simple activation function with something more sophisticated: a learned gating mechanism. Instead of uniformly transforming all features with the same nonlinearity, GLU lets the network selectively amplify or suppress features based on the input itself:

FFNGLU(x)=[(xW+b)⊗σ(xV+c)]W2+b2\text{FFN}_{\text{GLU}}(x) = \left[(xW + b) \otimes \sigma(xV + c)\right] W_2 + b_2

Let's trace through what each component does in this context:

  1. Expansion to Hidden Space: Both xWxW and xVxV project the input from dmodeld_{\text{model}} to the larger hidden dimension dffd_{ff}
  2. Gated Combination: The sigmoid-gated multiplication produces a dffd_{ff}-dimensional hidden representation
  3. Contraction Back: W2W_2 projects back to dmodeld_{\text{model}} for the residual connection

The variables are:

  • x∈Rdmodelx \in \mathbb{R}^{d_{\text{model}}}: input from attention/normalization
  • W,V∈Rdmodel×dffW, V \in \mathbb{R}^{d_{\text{model}} \times d_{ff}}: the two projection matrices for value and gate
  • b,c∈Rdffb, c \in \mathbb{R}^{d_{ff}}: corresponding bias vectors
  • W2∈Rdff×dmodelW_2 \in \mathbb{R}^{d_{ff} \times d_{\text{model}}}: output projection back to model dimension
  • b2∈Rdmodelb_2 \in \mathbb{R}^{d_{\text{model}}}: output bias

The Parameter Cost

Notice that the GLU FFN requires three weight matrices (WW, VV, and W2W_2) instead of the standard FFN's two (W1W_1 and W2W_2). This means GLU-based FFNs have 50% more parameters for the same hidden dimension. We'll analyze this trade-off in detail later and see how modern models address it.

In[8]:
Code
def ffn_glu(x, W, b, V, c, W2, b2):
    """
    Feed-forward network with GLU activation.

    Args:
        x: Input, shape (..., d_model)
        W, b: Value projection (d_model -> d_ff)
        V, c: Gate projection (d_model -> d_ff)
        W2, b2: Output projection (d_ff -> d_model)

    Returns:
        Output, shape (..., d_model)
    """
    # GLU combines value and gate
    value = x @ W + b
    gate = sigmoid(x @ V + c)
    hidden = value * gate

    # Project back to model dimension
    output = hidden @ W2 + b2
    return output


# FFN dimensions
d_model_ffn = 512
d_ff = 2048

# Initialize all weights
W_ffn = np.random.randn(d_model_ffn, d_ff) * np.sqrt(2.0 / (d_model_ffn + d_ff))
b_ffn = np.zeros(d_ff)
V_ffn = np.random.randn(d_model_ffn, d_ff) * np.sqrt(2.0 / (d_model_ffn + d_ff))
c_ffn = np.zeros(d_ff)
W2_ffn = np.random.randn(d_ff, d_model_ffn) * np.sqrt(
    2.0 / (d_ff + d_model_ffn)
)
b2_ffn = np.zeros(d_model_ffn)

# Process a sequence
seq_len = 16
X = np.random.randn(seq_len, d_model_ffn)
Y = ffn_glu(X, W_ffn, b_ffn, V_ffn, c_ffn, W2_ffn, b2_ffn)
Out[9]:
Console
GLU FFN example:
  d_model: 512
  d_ff:    2048
  Sequence length: 16
  Input shape:  (16, 512)
  Output shape: (16, 512)

The output shape matches the input shape, confirming that the GLU FFN correctly projects back to the model dimension. This dimensional consistency is essential for the residual connection that adds the FFN output back to its input in a transformer block.