MNIST Classifier

This notebook prepares an MNIST classifier using a Convolutional Neural Network (CNN)

In [1]:
# import required libs
import keras
import numpy as np
from keras import backend as K
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D

import matplotlib.pyplot as plt
params = {'legend.fontsize': 'x-large',
          'figure.figsize': (15, 5),
          'axes.labelsize': 'x-large',
          'axes.titlesize':'x-large',
          'xtick.labelsize':'x-large',
          'ytick.labelsize':'x-large'}

plt.rcParams.update(params)

%matplotlib inline
Using TensorFlow backend.

Set Parameters

In [2]:
batch_size = 128
num_classes = 10
epochs = 2

# input image dimensions
img_rows, img_cols = 28, 28

Get MNIST Dataset

In [3]:
# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

Pre-process image data

In [4]:
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
In [5]:
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

Build a CNN based deep neural network

In [6]:
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

Visualize the network architecture

In [10]:
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot

SVG(model_to_dot(model, show_shapes=True, 
                 show_layer_names=True, rankdir='TB').create(prog='dot', format='svg'))
Out[10]:
G 2532596584800 conv2d_1_input: InputLayer input: output: (None, 28, 28, 1) (None, 28, 28, 1) 2532596436720 conv2d_1: Conv2D input: output: (None, 28, 28, 1) (None, 26, 26, 32) 2532596584800->2532596436720 2532578146848 conv2d_2: Conv2D input: output: (None, 26, 26, 32) (None, 24, 24, 64) 2532596436720->2532578146848 2532596743584 max_pooling2d_1: MaxPooling2D input: output: (None, 24, 24, 64) (None, 12, 12, 64) 2532578146848->2532596743584 2532633091320 dropout_1: Dropout input: output: (None, 12, 12, 64) (None, 12, 12, 64) 2532596743584->2532633091320 2532633280128 flatten_1: Flatten input: output: (None, 12, 12, 64) (None, 9216) 2532633091320->2532633280128 2532633276992 dense_1: Dense input: output: (None, 9216) (None, 128) 2532633280128->2532633276992 2532633280352 dropout_2: Dropout input: output: (None, 128) (None, 128) 2532633276992->2532633280352 2532633315480 dense_2: Dense input: output: (None, 128) (None, 10) 2532633280352->2532633315480

Compile the model

In [8]:
model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])

Train the classifier

In [9]:
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=2,
          verbose=1,
          validation_data=(x_test, y_test))
Train on 60000 samples, validate on 10000 samples
Epoch 1/2
60000/60000 [==============================] - 232s - loss: 0.3500 - acc: 0.8927 - val_loss: 0.0904 - val_acc: 0.9715
Epoch 2/2
60000/60000 [==============================] - 235s - loss: 0.1239 - acc: 0.9642 - val_loss: 0.0584 - val_acc: 0.9817
Out[9]:
<keras.callbacks.History at 0x234331c4cc0>

Predict and test model performance

In [10]:
score = model.evaluate(x_test, y_test, verbose=1)
10000/10000 [==============================] - 11s    
In [11]:
print('Test loss:', score[0])
print('Test accuracy:', score[1])
Test loss: 0.0584113781168
Test accuracy: 0.9817

How CNN Classifies an Image?

Prepare image for CNN

In [15]:
test_image =np.expand_dims(x_test[4], axis=3)
test_image = test_image.reshape(1,img_rows, img_cols,1)

Sample Handwritten digit

In [33]:
plt.imshow(x_test[4][:,:,0], cmap=plt.get_cmap('binary'))
Out[33]:
<matplotlib.image.AxesImage at 0x2343582dbe0>

Digit Label

In [ ]:
y_test[4]

Predict the digit

In [19]:
model.predict_classes(test_image,batch_size=1)
1/1 [==============================] - 0s
Out[19]:
array([4], dtype=int64)
In [20]:
# https://github.com/fchollet/keras/issues/431
def get_activations(model, model_inputs, print_shape_only=True, layer_name=None):
    import keras.backend as K
    print('----- activations -----')
    activations = []
    inp = model.input

    model_multi_inputs_cond = True
    if not isinstance(inp, list):
        # only one input! let's wrap it in a list.
        inp = [inp]
        model_multi_inputs_cond = False

    outputs = [layer.output for layer in model.layers if
               layer.name == layer_name or layer_name is None]  # all layer outputs

    funcs = [K.function(inp + [K.learning_phase()], [out]) for out in outputs]  # evaluation functions

    if model_multi_inputs_cond:
        list_inputs = []
        list_inputs.extend(model_inputs)
        list_inputs.append(1.)
    else:
        list_inputs = [model_inputs, 1.]

    # Learning phase. 1 = Test mode (no dropout or batch normalization)
    # layer_outputs = [func([model_inputs, 1.])[0] for func in funcs]
    layer_outputs = [func(list_inputs)[0] for func in funcs]
    for layer_activations in layer_outputs:
        activations.append(layer_activations)
        if print_shape_only:
            print(layer_activations.shape)
        else:
            print(layer_activations)
    return activations
In [21]:
# https://github.com/philipperemy/keras-visualize-activations/blob/master/read_activations.py
def display_activations(activation_maps):
    import numpy as np
    import matplotlib.pyplot as plt
    """
    (1, 26, 26, 32)
    (1, 24, 24, 64)
    (1, 12, 12, 64)
    (1, 12, 12, 64)
    (1, 9216)
    (1, 128)
    (1, 128)
    (1, 10)
    """
    batch_size = activation_maps[0].shape[0]
    assert batch_size == 1, 'One image at a time to visualize.'
    for i, activation_map in enumerate(activation_maps):
        print('Displaying activation map {}'.format(i))
        shape = activation_map.shape
        if len(shape) == 4:
            activations = np.hstack(np.transpose(activation_map[0], (2, 0, 1)))
        elif len(shape) == 2:
            # try to make it square as much as possible. we can skip some activations.
            activations = activation_map[0]
            num_activations = len(activations)
            if num_activations > 1024:  # too hard to display it on the screen.
                square_param = int(np.floor(np.sqrt(num_activations)))
                activations = activations[0: square_param * square_param]
                activations = np.reshape(activations, (square_param, square_param))
            else:
                activations = np.expand_dims(activations, axis=0)
        else:
            raise Exception('len(shape) = 3 has not been implemented.')
        plt.imshow(activations, interpolation='None', cmap='binary')
        plt.show()
In [22]:
activations = get_activations(model, test_image)
----- activations -----
(1, 26, 26, 32)
(1, 24, 24, 64)
(1, 12, 12, 64)
(1, 12, 12, 64)
(1, 9216)
(1, 128)
(1, 128)
(1, 10)
In [23]:
display_activations(activations)
Displaying activation map 0
Displaying activation map 1
Displaying activation map 2
Displaying activation map 3
Displaying activation map 4
Displaying activation map 5
Displaying activation map 6
Displaying activation map 7