Key ideas
- Neuron: pre-activation ( for a row vector ), then output . The bias is a learnable constant added to the weighted sum; it shifts the threshold, so the unit need not pass through the origin (a sigmoid unit is centred at ).
- Perceptron: one neuron with a step activation, i.e. a binary linear classifier whose decision boundary is a single hyperplane. On a misclassified example it updates (labels ). It is guaranteed to converge only when the data are linearly separable, so it cannot learn XOR1.
- MLP / feedforward network: affine layers with nonlinear activations, wired as a graph with no directed cycle (skip connections are allowed, feedback loops make it recurrent). Depth counts the learned layers (hidden + output), not the input layer, which computes nothing2: widths 784–256–10 give depth 2.
- Why nonlinearity: composing affine maps gives an affine map, so without activations a stack of layers collapses into one, , and extra layers add no expressive power2; a narrow linear layer can even cap the rank of the overall map (a 50–5–50 linear net has rank at most 5).
- Universal approximation: with a non-polynomial activation, one hidden layer that is wide enough can approximate any continuous function on a compact (closed and bounded) region to any accuracy3. It is an existence result: it gives no useful width, no way to find the weights3, and no promise that a net fitting the training data generalizes. Training is where the real difficulty lies, and deeper nets often represent the same function far more compactly2.
- ReLU networks compute continuous piecewise-linear functions. On a scalar input, each unit of a one-hidden-layer net adds at most one kink ( units, at most pieces), while depth multiplies pieces: the 2-unit tent map on has 2 pieces that each sweep once, so every composition doubles the count: stacked tent layers give pieces4.
| Activation | Formula | Range | Derivative |
|---|---|---|---|
| sigmoid | , at most 0.25 (at 0) | ||
| tanh | , at most 1 (at 0) | ||
| ReLU | 0 if , 1 if | ||
| leaky ReLU / PReLU | all reals () | 1 if , if ; | |
| GELU / SiLU (Swish) | / | dips slightly below 0 (GELU to about ) | smooth, not monotonic |
- Sigmoid and tanh saturate: for large their slope is near 0, so gradients vanish2; through sigmoid layers the activations alone multiply a gradient by at most . ReLU passes slope 1 through active units and 0 through inactive ones; at exactly the left derivative 0 is used2, as in PyTorch (see In code). A unit whose pre-activation is negative on every training example is "dead": neither it nor its incoming weights get any gradient. PReLU keeps a slope for 2, and GELU ( = standard normal CDF) and SiLU are smooth ReLU-like curves with a small negative dip5.
- Output layer and loss follow the target:
| Target | Output | Loss |
|---|---|---|
| real number | identity | MSE (predicts the conditional mean) or MAE (median) |
| one binary label | sigmoid | binary cross-entropy (log loss) |
| one of exclusive classes | softmax | categorical cross-entropy |
| several independent yes/no labels | sigmoids | sum of binary cross-entropies |
- PyTorch's
nn.CrossEntropyLosstakes raw logits and is LogSoftmax followed by NLLLoss6, so no softmax layer goes before it;binary_cross_entropy_with_logitslikewise takes a logit (see In code). Softmax only depends on differences of logits, since ; implementations therefore subtract the largest logit before exponentiating. Scaling the logits (temperature) does change the probabilities. - Backpropagation applies the chain rule from the loss back towards the inputs, reusing values stored during the forward pass, one reason training needs more memory than prediction7. It only computes gradients; the optimizer then updates the weights. When a value feeds several later nodes, its gradient is the sum over those paths, and every gradient has the same shape as the variable it belongs to.
Formulas
Dense layer on a minibatch in the row convention: with input ( for the first layer), the weight from input to unit , the bias row broadcast to every example, and upstream gradient :
These are the column-convention rules for , namely for the weights and for the input7, transposed and summed over the batch; for a mean loss the is already inside . The count does not depend on the batch size: a 3–5–2 net has parameters.
Through an activation, 7. Softmax with cross-entropy ( below, in its log-sum-exp form) gives the logit gradient 8: the components sum to 0 and only the true class's is negative. Binary cross-entropy with a sigmoid gives ; half-squared error with a sigmoid gives , which vanishes when the unit saturates, and with an identity output simply .
Gradient checking: by Taylor expansion the central difference has error , the forward difference only .
Worked example
A 2–2–1 network in the row convention, , , , with , (row = weights from input ), , , , target and .
- Forward: , so , and .
- Backward: , so and . Then ; multiplying by the ReLU slopes gives .
- Weight gradients: , i.e. , giving , and . The inactive second unit gets no gradient at all.
In code
import torch
import torch.nn.functional as F
z = torch.tensor([[2.0, 1.0, 0.0]], requires_grad=True) # logits, true class 0
loss = F.cross_entropy(z, torch.tensor([0]))
loss.backward()
print(round(loss.item(), 4)) # 0.4076
print(torch.softmax(z, 1).detach()) # tensor([[0.6652, 0.2447, 0.0900]])
print(z.grad) # tensor([[-0.3348, 0.2447, 0.0900]]) = p - y
x = torch.tensor([-2.0, 0.0, 3.0], requires_grad=True)
torch.relu(x).sum().backward()
print(x.grad) # tensor([0., 0., 1.]) slope 0 at exactly 0
bce = F.binary_cross_entropy_with_logits(torch.tensor([2.0]), torch.tensor([0.0]))
print(round(bce.item(), 4)) # 2.1269 = -log(1 - sigmoid(2))
Common pitfalls
- ReLU and sigmoid are both monotonic (GELU and SiLU are not); tanh saturates just like the sigmoid, since it is a rescaled sigmoid.
- The sigmoid's derivative is computed from its output, with ; plugging the input into gives wrong values, even negative ones.
- Softmax is for mutually exclusive classes; multilabel problems need one sigmoid per label.
- Parameter counts include one bias per unit; activations, softmax and the batch size add no parameters.
- Stacking layers without a nonlinearity adds no expressive power.
- Backpropagation computes gradients; it does not update the weights, split the data or pick hyperparameters.
- Accuracy and the 0-1 loss change only in jumps (zero gradient almost everywhere), so networks are trained on cross-entropy instead.
- MSE pulls the prediction to the conditional mean: if a delivery takes 15 or 45 minutes with equal odds, it predicts about 30, a time that almost never happens.
References
- Wikipedia: Perceptron — Learning algorithm for a single-layer perceptron (Steps; Convergence of one perceptron on a linearly separable dataset); History, Perceptrons (1969)
- Dive into Deep Learning — Sec. 5.1 Multilayer Perceptrons (5.1.1.2 Incorporating Hidden Layers, 5.1.1.3 From Linear to Nonlinear, 5.1.1.4 Universal Approximators, 5.1.2 Activation Functions)
- Wikipedia: Universal approximation theorem — Introduction; Arbitrary-width case
- Telgarsky (2016), Benefits of depth in neural networks — Sec. 3.3 Many layers, many oscillations (Lemmas 3.10–3.11, the ReLU triangle map)
- Hendrycks & Gimpel (2016), Gaussian Error Linear Units (GELUs) — Sec. 2 GELU Formulation (GELU and SiLU); Sec. 4 Discussion
- PyTorch documentation: torch.nn.CrossEntropyLoss — CrossEntropyLoss (input is unnormalized logits; equivalent to LogSoftmax followed by NLLLoss)
- Dive into Deep Learning — Sec. 5.3.3 Backpropagation (Eqs. 5.3.11–5.3.14), 5.3.4 Training Neural Networks
- Dive into Deep Learning — Sec. 4.1.2.2 Softmax and Cross-Entropy Loss (Eqs. 4.1.9–4.1.10)