{"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":"**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":{}},{"cell_type":"code","source":"#import libraries\nimport os\nimport re\nimport random\n\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\n\nfrom keras.preprocessing import image\nfrom keras.preprocessing.image import ImageDataGenerator\n\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.applications import VGG16\n\nfrom tensorflow.keras import optimizers\n","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:11.439596Z","iopub.execute_input":"2022-02-05T18:32:11.440070Z","iopub.status.idle":"2022-02-05T18:32:11.451118Z","shell.execute_reply.started":"2022-02-05T18:32:11.440025Z","shell.execute_reply":"2022-02-05T18:32:11.450197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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\n\nsrc_train = os.path.join(os.getcwd(), 'src_train')\n\nos.rename(os.path.join(os.getcwd(), 'train'), src_train)","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","execution":{"iopub.status.busy":"2022-02-05T18:32:11.454773Z","iopub.execute_input":"2022-02-05T18:32:11.455191Z","iopub.status.idle":"2022-02-05T18:32:23.398143Z","shell.execute_reply.started":"2022-02-05T18:32:11.455160Z","shell.execute_reply":"2022-02-05T18:32:23.397195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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')","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:23.401735Z","iopub.execute_input":"2022-02-05T18:32:23.402012Z","iopub.status.idle":"2022-02-05T18:32:29.437435Z","shell.execute_reply.started":"2022-02-05T18:32:23.401983Z","shell.execute_reply":"2022-02-05T18:32:29.436479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'# of files in train folder:{len(os.listdir(src_train))}' )\nprint(f'# of files in test folder:{len(os.listdir(test_dir))}')","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:29.438940Z","iopub.execute_input":"2022-02-05T18:32:29.439347Z","iopub.status.idle":"2022-02-05T18:32:29.468065Z","shell.execute_reply.started":"2022-02-05T18:32:29.439291Z","shell.execute_reply":"2022-02-05T18:32:29.467248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code displays 8 random images from the training folder.","metadata":{}},{"cell_type":"code","source":"#help function to display images in a grid\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","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:29.471676Z","iopub.execute_input":"2022-02-05T18:32:29.471924Z","iopub.status.idle":"2022-02-05T18:32:29.482312Z","shell.execute_reply.started":"2022-02-05T18:32:29.471899Z","shell.execute_reply":"2022-02-05T18:32:29.481498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"random.seed(1234)\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()","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:29.485317Z","iopub.execute_input":"2022-02-05T18:32:29.485659Z","iopub.status.idle":"2022-02-05T18:32:30.254072Z","shell.execute_reply.started":"2022-02-05T18:32:29.485627Z","shell.execute_reply":"2022-02-05T18:32:30.252994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{}},{"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)","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:30.255428Z","iopub.execute_input":"2022-02-05T18:32:30.255798Z","iopub.status.idle":"2022-02-05T18:32:30.461151Z","shell.execute_reply.started":"2022-02-05T18:32:30.255760Z","shell.execute_reply":"2022-02-05T18:32:30.460268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Code below copies the a random sample of images from the training folder to respective folder.","metadata":{}},{"cell_type":"code","source":"src_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)","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:30.462327Z","iopub.execute_input":"2022-02-05T18:32:30.462792Z","iopub.status.idle":"2022-02-05T18:32:31.200352Z","shell.execute_reply.started":"2022-02-05T18:32:30.462753Z","shell.execute_reply":"2022-02-05T18:32:31.199427Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{}},{"cell_type":"code","source":"#define data augmentation on training data\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)","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:31.204667Z","iopub.execute_input":"2022-02-05T18:32:31.206767Z","iopub.status.idle":"2022-02-05T18:32:31.215249Z","shell.execute_reply.started":"2022-02-05T18:32:31.205133Z","shell.execute_reply":"2022-02-05T18:32:31.214268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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')","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:31.219364Z","iopub.execute_input":"2022-02-05T18:32:31.221681Z","iopub.status.idle":"2022-02-05T18:32:31.442975Z","shell.execute_reply.started":"2022-02-05T18:32:31.221637Z","shell.execute_reply":"2022-02-05T18:32:31.442051Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{}},{"cell_type":"code","source":"#install VGG conv base\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","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:31.444340Z","iopub.execute_input":"2022-02-05T18:32:31.444882Z","iopub.status.idle":"2022-02-05T18:32:34.123325Z","shell.execute_reply.started":"2022-02-05T18:32:31.444841Z","shell.execute_reply":"2022-02-05T18:32:34.122508Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code build a model and trains the model end -to -end with a frozen convolutional bas","metadata":{}},{"cell_type":"code","source":"#build model using frozen conv_base and adding a classifier layer\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)","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:32:34.126971Z","iopub.execute_input":"2022-02-05T18:32:34.127246Z","iopub.status.idle":"2022-02-05T18:41:42.211070Z","shell.execute_reply.started":"2022-02-05T18:32:34.127218Z","shell.execute_reply":"2022-02-05T18:41:42.210318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code plots the accuracy and loss of the model on training and validation sets.","metadata":{}},{"cell_type":"code","source":"#display loss & accuracy curves of the model\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()","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:42.212918Z","iopub.execute_input":"2022-02-05T18:41:42.213284Z","iopub.status.idle":"2022-02-05T18:41:42.499223Z","shell.execute_reply.started":"2022-02-05T18:41:42.213247Z","shell.execute_reply":"2022-02-05T18:41:42.498555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**4. Testing model on hold out set**","metadata":{}},{"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","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:42.500646Z","iopub.execute_input":"2022-02-05T18:41:42.500971Z","iopub.status.idle":"2022-02-05T18:41:42.507952Z","shell.execute_reply.started":"2022-02-05T18:41:42.500935Z","shell.execute_reply":"2022-02-05T18:41:42.507142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code run the model on the hold out set.","metadata":{}},{"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)]","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:42.509540Z","iopub.execute_input":"2022-02-05T18:41:42.509989Z","iopub.status.idle":"2022-02-05T18:41:43.726105Z","shell.execute_reply.started":"2022-02-05T18:41:42.509950Z","shell.execute_reply":"2022-02-05T18:41:43.725332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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)]","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:43.727465Z","iopub.execute_input":"2022-02-05T18:41:43.727798Z","iopub.status.idle":"2022-02-05T18:41:43.732860Z","shell.execute_reply.started":"2022-02-05T18:41:43.727763Z","shell.execute_reply":"2022-02-05T18:41:43.732049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#store predictions in pandas dataframe\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","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:43.734257Z","iopub.execute_input":"2022-02-05T18:41:43.734818Z","iopub.status.idle":"2022-02-05T18:41:43.785203Z","shell.execute_reply.started":"2022-02-05T18:41:43.734780Z","shell.execute_reply":"2022-02-05T18:41:43.784406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code, displays first 12 images that were misclassified from the hold out set.","metadata":{}},{"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()","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:43.786370Z","iopub.execute_input":"2022-02-05T18:41:43.786696Z","iopub.status.idle":"2022-02-05T18:41:44.841772Z","shell.execute_reply.started":"2022-02-05T18:41:43.786661Z","shell.execute_reply":"2022-02-05T18:41:44.840942Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**5. Run model on test images**\n\nBelow code runs the model on the test images and store the result in a format ready for submission to the Kaggle competition.","metadata":{}},{"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)]","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:41:44.842991Z","iopub.execute_input":"2022-02-05T18:41:44.843462Z","iopub.status.idle":"2022-02-05T18:42:38.660727Z","shell.execute_reply.started":"2022-02-05T18:41:44.843425Z","shell.execute_reply":"2022-02-05T18:42:38.659897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:42:38.662114Z","iopub.execute_input":"2022-02-05T18:42:38.662481Z","iopub.status.idle":"2022-02-05T18:42:39.176147Z","shell.execute_reply.started":"2022-02-05T18:42:38.662446Z","shell.execute_reply":"2022-02-05T18:42:39.175410Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Display 12 random images and their predictions from the test set","metadata":{}},{"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()","metadata":{"execution":{"iopub.status.busy":"2022-02-05T18:42:39.177383Z","iopub.execute_input":"2022-02-05T18:42:39.177783Z","iopub.status.idle":"2022-02-05T18:42:40.475486Z","shell.execute_reply.started":"2022-02-05T18:42:39.177738Z","shell.execute_reply":"2022-02-05T18:42:40.473800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}