Skip to content

Partial Differentiation and Gradients

Everything on the previous page had one input. Real functions do not: a loss depends on every weight in the network at once. The generalisation is almost anticlimactic — vary one variable and hold the others fixed — and the only genuinely new thing is what you do with the answers once you have nn of them.

You collect them into a vector. The book collects them into a row vector, and that choice is not cosmetic: it is what lets §5.3 write the multivariate chain rule as a plain matrix product with no transposes to remember.

  • Definition 5.5: the partial derivative, which is Definition 5.2 with the other variables frozen.
  • Equation 5.40: the gradient as a 1×n1\times n row vector, and the book’s two reasons for the row convention.
  • Three claims about the gradient, each measured: it is the direction of steepest ascent, its norm is that steepest rate, and it is perpendicular to the level set.
  • §5.2.1: the product, sum and chain rules survive — with one warning, since matrix multiplication does not commute.
  • §5.2.2 and Equation 5.53: the multivariate chain rule, as a matrix product.
  • Where the difference quotient’s round-off floor lands in several dimensions, and why gradient checking needs a relative tolerance.

You are on a hillside and cannot see. You can feel the slope under your feet in the two compass directions — north–south and east–west — and that is all.

Those two numbers are the partial derivatives. And they are enough: the direction of steepest ascent is not “whichever of the two is bigger”, it is the combination of them, and the vector (Nf,Ef)(\partial_N f, \partial_E f) points exactly that way. Its length is how steep the hill is in that best direction. Walk perpendicular to it and your altitude does not change — you are following a contour.

None of that is obvious from the definition, and all of it is checkable.

diagram Diagram mermaid

Every partial derivative is an ordinary scalar derivative — the book’s own margin note says so — so §5.1’s rules apply unchanged to each one. Nothing new is needed to compute them.

That second reason is the practical one. With the row convention, composing f:RnRf:\mathbb{R}^n\to\mathbb{R} with g:RmRng:\mathbb{R}^m\to\mathbb{R}^n gives

d(fg)dy1×m=fx1×nxyn×m\underbrace{\frac{\mathrm{d}(f\circ g)}{\mathrm{d}\mathbf{y}}}_{1\times m} = \underbrace{\frac{\partial f}{\partial\mathbf{x}}}_{1\times n}\underbrace{\frac{\partial\mathbf{x}}{\partial\mathbf{y}}}_{n\times m}

The shapes chain left to right in the order the functions are applied, and there is nothing to transpose. With the column convention the same composition is Jggradf\mathbf{J}_g^\top\,\operatorname{grad} f — one transpose, and the factors in the opposite order to the functions. Neither is wrong; one of them costs a transpose per layer, and a network is a composition of many layers.

Nothing in Definition 5.5 mentions steepness or perpendicularity. Both follow from one identity. For a unit direction d\mathbf{d}, the directional derivative is

Ddf(x)=f(x)d=fdcosθ=fcosθD_\mathbf{d}f(\mathbf{x}) = \nabla f(\mathbf{x})\,\mathbf{d} = \lVert\nabla f\rVert\,\lVert\mathbf{d}\rVert\cos\theta = \lVert\nabla f\rVert\cos\theta

using §3.4’s definition of the angle. That single line settles all three:

claimwhy
the gradient points in the direction of steepest ascentcosθ\cos\theta is largest at θ=0\theta = 0, i.e. d\mathbf{d} parallel to f\nabla f
the steepest rate equals f\lVert\nabla f\rVertat θ=0\theta = 0, cosθ=1\cos\theta = 1
the gradient is perpendicular to the level setalong a contour ff does not change, so Ddf=0D_\mathbf{d}f = 0, so cosθ=0\cos\theta = 0

The measurements below check each one, because a chain of three “so”s is exactly where a sign error hides.

§5.2.1 states that the sum, product and chain rules all still apply. Then it adds the warning that matters:

However, when we compute derivatives with respect to vectors xRn\mathbf{x}\in\mathbb{R}^n we need to pay attention: our gradients now involve vectors and matrices, and matrix multiplication is not commutative, i.e. the order matters.

For f:R2Rf:\mathbb{R}^2\to\mathbb{R} with x1(t)x_1(t) and x2(t)x_2(t):

dfdt=[fx1fx2][x1(t)tx2(t)t]=fx1x1t+fx2x2t(5.49)\frac{\mathrm{d}f}{\mathrm{d}t} = \begin{bmatrix}\dfrac{\partial f}{\partial x_1} & \dfrac{\partial f}{\partial x_2}\end{bmatrix} \begin{bmatrix}\dfrac{\partial x_1(t)}{\partial t}\\[6pt] \dfrac{\partial x_2(t)}{\partial t}\end{bmatrix} = \frac{\partial f}{\partial x_1}\frac{\partial x_1}{\partial t} + \frac{\partial f}{\partial x_2}\frac{\partial x_2}{\partial t} \tag{5.49}

and if x1(s,t)x_1(s,t), x2(s,t)x_2(s,t) depend on two variables, the same rule gives Equations 5.51 and 5.52, which assemble into

dfd(s,t)=fxx(s,t)=[fx1fx2]1×2[x1sx1tx2sx2t]2×2(5.53)\frac{\mathrm{d}f}{\mathrm{d}(s,t)} = \frac{\partial f}{\partial\mathbf{x}}\frac{\partial\mathbf{x}}{\partial(s,t)} = \underbrace{\begin{bmatrix}\dfrac{\partial f}{\partial x_1} & \dfrac{\partial f}{\partial x_2}\end{bmatrix}}_{1\times2} \underbrace{\begin{bmatrix}\dfrac{\partial x_1}{\partial s} & \dfrac{\partial x_1}{\partial t}\\[6pt] \dfrac{\partial x_2}{\partial s} & \dfrac{\partial x_2}{\partial t}\end{bmatrix}}_{2\times2} \tag{5.53}

Example 5.6 — one inner function, two partials

Section titled “Example 5.6 — one inner function, two partials”

f(x,y)=(x+2y3)2f(x, y) = (x + 2y^3)^2. Both partials come from one application of the chain rule to the same inner function u=x+2y3u = x + 2y^3:

f(x,y)x=2(x+2y3)x(x+2y3)=2(x+2y3)(5.41)\frac{\partial f(x,y)}{\partial x} = 2(x + 2y^3)\frac{\partial}{\partial x}(x + 2y^3) = 2(x + 2y^3) \tag{5.41} f(x,y)y=2(x+2y3)y(x+2y3)=12(x+2y3)y2(5.42)\frac{\partial f(x,y)}{\partial y} = 2(x + 2y^3)\frac{\partial}{\partial y}(x + 2y^3) = 12(x + 2y^3)y^2 \tag{5.42}

At (0.8,0.6)(0.8, 0.6): u=0.8+2(0.216)=1.232u = 0.8 + 2(0.216) = 1.232, so

f=[2(1.232)12(1.232)(0.36)]=[2.4645.32224]\nabla f = \begin{bmatrix}2(1.232) & 12(1.232)(0.36)\end{bmatrix} = \begin{bmatrix}2.464 & 5.32224\end{bmatrix}

Both verified against a central-difference Jacobian to 101210^{-12}.

f(x1,x2)=x12x2+x1x23Rf(x_1, x_2) = x_1^2x_2 + x_1x_2^3 \in \mathbb{R}. Differentiating with respect to each variable in turn, treating the other as a constant:

f(x1,x2)x1=2x1x2+x23(5.43)\frac{\partial f(x_1,x_2)}{\partial x_1} = 2x_1x_2 + x_2^3 \tag{5.43} f(x1,x2)x2=x12+3x1x22(5.44)\frac{\partial f(x_1,x_2)}{\partial x_2} = x_1^2 + 3x_1x_2^2 \tag{5.44} dfdx=[2x1x2+x23x12+3x1x22]R1×2(5.45)\frac{\mathrm{d}f}{\mathrm{d}\mathbf{x}} = \begin{bmatrix}2x_1x_2 + x_2^3 & x_1^2 + 3x_1x_2^2\end{bmatrix} \in \mathbb{R}^{1\times2} \tag{5.45}

At x=(1,1)\mathbf{x} = (1,1) this is [34]\begin{bmatrix}3 & 4\end{bmatrix}, so f=5\lVert\nabla f\rVert = 5 — a 334455 triangle, which makes the checks below easy to read.

Notice that neither partial derivative is a function of its own variable alone. f/x1\partial f/\partial x_1 depends on x2x_2. That is why the gradient has to be a vector rather than a pair of independent slopes, and it is the whole reason optimising one coordinate at a time does not work.

Example 5.8 — the chain rule along a curve

Section titled “Example 5.8 — the chain rule along a curve”

f(x1,x2)=x12+2x2f(x_1,x_2) = x_1^2 + 2x_2 with x1=sintx_1 = \sin t, x2=costx_2 = \cos t:

dfdt=fx1x1t+fx2x2t(5.50a)\frac{\mathrm{d}f}{\mathrm{d}t} = \frac{\partial f}{\partial x_1}\frac{\partial x_1}{\partial t} + \frac{\partial f}{\partial x_2}\frac{\partial x_2}{\partial t} \tag{5.50a} =2sintsintt+2costt(5.50b)= 2\sin t\,\frac{\partial \sin t}{\partial t} + 2\,\frac{\partial\cos t}{\partial t} \tag{5.50b} =2sintcost2sint=2sint(cost1)(5.50c)= 2\sin t\cos t - 2\sin t = 2\sin t\,(\cos t - 1) \tag{5.50c}

The factored form is worth keeping: df/dt=0\mathrm{d}f/\mathrm{d}t = 0 exactly when sint=0\sin t = 0 or cost=1\cos t = 1, so at every multiple of π\pi. And since cost10\cos t - 1 \leq 0 always, the sign of the derivative is the opposite of the sign of sint\sin t — the function decreases on (0,π)(0,\pi) and increases on (π,2π)(\pi, 2\pi).

gradient_claims.py
import numpy as np
 
# The book's Example 5.7.
f = lambda x, y: x ** 2 * y + x * y ** 3
fx = lambda x, y: 2 * x * y + y ** 3
fy = lambda x, y: x ** 2 + 3 * x * y ** 2
 
x0, y0 = 1.0, 1.0
grad = np.array([fx(x0, y0), fy(x0, y0)])            # Eq 5.45: a 1 x 2 row
gn = float(np.linalg.norm(grad))
 
print("Eq 5.45  grad f =", grad, " shape (1, 2) as a row")
print("         |grad f| =", gn)
print()
 
# CLAIM 1 and 2: sweep every direction and see which wins, and by how much.
th = np.linspace(0, 2 * np.pi, 100001)[:-1]
rate = grad[0] * np.cos(th) + grad[1] * np.sin(th)
i = int(np.argmax(rate))
print("claim 1: the steepest direction is the gradient's own direction")
print(f"         gradient angle {np.degrees(np.arctan2(grad[1], grad[0])):.6f} deg")
print(f"         sampled argmax {np.degrees(th[i]):.6f} deg")
print("claim 2: the steepest rate equals |grad f|")
print(f"         sampled max    {rate[i]:.9f}")
print(f"         |grad f|       {gn:.9f}")
print(f"         no direction beat it: {bool(np.all(rate <= gn + 1e-12))}")
print()
 
# CLAIM 3: perpendicular to the level set. The contour tangent is the gradient
# rotated by 90 degrees, so the inner product must vanish -- and stepping along
# it must leave f almost unchanged.
tangent = np.array([-grad[1], grad[0]]) / gn
print("claim 3: perpendicular to the level set")
print(f"         grad . tangent = {float(grad @ tangent):.1e}")
step = 1e-3
along_t = abs(f(x0 + step * tangent[0], y0 + step * tangent[1]) - f(x0, y0))
along_g = abs(f(x0 + step * grad[0] / gn, y0 + step * grad[1] / gn) - f(x0, y0))
print(f"         |df| stepping {step} along the tangent:  {along_t:.3e}")
print(f"         |df| stepping {step} along the gradient: {along_g:.3e}")
print(f"         ratio: {along_g / along_t:.0f}x")
output
Eq 5.45  grad f = [3. 4.]  shape (1, 2) as a row
         |grad f| = 5.0
 
claim 1: the steepest direction is the gradient's own direction
         gradient angle 53.130102 deg
         sampled argmax 53.128800 deg
claim 2: the steepest rate equals |grad f|
         sampled max    4.999999999
         |grad f|       5.000000000
         no direction beat it: True
 
claim 3: perpendicular to the level set
         grad . tangent = -4.4e-16
         |df| stepping 0.001 along the tangent:  6.803e-07
         |df| stepping 0.001 along the gradient: 5.005e-03
         ratio: 7357x
sketch Two partials, one gradient p5.js
Drag the point around the contour map. The two coloured bars are the partial derivatives — the slope along a horizontal and a vertical cut — and the red arrow is the vector they assemble into. Notice the arrow is never the longer of the two bars: it is their combination, and it always crosses the contour at a right angle.
sketch Every direction, and which one wins p5.js
The blue curve is the rate of change in each compass direction — a cosine, because the directional derivative is an inner product. Drag the direction marker and watch the readout. Its peak is at the gradient's own angle and its height is exactly the gradient's length; where it crosses zero you are on the contour.
fieldExample 5.7, built from Definition 5.5partial derivatives and the gradient, §5.2
-2-1012-2-1012x1x2(1, 1)
f(x) 2df/dx1 3df/dx2 4|grad f| 5shape 1 x 2
The function is the book's Example 5.7. Neither partial derivative is a function of its own variable alone, which is the whole reason the gradient has to be a vector rather than a number. Everything below is evaluated at x = (1, 1), where f = 2.00000.
1/7

Two limits, one row vector, then the three claims checked in turn. The frame to stop on is the difference-quotient table: the central difference is exact at h = 1 here, because the slice is quadratic, and then round-off makes smaller h worse.

fieldA Gaussian hill, where the forward difference gets luckypartial derivatives and the gradient, §5.2
-3-2-10123-3-2-10123x1x2(1, 0.7)
f(x) 0.474734df/dx1 -0.474734df/dx2 -0.332314|grad f| 0.579487shape 1 x 2
The function is an unnormalised Gaussian. Its gradient is the function itself times minus the position, so the gradient vanishes both at the peak and far away — the flat tail that makes gradient descent stall. Everything below is evaluated at x = (1, 0.7), where f = 0.474734.
1/7

The second derivative of exp(−r²/2) in x is (x²−1)f, which is exactly zero at x = 1 — one of the points evaluated here. The leading forward-difference error is (h/2)f'', so at that point the forward difference is accidentally second order, and the lab says so.

gradient_from_scratch.py
import numpy as np
 
def numeric_gradient(f, x, h=1e-6):
    """Definition 5.5, one coordinate at a time, centrally. Returns a 1 x n row."""
    x = np.asarray(x, dtype=float)
    g = np.zeros((1, x.size))
    for i in range(x.size):
        e = np.zeros_like(x)
        e[i] = h
        g[0, i] = (f(x + e) - f(x - e)) / (2 * h)
    return g
 
def analytic_gradient(x):
    """Example 5.7, Eq 5.45."""
    x1, x2 = x
    return np.array([[2 * x1 * x2 + x2 ** 3, x1 ** 2 + 3 * x1 * x2 ** 2]])
 
f = lambda v: v[0] ** 2 * v[1] + v[0] * v[1] ** 3
 
print(f"{'point':>16}  {'analytic':>26}  {'numeric':>26}  {'rel err':>9}")
for pt in ([1.0, 1.0], [0.5, -1.5], [2.0, 0.3], [-1.0, 2.0], [0.0, 0.0]):
    a = analytic_gradient(pt)
    n = numeric_gradient(f, pt)
    scale = max(float(np.abs(a).max()), 1e-12)
    print(f"{str(pt):>16}  {str(np.round(a[0], 6)):>26}  {str(np.round(n[0], 6)):>26}"
          f"  {float(np.abs(a - n).max()) / scale:>9.1e}")
 
print()
print("shape of the gradient:", analytic_gradient([1.0, 1.0]).shape, "-- a ROW, Eq 5.40")
print()
 
# The multivariate chain rule, Eq 5.53, as a matrix product.
# f(x1, x2) = x1^2 + 2 x2 with x1 = sin t, x2 = cos t  (Example 5.8)
t = 0.7
df_dx = np.array([[2 * np.sin(t), 2.0]])                     # 1 x 2
dx_dt = np.array([[np.cos(t)], [-np.sin(t)]])                # 2 x 1
chain = float((df_dx @ dx_dt).item())
closed = 2 * np.sin(t) * (np.cos(t) - 1)                     # Eq 5.50c
h = 1e-6
g = lambda tt: np.sin(tt) ** 2 + 2 * np.cos(tt)
numeric = (g(t + h) - g(t - h)) / (2 * h)
print("Example 5.8 at t = 0.7")
print(f"  (df/dx)(dx/dt), a (1x2)(2x1) product : {chain:.12f}")
print(f"  2 sin t (cos t - 1), Eq 5.50c        : {closed:.12f}")
print(f"  central difference in t              : {numeric:.12f}")
print(f"  largest gap                          : {max(abs(chain-closed), abs(chain-numeric)):.1e}")
output
           point                    analytic                     numeric    rel err
      [1.0, 1.0]                     [3. 4.]                     [3. 4.]    3.4e-11
     [0.5, -1.5]             [-4.875  3.625]             [-4.875  3.625]    1.0e-10
      [2.0, 0.3]               [1.227 4.54 ]               [1.227 4.54 ]    1.3e-11
     [-1.0, 2.0]                 [  4. -11.]                 [  4. -11.]    5.9e-11
      [0.0, 0.0]                     [0. 0.]                     [0. 0.]    0.0e+00
 
shape of the gradient: (1, 2) -- a ROW, Eq 5.40
 
Example 5.8 at t = 0.7
  (df/dx)(dx/dt), a (1x2)(2x1) product : -0.302985644487
  2 sin t (cos t - 1), Eq 5.50c        : -0.302985644487
  central difference in t              : -0.302985644463
  largest gap                          : 2.4e-11

Three things worth noting.

The shape is (1, 2) — a row. Writing np.array([fx, fy]) gives shape (2,), which NumPy will happily broadcast in ways that hide a transpose error until the dimensions stop being equal.

The [0.0, 0.0] row has relative error exactly 0.0e+00, because both partials are exactly zero there — that is a critical point of Example 5.7, and it is a saddle, not an optimum.

And the chain rule row is the point of Equation 5.53: the answer comes out of a (1×2)(2×1)(1\times2)(2\times1) matrix product, agrees with the hand-factored closed form to every printed digit, and agrees with a direct numerical derivative in tt to 2.4×10112.4\times10^{-11}. Three routes, one answer.

One detail in that code is a real trap. float(df_dx @ dx_dt) raises a TypeError on recent NumPy, because the product is a (1,1)(1,1) array rather than a scalar — the shapes chained correctly and that is exactly why. Use .item().

figure The gradient field of Example 5.7 matplotlib
A contour map of the Example 5.7 surface with a grid of normalised gradient arrows coloured by magnitude, each crossing its contour at a right angle, and the point (1,1) marked. A contour map of the Example 5.7 surface with a grid of normalised gradient arrows coloured by magnitude, each crossing its contour at a right angle, and the point (1,1) marked.
Contours in grey, gradient arrows normalised so direction is readable and coloured by magnitude. Across all 225 grid points the gradient's normalised inner product with the contour tangent is 0.0e+00 — exactly, not approximately. The largest gradient magnitude on the window is 35.954.
figure Both claims, in one sweep matplotlib
Left, a polar rosette of the directional derivative forming a circle through the origin with the gradient arrow along its widest axis. Right, the same data as a cosine curve against angle, with horizontal lines at plus and minus the gradient norm. Left, a polar rosette of the directional derivative forming a circle through the origin with the gradient arrow along its widest axis. Right, the same data as a cosine curve against angle, with horizontal lines at plus and minus the gradient norm.
At (1,1) the gradient is [3, 4] with norm exactly 5, at an angle of 53.1301 degrees. The sampled maximum over a half-degree grid is 4.999987 at 53.0000 degrees — short of the true maximum by 1.29e-05, which is what a discrete grid can reach, not an error.
figure Equation 5.40's two reasons, written out matplotlib
Two columns of monospaced text comparing the row-vector and column-vector gradient conventions, each showing the shapes of the chain rule factors and counting the transposes required. Two columns of monospaced text comparing the row-vector and column-vector gradient conventions, each showing the shapes of the chain rule factors and counting the transposes required.
With gradients as rows, composing f with g is a (1 x n)(n x m) product: the shapes chain left to right in the order the functions apply, and no transposes appear. With gradients as columns the same composition needs one transpose, and the factors come in the opposite order to the functions.

From the gradient field. The reported inner product is 0.0e+00 at all 225 grid points, and it is worth being precise about why. The tangent is constructed as (2f,1f)(-\partial_2 f, \partial_1 f), so the inner product is 1f2f+2f1f-\partial_1 f\,\partial_2 f + \partial_2 f\,\partial_1 f — the same two products subtracted, which cancels bit-for-bit. Normalise the tangent first, as the exercises below do, and the same quantity comes back as 4.4×1016-4.4\times10^{-16} instead: the division introduces rounding that the unnormalised form never had.

So that zero is a check on the construction, not on the geometry. The geometric content is that this construction is the contour direction — which the picture shows by the arrows meeting the grey curves squarely, and which the exercises measure by stepping along it.

The step comparison is the geometric check. Moving 10310^{-3} along the contour tangent changes ff by 6.8×1076.8\times10^{-7}; moving the same distance along the gradient changes it by 5.0×1035.0\times10^{-3}7357 times more. Across four points the ratio runs from 27342734 to 73577357.

The colouring carries the other lesson. Where the contours bunch, the arrows are long; where the surface is flat they nearly vanish. Gradient magnitude is inversely related to contour spacing, which is why gradient descent naturally takes big steps on steep ground and small ones near an optimum — and why it stalls completely on a plateau.

From the sweep. The left panel’s shape is the whole argument. The directional derivative fd\nabla f\cdot\mathbf{d} is linear in d\mathbf{d}, so plotted at radius == rate it traces a circle through the origin. A circle through the origin has exactly one widest point, and that point is where d\mathbf{d} aligns with f\nabla f.

Two numbers close it:

value
gradient angle53.1301°53.1301°
sampled argmax over a 0.5°0.5° grid53.0000°53.0000°
f\lVert\nabla f\rVert5.0000005.000000
sampled maximum4.9999874.999987
shortfall1.29×1051.29\times10^{-5}

Exercise 2 repeats the sweep on a 100000100\,000-point grid: the shortfall falls to 1.29×1091.29\times10^{-9}, still not zero. Only evaluating at the exact gradient direction gives a gap of 0.0e+00.

The shortfall is geometry, not error: a grid can only get within half a step of the true angle, and 5(1cos0.13°)1.3×1055(1 - \cos 0.13°) \approx 1.3\times10^{-5}. This is the same lesson as Chapter 4’s Exercise 4.12 — sampling demonstrates the bound and never the attainment. Evaluating at the exact gradient direction gives 5.0000005.000000 on the nose.

The right panel adds one thing the rosette hides: the curve spends half its length below zero. Half of all directions go downhill. That is obvious once stated and it is the reason gradient descent steps along f-\nabla f rather than searching.

From the shapes figure. The transpose count is the argument. A three-layer network is a composition of six functions; with rows that is six matrix products in the order you wrote the layers, and with columns it is six products in reverse order with a transpose on each. Both compute the same numbers. Only one of them is easy to get right at 3 a.m.

objectshapewhat it answers
partial derivative f/xi\partial f/\partial x_iscalarhow fast ff changes if only xix_i moves
gradient f\nabla f1×n1\times nthe direction of fastest increase, and its rate
directional derivative DdfD_\mathbf{d}fscalarhow fast ff changes along one chosen d\mathbf{d}
Jacobian (§5.3)m×nm\times nthe same, for a function with mm outputs
Hessian (§5.7)n×nn\times nhow the gradient itself changes — curvature
level set / contouran (n1)(n-1)-dimensional surfacewhere ff does not change at all

The gradient and the contour are the same information twice: one is the direction of maximum change, the other the directions of none, and they are orthogonal complements of each other in the sense of §3.6.

pch.quizTag Check your understanding
  1. The book gives two reasons for making the gradient a row vector. What is the practical one?

    pch.quizShowAnswer

    B — The multivariate chain rule becomes a plain matrix product whose shapes chain left to right in the order the functions apply — with the column convention the same composition needs a transpose per layer and the factors come in reverse order — The book states this at Equation 5.53: writing the chain rule as a matrix multiplication only makes sense if the gradient is a row. Note that PyTorch does the opposite, storing gradients shaped like the parameter — so check which convention is in force before transposing.

  2. Why do 'steepest ascent', 'the rate is the norm' and 'perpendicular to the contour' all follow from one identity?

    pch.quizShowAnswer

    B — Because the directional derivative is grad f dotted with d, which equals the norm of grad f times cos theta — so the maximum is at theta = 0, its value is the norm, and it is zero at theta = 90 degrees — One inner product, three corollaries. It also explains the shape of the sweep: linear in d means the polar plot is a circle through the origin, and a circle through the origin has exactly one widest point.

  3. The sampled maximum of the directional derivative came out as 4.999987 against a true 5.000000. Is that an error?

    pch.quizShowAnswer

    B — No. The sweep is on a half-degree grid, so it can only get within half a step of the true angle, and 5 times (1 minus cos 0.13 degrees) is about 1.3e-05 — exactly the observed shortfall. Evaluating at the exact gradient direction gives 5.000000 — The same lesson as Exercise 4.12 in Chapter 4: sampling can demonstrate an upper bound convincingly and cannot demonstrate that the bound is attained. For attainment you evaluate at the claimed maximiser.

  4. In Example 5.7, why does optimising x1 and then x2 not reach the same place as following the gradient?

    pch.quizShowAnswer

    B — Because the partial derivative with respect to x1 is 2 x1 x2 + x2 cubed, which depends on x2 — so changing x2 changes the best x1. The partials are not independent slopes, which is why they have to be assembled into a vector — Coordinate descent is a legitimate algorithm with its own guarantees, but it is a different one. On a function with strong cross terms the gap in step count can be arbitrarily large.

  5. In the from-scratch table, the point [0.0, 0.0] gives a relative error of exactly 0.0e+00. Why, and what does it tell you about that point?

    pch.quizShowAnswer

    B — Both partials are exactly zero there, so both routes return zero and the difference is exactly zero. It is a critical point of Example 5.7 — and a saddle, not an optimum, which the first derivative cannot tell you — A gradient check that only ever tested critical points would pass trivially. Distinguishing a saddle from a minimum needs the Hessian, which is §5.7 — and that is exactly why §5.7 exists.

Exercise 1 – Example 5.7, from the definition

Section titled “Exercise 1 – Example 5.7, from the definition”

Exercise 3 – Perpendicular to the level set

Section titled “Exercise 3 – Perpendicular to the level set”

Exercise 4 – Example 5.8 and the chain rule as a matrix product

Section titled “Exercise 4 – Example 5.8 and the chain rule as a matrix product”

Exercise 5 – Where the numeric gradient stops being trustworthy

Section titled “Exercise 5 – Where the numeric gradient stops being trustworthy”
  • A partial derivative is Definition 5.2 with the other variables frozen, so every rule from §5.1 applies to it unchanged.
  • The gradient is the row vector of all n partials, shape 1 by n — Equation 5.40 — and the book gives two reasons for the row: it generalises to vector-valued functions without a shape change, and it makes the chain rule a plain matrix product.
  • One identity gives three claims. The directional derivative is grad f dotted with d, which is the norm of grad f times cos theta; so the steepest direction is the gradient’s, the steepest rate is its norm, and the contour directions give zero.
  • Measured on Example 5.7 at (1,1): grad = [3, 4], norm exactly 5, angle 53.1301 degrees; a half-degree sweep finds 4.999987 at 53.0000 degrees, short by 1.29e-05 — grid geometry, not error.
  • The gradient is perpendicular to the level set, and the effect is large: the same step along the gradient moves f between 2734 and 7357 times more than along the contour.
  • Half of all directions go downhill, which is why descent steps along minus the gradient instead of searching.
  • Equation 5.53’s chain rule is a (1 x n)(n x m) matrix product — shapes chaining left to right in the order the functions apply, no transposes.
  • A NumPy 1-D array is not a row vector. Shape (2,) broadcasts either way, so a transposed Jacobian produces a plausible number rather than an error.
  • Gradient checking needs a relative tolerance and a central difference. At h = 1e-6 a correct gradient lands around 1e-11 relative; at h = 1e-15 the check is 4.7e+10 times worse than at its best.
  • A vanishing gradient is not a minimum. The saddle x1 squared minus x2 squared has zero gradient at the origin; telling the cases apart needs the Hessian, which is §5.7.

Next: Gradients of Vector-Valued Functions — the same row, stacked m times into a Jacobian.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading