AI · Class 01 · Interactive

Intro to Neural Networks

From one line of math to the GPU memory wall. Drag sliders, flip switches, step gradient descent by hand — then watch PyTorch do the whole thing at scale.

🗺️ What we'll cover

A neural network is a pile of multiplications, additions, and one elegant calculus trick. This class builds the whole thing from scratch, in one sitting, with sliders you can drag and switches you can flip at every step.

1
Math Recap — derivatives and the chain rule, the only calculus you need.
Slide a point along and watch the tangent swing. Chain speed × grade on an ATV.
Calculus
2
The First Neuron — McCulloch & Pitts, 1943, and the alphabet soup that followed.
Flip three binary inputs. Tune weights and a threshold. Watch the neuron fire.
Switches
3
A Simple Network y = w·x + b , weights vs bias, five data points.
Drag w and b sliders and feel the line hunt for the right fit.
Sliders
4
Forward Pass — error, squared error, MSE — real Python.
Watch every per-point squared error as a live bar chart as you move the sliders.
Live Loss
5
Chain Rule in the Net — derive each gradient stage, sanity-check numerically.
Nudge w by 0.001 and compare the numerical slope to the chain-rule formula.
Proof
6
Gradient Descent — update rule, learning rate, 100 epochs — live.
Click Step or Run . Watch the loss collapse while the line locks onto the data.
Train
7
PyTorch & DAGs — let loss.backward() chain-rule for us.
Light up forward & backward edges on a tiny computation graph.
Autograd
8
GPU Memory Wall — LeNet-5 → GPT-4 → frontier.
Pick any parameter count, dtype, and GPU. See how many cards it takes to train.
Physics
🏄
Treat this like learning to surf. Not many stand up on the first paddle out. Every session you fall less and read the ocean better. Neural nets are the same — the fundamentals (derivatives, chain rule, gradients) show up everywhere once you've seen them once. Patience is key. Concepts compound. What feels impossible at slide 30 is obvious by slide 80.

🎛️ How to drive this class

Every tab has at least one INTERACTIVE panel. The story text is for reading. The panels are for doing . Read one paragraph, then go play with the widget above or below it, then come back. That's the whole recipe.

  • Sliders — drag to change a value. The formulas, plots, and numbers update live.
  • Buttons — a Step button does one gradient step; a Run button trains continuously; a Reset button zeroes everything.
  • Switches — click bit-inputs on / off, choose a GPU, or toggle inference vs training.
  • Canvases — live plots redraw on every interaction; nothing is pre-rendered.

Nothing sends anything anywhere — everything runs in your browser. Refresh the page at any time to start over.

🎯 Two ideas you need before you touch a neural network

A neural network is just a pile of multiplications and additions that we adjust to make its outputs less wrong. To do the adjusting, you need exactly two tools from calculus:

  • Derivatives — how much does an output change when you nudge an input?
  • The chain rule — how do you chain those nudges together across a pipeline of operations?

Everything in this deck — forward pass, loss, backprop, PyTorch's loss.backward() — is these two ideas applied relentlessly. So let's spend five minutes on them first.

📐 Derivatives — the slope at a point

A derivative answers one question: if I move the input by a tiny amount, how much does the output move? That ratio — output change over input change, in the limit — is the slope of the function at that point.

f(x) = x² → df/dx = 2x

At x = 3, the slope is 2·3 = 6. That means: nudging x up by 0.001 makes x² go up by roughly 0.006. Not exact, but very close — and the smaller the nudge, the better the approximation. (3.001)² − 3² = 9.006001 − 9 = 0.006001. The 0.000001 extra is what vanishes in the limit.

Derivative playground — drag x, watch the tangent

x 1.5
f(x) = x²
2.25
slope = 2x
3.00
nudge Δx = 0.001
+0.003001
slope × Δx (predicted)
+0.003000

Blue curve = f(x) = x². Orange line = the tangent at the current x. At x = 0 the slope is flat; push x further out and the tangent steepens quadratically. That's what df/dx = 2x says in one sentence.

Power rule

For any x raised to a power n:

d/dx (xⁿ) = n·xⁿ⁻¹

x² → 2x, x³ → 3x², x⁴ → 4x³

Constants vanish

Constants don't change when you nudge x — so their derivative is zero.

d/dx (7) = 0

This is why y_true drops out of loss gradients — it's constant with respect to the weights.

💡
A derivative is a signal that tells you which way to move. Positive slope → increasing x increases f(x). Negative slope → increasing x decreases f(x). We're going to use that signal to decide which way to push every weight in a neural network.

🔗 The chain rule — nudges flow through pipelines

Real functions aren't single terms. They're pipelines: you transform x to get an intermediate value, then transform that to get another, then finally get the output. The chain rule says: to get the end-to-end derivative, multiply the derivatives of each step along the way.

if y = f(g(x)) then dy/dx = f'(g(x)) · g'(x)

In words: how x affects y = (how g affects y) × (how x affects g). Each arrow in the pipeline contributes one factor.

Example: y = (3x + 1)²

Split into two stages:

  • Inner: u = 3x + 1 → du/dx = 3
  • Outer: y = u² → dy/du = 2u

Chain them: dy/dx = 2u · 3 = 6(3x + 1) . One function, two nudges, multiplied.

🏍️ The chain rule, ATV version — Monteverde

You're on an ATV descending from a ridge in Monteverde. You want to know: how fast is my altitude dropping per minute? Two things feed into that:

  • Speed : how much distance you cover per unit time — dx/dt = 30 km/h
  • Grade : how much altitude you lose per unit distance — dh/dx = −32 m/km
dh/dt = dh/dx × dx/dt = (−32 m/km) × (30 km/h) = −960 m/h

The km units cancel. You drop 960 meters per hour — faster if you speed up, faster if the grade steepens. Neither factor alone tells you the answer. The chain rule chains them.

ATV chain rule — twist the throttle, change the grade

speed · dx/dt 30 km/h
grade · dh/dx -32 m/km
dh/dt = (−32 m/km) × (30 km/h) = −960 m/h
altitude rate (m/h)
−960
altitude rate (m/min)
−16.0
minutes to drop 500 m
31 min

Two nudges, multiplied. Double the speed → twice the altitude-rate. Double the grade → twice the altitude-rate. Exactly the structure backprop uses to chain gradients through a pipeline of operations.

🏍️
This is exactly what happens inside a neural network. A weight doesn't directly cause loss — it affects a prediction, which affects an error, which affects a squared error, which gets averaged into loss. The chain rule is how we measure the weight's total effect: multiply the nudge at each stage.

🍂 The chain rule of composting

Another version — same math. You're composting. Three things speed up decomposition, and they multiply :

The chain (3 factors)
shred × turn × heat = speedup
4 × 3 × 2 = 24×
Factor Value Meaning
shred surface area
turn oxygen
heat temperature
total 24× final multiplier

Now the backward pass — which factor matters most right now?

∂speedup/∂shred = turn × heat = 3 × 2 = 6
∂speedup/∂turn = shred × heat = 4 × 2 = 8
∂speedup/∂heat = shred × turn = 4 × 3 = 12

Heat has the biggest gradient → improving heat gives you the most bang for your buck. That's exactly how a neural network decides which weight to adjust most: compute each parameter's gradient, update most where the gradient is largest.

Composting chain rule — turn the knobs, see which one dominates

shred × (surface area)
turn × (oxygen)
heat × (temperature)
shred × turn × heat = 4 × 3 × 2 = 24× speedup

Backward pass — gradient per factor

∂/∂shred
6
∂/∂turn
8
∂/∂heat
12

Heat wins — improving it gives the most speedup per unit effort. Raise heat or drop it and watch which gradient overtakes.

🧠 The first neuron — McCulloch & Pitts, 1943

Before there were CNNs, transformers, or GPUs, there was a paper in the Bulletin of Mathematical Biophysics , Volume 5, 1943. Warren McCulloch (a neurophysiologist) and Walter Pitts (an 18-year-old homeless mathematical prodigy) proposed the first mathematical model of a neuron.

Their claim was audacious: cognition could be explained as computation performed by interconnected binary threshold units. That model is the root of everything in modern deep learning.

The McCulloch–Pitts neuron

  • Inputs arrive as binary signals (0 or 1).
  • Each input has a weight — "how much do I trust this signal?"
  • Sum the weighted inputs.
  • Compare to a threshold. Above it → fire (output 1). Below it → silent (output 0).
output = 1 if Σ wᵢ·xᵢ ≥ threshold, else 0
📜
"At any instant a neuron has some threshold, which excitation must exceed to initiate an impulse." — McCulloch & Pitts, 1943. Everything since is variations on this theme: keep the weighted sum, soften the threshold (→ sigmoid, ReLU, softmax), chain many of them together, and learn the weights from data instead of hand-designing them.
🌀
What McCulloch–Pitts left out: a way to learn the weights. The 1943 neuron is a one-way street — signals flow forward, weights are hand-set, and there's no machinery that lets a mistake at the output reach back and adjust earlier weights. A young Paul Werbos read Sigmund Freud as a teenager and decided the missing piece was a backwards flow of credit assignment — what Freud called the flow of psychic energy ( cathexis ), translated into math. Werbos later wrote ( AD2004 ): "In 1968, I proposed that we somehow imitate Freud's concept of a backwards flow of credit assignment." That intuition is what eventually becomes backpropagation — see the history in Tab 6 (Gradient Descent).

Build your own McCulloch–Pitts neuron

Flip the inputs, tune the weights, slide the threshold. The neuron fires whenever the weighted sum clears the bar. Try this: make an AND gate (threshold = 3, weights = 1), then change it to OR (threshold = 1), then NAND (negate a weight), XOR (you can't — one of the original papers' most famous limits).

input x₁
input x₂
input x₃
weight w₁ 1.0
weight w₂ 1.0
weight w₃ 1.0
threshold θ 1.0
Σ wᵢ·xᵢ = 1· 1 + 1· 0 + 1· 0 = 1.00  ≥  1.00   →   fire
OUTPUT = 1 · FIRE

Try these presets

🍜 Neural network alphabet soup

Before we build one from scratch, here's what's on the menu. Every "X-NN" you've heard of is a different way to wire neurons together for a different shape of data.

🖼️ CNN

Convolutional Neural Network
Slides small filters across spatial data to detect patterns.
[Image] → [Conv] → [Conv] → [Pool] → [Dense] → [Class]
Input: Grids (images, spectrograms)
Key idea: Local patterns matter, position invariant
Image classification
Object detection
Medical imaging
Video analysis

🔁 RNN / LSTM

Recurrent / Long Short-Term Memory
Processes sequences by maintaining hidden state memory.
[x₁] → [h₁] → [x₂] → [h₂] → [x₃] → [h₃] → [out]
Input: Sequences (text, time series)
Key idea: Order matters, memory of past (mostly replaced by Transformers)
Time series
Speech recognition
Music generation
Legacy NLP

⚡ Transformer

Self-Attention Architecture
Every token attends to every other token, in parallel.
[Tokens] → [Self-Attention] → [FFN] → [× N layers]
Input: Sequences, but processed all at once
Key idea: Attention beats recurrence, parallelizable
LLMs (GPT, Claude)
Translation
ViT (Image)
AlphaFold (Protein)
Code

🕸️ GNN

Graph Neural Network
Nodes aggregate information from their neighbors.
(A)—(B)—(C) message passing
 |      |
(D)———(E)
Input: Graphs (nodes + edges)
Key idea: Structure matters, relationships encoded
Social networks
Fraud detection
Molecules / drugs
Recommendations
Traffic
🏆
The transformer is eating everything. Vision (ViT), audio (Whisper), code (Claude Code), proteins (AlphaFold), LLMs — attention has become the dominant paradigm. CNNs and RNNs still hold niches, but most of the new capability growth is on transformers. Which is why understanding the one neuron underneath all of them — weights, bias, nudges, gradients — is worth the investment.
🎬
The name is a joke that stuck. The 2017 paper "Attention Is All You Need" was Google's — Vaswani et al. The name Transformer was championed by Jakob Uszkoreit (4th author) who liked how it sounded, and early internal materials leaned into the Transformers franchise imagery. Unrelated: a variant called Universal Transformers has US patent US10740433B2 . A residual connectionoutput = x + f(x) , from He et al. 2015 ( Deep Residual Learning ) — is what lets attention layers stack deep. Interpretability researchers call the accumulated sum across all those skip connections the residual stream . KV cache is a decode-time optimization: store past keys and values so next-token generation reuses them instead of recomputing.

📈 Learning curve

A few honest warnings before we start coding:

  • Transformers are not intuitive. Neither was the first time you saw a for-loop.
  • A neural network can be implemented in a few lines of code — even a transformer . The math is harder than the code.
  • A single line change inside a research codebase often represents a full published paper worth of thought. Read slowly.
  • Patience is key. The concepts compound. What feels impossible at slide 30 is obvious by slide 80.
🏄
Treat this like learning to surf. Nobody stands up on the first paddle out. Every session you fall less, catch more, and read the ocean a little better. Neural nets are the same — the fundamentals (derivatives, chain rule, gradients) show up everywhere once you've seen them once.

🧮 The simplest neural network

Strip a neural network to its bones and you get one line of math:

y = w · x + b

That's it. A weight w , an input x , a bias b , and an output y . A real neural network stacks millions or billions of these and pipes them through nonlinearities — but the fundamental unit is this single line. Understand it, and you understand what every weight in GPT-4 is doing.

x · w — evidence from inputs

Weights interpret evidence. They decide how much an input should push the output up or down. A big weight means "this signal matters a lot"; a near-zero weight means "ignore it."

b — baseline belief

Bias sets the default. It's the output when x = 0 — the prior belief before any evidence comes in. It lets the network shift its whole prediction up or down without changing how it weighs inputs.

🎛️ Why bias matters — real examples

Bias sounds mundane until you see where it hides in every modern network:

🧬 AlphaFold (protein folding)

"Attention with a bias." Bias terms in the attention layers encode structural priors about how amino acids pair up before any data is looked at.

💳 Fraud detection

Most transactions aren't fraud. A negative bias biases the model toward "not fraud" so it doesn't cry wolf on every charge.

🖼️ CNN filters — trigger sensitivity

Each convolutional filter has a bias that controls its trigger sensitivity. Consider a filter that detects vertical edges:

  • Negative bias → only fires on strong, obvious edges
  • Zero bias → fires on moderate edges
  • Positive bias → fires on subtle gradients too
📡
Same math, totally different roles. In a regression model, bias is the y-intercept. In attention, it's a structural prior. In CNNs, it's trigger sensitivity. But it's always the same thing: the output when the inputs contribute nothing.

🎯 A concrete example

Let's pick specific numbers. Suppose the true relationship we want to model is:

y = 3·x + 1

The weight is 3, the bias is 1. If we feed it inputs 1 through 5, it should produce:

x = [ 1, 2, 3, 4, 5 ]
y = [ 4, 7, 10, 13, 16 ]

A story to hang this on: an object started 1 m from the origin and is moving at 3 m/s. y = 3·x + 1 is its position after x seconds. The network only sees the pairs — and has to rediscover the 3 and the 1.

Fit the line by hand — drag w and b

The five blue dots are the training data. The orange line is your current y = w·x + b . Try to line it up with the dots — the loss number below tells you how close.

weight w 0.00
bias b 0.00
current line
y = 0.00·x + 0.00
MSE loss
118.00
vs target (w=3, b=1)
Δ = 3.16

The whole loss landscape is a parabolic bowl in (w, b) space — one unique minimum. Gradient descent's job is to roll down to the bottom of that bowl. You're doing it by hand right now.

Now here's the setup for learning: we have the inputs x and the true outputs y, but we don't know w or b. The job of the neural network is to figure them out from examples alone.

1

Initialize w and b to something — anything. A common choice: both zero. w = 0.0 ; b = 0.0

2

Make a prediction for every input. Call it y_pred = w·x + b . At the start, all predictions are 0 — garbage.

3

Measure how wrong we are. Compute a loss: a single number that summarizes "how badly did this fail?"

4

Use the loss to adjust w and b so next time we're less wrong. Repeat until the loss is small.

🎯
Steps 3 and 4 are where the math from Tab 1 earns its keep. Measuring wrongness is arithmetic. Turning that measurement into an adjustment is the chain rule.

💻 Forward pass — in real Python

Let's put real numbers through real code. The entire forward pass — from raw inputs to a single loss number — is a handful of numpy calls.

# Initial state
w = 0.0
b = 0.0

X = np.array([1, 2, 3, 4, 5])
y = np.array([4, 7, 10, 13, 16])  # true values

Five training examples. We know what the right answer looks like. Now the forward pass:

# Forward pass with w=0, b=0
y_pred = w * X + b
# y_pred = 0 * [1, 2, 3, 4, 5] + 0
# y_pred = [0, 0, 0, 0, 0]

Every prediction is zero. Predictably bad — we haven't trained anything yet. But the structure is there: five inputs, five predictions, all coming out of the same one-line formula w·X + b .

📉 Measuring how wrong we are — step by step

We need a single number that captures the total badness. Three stages:

1
Error = prediction − truth. Signed, so we know which direction we're off by.
# Error (prediction - truth)
error = y_pred - y
# error = [0-4, 0-7, 0-10, 0-13, 0-16]
# error = [-4, -7, -10, -13, -16]
2
Square it. Two reasons: (1) negative errors and positive errors shouldn't cancel out, and (2) squaring penalizes big misses more than small ones.
# Square it (makes everything positive, penalizes big errors more)
squared_error = error ** 2
# squared_error = [16, 49, 100, 169, 256]
3
Average it. One number for the whole dataset. This is the Mean Squared Error — the workhorse regression loss.
# Mean (average of all squared errors)
loss = np.mean(squared_error)
# loss = (16 + 49 + 100 + 169 + 256) / 5
# loss = 590 / 5
# loss = 118.0
Loss at step 0
118.0
MSE — with w=0, b=0 and target y = 3x + 1
📏
MSE in one line: loss = np.mean((y_pred - y) ** 2) . The number itself doesn't matter yet — what matters is that we can change w and b and watch this number go down . That's what makes it a useful compass.

Per-point loss breakdown — drag w, watch every squared error squirm

Each bar is one training example's squared_error . Their mean is the MSE loss on the right. At w = 0 , the big-x examples dominate — a squared error of 256 for x = 5. As you climb toward w = 3 , all the bars collapse.

weight w 0.00
bias b 0.00
worst point
x=5 · err²=256
best point
x=1 · err²=16
MSE = mean of bars
118.00

🎚️ From loss number to loss signal

We have loss = 118. But which direction should we push w? Which way for b? The loss alone doesn't say — it just reports the current damage.

To make a decision, we need to know: if I nudge w a tiny bit, how does the loss change? That's the derivative ∂L/∂w . Same for the bias: ∂L/∂b .

∂L/∂w = "nudge w a hair → how much does the loss move?"
∂L/∂b = "nudge b a hair → how much does the loss move?"

These two numbers — a pair of gradients — are the entire steering system of the neural network. We're going to derive them next, using the chain rule you met in Tab 1.

🔗 Why we need the chain rule here

We want ∂(squared_error) / ∂w . But w doesn't appear in squared_error directly . Trace the pipeline:

w → y_pred → error → squared_error

w affects y_pred, which affects error, which affects squared_error. So we chain the nudges, one stage at a time:

∂(squared_error) / ∂w =
    ∂(squared_error) / ∂(error)
    × ∂(error) / ∂(y_pred)
    × ∂(y_pred) / ∂w

Three nudges, multiplied together. Exactly the same pattern as dh/dt = dh/dx · dx/dt from the ATV example — just with more stages.

🧮 Deriving each stage

1

How does w affect y_pred?

y_pred = w·x + b → ∂(y_pred)/∂w = x
2

How does y_pred affect error?

error = y_pred − y_true → ∂(error)/∂(y_pred) = 1

y_true is a constant — its derivative is zero. Only y_pred moves.

3

How does error affect squared_error?

squared_error = error² → ∂(squared_error)/∂(error) = 2·error

Power rule. The 2 comes from differentiating a square; the factor of error is what's left.

Now chain them:

∂(squared_error)/∂w = 2·error × 1 × x = 2·error·x

🔬 Sanity-check by hand

Let's verify for the first training point, where x = 1, y_true = 4, y_pred = 0, error = −4. The chain-rule formula says:

∂(squared_error)/∂w = 2 · (−4) · 1 = −8

Does that match what we'd get by literally nudging w? Let's check. Nudge w by 0.001 and recompute the squared error for the same point:

y_pred = 0.001 * 1 + 0 = 0.001
error = 0.001 - 4 = -3.999
squared_error = (-3.999)² = 15.992
# went from 16 to 15.992 → change of -0.008
# Δ(squared_error) / Δw = -0.008 / 0.001 = -8

Same answer — −8 . The chain rule gets it in one shot without needing to poke around with nudges.

What does −8 mean? "If you increase w by a tiny amount, the squared error for this point will decrease by about 8× that amount." Negative gradient → increasing w decreases the loss → we should increase w. That's the whole basis of gradient descent.

Numerical vs chain-rule gradient — side by side

Pick a training point, pick a current w , pick a nudge size. Watch the two methods — crude numerical (recompute with and without the nudge) vs chain rule (closed-form 2·error·x) — agree to more decimal places as the nudge shrinks.

x (point) 1
current w 0.00
Δw 0.001
y_true (target)
4
y_pred = w·x
0.00
chain rule (2·err·x)
−8.0000
numerical Δ(err²)/Δw
−8.0000

Drag Δw toward 0.0001 — agreement gets perfect. That's what a derivative is: the numerical answer in the limit.

🔗 The bias gradient — same pattern

For b, the chain is the same except for the final stage: ∂(y_pred)/∂b = 1 instead of x .

∂(squared_error)/∂b = 2·error × 1 × 1 = 2·error

So for the first training point: 2 · (−4) = −8 . Nudging b up decreases the error for that example — just like nudging w, because at x=1 they happen to have the same effect.

📋 The gradient table — all five points

MSE averages over the whole dataset, so the final gradient is the mean of each point's gradient. Compute for every training example, then average:

x y_true y_pred error 2·error·x (∂L/∂w) 2·error (∂L/∂b)
1 4 0 −4 −8 −8
2 7 0 −7 −28 −14
3 10 0 −10 −60 −20
4 13 0 −13 −104 −26
5 16 0 −16 −160 −32
mean = −72 −20
∂L/∂w = −72
∂L/∂b = −20

Both negative → both should increase. The magnitude tells you which one to push harder: w's gradient is ~3.6× larger, so w moves proportionally more. That's the steering signal — and it came straight out of the chain rule.

⬇️ Using gradients to adjust weights

We have ∂L/∂w = −72 and ∂L/∂b = −20 . Two rules decide the direction:

  • If the gradient is positive → increasing the parameter increases loss → decrease it.
  • If the gradient is negative → increasing the parameter decreases loss → increase it.

Both of our gradients are negative. So both parameters should go up. But by how much ?

🎚️
If we subtract the full gradient, we overshoot wildly. The gradient is a local slope, not a long-distance guarantee. We scale it down by a learning rate — a small constant, typically 0.01 — so each step is a small, safe step in the right direction.

📐 The update rule

For every parameter, one step of gradient descent is:

new = old − learning_rate · gradient

Plug in:

# Starting point
w = 0.0, b = 0.0
lr = 0.01

# Gradients (computed via chain rule)
dw = -72
db = -20

# Update
w_new = 0 - 0.01 * (-72) = 0 + 0.72 = 0.72
b_new = 0 - 0.01 * (-20) = 0 + 0.20 = 0.20

After one training step, our model went from y = 0·x + 0 to y = 0.72·x + 0.20 . Still wrong (target is 3·x + 1), but closer. Each step nudges it a little closer.

🔁 Gradient Descent — step-by-step breakdown

The full recipe of one training step — exactly what PyTorch's optimizer.step() hides under a friendly name:

1
Forward Pass
y_pred = 0.00 × X + 0.00 = [0.0, 0.0, 0.0, 0.0, 0.0]
2
Compute Error
error = y_pred − y = [-4, -7, -10, -13, -16]
3
Compute Gradients
∂L/∂w = mean(2 × error × X) = -72.0000
∂L/∂b = mean(2 × error)     = -20.0000
4
Update Weights
w = 0.00000.0100 × -72.0000 = 0.7200
b = 0.00000.0100 × -20.0000 = 0.2000
5
Loss Change
118.0000 → 68.7664 (↓ 49.2336)

📉 Loss history — 10 epochs

An epoch = one full pass over the training data. Do step 1–5 above, ten times. Watch what happens.

Epoch w (target 3) b (target 1) Loss
1 0.7200 0.2000 68.7664
2 1.2207 0.3392 40.1220
3 1.6891 0.4696 23.3558
4 2.0093 0.5588 13.6124
5 2.2538 0.6271 7.9344
6 2.4403 0.6793 4.6256
7 2.5827 0.7193 2.6973
8 2.6913 0.7500 1.5736
9 2.7742 0.7735 0.9188
10 2.8375 0.7916 0.5371
Starting loss
118.00
After 10 epochs
0.5371
Reduction
99.5%
📈
w is climbing toward 3, b is climbing toward 1. The network isn't being programmed — it's being shown examples and inferring the rule. At 100 epochs, both parameters converge essentially exactly to 3.00 and 1.00, and loss is ~0. All of it driven by the same chain-rule recipe you watched derive above.

Train live — step by step, or run 100 epochs at once

The model starts at w = 0, b = 0 . Click Step to apply one gradient update; Run to animate the whole trajectory; Reset to start over. Crank the learning rate past ~0.05 and watch what "overshoot" actually looks like.

learning rate 0.010
epoch
0
w (target 3.00)
0.0000
b (target 1.00)
0.0000
loss
118.0000
∂L/∂w
∂L/∂b

The canvas shows the loss-vs-epoch curve. Low learning rates creep; high learning rates zig-zag and can blow up entirely. Somewhere around 0.03 is fastest without oscillation for this problem — every problem has its own sweet spot.

🚀 Beyond vanilla SGD — Adam & AdamW

What we just implemented is SGD (stochastic gradient descent). It works, but it has a weakness: it only uses the current gradient and treats every parameter the same. Real networks are trained with smarter optimizers:

🧲 Momentum

Keep a running mean of recent gradients. Accelerate along consistent directions, dampen wobbles. Like the ATV — once you're moving down the hill, you keep moving.

📊 Adaptive rates (variance)

Track each parameter's gradient variance . Noisy parameters get smaller steps; steady ones get bigger steps. Every weight gets its own custom learning rate.

⚙️
Adam & AdamW combine momentum and adaptive rates. Under the hood, they keep running estimates of mean and variance per parameter — so for a model with 175 billion parameters, the optimizer is tracking 2 × 175B = 350B extra numbers, on top of the weights themselves. This is why GPU memory budgets blow up so fast (see the GPU Memory Wall tab).

💻 The whole thing in numpy — 25 lines

Everything we derived — forward pass, loss, gradients via chain rule, update — in one file:

#!/usr/bin/python3
import numpy as np

# Single linear neuron: y = w·x + b
# Training data: y = 3x + 1
X = np.array([1, 2, 3, 4, 5], dtype=np.float32)
y = np.array([4, 7, 10, 13, 16], dtype=np.float32)

# Initialize parameters
w = 0.0
b = 0.0
lr = 0.01

# Training loop
for epoch in range(100):
    # Forward pass
    y_pred = w * X + b

    # Loss (MSE)
    loss = np.mean((y_pred - y) ** 2)

    # Gradients (by hand)
    dw = np.mean(2 * (y_pred - y) * X)
    db = np.mean(2 * (y_pred - y))

    # Update
    w -= lr * dw
    b -= lr * db

    if epoch % 20 == 0:
        print(f"epoch {epoch}: loss={loss:.4f}, w={w:.4f}, b={b:.4f}")

print(f"\nLearned: y = {w:.2f}x + {b:.2f}")
print(f"Target:  y = 3.00x + 1.00")

After 100 epochs this script prints Learned: y = 3.00x + 1.00 . No magic, no black box — the whole thing is ~25 lines of arithmetic.

🔥 Same thing in PyTorch — let the framework chain-rule for us

Once you have one neuron working, you want to scale. PyTorch (originally led by Meta) is the deep-learning library that makes that scaling tolerable. Its core trick: you write the forward pass, it remembers the chain, and when you call loss.backward() it runs automatic differentiation — applies the chain rule for you across the entire graph.

#!/usr/bin/python3
import torch

# Same data
X = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32)
y = torch.tensor([4, 7, 10, 13, 16], dtype=torch.float32)

# requires_grad=True tells PyTorch to track gradients
w = torch.tensor(0.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)
lr = 0.01

for epoch in range(100):
    # Forward pass
    y_pred = w * X + b
    loss = torch.mean((y_pred - y) ** 2)

    # Backward pass — PyTorch runs the chain rule for us
    loss.backward()

    # Update (no_grad: don't track this as part of the graph)
    with torch.no_grad():
        w -= lr * w.grad
        b -= lr * b.grad

    # Zero the gradients (they accumulate by default)
    w.grad.zero_()
    b.grad.zero_()

Notice what disappeared: the hand-derived dw = 2·error·X line. PyTorch's autograd walks the chain for us. That's the entire reason you can train a 175-billion-parameter transformer without going insane — no human is computing ∂L/∂w₁₂₃,₄₅₆,₇₈₉ by hand.

🧱
PyTorch's high-level building blocks replace the arithmetic once you're building real models: nn.Linear() wraps w·x + b , nn.MSELoss() wraps our loss, torch.optim.SGD() wraps the update rule. Same math, more leverage. Install with sudo apt-get install python3-torch .

🧩 Terminology sync: "Backpropagation"

Everything we've been doing manually has a name:

  • Forward pass — compute the prediction from the inputs.
  • Backward pass (backpropagation) — compute the gradient of the loss with respect to every parameter, by applying the chain rule from the loss back to the inputs.
  • Dynamic programming — the backward pass reuses intermediate results efficiently instead of recomputing them. Roots in Richard Bellman's work at RAND in the 1950s, originally for aerospace and engineering.
📚
The history is messier than the textbooks let on. The reverse-mode automatic differentiation math was developed by Seppo Linnainmaa , a Finnish mathematician, in his 1970 master's thesis — without any neural-network application in mind. Two years later Paul Werbos proposed essentially the same algorithm at Harvard and explicitly aimed it at training multi-layer neural nets — calling it dynamic feedback , with motivation borrowed from Freud rather than from optimization theory. The famous Rumelhart, Hinton & Williams 1986 paper is what made "backprop" the de-facto training algorithm. Between those dates: decades of slow acceptance, an "AI winter," and a few patient researchers.

How "backprop" became the default training algorithm

1950s · Bellman
Dynamic programming at RAND
Richard Bellman formalises dynamic programming for aerospace and engineering problems. The idea that intermediate results can be stored and reused sits under backprop today.
1970 · Linnainmaa
Reverse-mode automatic differentiation
A 20-year-old Finnish mathematician, Seppo Linnainmaa, derives the algorithm in his master's thesis — without any neural-network application in mind.
1968 idea · 1972 proposal · 1974 thesis · Werbos
"Dynamic feedback" — backprop, aimed at neural nets
Paul Werbos has the algorithm and a proof in his Harvard PhD, Beyond Regression (1974), with the original idea — explicitly imitating "Freud's concept of a backwards flow of credit assignment" ( AD2004 ) — going back to 1968. He calls it dynamic feedback ; nobody calls it backpropagation yet. The committee makes him narrow the scope to pattern recognition to get the PhD; the broader theory of intelligence sits dormant.
1986 · Rumelhart, Hinton & Williams
"Learning representations by back-propagating errors"
The paper that makes backprop the training algorithm for multi-layer networks. 16 years after Linnainmaa. The AI winter almost swallowed the idea in between.
2015 · Autograd / PyTorch (2016)
Define-by-run autograd
Autograd (Python) and then PyTorch turn backprop into a free gift: write the forward pass, call loss.backward() , every gradient shows up.

The episode the textbooks skip — Werbos & Minsky

Werbos got the math first, but the field didn't pick it up for another twelve years. The reason is closer to a soap opera than to a research narrative. Werbos walked the algorithm in to Marvin Minsky — whose 1969 Perceptrons book had argued that multi-layer learning was a dead end and helped trigger the first AI winter:

🎭
From the Mind Matters podcast (#137, 2021) — Werbos in his own words:
"The real history has never been written. It's like a soap opera you wouldn't believe. So one episode of the soap opera, I needed support to do a thesis. And I had taken independent studies with Marvin Minsky. I knew his way of thinking, and I walked in and said, 'Marvin, you've got this great book, but the thing is, the problem can be solved. Here's how to solve it. Why don't we become co-authors so that you don't be embarrassed when it comes out? I'm willing to share, I'm not trying to own all this. Here's how you solve the problem that you described in your book.'"
Minsky's answer, in Werbos's recollection: "if I do this, the modelers will all kill me, and I have to deal with my reputation." So the field waited until 1986 for Rumelhart, Hinton & Williams to publish the same idea on a bigger stage.

On where the math actually came from, Werbos is unusually direct. He read Freud as a teenager — long before he ever read von Neumann — and decided the shape of the missing algorithm was the one Freud had described informally: the mind allocates and re-allocates "psychic energy" ( cathexis ) to optimize outcomes (the pleasure principle ). Translating that picture into a quantifiable framework — a backwards flow of partial derivatives that assigns blame for the loss back to every weight — is the path that produced the chain rule for ordered derivatives. As Werbos puts it in the same interview:

🧠
"The most important part of my PhD thesis, some people say, is I translated backpropagation. First, it was Freud, then it was an algorithm, and then it was mathematics. So I translated it into something I called the chain rule for ordered derivatives… So I translated Freud into math, von Neumann translated Fourier into math."
That's a one-line summary of where the backwards in backpropagation comes from. Not from optimization theory. From psychoanalysis, re-derived as calculus.

Sources used in this section: Mind Matters #137 transcript · Werbos AD2004 · Backpropagation and Freud's psychic energy · Werbos on noosphere species theory .

🕸️ Scaling & the computation DAG

When you have one neuron, you can draw the chain on a napkin. When you have millions, you need a Direct Acyclic Graph (DAG) . Every operation is a node; every dependency is an edge. PyTorch builds this graph as your forward pass runs, then walks it backward to compute gradients.

x₁ x₂ w₁₁ w₂₁ w₁₂ w₂₂ × × × × Σ Σ σ σ Σ σ y Inputs Weights Multiply Sum Activation Sum Activation Output
Total nodes
24
Forward ops
12
Backward ops
21

This is a tiny 2-input, 2-hidden-unit, 1-output toy network. A modern transformer's DAG has billions of nodes — but it's the same structure: multiply, sum, activate, repeat. Every node's gradient is computed by looking at its outgoing edges and applying the chain rule.

🏍️
Remember the ATV and the composting examples from Tab 1? Those were DAGs with 2–3 nodes. The neural network is the same picture, just blown up to industrial scale. Every gradient in the backward pass is still (upstream gradient) × (local derivative) — the chain rule, applied at every edge.

📐 Scale-up: LeNet-5 (1998) — the first real CNN

Yann LeCun 's LeNet-5 (1998) was the first broadly successful convolutional neural network, trained on the MNIST handwritten-digit dataset. It applied a modified version of backpropagation to a multi-layer convnet and actually worked at a useful scale.

Year
1998
Parameters
61,706
Training hardware
CPU — no GPU
🧠
1 parameter = anything the network learns during training. Every weight, every bias, every convolutional filter coefficient. LeNet-5 learned ~62K of them. A modern LLM learns 10 million times more. Keep that number — 62K — in your head as a baseline, because the next 27 years are about how wildly that number exploded.

🧮 The memory math — why params become a wall

Parameter count is the easy number. The hard number is memory — how much RAM you need just to hold the model in GPU.

Inference FP16
2 bytes × params
Inference FP32
4 bytes × params
Training (Adam FP16)
~16 bytes × params

Why is training so much worse? Adam keeps a running mean and variance per parameter (Tab 6). That's 2× the weights, doubled again for FP32 optimizer state, plus activations for the backward pass, plus gradient buffers. Rule of thumb: ~2 GB per 1B parameters at FP16 for inference, ~16 GB per 1B parameters for training.

🧱
This is the wall. You can always add parameters on paper. But every parameter has to live somewhere, and GPUs have limited memory — an H100 has 80 GB. Once your training state exceeds what a single GPU can hold, you need thousands of them networked together, which costs tens to hundreds of millions of dollars. That's why we call it the Memory Wall.

🕰️ 1998 – 2015 · No memory wall

For the first 17 years of deep learning, models stayed within one consumer GPU's reach. A single card trained what you needed.

Model Year Params Inference (FP16) Training (Adam) GPU Required
🎞️ Classic CNNs (1998 – 2015)
LeNet-5 1998 60K 120 KB 960 KB CPU OK
AlexNet 2012 60M 120 MB 960 MB GTX 580
VGG-16 2014 138M 276 MB 2.2 GB GTX 980
ResNet-50 2015 25M 50 MB 400 MB GTX 980

AlexNet (2012) is the shot heard round the world — it won ImageNet by a huge margin and convinced the field that GPUs + deep nets were the future. ResNet-50 (2015) proved deeper networks could work with residual connections. Both fit in a gaming GPU.

🤖 2017 – 2019 · Early Transformers — mild signs

Then came the 2017 paper "Attention Is All You Need." Parameters start climbing fast; training memory starts eating real cards.

Model Year Params Inference (FP16) Training (Adam) GPU Required
🤖 Early Transformers (2017 – 2019)
Transformer (original) 2017 65M 130 MB 1 GB GTX 1080
BERT-Base 2018 110M 220 MB 1.8 GB GTX 1080 Ti
BERT-Large 2018 340M 680 MB 5.4 GB V100 16GB
GPT-2 Small 2019 117M 234 MB 1.9 GB RTX 2080
GPT-2 XL 2019 1.5B 3 GB 24 GB RTX 3090

BERT-Large needs a V100 for training. GPT-2 XL needs the biggest consumer card you can buy. Still one-GPU territory — but only just.

🚀 2020 – 2022 · GPT-3 era — the wall arrives

GPT-3 at 175 billion parameters is the moment single-GPU training dies. You can't hold the weights of GPT-3 on a single GPU, let alone the Adam state.

Model Year Params Inference (FP16) Training (Adam) GPU Required
🚀 GPT-3 Era (2020 – 2022)
GPT-3 Small 2020 125M 250 MB 2 GB RTX 3080
GPT-3 Medium 2020 2.7B 5.4 GB 43 GB A100 40GB
GPT-3 XL 2020 6.7B 13.4 GB 107 GB 2× A100 80GB
GPT-3 (175B) 2020 175B 350 GB 2.8 TB 1000+ V100s
🚨
This is the tipping point. 175B params × 16 bytes = 2.8 TB of training state. No single GPU holds that — you need thousands of GPUs networked together, pipelined (pipeline parallelism), sharded (tensor parallelism), and synchronized (data parallelism). Training becomes a distributed systems problem, not just an ML problem.

🌟 2023 – 2025 · Modern LLMs — strong wall

Llama 2 democratized the 7B – 70B range. Frontier models (GPT-4, Llama-3 405B) pushed well into territory where fleets of H100s are the only option.

Model Year Params Inference (FP16) Training (Adam) GPU Required
🌟 Modern LLMs (2023 – 2025)
Llama-2 7B 2023 7B 14 GB 112 GB RTX 4090
Llama-2 13B 2023 13B 26 GB 208 GB A100 40GB
Llama-2 70B 2023 70B 140 GB 1.1 TB 2× A100 80GB
Mistral 7B 2023 7B 14 GB 112 GB RTX 4090
Mixtral 8×7B (MoE) 2023 47B 94 GB 752 GB 2× A100 80GB
GPT-4* (est.) 2023 ~1.8T ~3.6 TB ~29 TB 10,000+ A100s
Llama-3 405B 2024 405B 810 GB 6.5 TB 16,000 H100s
The jump from LeNet-5 (60K params) to Llama-3 405B (405B params) is 6.8 million×. Memory requirements climbed even faster because Adam's overhead grows with params too. This is why NVIDIA's moat is so deep — not because the GPU chips are mysterious, but because training a frontier model requires a datacenter's worth of them wired together with InfiniBand, and the software stack (CUDA, NCCL, cuDNN) to make that tolerable.

🛸 2025+ · Frontier & beyond — insane signs

Rumored and theoretical frontier sizes. The numbers start to look unhinged because they are.

Model Year Params Inference (FP16) Training (Adam) GPU Required
🛸 Frontier & Beyond (2025+)
Llama-4 (rumored) 2025 ~2T? ~4 TB ~32 TB 50,000+ H100s?
10T model (theoretical) 2026? 10T 20 TB 160 TB 100,000+ GPUs
🔭
Where does it end? Physics and economics, not math. Every parameter you add:
  • needs memory on a chip,
  • needs to be moved across a network during training,
  • needs electricity to run at inference time.

The industry's response is multi-front: smaller models that punch above their weight (distillation), smarter architectures (MoE — mixture of experts — only activates a subset per token), and better quantization (4-bit and lower). But the underlying math is the same single neuron you saw in Tab 3, applied a trillion times , trained via the chain rule, scaled across a datacenter.

🏁 Where this leaves us

Start to finish: a single weight, a single bias, one derivative rule, and the chain rule. That's the whole toolkit. Everything that makes a modern neural network "hard" comes from scaling that toolkit — not from new math.

  • The forward pass is w·x + b , stacked and nonlinearly composed.
  • The backward pass is the chain rule, run automatically by PyTorch's autograd engine across a DAG.
  • The optimizer (SGD, Adam) turns gradients into parameter updates — the same new = old − lr · grad you saw in Tab 6.
  • The GPU memory wall is the physical limit on how many parameters and how much optimizer state you can fit per accelerator.
🏄
Patience is key. Everything you just saw — from the McCulloch–Pitts neuron to a 405B-parameter LLM — is one weighted sum, one activation, and the chain rule, composed relentlessly. The hard part isn't the math; it's the scale, the software, and the memory. Once you see the core loop, every transformer paper you read afterward is just a variation on the theme.

🧮 Memory Wall Explorer — pick any model, any GPU

Slide through parameter counts from a laptop-sized MLP to the theoretical 10T model. Choose a precision. Choose a GPU. See whether it fits — and if not, how many you need.

Will this model fit?

parameters (log scale) 7.0 B
mode training (Adam)
precision FP16 / BF16
GPU H100 · 80 GB
weights
14.0 GB
gradients
14.0 GB
optimizer (Adam)
56.0 GB
activations (est.)
28.0 GB
total needed
112.0 GB
GPUs required
2

A 7B model in FP16 training with Adam needs ~112 GB — that's one-and-a-change H100s, or one B200 with room to spare. Now try LLaMA-3 405B.