stough 202-
 Digit Recognition Through Averages](/imageprocessing-awesome/build/play_mnist_correlate-b78850106fc10753c0e53b60c86b940e.jpg)
In this notebook we’ll define a simple linear scheme for recognizing hand-written digits. Every image we train on and ever test against is a single-channel thumbnail. Look through mnist_explore.ipynb for more.
Our approach for recognizing the digit in a single image is simple: compare each pixel in the image against a template for each digit class. We’ll do this through simple correlation: a dot-product of the image with the digit template. The higher the correlation, the higher the likelihood that the image is example of that template digit.
%matplotlib inline
# or widget
import matplotlib.pyplot as plt
import numpy as np
from random import shuffle
import copy
import tempfile
from functools import partial
from multiprocessing.pool import ThreadPool
from torchvision import datasets, transforms
from torchvision.utils import make_grid
# For importing from alternative directory sources
import sys
sys.path.insert(0, '../dip_utils')
from data_paths import mnist_root
from matrix_utils import (arr_info,
make_linmap)Loading and Formatting the MNIST Data¶
# Thank you: https://www.aiworkbox.com/lessons/load-mnist-dataset-from-pytorch-torchvision
# https://pytorch.org/vision/stable/datasets.html
mnist_trainset = datasets.MNIST(root=mnist_root(), train=True, download=True, transform=None)
mnist_testset = datasets.MNIST(root=mnist_root(), train=False, download=True, transform=None)
x_train = np.stack([np.array(x).ravel() for x,_ in mnist_trainset])
x_test = np.stack([np.array(x).ravel() for x,_ in mnist_testset])
y_train = np.stack([y for _,y in mnist_trainset]).astype('long')
y_test = np.stack([y for _,y in mnist_testset]).astype('long')
# These are Nx784
x_train = x_train.astype(np.float32)/255.0
x_test = x_test.astype(np.float32)/255.0arr_info(x_train)0. Play around with all the data being provided.¶
As you see above, each row of x_train (or x_test) is a 784-dimensional vector, just a flattened version of some MNIST image in either the training or test data. Further, y_train (or y_test) contains the digit class. Explore these structures that have been provided, so that you will have some idea what in the world is happenning below.
Visualize a corresponding row and element from
x_trainandy_train.What are the shapes of
x_trainandy_train? What aboutx_testandy_test?Print out
y_train[1234]Try to visualize the corresponding hand-written digit image in
x_train, that isx_train[1234]orx_train[1234,:]. Of course you cannotimshowa 1D row, so usenp.reshape.
Hopefully this exploration should convince you that we have training x_train and test x_test datasets, and these sets are also labeled through the arrays y_train and y_test. We’re going to use the training data to formulate MNIST image templates for each digit, and then use the test data to see how well those templates generalize to data that we didn’t use in training.
1. Vector-normalize the x_train and x_test data.¶
Normalize each row so that every image has a magnitude of 1. We want to do this so that when we do correlations/dot-products, the possible outcomes are all values in the range normalized range . Use norm and divide to pretty easily perform this normalization.
If you’re writing your own for loop, you haven’t looked closely enough at the documentation for these powerfull numpy functions.
You may run into an issue with
broadcastrules on division. For example, if we just look at the shapes and ,dividewould know how to divide these two matrices: it would virtually make three vertical copies of the second so that the two shapes matched at . Howeverdividedoesn’t know what to do with and for example, because the number of dimensions isn’t even the same. Read the documentation and work with small examples to solve your issues.
## Check correctness, should be all True...
print(f'Is every row of x_test normalized?')
np.isclose(np.linalg.norm(x_test, axis=-1), 1.0)2. Compute a matrix of template digits.¶
See mnist_explore.ipynb, where we compute the average seven for example. Do this for every digit using the x_train (and y_train) data. I call the resulting matrix A in the test cells.
## Test norm, should be all True...
np.isclose(np.linalg.norm(A, axis=-1), 1.0)## Test sanity, visualize all the template digits
f, ax = plt.subplots(2,5, figsize=(8,3))
for i in range(5):
ax[0, i].imshow(np.reshape(A[i,:], (28,28)), cmap='gray')
ax[1, i].imshow(np.reshape(A[i+5,:], (28,28)), cmap='gray')
[a.axes.get_xaxis().set_visible(False) for a in ax.flatten()]
[a.axes.get_yaxis().set_visible(False) for a in ax.flatten()]
plt.tight_layout()3. Correlate x_test against the template matrix.¶
Use matmul to compute the dot-product of every test case against every digit template. Given that there are 10K test images, our result should be .
4. Compute the classification accuracy of this scheme.¶
Use argmax to determine for each test case which template digit yielded the best score. Then compute the number that matched the correct labels from y_test.