Index Ai Academy
Phase 2 · Math, when you need it · Calculus

Curvature and step sizes

Second derivatives, minimum or maximum, why curvature caps the learning rate, convex losses and Newton's method.

About 25 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

Gradient descent only looks at the slope, but how fast the slope changes, the curvature, decides whether a learning rate converges, crawls or blows up. Curvature is why a loss that's steep in one direction and gentle in another trains slowly, why linear and logistic regression always find the same answer, and why optimizers like Adam exist. This lesson builds the second derivative from the first, uses it to classify flat points, and shows exactly where the "too big" learning rate comes from.

What you'll be able to do

  • Compute a second derivative numerically and read it as how a curve bends
  • Use the second derivative to tell a minimum from a maximum
  • Explain why curvature sets the largest learning rate that still converges
  • Recognize convex losses, and say why they're easier to optimize than a neural network's

What it covers

  1. The slope of the slope
  2. Bending up, bending down
  3. Valley or hilltop?
  4. Curvature sets the largest learning rate
  5. Ravines: two curvatures at once
  6. Convex: one valley only
  7. Newton's method: divide by the curvature
  8. Curvature in AI
  9. An optimizer study: why does this model train so slowly?
Quick check
Before any explaining, a warm-up. A loss at w = 0, 1, 2 and 3 is 9, 4, 1 and 0, so each step right lowers it by 5, then 3, then 1. What is happening to the slope as you move right?
  1. It's getting steeper
  2. It changes from uphill to downhill
  3. It's getting less steep
  4. It stays the same
Show the answer

It's getting less steep

The drops shrink, 5, 3, 1: the curve is still going down, but less steeply each time. How fast the slope changes is the curvature, and it's what this lesson is about.

The slope of the slope

The derivative tells you the slope of a curve at each point. The second derivative asks the same question about the slope itself: how fast is the slope changing? For f(x) = x², the slope is 2x, and the slope of 2x is 2, everywhere. So x² has a second derivative of 2: its slope rises by 2 for every step of 1 to the right.

You can estimate it the same way you estimated slopes, from nearby values. Compare the point with its neighbors on both sides:

second derivative ≈ (f(x + h) − 2 × f(x) + f(x − h)) ÷ h²

def f(x):
    return x ** 2

def second_derivative(f, x, h=1e-3):
    return (f(x + h) - 2 * f(x) + f(x - h)) / h ** 2

print(round(second_derivative(f, 3), 3), round(second_derivative(f, -10), 3))
It runs right here in your browser, with nothing to install.

Both print 2: a parabola bends by the same amount everywhere. The formula is the change in slope between the right-hand side and the left-hand side, divided by the step once more.

Quick check
The second derivative of x³ is 6x. What does this print?
def f(x):
    return x ** 3

h = 0.01
print(round((f(2 + h) - 2 * f(2) + f(2 - h)) / h ** 2, 1))
  1. 8.0
  2. 12.0
  3. 0.0
  4. 6.0
Show the answer

12.0

At x = 2 the second derivative of x³ is 6 × 2 = 12, and the neighbor formula estimates it almost exactly.

Bending up, bending down

The sign of the second derivative tells you which way a curve bends:

  • positive: the slope is rising, so the curve bends upward, like a bowl;
  • negative: the slope is falling, so it bends downward, like a hilltop;
  • zero: locally it's straight.

That settles a question the first derivative can't. Where the slope is zero the curve is flat, but flat could be the bottom of a valley, the top of a hill, or a ledge. If the curve bends up there, it's a minimum; if it bends down, a maximum. If the second derivative is zero too, you can't tell from it alone: x³ is flat at 0 but is neither.

def f(x):
    return x ** 3 - 3 * x       # flat at x = -1 and x = 1

def second_derivative(f, x, h=1e-3):
    return (f(x + h) - 2 * f(x) + f(x - h)) / h ** 2

for x in [-1, 1]:
    print(x, round(second_derivative(f, x), 2))
Quick check
In the code above, f(x) = x³ − 3x is flat at x = 1, and its second derivative there is 6. What kind of point is x = 1?
  1. A maximum
  2. You can't tell
  3. A minimum
  4. A flat ledge, neither
Show the answer

A minimum

Flat and bending upward is the bottom of a valley: a minimum. At x = −1 the second derivative is −6, so that flat point is a maximum.

Drill

Valley or hilltop?

Write classify(f, x) that returns "minimum" if the second derivative at x is above 0.01, "maximum" if it's below −0.01, and "unclear" otherwise. second_derivative is already written.

def second_derivative(f, x, h=1e-3):
    return (f(x + h) - 2 * f(x) + f(x - h)) / h ** 2

def classify(f, x):
    return "unclear"

print(classify(lambda x: x ** 3 - 3 * x, 1), classify(lambda x: x ** 3 - 3 * x, -1))
In the lesson, your code is checked as you write it.
Quick check
A reminder from gradient descent. The loss is (w − 2)², with slope 2 × (w − 2). What does one step print?
w = 0.0
learning_rate = 0.25
w = w - learning_rate * 2 * (w - 2)
print(w)
  1. 0.5
  2. 1.0
  3. 2.0
  4. -1.0
Show the answer

1.0

The slope at 0 is 2 × (0 − 2) = −4. The step is 0 − 0.25 × (−4) = 1.0: against the slope, toward the minimum at 2.

Curvature sets the largest learning rate

Picture the simplest loss with curvature c: L(w) = c ÷ 2 × w², a bowl with its bottom at 0. Its slope is c × w, so one gradient descent step gives

w − η × c × w = (1 − η × c) × w

where η is the learning rate. Every step multiplies the distance from the bottom by (1 − η × c).

Quick check
Curvature 2 and learning rate 0.25, so η × c = 0.5. What share of the distance to the bottom is left after one step?
  1. None: it lands on the bottom
  2. A quarter
  3. All of it, plus half again
  4. Half
Show the answer

Half

Each step multiplies the distance by 1 − η × c = 1 − 0.5 = 0.5, so half is left, then a quarter, then an eighth.

That factor, 1 − η × c, tells the whole story:

  • if η × c is below 1, the factor is between 0 and 1: w shrinks steadily toward the bottom;
  • if η × c is between 1 and 2, the factor is between −1 and 0: w overshoots, flips sides, but still shrinks;
  • if η × c is above 2, the factor is below −1: every step overshoots by more, and training blows up.

So the largest learning rate that converges is 2 ÷ c. A sharply curved loss needs small steps; a gently curved one can take big ones. This is where the "too big a learning rate diverges" rule from gradient descent comes from.

Try it now

The shrink factor is how much of the distance to the bottom survives each step. Below 1, training converges; above 1, it blows up.

Quick check
Curvature 4, learning rate 0.6, so η × c = 2.4. What does this print after two steps?
w = 1.0
for step in range(2):
    w = w - 0.6 * 4 * w
print(round(w, 2))
  1. 1.96
  2. 0.0
  3. -1.4
  4. 0.16
Show the answer

1.96

Each step multiplies w by 1 − 2.4 = −1.4. After one step w is −1.4, after two it's 1.96: further from the bottom than where it started.

Ravines: two curvatures at once

Real losses have many weights, and the curvature can be very different in different directions. Picture a long, narrow valley: steep side walls, a gently sloping floor. The learning rate must stay under 2 ÷ c for the steepest direction, or the walls send training bouncing out. But a rate that small barely moves along the gentle floor.

Take L(a, b) = 0.5 × (10 × a² + b²): curvature 10 along a, curvature 1 along b. The largest safe rate is 2 ÷ 10 = 0.2. At 0.1, a reaches the bottom in one step, but b only shrinks by 10% per step:

a, b = 1.0, 1.0
for step in range(5):
    a, b = a - 0.1 * 10 * a, b - 0.1 * b
    print(step + 1, round(a, 3), round(b, 3))

This is the most common reason training is slow, and it's what momentum and Adam were invented for. Momentum builds up speed along the consistent floor direction while the bouncing across the walls cancels out. Adam scales each weight's step by how large its recent gradients have been, a cheap stand-in for knowing each direction's curvature.

Quick check
Same ravine, same learning rate. What would raise b's progress per step without breaking a?
  1. Nothing with one rate: above 0.2, a blows up
  2. Raising the learning rate to 0.3 for every weight
  3. Lowering the learning rate to 0.05 to be safe
  4. Starting b closer to the bottom of the valley
Show the answer

Nothing with one rate: above 0.2, a blows up

With a single learning rate, a's curvature of 10 caps it below 0.2, and at that cap b still only shrinks by about 20% per step. Fixing that needs a different step size per direction, which is what Adam and momentum provide.

Convex: one valley only

A function is convex if a straight line between any two points on its curve never dips below the curve. In one dimension that's the same as the second derivative never being negative: the curve bends up, or is straight, everywhere. Convex losses have a wonderful property: any minimum you find is the lowest one there is. There are no false valleys to get stuck in, so every starting point leads to the same answer.

The losses of linear regression (mean squared error) and logistic regression are convex in their weights. That's why those models train reliably, and why two people fitting the same data get the same coefficients. A neural network's loss is not convex: it has many valleys, ridges and flat saddle points, places where the curve bends up in some directions and down in others. Different random starts end in different places. In practice, very large networks mostly reach valleys that are equally good, but there's no guarantee.

def chord_above(f, x1, x2):
    middle = (x1 + x2) / 2
    return (f(x1) + f(x2)) / 2 >= f(middle)

print(chord_above(lambda x: x ** 2, -3, 1), chord_above(lambda x: x ** 3, -3, 1))

Newton's method: divide by the curvature

If you know the curvature, you can choose the step size for each point instead of guessing a learning rate. Newton's method steps by the slope divided by the curvature:

w ← w − slope ÷ curvature

In a perfect bowl this lands exactly on the bottom in one step. On other curves it converges remarkably fast. Here it is on f(w) = eʷ − 2w, whose slope is eʷ − 2 and curvature eʷ; the minimum is at w = log 2 ≈ 0.6931:

import math
w = 0.0
for step in range(4):
    w = w - (math.exp(w) - 2) / math.exp(w)
    print(step + 1, round(w, 4))

So why doesn't every model train this way? With many weights, curvature becomes a table, the Hessian, with one entry for every pair of weights. A model with a billion weights would need a billion billion entries. Deep learning uses first-order methods, slope only, with tricks like momentum and Adam to make up for the missing curvature.

Quick check
Why don't large neural networks use Newton's method directly?
  1. A neural network's loss has no curvature
  2. Newton's method always overshoots the minimum
  3. The curvature table has an entry for every pair of weights
  4. Newton's method works only for one weight at a time
Show the answer

The curvature table has an entry for every pair of weights

For n weights the Hessian has n × n entries. With a billion weights that's far too many to compute or store, so training uses the slope plus cheap substitutes for curvature.

Curvature in AI

You'll meet curvature in quieter forms too. Learning-rate warmup starts training with tiny steps because the curvature early in training can be sharp. Researchers compare sharp and flat minima: a model sitting in a wide, gently curved valley tends to cope better with small changes to its inputs than one in a narrow, steep one. And when a training loss suddenly spikes, a step that was too large for the local curvature is often the cause, which is why a lower learning rate is the first thing to try.

Portfolio brief

An optimizer study: why does this model train so slowly?

A colleague fitted a price model by gradient descent and gave up: "it takes forever, and if I raise the learning rate it explodes." The model is a simple one, price ≈ w_size × size + w_rooms × rooms + b, fitted to 200 house sales by minimizing the mean squared error. Your job is a short study that explains, with curvature, exactly what is going on, and shows the fix working.

The starter builds three lists: sizes (floor area in square meters), rooms and prices (in thousands). Write everything from scratch in plain Python, so every number in your study is one you computed.

The report is the deliverable: a few tables and a short explanation your colleague could read once and act on.

It must

  • Write the loss and its three slopes, and check each slope against a numerical estimate at two different points
  • Estimate the curvature along each weight with the second-difference formula, and predict the largest safe learning rate from it
  • Sweep learning rates around your prediction (500 steps each), and report the final loss or "diverged" for each in a table
  • At the best safe rate, show how far each weight still is from the best fit after 500 steps, and explain which weights crawl and why
  • Standardize both features (subtract the mean, divide by the standard deviation), measure the curvatures again, and rerun the sweep
  • If an observed limit differs from your prediction, find out why: measure the curvature along a direction that moves two weights at once, such as (1, 1, 0) ÷ √2
  • Write at most 200 words for your colleague: what was wrong, the evidence, the fix, and the learning rate to use

Go further

  • Add momentum (keep a running velocity, v = 0.9 × v + slope) and compare the steps needed to reach a loss within 1% of the best
  • Try a per-weight step: divide each slope by that weight's own curvature, a simple cousin of Newton's method, on the unscaled data
  • Plot the loss curves for three learning rates, before and after scaling, with a log scale on the loss axis
import random


def _simulate(seed=5):
    """Build the house data: 200 sales with floor area, rooms and price."""
    rng = random.Random(seed)
    rows = []
    for _ in range(200):
        size = rng.uniform(50, 250)                                   # floor area in square meters
        rooms = min(6, max(1, round(size / 45 + rng.gauss(0, 0.7))))
        price = 3 * size + 15 * rooms + 50 + rng.gauss(0, 20)         # price in thousands
        rows.append((round(size, 1), rooms, round(price, 1)))
    return rows


houses = _simulate()
sizes = [h[0] for h in houses]
rooms = [h[1] for h in houses]
prices = [h[2] for h in houses]
print(houses[:3])
print(len(houses), "houses; sizes", min(sizes), "to", max(sizes), "| rooms", min(rooms), "to", max(rooms))

# The model: price ≈ w_size × size + w_rooms × rooms + b, fitted by gradient descent on the mean squared error.
# Your study starts here: the loss and its slopes, then the curvatures, then the learning-rate sweep.
In the lesson, you build it here and the tutor can review it.

Practice what you just read

  • 5 code examples you can edit and run
  • a practice exercise that checks your code
  • a 6-question quiz
  • 5 review cards for spaced repetition

Create a free account

Try a quiz question

At a flat point, the second derivative is negative. What is the point?

  1. A maximum
  2. A minimum
  3. A saddle point
  4. It can't be flat

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson