Problem
The neurons and layers page showed that stacking linear layers without anything between them collapses to one linear layer. Something nonlinear has to sit between them. But the choice matters in practice: it decides how well gradients travel backwards through many layers (see backpropagation), how fast training is, and which units can stop learning altogether.
Intuition
An activation is a small fixed curve applied to each number independently. Two properties of the curve decide almost everything:
- Its range and flatness. Sigmoid and tanh squash large inputs into a bounded interval and go flat there (saturate), so the gradient through them shrinks towards zero. ReLU and its relatives are unbounded on the positive side with slope 1, so the gradient passes through unchanged.
- What happens for negative inputs. ReLU outputs exactly 0 with slope 0 there: a unit whose input is always negative has no gradient and never recovers (a dead unit). Leaky ReLU, GELU and SiLU keep a small slope or a small negative dip, so something still flows back.
Modern transformers use smooth ReLU-like curves (GELU, SiLU) inside the feed-forward blocks, often as the gate of a gated linear unit (GLU) such as SwiGLU.
GELU and SiLU hug ReLU for positive inputs and are smooth at zero. Their dip below zero is small and shallow.
Mechanics
The functions and their slopes
| Name | f(x) | f′(x) | Notes |
|---|---|---|---|
| sigmoid | 1 / (1 + e⁻ˣ) | σ(1 − σ), at most 0.25 | Output in (0, 1); saturates at both ends. |
| tanh | (eˣ − e⁻ˣ) / (eˣ + e⁻ˣ) | 1 − tanh², at most 1 | Zero-centered; saturates at both ends. |
| ReLU | max(0, x) | 1 for x > 0, else 0 | Cheap; dead for x < 0. |
| leaky ReLU | x if x > 0 else 0.01x | 1 or 0.01 | Small slope keeps negative inputs trainable. |
| GELU | x · Φ(x) | Φ(x) + x·φ(x) | Φ is the standard normal CDF, φ its density. Smooth. |
| SiLU (Swish) | x · σ(x) | σ(x)(1 + x(1 − σ(x))) | Smooth; GELU is close to x·σ(1.702x). |
| softplus | ln(1 + eˣ) | σ(x) | A smooth ReLU; always positive. |
Sigmoid's slope never exceeds 0.25 and tanh's never exceeds 1; both fall to zero away from the origin.
Saturation and vanishing gradients
Backpropagation multiplies the activation's slope into the gradient at every layer. If the slope is at most 0.25, as for sigmoid, ten layers shrink the signal by at least a factor 4¹⁰ ≈ one million. This is the vanishing gradient problem that kept deep sigmoid networks hard to train until ReLU and good initialization arrived. The cure is an activation whose slope is near 1 over a wide range, plus residual connections and normalization (see layer norm and residuals).
Dead units
ReLU's slope is exactly 0 for negative inputs. If a large learning-rate step pushes a unit's bias or weights so that its pre-activation is negative for every example, that unit's output and gradient are both 0 from then on. Nothing in the loss can bring it back. Leaky ReLU (a small fixed slope) or the smooth GELU and SiLU leave a path for gradient.
GELU and SiLU
GELU multiplies x by the probability Φ(x) that a standard normal variable is below x, a smooth gate that is nearly 0 for very negative inputs and nearly 1 for large positive ones. SiLU does the same with the logistic function σ(x) as the gate. Both are non-monotonic: they dip slightly below zero before rising.
PyTorch's nn.GELU takes approximate="none" (exact, using the error function) or "tanh", the cheaper formula used in the original GPT-2 and BERT code.
Gated linear units
A GLU splits a projection into two halves a and b and returns a ⊙ gate(b). With a sigmoid gate that is the original GLU (PyTorch's F.glu); with SiLU as the gate on a it is SwiGLU, and with GELU it is GeGLU. The feed-forward blocks of many recent language models use SwiGLU: three weight matrices instead of two, with the hidden size reduced (typically to 2/3) to keep the parameter count similar. See the transformer page for where the feed-forward block sits.
| Component | Shape/Params | Meaning | Source |
|---|---|---|---|
ReLU |
elementwise, inplace |
Module wrapper around F.relu. |
activation.py |
Sigmoid / Tanh |
elementwise | Modules that return torch.sigmoid(input) and torch.tanh(input). |
activation.py, Tanh |
LeakyReLU |
negative_slope=0.01 |
Slope on the negative side. | activation.py |
GELU |
approximate="none" or "tanh" |
Gaussian-gated linear unit, exact or tanh-approximated. | activation.py |
SiLU |
elementwise | x times sigmoid of x. | activation.py |
GLU |
dim=-1 |
Splits the input in two along dim and returns the first half times the sigmoid of the second. |
activation.py |
F.relu, F.glu |
input, dim |
The functional forms the modules call; glu requires an even size along dim. |
relu, glu |
F.leaky_relu, F.gelu |
negative_slope, approximate |
gelu is bound to the C++ kernel and documented in place. |
leaky_relu, gelu |
F.tanh, F.sigmoid, F.silu |
elementwise | Thin wrappers over the tensor ops and C++ kernels. | tanh, sigmoid, silu |
Worked example
Values and slopes at five points
| Function | x = -3 | x = -1 | x = 0 | x = 1 | x = 3 |
|---|---|---|---|---|---|
| sigmoid | 0.047 (0.045) | 0.269 (0.197) | 0.500 (0.250) | 0.731 (0.197) | 0.953 (0.045) |
| tanh | -0.995 (0.010) | -0.762 (0.420) | 0.000 (1.000) | 0.762 (0.420) | 0.995 (0.010) |
| relu | 0.000 (0.000) | 0.000 (0.000) | 0.000 (0.000) | 1.000 (1.000) | 3.000 (1.000) |
| leaky_relu | -0.030 (0.010) | -0.010 (0.010) | 0.000 (0.010) | 1.000 (1.000) | 3.000 (1.000) |
| gelu | -0.004 (-0.012) | -0.159 (-0.083) | 0.000 (0.500) | 0.841 (1.083) | 2.996 (1.012) |
| silu | -0.142 (-0.088) | -0.269 (0.072) | 0.000 (0.500) | 0.731 (0.928) | 2.858 (1.088) |
| softplus | 0.049 (0.047) | 0.313 (0.269) | 0.693 (0.500) | 1.313 (0.731) | 3.049 (0.953) |
Each cell is f(x) with the slope f′(x) in brackets. At x = 3 sigmoid's output is 0.953 and its slope is 0.045: nearly flat. At x = −3, ReLU has value and slope 0, leaky ReLU has -0.030 and slope 0.01, GELU has -0.004 with slope -0.012. At x = −1, GELU is -0.159 and SiLU is -0.269: the negative dip. The slopes are checked against finite differences in the test suite.
Ten layers of gradient
Suppose every one of 10 layers has pre-activation z, so backpropagation multiplies the activation's slope 10 times. The product, ignoring the weights:
| Activation | z = 0 | z = 2 |
|---|---|---|
| sigmoid | 9.5e-07 | 1.6e-10 |
| tanh | 1.000 | 3.1e-12 |
| relu | 0 | 1.000 |
| gelu | 9.8e-04 | 2.266 |
| silu | 9.8e-04 | 2.384 |
Sigmoid, even at its best point z = 0, passes 9.5e-07 of the gradient (0.25 to the 10th power), and at z = 2 only 1.6e-10. Tanh passes everything at z = 0 but almost nothing at z = 2, where it is saturated. ReLU passes exactly 1 when the unit is on, and exactly 0 when it is off, which leads to the next example. GELU and SiLU have slope slightly above 1 near z = 2, so their product there exceeds 1: smooth curves can amplify as well as shrink. (Real networks also multiply by weight matrices; careful initialization keeps those near 1 in scale.)
How a negative bias kills a unit
A ReLU unit's input is a standard normal variable (mean 0, std 1) plus a bias. The fraction of 4,000 examples for which the unit is active (input above 0):
| Bias | 0 | -1 | -3 | -5 |
|---|---|---|---|---|
| Fraction active | 49.1% | 16.5% | 0.2% | 0.0% |
At bias 0 the unit is active half the time. At −3 it fires on a tiny fraction of examples and receives gradient only from those; at −5 it never fires in this sample: dead. A GELU or leaky unit in the same position would still pass a small gradient back.
GELU: exact versus tanh approximation
Over x from −6 to 6, the largest gap between the exact GELU (via the error function) and its tanh approximation is 0.00047, at x ≈ 2.7. Tiny, but not zero, and it matters when reproducing a checkpoint trained with one version and run with the other. At x = 1 they are 0.84134 and 0.84119.
Gating one number with another
Let a = 1 and b = 2 (the two halves of a projection). The four gated units give: GLU = a·σ(b) = 0.881; SwiGLU = silu(a)·b = 1.462; GeGLU = gelu(a)·b = 1.683; ReGLU = relu(a)·b = 2.000. In all of them one half passes through a nonlinearity and the other half scales it, so the layer can learn which features to let through for each input.
Implementation
import torch
import torch.nn.functional as F
from torch import nn
x = torch.linspace(-4, 4, 9)
print(F.relu(x), F.gelu(x), F.silu(x), F.gelu(x, approximate="tanh"))
class SwiGLUFeedForward(nn.Module):
def __init__(self, d_model, d_hidden):
super().__init__()
self.gate = nn.Linear(d_model, d_hidden, bias=False)
self.up = nn.Linear(d_model, d_hidden, bias=False)
self.down = nn.Linear(d_hidden, d_model, bias=False)
def forward(self, x):
return self.down(F.silu(self.gate(x)) * self.up(x))
F.glu takes one tensor and splits it in half; the SwiGLU feed-forward above uses two separate projections instead, which is how most transformer implementations write it.
Tradeoffs
- ReLU is cheap and can die. Its simplicity is hard to beat for small models, but a bad learning rate can leave units permanently off.
- Smooth curves cost more. GELU and SiLU need an exponential or error function per element, though on a GPU the cost is small next to the matrix multiplications.
- Sigmoid and tanh are for gates and outputs. They are still right for recurrent-network gates, binary probabilities and bounded outputs, but not as the default hidden activation of a deep network.
- Gated units add parameters. SwiGLU uses three matrices where a plain feed-forward uses two, so the hidden size is usually adjusted for a fair comparison.
- Evidence is empirical. The gains of GELU, SiLU and SwiGLU over ReLU are measured on benchmarks, not proved; the difference between the smooth variants is small.
- Exact and approximate GELU differ. Match the variant a checkpoint was trained with.
Check yourself
What is the largest value the derivative of sigmoid can take, and what does it imply for a 20-layer sigmoid network?
0.25, at z = 0. The activation slopes alone multiply to at most 0.25²⁰ ≈ 10⁻¹², so almost no gradient reaches the early layers.
Why can a ReLU unit become permanently dead but a leaky ReLU unit cannot?
ReLU's slope is exactly 0 for negative inputs, so a unit that is negative for all examples gets no gradient. Leaky ReLU keeps a small slope there, so gradient still flows and the unit can recover.
In SwiGLU(x) = silu(xW) ⊙ (xV), which part is the gate?
The silu(xW) branch: it scales the other branch xV element by element, so features can be passed or suppressed depending on the input.
Connections
Read first
Read next
No page builds on this one yet.
References
- Hendrycks and Gimpel. Gaussian Error Linear Units (GELUs). 2016. arXiv:1606.08415.
- Ramachandran, Zoph and Le. Searching for activation functions. 2017. arXiv:1710.05941.
- Dauphin, Fan, Auli and Grangier. Language modeling with gated convolutional networks. ICML 2017. arXiv:1612.08083.
- Shazeer. GLU variants improve transformer. 2020. arXiv:2002.05202.
- Glorot, Bordes and Bengio. Deep sparse rectifier neural networks. AISTATS 2011.
- PyTorch v2.15.0:
GELU,F.glu.