19 / 27 · Neural Networks
From linear models to networks
Why stacking linear maps needs a nonlinearity, the multilayer perceptron with its layers, shapes and activation functions, what the universal approximation theorem promises and what it does not, depth against width, a network learning its own features, and the output layers and losses for regression and classification.
Gradient boosting · Logistic regression · Multiclass and multilabel classification
On this page 13
The previous note ended with the strongest tool for tables: boosted trees, and stacking on top of them. Every model of the course so far takes its features as given. Least squares and logistic regression fit a linear function of them, kernels fix a nonlinear feature map in advance, and trees split on the columns someone prepared. That works when the columns already mean something, such as an income, an age or a count of purchases. It fails for raw signals. A single pixel, a single audio sample or a single token identity usually says little on its own, and for decades the useful features of images, sound and text were designed by hand: edge histograms, spectral coefficients, n-gram counts. A neural network learns the feature map from the data, together with the linear model on top of it, by the same gradient descent as before. This note builds the network from linear models, states what such networks can represent, and sets up their outputs and losses; the next note computes their gradients.
Notation. An input . A network has layers of widths , with . Layer has a weight matrix and a bias ; it computes the pre-activation and the activation , with and an activation function applied to each coordinate. The last layer has no activation: its output is a prediction or a vector of logits. is the logistic sigmoid, and collects all weights and biases.
Why stacking linear layers is not enough#
The first idea is to compose linear maps. It gains nothing: two affine layers are one affine layer,
and by induction so are any number of them. Depth without a nonlinearity adds parameters, not expressiveness: the stack is a linear model with weight matrix , whose rank is at most the smallest width, so a narrow layer can only restrict it.
A nonlinearity between the layers changes this. With one hidden layer,
the output is a linear model applied to the features , and those features are themselves learned.
Worked example: XOR. The four points with labels cannot be separated by a line. If held exactly for the two points labelled 1, then and , while and . Adding the first two inequalities gives , a contradiction. Two ReLU units, , are enough (Goodfellow, Bengio and Courville, 2016, section 6.1). Take , and :
| 0 | 0 | 0 | 0 | |
| 1 | 1 | 0 | 1 | |
| 1 | 1 | 0 | 1 | |
| 2 | 2 | 1 | 0 |
In the hidden coordinates the inputs become , , and . The two points of class 1 land on the same spot, and a line, such as , separates the classes. The hidden layer has made the problem linear.
The multilayer perceptron#
Stacking such layers gives the multilayer perceptron (MLP), also called a fully connected or feed-forward network:
Each hidden unit computes for one row of its weight matrix: a linear model of the previous layer followed by a fixed nonlinearity. With , a hidden unit has the functional form of a logistic regression, a sigmoid of an affine function, and the network is a linear model on features of that form computed from features of that form. The analogy stops at the form: a hidden unit has no observed 0/1 target of its own, and its weights are trained jointly with the rest of the network.
In practice the network processes a mini-batch of examples at once, stored as the rows of . Then every layer is one matrix product, , with and the bias added to every row. PyTorch’s nn.Linear(d_in, d_out) stores its weight with shape (d_out, d_in), the shape of here, and computes x @ W.T + b.
Worked example: shapes. A classifier of images of digits into 10 classes, with layer widths :
| layer | weight | bias | parameters | output for a batch of 32 |
|---|---|---|---|---|
| input | ||||
| 1 | 128 | 8,320 | ||
| 2 | 64 | 8,256 | ||
| 3 (logits) | 10 | 650 |
The network has parameters, almost all of them in the weight matrices; the count grows with the product of neighbouring widths.
Activation functions#
| range | typical use | |||
|---|---|---|---|---|
| sigmoid | output probabilities, gates | |||
| tanh | recurrent networks | |||
| ReLU | for , for | default for hidden layers | ||
| GELU | Transformers |
Here and are the distribution function and the density of the standard normal. Three properties matter:
- Saturation. The sigmoid and tanh flatten for large , where their derivative is close to zero. A unit driven there passes almost no gradient back, and in a stack of sigmoid layers the sigmoid’s derivative contributes a factor of at most per layer; the Generative Models course works this out.
- ReLU (Nair and Hinton, 2010; Glorot, Bordes and Bengio, 2011) has slope exactly 1 wherever it is active and is cheap to compute. Its weakness is the other side: a unit whose pre-activation is negative for every input gets zero gradient and never recovers, a “dead” unit.
- GELU (Hendrycks and Gimpel, 2016, section 2) multiplies by the probability that a standard normal variable is below it. It is a smooth ReLU that lets small negative values through: its minimum is at . BERT and GPT-2 use it in the feed-forward part of each Transformer block.
The choice is less important than having a nonlinearity at all. The next section shows the one property that the universal approximation theorem for a single hidden layer requires of the activation: it must not be a polynomial. That is a condition of the theorem, not of every useful network.
What one hidden layer can represent#
Optional depth: the approximation theorems behind the key idea below, and what they leave open. The rest of the note does not depend on it.
The theorem. Let be any continuous sigmoidal function, meaning as and as . For every continuous function on the unit cube and every there are a number and parameters , such that
(Cybenko, 1989, Theorem 2). is an MLP with one hidden layer of units and a linear output. Hornik (1991), Theorem 2, extends the statement to any continuous, bounded and nonconstant activation on any compact (closed and bounded) set, and Leshno et al. (1993), Theorem 1, settle it for unbounded activations such as the ReLU: for a locally bounded, piecewise continuous , one-hidden-layer networks approximate every continuous function uniformly on compact sets if and only if is not a polynomial. A polynomial activation of degree fails because the network is then a polynomial of degree at most in , whatever its width.
Worked example: ReLU units as a piecewise-linear fit. In one dimension the construction is explicit. Put knots on and let be the slope of the straight segment from to . The broken line through the points is
a network with one hidden layer of ReLU units: unit switches on at the knot and changes the slope by . For the largest error on is , , and with units. Doubling the width divides the error by about 4, as the interpolation bound with spacing predicts.
What the theorem does not promise#
- A reasonable size. The theorem is about existence. Cybenko’s own summary (section 5) expects that most approximation problems need astronomical numbers of units, because of the curse of dimensionality met in the nearest neighbours note. The piecewise-linear construction tiles the input: with 16 knots per axis in dimensions it needs cells. Whether far fewer units suffice depends on the function. Barron (1993) shows that for functions whose Fourier transform has a finite first moment , sigmoidal units reach an integrated squared error of order , with no dependence on the dimension, while any fixed set of basis functions cannot beat order on that class.
- That training finds it. The weights exist; the loss as a function of them is not convex, and gradient descent may stop elsewhere. The XOR experiment below shows it happening.
- That a sample suffices. Approximating the true function and estimating it from noisy examples are different problems. A wide network can fit any training set, noise included, which is overfitting in its purest form.
- Anything outside the region. The guarantee holds on a compact set. Beyond the range of the training inputs a ReLU network simply continues linearly, which is rarely what the data would do there.
Depth versus width#
Optional depth: why depth can be exponentially more efficient than width, with an exact example. The rest of the note does not depend on it.
If one hidden layer is universal, why use many? Because composition is much more efficient than addition for some functions. A one-dimensional example makes this exact.
Worked example: the tent map. On the network
is a tent: for and for , with values in . Composing it with itself, , runs the tent twice up and down: 4 linear pieces. Each further composition doubles the count, so has pieces. As a network, has hidden layers of 2 ReLU units each (Telgarsky, 2016, Lemma 3.10).
A network with one hidden layer of ReLU units computes on a line. This is linear between the break points , so it has at most pieces. To represent exactly it needs units: 31 against the deep network’s 10 for , and against 40 for .
Depth multiplies the number of linear pieces
The general statements behind the example: the number of linear regions of a ReLU network can grow exponentially with depth and only polynomially with width (Montúfar et al., 2014), and there are functions computed by deep narrow ReLU networks that no network with far fewer layers approximates unless it is exponentially wide (Telgarsky, 2016, Theorem 1.1). Depth lets later layers reuse what earlier layers computed. In image networks the early layers detect edges and later layers combine them into parts and objects, as the convolution section of the Generative Models course describes.
Depth has a price. Every layer multiplies the gradient by another factor, and deep stacks are harder to optimize than shallow ones; Training deep networks deals with that. Width also helps optimization: a wider network has many more ways to fit the data, and gradient descent finds one of them more reliably, as the next experiment shows.
A network learns its own features#
The XOR construction above was designed by hand. The same features come out of training. Take 200 points in four clusters around , with class 1 where the signs differ, and a network with two tanh hidden units and a sigmoid output, trained by gradient descent (Adam, 300 full-batch steps) on the binary cross-entropy:
A network finds its own features for XOR
The trained network classifies all 200 points correctly. Its first hidden unit, , is negative only when both coordinates are negative: an OR. The second, , is positive only when both are positive: an AND. The output unit computes , which is large exactly when the OR is on and the AND is off. Up to the smooth activation, these are the two features of the hand-made solution, found by fitting.
The optimization is not guaranteed, though. Training the same two-unit network from 10 random initializations, only 4 runs separate the classes; the other 6 stop at a poor solution with 65–75% accuracy. With four hidden units all 10 runs succeed. Extra width costs parameters and buys an easier optimization problem.
The same reading holds for any network: the last layer is a linear model on the features computed by the layers below it. On the two-moons data of the Code section (1000 points, noise 0.2, half held out), logistic regression on the raw coordinates reaches a test accuracy of , while a network reaches . Fitting a fresh logistic regression to the network’s 16 last-layer features also gives : everything nonlinear happened in the feature map, and the classifier on top is the logistic regression of earlier notes.
Output layers and losses#
The output layer and the loss are chosen together, and the choice is the probabilistic one of the linear models: the network outputs the parameters of a distribution for , and the loss is the negative log-likelihood, as derived in the maximum likelihood note.
| task | output units | output activation | model for | loss | PyTorch |
|---|---|---|---|---|---|
| regression | 1 (or ) | none | squared error | MSELoss | |
| two classes | 1 logit | sigmoid | binary cross-entropy | BCEWithLogitsLoss | |
| classes | logits | softmax | cross-entropy | CrossEntropyLoss | |
| labels | logits | sigmoids | independent Bernoullis | sum of binary cross-entropies | BCEWithLogitsLoss |
The binary row makes the last layer a logistic regression on , and the two lower rows are the softmax and the one-vs-rest models of the multiclass note, now on learned features. With the softmax and true class , the cross-entropy is
Worked example. For the logits , with sum , so . If the true class is the first, the loss is ; if it were the third, the loss would be .
The PyTorch losses in the table take logits, not probabilities, and they compute as with . For logits the naive overflows, while the shifted form gives the loss exactly. Applying a sigmoid or softmax in the model and a loss on top of the probabilities loses this protection. The same softmax over a vocabulary is the output layer of every neural language model in the NLP course, and the Generative Models course explains why minimizing cross-entropy minimizes a KL divergence.
Convolutions: a constrained linear layer#
When the input has a grid structure, such as the pixels of an image, the samples of a sound or the tokens of a sentence, a fully connected first layer wastes parameters and ignores locality. A colour image has inputs; a fully connected layer producing a map with 16 channels needs million weights. A convolution layer applies the same small kernel at every position instead: each output looks only at a neighbourhood (locality), and the kernel is shared across positions (weight sharing), so the same layer with kernels has parameters. It is still a linear layer, with a weight matrix constrained to be sparse and repetitive, and everything in this note and the next applies to it. Its shapes, parameters and initialization are worked out in the Generative Models course, and the NLP course uses one-dimensional convolutions over the tokens of a text.
What changed#
| Linear models, kernels, boosting | Neural networks | |
|---|---|---|
| Features | given, hand-made, or fixed by a kernel | learned by the hidden layers |
| Model | linear in fixed features, or a sum of trees | compositions of linear maps and nonlinearities |
| What it can represent | limited by the features | any continuous function on a bounded region, with enough units |
| Output and loss | sigmoid or softmax with cross-entropy, squared error | the same, as the last layer |
| Training | convex (linear, logistic) or greedy (trees) | nonconvex, by gradient descent through all layers |
Limitations#
- Nonconvex training. Different initializations give different networks, and some runs fail, as the two-unit XOR network did in 6 of 10 runs. Results must be checked over seeds.
- Data and compute. A network has many parameters and needs many examples to estimate them. On typical tabular data of moderate size, tree ensembles still match or beat networks (Grinsztajn, Oyallon and Varoquaux, 2022); networks win where features must be learned from raw signals.
- Existence is not construction. The universal approximation theorem gives no width, no training procedure and no sample size.
- Many design choices. Depth, width, activation, initialization, learning rate and regularization all matter; the last note of this section collects the standard choices.
- Opaque features. A learned feature is a direction in a hidden layer, not a named column, which makes the model harder to explain than a tree or a linear model.
What you should remember#
- Linear layers compose to a linear layer; a nonlinearity between them is what makes depth useful.
- An MLP computes with ; the last layer is a linear model (logits) on learned features.
- ReLU is the default hidden activation, GELU its smooth version; sigmoid and tanh saturate.
- One hidden layer approximates any continuous function on a compact set iff the activation is not a polynomial (Cybenko, Hornik, Leshno et al.), but without a bound on the width, a guarantee of training or of generalization.
- Depth can be exponentially more efficient than width: layers of 2 ReLU units make linear pieces, one layer needs units.
- Output layer and loss come from the likelihood: identity with squared error, sigmoid with binary cross-entropy, softmax with cross-entropy; pass logits to the loss.
Code#
Linear layers collapsing, XOR by hand, parameter counts, the softmax loss, features learned on two moons, ReLU interpolation and the tent map:
import numpy as np, torch
from torch import nn
from sklearn.datasets import make_moons
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(0)
relu = lambda z: np.maximum(z, 0)
# two linear layers are one linear layer: W2 (W1 x + b1) + b2 = (W2 W1) x + (W2 b1 + b2)
W1, b1, W2, b2, x = rng.normal(size=(4, 3)), rng.normal(size=4), rng.normal(size=(2, 4)), rng.normal(size=2), rng.normal(size=3)
print(np.allclose(W2 @ (W1 @ x + b1) + b2, (W2 @ W1) @ x + (W2 @ b1 + b2))) # True
# XOR with two ReLU units: h = ReLU(x1 + x2, x1 + x2 - 1), output h1 - 2 h2
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
h = relu(X @ np.array([[1, 1], [1, 1]]) + np.array([0, -1]))
print(h.tolist(), (h @ np.array([1, -2])).tolist()) # [[0, 0], [1, 0], [1, 0], [2, 1]] [0, 1, 1, 0]
# parameters of a 64 -> 128 -> 64 -> 10 network
mlp = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 10))
print(sum(p.numel() for p in mlp.parameters())) # 17226
# softmax and cross-entropy for the logits (2, 1, -1) when the true class is the first
z = torch.tensor([2.0, 1.0, -1.0])
print(torch.softmax(z, 0).numpy().round(3), round(nn.functional.cross_entropy(z[None], torch.tensor([0])).item(), 3))
# [0.705 0.259 0.035] 0.349
# two moons: logistic regression on (x1, x2) against a network 2 -> 16 -> 16 -> 1
Xm, ym = make_moons(1000, noise=0.2, random_state=0)
Xtr, Xte, ytr, yte = train_test_split(Xm, ym, test_size=0.5, random_state=0)
print(round(LogisticRegression().fit(Xtr, ytr).score(Xte, yte), 3)) # 0.848
torch.manual_seed(0)
body = nn.Sequential(nn.Linear(2, 16), nn.ReLU(), nn.Linear(16, 16), nn.ReLU())
head = nn.Linear(16, 1)
opt = torch.optim.Adam([*body.parameters(), *head.parameters()], lr=0.01)
Xt, yt = torch.tensor(Xtr, dtype=torch.float32), torch.tensor(ytr, dtype=torch.float32)
for step in range(1000):
opt.zero_grad()
loss = nn.functional.binary_cross_entropy_with_logits(head(body(Xt)).squeeze(1), yt)
loss.backward()
opt.step()
with torch.no_grad():
Ftr, Fte = body(Xt).numpy(), body(torch.tensor(Xte, dtype=torch.float32)).numpy()
acc = ((head(torch.tensor(Fte)).squeeze(1) > 0).numpy() == yte).mean()
# the network, and a fresh logistic regression on the network's 16 last-layer features
print(round(float(acc), 3), round(LogisticRegression(max_iter=1000).fit(Ftr, ytr).score(Fte, yte), 3)) # 0.958 0.958
# one hidden layer of N ReLU units interpolating sin(2 pi x) at N + 1 knots: the error falls like 1/N^2
f = lambda t: np.sin(2 * np.pi * t)
grid = np.linspace(0, 1, 10001)
for N in (4, 8, 16, 32):
knots = np.linspace(0, 1, N + 1)
slopes = np.diff(f(knots)) * N
c = np.diff(slopes, prepend=0.0)
approx = f(0) + relu(grid[:, None] - knots[:-1]) @ c
print(N, round(float(np.abs(approx - f(grid)).max()), 4))
# 4 0.2105 | 8 0.0704 | 16 0.0188 | 32 0.0048
# depth: the tent map g(x) = 2 ReLU(x) - 4 ReLU(x - 1/2) composed k times has 2^k linear pieces
g = lambda t: 2 * relu(t) - 4 * relu(t - 0.5)
t = np.linspace(0, 1, 2**12 + 1)
for k in (1, 2, 3, 5):
y = t.copy()
for _ in range(k):
y = g(y)
slopes = np.round(np.diff(y) / np.diff(t), 6)
print(k, 1 + int((slopes[1:] != slopes[:-1]).sum()))
# 1 2 | 2 4 | 3 8 | 5 32
Next#
A network is a composition of simple functions with thousands or millions of parameters, and fitting it by gradient descent needs the gradient of the loss with respect to every one of them at every step. Differentiating numerically would cost one forward pass per parameter. The next note shows how the chain rule, applied from the loss backwards through the computational graph, gives all of them for about the cost of a single forward pass.
References#
- G. Cybenko. Approximation by superpositions of a sigmoidal function. Mathematics of Control, Signals, and Systems, 2(4):303–314, 1989. Theorem 2 (one hidden layer is dense in ); section 5 (on the number of units).
- K. Hornik. Approximation capabilities of multilayer feedforward networks. Neural Networks, 4(2):251–257, 1991. Theorems 1 and 2.
- M. Leshno, V. Ya. Lin, A. Pinkus, S. Schocken. Multilayer feedforward networks with a nonpolynomial activation function can approximate any function. Neural Networks, 6(6):861–867, 1993. Theorem 1.
- A. R. Barron. Universal approximation bounds for superpositions of a sigmoidal function. IEEE Transactions on Information Theory, 39(3):930–945, 1993.
- M. Telgarsky. Benefits of depth in neural networks. COLT, 2016. Theorem 1.1 (depth separation); Lemma 3.10 (the tent map as a ReLU network).
- G. Montúfar, R. Pascanu, K. Cho, Y. Bengio. On the number of linear regions of deep neural networks. NeurIPS, 2014.
- V. Nair, G. E. Hinton. Rectified linear units improve restricted Boltzmann machines. ICML, 2010.
- X. Glorot, A. Bordes, Y. Bengio. Deep sparse rectifier neural networks. AISTATS, 2011.
- D. Hendrycks, K. Gimpel. Gaussian error linear units (GELUs). arXiv:1606.08415, 2016. Section 2.
- L. Grinsztajn, E. Oyallon, G. Varoquaux. Why do tree-based models still outperform deep learning on typical tabular data? NeurIPS Datasets and Benchmarks Track, 2022.
- I. Goodfellow, Y. Bengio, A. Courville. Deep Learning. MIT Press, 2016. Section 6.1 (learning XOR), sections 6.2–6.4 (output units, hidden units, architecture and universal approximation).
- C. M. Bishop. Pattern Recognition and Machine Learning. Springer, 2006. Section 5.1 (feed-forward network functions).
- E. Sokolov et al. Машинное обучение (HSE), lecture notes, lecture 7: introduction to deep learning.