Thomas Piacentini
← Atelier

Fiche

Neural Network Basics

Texte original · anglais

Perceptron, forward pass, backprop, loss choice and a mini framework. Formulas, shapes, defaults and fixes.

Forward and backward through a 2-layer net

forwarda1a2backwardInput xno computationclientHidden layera1 = sigmoid(W1 x + b1)processOutput layera2 = sigmoid(W2 a1 + b2)processLossL = (a2 - y)^2storedL/dz22(a2 - y) a2 (1 - a2)dL/dW2 = dL/dz2 a1dL/da1 = dL/dz2 W2dL/dz1dL/da1 a1 (1 - a1)dL/dW1 = dL/dz1 xdL/db1 = dL/dz1
Nodes and connections
NodeRoleKind
Input xno computationclient
Hidden layera1 = sigmoid(W1 x + b1)process
Output layera2 = sigmoid(W2 a1 + b2)process
LossL = (a2 - y)^2store
dL/dz22(a2 - y) a2 (1 - a2)
dL/dW2 = dL/dz2 a1dL/da1 = dL/dz2 W2
dL/dz1dL/da1 a1 (1 - a1)
dL/dW1 = dL/dz1 xdL/db1 = dL/dz1
FromToConnection
Input xHidden layerforward
Hidden layerOutput layera1
Output layerLossa2
LossdL/dz2backward
dL/dz2dL/dW2 = dL/dz2 a1
dL/dW2 = dL/dz2 a1dL/dz1
dL/dz1dL/dW1 = dL/dz1 x
Sources

One training step

  1. DataLoader yields a batch of inputs and targets. Shuffles each epoch.
  2. Model runs the forward pass layer by layer. Each layer stores its input and output.
  3. Loss compares predictions to targets and gives one scalar.
  4. Optimizer optimizer.zero_grad() resets every gradient to zero.
  5. Loss loss.backward() sends gradients right to left through the graph.
  6. Optimizer optimizer.step() updates each parameter, for SGD param -= lr * grad.
  7. Model model.eval() before test. Inference runs only the forward pass.
Sources

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.
Sources

Tasks in code

TaskCode
Train a perceptron on ANDp_and = Perceptron(2) then p_and.train(and_data)
Chain layersNetwork([hidden, output])
Autograd net, 2-4-1net = Network([2, 4, 1])
Backprop from the losstotal_loss.backward()
Stable multi-class lossF.cross_entropy(logits, labels)
Smoothed targetslabel_smoothing=0.1
Pick an optimizerAdam(model.parameters(), lr=0.01)
Batch the dataDataLoader(train_data, batch_size=16, shuffle=True)
Sources

Value node with backward

out = Value(self.data + other.data, (self, other), '+')
self.grad += out.gradadd passes the gradient through
other.grad += out.grad
out = Value(self.data * other.data, (self, other), '*')
self.grad += other.data * out.gradmul swaps the inputs
other.grad += self.data * out.grad
self.grad += (s * (1 - s)) * out.grad # sigmoid reuses s
build_topo(self)topological sort of the graph
self.grad = 1.0dL/dL = 1
for v in reversed(topo):
v._backward()push gradients to children
Sources

Mini 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 on
for 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 dropout
Sources

Which loss

CommandWhat it does
F.mse_lossRegression. Penalizes outliers quadratically
MAE / HuberRegression that is robust to outliers
F.binary_cross_entropyBinary classification on probabilities
F.cross_entropyMulti-class. Log-softmax and NLL in one step
Label smoothingAdd when multi-class CE is overconfident
Focal LossImbalanced classes. Down-weights easy examples
Triplet LossAnchor, positive, negative with a margin
InfoNCELarge batch self-supervised embeddings
Sources

Defaults in the lesson code

SettingDefaultNote
Perceptron learning_rate0.1epochs=100
Adam lr0.001beta1=0.9, beta2=0.999, eps=1e-8
SGD lr0.01param -= lr * grad
Dropout p0.5Survivors scale by 1/(1-p)
BatchNormmomentum=0.1eps=1e-5, running stats for eval
BCE clip eps1e-15BCELoss in the framework uses 1e-7
alpha0.1Label smoothing, target 0.91 for 10 classes
temperature0.07Contrastive loss, SimCLR default
Sources

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.
Sources

Troubleshooting

SymptomCauseFix
Perceptron never converges on XORXOR is not linearly separableAdd a hidden layer
Classifier outputs 0.5 for allMSE loss on classificationUse cross-entropy
Early layers do not learnSigmoid shrinks gradients per layerReLU (Lesson 04), scaled init
Loss is inf or NaN\log(0) = -\inftyClip p to [eps, 1 - eps]
math.exp(1000) overflowLarge input to sigmoidClamp to [-500, 500]
Gradient wrong for a reused value= overwrites other pathsAccumulate with +=
Gradients grow each stepOld gradients stayCall zero_grad() first
Test results change each runDropout still onCall model.eval()
Sources

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() and eval()?
Dropout and BatchNorm act differently in each mode.
Why is zero_grad a separate call?
Gradients accumulate with += until you reset them.
Sources
Original source
---
id: neural-net-basics
title: Neural Network Basics
code: NN
subtitle: Perceptron, forward pass, backprop, loss choice and a mini framework. Formulas, shapes, defaults and fixes.
hue: 265
sources: phases/03-deep-learning-core/01-the-perceptron, phases/03-deep-learning-core/02-multi-layer-networks, phases/03-deep-learning-core/03-backpropagation, phases/03-deep-learning-core/05-loss-functions, phases/03-deep-learning-core/10-mini-framework
source_hash: 79fe5ec046db5770
---

## Forward and backward through a 2-layer net
type: architecture
from: phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md

```arch
node | x | 1,1 | Input x | no computation | client
node | l1 | 2,1 | Hidden layer | a1 = sigmoid(W1 x + b1) | process
node | l2 | 3,1 | Output layer | a2 = sigmoid(W2 a1 + b2) | process
node | loss | 4,1 | Loss | L = (a2 - y)^2 | store
node | g2 | 4,2 | dL/dz2 | 2(a2 - y) a2 (1 - a2) |
node | w2 | 3,2 | dL/dW2 = dL/dz2 a1 | dL/da1 = dL/dz2 W2 |
node | g1 | 2,2 | dL/dz1 | dL/da1 a1 (1 - a1) |
node | w1 | 1,2 | dL/dW1 = dL/dz1 x | dL/db1 = dL/dz1 |
edge | x | l1 | forward
edge | l1 | l2 | a1
edge | l2 | loss | a2
edge | loss | g2 | backward
edge | g2 | w2 |
edge | w2 | g1 |
edge | g1 | w1 |
```

## One training step
type: flow
from: phases/03-deep-learning-core/10-mini-framework/docs/en.md, phases/03-deep-learning-core/03-backpropagation/docs/en.md

1. **DataLoader**: yields a batch of inputs and targets. Shuffles each epoch.
2. **Model**: runs the forward pass layer by layer. Each layer stores its input and output.
3. **Loss**: compares predictions to targets and gives one scalar.
4. **Optimizer**: `optimizer.zero_grad()` resets every gradient to zero.
5. **Loss**: `loss.backward()` sends gradients right to left through the graph.
6. **Optimizer**: `optimizer.step()` updates each parameter, for SGD `param -= lr * grad`.
7. **Model**: `model.eval()` before test. Inference runs only the forward pass.

## Formulas
type: concepts
from: phases/03-deep-learning-core/01-the-perceptron/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md, phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md

- **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
type: task-table
from: phases/03-deep-learning-core/01-the-perceptron/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md, phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md, phases/03-deep-learning-core/10-mini-framework/docs/en.md

| Task | Code |
|---|---|
| Train a perceptron on AND | `p_and = Perceptron(2)` then `p_and.train(and_data)` |
| Chain layers | `Network([hidden, output])` |
| Autograd net, 2-4-1 | `net = Network([2, 4, 1])` |
| Backprop from the loss | `total_loss.backward()` |
| Stable multi-class loss | `F.cross_entropy(logits, labels)` |
| Smoothed targets | `label_smoothing=0.1` |
| Pick an optimizer | `Adam(model.parameters(), lr=0.01)` |
| Batch the data | `DataLoader(train_data, batch_size=16, shuffle=True)` |

## Value node with backward
type: annotated-code
lang: python
from: phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/03-backpropagation/code/main.py

```python
out = Value(self.data + other.data, (self, other), '+')
self.grad += out.grad                 # add passes the gradient through
other.grad += out.grad
out = Value(self.data * other.data, (self, other), '*')
self.grad += other.data * out.grad    # mul swaps the inputs
other.grad += self.data * out.grad
self.grad += (s * (1 - s)) * out.grad # sigmoid reuses s
build_topo(self)                      # topological sort of the graph
self.grad = 1.0                       # dL/dL = 1
for v in reversed(topo):
    v._backward()                     # push gradients to children
```

## Mini framework training loop
type: annotated-code
lang: python
from: phases/03-deep-learning-core/10-mini-framework/docs/en.md, phases/03-deep-learning-core/10-mini-framework/code/main.py

```python
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 on
for 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 dropout
```

## Which loss
type: command-table
from: phases/03-deep-learning-core/05-loss-functions/docs/en.md

| 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 |

## Defaults in the lesson code
type: defaults
from: phases/03-deep-learning-core/01-the-perceptron/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md, phases/03-deep-learning-core/10-mini-framework/docs/en.md

| Setting | Default | Note |
|---|---|---|
| Perceptron `learning_rate` | 0.1 | `epochs=100` |
| Adam `lr` | 0.001 | `beta1=0.9`, `beta2=0.999`, `eps=1e-8` |
| SGD `lr` | 0.01 | `param -= lr * grad` |
| Dropout `p` | 0.5 | Survivors scale by 1/(1-p) |
| BatchNorm | `momentum=0.1` | `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 |

## Numbers to remember
type: tips
from: phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md

- 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.

## Troubleshooting
type: troubleshooting
from: phases/03-deep-learning-core/01-the-perceptron/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md, phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md, phases/03-deep-learning-core/10-mini-framework/docs/en.md

| 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()` |

## Questions
type: qa
from: phases/03-deep-learning-core/01-the-perceptron/docs/en.md, phases/03-deep-learning-core/02-multi-layer-networks/docs/en.md, phases/03-deep-learning-core/03-backpropagation/docs/en.md, phases/03-deep-learning-core/05-loss-functions/docs/en.md, phases/03-deep-learning-core/10-mini-framework/docs/en.md

- **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()` and `eval()`?** Dropout and BatchNorm act differently in each mode.
- **Why is `zero_grad` a separate call?** Gradients accumulate with `+=` until you reset them.
Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓