Which Way Do We Nudge?
Last time we landed on one idea: learning is nudging a pile of numbers until the model stops being wrong.
But that leaves a giant hole. When the model guesses wrong, how does it know which way to nudge each number? Up or down? By a lot or a little?
Getting one number right by trial and error might be fine. But a real model has billions of them. You cannot guess. You need a method.
That method is gradient descent, and it is the single most important algorithm in all of machine learning.
First, a Number for “How Wrong”
Remember the “Mistakes so far” counter from the last note? Let’s make it precise.
At any moment, the model’s current numbers produce some total amount of wrongness. We squeeze all of it into a single number.
The loss is one number that measures how wrong the model is right now. High loss means very wrong. Zero loss means perfect.
Everything from here is about one goal: make the loss as small as possible.
A Landscape of Wrongness
Here is the picture that unlocks everything.
Imagine every possible setting of the model’s numbers spread out as positions on a landscape. At each position, the height of the ground is the loss for that setting. Bad settings sit high on the hills. Good settings sit low in the valleys.
Training is just finding the lowest point on this landscape. The bottom of a valley is where the model is least wrong.
But there’s a catch: you cannot see the landscape. It has billions of dimensions and nobody has a map. You start at a random spot (random starting numbers), blindfolded, in fog.
So you do the one thing you can do, feel the ground right under your feet. Even blindfolded, you can tell which way slopes downhill. So you repeat a tiny loop:
- Feel which direction goes downhill from where you stand.
- Take a small step that way.
- Feel again from the new spot.
- Repeat until the ground goes flat.
That’s the entire algorithm. Feel the slope, step downhill, repeat.
The Real Names
Every piece of that story has a technical name, and none of them are scary:
| The intuition | The real name |
|---|---|
| The landscape of wrongness | the loss function |
| “Which way is downhill?” | the gradient |
| How big a step you take | the learning rate |
| Feel slope → step → repeat | gradient descent |
There is exactly one twist worth remembering. The gradient actually points in the direction of steepest increase, straight uphill. But we want to go down. So we always step in the opposite direction of the gradient.
That “step against the gradient” is literally why it is called gradient descent.
The One Knob That Ruins Everything
Of all the settings in machine learning, the learning rate (your step size) is the one people most often get wrong. Watch what the same valley does under three different step sizes:
- Steps too small → you will reach the bottom eventually, but it takes forever. Painfully slow.
- Steps too big → you leap clean over the valley, land on the far hillside, and overshoot back again. You bounce around and never settle. It can even get worse every step.
- Steps just right → you glide smoothly down into the valley.
Picking a good learning rate is one of the most important practical skills in the entire field. Too timid and you waste months. Too bold and the whole thing explodes.
One More Catch: You Might Get Stuck
Downhill has an honest limitation. It only promises to find a valley, not the deepest one.
If you start on the wrong slope, you roll into the nearest dip and stop, even when a much deeper valley sits right next door. A shallow valley is called a local minimum. The true deepest point is the global minimum.
In practice, this matters far less than you’d fear. In the billion-dimensional landscapes of real models, there’s almost always some downhill direction to keep going, so getting truly, hopelessly stuck is rare. But the honest truth stands: gradient descent guarantees a bottom, not the best bottom.
Why This Is the Whole Ballgame
Here is the payoff, and it is bigger than it looks.
This single algorithm, feel the slope and step downhill, is how essentially every model is trained. The cat-or-dog classifier from the last note, and a frontier model like the ones powering today’s AI assistants, are the same loop. The only difference is the size of the landscape: two dimensions for our toy, a trillion for the frontier model.
When you hear that a model was “trained for three months on ten thousand GPUs,” this is what that means. Three months of taking tiny downhill steps.
We now have both halves of learning:
- What to change → the pile of numbers (from note 1)
- Which way to change it → downhill on the loss (gradient descent)
That’s the engine. Everything ahead, every architecture, every frontier trick, is built on top of this exact loop.
Try it in Python
Now let’s build the whole thing ourselves, gradient descent, in a dozen or so lines of pure Python. No libraries, nothing to install. We go one small step at a time, and every step hides a Reveal the answer you should open only after a genuine try.
The game
A secret number is hidden from you. You may make a guess, and a wrongness meter hands back one number: how far off you are. Zero means you nailed it. It never reveals the secret, or even which way to move.
Two things make the game work:
- Too low and too high are equally wrong. Guessing 0 and guessing 6 both peg the meter, because wrongness is about distance from the secret, not direction.
- The only goal is to drive the meter to 0.
And here is the rule that turns this into real machine learning: you build both sides. You build the meter (which knows the secret is 3), and you build the searcher (which must find 3 without ever looking at that 3). The searcher only gets to read the meter, exactly like you in the game.
Step 1: build the wrongness meter
Your task. Write a function loss(x) that takes a guess and returns how wrong it is: 0 at exactly 3, growing as the guess moves away, with too-low and too-high counting equally.
Hint: subtracting the secret gives you the gap between guess and answer. What could you do to that gap so it is always positive, and zero only when the guess is right?
Reveal the answer
def loss(guess):
secret = 3
gap = guess - secret
return gap ** 2Work out the gap from the secret, then square it. Squaring makes the score positive (so too-low and too-high both count) and makes 0 the smallest score possible, reached only when the guess is 3. Same guesses as the animation, same numbers back:
print(loss(0)) # 9 way too low
print(loss(6)) # 9 way too high, equally wrong
print(loss(1.5)) # 2.25 getting warmer
print(loss(3)) # 0 nailed itStep 2: feel which way is downhill
Now the searcher. It stands at some guess and it is not allowed to peek at the 3. All it can do is read the meter. So how does it know whether to move left or right?
It pokes: it reads the meter a tiny step to each side and sees which one comes back less wrong. That side is downhill.
Your task. Write a function slope(x) that pokes a hair to the left and a hair to the right and reports how fast the wrongness is changing. Positive means “uphill to the right, so go left,” negative means “downhill to the right, so go right,” and the size is how steep it is.
Hint: poke by some tiny amount h. Compare loss(x + h) with loss(x - h); their difference, divided by the distance you moved, is the slope.
Reveal the answer
def slope(guess):
poke = 0.0001
wrong_on_the_left = loss(guess - poke)
wrong_on_the_right = loss(guess + poke)
change = wrong_on_the_right - wrong_on_the_left
distance = 2 * poke
return change / distanceIt reads the meter a hair to each side of the guess, sees how much it changed, and divides by how far it moved. No calculus, just two pokes:
print(slope(5)) # about 4.0 positive, so downhill is to the LEFT
print(slope(1)) # about -4.0 negative, so downhill is to the RIGHT
print(slope(3)) # about 0.0 flat, this is the bottomThat single number is what the note called the gradient.
Step 3: take a step, and repeat
You can feel the slope, so now just walk downhill: from your current guess, take a small step in the opposite direction of the slope, then feel again, and repeat.
The move is: feel the slope where you stand, then shift the guess against it. In code that is guess = guess - (step_size * slope_here). The step_size (the learning rate) sets how far you move, and subtracting the slope is what sends you downhill instead of up.
Your task. Start at guess = 0, set step_size = 0.1, and repeat that move about 30 times, printing the guess and its wrongness each round. Watch it find 3 on its own.
Reveal the answer
guess = 0.0
step_size = 0.1
for i in range(30):
slope_here = slope(guess)
guess = guess - (step_size * slope_here)
print(f"step {i}: guess = {guess:.4f} wrongness = {loss(guess):.4f}")The first and last lines look like this:
step 0: guess = 0.6000 wrongness = 5.7600
step 1: guess = 1.0800 wrongness = 3.6864
...
step 29: guess = 2.9963 wrongness = 0.0000 The guess climbed from 0 to (almost exactly) 3, and it never saw the answer. That loop, feel the slope, step downhill, repeat, is gradient descent. You just built it from scratch.
Step 4: break it with the learning rate
The whole thing hinges on one number: lr. Change it, predict what will happen, then run and see.
lr | What happens |
|---|---|
0.01 | crawls, barely moves in 30 steps |
0.1 | glides smoothly to 3 |
1.0 | bounces between two points forever, never settles |
1.1 | loss grows every step and explodes to infinity |
Feeling the loss get worse every round at lr = 1.1 teaches the lesson better than any warning could: too timid wastes forever, too bold blows up.
Going further: getting stuck
One honest limit from the note: downhill finds a bottom, not always the deepest one. Watch what happens when the meter has two valleys and you start from two different places:
Both balls run the exact same code, yet they settle in different valleys, decided only by where each one started. Try it yourself: swap in a bumpier meter and run your Step 3 loop from guess = -3, then from guess = 3.
Reveal the answer
def loss(guess):
two_valleys = 0.1 * (guess * guess - 4) ** 2
tilt = 0.3 * guess
return two_valleys + tilt + 2Run your Step 3 loop with this loss (use step_size = 0.05), once from guess = -3 and once from guess = 3:
start guess = -3 -> settles near -2.1 (the deep valley)
start guess = 3 -> settles near 1.9 (a shallower one: a local minimum) That shallower stopping point is a local minimum, exactly the catch the note warned about: gradient descent guarantees you a bottom, never the best one.