Activation functions and non-linearity
30 min read
The previous lesson, on the artificial neuron, left one piece still unchosen. The weights and the bias are the neuron itself (they fix the hyperplane, and with it everything it can ever tell apart), but between the pre-activation and what the neuron actually returns sits one more function, , that hasn't been chosen yet: the step filled that role by default, and only provisionally. Choosing it properly is what this lesson is about, and it's no cosmetic detail. Two things ride on : how much of the number the neuron computes survives, and what happens when you put one neuron behind another.
Start by watching what the step does when a weight moves. Take any neuron and nudge a single one of its weights from to , to , to . The pre-activation tracks the movement exactly: it grows a little each time, passing through every value in between. But the output doesn't notice any of it. It sits at the whole time is negative, and the instant crosses zero, it jumps straight to and stays there. Whatever there was to learn about how far the weight had moved gets lost along the way, and always at the very same point.
Before writing down a single candidate, it helps to look at them side by side. The explorable draws each one over , overlays its derivative as a dashed line, and shades in red the stretches where that derivative all but vanishes.
The derivative f′ button draws that dashed curve. The first four solid curves look a lot alike (all rising, all faster near zero), but their dashed curves have almost nothing in common: it's the derivative, not the function itself, that tells one candidate apart from another. And then there's the step, which shows exactly what it's missing: its dashed curve is a flat line pinned to zero, and the red covers the whole graph.
Why the activation cannot be a straight line
Start with the requirement that doesn't depend on any particular case. Chain two neurons together: the first looks at the input and returns a number, and the second looks at that number and returns another. There's one neuron at each level, so the first level's weights form a vector and the second's is a single scalar; the superscript in parentheses marks the level, the same notation the layers will carry once each one holds several neurons:
with and . Now set to the identity in both activations, , and substitute the first expression into the second:
The right-hand side is a neuron: a single one, with weight vector and bias , and no input can tell it apart from the chain that produced it, since the two give the same number for every . The previous lesson already told you what that means: everything a neuron tells apart lives on its boundary, and that boundary is a hyperplane. So the chain of two separates with a hyperplane too. Chaining hasn't curved the boundary at all.
And there was nothing special about picking the identity. Try any straight line for , with , and you'll find that gets absorbed into the next level's weights and into its bias: what comes out the other side is, again, a pre-activation. Any straight line behaves the same way.
It helps to name this precisely. The expression isn't linear in the strict algebraic sense (a linear map sends zero to zero, and this one sends it to instead): it's affine. Composing affine functions always gives back an affine function, and that holds no matter how far you chain it: a hundred neurons in a row, each with a straight line for , add up to exactly one neuron. The property that has to break, and it can only break inside the activation, is the one this lesson takes its title from: non-linearity, though what actually gets broken is affineness.
The same computation with several neurons at each level
None of this depended on having a single neuron per level. With several neurons per level (and, as before, the identity in both activations), each level's weights gather into a matrix and its biases into a vector, and the chain becomes
which is a single level all over again. The full matrix form, with its sizes worked out, belongs to a lesson further on, the one on the forward pass; here it serves one purpose only: showing that the collapse wasn't an artefact of putting a single neuron at each level.
The second requirement is easier to see in what happens to the step. Its derivative is zero everywhere it even exists,
and at it doesn't even exist, because that's exactly where the function jumps. There's a debt worth naming out loud here: how a neuron actually finds its own weights from examples belongs to a lesson further on, the one on gradient descent. All you need for now is what that method runs on: derivatives of . An activation whose derivative is zero everywhere has nothing to say about which direction to move the weights in, so the whole procedure grinds to a halt. The step passes the first requirement (it's about as far from a straight line as you can get) and fails the second completely.
Three candidates and their derivative
The three candidates that follow all meet both requirements, and I've written each one alongside its derivative already worked out: the rest of this block will lean on those derivatives far more than on the functions themselves.
The sigmoid already has its own reserved symbol in this course, :
continuous, increasing, with values in : it sends to and squeezes the extremes towards and without ever quite reaching either. Let's work out its derivative. Written as , the chain rule plus a convenient factorisation give
where
The sigmoid's derivative comes out in terms of its own value, with no fresh exponential needed, which is exactly what makes it cheap to evaluate thousands of times over.
The hyperbolic tangent traces the same shape over a different range of values:
Its values sit in and : it's centred on zero, which is the difference that actually matters compared with the sigmoid, whose output is always positive. Its derivative takes an equally convenient form,
and at the origin it equals , four times higher than the sigmoid's own peak.
Where 1 − tanh² comes from, and the likeness between the two curves
To get the derivative, write and : each one is the derivative of the other, and . The quotient rule then gives
The resemblance between the two curves isn't a coincidence, either: the hyperbolic tangent is really a sigmoid, stretched by a factor of two and shifted, . Expand the right-hand side and multiply top and bottom by ,
and that also explains the factor of four between the two derivatives: differentiating picks up a from the outer factor and another from the chain rule.
The third candidate is the youngest and the most widely used: ReLU, short for rectified linear unit. It's defined as : it passes the positive part straight through and sends the negative part to zero, so it crushes nothing from above. Whenever is positive, its full magnitude reaches the output intact. Its derivative is
and at it doesn't exist at all: the slope is coming from the left and coming from the right, and a function with a corner has no single tangent line at that point. This course takes it to be there, by convention rather than by any theorem, and the choice changes nothing that can actually be measured.
Where each one switches off
All three derivatives share one problem, and each one splits it up differently. Call saturation the region where the derivative of is so close to zero that the input can move a long way while the output barely budges: that's what the red bands of the explorable mark out.
For the sigmoid, the arithmetic is straightforward. At , , so its derivative there works out to : almost forty times smaller than at the origin. The hyperbolic tangent switches off earlier and faster still (, more than thirty times below the sigmoid at the same point), which is why its red bands start closer to the origin even though its derivative starts out four times higher. Starting higher and falling faster turn out to be entirely compatible.
ReLU breaks that pattern, and its half-graph red band doesn't contradict that at all. On the right, its derivative stays exactly no matter how far out you go: it never saturates there, and that's its whole appeal. On the left, it doesn't saturate either, since saturating means approaching zero: it's switched off outright, with the derivative sitting at over an entire half-axis. Out of that comes a defect with a name of its own, the dying ReLU: if a neuron's pre-activation stays negative for every example it ever sees, its derivative is zero on all of them, nothing ever moves it, and it never comes back.
That leaves the fourth button. GELU, short for Gaussian error linear unit, is the one used by the Transformers of Block 5; its definition calls on the cumulative distribution function of a normal distribution, and that's beyond the scope of this lesson. It looks much like ReLU except near the origin, where instead of folding into a sharp corner it curves smoothly.
The activations and their derivatives in NumPy
The cell defines the four functions and the three derivatives, then runs them through four checks. Run it and look, in this order, at whether the derivatives hold up against a numerical check, how much of each one survives far from the origin, what happens to the chain when the identity gets swapped for the sigmoid, and what the sigmoid does with the ten reviews from the previous lesson.
np.random.seed(0)
# --- The four candidates, and the derivative of the three that have one.
def step(z):
return (z >= 0).astype(float)
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
def d_sigmoid(z):
s = sigmoid(z)
return s * (1.0 - s)
def d_tanh(z):
t = np.tanh(z)
return 1.0 - t * t
def relu(z):
return np.maximum(z, 0.0)
def d_relu(z):
return (z > 0).astype(float)
# 1. Are they differentiated correctly? Central difference against the closed form.
h = 1e-5
zs = np.array([-3.0, -0.5, 0.7, 2.5])
print("z =", zs, " shape", zs.shape)
for name, f, df in [("sigmoid", sigmoid, d_sigmoid),
("tanh", np.tanh, d_tanh),
("ReLU", relu, d_relu)]:
central = (f(zs + h) - f(zs - h)) / (2 * h)
print(" ", name.ljust(8), "max |formula - central difference| =",
"%.1e" % np.abs(df(zs) - central).max())
print()
# 2. How much of each derivative is left as we move away from the origin.
far = np.array([0.0, 2.0, 5.0, 10.0])
print(" z:", " ".join("%9.1f" % v for v in far))
print(" dσ/dz:", " ".join("%9.2e" % v for v in d_sigmoid(far)))
print(" dtanh/dz:", " ".join("%9.2e" % v for v in d_tanh(far)))
print(" dReLU/dz:", " ".join("%9.2e" % v for v in d_relu(far)))
print()
# 3. Two neurons in a chain. Every affine function zeroes f(x+y) - f(x) - f(y) + f(0).
d = 8
w1 = np.random.randn(d)
b1 = 0.4
w2 = -1.3
b2 = 0.7
def identity(z):
return z
def chain(x, phi):
return w2 * phi(w1 @ x + b1) + b2
x = np.random.randn(d)
y = np.random.randn(d)
zero = np.zeros(d)
for name, phi in [("identity", identity), ("sigmoid ", sigmoid)]:
defect = chain(x + y, phi) - chain(x, phi) - chain(y, phi) + chain(zero, phi)
print("φ =", name, " f(x+y) - f(x) - f(y) + f(0) =", "%+.2e" % defect)
w_eq = w2 * w1
b_eq = w2 * b1 + b2
print("with the identity, a single neuron reproduces it:",
bool(np.isclose(chain(x, identity), w_eq @ x + b_eq)))
print()
# 4. The ten pre-activations of the previous lesson's review neuron.
z_reviews = np.array([2.0, -2.0, 2.0, -2.0, 2.0, -1.0, -2.0, 1.0, 0.0, 2.0])
print(" z:", " ".join("%6.1f" % v for v in z_reviews))
print(" step:", " ".join("%6.0f" % v for v in step(z_reviews)))
print(" σ:", " ".join("%6.3f" % v for v in sigmoid(z_reviews)))
The first run downloads the Python interpreter (~15 MB). After that it stays in the browser cache and is reused across every lesson.
The first check earns the right to trust everything that follows. The central difference with approximates the derivative without knowing the formula at all, and all three agree with it to about . If you ever get an activation's derivative wrong, this catches it in two lines.
The second table puts actual numbers on saturation. The sigmoid's derivative drops from at the origin to at , four orders of magnitude; the hyperbolic tangent's covers eight over that same stretch. The ReLU row doesn't decay at all: across the last three columns, however far out you go. The in the first column is the convention above, written into code.
The third check settles the question without training a single thing. Every affine function satisfies the identity
no matter what and are (expand it out and you'll see the four biases cancel). With the identity as , the chain of two neurons gives : zero, as far as the computer's arithmetic can tell. With the sigmoid, it gives instead. It's the exact same pair of neurons and the exact same inputs; the only thing that changed is the activation.
The last table circles back to the previous lesson: the same ten pre-activations from that review neuron, weights untouched, run through both the step and the sigmoid. The step hands back the same column as before; the sigmoid pulls apart what the step had lumped together. The review sitting right on the boundary drops from a confident to , and the four negatives, which had all received the same flat , now split into two distinct values: for the one sitting at , and for the three sitting at . The magnitude of is back, without touching a single weight.
Test your intuition
Three questions (a derivative, the collapse of the chain, and where each activation switches off), plus a coding challenge with ReLU.
For some input, a neuron with a sigmoid gives . What is at that same point?
A margin of ±0.001 is accepted.
You chain two neurons: the first computes and the second , with the identity in both activations. What function of do you get?
Tick everything that is true about the derivatives of the activations in this lesson.
Select every correct option. This is graded all-or-nothing: there is no partial credit.
Write the two functions the cell uses to handle ReLU, working over a NumPy array of any shape and without Python loops:
relu(z)returns coordinate by coordinate.d_relu(z)returns its derivative: where and everywhere else, taking at as well.
Neither function may modify the array it receives.
The first run downloads the Python interpreter (~15 MB); after that it stays in the browser cache. This challenge is much easier to solve on a physical keyboard: on a phone, read it and come back later.
There's still something worth saying about what hasn't changed, before it starts to look like the sigmoid has solved more than it actually has. The decision boundary is exactly where it always was. Asking where crosses is the same as asking where crosses , because is increasing and : the hyperplane from the previous lesson survives every substitution this lesson has made, completely intact. What's changed is what the neuron reports about its decision, not the decision itself.
So stacking neurons can now build something a single one never could (without non-linearity, it couldn't even manage that much), but nobody has shown you a problem that actually forces you to stack them. There's a more uncomfortable question still missing: is there something a neuron, with any activation function at all, can never do, full stop? There is, and it fits in four points drawn on a sheet of paper: it's the next lesson, on XOR (exclusive or) and hidden layers.
Further reading2 sources · 1 paper, 1 book
Where this lesson comes from, and where to go next. None of it is needed to carry on with the course.
- Deep Learning, ch. 6: Deep Feedforward Networks
Its §6.3, on hidden units, ranks the three activations (ReLU, sigmoid, hyperbolic tangent) and argues why ReLU is today's default. It chooses by what works, not by what is proved, and only touches saturation in passing.
- Gaussian Error Linear Units (GELUs)
Defines the GELU the lesson names but does not develop: the input times the cumulative distribution function of a normal. Measures it against ReLU and ELU on vision, text and speech; nothing on saturation or the dying ReLU.