A neural network to classify the handwritten digits 0-9 in the MNIST dataset. This network uses 784 inputs (for the 28x28 images of the handwritten digits), 30 neuron hidden layer, and 10 outputs.
MNIST dataset stored in data/mnist.pkl.gz. Zipped up and stored in a pickled format. See mnist_loader.py comments to learn more.
Network weights can be saved to and loaded from a file in a pickled format using save()
and load()
functions. Weights are saved into data/weights_file.
All code was developed using Python 2.7.12 on an Ubuntu 16.04 LTS system.
Libraries
- Numpy
- Image
- pickle
- random
- gzip
$ python test_net.py
$ python
>>> import neural_net
>>> import mnist_loader
>>> training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
>>> network = neural_net.Net([784, 30, 10])
Each index in the array input represents a layer in the network with the first and last representing the input and output layer respectively. Therefore, the above example represent a three layer network with 784 inputs (28x28 images), a 30 neuron hidden layer, and an output layer (digits 0-9). For now, this is the only network design that works because the layers are hardcoded in, but it will soon be abstracted to an n-layer network where you can use a network design like below
>>> network = neural_net.Net([784, 20, 30, 10])
From here, you can load a saved network or you can train network
from scratch. To load a previously saved network
>>> network.load()
>>> network.train(training_data, 3.0)
Note: Training takes O(n^2) time now (a really long time for the whole data set) because I wanted to be very explicit about how errors and weights were being calculated. I did this so that it follows the backpropagation process explained below very closely and clearly. This makes it easy for people to see exactly what happening without getting confused by any vector/matrix math. I may create a new project for a more efficient version, because I like this as proof of concept and learning tool.
>>> network.test(test_data)
This is like asking network
the question
"Hey, I have trained you to be able to classify a 3. Now, I'm curious, what do you think a 3 looks like?".
>>> network.imagine(3)
Think of the output like a heatmap that indicates what pixels are most important in differentiating a given digit from any other digit.
>>> network.save()
>>> network.load()
Using the sigmoid activation function:
Input pattern is applied and the output is calculated
Calculate the input to the hidden layer neurons
Feed inputs of hidden layer neurons through the activation function
Multiply the hidden layer outputs by the corresponding weights to calculate the inputs to the output layer neurons
Error of each neuron is calculated and the error is used to mathematically change the weights to minimize them, repeatedly.
_ = subscript, W+ = new weight, W = old weight, δ = error, η = learning rate.
Calculate (back-propagate) hidden layer errors
Change hidden layer weights
"Hey, I have trained you to be able to classify a 3. Now, I'm curious, what do you think a 3 looks like?".
Note: Assuming a trained network and desired outputs provided. Output provided as a vector of 0s and 1s. For example: [0,0,0,1,0,0,0,0,0,0] is the output for a 3. Below math is for network example above, but can be applied more generally.
Note: This math is not entirely correct but seems to be giving some reasonable results. Also, in_A, in_B, in_C
refers to the reverse input into a because this whole process is taking the desired output and passing it backwards to solve for inputs.
Multiply in_A, in_B, in_C
by each hidden layer weight
Now, to test your imagined input, feed the input back through the network and see if you classify it correctly!
Note: Don't forget to normalize inputs once you've solved for them, or else it will just look like noise!
MNIST Data and loader borrowed from mnielsen
Network image and math for backpropagation from here