Assumes you know from ML-101
You have completed ML-101 lesson 3 — Linear Regression, where you met the normal equation; ML-101 lesson 11 — Gradient Descent & Optimization, where you saw that points uphill; and ML-101 lesson 14 — Neural Networks & Backprop, where the chain rule was applied without comment. This lesson does not re-derive those facts from the foundations of real analysis. Instead it re-states them in the language that the rest of ML-201 will use: linear maps between finite-dimensional spaces, Jacobians as the matrix form of the chain rule, and gradients as the Riesz representer of the differential. The aim is not to teach new symbols; it is to fix the meaning of the symbols you already use, so that every later derivation in ML-201 can speak in the same vocabulary.
Learning Objectives
- Given a differentiable map , write the Jacobian as the matrix of partial derivatives, and state the chain rule for the composition as a matrix product.
- Prove that is the direction of steepest ascent of at , and translate that proof into the algorithm "negative gradient descent".
- For a joint distribution , write Bayes' theorem as a reweighting of the prior by the likelihood ratio , and identify the place in the calculation where the marginal is intractable.
- Name the assumption whose violation breaks the chain rule (non-differentiability), breaks the gradient-as-steepest-ascent statement (non-Euclidean norm), and breaks Bayes' theorem (zero measure on the conditioning event).
- Translate any expression in this lesson into runnable NumPy code, and predict the shape of every intermediate array.
Linear maps and the matrix as function
A matrix is not a grid of numbers. It is a function , defined by the rule , with the properties that is linear () and continuous (which on a finite-dimensional space follows from linearity plus a bounded operator norm). The grid-of-numbers picture is the representation of in the standard basis; the picture that supports every later derivation is "linear function from one vector space to another".
The matrix-vector product has a concrete shape rule: the -th row of is dotted with to produce the -th entry of . The -th column of is the image of the -th standard basis vector . The shape rule is the same when you swap for : now rows become columns, and . This is the source of the backprop shape convention: in a two-layer network , the weight maps hidden activations to outputs, and the gradient of the loss with respect to must be the product of matrices whose shapes match the rule — not the product of any two adjacent matrices.
The matrix-as-function picture is what makes a non-square matrix coherent. maps a 12-dimensional input to a 4-dimensional output in one application, with no iteration and no implicit "filling in". When you see with , ask which space is the domain and which is the codomain — the question is unambiguous once is read as a function.
Three consequences matter for the rest of the course:
- Rank is a function statement. , which is at most and equals that bound iff is surjective onto its image. Rank deficiency is not "small numbers in the determinant"; it is the non-existence of a pre-image for some output.
- Singular values measure stretch. is the largest factor by which stretches any unit vector, and the singular vectors are the directions in which that maximum is attained. The condition number is the ratio of the largest to the smallest stretch. It controls the speed of gradient descent: under the spectral bound on , convergence in times the optimal steps is required for iteration complexity.
- Transpose is the adjoint. , with respect to the standard inner product. This identity is the bridge between the matrix-as-function picture and the gradient-as-linear-functional picture in the next section.
Jacobians and the chain rule, in matrix form
For differentiable at , the Jacobian is the matrix
The -th row of is the gradient of the -th scalar component . The Jacobian is the linear approximation to at :
This is a Taylor expansion up to first order, with the Jacobian playing the role the derivative plays for scalar functions. The full statement of differentiability at is exactly this limit, with meaning a term whose magnitude is negligible relative to .
The chain rule in matrix form is the statement that the Jacobian of a composition is the product of Jacobians. If and , then
The dimensions check: , , and the product lives in , which is the correct shape for the Jacobian of . This is the single line that backs backpropagation in ML-201 lesson 4: every backprop step is a matrix product along a path in the computational graph, and the matrix product is literally the chain rule, not an analogy.
The scalar case is recovered when . Then , which is conventionally written as a row vector and identified with the gradient when is scalar-valued. The chain rule for scalar reduces to , which is the form taught in ML-101 lesson 11. The matrix form is the correct generalisation; the scalar form is what survives when the intermediate space is one-dimensional.
import numpy as np
def numerical_jacobian(f, x, h=1e-5):
"""Central-difference estimate of the Jacobian of vector-valued f at x.
f: R^m -> R^n. Returns an (n, m) matrix. The estimate is O(h^2) per entry.
"""
x = np.asarray(x, dtype=float)
n = len(f(x))
m = len(x)
J = np.zeros((n, m))
for j in range(m):
ej = np.zeros_like(x); ej[j] = h
J[:, j] = (f(x + ej) - f(x - ej)) / (2 * h)
return J
# Example: f(x) = (x_0^2, x_0 * x_1, exp(x_1)).
f = lambda x: np.array([x[0] ** 2, x[0] * x[1], np.exp(x[1])])
x0 = np.array([0.7, -0.4])
J_num = numerical_jacobian(f, x0)
J_ana = np.array([
[2 * x0[0], 0.0], # d/dx of x_0^2
[x0[1], x0[0]], # d/dx of x_0 * x_1
[0.0, np.exp(x0[1])]
])
print("max abs error:", float(np.abs(J_num - J_ana).max()))
The assumption whose violation breaks the chain rule is differentiability at the evaluation point. If has a kink at (a ReLU evaluated exactly at zero, for instance), the Jacobian does not exist there and the chain rule has nothing to multiply. The 201-level fix is sub-derivative or Clarke generalised Jacobian, which is a different theory and a different product rule; the 101-level fix is "treat the kink as one-sided" and pretend the chain rule still holds — it does not.
Gradient as steepest ascent
For a scalar function , the gradient is the vector
The first-order Taylor expansion gives . To maximise this linear approximation over unit vectors with , write , where is the angle between and . The linear term becomes , which is maximised at , giving the maximiser and the maximum value . So points uphill, and points downhill; the gradient descent step is exactly steepest descent.
The argument is more subtle than ML-101 presented. The conclusion "gradient points uphill" is true under the Euclidean norm. If the descent step uses a different norm — say the dual norm of a weighted , or the norm — then the direction of steepest descent is not the negative gradient; it is , where is the vector solving for all , with equality on the minimiser. This is the entire mathematical content of mirror descent, natural-gradient descent, and Hessian-aware optimisation, all of which would be incoherent under the 101-level claim "the negative gradient is always the steepest direction".
import numpy as np
def gradient_descent(f, grad_f, x0, lr=1e-2, n_steps=200):
"""Plain Euclidean gradient descent: x_{k+1} = x_k - lr * grad_f(x_k)."""
x = np.asarray(x0, dtype=float).copy()
history = [x.copy()]
for _ in range(n_steps):
x = x - lr * grad_f(x) # negative gradient = direction of steepest DESCENT
history.append(x.copy())
return x, np.asarray(history)
# Toy quadratic f(x) = 0.5 x^T A x, with grad f(x) = A x.
A = np.array([[3.0, 1.0], [1.0, 2.0]])
f = lambda x: 0.5 * x @ A @ x
grad_f = lambda x: A @ x
x_star, path = gradient_descent(f, grad_f, x0=np.array([4.0, -3.0]), lr=0.2)
print("final x:", x_star.round(6)) # should approach [0, 0] (the unique minimiser)
The deeper 201-level point is that the gradient is the Riesz representer of the differential. For every continuous linear functional , there is a unique vector such that for all . The differential is such a linear functional; its Riesz representer is the gradient. This definition is independent of basis, and that basis-independence is why the gradient is the same object in and in the parameter space of a model, and why the chain rule can be expressed without choosing coordinates.
Conditional probability and Bayes' theorem, derived
Conditional probability is defined, in the measure-theoretic foundations, by
provided . The conditional probability is not an independent primitive; it is defined in terms of the joint. ML-101 used this definition to compute in tabular examples; ML-201 uses it to derive Bayes' theorem, which is a corollary of the definition:
The numerator is the joint; the denominator is the marginal of the joint over . This is not a profound identity. It is a reweighting of the prior by the likelihood ratio . Every "Bayesian" calculation is this reweighting, and the only hard part is computing the marginal in the denominator.
import numpy as np
def bayes_predict(prior: np.ndarray, likelihood: np.ndarray) -> np.ndarray:
"""Posterior p(y | x) given prior p(y) (shape (k,)) and likelihood p(x | y) (shape (n, k)).
Each column j is the n-vector of p(x | y=j). Returns posterior of shape (n, k).
"""
joint = likelihood * prior[None, :] # shape (n, k), elementwise
marginal = joint.sum(axis=1, keepdims=True) # shape (n, 1)
return joint / marginal # shape (n, k), normalised
# Toy: 3 classes, 5 input bins.
prior = np.array([0.5, 0.3, 0.2]) # p(y)
likelihood = np.array([[0.10, 0.20, 0.30],
[0.30, 0.20, 0.10],
[0.40, 0.30, 0.20],
[0.10, 0.20, 0.30],
[0.10, 0.10, 0.10]]) # p(x | y)
posterior = bayes_predict(prior, likelihood)
print("posterior row sums (should all be 1):", posterior.sum(axis=1).round(6))
The intractable marginal is the reason variational methods exist. When is continuous, the sum becomes an integral, and has no closed form for almost any model of practical interest. The expectation under that integral is what the sampler or the variational approximation is computing. The derivation is one line; the work is the integral.
The assumption whose violation breaks Bayes' theorem is for the observed . If the support of does not contain the observation, then is undefined on that for every , and the conditional is not a probability at all. In practice this is rare; in theory it matters, because a model whose likelihood vanishes on the test set is a model that cannot be evaluated on the test set. The 101-level mistake is to compute by plugging in without checking that is in the support; the 201-level fix is to assert the support assumption explicitly.
The five shape rules that catch every bug
The chain rule in matrix form has five shape rules that, memorised once, catch every "shape mismatch" bug in every later derivation in ML-201.
| Computation | Shape rule | Failure mode if violated |
|---|---|---|
| Mismatched inner dimension; multiplication undefined | ||
| for | Wrong broadcast rule; off-by-one in axis argument | |
| for | Forget the transpose; gradient against | |
| for (Hadamard) | Multiply instead of Hadamard; shapes can match but math is wrong | |
| for | Confuse and direction of multiplication |
These five rules are stated here as a reference; the lesson 4 derivation of backprop uses every one of them. A learner who can read a derivation and verify each rule against the table is a learner who will not need a debugger to find the shape mismatch. A learner who cannot is a learner who will spend a working day on what is actually a one-minute check.
The deeper statement, which is not on the table but which the table makes concrete, is that the chain rule is always the same chain rule. Every "advanced" gradient — through attention, through convolution, through a recurrent cell — is the same matrix product along the path of the computation, with the same shape rules. There is no special gradient for attention; the attention gradient is the chain rule applied to a particular factorisation of the matrix product. ML-201 lesson 4 makes this explicit by deriving backprop for a two-layer network and then generalising to arbitrary graphs.
Key Takeaways
- A matrix is a linear function , not a grid of numbers. Read it as a function and the rest of the lesson follows.
- The Jacobian is the matrix form of the derivative, and the chain rule is the matrix product. Every backprop step is one matrix product along one path in the computational graph.
- The gradient is the Riesz representer of the differential, and points uphill only under the Euclidean norm. Mirror descent uses a different norm and gets a different "steepest" direction.
- Bayes' theorem is the reweighting of a prior by a likelihood ratio, with a marginal in the denominator that is intractable for almost any model of practical interest. The integral is the work.
- Memorise the five shape rules. They catch every "shape mismatch" bug in every later derivation, and they are the only piece of memorisation the lesson asks for.