07

Layers and activations

How do networks gain power by stacking layers?

10 min

Layers and activations

With voice. You can pause and jump to any step.

Lesson complete

Subtitles

One hidden layer was enough to solve XOR. Today we stack more layers and see how much power that gains, and why stacking, by itself, is not enough.

Voice

No voice: the lesson advances on its own with subtitles.

The voice for this lesson is being recorded: for now it advances on its own with subtitles.

0:00 / 0:00

What you will achieve

  • Explain why stacking linear layers is equivalent to a single layer, and what changes when a nonlinear activation is added.
  • Compute by hand the forward pass of a tiny network, with and without ReLU.
  • Recognize what the universal approximation theorem says, and what it does not, and why layers are stacked in practice.

Key ideas

  1. A layer is a group of neurons that look at the same inputs; the forward pass computes layer by layer, from input to output.
  2. Without activation, two layers equal one: the product of two linear transformations is another linear transformation. Stacking adds nothing.
  3. Nonlinearity (ReLU, tanh, sigmoid) folds space: what a straight line could not separate becomes separable after the fold.
  4. With enough neurons, a single hidden layer can approximate any continuous function on a closed, bounded set (universal approximation), but the theorem does not say how many neurons are needed nor that training will find them.
  5. Stacking layers multiplies the folds: for certain functions, a deep network needs far fewer neurons than a single layer.

What many people believe

Common belief The more layers a network has, the more powerful it is, always.

What is true Without a nonlinear activation between them, any number of layers equals a single one. With it, more layers allow more shapes, but they also cost more to train and to run, and they do not guarantee a better result.

Check yourself

Four questions. No grade, no account: they just show whether the idea stuck.

ConceptWhy does stacking linear layers, without activation, give no more power than a single layer?
ConceptWhat does the universal approximation theorem say, in essence?
CalculationA hidden layer with ReLU receives the sums 2 and −1. The output combines them with weights 2 and 1, no bias. What is the output?
ApplicationA vendor says its classifier uses a twenty-layer neural network. Which question helps most to decide whether it suits you?

Under the hoodFor those who want the math and the code. You do not need it to follow the course.

A layer, in one formula

A layer with mm neurons and nn inputs stores a weight matrix W∈Rm×nW\in\mathbb{R}^{m\times n} (one row per neuron) and a bias vector b∈Rm\mathbf{b}\in\mathbb{R}^m. Given the input x\mathbf{x}, it computes

h=ϕ(Wx+b)\mathbf{h} = \phi(W\mathbf{x} + \mathbf{b})

where ϕ\phi is the activation, applied component by component. A forward pass chains this formula layer by layer. A dense network with sizes n0,n1,…,nLn_0, n_1,\dots,n_L has ∑l=1L(nl−1+1) nl\sum_{l=1}^{L}(n_{l-1}+1)\,n_l parameters: for example, the 2-8-8-8-1 network of the playground has 3⋅8+9⋅8+9⋅8+9⋅1=1773\cdot 8 + 9\cdot 8 + 9\cdot 8 + 9\cdot 1 = 177.

The linear collapse

If ϕ\phi is the identity, two layers give

W2(W1x+b1)+b2=(W2W1) x+(W2b1+b2),W_2(W_1\mathbf{x}+\mathbf{b}_1)+\mathbf{b}_2 = (W_2W_1)\,\mathbf{x} + (W_2\mathbf{b}_1+\mathbf{b}_2),

which is a single layer with weights W2W1W_2W_1 and bias W2b1+b2W_2\mathbf{b}_1+\mathbf{b}_2. With the numbers of the lesson:

(21)(123−1)=(53).\begin{pmatrix}2 & 1\end{pmatrix}\begin{pmatrix}1 & 2\\ 3 & -1\end{pmatrix}=\begin{pmatrix}5 & 3\end{pmatrix}.

By induction, LL linear layers equal one. With ϕ=ReLU\phi=\mathrm{ReLU} and input (0,1)(0,1), the hidden layer gives (2,−1)(2,-1), ReLU turns it into (2,0)(2,0), and the output is 2⋅2+1⋅0=42\cdot2+1\cdot0=4, not 33: the equivalence breaks.

The activations

σ(z)=11+e−z,tanh⁡(z)=2σ(2z)−1,ReLU(z)=max⁡(0,z).\sigma(z)=\frac{1}{1+e^{-z}},\qquad \tanh(z)=2\sigma(2z)-1,\qquad \mathrm{ReLU}(z)=\max(0,z).

Their derivatives matter for training (lesson 8): σ′(z)=σ(z)(1−σ(z))≤14\sigma'(z)=\sigma(z)(1-\sigma(z))\le \tfrac14, tanh⁡′(z)=1−tanh⁡2(z)≤1\tanh'(z)=1-\tanh^2(z)\le 1, and ReLU′(z)∈{0,1}\mathrm{ReLU}'(z)\in\{0,1\} (at z=0z=0 it does not exist; by convention it is taken as 0). ReLU, the “rectified linear unit”, has smooth variants such as GELU (Hendrycks and Gimpel, 2016), which BERT uses instead of ReLU (Devlin et al., 2019).

Universal approximation

What it says (Cybenko, 1989, theorem 2). A function σ\sigma is sigmoidal if σ(t)→1\sigma(t)\to1 as t→+∞t\to+\infty and σ(t)→0\sigma(t)\to0 as t→−∞t\to-\infty. If σ\sigma is continuous and sigmoidal, then for every continuous function ff on the unit cube K=[0,1]nK=[0,1]^n (rescaling, on any cube) and every ε>0\varepsilon>0 there exist NN, αj\alpha_j, wj\mathbf{w}_j and bjb_j such that

∣∑j=1Nαj σ(wj⋅x+bj)−f(x)∣<εfor all x∈K.\Big|\sum_{j=1}^{N}\alpha_j\,\sigma(\mathbf{w}_j\cdot\mathbf{x}+b_j)-f(\mathbf{x})\Big|<\varepsilon\quad\text{for all }\mathbf{x}\in K.

Who else, and under which conditions. The same year, Hornik, Stinchcombe, and White proved it for “squashing functions” (non-decreasing, with limits 0 and 1, not necessarily continuous), and for measurable functions in a weaker sense (approximation in probability); in a closing note they acknowledge Funahashi’s work (1989), which had reached a close result independently. The hypotheses do not coincide: Cybenko does not require σ\sigma to be monotone, and Hornik et al. do not require it to be continuous. Hornik (1991) showed that a bounded, nonconstant activation is enough (LpL^p approximation; if it is also continuous, uniform approximation on compact sets). Leshno, Lin, Pinkus, and Schocken (1993) settled the question: for a locally bounded, piecewise continuous activation, a network with one hidden layer approximates every continuous function, uniformly on every compact set, if and only if the activation is not a polynomial. ReLU meets that condition. The theorem also needs the bias (the threshold): without it, it fails.

What it does not say. It approximates a known function on a bounded set; it says nothing outside it, nor about learning from examples. It does not bound NN or say how to find the weights. Cybenko admits this in his closing discussion: his paper is limited to existence, and he suspects that the vast majority of approximation problems will need an astronomical number of terms, because of the curse of dimensionality. Hornik et al. add that a failure in practice may be due to inadequate learning, too few neurons, or a random relation between input and output.

The construction in the lesson. It is not Cybenko’s proof, which uses the Hahn–Banach theorem and builds nothing. For a function ff of one variable, with nn equally spaced knots tkt_k and slopes sk=f(tk+h)−f(tk)hs_k=\frac{f(t_k+h)-f(t_k)}{h}, h=(b−a)/nh=(b-a)/n,

f^(x)=f(a)+∑k=0n−1(sk−sk−1) ReLU(x−tk),s−1=0,\hat f(x)=f(a)+\sum_{k=0}^{n-1}(s_k-s_{k-1})\,\mathrm{ReLU}(x-t_k),\quad s_{-1}=0,

is a one-hidden-layer network with nn ReLU neurons that equals ff at the knots (piecewise linear interpolation). If ∣f′′∣≤M|f''|\le M, the maximum error is at most (b−a)2M8n2\dfrac{(b-a)^2M}{8n^2}: doubling the neurons divides the bound by four. For the scene’s curve, M≤3.36M\le 3.36; with n=24n=24 the bound is 0.026 and the measured error is 0.025. The ratio of measured errors is not always four: 2.5 from 3 to 6 neurons, 3.0 from 6 to 12, 4.1 from 12 to 24, and 3.8 from 24 to 48. Nor does it drop at every step (from 1 to 2, 4 to 5, and 7 to 8 neurons it rises a little, because equally spaced knots fall in other places). The turn’s goal, 0.05, is reached with 17 neurons (0.047; with 16, 0.057). Here the weights are computed, not trained.

Depth: the sawtooth

The tent function g(x)=2 ReLU(x)−4 ReLU(x−12)g(x)=2\,\mathrm{ReLU}(x)-4\,\mathrm{ReLU}(x-\tfrac12) on [0,1][0,1] uses two neurons: it rises from 0 to 1 on [0,12][0,\tfrac12] and falls from 1 to 0 on [12,1][\tfrac12,1]. The composition g(k)=g∘⋯∘gg^{(k)}=g\circ\cdots\circ g (kk layers, 2k2k neurons) has 2k2^k straight pieces (checked numerically up to k=6k=6). A network with one hidden layer of mm ReLU neurons has at most m+1m+1 pieces in one dimension (each neuron adds one corner), so to match it, it needs at least 2k−12^k-1 neurons. Telgarsky (2016) uses this same function and proves something stronger, for approximating and not just matching: for every kk there is a ReLU network with 2k3+82k^3+8 layers and 3k3+123k^3+12 neurons such that every ReLU network with at most kk layers and at most 2k2^k neurons stays at L1L^1 distance at least 1/641/64 on [0,1]d[0,1]^d. It is a result about specific functions, not a law: it does not say that every real task benefits equally from depth.

In code: the forward pass and the collapse

import numpy as np

W1 = np.array([[1, 2],
               [3, -1]])        # hidden layer: one row per neuron
W2 = np.array([[2, 1]])         # output

def net(x, activation):
    h = activation(W1 @ x)      # forward pass: hidden layer...
    return (W2 @ h)[0]          # ...and output

linear = lambda z: z
relu = lambda z: np.maximum(0, z)

print("W2 @ W1 =", W2 @ W1)     # the equivalent single neuron
for x in ([2, 1], [1, 0], [0, 1]):
    x = np.array(x)
    print(x, "no activation:", net(x, linear), "| one neuron:", (W2 @ W1 @ x)[0], "| with ReLU:", net(x, relu))

Expected output:

W2 @ W1 = [[5 3]]
[2 1] no activation: 13 | one neuron: 13 | with ReLU: 13
[1 0] no activation: 5 | one neuron: 5 | with ReLU: 5
[0 1] no activation: 3 | one neuron: 3 | with ReLU: 4

In code: a network that learns the circles

It is the same idea as the playground (a 2-6-1 network, ReLU and a sigmoid output), with plain gradient descent. The lines with dZ are backpropagation, explained in lesson 8.

import numpy as np
rng = np.random.default_rng(0)

# Data: a central group (class 0) surrounded by a ring (class 1)
n = 90
a0, r0 = rng.uniform(0, 2 * np.pi, n), np.sqrt(rng.uniform(0, 1, n)) * 1.15
a1, r1 = rng.uniform(0, 2 * np.pi, n), rng.uniform(1.95, 2.8, n)
X = np.vstack([np.c_[r0 * np.cos(a0), r0 * np.sin(a0)], np.c_[r1 * np.cos(a1), r1 * np.sin(a1)]])
X = (X + 0.12 * rng.normal(size=X.shape)) / 3          # coordinates in [-1, 1]
y = np.r_[np.zeros(n), np.ones(n)]

# Network 2 → 6 (ReLU) → 1 (sigmoid)
W1 = rng.normal(0, np.sqrt(2 / 2), (2, 6)); b1 = rng.uniform(-0.5, 0.5, 6)
W2 = rng.normal(0, np.sqrt(1 / 6), (6, 1)); b2 = np.zeros(1)
sig = lambda z: 1 / (1 + np.exp(-z))

for step in range(1500):
    Z1 = X @ W1 + b1; H = np.maximum(0, Z1); p = sig(H @ W2 + b2).ravel()   # forward pass
    dZ2 = (p - y)[:, None] / len(y)                                          # error at the output
    dW2, db2 = H.T @ dZ2, dZ2.sum(0)
    dZ1 = (dZ2 @ W2.T) * (Z1 > 0)                                            # the blame flows back to the hidden layer
    dW1, db1 = X.T @ dZ1, dZ1.sum(0)
    for P, G in ((W1, dW1), (b1, db1), (W2, dW2), (b2, db2)):
        P -= 0.5 * G                                                          # gradient descent
print("accuracy:", ((p > 0.5) == y).mean())

Expected output: accuracy: 1.0. With 6 neurons it gave 1.0 on all ten seeds tried (0 to 9); with 3 neurons, between 0.81 and 1.0; with 2, between 0.82 and 0.89 (NumPy’s random numbers may change between versions). These are hits on the same points used for training: being right on new data is a different question (lesson 10). The lesson’s playground uses the Adam optimizer instead of plain descent (lesson 9).

What comes next

Computing the gradient of every weight in a network like this is backpropagation (lesson 8). How to train well —batches, optimizers— is lesson 9, and lesson 10 shows that being right on the training points, as here, does not guarantee being right on new data.

Decision lens

What this idea means if your job is to decide, not to code.

  • “More layers” is not a quality measure: each layer adds cost to train and to run. The playground’s accuracy is measured on the same points used for training; ask for a comparison with a simple model on data that neither saw, before accepting that depth is justified.
  • The universal approximation theorem guarantees that a network exists, not that it works in your case: it does not say how much data, compute, or how many neurons are needed. If someone invokes it to sell a solution, ask for evidence on your problem.
  • Training is sensitive to the starting point: the same recipe with another seed can give different results (try it in the playground with “Reset” and few neurons). Ask for the training to be repeated and the variability reported, not just the best run.
  • Size is cost: GPT-3 has ninety-six layers and one hundred seventy-five billion parameters. When they talk about a “deep network,” ask for the parameter count and the cost per task (lessons 24 and 26).

Terms in this lesson

Full transcript
CommentsThis lesson’s public conversation: questions, facts to check, ideas.

Comments live in GitHub Discussions, in the course’s public repository; writing one requires a GitHub account. They load from giscus.app when you open this section. The course reviewers read them, and if a change is accepted, it is applied in both languages. Open the thread on GitHub

Sources