{"cells":[{"metadata":{},"cell_type":"markdown","source":"#### This is another well known dataset to do hands on *Image Classification * using Convolutional Neural Network (CNN).\n#### CNN as a subset of *Deep Learning* uses *Convolution* instead of linear matrix operation. *Convolution* is a mathematical operation between 2 functions f(x) and g(x) expressing how the shape of one is modified by the other as f *o* g(x).\n\n#### Import the libraries.","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd \nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport os","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### See the files in the input directory.","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"print(os.listdir(\"../input/dogs-vs-cats/\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"os.listdir(\"../input/dogs-vs-cats/\")[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Unzip the 'train' zip file into a 'Temp' folder in /kaggle/working directory.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from zipfile import ZipFile\nzf = ZipFile('../input/dogs-vs-cats/train.zip', 'r')\nzf.extractall('../kaggle/working/Temp')\nzf.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check whether the Unzip has worked.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Commented to reduce display...\n#print(os.listdir(\"../kaggle/working/Temp/train\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### The filename of a cat's image will start with 'cat' and a dog's image will start with 'dog'. So using this feature create a dataframe with file names and their categories.\n#### I took help for the following code block from https://www.kaggle.com/uysimty/keras-cnn-dog-or-cat-classification.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames = os.listdir(\"../kaggle/working/Temp/train\")\ncategories = []\nfor filename in filenames:\n    category = filename.split('.')[0]\n    if category == 'dog':\n        categories.append('dog')\n    else:\n        categories.append('cat')\n\ndf = pd.DataFrame({\n    'filename': filenames,\n    'category': categories\n})","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### First few rows of the dataframe.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check the distibution of categories.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"df['category'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.countplot(x='category', data=df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### We can see that there are same number of images of each category.\n#### We will now see a sample image from the 'train' set. Since each image has different dimension we will use a standard dimension of 128x128 which is a reduced version of the images' actual dimension.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nimg = image.load_img(\"../kaggle/working/Temp/train/\"+filenames[0])\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_image = image.load_img(\"../kaggle/working/Temp/train/\"+filenames[0], \n                            target_size=(128, 128))\ntest_image = image.img_to_array(test_image)\nplt.imshow(test_image[:, :, 2])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Create a *validation set* with 20% images from the 'train' set.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_data, val_data = train_test_split(df, test_size=0.20, random_state=42)\ntrain_data = train_data.reset_index(drop=True)\nval_data   = val_data.reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check first few lines from train dataset.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check first few lines from validation dataset.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"val_data.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check the distribution of category in train dataset.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data['category'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.countplot(x='category', data=train_data)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check the distribution of category in validation dataset.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"val_data['category'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.countplot(x='category', data=val_data)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### A CNN will have multiple layers of Convolution layers and then it will be fed into a fully connected network.\n\nIn our case we will have 2 layers of **Convolution Layers** and each will have below features -\n\n1. **Filters**: The number of output filters in the convolution\n2. **Kernel Size**: The height and width of the convolution window\n3. **Strides**: The stride of the convolution\n4. **Input Shape**: The first Convolution layer will have input shape of 128x128x3 (128x128 is the image size and 3 specifies the channel as 'RGB')\n\nThen we have **Batch Normalization** and **Dropout** as measure to prevent over-fitting and increase balance.\n\n**Max Pooling** reduces the dimension of the cluster from one layer to the next by using the maximum value.\n\n**Flatten** is used to change the dimension so that the output of Convolutional layer can be fed into a fully connected layer.\n\nWe are going to use **ImageDataGenerator** to preprocess the images.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import Convolution2D\nfrom keras.layers import MaxPooling2D\nfrom keras.layers import Flatten, BatchNormalization\nfrom keras.layers import Dense, Dropout\nfrom keras.preprocessing.image import ImageDataGenerator","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Build the model.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"classifier = Sequential([Convolution2D(filters=32, kernel_size=(3, 3), strides=(1, 1), input_shape=(128,128,3),\n                            padding='valid', activation='relu'),\n                         BatchNormalization(),\n                         MaxPooling2D(pool_size=(2, 2)),\n                         Dropout(0.2),\n                         Convolution2D(filters=32, kernel_size=(3, 3), strides=(1, 1),\n                            padding='valid', activation='relu'),\n                         BatchNormalization(),\n                         MaxPooling2D(pool_size=(2, 2)),\n                         Dropout(0.2),\n                         Flatten(),\n                         Dense(512, activation='relu'),\n                         BatchNormalization(),\n                         Dropout(0.25),\n                         Dense(2, activation='softmax')])\n\nclassifier.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n\nclassifier.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Build the train_generator.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator(rescale=1./255,\n                                   shear_range=0.2,\n                                   zoom_range=0.2,\n                                   horizontal_flip=True)\n\ntrain_generator = train_datagen.flow_from_dataframe(\n        train_data,\n        \"../kaggle/working/Temp/train/\",\n        x_col='filename',\n        y_col='category',\n        target_size=(128, 128),\n        batch_size=32,\n        class_mode='categorical')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Build the validation_generator.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"val_datagen = ImageDataGenerator(rescale=1./255)\n\nval_generator = val_datagen.flow_from_dataframe(\n        val_data,\n        \"../kaggle/working/Temp/train/\",\n        x_col='filename',\n        y_col='category',\n        target_size=(128, 128),\n        batch_size=32,\n        class_mode='categorical')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Early Stopping and Checkpoint","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import EarlyStopping\nfrom keras.callbacks import ModelCheckpoint\n\nes = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=30)\nmc = ModelCheckpoint('best_model.h5', monitor='val_accuracy', mode='max', verbose=1, save_best_only=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Fit the data into model.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"history=classifier.fit_generator(train_generator,\n                                steps_per_epoch=625,\n                                epochs=50,\n                                validation_data=val_generator,\n                                validation_steps=200,\n                                callbacks=[es, mc])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Plotting model accuracy.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"history.history","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'], '')\nplt.xlabel(\"Epochs\")\nplt.ylabel('Accuracy')\nplt.title('Change of Accuracy over Epochs')\nplt.legend(['accuracy', 'val_accuracy'])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Plotting model loss.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'], '')\nplt.xlabel(\"Epochs\")\nplt.ylabel('Loss')\nplt.title('Change of Loss over Epochs')\nplt.legend(['loss', 'val_loss'])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### We have specified y_col='category' and class_mode='categorical'. So the 'ImageDataGenerator' will convert the 'category' column into 2D one-hot-encoded matrix and we can see the value assigned to each class of the 'category' column through 'class_indices'.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator.class_indices","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Unzip the 'test' set.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from zipfile import ZipFile\nzf = ZipFile('../input/dogs-vs-cats/test1.zip', 'r')\nzf.extractall('../kaggle/working/Temp')\nzf.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Check whether the Unzip has worked.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Commented to reduce display...\n#print(os.listdir(\"../kaggle/working/Temp/test1\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Create a dataframe for the test data.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames = os.listdir(\"../kaggle/working/Temp/test1\")\n\ntest_data = pd.DataFrame({\n    'filename': filenames\n})","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Load the best model.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import load_model\n\nsaved_model = load_model('best_model.h5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Use one sample image from test set and predict its class.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"img = image.load_img(\"../kaggle/working/Temp/test1/\"+filenames[29])\n                            \ntest_image = image.load_img(\"../kaggle/working/Temp/test1/\"+filenames[29], \n                            target_size=(128, 128))\ntest_image = image.img_to_array(test_image)\nplt.imshow(img)\ntest_image = np.expand_dims(test_image, axis=0)\nresult = saved_model.predict(test_image)\nprint(np.argmax(result, axis=1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### We can see the class of the image is correctly predicted as '1' which means 'dog'.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"img = image.load_img(\"../kaggle/working/Temp/test1/\"+filenames[39])\n                            \ntest_image = image.load_img(\"../kaggle/working/Temp/test1/\"+filenames[39], \n                            target_size=(128, 128))\ntest_image = image.img_to_array(test_image)\nplt.imshow(img)\ntest_image = np.expand_dims(test_image, axis=0)\nresult = saved_model.predict(test_image)\nprint(np.argmax(result, axis=1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### We can see the class of the image is correctly predicted as '0' which means 'cat'.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"#### Preprocess the images from test set.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"test_datagen = ImageDataGenerator(rescale=1./255)\n\ntest_generator = test_datagen.flow_from_dataframe(\n        test_data,\n        \"../kaggle/working/Temp/test1/\",\n        x_col='filename',\n        y_col=None,\n        target_size=(128, 128),\n        batch_size=32,\n        class_mode=None)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Predict the classes of all the images from test set.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"predict = saved_model.predict_generator(test_generator)\nfinal_prediction = np.argmax(predict, axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Create the submission file.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"predict_df = pd.DataFrame(final_prediction, columns=['label'])\nsubmission_df = test_data.copy()\nsubmission_df['id'] = (submission_df['filename'].str.split('.').str[0]).astype(int)\nsubmission_df = pd.concat([submission_df, predict_df], axis=1)\nsubmission_df = submission_df.drop(['filename'], axis=1)\nsubmission_df = submission_df.sort_values(by=['id'])\nsubmission_df = submission_df.reset_index(drop=True)\nsubmission_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}