Skip to content
Math foundations · 6 of 6 · Python

Fit a line with gradient descent

Now put two earlier ideas together, the linear model and gradient descent, and let the model learn. Start with a line that is wrong: weight 0 and bias 0. Measure how far its predictions are from the true values. Work out which way to move the weight and the bias to shrink that error, and move both a little. Each round the line fits the points better. This is training, in its smallest form.

Learn it

Words to know

mean squared error
A loss for predicting numbers: the average of the squared differences between predictions and true values.
gradient
The slope of the loss for one number in the model: which way the loss moves, and how fast, when that number goes up a little.
learning rate
How far to move against the gradient on each update. Too small is slow; too large overshoots.

How it works

  1. Predict w * x + b for every point, and take the error prediction - y for each. The loss is the mean of the squared errors.
  2. The slope for w is 2 * mean(error * x). A point with a large x moves the weight more.
  3. The slope for b is 2 * mean(error).
  4. Update both together: w = w - rate * dw and b = b - rate * db. Both slopes come from the same errors, worked out before either number changes.

Worked example

Input
xs = [1, 2]
ys = [2, 4]
rate = 0.1
steps = 1

Output
[1, 0.6]
WhatCalculationValue
Predictionsw and b are 00 and 0
Errors0 - 2 and 0 - 4-2 and -4
Slope for w2 × ((-2×1) + (-4×2)) / 2-10
Slope for b2 × (-2 + -4) / 2-6
New w0 - 0.1 × (-10)1
New b0 - 0.1 × (-6)0.6
y02460123xpoint (1, 2)point (1, 2)point (2, 4)point (2, 4)
  • Start
  • After 1 step
  • After 2 steps
The line starts flat at 0. Each update tilts and lifts it toward the two points; the line that passes through both is y = 2x.

The true line is y = 2x. After one step the model already says 1.6 and 2.6 for the two points, much closer than 0 and 0.

Why it matters

Chapter 4 runs this same loop in PyTorch, where the slopes are worked out for you. Knowing what happens by hand makes that code readable.

Your turn

Fit a line w * x + b to some points with gradient descent on the mean squared error. Start from w = 0 and b = 0.

xs
the x value of each point; at least one
ys
the y value of each point, in the same order
rate
the learning rate
steps
how many updates to make; 0 or more
Returns
The list [w, b] after the last update.

Example 1

Input
xs = [1, 2]
ys = [2, 4]
rate = 0.1
steps = 1

Output
[1, 0.6]

One update moves w from 0 to 1 and b from 0 to 0.6, as in the worked example.

Example 2

Input
xs = [1, 2]
ys = [2, 4]
rate = 0.1
steps = 2

Output
[1.32, 0.78]

A second update starts from w = 1 and b = 0.6. The errors are smaller now, so the step is smaller too.

Example 3

Input
xs = [0, 1, 2, 3]
ys = [1, 3, 5, 7]
rate = 0.1
steps = 1

Output
[1.7, 0.8]

Four points on the line y = 2x + 1. The errors are -1, -3, -5 and -7, so the slopes are 2 × (-34) / 4 = -17 and 2 × (-16) / 4 = -8.

Example 4

Input
xs = [1]
ys = [2]
rate = 0.1
steps = 0

Output
[0, 0]

With zero steps the line stays where it started.

Hints

Each hint gives away a little more. Open one only when you need it.

  1. Hint 1: Where to start

    One update needs three things in order: the error of every point, the two slopes, and then the two new values.

  2. Hint 2: The key step

    Work out all the errors with the current w and b first, and keep them in a list. Use that one list for both slopes.

  3. Hint 3: The outline

    Set n to len(xs). Repeat steps times: build errors as w * x + b - y for each point; set dw to 2 * sum(e * x for e, x in zip(errors, xs)) / n and db to 2 * sum(errors) / n; then subtract rate * dw from w and rate * db from b. Return [w, b].

Solution

From here on the page gives the method away. If you are still solving, go back to the hints.

Step by step

  1. How far each prediction is from its true value. The errors are stored once, so both slopes are measured against the same line.
  2. The slope of the loss for w and for b. Each error is multiplied by x for the weight, because the weight's effect on a prediction grows with x. The bias affects every prediction equally.
  3. Move both against their slopes. Both slopes were worked out before this point, so changing w first does not disturb the update of b.

The same numbered steps are the comments in the solution.

Python
def solve(xs, ys, rate, steps):
    w = 0.0
    b = 0.0
    n = len(xs)
    for _ in range(steps):
        # 1. How far each prediction is from its true value
        errors = [w * x + b - y for x, y in zip(xs, ys)]
        # 2. The slope of the loss for w and for b
        dw = 2 * sum(error * x for error, x in zip(errors, xs)) / n
        db = 2 * sum(errors) / n
        # 3. Move both against their slopes
        w = w - rate * dw
        b = b - rate * db
    return [w, b]

Watch out

  • Do not update w and then work out the errors again for b. Both slopes must come from the line as it was at the start of the step.
  • Divide by the number of points. Without it the slopes grow with the size of the data, and a rate that worked for 2 points fails for 200.

Check yourself

Why is each error multiplied by x in the slope for the weight?

Changing the weight changes a prediction by x times as much. So points with a large x say more about which way the weight should move.

Why must both slopes be worked out before either value changes?

They describe the same line. If w moved first, the slope for b would be measured on a different line.