Before you read on

Think about this for a minute.

You write a program that converts miles to kilometers. You run it a thousand times. Is it any better at the job than it was on the first run?

Almost certainly not. It does exactly what it did the first time, no faster and no more accurately. Running a program does not improve it. So what would have to change for a program to genuinely get better at something?

Two ways to write the converter

The normal way is that you already know the rule. One mile is 1.609 kilometers, so you write:

function milesToKm(miles) {
  return miles * 1.609;
}

You supplied the 1.609. The program contributes nothing except multiplication.

Now suppose you did not know the number. You have never seen a conversion table. All you have is a handful of measurements someone recorded:

const examples = [
  [1, 1.609],
  [2, 3.218],
  [5, 8.045],
];

Could you write a program that works out the 1.609 by itself, using only those examples? That is a different kind of program, and it is the kind this whole course is about.

A program that improves

Here is one. It is short enough to read in full, and every line is explained underneath.

const examples = [[1, 1.609], [2, 3.218], [5, 8.045]];

let factor = 0;          // the program starts out knowing nothing

for (let step = 0; step < 200; step++) {
  for (const [miles, km] of examples) {
    const guess = miles * factor;   // what we currently predict
    const error = km - guess;       // how far off we are
    factor = factor + 0.01 * error * miles;   // nudge in the right direction
  }
}

console.log(factor);   // 1.609...

Read it once more before the explanation. The whole idea of machine learning is in those few lines.

  • factor starts at 0. The program has no knowledge at the start. Its first prediction for any distance is zero kilometers, which is badly wrong.
  • guess is the prediction it would make right now.
  • error is the gap between the correct answer and the guess. If the guess is too low, the error is positive. If it is too high, the error is negative.
  • The last line moves factor a small amount in whichever direction reduces the error. Positive error pushes it up, negative error pushes it down.
  • The 0.01 controls how big each nudge is. Try changing it and see what happens.

Nobody told this program that the answer was 1.609. It found the number from the examples alone.

guess measure the error nudge repeat 200 times 0 100 200 0.000 0.800 1.609 round factor nobody told it 1.609 it works the number out from three examples
The loop on the left is the whole of machine learning. The curve on the right is that loop actually running, on the eight lines of code in this lesson.

Watch it happen

Print the value on every pass instead of only at the end:

for (let step = 0; step < 200; step++) {
  for (const [miles, km] of examples) {
    const guess = miles * factor;
    const error = km - guess;
    factor = factor + 0.01 * error * miles;
  }
  if (step % 20 === 0) console.log(step, factor.toFixed(4));
}

You will see it climb quickly at first, then slow down as the error shrinks, and settle near 1.609. Later in this course we will draw that curve on a canvas rather than reading numbers, because the shape tells you a great deal about whether learning is going well.

So, the answer

Yes, a program can get better at a task, but only if three things are true. It has to have something adjustable inside it. It has to have a way of measuring how wrong it currently is. And it has to have a rule for changing itself in response to that measurement.

Our converter had all three: factor was adjustable, error measured the wrongness, and the last line was the rule for changing. Every model in this course, up to and including neural networks, has exactly these three parts. Only the details grow.

Try this before the next lesson

  1. Set the starting factor to 100 instead of 0. Does it still reach 1.609?
  2. Change 0.01 to 0.1. Then to 0.5. What breaks, and can you say why?
  3. Add a fourth example with a deliberately wrong value, such as [10, 50]. What happens to the answer, and is that behavior reasonable?

The second question is the one worth sitting with. We come back to it properly in the section on gradient descent.