All posts

Understanding neural networks | Part 1

11 min read


I decided to build Neural Networks from Scratch in Go. The book is written by Sentdex & friend and every line of code in it is Python, most of it NumPy.

That last part is the reason I picked Go.

The book has two spines running side by side. One is the mathematics. The other is NumPy. Nearly every chapter poses a problem, solves it with plain Python loops, then deletes those loops and replaces them with one NumPy call. The NumPy half is where the arithmetic goes to hide. Shapes, broadcasting, the transpose that makes the error go away, the axis argument that changes the answer. You can finish the book in Python having never once been forced to know what any of those did.

Go has no NumPy. So the second half of every chapter stops being the answer and becomes the assignment.

That was the plan. Chapter 1 was supposed to take twenty minutes.

Chapter 1 has no code in it at all. Fourteen pages, in English. I expected to skim it for vocabulary and get to the code.

Instead I stopped on the opening line of "A Brief History", which says that since computers existed, scientists have been

formulating ways to enable machines to take input and produce desired output

for classification and regression.

I read that as scene-setting. Then I noticed I could not say why it was true.

We have had machines taking input and producing output since the 1950s. Payroll systems do it. Compilers do it. So the sentence is not describing the invention of computing. It is describing a search for a second way of getting output from input, and that only makes sense if the first way had run out somewhere.

Where had it run out?

A program is a rule that a person wrote down

Here is what I did not have straight, and it took a while to see that I did not have it straight.

A program is a set of rules that a person wrote. A person worked out what to do, in what order, under what conditions, and then typed it. The machine holds no opinion about any of it. The intelligence in a payroll system is entirely in the head of whoever knew the tax rules, and the code is the transcript of that knowledge.

Which means every ordinary program carries a hidden requirement: a human must be able to state the rule. If a person can state it, a person can code it, and nothing further is needed.

I wrote that down and immediately wrote a question next to it, in a different pen, because it had nothing to do with neural networks and I could not let it go:

Is this why we say "if we can think it, we can achieve it"?

Maybe. I still think that (maybe) is the correct answer and I am leaving it in.

Now try to satisfy the requirement for something else. Look at a photo of a friend. You recognise them in about a tenth of a second, near perfectly. Write down the rule you used. Not a description of their face. A rule a machine could execute, that returns true for them in bad light, at a strange angle, ten years older or younger, with a beard, and false for their sibling.

Nobody has managed this. Not because it is hard work, but because we do not have access to the rule we used.

or do we have access?

I am not sure. The honest position is that introspection reports something when you ask it, and what it reports is not a rule, it is a story. But I have not earned a stronger claim than that yet.

Reading someone's handwriting. Hearing the difference between a question and a statement. Knowing a cat from a dog, and why a cat is a cat and a dog is a dog. Why. Why?There is a whole category of tasks that humans are reliable at doing and completely unable to say.

The bottleneck was never the machine's speed. It was that the human had to write the rule down, and for these tasks no human can.

The move that broke it is one substitution. You cannot state the rule for recognising an 8. You can look at a picture and say "that is an 8", instantly, thousands of times, without effort. So the human capability that is missing is specification. The human capability that is abundant is judgement.

So in neural networks we reverse engineer. Stop asking humans to state the rule. Start asking humans to mark right and wrong. Then write a program with adjustable numbers inside it, plus a procedure that adjusts those numbers until its outputs agree with the examples the human already marked.

That is training. Weights and biases are the adjustable numbers. The examples are the marked answers.

If you can state the rule, write the rule. Learning is what you reach for when the rule exists and cannot be stated.

The formula I wrote by hand was wrong, and reading had never revealed it

Nine pages into my notes I tried to compress the whole argument into two lines. This is what I wrote:

ordinary:  Rule + State  =>  outputs
this:      Rule + (?)    =>  outputs
                (introspection)

It is wrong, and it is wrong in a way that matters.

Look at where the rule is sitting. In both lines I put it on the left, as something I have. In the second line I put the question mark on the missing piece, and I labelled that missing piece introspection.

But the rule is not something I have in the second line. The rule is the thing I do not have. It is the entire reason the second line exists at all. I had spent nine pages arguing that the rule cannot be stated, and then I wrote a formula that hands myself the rule anyway and marks something else as unknown.

The correct pair is an inversion, not a substitution:

ordinary:  rule  + input   =>  output
learning:  input + output  =>  rule

Same three quantities. The unknown moves. In ordinary programming you know the rule and compute the output. In learning you know the inputs and the outputs, and you solve for the rule.

That is a cleaner statement of the whole field than anything else in my notes, and I only got to it because the wrong version was sitting on the page where I could see it.

This is the part I want to remember. I had read that argument, agreed with it, asked good questions about it, and pulled three separate sentences out of it to be explained. All of that felt like understanding. Then I tried to write it as two lines of my own and put the unknown in the wrong place.

You cannot write a wrong formula while reading. Reading has no output to be wrong. Only producing does.

Recognising a good explanation is not the same as being able to produce one, and only one of them is evidence.

The data now carries the specification

A specification is the complete statement of what a system must do. Every system has one whether or not anyone wrote it down. Ordinary programming keeps it in two readable places, a document and the code that implements it.

Build a digit recogniser and there is no document. Nobody wrote down what an 8 is. Nobody can.

The requirement did not evaporate. So where did it go?

It went into the labels. Sixty thousand images, each marked with a digit, are now the definition of what those digits are for that system. If you want to know what your model was asked to do, the only honest answer is to go and look at the data.

The model is wrong and you cannot fix it by editing a line, because the line does not exist. You fix it by changing the data and training again.

When examples define the requirement, an error in the examples is not bad input. It is a correct implementation of the wrong thing.

Two things follow from that which I did not expect, and I wrote them down before I could see where they led.

The first is that noise in the data is not a mistake. It is a requirement for a different task. The pixels I would throw away as irrelevant to recognising an 8 are exactly the pixels that identify whose handwriting it is. Nothing about the data itself marks any part of it as noise. The task does that, and a different task marks a different part.

The second is the general form. A mistake is an output for a different requirement. It is only a mistake relative to the specification you happened to be holding.

Which means the reaction to a mistake is not a mistake. It is a separate act, with its own inputs, made after the fact.

The same mechanism, pointed at people

I went somewhere with this that the book does not go, and I am keeping it because it is the reason any of it stuck.

If the examples define the requirement, and nobody has to write the requirement down for it to be binding, then the same thing is true of a person.

The things you know, the things you have done, the things you were primed to expect, are the things that make you judge and the things that make you move. Rich becomes richer and poor becomes poorer, and it has nothing to do with being rich or poor. Children primed from an early age to believe they are lucky, smart, great and successful tend to be lucky, smart, great and successful.

Is that why **Think and Grow Rich **works, to whatever extent it works?

Because that is all you know. That is all you have seen. That is how you are. That is who you are.

I am aware this is not a proof. It is an analogy running the other way, from the machine back to the person, and analogies that direction are the easiest kind to overrun. But the structure holds in the one place I care about: a specification that nobody wrote, carried entirely by examples, binding anyway, and invisible to the thing it is binding.

Guessing and approximating are not the same word

Somewhere in this I described what a network does as "some sort of guessing".

It is not guessing. Give a trained network the same image twice and you get the identical answer twice. There is no dice roll in it anywhere. Every output is a fixed arithmetic consequence of the weights and the input.

A network does not guess, it approximates. Approximate means close to the truth but not equal to it. A curve drawn near the points, not through them.

The difference is bias. Guessing has no bias. Approximating is wrong in specific, systematic, repeatable ways, and it will fail on the same kind of 8 every time.

That is what makes the field workable. If a network guessed there would be nothing to debug, because there would be nothing to find.

A trained network is not uncertain. It is wrong in ways that repeat, which is the only reason it can be improved.

My word was right about a different moment. Before training, the weights are random and the first output is a guess in the strictest sense.

Discrete or continuous, and there is no third

Classification and regression are not a pair somebody chose. They fall out of what an answer can be.

Either we believe the answer is one of a fixed set, or we believe the answer is a quantity along a continuum. Particle or wave. There is no third kind of number, which is why the book spends Chapters 2 to 16 on one and Chapter 17 on the other, and why that is enough.

The rule is what is fixed, not the data

A test case. A bank wants two systems. One approves loans by its written policy: income over a threshold, no defaults in five years, employed over a year. One flags fraudulent transactions.

I said the first is an ordinary program because its variables can be determined statically.

That reason is wrong, and the conclusion being right hid it from me. Income changes every month. Employment length changes daily. Default history changes whenever somebody defaults. None of it is static.

What is fixed is the rule, not the data. The bank already wrote the policy down in English. Nothing needs to be learned because nothing is missing.

On fraud I arrived somewhere I had not been led. A transaction is a transaction. At the moment it happens, nothing about it marks it as fraudulent. The label does not exist yet. It shows up weeks later when a customer calls and says they did not make that charge. The labels are retrospective, produced by a slow human process, and that process is the only reason the task is learnable. No reports, no labelled examples, no model, however much transaction data you hold.

I also hedged and said fraud might still be an ordinary program. Real banks agree with me enough to run rule engines in production. Amount over a threshold, country mismatch, six transactions in a minute. Those catch some fraud, flag your holiday abroad, and get learned around by anyone paying attention. Fraud is not a task where the rule cannot be stated. It is one where the rule can be partly stated and never finished. Most real problems live there.

The test is not whether the task is hard. It is whether the rule is already written.

The analogy

You were robbed. The police ask you to describe the man's face and you cannot. Nothing you say is of any use.

Then a sketch artist arrives. He draws a face, probably wrong. You look at it and say the nose is too wide. He adjusts. You look again and say the eyes are closer together. He adjusts. After forty rounds the face on the paper is the face you saw, and at no point did you state a single rule about it.

You could not specify. You could always evaluate. The correction loop did the rest.

The sketch is the model. Your "too wide" is the loss. The artist's adjustment is gradient descent. Chapter 5 is where I learn to say "too wide" in numbers, and Chapter 9 is where I learn how the artist decides what to change.

None of this would work if the data were arbitrary. It works because handwritten 8s genuinely resemble each other. There is real structure in the pixels, put there by the fact that people write 8s the same way for physical reasons. A rule exists. It is not available to introspection. Under those three conditions, and no others, searching for the rule is a sensible thing to attempt.

Note: I did check SOMETIMES the sketch matches the actual person, maybe most of the times it doesn’t.