VivaPrep
← Jaber Notes

Jaber Notes · 11 of 16

Neural Network Fundamentals

MLPs, activations, losses, full backprop derivation, init, gradients.

The neural network from first principles: perceptron to MLP, every common activation and its derivative, the full backpropagation derivation with vectorized mini-batch gradients, weight initialization variance analysis, and the vanishing-gradient fix.

Visual reference

Forward pass through a network

inputhiddenoutput
Every input node connects to every hidden node; every hidden node connects to every output node. Each connection has its own weight — that's what training adjusts.

Sigmoid function

10x
Any real number in, a value between 0 and 1 out. Far from zero, the curve flattens — that flat region is where gradients vanish.
Build everything from first principles: from a single neuron up to a full deep network, forward pass, and the complete backpropagation derivation.

Table of contents

  1. From linear models to neural networks
  2. The perceptron and its limits
  3. Multi-layer perceptron (MLP)
  4. Activation functions
  5. Forward pass (computation graph)
  6. Loss functions for deep learning
  7. Backpropagation — full derivation
  8. Universal approximation theorem
  9. Weight initialization
  10. Vanishing and exploding gradients

1. From linear models to neural networks

1.1 Limitation of linear classifiers

A single linear model can only separate classes with a hyperplane. For the XOR problem:

x1x_1x2x_2yy
000
011
101
110

No single line separates the 0s from the 1s. Minsky & Papert (1969) showed the perceptron cannot learn XOR — this was a key insight motivating multi-layer networks.

1.2 The key idea: learned feature transformations

Instead of hand-engineering features, learn a hierarchy of transformations:

xW(1),b(1)h(1)W(2),b(2)h(2)y^.\mathbf{x} \xrightarrow{W^{(1)}, b^{(1)}} \mathbf{h}^{(1)} \xrightarrow{W^{(2)}, b^{(2)}} \mathbf{h}^{(2)} \xrightarrow{\cdots} \hat{y}.

Each layer transforms its input into a new representation. The final layer is (usually) a linear classifier on top of these learned features.


2. The perceptron and its limits

2.1 Original perceptron (Rosenblatt, 1958)

A single neuron:

y^=sign(wx+b),sign(z)={+1z01z<0.\hat{y} = \text{sign}(\mathbf{w}^\top\mathbf{x} + b), \quad \text{sign}(z) = \begin{cases}+1 & z \geq 0 \\ -1 & z < 0.\end{cases}

Perceptron learning rule: for misclassified sample (xi,yi)(\mathbf{x}_i, y_i):

ww+ηyixi.\mathbf{w} \leftarrow \mathbf{w} + \eta\, y_i\, \mathbf{x}_i.

Perceptron convergence theorem: if data is linearly separable, the algorithm converges in a finite number of steps bounded by (\|\mathbf{x}\|_\max \cdot \|\mathbf{w}^\star\| / \text{margin})^2.

Limitation: non-differentiable step function → cannot use gradient descent. Solution: smooth differentiable activations.


3. Multi-layer perceptron (MLP)

3.1 Architecture

An MLP with LL layers. Denote:

  • a(0)=x\mathbf{a}^{(0)} = \mathbf{x} (input, dimension n0n_0)
  • Layer ll: weight matrix W(l)Rnl×nl1\mathbf{W}^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}}, bias b(l)Rnl\mathbf{b}^{(l)} \in \mathbb{R}^{n_l}
  • nln_l = number of neurons in layer ll; nLn_L = output size

Pre-activation (linear combination):

z(l)=W(l)a(l1)+b(l).\mathbf{z}^{(l)} = \mathbf{W}^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}.

Post-activation (element-wise nonlinearity):

a(l)=σ(l)(z(l)),\mathbf{a}^{(l)} = \sigma^{(l)}(\mathbf{z}^{(l)}),

where σ(l)\sigma^{(l)} is an activation function.

Final output: y^=a(L)\hat{\mathbf{y}} = \mathbf{a}^{(L)}.

3.2 Parameters

Total parameters:

θ=l=1Lnlnl1+nl=l=1Lnl(nl1+1).|\theta| = \sum_{l=1}^L n_l \cdot n_{l-1} + n_l = \sum_{l=1}^L n_l(n_{l-1} + 1).

4. Activation functions

4.1 Sigmoid

σ(z)=11+ez(0,1).\sigma(z) = \frac{1}{1+e^{-z}} \in (0,1).

Derivative: σ(z)=σ(z)(1σ(z))\sigma'(z) = \sigma(z)(1-\sigma(z)). Max derivative = 0.25 (at z=0z=0).

Problems:

  • Saturates at large z|z|: gradient → 0 (vanishing gradient).
  • Not zero-centered: outputs always positive → all-positive or all-negative gradients for weights, causing zig-zag updates.
  • Expensive (exp\exp).

Use: output layer for binary classification.

4.2 Tanh

tanh(z)=ezezez+ez=2σ(2z)1(1,1).\tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} = 2\sigma(2z) - 1 \in (-1,1).

Derivative: tanh(z)=1tanh2(z)\tanh'(z) = 1 - \tanh^2(z). Max derivative = 1 (at z=0z=0).

Zero-centered (advantage over sigmoid), but still saturates at large z|z|.

4.3 ReLU (Rectified Linear Unit)

ReLU(z)=max(0,z).\text{ReLU}(z) = \max(0, z).

Derivative: ReLU(z)=1[z>0]\text{ReLU}'(z) = \mathbf{1}[z > 0] (subgradient: set to 0 at z=0z=0).

Advantages:

  • Does not saturate for z>0z > 0.
  • Sparse activation (exactly 0 for z<0z < 0) — natural regularization.
  • Fast to compute.
  • Default choice for hidden layers in modern networks.

Problems:

  • Dying ReLU: if z<0z < 0 always (e.g., large negative bias, large learning rate update), gradient = 0 → neuron never updates.
  • Not zero-centered.
  • Unbounded (no saturation for large positive zz).

4.4 Leaky ReLU and variants

Leaky ReLU(z)={zz>0αzz0,α0.01.\text{Leaky ReLU}(z) = \begin{cases} z & z > 0 \\ \alpha z & z \leq 0 \end{cases}, \quad \alpha \approx 0.01.

Fixes dying ReLU: small but nonzero gradient for negative inputs.

PReLU (Parametric ReLU): α\alpha is learned per-neuron.

ELU (Exponential Linear Unit):

ELU(z)={zz>0α(ez1)z0.\text{ELU}(z) = \begin{cases} z & z > 0 \\ \alpha(e^z - 1) & z \leq 0. \end{cases}

Smooth, zero-centered in expectation, theoretically motivated — but slower than ReLU.

4.5 GELU (Gaussian Error Linear Unit)

GELU(z)=zΦ(z),\text{GELU}(z) = z \cdot \Phi(z),

where Φ\Phi is the standard Gaussian CDF. Approximation: GELU(z)0.5z(1+tanh[2/π(z+0.044715z3)])\text{GELU}(z) \approx 0.5z(1 + \tanh[\sqrt{2/\pi}(z + 0.044715z^3)]).

Stochastic interpretation: GELU = expected value of z1[z>ϵ]z \cdot \mathbf{1}[z > \epsilon] where ϵN(0,1)\epsilon \sim \mathcal{N}(0,1). "Soft" stochastic gating.

Default activation in BERT, GPT, and most modern transformers.

4.6 Softmax (output layer, multi-class)

softmax(z)k=ezkj=1Kezj.\text{softmax}(\mathbf{z})_k = \frac{e^{z_k}}{\sum_{j=1}^K e^{z_j}}.

Properties: outputs sum to 1, all positive → valid probability distribution.

Numerical stability: subtract maxjzj\max_j z_j before exponentiating (doesn't change value):

\text{softmax}(\mathbf{z})_k = \frac{e^{z_k - z_\max}}{\sum_j e^{z_j - z_\max}}.

Jacobian of softmax: softmax(z)k/zj=pk(δkjpj)\partial \text{softmax}(\mathbf{z})_k / \partial z_j = p_k(\delta_{kj} - p_j), where δkj\delta_{kj} is Kronecker delta.

4.7 Comparison summary

ActivationRangeZero-centeredSaturatesDying unitsBest use
Sigmoid(0,1)(0,1)NoYesNoBinary output
Tanh(1,1)(-1,1)YesYesNoLSTM gates, old nets
ReLU[0,)[0,\infty)NoNo (pos)YesDefault hidden
Leaky ReLUR\mathbb{R}NoNoNoWhen dying ReLU observed
GELUR\approx\mathbb{R}YesNoNoTransformers
Softmax(0,1)K(0,1)^KMulti-class output

5. Forward pass (computation graph)

5.1 Example: 2-layer network

Input xRn0\mathbf{x} \in \mathbb{R}^{n_0}, hidden layer hRn1\mathbf{h} \in \mathbb{R}^{n_1}, output scalar y^\hat{y}.

z(1)=W(1)x+b(1)Rn1\mathbf{z}^{(1)} = \mathbf{W}^{(1)}\mathbf{x} + \mathbf{b}^{(1)} \in \mathbb{R}^{n_1}
h=σ(z(1))Rn1\mathbf{h} = \sigma(\mathbf{z}^{(1)}) \in \mathbb{R}^{n_1}
z(2)=w(2)h+b(2)Rz^{(2)} = \mathbf{w}^{(2)\top}\mathbf{h} + b^{(2)} \in \mathbb{R}
y^=σ(2)(z(2))\hat{y} = \sigma^{(2)}(z^{(2)})

5.2 Vectorized over a mini-batch

For mini-batch XRB×n0\mathbf{X} \in \mathbb{R}^{B \times n_0} (batch size BB):

Z(l)=A(l1)W(l)+1b(l)RB×nl.\mathbf{Z}^{(l)} = \mathbf{A}^{(l-1)} \mathbf{W}^{(l)\top} + \mathbf{1}\mathbf{b}^{(l)\top} \in \mathbb{R}^{B \times n_l}.

All operations are matrix multiplications → parallelizable on GPU.


6. Loss functions for deep learning

6.1 Binary cross-entropy

For binary labels yi{0,1}y_i \in \{0,1\}, output y^i=σ(zi)(0,1)\hat{y}_i = \sigma(z_i) \in (0,1):

L=1ni=1n[yilogy^i+(1yi)log(1y^i)].\mathcal{L} = -\frac{1}{n}\sum_{i=1}^n [y_i \log \hat{y}_i + (1-y_i)\log(1-\hat{y}_i)].

6.2 Categorical cross-entropy

For one-hot labels yi{0,1}K\mathbf{y}_i \in \{0,1\}^K, output p^i=softmax(zi)\hat{\mathbf{p}}_i = \text{softmax}(\mathbf{z}_i):

L=1ni=1nk=1Kyiklogp^ik=1ni=1nlogp^i,yi.\mathcal{L} = -\frac{1}{n}\sum_{i=1}^n \sum_{k=1}^K y_{ik} \log \hat{p}_{ik} = -\frac{1}{n}\sum_{i=1}^n \log \hat{p}_{i,y_i}.

6.3 MSE (regression)

L=1ni(yiy^i)2.\mathcal{L} = \frac{1}{n}\sum_i (y_i - \hat{y}_i)^2.

6.4 Huber loss (robust regression)

Lδ(y,y^)={12(yy^)2yy^δδyy^δ22otherwise.\mathcal{L}_\delta(y,\hat{y}) = \begin{cases} \frac{1}{2}(y-\hat{y})^2 & |y-\hat{y}| \leq \delta \\ \delta|y-\hat{y}| - \frac{\delta^2}{2} & \text{otherwise.} \end{cases}

Quadratic near zero (smooth), linear for large errors (robust to outliers).

6.5 Numerical trick: log-sum-exp + softmax

In practice, compute cross-entropy directly from logits (numerically stable):

Li=zi,yi+logkezik.\mathcal{L}_i = -z_{i,y_i} + \log\sum_k e^{z_{ik}}.

This is what torch.nn.CrossEntropyLoss and tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) do internally.


7. Backpropagation — full derivation

7.1 The core idea

Backpropagation is reverse-mode automatic differentiation: efficiently compute L/θ\partial \mathcal{L}/\partial \theta for all parameters θ\theta by reusing computations via the chain rule.

Two passes:

  1. Forward pass: compute and cache all intermediate values z(l),a(l)\mathbf{z}^{(l)}, \mathbf{a}^{(l)}.
  2. Backward pass: propagate gradients from loss back to inputs using the chain rule.

7.2 Define the error signal

Define δ(l)\boldsymbol{\delta}^{(l)} (delta / error) as the gradient of the loss w.r.t. the pre-activation of layer ll:

δ(l)=Lz(l)Rnl.\boldsymbol{\delta}^{(l)} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(l)}} \in \mathbb{R}^{n_l}.

7.3 Output layer delta

For softmax + cross-entropy (very clean result):

δ(L)=p^y.\boldsymbol{\delta}^{(L)} = \hat{\mathbf{p}} - \mathbf{y}.

Derivation: let L=kyklogpk\mathcal{L} = -\sum_k y_k \log p_k where pk=softmax(zk)p_k = \text{softmax}(z_k).

Lzj=kLpkpkzj=kykpkpk(δkjpj)=pjyj.\frac{\partial \mathcal{L}}{\partial z_j} = \sum_k \frac{\partial \mathcal{L}}{\partial p_k} \cdot \frac{\partial p_k}{\partial z_j} = -\sum_k \frac{y_k}{p_k} \cdot p_k(\delta_{kj} - p_j) = p_j - y_j. \quad \checkmark

7.4 Backpropagating through a layer

By the chain rule, from layer l+1l+1 to layer ll:

La(l)=W(l+1)δ(l+1),\frac{\partial \mathcal{L}}{\partial \mathbf{a}^{(l)}} = \mathbf{W}^{(l+1)\top}\boldsymbol{\delta}^{(l+1)},
δ(l)=Lz(l)=La(l)σ(l)(z(l))\boldsymbol{\delta}^{(l)} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(l)}} = \frac{\partial \mathcal{L}}{\partial \mathbf{a}^{(l)}} \odot \sigma'^{(l)}(\mathbf{z}^{(l)})
=(W(l+1)δ(l+1))σ(l)(z(l)),= \left(\mathbf{W}^{(l+1)\top}\boldsymbol{\delta}^{(l+1)}\right) \odot \sigma'^{(l)}(\mathbf{z}^{(l)}),

where \odot is element-wise multiplication and σ\sigma' is the derivative of the activation.

7.5 Gradients w.r.t. parameters

LW(l)=δ(l)a(l1),Lb(l)=δ(l).\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} = \boldsymbol{\delta}^{(l)} \mathbf{a}^{(l-1)\top}, \quad \frac{\partial \mathcal{L}}{\partial \mathbf{b}^{(l)}} = \boldsymbol{\delta}^{(l)}.

For a mini-batch of size BB:

LW(l)=1BΔ(l)A(l1)Rnl×nl1,\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} = \frac{1}{B}\boldsymbol{\Delta}^{(l)\top}\mathbf{A}^{(l-1)} \in \mathbb{R}^{n_l \times n_{l-1}},

where Δ(l)RB×nl\boldsymbol{\Delta}^{(l)} \in \mathbb{R}^{B \times n_l} contains δ(l)\boldsymbol{\delta}^{(l)} for each sample.

7.6 Complete backprop algorithm

Forward pass:
  for l = 1 to L:
    z[l] = W[l] @ a[l-1] + b[l]
    a[l] = activation(z[l])
  L = loss(a[L], y)

Backward pass:
  delta[L] = dL/dz[L]         # output layer delta
  for l = L-1 downto 1:
    dL/dW[l] = delta[l] @ a[l-1].T / B
    dL/db[l] = mean(delta[l], axis=0)
    delta[l] = (W[l+1].T @ delta[l+1]) * sigma'(z[l])

Parameter update (SGD):
  for l = 1 to L:
    W[l] -= lr * dL/dW[l]
    b[l] -= lr * dL/db[l]

7.7 Complexity

Forward pass: O(lnlnl1)O(\sum_l n_l \cdot n_{l-1}) operations. Backward pass: same order — backprop has the same computational cost as 2 forward passes.

Memory: need to cache all activations a(l)\mathbf{a}^{(l)} during forward pass for use in backward pass. Memory \propto depth × batch size × layer size.

7.8 Automatic differentiation (autograd)

Modern frameworks (PyTorch, JAX, TensorFlow) implement dynamic or static computation graphs and handle backprop automatically via:

  • Forward mode AD: accumulate derivatives forward. Efficient for f:RRnf: \mathbb{R} \to \mathbb{R}^n.
  • Reverse mode AD (backprop): accumulate derivatives backward. Efficient for f:RnRf: \mathbb{R}^n \to \mathbb{R} (i.e., scalar loss). This is what we always use in DL.
import torch
import torch.nn as nn

x = torch.tensor([1.0, 2.0], requires_grad=True)
W = torch.randn(3, 2, requires_grad=True)
loss = ((W @ x)**2).sum()
loss.backward()
print(W.grad)   # dL/dW computed automatically

8. Universal approximation theorem

8.1 Statement

Cybenko (1989) / Hornik (1991): An MLP with a single hidden layer of sufficiently many neurons and a non-polynomial activation function can approximate any continuous function on a compact subset of Rn\mathbb{R}^n to arbitrary precision.

Formally: for any ε>0\varepsilon > 0 and continuous f:[0,1]nRf: [0,1]^n \to \mathbb{R}, there exists an MLP gg with one hidden layer such that supxf(x)g(x)<ε\sup_\mathbf{x} |f(\mathbf{x}) - g(\mathbf{x})| < \varepsilon.

8.2 Implications and limits

  • Existence does not imply learnability (may require exponentially many neurons, or gradient descent may not find the solution).
  • Width vs depth tradeoffs: deep networks with O(poly)O(\text{poly}) neurons can represent functions that require exponential neurons in a shallow network.
  • The theorem motivates why neural nets can work, but not how to train them.

9. Weight initialization

9.1 Why initialization matters

  • All-zero init: all neurons produce the same gradient (symmetry) → learn the same features. Symmetry breaking requires random init.
  • Too-large init: activations saturate or explode.
  • Too-small init: vanishing gradients.

9.2 Variance analysis

For layer ll with nln_l inputs, if weights WijN(0,σ2)W_{ij} \sim \mathcal{N}(0, \sigma^2) and inputs have unit variance:

Var(zj(l))=nl1σ2Var(ai(l1)).\text{Var}(z_j^{(l)}) = n_{l-1} \cdot \sigma^2 \cdot \text{Var}(a_i^{(l-1)}).

To keep variance stable across layers: σ2=1/nl1\sigma^2 = 1/n_{l-1}.

9.3 Xavier / Glorot initialization

Derived for tanh/sigmoid activations. Balances forward and backward variance:

σ2=2nl1+nl(uniform: WU[6/(nin+nout),6/(nin+nout)]).\sigma^2 = \frac{2}{n_{l-1} + n_l} \quad \text{(uniform: } W \sim U[-\sqrt{6/(n_\text{in}+n_\text{out})}, \sqrt{6/(n_\text{in}+n_\text{out})}]).

9.4 He / Kaiming initialization

Derived for ReLU activations. Since ReLU zeros out half the inputs on average:

σ2=2nl1(standard: WN(0,2/nin)).\sigma^2 = \frac{2}{n_{l-1}} \quad \text{(standard: }W \sim \mathcal{N}(0, 2/n_\text{in})).

Derivation: For ReLU, E[ReLU(z)2]=12E[z2]\mathbb{E}[\text{ReLU}(z)^2] = \frac{1}{2}\mathbb{E}[z^2] (half the distribution contributes). To keep variance constant: nl1σ212=1σ2=2/nl1n_{l-1} \cdot \sigma^2 \cdot \frac{1}{2} = 1 \Rightarrow \sigma^2 = 2/n_{l-1}.

# PyTorch uses Kaiming uniform by default for Conv and Linear
nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')
nn.init.xavier_uniform_(layer.weight)

9.5 Orthogonal initialization

Initialize weight matrices as random orthogonal matrices. Preserves gradient norms exactly in linear networks; works well for RNNs.


10. Vanishing and exploding gradients

10.1 Formal analysis

In a deep network, the gradient of the loss w.r.t. early layer weights:

LW(1)=δ(L)l=2LW(l)σ(l1)(z(l1)).\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(1)}} = \boldsymbol{\delta}^{(L)} \prod_{l=2}^{L} \mathbf{W}^{(l)\top} \odot \sigma'^{(l-1)}(\mathbf{z}^{(l-1)}).

This is a product of L1L-1 matrices (each element also multiplied by a scalar derivative).

If the eigenvalues of W(l)diag(σ)\mathbf{W}^{(l)\top} \cdot \text{diag}(\sigma') are <1< 1: gradients shrink exponentially → vanishing gradients. If eigenvalues are >1> 1: gradients grow exponentially → exploding gradients.

10.2 Sigmoid/tanh saturation

Sigmoid derivative: max=0.25\max = 0.25 (at z=0z=0). For all z>2|z| > 2: derivative <0.1< 0.1. Product of 10 layers: 0.25101060.25^{10} \approx 10^{-6}. Early layers receive negligible gradients.

10.3 Solutions

ProblemSolution
Vanishing (sigmoid/tanh)Use ReLU; use residual connections (skip connections)
Vanishing (depth)Batch normalization; careful init
Exploding gradientsGradient clipping (>cc/\|\nabla\| > c \Rightarrow \nabla \leftarrow c\nabla/\|\nabla\|)
BothResidual networks (ResNets) — gradient can flow directly through skip connections

10.4 Skip connections (ResNets preview)

a(l+2)=F(a(l),{W(l),W(l+1)})+a(l).\mathbf{a}^{(l+2)} = F(\mathbf{a}^{(l)}, \{W^{(l)}, W^{(l+1)}\}) + \mathbf{a}^{(l)}.

The +a(l)+\mathbf{a}^{(l)} term creates a direct path for gradients:

La(l)=La(l+2)(Fa(l)+I).\frac{\partial \mathcal{L}}{\partial \mathbf{a}^{(l)}} = \frac{\partial \mathcal{L}}{\partial \mathbf{a}^{(l+2)}} \cdot \left(\frac{\partial F}{\partial \mathbf{a}^{(l)}} + \mathbf{I}\right).

The identity I\mathbf{I} ensures gradients can flow even if F/a(l)0\partial F/\partial \mathbf{a}^{(l)} \approx 0.


*File: notes/11_neural_network_fundamentals.md — next: notes/12_training_deep_networks.md*