MNIST Data digit recognizer model.

Import required libraries to work with data and build our model

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as img
import seaborn as sns
%matplotlib inline

np.random.seed(0)

from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
import itertools

from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ReduceLROnPlateau


sns.set(style='white', context='notebook', palette='deep')

Load the data

In [2]:
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

Have a look at the data

In [3]:
train.head()
Out[3]:
label pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 ... pixel774 pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783
0 1 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 1 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 4 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

5 rows × 785 columns

In [4]:
test.head()
Out[4]:
pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 pixel9 ... pixel774 pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783
0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

5 rows × 784 columns

In [5]:
y_train = train['label']

X_train = train.drop(['label'], axis=1)

sns.countplot(y_train)

y_train.value_counts()
Out[5]:
1    4684
7    4401
3    4351
9    4188
2    4177
6    4137
0    4132
4    4072
8    4063
5    3795
Name: label, dtype: int64

Normalizind the data before feeding in CNN, and also reshape the data

In [6]:
# Normalize the data
X_train = X_train / 255.0
test = test / 255.0
In [7]:
# Reshape image in 3 dimensions(height = 28px, width =28px, canal=1)

X_train = X_train.values.reshape(-1, 28, 28, 1)
test = test.values.reshape(-1, 28, 28, 1)
In [8]:
# Encode labels to one hot vectors
y_train = to_categorical(y_train, num_classes=10)
In [9]:
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.1, random_state=0)

Create Model

In [10]:
model = Sequential()

model.add(Conv2D(filters = 32, kernel_size = (5,5),
                activation='relu', input_shape= (28,28,1)))

model.add(Conv2D(filters = 32, kernel_size = (5,5),
                activation='relu', input_shape= (28,28,1)))

model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Conv2D(filters = 64, kernel_size = (3, 3),
                activation='relu'))

model.add(Conv2D(filters = 64, kernel_size = (3,3),
                activation='relu'))

model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(256, activation = 'relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
In [11]:
optimizer = RMSprop(lr=0.001, rho=0.9)
In [12]:
model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
In [13]:
learning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy',
                                           patience=3,
                                           verbose=1,
                                           factor=0.5,
                                           min_lr=0.00001)
In [14]:
epochs = 30
batch_size = 86
In [15]:
datagen = ImageDataGenerator(
        rotation_range=10,
        zoom_range=0.1,
        width_shift_range=0.1,
        height_shift_range=0.1)
In [16]:
datagen.fit(X_train)
In [17]:
history = model.fit(datagen.flow(X_train, y_train, batch_size=batch_size),
                   epochs=epochs, validation_data=(X_val, y_val),
                   verbose=2, steps_per_epoch=X_train.shape[0]//batch_size,
                   callbacks=[learning_rate_reduction])
Epoch 1/30
439/439 - 104s - loss: 0.5867 - accuracy: 0.8071 - val_loss: 0.0863 - val_accuracy: 0.9740
Epoch 2/30
439/439 - 85s - loss: 0.1747 - accuracy: 0.9482 - val_loss: 0.0434 - val_accuracy: 0.9879
Epoch 3/30
439/439 - 85s - loss: 0.1213 - accuracy: 0.9642 - val_loss: 0.0344 - val_accuracy: 0.9879
Epoch 4/30
439/439 - 84s - loss: 0.0922 - accuracy: 0.9722 - val_loss: 0.0371 - val_accuracy: 0.9876
Epoch 5/30
439/439 - 85s - loss: 0.0826 - accuracy: 0.9755 - val_loss: 0.0330 - val_accuracy: 0.9914
Epoch 6/30
439/439 - 85s - loss: 0.0785 - accuracy: 0.9775 - val_loss: 0.0255 - val_accuracy: 0.9912
Epoch 7/30
439/439 - 83s - loss: 0.0673 - accuracy: 0.9808 - val_loss: 0.0282 - val_accuracy: 0.9931
Epoch 8/30
439/439 - 84s - loss: 0.0681 - accuracy: 0.9802 - val_loss: 0.0245 - val_accuracy: 0.9926
Epoch 9/30
439/439 - 83s - loss: 0.0641 - accuracy: 0.9828 - val_loss: 0.0349 - val_accuracy: 0.9912
Epoch 10/30

Epoch 00010: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257.
439/439 - 84s - loss: 0.0649 - accuracy: 0.9835 - val_loss: 0.0388 - val_accuracy: 0.9883
Epoch 11/30
439/439 - 86s - loss: 0.0492 - accuracy: 0.9861 - val_loss: 0.0232 - val_accuracy: 0.9936
Epoch 12/30
439/439 - 85s - loss: 0.0462 - accuracy: 0.9867 - val_loss: 0.0239 - val_accuracy: 0.9931
Epoch 13/30
439/439 - 83s - loss: 0.0433 - accuracy: 0.9883 - val_loss: 0.0261 - val_accuracy: 0.9936
Epoch 14/30

Epoch 00014: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628.
439/439 - 85s - loss: 0.0453 - accuracy: 0.9878 - val_loss: 0.0287 - val_accuracy: 0.9929
Epoch 15/30
439/439 - 87s - loss: 0.0373 - accuracy: 0.9898 - val_loss: 0.0270 - val_accuracy: 0.9926
Epoch 16/30
439/439 - 84s - loss: 0.0385 - accuracy: 0.9894 - val_loss: 0.0252 - val_accuracy: 0.9936
Epoch 17/30
439/439 - 83s - loss: 0.0371 - accuracy: 0.9898 - val_loss: 0.0248 - val_accuracy: 0.9948
Epoch 18/30
439/439 - 83s - loss: 0.0349 - accuracy: 0.9899 - val_loss: 0.0243 - val_accuracy: 0.9940
Epoch 19/30
439/439 - 85s - loss: 0.0358 - accuracy: 0.9907 - val_loss: 0.0234 - val_accuracy: 0.9948
Epoch 20/30

Epoch 00020: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814.
439/439 - 83s - loss: 0.0362 - accuracy: 0.9898 - val_loss: 0.0260 - val_accuracy: 0.9936
Epoch 21/30
439/439 - 82s - loss: 0.0321 - accuracy: 0.9909 - val_loss: 0.0242 - val_accuracy: 0.9940
Epoch 22/30
439/439 - 82s - loss: 0.0343 - accuracy: 0.9909 - val_loss: 0.0296 - val_accuracy: 0.9926
Epoch 23/30

Epoch 00023: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05.
439/439 - 84s - loss: 0.0334 - accuracy: 0.9914 - val_loss: 0.0272 - val_accuracy: 0.9940
Epoch 24/30
439/439 - 85s - loss: 0.0295 - accuracy: 0.9915 - val_loss: 0.0243 - val_accuracy: 0.9945
Epoch 25/30
439/439 - 84s - loss: 0.0303 - accuracy: 0.9916 - val_loss: 0.0290 - val_accuracy: 0.9931
Epoch 26/30

Epoch 00026: ReduceLROnPlateau reducing learning rate to 3.125000148429535e-05.
439/439 - 83s - loss: 0.0297 - accuracy: 0.9913 - val_loss: 0.0273 - val_accuracy: 0.9926
Epoch 27/30
439/439 - 81s - loss: 0.0277 - accuracy: 0.9919 - val_loss: 0.0269 - val_accuracy: 0.9936
Epoch 28/30
439/439 - 82s - loss: 0.0287 - accuracy: 0.9927 - val_loss: 0.0258 - val_accuracy: 0.9936
Epoch 29/30

Epoch 00029: ReduceLROnPlateau reducing learning rate to 1.5625000742147677e-05.
439/439 - 82s - loss: 0.0314 - accuracy: 0.9920 - val_loss: 0.0251 - val_accuracy: 0.9943
Epoch 30/30
439/439 - 84s - loss: 0.0296 - accuracy: 0.9915 - val_loss: 0.0259 - val_accuracy: 0.9936

Pretty good accuracy with first model itself, let's create the submission file

In [18]:
results = model.predict(test)

results = np.argmax(results, axis=1)

results = pd.Series(results, name='Label')
In [19]:
sample_submission = pd.read_csv('sample_submission.csv')
In [20]:
sample_submission['Label'] = results
In [21]:
sample_submission.to_csv("submission.csv", index=False)
In [ ]: