Neural Network Basics
Perceptron, forward pass, backprop, loss choice and a mini framework. Formulas, shapes, defaults and fixes.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Defaults and numbers
- Troubleshooting
- Questions and tips
Forward and backward through a 2-layer net#
From Lesson 03.03, Lesson 03.02
Value node with backward#
out = Value(self.data + other.data, (self, other), '+')self.grad += out.gradadd passes the gradient throughother.grad += out.gradout = Value(self.data * other.data, (self, other), '*')self.grad += other.data * out.gradmul swaps the inputsother.grad += self.data * out.gradself.grad += (s * (1 - s)) * out.grad # sigmoid reuses sbuild_topo(self)topological sort of the graphself.grad = 1.0dL/dL = 1for v in reversed(topo): v._backward()push gradients to childrenMini framework training loop#
model = Sequential(Linear(2, 16), ReLU(), Linear(16, 16), ReLU(), Linear(16, 8), ReLU(), Linear(8, 1), Sigmoid())criterion = BCELoss()optimizer = Adam(model.parameters(), lr=0.01)model.train()dropout and batch stats onfor batch_inputs, batch_targets in loader: for x, t in zip(batch_inputs, batch_targets): pred = model.forward(x) loss = criterion(pred, t) optimizer.zero_grad() grad = criterion.backward()dL/dpred model.backward(grad)modules in reverse optimizer.step()model.eval()running stats, no dropoutDefaults in the lesson code#
| Setting | Default | Note |
|---|---|---|
Perceptron learning_rate | 0.1 | epochs= |
Adam lr | 0.001 | beta1=0.9, beta2=0.999, eps=1e-8 |
SGD lr | 0.01 | param -= |
Dropout p | 0.5 | Survivors scale by 1/(1-p) |
| BatchNorm | momentum= | eps=1e-5, running stats for eval |
BCE clip eps | 1e-15 | BCELoss in the framework uses 1e-7 |
alpha | 0.1 | Label smoothing, target 0.91 for 10 classes |
temperature | 0.07 | Contrastive loss, SimCLR default |
From Lesson 03.01, Lesson 03.05, Lesson 03.10
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Perceptron never converges on XOR | XOR is not linearly separable | Add a hidden layer |
| Classifier outputs 0.5 for all | MSE loss on classification | Use cross-entropy |
| Early layers do not learn | Sigmoid shrinks gradients per layer | ReLU (Lesson 04), scaled init |
| Loss is inf or NaN | \log(0) = -\infty | Clip p to [eps, 1 - eps] |
math.exp(1000) overflow | Large input to sigmoid | Clamp to [-500, 500] |
| Gradient wrong for a reused value | = overwrites other paths | Accumulate with += |
| Gradients grow each step | Old gradients stay | Call zero_grad() first |
| Test results change each run | Dropout still on | Call model.eval() |
From Lesson 03.01, Lesson 03.02, Lesson 03.03, Lesson 03.05, Lesson 03.10
One training step#
From Lesson 03.10, Lesson 03.03
Formulas#
- Step
- \text{step}(z) = 1 if z \ge 0, else 0.
- Perceptron rule
w_i = w_i + learning_rate * error * x_i, with error = y_true minus y_pred.- Sigmoid
- \sigma(z) = \frac{1}{1 + e^{-z}}, range (0, 1). Its derivative is a(1 - a).
- Layer
- z = W x + b, then a = \text{sigmoid}(z).
- MSE
- \frac{1}{n} \sum (y_{\text{pred}} - y_{\text{true}})^2, gradient \frac{2}{n}(y_{\text{pred}} - y_{\text{true}}).
- BCE
- -\left(y \log(p) + (1 - y) \log(1 - p)\right)
- CCE
- -\sum_i y_i \log(p_i). Only the true class contributes.
- Softmax plus CE gradient
- probability minus 1 for the true class, probability for the others.
Tasks in code#
| Task | Code |
|---|---|
| Train a perceptron on AND | p_and = Perceptron(2) then p_and.train(and_data) |
| Chain layers | Network([hidden, |
| Autograd net, 2-4-1 | net = |
| Backprop from the loss | total_loss.backward() |
| Stable multi-class loss | F.cross_entropy(logits, |
| Smoothed targets | label_smoothing= |
| Pick an optimizer | Adam(model.parameters(), |
| Batch the data | DataLoader(train_data, |
From Lesson 03.01, Lesson 03.02, Lesson 03.03, Lesson 03.05, Lesson 03.10
Which loss#
| Command | What it does |
|---|---|
F.mse_loss | Regression. Penalizes outliers quadratically |
| MAE / Huber | Regression that is robust to outliers |
F.binary_cross_entropy | Binary classification on probabilities |
F.cross_entropy | Multi-class. Log-softmax and NLL in one step |
| Label smoothing | Add when multi-class CE is overconfident |
| Focal Loss | Imbalanced classes. Down-weights easy examples |
| Triplet Loss | Anchor, positive, negative with a margin |
| InfoNCE | Large batch self-supervised embeddings |
From Lesson 03.05
Numbers to remember#
- The sigmoid derivative peaks at 0.25 when a = 0.5.
- Ten sigmoid layers scale the gradient by at most 0.25^{10} = 0.000001.
- MSE that predicts 0.5 on a 50/50 binary task gives 0.25 and learns nothing.
- Cross-entropy gives -\log(0.5) = 0.693 and -\log(0.99) = 0.01.
- A wrong confident guess p = 0.01 costs -\log(0.01) = 4.6.
- Random guess on 10 classes costs -\log(0.1) = 2.3.
- Focal loss with \gamma = 2 weights an easy example at 0.01, a hard one at 0.81.
- One backward pass gives all gradients. Nudging each weight needs one forward pass per weight.
From Lesson 03.03, Lesson 03.05, Lesson 03.02
Questions#
- What shape is W?
- Rows match the current layer. Columns match the previous layer.
- Why sigmoid, not step, for training?
- Sigmoid is smooth, so gradients exist.
- Why store activations?
- The backward pass needs them. Memory buys one pass.
- Why topological sort?
- A node gets all its gradient before it passes it on.
- Why
F.cross_entropy, not softmax then log? - One operation is numerically stable.
- Why
train()andeval()? - Dropout and BatchNorm act differently in each mode.
- Why is
zero_grada separate call? - Gradients accumulate with
+=until you reset them.
From Lesson 03.01, Lesson 03.02, Lesson 03.03, Lesson 03.05, Lesson 03.10