{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nimport tensorflow as tf\nimport random\nimport zipfile\nimport os\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Unzip and extract data ","metadata":{}},{"cell_type":"code","source":"\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats/train.zip', 'r') as train_zip:\n    train_zip.extractall('.')  \nimage_dir = \"../working/train/\"\nfilenames = os.listdir(image_dir)\nlabels = [x.split(\".\")[0] for x in filenames]\ndata = pd.DataFrame({\"filename\": filenames, \"label\": labels})\ndata.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check for data imabalance and number of categories","metadata":{}},{"cell_type":"code","source":"data['label'].value_counts()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Data is balanced and has only two categories.","metadata":{}},{"cell_type":"markdown","source":"# Visualize the data for each category","metadata":{}},{"cell_type":"code","source":"\ngrouped_data = data.groupby(\"label\")\n\nnum_images_per_category = 5\n\nfig, axes = plt.subplots(len(grouped_data), num_images_per_category, figsize=(20, 20))\n\nfor i, (category, group) in enumerate(grouped_data):\n  \n    random_indices = random.sample(range(len(group)), num_images_per_category)\n\n    for j, index in enumerate(random_indices):\n        filename = group.iloc[index][\"filename\"]\n        label = group.iloc[index][\"label\"]\n        image_path = os.path.join(image_dir, filename)\n        image = Image.open(image_path)\n\n        axes[i, j].imshow(image)\n        axes[i, j].set_title(\"Label: \" + label, fontsize = 30)\n        \n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Images are of varying size.\n\nEvery time the cell is run different set of images will be displayed, one can scan the images of both categories. Presence of humans in some images could be a challenge for the model to classify. \n\nSome images have more than one cats or dogs respectively.\n","metadata":{}},{"cell_type":"markdown","source":"# Find median size of image for model building","metadata":{}},{"cell_type":"code","source":" \nimage_paths = [os.path.join(image_dir, filename) for filename in os.listdir(image_dir)]\n\nimage_width = []\nimage_height = []\nfor image_path in image_paths:\n    image = cv2.imread(image_path)\n    height, width, _ = image.shape\n    image_width.append(width)\n    image_height.append(height)\n\nmedian_width = np.median(image_width)\nmedian_height = np.median(image_height)\n\n\nprint('median_size:', median_width,  'X', median_height)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Since CNN models can process square images thus setting the image size for processing as \n370 X 370. ","metadata":{}},{"cell_type":"markdown","source":"# Create train, validation and test datasets","metadata":{}},{"cell_type":"code","source":"labels = data['label']\nX_train, X_temp = train_test_split(data, test_size=0.2, stratify=labels, random_state = 23)\n\nlabel_test_val = X_temp['label']\nX_test, X_val = train_test_split(X_temp, test_size=0.5, stratify=label_test_val, random_state = 23)\n\nprint ('X_train:', X_train['label'].value_counts())\n\nprint ('X_val:', X_val['label'].value_counts())\n\nprint ('X_test:', X_test['label'].value_counts())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"All three splits of data has both categories in the same proportion (equally distributed) as that in the original data. ","metadata":{}},{"cell_type":"markdown","source":"# Create image data generator","metadata":{}},{"cell_type":"code","source":"batch_size = 64\nsize = (370, 370)\nidg = tf.keras.preprocessing.image.ImageDataGenerator(preprocessing_function = tf.keras.applications.vgg16.preprocess_input)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_idg = idg.flow_from_dataframe(X_train, \"train/\", x_col= \"filename\", y_col= \"label\",\n                                    batch_size = batch_size,\n                                    target_size=size)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_idg = idg.flow_from_dataframe(X_val, \"train/\", x_col=\"filename\", y_col=\"label\",\n                                  batch_size = batch_size,\n                                  target_size = size, shuffle = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_idg = idg.flow_from_dataframe(X_test, \"train/\", x_col= \"filename\", y_col= \"label\",\n                                    batch_size = batch_size,\n                                    target_size=size, shuffle = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Set shuffle = False for validation and test dataset. By setting shuffle=False, the samples will be processed in the same order as they appear in the dataframe.","metadata":{}},{"cell_type":"markdown","source":"# Download vgg16 model and freeze the weights","metadata":{}},{"cell_type":"code","source":"vgg16_model = tf.keras.applications.vgg16.VGG16(include_top=False, input_shape=(370, 370, 3))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"vgg16_model.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for layer in vgg16_model.layers:\n  layer.trainable = False","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"By setting layer.trainable = False, freezing the weights of all the layers in the VGG16 model.The vgg16 derived weights will not be trained during the training of final model.","metadata":{}},{"cell_type":"markdown","source":"# Define functional model","metadata":{}},{"cell_type":"code","source":"flat = tf.keras.layers.Flatten() (vgg16_model.output)\ndropout1 = tf.keras.layers.Dropout(0.2, name=\"Dropout1\") (flat)\ndense1 = tf.keras.layers.Dense(128, activation=\"relu\") (dropout1)\ndropout2 = tf.keras.layers.Dropout(0.2, name=\"Dropout2\")(dense1)\noutput = tf.keras.layers.Dense(2, activation=\"softmax\") (dropout2)\n\nfinal_model = tf.keras.models.Model(inputs=[vgg16_model.input], outputs=[output])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plot the final model","metadata":{}},{"cell_type":"code","source":"tf.keras.utils.plot_model(final_model, show_shapes = True, show_layer_names=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Compile the model","metadata":{}},{"cell_type":"code","source":"final_model.compile(optimizer='adam',\n                    loss=tf.keras.losses.categorical_crossentropy,\n                    metrics = [\"acc\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create callback","metadata":{}},{"cell_type":"code","source":"model_ckpt = tf.keras.callbacks.ModelCheckpoint(\"DogCat\",\n                                                monitor=\"val_loss\",\n                                                save_best_only=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The ModelCheckpoint callback will save the best model during training based on the validation loss. \n\nThe saved model will be stored with the prefix \"DogCat\".","metadata":{}},{"cell_type":"markdown","source":"# Train the model","metadata":{}},{"cell_type":"code","source":"history = final_model.fit(train_idg, batch_size=batch_size, validation_data=val_idg, epochs = 8, callbacks=[model_ckpt])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Evaluate accuracy and loss for the model","metadata":{}},{"cell_type":"code","source":"acc = history.history['acc']\nval_acc = history.history['val_acc']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs_range = range(8)\n\nplt.figure(figsize=(8, 8))\nplt.subplot(1, 2, 1)\nplt.plot(epochs_range, acc, label='Training Accuracy')\nplt.plot(epochs_range, val_acc, label = 'Validation Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(1, 2, 2)\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Validation loss start increasing after 3 epochs.","metadata":{}},{"cell_type":"markdown","source":"# Check the accuracy of the model on test subset","metadata":{}},{"cell_type":"code","source":"cat_dog_model = tf.keras.models.load_model(\"DogCat\")\n\nresult = cat_dog_model.predict(test_idg)\n\nresult_argmax = np.argmax(result, axis=1)\n\ny_true = test_idg.labels\n\ny_pred = result_argmax\n\naccuracy = (y_pred == y_true).mean()\n\nprint(\"Test Accuracy:\", accuracy)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Vgg16 rocks! 98% accuracy  in our test subset that we split from training data.","metadata":{}},{"cell_type":"code","source":"print(classification_report(y_true, y_pred))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Both the classes are classified well.","metadata":{}},{"cell_type":"markdown","source":"# Make predictions on kaggle test data for submission.","metadata":{}},{"cell_type":"markdown","source":"Unzip and extract test data","metadata":{}},{"cell_type":"code","source":"\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats/test1.zip', 'r') as test1_zip:\n    test1_zip.extractall('.')  \ntest_dir = \"../working/test1/\"\nfilenames = os.listdir(test_dir)\ntest_data = pd.DataFrame({\"filename\": filenames})\ntest_data['label'] = 'unknown'\ntest_data.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Create data genenerator for test data","metadata":{}},{"cell_type":"code","source":"\ntest1_idg =  idg.flow_from_dataframe(test_data, \"test1/\", x_col= \"filename\",y_col = 'label',\n                                    batch_size = batch_size,\n                                    target_size=size, shuffle = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Make predictions on test data","metadata":{}},{"cell_type":"code","source":"test1_predict = cat_dog_model.predict(test1_idg)\n\ntest1_predict_argmax = np.argmax(test1_predict, axis=1)\n\ny_test_pred = test1_predict_argmax\n\ntest_data['label'] = y_test_pred\n\ntest_data.head()\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_idg.class_indices","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_mapping = {0: 'cat', 1: 'dog'}\ntest_data['label'] = test_data['label'].map(label_mapping)\ntest_data.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Visualize the predicted results","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 5, figsize=(20, 4))\nfor idx in range(5):\n    image_path = os.path.join(test_dir, test_data.iloc[idx]['filename'])\n    image = Image.open(image_path)\n    axes[idx].imshow(image)\n    axes[idx].set_title(\"Label: \" + test_data.iloc[idx]['label'])\n    axes[idx].axis('off')\nplt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Made quite well predictions!","metadata":{}},{"cell_type":"markdown","source":"If you find the notebook helpful - Upvote","metadata":{}}]}