Activation functions
ReLU, sigmoid, GELU, and why nonlinearity matters.
Create a free account Try part of it here
Why it matters
An activation function is a small bend a neural network applies after each weighted sum. Without it, a hundred layers can do no more than one. The bend you pick also decides how easily the network learns. The switch from sigmoid to ReLU was one of the changes that made deep learning work. The GELU and SwiGLU used inside today's LLMs are the latest step in the same story.
What you'll be able to do
- Show that stacked layers without activations collapse into one linear layer
- Compare sigmoid, tanh, ReLU, leaky ReLU and GELU, and their slopes
- Explain saturation and dead ReLUs, and why they slow or stop learning
- Choose the right activation for hidden layers and for outputs
What it covers
- Without a bend, layers collapse
- The common activations
- Worked example: three inputs, five activations
- What the slopes do to learning
- Choosing
Without a bend, layers collapse
Say you change dollars into euros, then euros into yen. You could have used one dollars-to-yen rate and got the same answer. Two straight-line rules in a row always make one straight-line rule.
A neural network layer works the same way, unless something bends it. In the last lesson, a neuron took a weighted sum and then passed it through an activation function. That function is the bend. Leave it out, and every layer is a plain "multiply, then add" rule.
Here's a tiny example with one number per layer:
- Layer 1 takes , the input, and gives .
- Layer 2 takes and gives .
Put the first rule inside the second: . One layer with weight 6 and bias −1 does the work of both. At , layer 1 gives 5 and layer 2 gives 11. The merged rule gives too.
Now put ReLU between the layers. ReLU keeps positive numbers and turns negative ones into 0. At , layer 1 gives −1. ReLU turns that into 0, so layer 2 gives −4. The merged straight line would have said −7. The bend broke the shortcut.
for x in [-1.0, 2.0]: h = 2 * x + 1 # layer 1 y = 3 * h - 4 # layer 2 merged = 6 * x - 1 # one layer doing both bent = 3 * max(0.0, h) - 4 # ReLU between the layers print(x, y, merged, bent)
The y and merged columns always agree. Only the last column, with ReLU, can differ. It does at .
With many numbers at once
A real layer handles a whole list of numbers. Its weights sit in a table called a matrix, which you met in Phase 2. Call layer 1's weight table . Its list of biases is . The input list is , so layer 1 gives . Layer 2 has its own and . Feed one into the other and you get:
In plain words: two layers in a row equal one layer. Its weights are and its biases are . It's the dollars-to-yen shortcut, with tables.
However many of these layers you stack, the result can only draw straight-line boundaries. XOR, from the last lesson, stays out of reach. NumPy confirms it with random weights:
import numpy as np rng = np.random.default_rng(0) W1, b1 = rng.normal(size=(4, 3)), rng.normal(size=4) W2, b2 = rng.normal(size=(2, 4)), rng.normal(size=2) x = np.array([1.0, -2.0, 0.5]) two_layers = W2 @ (W1 @ x + b1) + b2 one_layer = (W2 @ W1) @ x + (W2 @ b1 + b2) print("two linear layers:", np.round(two_layers, 4)) print("one merged layer: ", np.round(one_layer, 4)) with_relu = W2 @ np.maximum(0, W1 @ x + b1) + b2 print("with ReLU between:", np.round(with_relu, 4))
The first two lines match exactly: [-0.2398 3.0556]. With ReLU in between, the answer changes to [0.5434 1.6494], and no single merged layer can copy it.
Put a bend between the layers and the merge no longer works. Each layer can now bend what it receives. Stacks of bends can trace almost any shape.
Go deeper Why the merge works
Multiplying out brackets works for matrices the same way it works for numbers: . Add and group the terms. The part that multiplies is the new weight table. The part that doesn't is the new bias. Put ReLU around the inner bracket and this fails. ReLU treats positive and negative numbers differently, so the bracket can't be multiplied out any more.
Show the answer
Only linear functions, the same as a single layer
Linear layers compose into one linear layer: W₁₀ ⋯ W₁ x + (combined bias).
Drag along ReLU and watch its slope: exactly 1 for positive inputs and 0 for negative ones. Sigmoid and tanh flatten out at both ends.
The common activations
Each activation takes one number in and gives one number out. Call the number going in . It's the neuron's weighted sum plus bias, from the last lesson.
- ReLU keeps positive numbers and turns negative ones into 0. Think of a bank account that can't go below zero.
- Leaky ReLU keeps positive numbers too. Negative ones shrink to 1% of their size instead of vanishing, so −4 becomes −0.04.
- Sigmoid squeezes any number into the range 0 to 1. You used it in logistic regression.
- tanh (short for "hyperbolic tangent") has the same S-shape as sigmoid, but its range is −1 to 1.
- GELU is a smooth version of ReLU. Big positive numbers pass almost unchanged. Big negative ones fade to nearly 0. In between it curves gently, and dips a little below 0.
Here they are side by side. The letter is the number about 2.718 from the exponentials lesson. The symbol in GELU's row is explained just below the table.
| Activation | Formula | Output range | Where it's used |
|---|---|---|---|
| Sigmoid | 0 to 1 | output layer for yes/no probabilities | |
| tanh | −1 to 1 | older networks, RNNs | |
| ReLU | 0 to ∞ | default for hidden layers in MLPs and CNNs | |
| Leaky ReLU | if , else | −∞ to ∞ | a ReLU that never fully switches off |
| GELU | about −0.17 to ∞ | transformers (BERT, GPT-2 and many since) |
In plain words: ReLU is "the number or 0, whichever is bigger". Leaky ReLU is "the number, or 1% of it if it's negative". Sigmoid and tanh are S-shaped squashers. GELU is "the number, times a share between 0 and 1 that grows with the number".
In GELU, (capital phi, said "fie") is the normal distribution's cumulative probability. It's the chance that a value from the standard bell curve (average 0, spread 1) lands below , so it's always between 0 and 1. Many recent LLMs use SwiGLU instead. It's a gated cousin of these: it multiplies two linear projections, one of them passed through a smooth activation.
A hidden layer is any layer before the last one. An MLP is a plain stack of layers (the next lesson). CNNs are networks for images, and RNNs are older networks for sequences. You'll meet both later.
Worked example: three inputs, five activations
Push z = −1, 0 and 2 through each activation by hand first. ReLU gives 0, 0, 2. Leaky ReLU gives −0.01, 0, 2. Then run the code to see the curved ones.
import math print("z, ReLU, leaky ReLU, sigmoid, tanh, GELU") for z in [-1.0, 0.0, 2.0]: relu = max(0.0, z) leaky = z if z > 0 else 0.01 * z sigmoid = 1 / (1 + math.exp(-z)) tanh = math.tanh(z) gelu = z * 0.5 * (1 + math.erf(z / math.sqrt(2))) # z times Phi(z) print(z, relu, leaky, round(sigmoid, 3), round(tanh, 3), round(gelu, 3))
At z = 0, sigmoid gives exactly 0.5 and the others give 0. At z = 2, sigmoid gives 0.881 and tanh gives 0.964: both are already close to their ceilings. GELU gives 1.954, a bit under ReLU's
- At z = −1, GELU gives −0.159, a small dip below 0 that ReLU never makes. The
math.erfline
is the standard library's way to get . You don't need its details.
z = -4.0 relu = max(0.0, z) leaky = z if z > 0 else 0.01 * z print(relu, leaky)
Show the answer
0.0 -0.04
ReLU turns any negative into 0. Leaky ReLU keeps 1% of a negative: 0.01 × −4 = −0.04.
What the slopes do to learning
An activation's slope says how much its output moves when its input moves a little. You met slopes in the calculus lessons.
Slopes matter because of how training works. Training sends an error signal backward through the network. At each layer, the chain rule multiplies that signal by the activation's slope. A slope of 1 lets the whole signal through. A slope of 0.1 lets a tenth through. A slope of 0 blocks it. Think of a row of taps on a pipe: each one can open fully, open a little, or shut.
So the slope decides how much learning signal gets through. For sigmoid, written (the Greek letter sigma), the slope has a neat form. The mark means "the slope of sigma":
In plain words: the sigmoid's slope is its output times "one minus its output".
Try it with small numbers. At , the sigmoid gives 0.5, so the slope is . That's the biggest it ever gets. At , the sigmoid gives about 0.881, so the slope is about . The other slopes are simpler. ReLU's is 1 for positive inputs and 0 otherwise. Leaky ReLU's is 1 or 0.01. The code below works out all four at five points (tanh's slope is 1 - np.tanh(z) ** 2):
import numpy as np z = np.array([-6.0, -2.0, 0.0, 2.0, 6.0]) sig = 1 / (1 + np.exp(-z)) slopes = { "sigmoid": sig * (1 - sig), "tanh": 1 - np.tanh(z) ** 2, "ReLU": (z > 0).astype(float), "leaky ReLU": np.where(z > 0, 1.0, 0.01), } print("z: ", z) for name, s in slopes.items(): print(f"{name:<12}", np.round(s, 4))
Sigmoid's slope peaks at 0.25 and falls to 0.0025 at ±6. Tanh's peaks at 1 but is about 0 at ±6. ReLU's is 0 or 1, nothing in between.
- Saturation: sigmoid and tanh go flat at both ends. Picture a dimmer switch already turned all the way up: turning it further changes nothing. A neuron pushed to a large positive or negative input passes almost no signal back. Even at best, the sigmoid's slope is only 0.25. Multiply ten of those together and you get less than one millionth. Through deep layers the signal vanishes. A later lesson covers this.
- ReLU passes the signal through unchanged for any positive input. That's why deep networks became trainable once it caught on, around 2010–2012. It's also cheap to compute.
- Dead ReLUs: some ReLU neurons get a negative input for every example. Their slope is then 0 every time, so their weights never change again: no gradient reaches those weights, so they stop changing. Leaky ReLU and GELU keep a small slope to avoid this.
Go deeper Where the sigmoid's slope comes from
Write . Taking the slope with the chain rule gives . Split that into two fractions: , which is , times , which is . So the slope is . The product of two numbers that add up to 1 is largest when both are 0.5. That's why the slope never beats .
Show the answer
About 0.0025 of it, almost nothing
At z = 6 the sigmoid is saturated. Its output is about 0.9975, so its slope is about 0.9975 × 0.0025 ≈ 0.0025.
Choosing
Pick hidden-layer activations and output activations separately. They answer different questions.
- Hidden layers: ReLU for MLPs and CNNs. GELU or SwiGLU in transformers, the design behind LLMs. Don't use sigmoid there: it saturates.
- Output layer: this depends on the task, not on the hidden layers. Use sigmoid for one probability, such as "will this email get opened?". Use softmax for one-of-many classes (Phase 2). Use none at all (a linear output) to predict a number, such as next month's sales.
Show the answer
No activation: a plain linear output
Predicting a number calls for a linear output. Sigmoid would cap it between 0 and 1, softmax is for picking a class, and ReLU would block every temperature below zero.
Practice what you just read
- 4 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 5 review cards for spaced repetition
Try a quiz question
Which activation is the usual default for hidden layers in a plain feed-forward network?