{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"# Importing libraries\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nsns.set_style()\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Importing MNIST dataset\ndf_train = pd.read_csv('../input/digit-recognizer/train.csv')\ndf_test = pd.read_csv('../input/digit-recognizer/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = df_train['label']\ny_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train = df_train.drop('label', axis=1)\nx_train = x_train.values\nx_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reshaping array to visualize the images\nplt.imshow(x_train[0].reshape(28,28), cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train = x_train.reshape(42000,28,28)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nx_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train, test_size=0.1, random_state=19)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_valid.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Note:** The targets in y_train and y_test are simply numerical values. We need to perform one-hot encoding for our model as this is a multi-class classification problem."},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.utils import to_categorical\ny_cat_valid = to_categorical(y_valid, num_classes=10)\ny_cat_train = to_categorical(y_train, num_classes=10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Sampling first image visually\nfirst_image = x_train[0]\nplt.imshow(first_image, cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Actual image:')\nprint(y_train[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"first_image.max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"first_image.min()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Note:** We can simply divide the training and test data by the maximum value of 255 to scale the data."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Scaling values on train and test data\nx_train_scaled = x_train/255\nx_valid_scaled = x_valid/255","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Rechecking image after scaling\nplt.imshow(x_train_scaled[0], cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reshaping final training and testing data to prep for training\n# (batch_size, width, height, color_channels)\nx_train_final = x_train_scaled.reshape(37800,28,28,1)\nx_valid_final = x_valid_scaled.reshape(4200,28,28,1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Building the CNN Model\n- This models consists of only one convolution layer followed by a pooling layer to speed up computation while still retaining the meaningful information of the data.\n- The image was flattened and then fed into a dense layer followed by the output layer.\n- A softmax activation function was used for the output layer as this is a multi-class classification problem."},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Conv2D, MaxPool2D, Flatten, Dropout","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Building CNN Model\n\n# Instantiate model\nmodel = Sequential()\n\n# Convolution layer 1\nmodel.add(Conv2D(filters=32, kernel_size=(5,5), strides=(1,1), input_shape=(28,28,1), padding='Same', activation='relu'))\n# Convolution layer 2\nmodel.add(Conv2D(filters=32, kernel_size=(5,5), strides=(1,1), input_shape=(28,28,1), padding='Same', activation='relu'))\n# Pooling layer (selected half of kernel_size)\nmodel.add(MaxPool2D(pool_size=(2,2)))\nmodel.add(Dropout(0.25))\n\n# Convolution layer 3\nmodel.add(Conv2D(filters=32, kernel_size=(3,3), strides=(1,1), input_shape=(28,28,1), padding='Same', activation='relu'))\n# Convolution layer 4\nmodel.add(Conv2D(filters=32, kernel_size=(3,3), strides=(1,1), input_shape=(28,28,1), padding='Same', activation='relu'))\n# Pooling layer (selected half of kernel_size)\nmodel.add(MaxPool2D(pool_size=(2,2)))\nmodel.add(Dropout(0.25))\n\n# Flattening image\nmodel.add(Flatten())\n# Dense layer\nmodel.add(Dense(256, activation='relu'))\n\n# Output layer\nmodel.add(Dense(10, activation='softmax'))\n\n# Compiling model\nmodel.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"early_stop = EarlyStopping(monitor='val_loss', patience=2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_gen = ImageDataGenerator(rotation_range=10,\n                              width_shift_range=0.1,\n                              height_shift_range=0.1,\n                              shear_range=0.1,\n                              zoom_range=0.1,\n                              fill_mode='nearest')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_image_gen = image_gen.fit(x_train_final)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_image_gen","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit_generator(image_gen.flow(x_train_final, y_cat_train), epochs=10, validation_data=(x_valid_final, y_cat_valid), callbacks=[early_stop])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model Evaluation\n- The loss of the training and validation sets were plotted to ensure we are not overfitting our model\n- Accuracy of the training and validation data were also plotted\n- Overall accuracy was obtained from a classification report and confusion matrix"},{"metadata":{"trusted":true},"cell_type":"code","source":"metrics = pd.DataFrame(model.history.history)\nmetrics","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"metrics[['loss', 'val_loss']].plot()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"metrics[['accuracy', 'val_accuracy']].plot()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.evaluate(x_valid_final, y_cat_valid, verbose=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_pred = model.predict_classes(x_valid_final)\ny_pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Model evaluation\nfrom sklearn.metrics import classification_report, confusion_matrix\nprint('Classification Report:')\nprint(classification_report(y_valid, y_pred))\nprint('\\n')\nprint('Confusion Matrix:')\nprint(confusion_matrix(y_valid, y_pred))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Our model performs quite well based on the above evaluation, resulting in an overall accuracy of 99%."},{"metadata":{},"cell_type":"markdown","source":"# Sample Predictions\n- Random samples from the testing set were selected and our model was used to predict each sample target.\n- The actual image is displayed followed by the prediction."},{"metadata":{"trusted":true},"cell_type":"code","source":"np.random.seed(19)\nrandom_selection = np.random.randint(0, 4201, size=1)\nrandom_sample = x_valid_final[random_selection]\nplt.imshow(random_sample.reshape(28,28), cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Prediction:')\nprint(model.predict_classes(random_sample.reshape(1,28,28,1))[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"np.random.seed(20)\nrandom_selection_2 = np.random.randint(0, 4201, size=1)\nrandom_sample_2 = x_valid_final[random_selection_2]\nplt.imshow(random_sample_2.reshape(28,28), cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Prediction:')\nprint(model.predict_classes(random_sample_2.reshape(1,28,28,1))[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"np.random.seed(22)\nrandom_selection_3 = np.random.randint(0, 4201, size=1)\nrandom_sample_3 = x_valid_final[random_selection_3]\nplt.imshow(random_sample_3.reshape(28,28), cmap='Greys')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Prediction:')\nprint(model.predict_classes(random_sample_3.reshape(1,28,28,1))[0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generating Predictions from Test Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reshaping test data\nx_test = df_test.values\nx_test = x_test.reshape(28000,28,28)\nx_test.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Scaling test data\nx_test_scaled = x_test/255","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Generating preditions\ntest_predictions = model.predict_classes(x_test_scaled.reshape(28000,28,28,1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save test predictions to file\noutput = pd.DataFrame({'ImageId': df_test.index + 1,\n                       'Label': test_predictions})\noutput.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python [conda env:mytfenv]","language":"python","name":"conda-env-mytfenv-py"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.5"}},"nbformat":4,"nbformat_minor":1}