{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"1. Exploratory Data Analysis\n\nThere are 25,000 and 12,500 files with images of cats and dogs in the training and testing folders as specified by the kaggle competition description."},{"metadata":{"trusted":true},"cell_type":"code","source":"%%capture cell_output\n\n# unzipping train.zip \n!unzip \"../input/dogs-vs-cats/train.zip\"\n\n#rename train folder\nimport os\n\nsrc_train = os.path.join(os.getcwd(), 'src_train')\n\nos.rename(os.path.join(os.getcwd(), 'train'), src_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%capture cell_output\n\n# unzipping test1.zip \n!unzip \"../input/dogs-vs-cats/test1.zip\"\n\ntest_dir = os.path.join(os.getcwd(), 'test1')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('# of files in train folder: ', len(os.listdir(src_train)))\nprint('# of files in test folder: ', len(os.listdir(test_dir)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code displays 8 random images from the training folder."},{"metadata":{"trusted":true},"cell_type":"code","source":"#help function to display images in a grid\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\nfrom keras.preprocessing import image\n\ndef display_images_grid(images, img_folder, has_class_label=False, row_col_ind=(4, 4, 0)):\n    #rows, cols, i = 4, 4, 0\n    rows, cols, i = row_col_ind\n    \n    fig = plt.figure(figsize=(12, 12))\n\n    for fname in images [: rows * cols]:\n        plt.subplot(rows, cols,i+1)\n        plt.title(fname)\n        plt.xticks([]), plt.yticks([])\n        plt.tight_layout()\n        \n        if has_class_label:\n            fname = fname.split('/')[0].strip()\n            \n        img = image.load_img(os.path.join(img_folder, fname), target_size=(150, 150))\n        plt.imshow(img)\n        i += 1\n        \n    return plt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\n\nrandom.seed(1000)\n\n#view 8 random images from the source training folder\nplt = display_images_grid(images=random.sample(os.listdir(src_train), 8), img_folder=src_train)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2. Data preparation\n\nTransfer learning will be used in this demo by using a pre-trained convnet. The convolutional base of the model will be frozen so that its weights are not modified when training the classifier for the task of identify an image of a cat or dog. This will be done on a subset of 4,200 images (3000 training, 1000 validation and 200 holdout) with an even distribution of each class in the set.\n\nThe code below creates the 3 folder and their sub-folders."},{"metadata":{"trusted":true},"cell_type":"code","source":"#training folders\ntrain_dir = os.path.join(os.getcwd(), \"train\")\nif not os.path.isdir(train_dir):\n    os.mkdir(train_dir)\n\ntrain_cats = os.path.join(train_dir, \"cats\")\nif not os.path.isdir(train_cats):\n    os.mkdir(train_cats)\n\ntrain_dogs = os.path.join(train_dir, \"dogs\")\nif not os.path.isdir(train_dogs):\n    os.mkdir(train_dogs)\n\n#validation folders   \nvalidation_dir = os.path.join(os.getcwd(), \"validation\")\nif not os.path.isdir(validation_dir):\n    os.mkdir(validation_dir)\n\nval_cats = os.path.join(validation_dir, \"cats\")\nif not os.path.isdir(val_cats):\n    os.mkdir(val_cats)\n\nval_dogs = os.path.join(validation_dir, \"dogs\")\nif not os.path.isdir(val_dogs):\n    os.mkdir(val_dogs)\n\n#hold_out folder\nhold_out = os.path.join(os.getcwd(), \"hold_out\")\nif not os.path.isdir(hold_out):\n    os.mkdir(hold_out)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Code below copies the a random sample of images from the training folder to respective folder."},{"metadata":{"trusted":true},"cell_type":"code","source":"import re\n\nsrc_trn_files = os.listdir(src_train)\n\n#list of file names with cat images from train dir\ncat_files = [src_trn_files[i] for i, x in enumerate(src_trn_files) if re.match(r'^cat', x)]\n\n#list of file names with dog images from train dir\ndog_files = [src_trn_files[i] for i, x in enumerate(src_trn_files) if re.match(r'^dog', x)]\n\n#random sample 2100 cats image file names\ncat_files = random.sample(cat_files, 2100)\n\n#random sample 2100 dogs image file names\ndog_files = random.sample(dog_files, 2100)\n\nimport shutil\n\n#copy cats images to train_cats folder\nfor fname in cat_files[:1500]:\n    src = os.path.join(src_train, fname)\n    dst = os.path.join(train_cats, fname)\n    shutil.copyfile(src, dst)\n    \n#copy cats images to val_cats folder\nfor fname in cat_files[1500:2000]:\n    src = os.path.join(src_train, fname)\n    dst = os.path.join(val_cats, fname)\n    shutil.copyfile(src, dst)\n\n#copy dog images to train_dogs folder\nfor fname in dog_files[:1500]:\n    src = os.path.join(src_train, fname)\n    dst = os.path.join(train_dogs, fname)\n    shutil.copyfile(src, dst)\n\n#copy dogs images to val_dogs folder\nfor fname in dog_files[1500:2000]:\n    src = os.path.join(src_train, fname)\n    dst = os.path.join(val_dogs, fname)\n    shutil.copyfile(src, dst)\n    \n#copy dogs & cats images to hold_out folder\nfor fname in cat_files[2000:] + dog_files[2000:] :\n    src = os.path.join(src_train, fname)\n    dst = os.path.join(hold_out, fname)\n    shutil.copyfile(src, dst)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code creates data generators for train and validation. To help avoid overfitting, data augmentation is applied to the training generator."},{"metadata":{"trusted":true},"cell_type":"code","source":"#define data augmentation on training data\nimport numpy as np\nfrom keras.preprocessing.image import ImageDataGenerator\n\ntrain_datagen = ImageDataGenerator(rescale=1./255,\n                                   rotation_range=40,\n                                   width_shift_range=0.2,\n                                   height_shift_range=0.2,\n                                   shear_range=0.2,\n                                   zoom_range=0.2,\n                                   horizontal_flip=True,\n                                   fill_mode='nearest')\n\n\nval_datagen = ImageDataGenerator(rescale=1./255)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#create data generator\ntrain_generator = train_datagen.flow_from_directory(train_dir,\n                                                    target_size=(150, 150),\n                                                    batch_size=20,\n                                                    class_mode='binary')\n\nvalidation_generator = val_datagen.flow_from_directory(validation_dir,\n                                                        target_size=(150, 150),\n                                                        batch_size=20,\n                                                        class_mode='binary')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3. Building and training the model\n\nTo build the model, first a pre-trained model was downloaded from keras. Below code downloads the pre-trained model and freezes the convolutional base"},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.applications import VGG16\n\nconv_base = VGG16(weights='imagenet',\n                 include_top=False,\n                 input_shape=(150, 150, 3))\n\n#freeze the conv_base - so that weights are not changed during training\nconv_base.trainable = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code build a model and trains the model end -to -end with a frozen convolutional bas"},{"metadata":{"trusted":true},"cell_type":"code","source":"#build model using frozen conv_base and adding a classifier layer\nfrom tensorflow.keras import optimizers\n\nmodel = keras.Sequential()\n\nmodel.add(conv_base)\nmodel.add(layers.Flatten())\nmodel.add(layers.Dense(256, activation=\"relu\", input_dim=4 * 4 * 512))\nmodel.add(layers.Dense(1, activation=\"sigmoid\"))\n\n#compile the model\nmodel.compile(loss='binary_crossentropy',\n             optimizer=optimizers.RMSprop(lr=2e-5),\n             metrics=['acc'])\n\n#train the model\nhistory = model.fit(train_generator,\n                             steps_per_epoch=100,\n                             epochs=30,\n                             validation_data=validation_generator,\n                             validation_steps=50)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code plots the accuracy and loss of the model on training and validation sets."},{"metadata":{"trusted":true},"cell_type":"code","source":"#display loss & accuracy curves of the model\nimport matplotlib.pyplot as plt\n\nacc = history.history['acc']\nval_acc = history.history['val_acc']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(1, len(acc) + 1)\n\nplt.plot(epochs, acc, 'bo', label='Training acc')\nplt.plot(epochs, val_acc, 'b', label='Validation acc')\nplt.title('Training and validation accuracy')\nplt.legend()\n\nplt.figure()\n\nplt.plot(epochs, loss, 'bo', label='Training loss')\nplt.plot(epochs, val_loss, 'b', label='Validation loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Testing model on hold out set"},{"metadata":{"trusted":true},"cell_type":"code","source":"#function to convert images in folder to tensors\ndef convert_imgs_to_tensors(img_folder):\n  # dimensions of images\n  img_width, img_height = 150, 150\n\n  # load all images into a list\n  images = []\n\n  for img in os.listdir(img_folder):\n    img = os.path.join(img_folder, img)\n    img = image.load_img(img, target_size=(img_width, img_height))\n    img = image.img_to_array(img)\n    img = np.expand_dims(img, axis=0)\n    img /= 255.\n    images.append(img)\n\n  # stack up images list to pass for model\n  images = np.vstack(images)\n\n  return images","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code run the model on the hold out set."},{"metadata":{"trusted":true},"cell_type":"code","source":"#convert images in hold out to tensors\nimages = convert_imgs_to_tensors(hold_out)\n\n#make predictions\npredictions = [int(round(p[0])) for p in model.predict(images, batch_size=10)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#decode the predictions\nlabels = (train_generator.class_indices)\n\n#switch key and values\nreversed_dict = dict(map(reversed, labels.items()))\n\n#get the predicted labels\npredicted_labels = [reversed_dict[v1][:3] for k, v1 in enumerate(predictions)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#store predictions in pandas dataframe\nimport pandas as pd\n\ndf = pd.DataFrame({'filename': os.listdir(hold_out),\n                             'predicted_label': predicted_labels})\n\ndf['Correct_pred'] = df['predicted_label'].eq(df['filename'].str[:3]).astype(int)\ndf['fname_pred_label'] = df['filename'] +  ' / Pred. label: ' + df['predicted_label']\ndf","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below code, displays first 12 images that were misclassified from the hold out set."},{"metadata":{"trusted":true},"cell_type":"code","source":"#list of misclassified images from hold out set\nmis_class_img = df[df['Correct_pred'] == 0]['fname_pred_label'].head(12).to_list()\n\n#display the 12 images and their labels in grid\nplt = display_images_grid(images = mis_class_img, img_folder=hold_out, has_class_label=True)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Run model on test images"},{"metadata":{},"cell_type":"markdown","source":"Below code runs the model on the test images and store the result in a format ready for submission to the Kaggle competition."},{"metadata":{"trusted":true},"cell_type":"code","source":"#convert image to tensors\nimages = convert_imgs_to_tensors(test_dir)\n\n#make predictions\npredictions = [int(round(p[0])) for p in model.predict(images, batch_size=10)]\n\n#get the label\npredicted_labels = [reversed_dict[v1][:3] for k, v1 in enumerate(predictions)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#store the predictions in dataframe\ndf = pd.DataFrame({'filename': os.listdir(test_dir), 'label': predictions,\n                             'predicted_label': predicted_labels})\n\n#extract id from file name for kaggle submission\ndf['id'] = df['filename'].str.split('.').str[0]\n\n#column to store file name and prediction\ndf['fname_pred_label'] = df['filename'] +  ' / Pred. label: ' + df['predicted_label']\n\n#subset columns required for kaggle submission\ndf0 = df[['id', 'label']]\n\n#export to csv file\ndf0.to_csv('submission.csv', index=False)\ndf0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Display 12 random images and their predictions from the test set"},{"metadata":{"trusted":true},"cell_type":"code","source":"#get 12 random images from misclassified set\nimg_preds =  random.sample(df['fname_pred_label'].to_list(), 12)\n\n#display the 12 random images and their labels in grid\nplt = display_images_grid(images = img_preds, img_folder=test_dir, has_class_label=True)\n\nplt.show()","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}