Machine Learning for Beginners: An Introduction to Neural Networks

A simple explanation of how they work and how to implement one from scratch in Python.

Here’s something that might surprise you: neural networks aren’t that complicated! The term “neural network” gets used as a buzzword a lot, but in reality they’re often much simpler than people imagine.

This post is intended for complete beginners and assumes ZERO prior knowledge of machine learning. We’ll understand how neural networks work while implementing one from scratch in Python.

Let’s get started!

Note: I recommend reading this post on victorzhou.com — much of the formatting in this post looks better there.

1. Building Blocks: Neurons

First, we have to talk about neurons, the basic unit of a neural network. A neuron takes inputs, does some math with them, and produces one output. Here’s what a 2-input neuron looks like:

Image for post

3 things are happening here. First, each input is multiplied by a weight:

Image for post

Next, all the weighted inputs are added together with a bias b:

Image for post

Finally, the sum is passed through an activation function:

Image for post

The activation function is used to turn an unbounded input into an output that has a nice, predictable form. A commonly used activation function is the sigmoid function:

Image for post

The sigmoid function only outputs numbers in the range (0,1). You can think of it as compressing (−∞,+∞) to (0,1) — big negative numbers become ~0, and big positive numbers become ~1.

A Simple Example

Reminder: much of the formatting in this article looks better in the original post on victorzhou.com.

Assume we have a 2-input neuron that uses the sigmoid activation function and has the following parameters:

Image for post

w=[0, 1] is just a way of writing w1​=0, w2​=1 in vector form. Now, let’s give the neuron an input of x=[2, 3]. We’ll use the dot product to write things more concisely:

Image for post

The neuron outputs 0.999 given the inputs x=[2,3]. That’s it! This process of passing inputs forward to get an output is known as feedforward.

Coding a Neuron

Time to implement a neuron! We’ll use NumPy, a popular and powerful computing library for Python, to help us do math:

Recognize those numbers? That’s the example we just did! We get the same answer of 0.999.

2. Combining Neurons into a Neural Network

A neural network is nothing more than a bunch of neurons connected together. Here’s what a simple neural network might look like:

Image for post

This network has 2 inputs, a hidden layer with 2 neurons (h1​ and h2​), and an output layer with 1 neuron (o1​). Notice that the inputs for o1​ are the outputs from h1​ and h2​ — that’s what makes this a network.

A hidden layer is any layer between the input (first) layer and output (last) layer. There can be multiple hidden layers!

An Example: Feedforward

Let’s use the network pictured above and assume all neurons have the same weights w=[0,1], the same bias b=0, and the same sigmoid activation function. Let h1​, h2​, o1​ denote the outputs of the neurons they represent.

What happens if we pass in the input x=[2, 3]?

Image for post

The output of the neural network for input x=[2,3] is 0.7216. Pretty simple, right?

A neural network can have any number of layers with any number of neurons in those layers. The basic idea stays the same: feed the input(s) forward through the neurons in the network to get the output(s) at the end. For simplicity, we’ll keep using the network pictured above for the rest of this post.

Coding a Neural Network: Feedforward

Let’s implement feedforward for our neural network. Here’s the image of the network again for reference:

Image for post

We got 0.7216 again! Looks like it works.

3. Training a Neural Network, Part 1

Say we have the following measurements:

Image for post

Let’s train our network to predict someone’s gender given their weight and height:

Image for post

We’ll represent Male with a 0 and Female with a 1, and we’ll also shift the data to make it easier to use:

Image for post

Loss

Before we train our network, we first need a way to quantify how “good” it’s doing so that it can try to do “better”. That’s what the loss is.

We’ll use the mean squared error (MSE) loss:

Image for post

Let’s break this down:

  • n is the number of samples, which is 4 (Alice, Bob, Charlie, Diana).
  • y represents the variable being predicted, which is Gender.
  • y_true​ is the true value of the variable (the “correct answer”). For example, y_true​ for Alice would be 1 (Female).
  • y_pred​ is the predicted value of the variable. It’s whatever our network outputs.

(y_true​−y_pred​)² is known as the squared error. Our loss function is simply taking the average over all squared errors (hence the name mean squared error). The better our predictions are, the lower our loss will be!

Better predictions = Lower loss.

Training a network = trying to minimize its loss.

An Example Loss Calculation

Let’s say our network always outputs 00 — in other words, it’s confident all humans are Male 🤔. What would our loss be?

Image for post

Code: MSE Loss

Here’s some code to calculate loss for us:

If you don’t understand why this code works, read the NumPy quickstart on array operations.

Nice. Onwards!

Liking this post so far? I write a lot of beginner-friendly ML articles. Subscribe to my newsletter to get them in your inbox!

4. Training a Neural Network, Part 2

We now have a clear goal: minimize the loss of the neural network. We know we can change the network’s weights and biases to influence its predictions, but how do we do so in a way that decreases loss?

This section uses a bit of multivariable calculus. If you’re not comfortable with calculus, feel free to skip over the math parts.

For simplicity, let’s pretend we only have Alice in our dataset:

Image for post

Then the mean squared error loss is just Alice’s squared error:

Image for post

Another way to think about loss is as a function of weights and biases. Let’s label each weight and bias in our network:

Image for post

Then, we can write loss as a multivariable function:

Image for post

Imagine we wanted to tweak w1​. How would loss L change if we changed w1​? That’s a question the partial derivative can answer. How do we calculate it?

Here’s where the math starts to get more complex. Don’t be discouraged! I recommend getting a pen and paper to follow along — it’ll help you understand.

If you have trouble reading this: the formatting for the math below looks better in the original post on victorzhou.com.

To start, let’s rewrite the partial derivative in terms of ∂y_pred/w1​​ instead:

Image for post
This works because of the Chain Rule.

We can calculate ∂L/y_pred​ because we computed L= (1−y_pred​)² above:

Image for post

Now, let’s figure out what to do with ∂y_pred/w1. Just like before, let h1​, h2​, o1​ be the outputs of the neurons they represent. Then

Image for post
f is the sigmoid activation function, remember?

Since w1​ only affects h1​ (not h2​), we can write

Image for post
More Chain Rule.

We do the same thing for ∂h1​​/∂w1:

Image for post
You guessed it, Chain Rule.

x1​ here is weight, and x2​ is height. This is the second time we’ve seen f′(x) (the derivate of the sigmoid function) now! Let’s derive it:

Image for post

We’ll use this nice form for f′(x) later.

We’re done! We’ve managed to break down ∂L/w1​ into several parts we can calculate:

Image for post

This system of calculating partial derivatives by working backwards is known as backpropagation, or “backprop”.

Phew. That was a lot of symbols — it’s alright if you’re still a bit confused. Let’s do an example to see this in action!

Example: Calculating the Partial Derivative

We’re going to continue pretending only Alice is in our dataset:

Image for post

Let’s initialize all the weights to 1 and all the biases to 0. If we do a feedforward pass through the network, we get:

Image for post

The network outputs y_pred​=0.524, which doesn’t strongly favor Male (0) or Female (1). Let’s calculate ∂L/w1​​:

Image for post

Reminder: we derived f′(x)=f(x)∗(1−f(x)) for our sigmoid activation function earlier.

We did it! This tells us that if we were to increase w1​, L would increase a tiiiny bit as a result.

Training: Stochastic Gradient Descent

We have all the tools we need to train a neural network now! We’ll use an optimization algorithm called stochastic gradient descent (SGD) that tells us how to change our weights and biases to minimize loss. It’s basically just this update equation:

Image for post

η is a constant called the learning rate that controls how fast we train. All we’re doing is subtracting η w1/​∂L​ from w1​:

  • If ∂L/w1​ is positive, w1​ will decrease, which makes L decrease.
  • If ∂L/w1​​ is negative, w1​ will increase, which makes L decrease.

If we do this for every weight and bias in the network, the loss will slowly decrease and our network will improve.

Our training process will look like this:

  1. Choose one sample from our dataset. This is what makes it stochastic gradient descent — we only operate on one sample at a time.
  2. Calculate all the partial derivatives of loss with respect to weights or biases (e.g. ∂L/w1​, ∂L​/∂w2​, etc).
  3. Use the update equation to update each weight and bias.
  4. Go back to step 1.

Let’s see it in action!

Code: A Complete Neural Network

It’s finally time to implement a complete neural network:

Image for post
Image for post

You can run / play with this code yourself. It’s also available on Github.

Our loss steadily decreases as the network learns:

Image for post

We can now use the network to predict genders:

Now What?

You made it! A quick recap of what we did:

  • Introduced neurons, the building blocks of neural networks.
  • Used the sigmoid activation function in our neurons.
  • Saw that neural networks are just neurons connected together.
  • Created a dataset with Weight and Height as inputs (or features) and Gender as the output (or label).
  • Learned about loss functions and the mean squared error (MSE) loss.
  • Realized that training a network is just minimizing its loss.
  • Used backpropagation to calculate partial derivatives.
  • Used stochastic gradient descent (SGD) to train our network.

There’s still much more to do:

I may write about these topics or similar ones in the future, so subscribe if you want to get notified about new posts.

Thanks for reading!

Originally posted on victorzhou.com.

Written by

CS @ Princeton University. I write about web development, machine learning, and more at https://victorzhou.com.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store