{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Programming assignment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transfer learning"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Instructions\n",
    "\n",
    "In this notebook, you will create a neural network model to classify images of cats and dogs, using transfer learning: you will use part of a pre-trained image classifier model (trained on ImageNet) as a feature extractor, and train additional new layers to perform the cats and dogs classification task.\n",
    "\n",
    "Some code cells are provided you in the notebook. You should avoid editing provided code, and make sure to execute the cells in order to avoid unexpected errors. Some cells begin with the line: \n",
    "\n",
    "`#### GRADED CELL ####`\n",
    "\n",
    "Don't move or edit this first line - this is what the automatic grader looks for to recognise graded cells. These cells require you to write your own code to complete them, and are automatically graded when you submit the notebook. Don't edit the function name or signature provided in these cells, otherwise the automatic grader might not function properly. Inside these graded cells, you can use any functions or classes that are imported below, but make sure you don't use any variables that are outside the scope of the function.\n",
    "\n",
    "### How to submit\n",
    "\n",
    "Complete all the tasks you are asked for in the worksheet. When you have finished and are happy with your code, press the **Submit Assignment** button at the top of this notebook.\n",
    "\n",
    "### Let's get started!\n",
    "\n",
    "We'll start running some imports, and loading the dataset. Do not edit the existing imports in the following cell. If you would like to make further Tensorflow imports, you should add them here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### PACKAGE IMPORTS ####\n",
    "\n",
    "# Run this cell first to import all required packages. Do not make any imports elsewhere in the notebook\n",
    "\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras.models import  Sequential, Model\n",
    "import numpy as np\n",
    "import os\n",
    "import pandas as pd\n",
    "from sklearn.metrics import confusion_matrix\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import seaborn as sns\n",
    "\n",
    "# If you would like to make further imports from Tensorflow, add them here\n",
    "\n",
    "from tensorflow.keras.layers import Input,Dense,MaxPooling2D,Flatten,Conv2D,Dropout\n",
    "from tensorflow.keras.models import Model\n",
    "from tensorflow.keras.models import load_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"data/Cats-Dogs-Rex.jpg\" alt=\"Drawing\" style=\"height: 450px;\" align=\"center\"/>\n",
    "\n",
    "#### The Dogs vs Cats dataset\n",
    "\n",
    "In this assignment, you will use the [Dogs vs Cats dataset](https://www.kaggle.com/c/dogs-vs-cats/data), which was used for a 2013 Kaggle competition. It consists of 25000 images containing either a cat or a dog. We will only use a subset of 600 images and labels. The dataset is a subset of a much larger dataset of 3 million photos that were originally used as a CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart), referred to as “Asirra” or Animal Species Image Recognition for Restricting Access.\n",
    "\n",
    "* J. Elson, J. Douceur, J. Howell, and J. Saul. \"Asirra: A CAPTCHA that Exploits Interest-Aligned Manual Image Categorization.\" Proceedings of 14th ACM Conference on Computer and Communications Security (CCS), October 2007.\n",
    "\n",
    "Your goal is to train a classifier model using part of a pre-trained image classifier, using the principle of transfer learning."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Load and preprocess the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "images_train = np.load('data/images_train.npy') / 255.\n",
    "images_valid = np.load('data/images_valid.npy') / 255.\n",
    "images_test = np.load('data/images_test.npy') / 255.\n",
    "\n",
    "labels_train = np.load('data/labels_train.npy')\n",
    "labels_valid = np.load('data/labels_valid.npy')\n",
    "labels_test = np.load('data/labels_test.npy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "600 training data examples\n",
      "300 validation data examples\n",
      "300 test data examples\n"
     ]
    }
   ],
   "source": [
    "print(\"{} training data examples\".format(images_train.shape[0]))\n",
    "print(\"{} validation data examples\".format(images_valid.shape[0]))\n",
    "print(\"{} test data examples\".format(images_test.shape[0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Display sample images and labels from the training set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 856476 characters]\n",
      "text/plain": [
       "<Figure size 1080x720 with 15 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Display a few images and labels\n",
    "\n",
    "class_names = np.array(['Dog', 'Cat'])\n",
    "\n",
    "plt.figure(figsize=(15,10))\n",
    "inx = np.random.choice(images_train.shape[0], 15, replace=False)\n",
    "for n, i in enumerate(inx):\n",
    "    ax = plt.subplot(3,5,n+1)\n",
    "    plt.imshow(images_train[i])\n",
    "    plt.title(class_names[labels_train[i]])\n",
    "    plt.axis('off')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Create a benchmark model\n",
    "\n",
    "We will first train a CNN classifier model as a benchmark model before implementing the transfer learning approach. Using the functional API, build the benchmark model according to the following specifications:\n",
    "\n",
    "* The model should use the `input_shape` in the function argument to set the shape in the Input layer.\n",
    "* The first and second hidden layers should be Conv2D layers with 32 filters, 3x3 kernel size and ReLU activation.\n",
    "* The third hidden layer should be a MaxPooling2D layer with a 2x2 window size.\n",
    "* The fourth and fifth hidden layers should be Conv2D layers with 64 filters, 3x3 kernel size and ReLU activation.\n",
    "* The sixth hidden layer should be a MaxPooling2D layer with a 2x2 window size.\n",
    "* The seventh and eighth hidden layers should be Conv2D layers with 128 filters, 3x3 kernel size and ReLU activation.\n",
    "* The ninth hidden layer should be a MaxPooling2D layer with a 2x2 window size.\n",
    "* This should be followed by a Flatten layer, and a Dense layer with 128 units and ReLU activation\n",
    "* The final layer should be a Dense layer with a single neuron and sigmoid activation.\n",
    "* All of the Conv2D layers should use `'SAME'` padding.\n",
    "\n",
    "In total, the network should have 13 layers (including the `Input` layer).\n",
    "\n",
    "The model should then be compiled with the RMSProp optimiser with learning rate 0.001, binary cross entropy loss and and binary accuracy metric."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### GRADED CELL ####\n",
    "\n",
    "# Complete the following function. \n",
    "# Make sure to not change the function name or arguments.\n",
    "\n",
    "def get_benchmark_model(input_shape):\n",
    "    \"\"\"\n",
    "    This function should build and compile a CNN model according to the above specification,\n",
    "    using the functional API. The function takes input_shape as an argument, which should be\n",
    "    used to specify the shape in the Input layer.\n",
    "    Your function should return the model.\n",
    "    \"\"\"\n",
    "    \n",
    "    inputs = Input(input_shape,name = 'benchmark_input')\n",
    "    h = Conv2D(32,(3,3),activation = 'relu',padding = 'SAME')(inputs)\n",
    "    h = Conv2D(32,(3,3),activation = 'relu',padding = 'SAME')(h)\n",
    "    h = MaxPooling2D((2,2))(h)\n",
    "    h = Conv2D(64,(3,3),activation = 'relu',padding = 'SAME')(h)\n",
    "    h = Conv2D(64,(3,3),activation = 'relu',padding = 'SAME')(h)\n",
    "    h = MaxPooling2D((2,2))(h)\n",
    "    h = Conv2D(128,(3,3),activation = 'relu',padding = 'SAME')(h)\n",
    "    h = Conv2D(128,(3,3),activation = 'relu',padding = 'SAME')(h)\n",
    "    h = MaxPooling2D((2,2))(h)\n",
    "    h = Flatten()(h)\n",
    "    h = Dense(128,activation = 'relu')(h)\n",
    "    outputs = Dense(1,activation = 'sigmoid')(h)\n",
    "    \n",
    "    model = Model(inputs = inputs,outputs = outputs)\n",
    "    \n",
    "    model.compile(optimizer = tf.keras.optimizers.RMSprop(learning_rate = 0.001),\n",
    "                  loss = 'binary_crossentropy',\n",
    "                  metrics = ['accuracy'])\n",
    "    \n",
    "    return model\n",
    "    \n",
    "   \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"model\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "benchmark_input (InputLayer) [(None, 160, 160, 3)]     0         \n",
      "_________________________________________________________________\n",
      "conv2d (Conv2D)              (None, 160, 160, 32)      896       \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 160, 160, 32)      9248      \n",
      "_________________________________________________________________\n",
      "max_pooling2d (MaxPooling2D) (None, 80, 80, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 80, 80, 64)        18496     \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 80, 80, 64)        36928     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 40, 40, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_4 (Conv2D)            (None, 40, 40, 128)       73856     \n",
      "_________________________________________________________________\n",
      "conv2d_5 (Conv2D)            (None, 40, 40, 128)       147584    \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 20, 20, 128)       0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 51200)             0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, 128)               6553728   \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 1)                 129       \n",
      "=================================================================\n",
      "Total params: 6,840,865\n",
      "Trainable params: 6,840,865\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "# Build and compile the benchmark model, and display the model summary\n",
    "\n",
    "benchmark_model = get_benchmark_model(images_train[0].shape)\n",
    "benchmark_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Train the CNN benchmark model\n",
    "\n",
    "We will train the benchmark CNN model using an `EarlyStopping` callback. Feel free to increase the training time if you wish."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 600 samples, validate on 300 samples\n",
      "Epoch 1/10\n",
      " 96/600 [===>..........................] - ETA: 5:14 - loss: 6.5407 - accuracy: 0.5000"
     ]
    }
   ],
   "source": [
    "# Fit the benchmark model and save its training history\n",
    "\n",
    "earlystopping = tf.keras.callbacks.EarlyStopping(patience=2)\n",
    "history_benchmark = benchmark_model.fit(images_train, labels_train, epochs=10, batch_size=32,\n",
    "                                        validation_data=(images_valid, labels_valid), \n",
    "                                        callbacks=[earlystopping])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Plot the learning curves"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run this cell to plot accuracy vs epoch and loss vs epoch\n",
    "\n",
    "plt.figure(figsize=(15,5))\n",
    "plt.subplot(121)\n",
    "try:\n",
    "    plt.plot(history_benchmark.history['accuracy'])\n",
    "    plt.plot(history_benchmark.history['val_accuracy'])\n",
    "except KeyError:\n",
    "    plt.plot(history_benchmark.history['acc'])\n",
    "    plt.plot(history_benchmark.history['val_acc'])\n",
    "plt.title('Accuracy vs. epochs')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Training', 'Validation'], loc='lower right')\n",
    "\n",
    "plt.subplot(122)\n",
    "plt.plot(history_benchmark.history['loss'])\n",
    "plt.plot(history_benchmark.history['val_loss'])\n",
    "plt.title('Loss vs. epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Training', 'Validation'], loc='upper right')\n",
    "plt.show() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Evaluate the benchmark model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the benchmark model on the test set\n",
    "\n",
    "benchmark_test_loss, benchmark_test_acc = benchmark_model.evaluate(images_test, labels_test, verbose=0)\n",
    "print(\"Test loss: {}\".format(benchmark_test_loss))\n",
    "print(\"Test accuracy: {}\".format(benchmark_test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Load the pretrained image classifier model\n",
    "\n",
    "You will now begin to build our image classifier using transfer learning.\n",
    "You will use the pre-trained MobileNet V2 model, available to download from [Keras Applications](https://keras.io/applications/#mobilenetv2). However, we have already downloaded the pretrained model for you, and it is available at the location `./models/MobileNetV2.h5`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### GRADED CELL ####\n",
    "\n",
    "# Complete the following function. \n",
    "# Make sure to not change the function name or arguments.\n",
    "\n",
    "def load_pretrained_MobileNetV2(path):\n",
    "    \"\"\"\n",
    "    This function takes a path as an argument, and uses it to \n",
    "    load the full MobileNetV2 pretrained model from the path.\n",
    "    Your function should return the loaded model.\n",
    "    \"\"\"\n",
    "    \n",
    "    mobilenetv2_model = load_model(path)\n",
    "    \n",
    "    \n",
    "    return mobilenetv2_model\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the function loading the pretrained model and display its summary\n",
    "\n",
    "base_model = load_pretrained_MobileNetV2('models/MobileNetV2.h5')\n",
    "base_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Use the pre-trained model as a feature extractor\n",
    "\n",
    "You will remove the final layer of the network and replace it with new, untrained classifier layers for our task. You will first create a new model that has the same input tensor as the MobileNetV2 model, and uses the output tensor from the layer with name `global_average_pooling2d_6` as the model output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### GRADED CELL ####\n",
    "\n",
    "# Complete the following function. \n",
    "# Make sure to not change the function name or arguments.\n",
    "\n",
    "def remove_head(pretrained_model):\n",
    "    \"\"\"\n",
    "    This function should create and return a new model, using the input and output \n",
    "    tensors as specified above. \n",
    "    Use the 'get_layer' method to access the correct layer of the pre-trained model.\n",
    "    \"\"\"\n",
    "    inputs = pretrained_model.get_layer(name = 'benchmark_input')\n",
    "    outputs = get_layer('global_average_pooling2d_6').output\n",
    "    \n",
    "    model = Model(inputs = inputs,outputs = outputs)\n",
    "    \n",
    "    return model\n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the function removing the classification head and display the summary\n",
    "\n",
    "feature_extractor = remove_head(base_model)\n",
    "feature_extractor.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can now construct new final classifier layers for your model. Using the Sequential API, create a new model according to the following specifications:\n",
    "\n",
    "* The new model should begin with the feature extractor model.\n",
    "* This should then be followed with a new dense layer with 32 units and ReLU activation function.\n",
    "* This should be followed by a dropout layer with a rate of 0.5.\n",
    "* Finally, this should be followed by a Dense layer with a single neuron and a sigmoid activation function.\n",
    "\n",
    "In total, the network should be composed of the pretrained base model plus 3 layers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### GRADED CELL ####\n",
    "\n",
    "# Complete the following function. \n",
    "# Make sure to not change the function name or arguments.\n",
    "\n",
    "def add_new_classifier_head(feature_extractor_model):\n",
    "    \"\"\"\n",
    "    This function takes the feature extractor model as an argument, and should create\n",
    "    and return a new model according to the above specification.\n",
    "    \"\"\"\n",
    "    \n",
    "    inputs = feature_extractor_model.get_layer(index = 0)\n",
    "    h = Dense(32,activation = 'relu')(inputs)\n",
    "    h = Droput(0.5)(h)\n",
    "    outputs = Dense(1,activation = 'sigmoid')(h)\n",
    "    \n",
    "    model = Model(inputs = inputs,outputs = outputs)\n",
    "    \n",
    "    return model\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the function adding a new classification head and display the summary\n",
    "\n",
    "new_model = add_new_classifier_head(feature_extractor)\n",
    "new_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Freeze the weights of the pretrained model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You will now need to freeze the weights of the pre-trained feature extractor, so that only the weights of the new layers you have added will change during the training. \n",
    "\n",
    "You should then compile your model as before: use the RMSProp optimiser with learning rate 0.001, binary cross entropy loss and and binary accuracy metric."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### GRADED CELL ####\n",
    "\n",
    "# Complete the following function. \n",
    "# Make sure to not change the function name or arguments.\n",
    "\n",
    "def freeze_pretrained_weights(model):\n",
    "    \"\"\"\n",
    "    This function should freeze the weights of the pretrained base model.\n",
    "    Your function should return the model with frozen weights.\n",
    "    \"\"\"\n",
    "    \n",
    "    model.layers[0].trainable = False\n",
    "    \n",
    "    reutrn model\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the function freezing the pretrained weights and display the summary\n",
    "\n",
    "frozen_new_model = freeze_pretrained_weights(new_model)\n",
    "frozen_new_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Train the model\n",
    "\n",
    "You are now ready to train the new model on the dogs vs cats data subset. We will use an `EarlyStopping` callback with patience set to 2 epochs, as before. Feel free to increase the training time if you wish."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train the model and save its training history\n",
    "\n",
    "earlystopping = tf.keras.callbacks.EarlyStopping(patience=2)\n",
    "history_frozen_new_model = frozen_new_model.fit(images_train, labels_train, epochs=10, batch_size=32,\n",
    "                                                validation_data=(images_valid, labels_valid), \n",
    "                                                callbacks=[earlystopping])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Plot the learning curves"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run this cell to plot accuracy vs epoch and loss vs epoch\n",
    "\n",
    "plt.figure(figsize=(15,5))\n",
    "plt.subplot(121)\n",
    "try:\n",
    "    plt.plot(history_frozen_new_model.history['accuracy'])\n",
    "    plt.plot(history_frozen_new_model.history['val_accuracy'])\n",
    "except KeyError:\n",
    "    plt.plot(history_frozen_new_model.history['acc'])\n",
    "    plt.plot(history_frozen_new_model.history['val_acc'])\n",
    "plt.title('Accuracy vs. epochs')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Training', 'Validation'], loc='lower right')\n",
    "\n",
    "plt.subplot(122)\n",
    "plt.plot(history_frozen_new_model.history['loss'])\n",
    "plt.plot(history_frozen_new_model.history['val_loss'])\n",
    "plt.title('Loss vs. epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Training', 'Validation'], loc='upper right')\n",
    "plt.show() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Evaluate the new model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the benchmark model on the test set\n",
    "\n",
    "new_model_test_loss, new_model_test_acc = frozen_new_model.evaluate(images_test, labels_test, verbose=0)\n",
    "print(\"Test loss: {}\".format(new_model_test_loss))\n",
    "print(\"Test accuracy: {}\".format(new_model_test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Compare both models\n",
    "\n",
    "Finally, we will look at the comparison of training, validation and test metrics between the benchmark and transfer learning model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Gather the benchmark and new model metrics\n",
    "\n",
    "benchmark_train_loss = history_benchmark.history['loss'][-1]\n",
    "benchmark_valid_loss = history_benchmark.history['val_loss'][-1]\n",
    "\n",
    "try:\n",
    "    benchmark_train_acc = history_benchmark.history['acc'][-1]\n",
    "    benchmark_valid_acc = history_benchmark.history['val_acc'][-1]\n",
    "except KeyError:\n",
    "    benchmark_train_acc = history_benchmark.history['accuracy'][-1]\n",
    "    benchmark_valid_acc = history_benchmark.history['val_accuracy'][-1]\n",
    "\n",
    "new_model_train_loss = history_frozen_new_model.history['loss'][-1]\n",
    "new_model_valid_loss = history_frozen_new_model.history['val_loss'][-1]\n",
    "\n",
    "try:\n",
    "    new_model_train_acc = history_frozen_new_model.history['acc'][-1]\n",
    "    new_model_valid_acc = history_frozen_new_model.history['val_acc'][-1]\n",
    "except KeyError:\n",
    "    new_model_train_acc = history_frozen_new_model.history['accuracy'][-1]\n",
    "    new_model_valid_acc = history_frozen_new_model.history['val_accuracy'][-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Compile the metrics into a pandas DataFrame and display the table\n",
    "\n",
    "comparison_table = pd.DataFrame([['Training loss', benchmark_train_loss, new_model_train_loss],\n",
    "                                ['Training accuracy', benchmark_train_acc, new_model_train_acc],\n",
    "                                ['Validation loss', benchmark_valid_loss, new_model_valid_loss],\n",
    "                                ['Validation accuracy', benchmark_valid_acc, new_model_valid_acc],\n",
    "                                ['Test loss', benchmark_test_loss, new_model_test_loss],\n",
    "                                ['Test accuracy', benchmark_test_acc, new_model_test_acc]],\n",
    "                               columns=['Metric', 'Benchmark CNN', 'Transfer learning CNN'])\n",
    "comparison_table.index=['']*6\n",
    "comparison_table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot confusion matrices for benchmark and transfer learning models\n",
    "\n",
    "plt.figure(figsize=(15, 5))\n",
    "\n",
    "preds = benchmark_model.predict(images_test)\n",
    "preds = (preds >= 0.5).astype(np.int32)\n",
    "cm = confusion_matrix(labels_test, preds)\n",
    "df_cm = pd.DataFrame(cm, index=['Dog', 'Cat'], columns=['Dog', 'Cat'])\n",
    "plt.subplot(121)\n",
    "plt.title(\"Confusion matrix for benchmark model\\n\")\n",
    "sns.heatmap(df_cm, annot=True, fmt=\"d\", cmap=\"YlGnBu\")\n",
    "plt.ylabel(\"Predicted\")\n",
    "plt.xlabel(\"Actual\")\n",
    "\n",
    "preds = frozen_new_model.predict(images_test)\n",
    "preds = (preds >= 0.5).astype(np.int32)\n",
    "cm = confusion_matrix(labels_test, preds)\n",
    "df_cm = pd.DataFrame(cm, index=['Dog', 'Cat'], columns=['Dog', 'Cat'])\n",
    "plt.subplot(122)\n",
    "plt.title(\"Confusion matrix for transfer learning model\\n\")\n",
    "sns.heatmap(df_cm, annot=True, fmt=\"d\", cmap=\"YlGnBu\")\n",
    "plt.ylabel(\"Predicted\")\n",
    "plt.xlabel(\"Actual\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Congratulations for completing this programming assignment! In the next week of the course we will learn how to develop an effective data pipeline."
   ]
  }
 ],
 "metadata": {
  "coursera": {
   "course_slug": "tensor-flow-2-2",
   "graded_item_id": "KDxTq",
   "launcher_item_id": "aYhdg"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "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.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
