Here I’m doing one of the Keras hello world micro projects, involving classification of tiny 28x28 wardrobe images. This was really fun. ( Here’s the original home for the python notebook). One really fun part of this was that at the end I hand drew my own clothing samples, paired them down using the python PIL library and threw them against the classifier. Surprisingly, the mini model performed really well. That’s some generalizability!

Also this was a really nice expansion of my existing matplotlib knowledge. The grid plotting protips here were a great addition to my repertoire.

from __future__ import absolute_import, division, print_function, unicode_literals

# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras

# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
1.13.1


fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 2s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step


class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']


plt.figure()
plt.imshow(train_images[3])
plt.colorbar()
plt.grid(False)
plt.show()
# Hmm so this `cmap=plt.cm.binary` kwarg displays grayscale instead of that strange purple to yellow scale.
plt.figure()
plt.imshow(train_images[3]/255.0, cmap=plt.cm.binary)
plt.colorbar()
plt.grid(False)
plt.show()
# This is almost like the output of a TSA luggage xray scanner 

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i]) # , cmap=plt.cm.binary
    plt.xlabel(class_names[train_labels[i]])
plt.show()

train_images_scaled = train_images / 255.0
test_images_scaled = test_images / 255.0
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.softmax)
])
WARNING:tensorflow:From /usr/local/miniconda3/envs/pandars3/lib/python3.7/site-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
model.compile(optimizer='adam', 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# Looking at the use of the non-scaled data first.
# Wow that looks like terrible accuracy.
model.fit(train_images, train_labels, epochs=5)
Epoch 1/5
60000/60000 [==============================] - 4s 70us/sample - loss: 14.5146 - acc: 0.0995
Epoch 2/5
60000/60000 [==============================] - 4s 66us/sample - loss: 13.7328 - acc: 0.1479
Epoch 3/5
60000/60000 [==============================] - 4s 66us/sample - loss: 13.0450 - acc: 0.1906
Epoch 4/5
60000/60000 [==============================] - 4s 66us/sample - loss: 13.0474 - acc: 0.1905
Epoch 5/5
60000/60000 [==============================] - 4s 68us/sample - loss: 12.9979 - acc: 0.1936





<tensorflow.python.keras.callbacks.History at 0x139540978>
# Yea if that is out of 1.0 then this 0.1917 is pretty low
test_loss, test_acc = model.evaluate(test_images, test_labels)

print('Test accuracy:', test_acc)
10000/10000 [==============================] - 0s 26us/sample - loss: 13.0283 - acc: 0.1917
Test accuracy: 0.1917
# Try on that scaled data now ..
# Okay this looks better. more like the result in the tutorial.
model.fit(train_images_scaled, train_labels, epochs=5)
Epoch 1/5
60000/60000 [==============================] - 4s 68us/sample - loss: 0.5902 - acc: 0.8182
Epoch 2/5
60000/60000 [==============================] - 4s 66us/sample - loss: 0.3900 - acc: 0.8622
Epoch 3/5
60000/60000 [==============================] - 4s 67us/sample - loss: 0.3526 - acc: 0.8739
Epoch 4/5
60000/60000 [==============================] - 4s 66us/sample - loss: 0.3319 - acc: 0.8788
Epoch 5/5
60000/60000 [==============================] - 4s 66us/sample - loss: 0.3141 - acc: 0.8858





<tensorflow.python.keras.callbacks.History at 0x1424e8fd0>
model.input_shape, model.output_shape
((None, 28, 28), (None, 10))
model.weights
[<tf.Variable 'dense/kernel:0' shape=(784, 128) dtype=float32>,
 <tf.Variable 'dense/bias:0' shape=(128,) dtype=float32>,
 <tf.Variable 'dense_1/kernel:0' shape=(128, 10) dtype=float32>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32>]
test_loss, test_acc = model.evaluate(test_images_scaled, test_labels)

print('Test accuracy:', test_acc)
10000/10000 [==============================] - 0s 34us/sample - loss: 0.3489 - acc: 0.8740
Test accuracy: 0.874
predictions = model.predict(test_images_scaled)
predictions[0], test_labels[0]
(array([1.3787793e-05, 5.9650089e-09, 1.9482790e-07, 1.8630770e-09,
        3.6141572e-07, 3.6579393e-02, 1.0138750e-05, 1.5758899e-01,
        1.9775856e-04, 8.0560941e-01], dtype=float32), 9)
def plot_image(i, predictions_array, true_label, img):
  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  
  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'
  
  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  predictions_array, true_label = predictions_array[i], true_label[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1]) 
  predicted_label = np.argmax(predictions_array)
 
  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')
i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()
thisplot = plt.bar(range(5), [.1, .2, .3, .4, .9], color="#777777")
thisplot[2].set_color('orange')
# Plot the first X test images, their predicted label, and the true label
# Color correct predictions in blue, incorrect predictions in red
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions, test_labels, test_images)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions, test_labels)
plt.show()
# For fun, I want to try and run the model on my own hand drawn images.
# Raw im
import IPython.display as ipd # import Image

# These four pngs were created w/ Adobe sketchbook
# Then I manually cropped them with macOS Preview to make sure they are squares.
rawnames = ['2019-05-13 14.29.04.png',
                 '2019-05-13 14.30.20.png',
                 '2019-05-13 14.31.18.png',
                 '2019-05-13 14.32.27.png']
filenames = [
        'keras-fashion-helloworld/myimages-originals/' + x
        for x in rawnames
    ]

[ipd.display(ipd.Image(filename=fn))
    for fn in filenames]
# ipd.Image(filename='keras-fashion-helloworld/myimages-originals/2019-05-13 14.29.04--8.png')
[None, None, None, None]
# with the help of PIL, obtained with `pip install Pillow`
from PIL import Image

def extract_vec(img):
    # re-scale to 28x28 in place
    img.thumbnail((28, 28), Image.ANTIALIAS)
    
    values = list(img.getdata())
    
    pixels = np.array([x[0] for x in values])
    
    return np.resize(pixels, (28, 28))
    
    
    
# quick example of first image, 
print(extract_vec(Image.open(filenames[0])))
[[254 250 240 238 242 255 255 252 247 232 226 226 228 229 228 229 228 234
  244 246 245 247 248 255 255 254 255 255]
 [244 223 195 187 170 143 137 141 139 155 172 171 185 194 190 186 183 189
  121 101 110 113 104 147 205 242 249 255]
 [226 195 148  86  26  13  29  52  65  78  71  89 103 114 115 104  92  74
   61  99 103  99  82  58  67 148 224 248]
 [202 117  52  47  53  61  86  88  91  96  91  84  79  79  75  74  78  71
  111 120 122 129 115  99  94  79 109 218]
 [104  59  84  97  67  56  54  50  65  68  72  82  89  94  93  97 115 109
  125 115  92  94  82  69  70  89  89 112]
 [ 89 139 127  79  58  53  61  48  55  63  52  51  59  63  64  69  79  86
  110 119  91  75  79  71  69  72 110 138]
 [159  77 115 106  76  68  74  57  53  55  40  41  52  58  57  60  65  68
   88 101  97 100  92  84  94  98 107 117]
 [213 149  62 100  97  76  70  58  61  84  80  43  56  67  62  53  60  55
   57  70  94 108 107 112 107 119  90 148]
 [216 198 126  43 102 100  62  69  84  88 102  59  57  68  58  55  64  66
   64  61  88  98 104 137 131  99 115 179]
 [248 226 208 110  61  62  47  55  86  69  55  57  64  67  57  49  67  82
   76  66  87  87  67 106 129 108 169 199]
 [255 254 249 214  51  89 159  94  69  57  43  45  58  53  37  33  44  48
   51  50  68  74 125 158  71 130 212 231]
 [255 255 255 255 212 165 223  91  51  45  42  39  40  39  45  47  37  41
   42  41  58  63 136 235 209 200 236 252]
 [255 255 255 255 255 242 189  69  53  59  52  37  27  35  58  59  47  45
   36  34  55  60 139 237 242 248 252 254]
 [255 255 255 254 254 247 157  61  71  76  60  33  26  27  42  55  58  45
   36  32  55  62 130 245 252 254 254 255]
 [255 255 255 254 255 237 154  67  79  64  46  28  16  18  36  52  48  36
   29  37  58  56 114 240 255 254 255 255]
 [255 255 255 254 255 245 162  67  62  52  40  24  12  11  24  36  33  23
   25  32  52  58 113 232 255 255 255 255]
 [255 255 255 255 255 249 185  70  47  37  33  30  20  12  12  19  17  13
   17  23  50  56 106 227 254 255 255 255]
 [255 255 255 255 255 252 192  77  49  38  33  31  18   9   6  10  12   9
   11  22  56  52  98 219 250 255 255 255]
 [255 255 255 255 255 254 212  86  53  38  38  34  14   5   5   8   7   7
   14  25  59  58  77 209 247 255 255 255]
 [255 255 255 255 254 255 248 119  35  23  24  21  11   5   6  10  10  11
   16  35  72  69  62 205 251 255 255 255]
 [255 255 255 255 255 253 255 155  37  23  10   9   7   6  12  17  14  12
   20  45  73  65  67 205 251 255 255 255]
 [255 255 255 255 255 254 255 155  53  43  25  23  19  17  23  27  19  20
   27  47  76  78  73 208 252 255 255 255]
 [255 255 255 255 254 255 245 152  73  64  33  29  28  31  44  43  33  36
   43  57  78  89  75 216 254 255 255 255]
 [255 255 255 255 255 255 239 155  68  76  41  26  25  45  62  63  49  51
   63  76  92  97  79 211 252 255 255 255]
 [255 255 255 255 255 255 239 159  69  75  55  32  36  57  69  72  64  63
   82  94 114 116  99 223 251 255 255 255]
 [255 255 255 255 255 255 240 165  89 103  71  52  60  74  75  76  80  88
  124 134 134 143 114 233 252 255 255 255]
 [255 255 255 255 255 255 253 184  85 107  82  68  74  93  94  91  93 109
  149 163 137 102  79 239 255 254 255 255]
 [255 255 255 255 255 255 255 235 175 138 106  93  84  81  78  71  71  75
   91 102 125 155 175 255 254 255 255 255]]
filenames[0]
'keras-fashion-helloworld/myimages-originals/2019-05-13 14.29.04.png'
# 
my_clothing_vecs = np.array([extract_vec(Image.open(fn)) for fn in filenames])

# and so lets try and display them with matplot lib again like the provided images...
plt.figure(figsize=(10,10))
for i in range(len(my_clothing_vecs)):
    plt.subplot(2, 2, i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(my_clothing_vecs[i]) # , cmap=plt.cm.binary
    plt.xlabel(rawnames[i])
plt.show()
my_clothing_vecs.shape
(4, 28, 28)
# Ah crap, this looks inverted compared to what I see in the tutorial. 
# Going to attempt to invert that real quick...

invert255 = lambda x: abs(255 - x) 

def invert_many(img_vec):
    input_shape = img_vec.shape
    length = input_shape[0] * input_shape[1]
    
    invert_pxl = lambda x: abs(255 - x) 
    
    return np.resize(
        [invert_pxl(x)
         for x in np.resize(img_vec, (1, length))],
        
                     input_shape)
    
# invert them all
clothing_vecs_inverted = np.vectorize(invert255)(my_clothing_vecs)
my_clothing_vecs.shape, clothing_vecs_inverted.shape
((4, 28, 28), (4, 28, 28))
# Okay cool... now this looks like the other earlier data.

plt.figure(figsize=(10,10))
for i in range(len(clothing_vecs_inverted)):
    plt.subplot(2, 2, i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(clothing_vecs_inverted[i]) # , cmap=plt.cm.binary
    plt.xlabel(rawnames[i])
plt.show()
# Now lets see what the predictions say

# First the scaling...
my_scaled_and_inverted = clothing_vecs_inverted/255.0

newpredictions = model.predict(my_scaled_and_inverted)
print(list(enumerate(class_names)))
[(0, 'T-shirt/top'), (1, 'Trouser'), (2, 'Pullover'), (3, 'Dress'), (4, 'Coat'), (5, 'Sandal'), (6, 'Shirt'), (7, 'Sneaker'), (8, 'Bag'), (9, 'Ankle boot')]
my_test_labels = np.array([0, 7, 1, 4])
newpredictions
array([[4.26309437e-01, 2.92650770e-06, 1.60550058e-04, 9.47394001e-04,
        1.30431754e-05, 1.05808425e-07, 5.70735812e-01, 5.16380161e-10,
        1.83065375e-03, 7.26391747e-09],
       [2.88550858e-03, 3.26211921e-05, 8.07017728e-04, 7.98632973e-05,
        3.09603085e-04, 7.77611732e-01, 1.04915572e-03, 1.50996149e-01,
        6.37709629e-03, 5.98511957e-02],
       [1.78965013e-02, 8.74079406e-01, 4.85132486e-02, 3.52283381e-03,
        3.91321108e-02, 1.26371131e-04, 1.65334735e-02, 1.32581690e-07,
        1.95919056e-04, 2.40851268e-08],
       [6.74940348e-02, 6.74244831e-04, 7.51323474e-04, 2.22629853e-04,
        3.27958390e-02, 3.78045649e-03, 6.39505684e-01, 7.55926128e-04,
        1.57964438e-01, 9.60554630e-02]], dtype=float32)
# Plot the first X test images, their predicted label, and the true label
# Color correct predictions in blue, incorrect predictions in red
num_rows = 2
num_cols = 2
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, newpredictions, my_test_labels, my_scaled_and_inverted)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, newpredictions, my_test_labels)
plt.show()
# Wow, that looks pretty good. 
# - Haha apparently the Tshirt I drew looks a bit more like a "Shirt"
# - And my coat looks like a "Shirt" . Of course that was expected. I have no idea how that distinction 
#     is captured by the model
# - And yea, my sneaker looks kind of like a sandal? Okay makes sense.