Block 4.2 Advanced AI
How should we change the parameters so that this number gets smaller?
derivative → partial derivative → gradient → curvature → optimiser.
By the end of this lecture, you should be able to:
Suppose a model predicts
\[ \hat{\vect{y}} = \vmodel{\vect{x}}, \]
where \(\params\) contains all trainable parameters.
A loss function assigns one scalar to the model’s performance on the training data:
\[ \loss[\params]. \]
Training means searching for
\[ \hat{\params} = \argmin_{\params} \loss[\params]. \]
In optimisation texts, \(\loss\) is often called the objective function. In machine learning, loss and cost are also common. Here, the objective is to minimise the loss.
The expression
\[ \hat{\params} = \argmin_{\params} \loss[\params] \]
is easier to read from the inside out.
The optimiser changes parameters. Those parameters determine the predictions, and the predictions determine the loss.
For a model with two parameters,
\[ \params = \begin{bmatrix} \phi_0\\\\ \phi_1 \end{bmatrix}, \]
one parameter setting is one point in a two-dimensional parameter space.
The loss assigns a height to every point:
\[ (\phi_0,\phi_1) \longmapsto \loss[\phi_0,\phi_1]. \]
Training can now be viewed geometrically: move through parameter space toward lower loss.
Each parameter setting is a point in parameter space; the loss assigns a height to that point.
Suppose we move from \(z_0\) to \(z_0+\Delta z\).
The function changes by
\[ \Delta g = g(z_0+\Delta z)-g(z_0). \]
Over that interval, the average slope is
\[ \frac{\Delta g}{\Delta z} = \frac{g(z_0+\Delta z)-g(z_0)}{\Delta z}. \]
As the interval shrinks, the secant slope approaches the local slope at \(z_0\).
For a scalar function \(g(z)\),
\[ \frac{dg}{dz}\bigg|_{z_0} = \lim_{\Delta z\to 0} \frac{g(z_0+\Delta z)-g(z_0)}{\Delta z}. \]
The limit says: measure the slope over smaller and smaller intervals around \(z_0\).
For this lecture, the important result is the value of that local slope — not the formal machinery of limits.
As the second point moves toward \(z_0\), the secant slope approaches the tangent slope: the derivative at \(z_0\).
For a sufficiently small step \(\Delta z\),
\[ \frac{\Delta g}{\Delta z} \approx \frac{dg}{dz}\bigg|_{z_0}. \]
Multiply by \(\Delta z\):
\[ \underbrace{\Delta g}_{\text{predicted change}} \approx \underbrace{\frac{dg}{dz}\bigg|_{z_0}}_{\text{change per unit}} \underbrace{\Delta z}_{\text{proposed step}}. \]
For example, if the local slope is \(3\) and \(\Delta z=0.1\),
\[ \Delta g\approx 3\times 0.1=0.3. \]
Since \(\Delta g=g(z_0+\Delta z)-g(z_0)\),
\[ \boxed{ g(z_0+\Delta z) \approx g(z_0) + \frac{dg}{dz}\bigg|_{z_0}\Delta z } \]
The derivative gives the local slope and, for a small step, predicts the change.
Near \(z_0\), the tangent line uses the derivative to predict the effect of a small input change \(\Delta z\).
A neural network does not have one parameter.
It may have millions or billions:
\[ \params = [\phi_0,\phi_1,\ldots,\phi_N]\transpose. \]
With many parameters, ask the derivative question one coordinate at a time:
What happens to the loss if I nudge this parameter while temporarily holding the others fixed?
This is a partial derivative.
For a loss \(\loss[\phi_0,\phi_1]\),
\[ \frac{\partial \loss}{\partial \phi_0} \]
means:
Likewise,
\[ \frac{\partial \loss}{\partial \phi_1} \]
asks the same question along the other coordinate direction.
Notation:
Read \(\partial\loss/\partial\phi_0\) as “partial L with respect to phi-zero.”
Each partial derivative measures the local slope along one parameter axis while the other parameters are held fixed.
One partial derivative tells us about one coordinate.
The gradient collects all coordinate sensitivities in one vector:
\[ \boxed{ \nabla_{\params}\loss = \begin{bmatrix} \dfrac{\partial \loss}{\partial \phi_0}\\[0.6em] \dfrac{\partial \loss}{\partial \phi_1}\\ \vdots\\ \dfrac{\partial \loss}{\partial \phi_N} \end{bmatrix} } \]
\(\nabla\) is nabla.
Read \(\nabla_{\params}\loss\) as “the gradient of the loss with respect to the parameters.”
Its shape matches \(\params\): one sensitivity for every parameter.
Suppose we change all parameters by a small vector \(\Delta\params\).
The scalar prediction rule becomes
\[ \boxed{ \loss[\params+\Delta\params] \approx \loss[\params] + \inner{\nabla_{\params}\loss}{\Delta\params} } \]
The dot product adds the predicted contribution from every coordinate:
\[ \inner{\nabla_{\params}\loss}{\Delta\params} = \frac{\partial\loss}{\partial\phi_0}\Delta\phi_0 + \frac{\partial\loss}{\partial\phi_1}\Delta\phi_1 + \cdots + \frac{\partial\loss}{\partial\phi_N}\Delta\phi_N. \]
For parameter moves with the same Euclidean length,
\[ \inner{\nabla_{\params}\loss}{\Delta\params} \]
is largest when \(\Delta\params\) points in the same direction as the gradient.
Using the angle form of the dot product from week 1:
\[ \inner{\nabla_{\params}\loss}{\Delta\params} = \norm{\nabla_{\params}\loss}\,\norm{\Delta\params}\cos\theta. \]
So, under the ordinary Euclidean notion of step size, and wherever the gradient isn’t \(\vect{0}\):
Among equal-length parameter moves, the move aligned with the gradient gives the largest predicted increase in loss; the opposite move gives the largest predicted decrease.
On a contour map, \(\nabla_{\params}\loss\) points in the direction of steepest local increase; \(-\nabla_{\params}\loss\) gives the steepest local decrease.
Gradient descent uses this direction directly:
\[ \underbrace{\params_{t+1}}_{\text{new parameters}} = \underbrace{\params_{t}}_{\text{current parameters}} - \underbrace{\alpha}_{\text{step size}} \underbrace{\nabla_{\params}\loss[\params_t]}_{\text{uphill direction}}. \]
The learning rate \(\alpha\) sets the size of each update.
Too small:
Too large:
A classical way to choose the step length is a line search: first choose a downhill direction, then search along that line for a useful value of \(\alpha\).
The gradient chooses a descent direction; the learning rate or line search determines how far to move along it.
Start with the familiar model
\[ \hat{y}_i = \model{x_i} = \phi_0+\phi_1x_i. \]
For one training example \((x_i,y_i)\), define the residual
\[ r_i = \underbrace{\phi_0+\phi_1x_i}_{\text{prediction}} - \underbrace{y_i}_{\text{target}}. \]
Week 1 wrote the residual as target minus prediction. This lecture follows Prince’s order, so the residual sign flips but the squared loss is unchanged.
With squared error,
\[ \exloss_i=r_i^2. \]
Take one example with
\[ x_i=2,\qquad y_i=5,\qquad \phi_0=1,\qquad \phi_1=1. \]
The model predicts \(3\), so
\[ r_i=3-5=-2,\qquad \exloss_i=(-2)^2=4. \]
Now nudge only the intercept by \(\Delta\phi_0=0.1\). The prediction rises from \(3\) to \(3.1\), so the residual becomes less negative and the squared error should fall.
The derivative predicts the size of that local change.
The chain rule multiplies local sensitivities along a dependency path from a parameter to the loss.
Learning problems are built from dependent calculations:
\[ \phi_0 \longrightarrow r_i \longrightarrow \exloss_i. \]
The chain rule multiplies the local sensitivities along that path:
\[ \boxed{ \frac{\partial\exloss_i}{\partial\phi_0} = \underbrace{\frac{\partial\exloss_i}{\partial r_i}}_{\text{loss sensitivity}} \underbrace{\frac{\partial r_i}{\partial\phi_0}}_{\text{model sensitivity}} } \]
More generally, if changing \(a\) changes \(b\), and changing \(b\) changes \(c\),
\[ \frac{dc}{da} = \frac{dc}{db}\frac{db}{da}. \]
A deep network uses the same rule repeatedly through a much larger dependency graph.
We want
\[ \frac{\partial \exloss_i}{\partial \phi_0}. \]
Start from
\[ \exloss_i=(\phi_0+\phi_1x_i-y_i)^2. \]
Use the chain rule:
\[ \frac{\partial \exloss_i}{\partial \phi_0} = \underbrace{2r_i}_{\text{how loss changes with residual}} \underbrace{\frac{\partial r_i}{\partial \phi_0}}_{=1} = 2r_i. \]
The intercept derivative is proportional to the signed residual.
For the slope parameter:
\[ \frac{\partial \exloss_i}{\partial \phi_1} = \underbrace{2r_i}_{\text{error signal}} \underbrace{\frac{\partial r_i}{\partial \phi_1}}_{=x_i} = 2x_i r_i. \]
Why does \(x_i\) appear?
Because changing the slope affects the prediction by an amount proportional to the input:
\[ \frac{\partial}{\partial\phi_1}(\phi_1x_i)=x_i. \]
The example from earlier: \(x_i=2\), \(r_i=-2\), and a nudge of \(\Delta\phi_0=0.1\).
The derivative predicts \(-0.4\); the actual change is \(-0.39\).
Their difference is second order: halve the step and it quarters.
Stack the two partial derivatives to form the per-example gradient:
\[ \nabla_{\params}\exloss_i = \begin{bmatrix} 2r_i\\\\ 2x_i r_i \end{bmatrix} = 2r_i \begin{bmatrix} 1\\\\ x_i \end{bmatrix}. \]
The two factors have different roles:
If
\[ \loss[\params] = \sum_{i=1}^{I}\exloss_i, \]
then differentiation distributes across the sum:
\[ \nabla_{\params}\loss = \sum_{i=1}^{I} \nabla_{\params}\exloss_i. \]
The full gradient is the sum of the per-example gradients.
Sum or mean?
We use a sum here because it follows directly from the per-example derivation. Many libraries report the mean loss instead:
\[ \bar{\loss}=\frac{1}{I}\sum_{i=1}^{I}\exloss_i. \]
The minimiser is unchanged; the gradient is simply scaled by \(1/I\). That constant scale can be accounted for in the learning rate.
Writing one derivative per parameter is manageable for two parameters.
With millions of parameters, coordinate-by-coordinate notation is no longer practical.
Matrix calculus expresses the same derivative relationships compactly for vectors and matrices, while keeping the structure of the computation visible.
The design matrix collects all examples so that the complete prediction vector is computed as \(\mat{X}\params\).
For all \(I\) examples, create the design matrix
\[ \mat{X} = \begin{bmatrix} 1 & x_1\\\\ 1 & x_2\\\\ \vdots & \vdots\\\\ 1 & x_I \end{bmatrix}, \qquad \params = \begin{bmatrix} \phi_0\\\\ \phi_1 \end{bmatrix}. \]
Stacking the examples gives all predictions in one matrix multiplication:
\[ \hat{\vect{y}} = \mat{X}\params. \]
The residual vector is
\[ \vect{r}=\mat{X}\params-\vect{y}. \]
The sum of squared residuals is
\[ \loss[\params] = \sum_{i=1}^{I} r_i^2 = \vect{r}\transpose\vect{r}. \]
Substituting \(\vect{r}=\mat{X}\params-\vect{y}\):
\[ \loss[\params] = (\mat{X}\params-\vect{y})\transpose (\mat{X}\params-\vect{y}). \]
The vector expression is identical to the scalar least-squares loss.
We already have
\[ \loss=\vect{r}\transpose\vect{r}. \]
So the loss sensitivity with respect to the residual vector is
\[ \nabla_{\vect{r}}\loss=2\vect{r}. \]
This is the vector version of \(d(r^2)/dr=2r\).
And because
\[ \vect{r}=\mat{X}\params-\vect{y}, \]
a small parameter move produces
\[ \Delta\vect{r}=\mat{X}\Delta\params. \]
The first-order change in loss is
\[ \Delta\loss \approx \inner{2\vect{r}}{\Delta\vect{r}} = (2\vect{r})\transpose\mat{X}\Delta\params. \]
Rearrange the scalar product:
\[ \Delta\loss \approx \left(2\mat{X}\transpose\vect{r}\right)\transpose\Delta\params. \]
Compare this with
\[ \Delta\loss\approx(\nabla_{\params}\loss)\transpose\Delta\params. \]
Both expressions must agree for every small \(\Delta\params\), so their coefficient vectors are equal:
\[ \boxed{\nabla_{\params}\loss=2\mat{X}\transpose\vect{r}=2\mat{X}\transpose(\mat{X}\params-\vect{y})}. \]
1. \(\mat{X}\params-\vect{y}\): compute the residuals.
2. \(2(\cdot)\): convert each residual into a loss sensitivity.
3. \(\mat{X}\transpose(\cdot)\): map those sensitivities back onto the parameter coordinates.
This is the same chain rule as the one-example derivation, written for the whole dataset.
Suppose a vector-valued function produces
\[ \vect{z}=\vect{f}(\params) \in\reals^{\dout}, \qquad \params\in\reals^{N+1}. \]
Its Jacobian collects every first-order sensitivity:
\[ \mat{J}_{\vect{f}} = \frac{\partial \vect{z}}{\partial \params\transpose} = \begin{bmatrix} \dfrac{\partial z_1}{\partial \phi_0} & \cdots & \dfrac{\partial z_1}{\partial \phi_N}\\\\ \vdots & \ddots & \vdots\\\\ \dfrac{\partial z_{\dout}}{\partial \phi_0} & \cdots & \dfrac{\partial z_{\dout}}{\partial \phi_N} \end{bmatrix}. \]
Our convention is: rows correspond to outputs; columns correspond to parameters. With this convention, \(\mat{J}_{\vect{f}}\) has shape \(\dout\times(N+1)\).
The Jacobian maps a small parameter perturbation to the corresponding first-order change in the model outputs.
A loss often has the structure
\[ \params \longrightarrow \hat{\vect{y}} \longrightarrow \loss. \]
The multivariable chain rule says
\[ \nabla_{\params}\loss = \mat{J}_{\vect{f}}\transpose \nabla_{\hat{\vect{y}}}\loss. \]
Check the dimensions:
\[ \underbrace{\mat{J}_{\vect{f}}\transpose}_{(N+1)\times\dout} \underbrace{\nabla_{\hat{\vect{y}}}\loss}_{\dout\times 1} = \underbrace{\nabla_{\params}\loss}_{(N+1)\times 1}. \]
The same chain-rule pattern propagates loss sensitivities through a deep network.
Linear least squares has a convex loss landscape.
For a convex function, every local minimum is also global.
Geometrically, every chord joining two points on the graph lies on or above the function.
Convexity removes suboptimal local minima from the landscape.
Gradient descent still needs a suitable step-size strategy to converge.
For a scalar function \(g(z)\),
\[ g'(z)=\frac{dg}{dz} \]
gives the local slope.
Differentiate the slope itself:
\[ g''(z)=\frac{d^2g}{dz^2}. \]
The second derivative measures how the slope changes as we move.
Locally:
For a scalar loss with many parameters, the gradient is itself a vector-valued function:
\[ \params \longmapsto \nabla_{\params}\loss. \]
Differentiate that vector with respect to the parameters:
\[ \boxed{ \mat{H} = \frac{\partial(\nabla_{\params}\loss)}{\partial\params\transpose} = \nabla_{\params}^{2}\loss } \]
This matrix is the Hessian.
Expanded,
\[ \mat{H} = \begin{bmatrix} \dfrac{\partial^2 \loss}{\partial \phi_0^2} & \dfrac{\partial^2 \loss}{\partial \phi_0\partial\phi_1} & \cdots\\[0.8em] \dfrac{\partial^2 \loss}{\partial \phi_1\partial\phi_0} & \dfrac{\partial^2 \loss}{\partial \phi_1^2} & \cdots\\ \vdots & \vdots & \ddots \end{bmatrix}. \]
To interpret the Hessian geometrically, recall how a matrix acts on directions.
Recall that matrix multiplication can be read geometrically as a linear transformation.
Here the matrix stretches one coordinate and shrinks the other:
\[ \mat{A} = \begin{bmatrix} 1.8 & 0\\ 0 & 0.65 \end{bmatrix}. \]
Under non-uniform scaling, the grid and unit circle stretch into a rectangle-like grid and an ellipse. The hue tracks each point from the original circle to its transformed position.
For this scaling matrix, horizontal and vertical vectors change length but remain on their original lines.
Now rotate every vector by \(40^\circ\):
\[ \mat{R} = \begin{bmatrix} \cos 40^\circ & -\sin 40^\circ\\ \sin 40^\circ & \cos 40^\circ \end{bmatrix}. \]
A rotation preserves the shape of the unit circle, so hue is important: every coloured point moves around the circle. No non-zero real vector remains on its original line for this angle.
The circle still looks like a circle, but the hue shows that every non-zero real vector has moved to a different line.
A shear mixes the coordinates:
\[ \mat{S} = \begin{bmatrix} 1 & 1.1\\ 0 & 1 \end{bmatrix}. \]
A shear tilts the grid and turns most vectors, but the horizontal axis remains on the same line.
Most directions turn, but vectors on the horizontal axis remain on that same line.
A real \(2\times2\) matrix therefore need not have two distinct real invariant directions.
Those invariant directions are eigenvector directions.
A non-zero vector \(\vect{v}\) is an eigenvector of \(\mat{A}\) when
\[ \boxed{\mat{A}\vect{v}=\lambda\vect{v}}. \]
Geometrically:
The sign and magnitude of \(\lambda\) tell us what happened:
For the scaling matrix above,
\[ \mat{A}\begin{bmatrix}1\\0\end{bmatrix} =1.8\begin{bmatrix}1\\0\end{bmatrix}, \qquad \mat{A}\begin{bmatrix}0\\1\end{bmatrix} =0.65\begin{bmatrix}0\\1\end{bmatrix}. \]
The coordinate axes are eigenvector directions; \(1.8\) and \(0.65\) are their eigenvalues.
To find an eigenvector, we need a non-zero solution of
\[ (\mat{A}-\lambda\mat{I})\vect{v}=\vect{0}. \]
A non-zero solution exists only when \(\mat{A}-\lambda\mat{I}\) is singular: it must map some non-zero vector to zero.
For a square matrix, singular means determinant zero:
\[ \boxed{\det(\mat{A}-\lambda\mat{I})=0}. \]
This characteristic equation is a polynomial in \(\lambda\).
The eigenvalues are the roots of this polynomial.
As with ordinary polynomial roots, they need not all be real.
For a \(90^\circ\) rotation,
\[ \mat{R}_{90} = \begin{bmatrix} 0 & -1\\ 1 & 0 \end{bmatrix}, \qquad \det(\mat{R}_{90}-\lambda\mat{I}) = \lambda^2+1. \]
Its roots are \(\lambda=\pm i\), so there is no real eigendirection. That matches the geometry: every real vector turns.
A repeated root need not provide the same number of independent eigenvectors.
For the shear matrix
\[ \mat{S} = \begin{bmatrix} 1 & 1.1\\ 0 & 1 \end{bmatrix}, \]
we get
\[ \det(\mat{S}-\lambda\mat{I})=(1-\lambda)^2. \]
So \(\lambda=1\) is a repeated eigenvalue, but the horizontal axis supplies only one independent real eigendirection.
Symmetric Hessians
For the sufficiently smooth losses considered here, the mixed second partials agree, so the Hessian is symmetric.
Real symmetric matrices have:
For a Hessian, the eigenvectors therefore give a complete set of real principal directions.
For a small parameter move \(\Delta\params\), the Hessian gives the first-order change in the gradient:
\[ \boxed{ \nabla_{\params}\loss[\params+\Delta\params] \approx \nabla_{\params}\loss[\params] + \mat{H}\Delta\params } \]
\(\mat{H}\Delta\params\) is the first-order predicted change in the gradient caused by the parameter move \(\Delta\params\).
Near the current parameters,
\[ \loss[\params+\Delta\params] \approx \loss[\params] + \underbrace{\inner{\nabla_{\params}\loss}{\Delta\params}}_{\text{first-order change}} + \frac{1}{2} \underbrace{\Delta\params\transpose\mat{H}\Delta\params}_{\text{curvature correction}}. \]
The linear term contains the local slope information; the quadratic term adds the curvature correction.
Recall
\[ \nabla_{\params}\loss = 2\mat{X}\transpose(\mat{X}\params-\vect{y}). \]
Differentiate once more:
\[ \mat{H} = 2\mat{X}\transpose\mat{X}. \]
The Hessian no longer depends on \(\params\): ordinary least squares has the same curvature everywhere in parameter space.
Because \(\mat{X}\transpose\mat{X}\) is positive semidefinite, the least-squares objective is convex.
The terminology comes from Greek:
So isotropic is literally the idea of equal turning:
turn in a different direction, and the behaviour stays the same.
Anisotropic adds a negative prefix: the behaviour is not the same in every direction.
A familiar tropos
Homer describes Odysseus as polytropos (πολύτροπος): poly, many, + tropos, turns / ways.
In Homer the epithet has a broader sense — resourceful, wily, “of many turns” — but the shared root is useful here:
polytropos → many-turning; isotropic → equal-turning.
Isotropic means that turning to a different direction changes little; anisotropic means that curvature depends strongly on the direction.
For the positive-curvature landscapes shown here:
\[ \lambda_1 \approx \lambda_2 \quad\Rightarrow\quad \text{roughly circular contours}, \]
whereas
\[ \lambda_{\mathrm{steep}} \gg \lambda_{\mathrm{shallow}} \quad\Rightarrow\quad \text{elongated contours}. \]
The Hessian eigenvectors identify principal curvature directions in parameter space.
Let \(\vect{v}_k\) be a unit eigenvector of the Hessian and move along that direction, \(\Delta\params=c\,\vect{v}_k\). Then
\[ \Delta\params\transpose\mat{H}\Delta\params = \lambda_k c^2. \]
The Hessian eigenvectors give the principal curvature directions. One direction can be shallow while another is steep, producing an elongated valley.
The signs of the Hessian eigenvalues distinguish locally bowl-shaped, hill-shaped, and saddle-like geometry.
At a stationary point where \(\nabla_{\params}\loss=\vect{0}\):
Zero eigenvalues require more care: second-order information alone may not decide the case.
For a twice-differentiable function on a convex domain:
For models nonlinear in their parameters, the loss landscape is generally non-convex.
Possible features include:
The gradient still gives a local descent direction, but non-convexity removes the guarantee that following it will reach the global minimum.
Non-convex does not mean untrainable
Non-convexity removes the global-minimum guarantee. It does not imply that gradient-based training fails.
Prince’s Gabor model (equation 6.8):
\[ \model{x} = \sin(z)\,\exp\!\left(-\frac{z^{2}}{32}\right), \qquad z=\phi_0+0.06\,\phi_1x. \]
With two parameters we can still plot the complete loss surface; unlike least squares, it is non-convex.
Convex objectives have no suboptimal local minima; non-convex objectives can contain multiple valleys and saddle points.
A local minimum has no nearby downhill direction, but a better solution may exist elsewhere.
A saddle point need not be a minimum even when the gradient is zero. At a strict saddle, curvature is upward in some directions and downward in others.
Therefore
\[ \nabla_{\params}\loss\approx \vect{0} \]
shows that the local slope is small; it does not prove that the global minimum has been found.
Near a well-behaved local minimum, second-order methods such as Newton’s method use curvature to rescale the gradient:
\[ \Delta\params \approx -\mat{H}\inv\nabla_{\params}\loss. \]
Curvature adjusts the scale of the update:
But a neural network with millions of parameters would have an enormous Hessian. Forming and inverting it directly is usually impractical.
The full gradient uses every training example:
\[ \nabla_{\params}\loss = \sum_{i=1}^{I}\nabla_{\params}\exloss_i. \]
For a large dataset, computing that exact sum before every update is expensive. A minibatch lets us update the parameters after examining only part of the dataset.
Instead of using the whole dataset, choose a minibatch \(\set{B}_t\) at step \(t\).
Define the minibatch gradient
\[ \vect{g}_t = \frac{1}{|\set{B}_t|} \sum_{i\in \set{B}_t} \nabla_{\params}\exloss_i(\params_t). \]
Then update
\[ \params_{t+1} = \params_t - \alpha\vect{g}_t. \]
Averaging makes the gradient scale less dependent on batch size. Relative to the earlier sum-loss convention, this estimates \(\frac{1}{I}\nabla_{\params}\loss\); the constant factor \(I\) can be absorbed into the learning rate.
At the same parameter setting, different minibatches give different descent directions. The highlighted arrow is the full-data direction; all arrows are drawn at a fixed length, so only their direction carries meaning.
Full-batch gradient descent follows one deterministic gradient field; minibatch sampling perturbs the gradient and therefore the trajectory.
A minibatch gradient is an estimate of the full-dataset mean gradient.
The sampling noise changes the optimisation trajectory.
Each batch defines a slightly different local estimate of “downhill.”
Minibatching introduces sampling noise into the trajectory:
Batch size and learning rate interact
Batch size and learning rate jointly determine how noisy the parameter trajectory is.
These terms describe different units of training.
Special cases:
A common training loop shuffles the examples and then forms minibatches without replacement.
This ensures that:
The randomness comes from which examples are grouped together and when they are seen.
With full-batch gradient descent, each point in parameter space has one deterministic gradient.
With SGD, the gradient also depends on the sampled batch:
\[ \vect{g}_t = \vect{g}(\params_t,\set{B}_t). \]
SGD therefore follows the gradient of a slightly different minibatch loss at each step.
The path may temporarily move uphill on the full training loss even when the current minibatch gradient points downhill for its own batch.
Many training schedules use larger steps early and reduce them later.
Early training:
Later training:
A learning-rate schedule reduces \(\alpha\) during training.
Some training recipes also use warm-up: begin with a very small learning rate and gradually increase it, limiting early update sizes while activations, gradients, and optimiser state may not yet be well behaved.
Imagine a long narrow valley:
Plain gradient descent can zig-zag across the steep direction while making slow progress along the shallow direction.
Momentum averages gradients over successive steps: persistent directions reinforce, while directions that keep reversing tend to cancel.
Let \(\vect{g}_t\) be the current minibatch gradient.
Momentum maintains an exponentially smoothed gradient:
\[ \underbrace{\vect{m}_{t+1}}_{\text{smoothed gradient}} = \underbrace{\beta\vect{m}_t}_{\text{keep some history}} + \underbrace{(1-\beta)\vect{g}_t}_{\text{add current evidence}}. \]
Then update
\[ \params_{t+1} = \params_t - \alpha\vect{m}_{t+1}. \]
Under a constant gradient, this exponential moving average settles at \(\vect{m}=\vect{g}\).
Averaging gradients over time damps oscillation across steep directions while preserving progress along persistent directions.
Repeatedly aligned gradients reinforce one another.
Repeatedly reversing gradients cancel one another.
Momentum tends to:
\(\beta\) controls memory:
Ordinary momentum measures the gradient where we are now and then combines it with momentum.
Nesterov momentum first moves to the position predicted by the momentum term.
\[ \text{look-ahead point} = \params_t - \alpha\beta\vect{m}_t. \]
Then measure the gradient at that predicted position.
The gradient at the look-ahead point corrects the momentum-predicted trajectory.
Nesterov momentum first follows the momentum direction, then evaluates the gradient at the predicted look-ahead point.
A fixed global learning rate assumes the same step scale is sensible in every coordinate.
Adaptive methods instead scale each coordinate using its own gradient history.
AdaGrad introduced this general idea; RMSProp and AdaDelta modified how squared-gradient history is accumulated. Adam combines this adaptive scaling with momentum-like averaging of the gradient itself.
Suppose the gradient is \(\vect{g}_t\).
As a thought experiment, divide each coordinate by its own magnitude:
\[ \frac{\vect{g}_t}{\sqrt{\vect{g}_t\odot\vect{g}_t}+\epsilon}, \]
then for \(|g_{t,j}|\gg\epsilon\), each coordinate is approximately \(\operatorname{sign}(g_{t,j})\).
This is coordinatewise scaling, not vector normalisation by \(\lVert\vect{g}_t\rVert\).
This prevents steep coordinates from automatically receiving the largest parameter changes, but using only the sign discards too much information and can oscillate around the minimum.
Adam maintains two moving averages.
First moment — a smoothed gradient:
\[ \vect{m}_{t+1} = \beta_1\vect{m}_t + (1-\beta_1)\vect{g}_t. \]
Second raw moment — a smoothed squared gradient:
\[ \vect{v}_{t+1} = \beta_2\vect{v}_t + (1-\beta_2) (\vect{g}_t\odot\vect{g}_t). \]
\(\vect{m}_t\) tracks the recent signed gradient; \(\vect{v}_t\) tracks typical squared magnitude per coordinate.
The terminology “first moment” and “second raw moment” is mean-like language. In particular, \(\vect{v}_t\) is not the variance of the gradient.
In PyTorch, Adam(betas=...) takes the pair \((\beta_1, \beta_2)\).
Adam combines a moving average of gradients with a moving average of squared gradients to produce an adaptively scaled update.
At the start of training,
\[ \vect{m}_0=\vect{0}, \qquad \vect{v}_0=\vect{0}. \]
Because both moving averages start at zero, their early values are biased toward zero.
Adam compensates with
\[ \tilde{\vect{m}}_{t+1} = \frac{\vect{m}_{t+1}}{1-\beta_1^{t+1}}, \qquad \tilde{\vect{v}}_{t+1} = \frac{\vect{v}_{t+1}}{1-\beta_2^{t+1}}. \]
As \(t\) grows, \(\beta_1^{t+1}\) and \(\beta_2^{t+1}\) shrink and the bias-correction factors approach one.
The parameter update is
\[ \params_{t+1} = \params_t - \underbrace{\alpha}_{\text{global scale}} \frac{ \underbrace{\tilde{\vect{m}}_{t+1}}_{\text{smoothed gradient}} }{ \underbrace{\sqrt{\tilde{\vect{v}}_{t+1}}}_{\text{coordinatewise scale}} + \underbrace{\epsilon}_{\text{numerical stability}} }. \]
Adam combines momentum-like gradient smoothing with per-coordinate adaptive scaling. The square root and division are applied elementwise.
A single global step size is difficult to tune when gradient magnitudes differ substantially across coordinates.
Adam reduces the effective step in coordinates with persistently large squared gradients and allows relatively larger movement where gradients have been smaller.
Adaptive scaling reduces sensitivity to differences in gradient scale across coordinates, but the global learning rate still matters.
The relative behaviour of adaptive methods and SGD depends on the objective and training setup.
In practice:
Neither optimiser dominates across architectures, datasets, and training recipes.
Model parameters are learned from data:
\[ \params. \]
Training hyperparameters control the learning procedure, for example:
Parameters are optimised inside one training run.
Hyperparameters are compared across training runs.
The optimisers differ in the information used to construct each update.
Gradient descent
Momentum
Adam
Newton-style methods
Training repeatedly follows this sequence:
\[ \text{parameters} \rightarrow \text{predictions} \rightarrow \text{loss} \rightarrow \text{gradient} \rightarrow \text{parameter update}. \]
Once the gradient is known, the optimisation methods in this lecture perform the final step:
\[ \nabla_{\params}\loss \quad\longrightarrow\quad \Delta\params. \]
The optimiser decides how to turn that gradient into a parameter update.
For a deep network, the remaining problem is to compute that gradient efficiently.
A deep network does not map parameters to loss in one step.
A simplified computation looks like
\[ \vect{x} \rightarrow \hidden_1 \rightarrow \hidden_2 \rightarrow \cdots \rightarrow \hat{\vect{y}} \rightarrow \loss. \]
Each intermediate quantity depends on the one before it, so changing an early parameter can affect the loss through many successive operations.
The chain rule tells us how to compose these local sensitivities.
The forward computation produces activations and a loss; the reverse computation asks how that loss is sensitive to earlier quantities and parameters.
Suppose the network is written as a composition
\[ \hidden_1=\vmodel[\params_1]{\vect{x}}, \qquad \hidden_2=\vmodel[\params_2]{\hidden_1}, \qquad \exloss_i=\exloss(\hidden_2). \]
The optimiser ultimately needs sensitivities such as
\[ \nabla_{\params_1}\exloss_i, \qquad \nabla_{\params_2}\exloss_i. \]
Computing those gradients independently, parameter by parameter, would be impractical in a deep network.
Backpropagation is the efficient reverse computation used to obtain them from the computation graph.
Backpropagation repeatedly combines derivatives from successive layers.
For an early layer, the gradient contains products of many local derivative terms.
If those terms are repeatedly small, the gradient can shrink as it moves backward.
If they are repeatedly large, the gradient can grow rapidly.
The initial parameter values therefore affect not only the initial predictions, but also the scale of the gradients available for learning.
Two issues become central in deep networks:
Backpropagation addresses the first; the choice of initial parameter distribution addresses the second.
Primary source for this lecture: