S019 · Calculus I — Derivatives & Chain Rule
Rate of change is the heart of learning: the derivative as slope, symbolic vs numeric differentiation, and the chain rule that powers every neural net.
🎯 See a function, know its derivative, and unroll the chain rule for compositions of 3–4 functions — the exact skill backpropagation is built on.
Why this session exists
The universe of machine learning has exactly one universal algorithm: compute the gradient of a loss function and step in the opposite direction. Gradient. Derivative. Slope. If you don't feel derivatives — as the "how does the output wiggle when I wiggle the input" quantity — every ML explanation past this point sounds like magic. It isn't. It's calculus, and the version you need is small enough to fit on one page.
- Explain the derivative as ‘instantaneous rate of change’ AND ‘slope of the tangent line’ AND ‘limit of a difference quotient’.
- Compute derivatives of polynomials, exponentials, and logs by hand using the power/product/quotient rules.
- Apply the chain rule to a composition of 3+ functions symbolically and verify with a numeric check.
- Implement forward-mode numeric differentiation in 5 lines of Python and know its limitations.
- Recognise the derivative-shaped things in ML: gradient (multi-var derivative), Jacobian (vector-valued), backprop (repeated chain rule).
Prerequisites
- S017/S018 · Linear algebra — gradients are vectors; Jacobians are matrices.
- High-school precalculus: polynomials, exponents, logs. That's it.
(a) Intuition · 5 min
Your car's odometer tells you total distance travelled — that's the function f(t). Your speedometer tells you how fast that number is changing right this instant — that's the derivative f'(t). If you know your speed at every moment, you can reconstruct where you'll be. That's integration, tomorrow's story.
Now imagine a hill. The steeper the hill, the faster you gain elevation per step forward. That "elevation per step" IS the derivative of the elevation function. Positive: uphill. Negative: downhill. Zero: flat — the top of a peak or bottom of a valley. That's it. Rate of change.
In code, if y = f(x), the derivative f'(x) tells you: "if I wiggle x by a tiny ε, y wiggles by roughly f'(x)·ε." So if you want y to go DOWN, wiggle x by minus ε·f'(x). Do that repeatedly and you slide downhill in y — you're doing gradient descent, which is 100% of how neural nets learn.
That "how much does the output change per unit change in the input" number is the single most-computed quantity in ML. Every framework spends most of its cycles computing it.
The three equivalent definitions
- Geometric — the slope of the tangent line to y = f(x) at the point (x, f(x)).
- Numeric — the limit of (f(x + h) − f(x)) / h as h → 0.
- Physical — the instantaneous rate of change of f with respect to x. If f is position, f' is velocity.
A quick history
- 1665Newton invents fluxions · WoolsthorpeIsaac Newton (age 22, isolated by plague) invents calculus to explain planetary motion.
- 1684Leibniz publishes derivative notationGottfried Leibniz independently discovers calculus. His notation (dy/dx) wins; Newton is furious.
- 1821Cauchy formalises limitsRigorous ε-δ foundations. Calculus stops being ‘magic that works’ and becomes a theorem.
- 1974Reverse-mode autodiff · SpeelpenningPaul Werbos (1974) + Bert Speelpenning independently discover what we now call backpropagation.
- 1986Backprop popularised · Rumelhart, Hinton, WilliamsThe Nature paper. Kicks off the connectionist wave. Every deep-learning framework computes chain rule this way.
- 2018+JAX · autograd everywhereGoogle's JAX makes autodiff a first-class Python transform. PyTorch's autograd, TensorFlow's GradientTape follow.
(b) Visual walkthrough · 15 min
The derivative as a limit — the picture
The rules you actually need
The derivative rulebook (memorise these five)
Chain rule — the star of the show
y = f(g(x)) means: put x through g first to get u = g(x), then put u through f to get y = f(u).
dy/du = f'(u), evaluated at u = g(x). This is the ‘outer’ contribution.
du/dx = g'(x). This is how much u wiggles when x wiggles.
The famous formula. For deeper nesting, chain more terms — that's backprop.
Example: y = sin(x²). Outer = sin, inner = x². Then y' = cos(x²) · 2x.
Symbolic vs numeric vs automatic differentiation
Exact algebra
- Returns a formula, not a number
- Slow — expression trees blow up for deep composition
- Great for teaching + verifying
- Rarely used in production ML
Approximate, uses only function evaluations
- (f(x+h) - f(x)) / h with tiny h
- Simple, universal — works on ANY function
- Precision limited by float epsilon
- O(n) function calls for n-variable gradient — too slow for ML
Exact, reverse-mode
- Build a computation graph on the forward pass
- Backprop: apply chain rule once per node
- Same speed as forward — one gradient for millions of parameters
- This is how EVERY modern ML framework does it
"A derivative is the slope of a curve — a geometry fact about graphs. Useful in calculus class, but in ML it's just something the autograd library computes for me."
A derivative is a local linear approximation: it answers "if I nudge the input by a tiny amount, how much does the output move, and in which direction?" That reading is what makes it the engine of optimisation, sensitivity analysis, and error propagation — the slope picture is one special case in one dimension.
Because the tangent-line image is genuinely correct and it is drawn on the first day, so it becomes the whole concept. It is also sufficient for every exercise you are asked to do by hand. It quietly stops helping the moment there is more than one input: with a million parameters there is no curve to look at, and "slope" has no meaning until you specify a direction. Meanwhile the nudge reading generalises immediately — each partial derivative is "how much does the loss move if I nudge this one parameter, holding the rest fixed", which is exactly what backpropagation computes and exactly what an optimiser consumes. The geometric picture also hides the property that matters most in practice: the approximation is only valid locally, which is the entire reason learning rates exist.
The derivative predicts the nudge — and you can watch the prediction degrade as the step grows:
f = lambda x: x**3
df = lambda x: 3*x**2
x = 2.0
for h in [1e-1, 1e-3, 1e-5]:
actual = f(x + h) - f(x)
predicted = df(x) * h
print(h, actual, predicted, abs(actual - predicted))
# error shrinks like h^2 - the linear model is exact only in the limitWhy does the chain rule multiply derivatives? And why does that single fact explain both vanishing gradients and why deep networks were hard to train before residual connections?
- 1Near a point, a differentiable function is well approximated by a linear one: a nudge
δin the input produces roughlyf'(x)·δin the output.forced by · that is the definition of the derivative — the best local linear fit - 2Composing functions means feeding one's output into the next, so the second function receives the first's nudge as its input nudge.forced by · composition is exactly "the output of g becomes the input of f"
- 3Applying the linear approximation twice: an input nudge
δbecomesg'(x)·δ, which becomesf'(g(x))·g'(x)·δ.forced by · each layer applies its own local scaling factor to whatever arrives - 4Therefore the sensitivity of a composition is the product of the per-stage sensitivities — the chain rule is scaling factors accumulating multiplicatively through the chain.forced by · applying one scale factor after another multiplies them; this is the only thing composition of linear maps can do
- 5In a network of L layers, the gradient reaching layer 1 is a product of L factors. If those factors average below 1, the product decays exponentially in L; above 1, it explodes exponentially.forced by · a product of L numbers is governed by their geometric mean raised to the power L
Therefore vanishing and exploding gradients are not mysterious pathologies of deep learning — they are what multiplication does over long chains, and they were predictable from the chain rule alone.
And note precisely what this predicts, all of which is historically what happened: saturating activations like sigmoid have derivatives bounded well below 1, so deep sigmoid stacks must vanish — hence ReLU, whose derivative is exactly 1 on the active side. It predicts that gradient clipping addresses only the exploding half. And it predicts why a residual connection helps so decisively: y = x + F(x) has derivative 1 + F'(x), so the product always carries a path of 1s through the chain, and the gradient reaching early layers no longer decays with depth. Batch normalisation attacks the same product from the other side by keeping per-layer scales near 1.
For any quantity that depends on any input, ask: if I move this input a little, how much does the output move? That number is the derivative, and its sign tells you which way to move to increase the output. That is the whole of differential calculus as applied to engineering.
In many dimensions, collect one nudge answer per input and you have the gradient — a vector pointing in the direction of steepest increase. Optimisation is then just "compute the nudge answers, step the opposite way, repeat", and the only remaining question is how big a step the local approximation can still be trusted for.
- The linear approximation is only valid locally. Step too far and the prediction is wrong — this is exactly what a too-large learning rate is, and why loss diverges rather than merely converging slowly.
- Derivative zero means flat, which could be a minimum, a maximum, or a saddle. In high dimensions saddles vastly outnumber true minima, which is why plain gradient descent stalls and why momentum exists.
- Non-differentiable points are real and matter: ReLU at 0, absolute value, hard thresholds. Frameworks pick a subgradient by convention, which is why L1 regularisation drives coefficients exactly to zero while L2 only shrinks them.
- The chain rule composes multiplicatively, so anything that keeps per-stage factors near 1 — residual paths, normalisation, careful initialisation — is the mechanism that makes depth trainable.
Fire this model the moment you see: a loss that diverges to NaN · early layers that barely change during training · a sensitivity or "what-if" question · error bars propagating through a calculation · a hyperparameter you are tuning by hand · requires_grad or .backward().
You need derivatives of a function in code. Hand-derive them, use finite differences, or use automatic differentiation?
Use autodiff, and use finite differences to check it whenever you write a custom operation. Gradient checking is cheap insurance against the worst class of ML bug: a wrong gradient produces no error, no warning, and a model that trains to a plausible-looking but incorrect solution.
The asymmetry worth internalising is why reverse mode dominates: forward mode costs one pass per input, reverse mode costs one pass per output. Training has millions of inputs and a single scalar loss, so reverse mode is the obvious winner — and that asymmetry is the entire reason backpropagation, rather than any other differentiation scheme, is what made deep learning computationally feasible.
(c) Hands-on · 25 min
You'll compute derivatives three ways — by hand, numerically, and with SymPy — and verify they agree. Then you'll build a tiny scalar autodiff engine to see how PyTorch works under the hood. Save as calculus_demo.py.
Run it:
uv run --with numpy python calculus_demo.pyExpected output:
What each block does
Anatomy of the code
Add a deeper computation and check the gradient:
# y = ((x**2 + 3)*x).sin() at x = 1.2
x = Value(1.2)
b = x**2 + Value(3)
c = b * x
y = c.sin()
y.backward()
# Chain rule by hand: dy/dx = cos(c) · dc/dx
# dc/dx = db/dx * x + b * 1 = 2x * x + b = 2x^2 + (x^2 + 3) = 3x^2 + 3
# So dy/dx = cos((x^2 + 3)*x) * (3x^2 + 3)
import numpy as np
manual = np.cos((1.2**2 + 3) * 1.2) * (3 * 1.2**2 + 3)
print(f"autodiff = {x.grad:.6f} manual = {manual:.6f}")Now change Value(3) to Value(10) and predict how x.grad will change before running.
(d) Production reality · 15 min
Every deep-learning engineer eventually meets the ‘I don't really understand what autograd does’ moment — usually when a gradient explodes, a NaN appears mid-training, or a custom layer's gradient is wrong.
Copy-pasting optimizer.zero_grad(); loss.backward(); optimizer.step() without understanding the internals means you can't debug when it breaks.
micrograd (100 lines of Python) reimplements the essence of PyTorch's autograd. Once you've read it, PyTorch's C++ source stops being magic. Every ML engineer should read it once. The 45-min ‘Spelled-out intro to neural networks and backpropagation’ video is the best teaching artifact on the subject.nan and stays there forever. The culprit: log(0) hidden in a softmax cross-entropy calculation on a batch where one class had probability zero due to floating-point underflow.log-sum-exp-stabilised versions of softmax/cross-entropy (built into PyTorch as F.cross_entropy). Add gradient clipping (torch.nn.utils.clip_grad_norm_). Check for NaN with torch.isnan(loss).any() and abort the epoch. Learn to spot the ‘loss is a nice number, gradient is NaN’ pattern early.Where this shows up in the rest of the plan
(e) Recall + stretch · 10 min
Explain-out-loud test
- What does a derivative measure, in one sentence?
- State the chain rule and give a 3-line example.
- Why does every modern ML framework use reverse-mode autograd?
What comes next
Hub: The 6-Month Learning Plan
Part of a 130-session evergreen learning series. Session structure: intuition → visual → hands-on → production war stories → recall. Duration: 90 minutes.