Skip to content

Instantly share code, notes, and snippets.

@johnleung8888
Last active October 24, 2023 17:13
Show Gist options
  • Save johnleung8888/f061853e73c535ddd4a5965c0b7d5a23 to your computer and use it in GitHub Desktop.
Save johnleung8888/f061853e73c535ddd4a5965c0b7d5a23 to your computer and use it in GitHub Desktop.
C2W2_Assignment.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/johnleung8888/f061853e73c535ddd4a5965c0b7d5a23/c2w2_assignment.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "AuW-xg_bTsaF"
},
"source": [
"# Week 2: Tackle Overfitting with Data Augmentation\n",
"\n",
"Welcome to this assignment! As in the previous week, you will be using the famous `cats vs dogs` dataset to train a model that can classify images of dogs from images of cats. For this, you will create your own Convolutional Neural Network in Tensorflow and leverage Keras' image preprocessing utilities, more so this time around since Keras provides excellent support for augmenting image data.\n",
"\n",
"You will also need to create the helper functions to move the images around the filesystem as you did last week, so if you need to refresh your memory with the `os` module be sure to take a look a the [docs](https://docs.python.org/3/library/os.html).\n",
"\n",
"Let's get started!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "dn-6c02VmqiN"
},
"outputs": [],
"source": [
"import os\n",
"import zipfile\n",
"import random\n",
"import shutil\n",
"import tensorflow as tf\n",
"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
"from shutil import copyfile\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bLTQd84RUs1j"
},
"source": [
"Download the dataset from its original source by running the cell below. \n",
"\n",
"Note that the `zip` file that contains the images is unzipped under the `/tmp` directory."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3sd9dQWa23aj",
"lines_to_next_cell": 2,
"outputId": "c5dc3992-3bdc-4ae1-e49e-115c4c87faf4",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"--2022-02-15 05:42:59-- https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip\n",
"Resolving download.microsoft.com (download.microsoft.com)... 184.51.220.111, 2600:1407:a800:2ae::e59, 2600:1407:a800:280::e59\n",
"Connecting to download.microsoft.com (download.microsoft.com)|184.51.220.111|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 824894548 (787M) [application/octet-stream]\n",
"Saving to: ‘/tmp/cats-and-dogs.zip’\n",
"\n",
"/tmp/cats-and-dogs. 100%[===================>] 786.68M 93.4MB/s in 8.5s \n",
"\n",
"2022-02-15 05:43:08 (92.4 MB/s) - ‘/tmp/cats-and-dogs.zip’ saved [824894548/824894548]\n",
"\n"
]
}
],
"source": [
"# If the URL doesn't work, visit https://www.microsoft.com/en-us/download/confirmation.aspx?id=54765\n",
"# And right click on the 'Download Manually' link to get a new URL to the dataset\n",
"\n",
"# Note: This is a very large dataset and will take some time to download\n",
"\n",
"!wget --no-check-certificate \\\n",
" \"https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip\" \\\n",
" -O \"/tmp/cats-and-dogs.zip\"\n",
"\n",
"local_zip = '/tmp/cats-and-dogs.zip'\n",
"zip_ref = zipfile.ZipFile(local_zip, 'r')\n",
"zip_ref.extractall('/tmp')\n",
"zip_ref.close()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "e_HsUV9WVJHL"
},
"source": [
"Now the images are stored within the `/tmp/PetImages` directory. There is a subdirectory for each class, so one for dogs and one for cats."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "DM851ZmN28J3",
"outputId": "83398339-021e-4986-8071-45c82b585e7d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"There are 12501 images of dogs.\n",
"There are 12501 images of cats.\n"
]
}
],
"source": [
"source_path = '/tmp/PetImages'\n",
"\n",
"source_path_dogs = os.path.join(source_path, 'Dog')\n",
"source_path_cats = os.path.join(source_path, 'Cat')\n",
"\n",
"\n",
"# os.listdir returns a list containing all files under the given path\n",
"print(f\"There are {len(os.listdir(source_path_dogs))} images of dogs.\")\n",
"print(f\"There are {len(os.listdir(source_path_cats))} images of cats.\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "G7dI86rmRGmC"
},
"source": [
"**Expected Output:**\n",
"\n",
"```\n",
"There are 12501 images of dogs.\n",
"There are 12501 images of cats.\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "iFbMliudNIjW"
},
"source": [
"You will need a directory for cats-v-dogs, and subdirectories for training\n",
"and testing. These in turn will need subdirectories for 'cats' and 'dogs'. To accomplish this, complete the `create_train_test_dirs` below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "code",
"id": "F-QkLjxpmyK2"
},
"outputs": [],
"source": [
"# Define root directory\n",
"root_dir = '/tmp/cats-v-dogs'\n",
"\n",
"# Empty directory to prevent FileExistsError is the function is run several times\n",
"if os.path.exists(root_dir):\n",
" shutil.rmtree(root_dir)\n",
"\n",
"# GRADED FUNCTION: create_train_test_dirs\n",
"def create_train_test_dirs(root_path):\n",
" ### START CODE HERE\n",
" path = os.path.join(root_dir, \"training\")\n",
" os.makedirs(path)\n",
" path_1 = os.path.join(path, \"cats\")\n",
" os.makedirs(path_1)\n",
" path_2 = os.path.join(path, \"dogs\")\n",
" os.makedirs(path_2)\n",
" path = os.path.join(root_dir, \"testing\")\n",
" os.makedirs(path)\n",
" path_3 = os.path.join(path, \"cats\")\n",
" os.makedirs(path_3)\n",
" path_4 = os.path.join(path, \"dogs\")\n",
" os.makedirs(path_4)\n",
" \n",
" # HINT:\n",
" # Use os.makedirs to create your directories with intermediate subdirectories\n",
"\n",
" pass\n",
" \n",
" ### END CODE HERE\n",
"\n",
" \n",
"try:\n",
" create_train_test_dirs(root_path=root_dir)\n",
"except FileExistsError:\n",
" print(\"You should not be seeing this since the upper directory is removed beforehand\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "5dhtL344OK00",
"outputId": "f9c9bb79-b2c3-4eb1-e215-fdd70ad58eb9",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/tmp/cats-v-dogs/testing\n",
"/tmp/cats-v-dogs/training\n",
"/tmp/cats-v-dogs/testing/cats\n",
"/tmp/cats-v-dogs/testing/dogs\n",
"/tmp/cats-v-dogs/training/cats\n",
"/tmp/cats-v-dogs/training/dogs\n"
]
}
],
"source": [
"# Test your create_train_test_dirs function\n",
"\n",
"for rootdir, dirs, files in os.walk(root_dir):\n",
" for subdir in dirs:\n",
" print(os.path.join(rootdir, subdir))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "D7A0RK3IQsvg"
},
"source": [
"**Expected Output (directory order might vary):**\n",
"\n",
"``` txt\n",
"/tmp/cats-v-dogs/training\n",
"/tmp/cats-v-dogs/testing\n",
"/tmp/cats-v-dogs/training/cats\n",
"/tmp/cats-v-dogs/training/dogs\n",
"/tmp/cats-v-dogs/testing/cats\n",
"/tmp/cats-v-dogs/testing/dogs\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "R93T7HdE5txZ"
},
"source": [
"Code the `split_data` function which takes in the following arguments:\n",
"- SOURCE: directory containing the files\n",
"\n",
"- TRAINING: directory that a portion of the files will be copied to (will be used for training)\n",
"- TESTING: directory that a portion of the files will be copied to (will be used for testing)\n",
"- SPLIT SIZE: to determine the portion\n",
"\n",
"The files should be randomized, so that the training set is a random sample of the files, and the test set is made up of the remaining files.\n",
"\n",
"For example, if `SOURCE` is `PetImages/Cat`, and `SPLIT` SIZE is .9 then 90% of the images in `PetImages/Cat` will be copied to the `TRAINING` dir\n",
"and 10% of the images will be copied to the `TESTING` dir.\n",
"\n",
"All images should be checked before the copy, so if they have a zero file length, they will be omitted from the copying process. If this is the case then your function should print out a message such as `\"filename is zero length, so ignoring.\"`. **You should perform this check before the split so that only non-zero images are considered when doing the actual split.**\n",
"\n",
"\n",
"Hints:\n",
"\n",
"- `os.listdir(DIRECTORY)` returns a list with the contents of that directory.\n",
"\n",
"- `os.path.getsize(PATH)` returns the size of the file\n",
"\n",
"- `copyfile(source, destination)` copies a file from source to destination\n",
"\n",
"- `random.sample(list, len(list))` shuffles a list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "code",
"id": "zvSODo0f9LaU"
},
"outputs": [],
"source": [
"# GRADED FUNCTION: split_data\n",
"def split_data(SOURCE, TRAINING, TESTING, SPLIT_SIZE):\n",
"\n",
" ### START CODE HERE\n",
" files = []\n",
" for filename in os.listdir(SOURCE):\n",
" file = SOURCE + filename\n",
" if os.path.getsize(file) > 0:\n",
" files.append(filename)\n",
" else:\n",
" print(filename + ' is zero length, so ignoring.')\n",
"\n",
" training_length = int(len(files) * SPLIT_SIZE)\n",
" testing_length = int(len(files) - training_length)\n",
" shuffled_set = random.sample(files, len(files))\n",
" training_set = shuffled_set[0:training_length]\n",
" testing_set = shuffled_set[-testing_length:]\n",
" \n",
" for filename in training_set:\n",
" src_file = SOURCE + filename\n",
" dest_file = TRAINING + filename\n",
" copyfile(src_file, dest_file)\n",
" \n",
" for filename in testing_set:\n",
" src_file = SOURCE + filename\n",
" dest_file = TESTING + filename\n",
" copyfile(src_file, dest_file)\n",
" pass\n",
"\n",
" ### END CODE HERE\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "FlIdoUeX9S-9",
"outputId": "859c9df3-d2b0-4df3-ded7-9492386b6ecb",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"666.jpg is zero length, so ignoring.\n",
"11702.jpg is zero length, so ignoring.\n",
"\n",
"\n",
"There are 11250 images of cats for training\n",
"There are 11250 images of dogs for training\n",
"There are 1250 images of cats for testing\n",
"There are 1250 images of dogs for testing\n"
]
}
],
"source": [
"# Test your split_data function\n",
"\n",
"# Define paths\n",
"CAT_SOURCE_DIR = \"/tmp/PetImages/Cat/\"\n",
"DOG_SOURCE_DIR = \"/tmp/PetImages/Dog/\"\n",
"\n",
"TRAINING_DIR = \"/tmp/cats-v-dogs/training/\"\n",
"TESTING_DIR = \"/tmp/cats-v-dogs/testing/\"\n",
"\n",
"TRAINING_CATS_DIR = os.path.join(TRAINING_DIR, \"cats/\")\n",
"TESTING_CATS_DIR = os.path.join(TESTING_DIR, \"cats/\")\n",
"\n",
"TRAINING_DOGS_DIR = os.path.join(TRAINING_DIR, \"dogs/\")\n",
"TESTING_DOGS_DIR = os.path.join(TESTING_DIR, \"dogs/\")\n",
"\n",
"# Empty directories in case you run this cell multiple times\n",
"if len(os.listdir(TRAINING_CATS_DIR)) > 0:\n",
" for file in os.scandir(TRAINING_CATS_DIR):\n",
" os.remove(file.path)\n",
"if len(os.listdir(TRAINING_DOGS_DIR)) > 0:\n",
" for file in os.scandir(TRAINING_DOGS_DIR):\n",
" os.remove(file.path)\n",
"if len(os.listdir(TESTING_CATS_DIR)) > 0:\n",
" for file in os.scandir(TESTING_CATS_DIR):\n",
" os.remove(file.path)\n",
"if len(os.listdir(TESTING_DOGS_DIR)) > 0:\n",
" for file in os.scandir(TESTING_DOGS_DIR):\n",
" os.remove(file.path)\n",
"\n",
"# Define proportion of images used for training\n",
"split_size = .9\n",
"\n",
"# Run the function\n",
"# NOTE: Messages about zero length images should be printed out\n",
"split_data(CAT_SOURCE_DIR, TRAINING_CATS_DIR, TESTING_CATS_DIR, split_size)\n",
"split_data(DOG_SOURCE_DIR, TRAINING_DOGS_DIR, TESTING_DOGS_DIR, split_size)\n",
"\n",
"# Check that the number of images matches the expected output\n",
"print(f\"\\n\\nThere are {len(os.listdir(TRAINING_CATS_DIR))} images of cats for training\")\n",
"print(f\"There are {len(os.listdir(TRAINING_DOGS_DIR))} images of dogs for training\")\n",
"print(f\"There are {len(os.listdir(TESTING_CATS_DIR))} images of cats for testing\")\n",
"print(f\"There are {len(os.listdir(TESTING_DOGS_DIR))} images of dogs for testing\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "hvskJNOFVSaz"
},
"source": [
"**Expected Output:**\n",
"\n",
"```\n",
"666.jpg is zero length, so ignoring.\n",
"11702.jpg is zero length, so ignoring.\n",
"```\n",
"\n",
"```\n",
"There are 11250 images of cats for training\n",
"There are 11250 images of dogs for training\n",
"There are 1250 images of cats for testing\n",
"There are 1250 images of dogs for testing\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Zil4QmOD_mXF"
},
"source": [
"Now that you have successfully organized the data in a way that can be easily fed to Keras' `ImageDataGenerator`, it is time for you to code the generators that will yield batches of images, both for training and validation. For this, complete the `train_val_generators` function below.\n",
"\n",
"Something important to note is that the images in this dataset come in a variety of resolutions. Luckily, the `flow_from_directory` method allows you to standarize this by defining a tuple called `target_size` that will be used to convert each image to this target resolution. **For this exercise use a `target_size` of (150, 150)**.\n",
"\n",
"**Note:** So far, you have seen the term `testing` being used a lot for referring to a subset of images within the dataset. In this exercise, all of the `testing` data is actually being used as `validation` data. This is not very important within the context of the task at hand but it is worth mentioning to avoid confusion."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "code",
"id": "fQrZfVgz4j2g"
},
"outputs": [],
"source": [
"# GRADED FUNCTION: train_val_generators\n",
"def train_val_generators(TRAINING_DIR, VALIDATION_DIR):\n",
" ### START CODE HERE\n",
"\n",
" # Instantiate the ImageDataGenerator class (don't forget to set the arguments to augment the images)\n",
" \n",
" train_datagen = ImageDataGenerator(rescale=1.0/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",
" # Pass in the appropriate arguments to the flow_from_directory method\n",
" train_generator = train_datagen.flow_from_directory(directory=TRAINING_DIR,\n",
" batch_size=128,\n",
" class_mode='binary',\n",
" target_size=(150, 150))\n",
"\n",
" # Instantiate the ImageDataGenerator class (don't forget to set the rescale argument)\n",
" validation_datagen = ImageDataGenerator(rescale=1.0/255.)\n",
"\n",
" # Pass in the appropriate arguments to the flow_from_directory method\n",
" validation_generator = validation_datagen.flow_from_directory(directory=VALIDATION_DIR,\n",
" batch_size=16,\n",
" class_mode='binary',\n",
" target_size=(150, 150))\n",
" ### END CODE HERE\n",
" return train_generator, validation_generator\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "qM7FxrjGiobD",
"outputId": "e83f12ce-adbc-4dc5-a979-52b5975f5fa4",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Found 22498 images belonging to 2 classes.\n",
"Found 2500 images belonging to 2 classes.\n"
]
}
],
"source": [
"# Test your generators\n",
"train_generator, validation_generator = train_val_generators(TRAINING_DIR, TESTING_DIR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "tiPNmSfZjHwJ"
},
"source": [
"**Expected Output:**\n",
"\n",
"```\n",
"Found 22498 images belonging to 2 classes.\n",
"Found 2500 images belonging to 2 classes.\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "TI3oEmyQCZoO"
},
"source": [
"One last step before training is to define the architecture of the model that will be trained.\n",
"\n",
"Complete the `create_model` function below which should return a Keras' `Sequential` model.\n",
"\n",
"Aside from defining the architecture of the model, you should also compile it so make sure to use a `loss` function that is compatible with the `class_mode` you defined in the previous exercise, which should also be compatible with the output of your network. You can tell if they aren't compatible if you get an error during training.\n",
"\n",
"**Note that you should use at least 3 convolution layers to achieve the desired performance.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "code",
"id": "oDPK8tUB_O9e",
"lines_to_next_cell": 2
},
"outputs": [],
"source": [
"# GRADED FUNCTION: create_model\n",
"def create_model():\n",
" # DEFINE A KERAS MODEL TO CLASSIFY CATS V DOGS\n",
" # USE AT LEAST 3 CONVOLUTION LAYERS\n",
"\n",
" ### START CODE HERE\n",
"\n",
" model = tf.keras.models.Sequential([ \n",
" tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),\n",
" tf.keras.layers.MaxPooling2D(2, 2),\n",
" tf.keras.layers.Conv2D(32, (3,3), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\n",
" tf.keras.layers.MaxPooling2D(2,2),\n",
" tf.keras.layers.Flatten(),\n",
" # 512 neuron hidden layer\n",
" tf.keras.layers.Dense(512, activation='relu'),\n",
" # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other ('humans')\n",
" tf.keras.layers.Dense(1, activation='sigmoid')\n",
" ])\n",
"\n",
" \n",
" model.compile(loss='binary_crossentropy',\n",
" optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.001),\n",
" metrics=['accuracy']) \n",
" \n",
" ### END CODE HERE\n",
"\n",
" return model\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "SMFNJZmTCZv6"
},
"source": [
"Now it is time to train your model!\n",
"\n",
"Note: You can ignore the `UserWarning: Possibly corrupt EXIF data.` warnings."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "5qE1G6JB4fMn",
"outputId": "70c05564-85a4-48ed-d24e-559eb96cc29f",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Epoch 1/15\n",
" 41/176 [=====>........................] - ETA: 2:25 - loss: 0.6949 - accuracy: 0.5171"
]
},
{
"output_type": "stream",
"name": "stderr",
"text": [
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 32 bytes but only got 0. Skipping tag 270\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 5 bytes but only got 0. Skipping tag 271\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 8 bytes but only got 0. Skipping tag 272\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 8 bytes but only got 0. Skipping tag 282\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 8 bytes but only got 0. Skipping tag 283\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 20 bytes but only got 0. Skipping tag 306\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:770: UserWarning: Possibly corrupt EXIF data. Expecting to read 48 bytes but only got 0. Skipping tag 532\n",
" \" Skipping tag %s\" % (size, len(data), tag)\n",
"/usr/local/lib/python3.7/dist-packages/PIL/TiffImagePlugin.py:788: UserWarning: Corrupt EXIF data. Expecting to read 2 bytes but only got 0. \n",
" warnings.warn(str(msg))\n"
]
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"176/176 [==============================] - 202s 1s/step - loss: 0.6812 - accuracy: 0.5660 - val_loss: 0.6442 - val_accuracy: 0.6196\n",
"Epoch 2/15\n",
"176/176 [==============================] - 200s 1s/step - loss: 0.6515 - accuracy: 0.6202 - val_loss: 0.6339 - val_accuracy: 0.6108\n",
"Epoch 3/15\n",
"176/176 [==============================] - 200s 1s/step - loss: 0.6235 - accuracy: 0.6516 - val_loss: 0.5921 - val_accuracy: 0.6892\n",
"Epoch 4/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.6007 - accuracy: 0.6729 - val_loss: 0.5678 - val_accuracy: 0.7076\n",
"Epoch 5/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.5808 - accuracy: 0.6959 - val_loss: 0.5291 - val_accuracy: 0.7304\n",
"Epoch 6/15\n",
"176/176 [==============================] - 200s 1s/step - loss: 0.5627 - accuracy: 0.7114 - val_loss: 0.5018 - val_accuracy: 0.7624\n",
"Epoch 7/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.5448 - accuracy: 0.7242 - val_loss: 0.4670 - val_accuracy: 0.7784\n",
"Epoch 8/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.5298 - accuracy: 0.7358 - val_loss: 0.4790 - val_accuracy: 0.7660\n",
"Epoch 9/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.5205 - accuracy: 0.7397 - val_loss: 0.4406 - val_accuracy: 0.7896\n",
"Epoch 10/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.5016 - accuracy: 0.7544 - val_loss: 0.4138 - val_accuracy: 0.8024\n",
"Epoch 11/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.4826 - accuracy: 0.7627 - val_loss: 0.3815 - val_accuracy: 0.8300\n",
"Epoch 12/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.4647 - accuracy: 0.7775 - val_loss: 0.4196 - val_accuracy: 0.8072\n",
"Epoch 13/15\n",
"176/176 [==============================] - 200s 1s/step - loss: 0.4542 - accuracy: 0.7849 - val_loss: 0.3984 - val_accuracy: 0.8316\n",
"Epoch 14/15\n",
"176/176 [==============================] - 200s 1s/step - loss: 0.4277 - accuracy: 0.7974 - val_loss: 0.3594 - val_accuracy: 0.8340\n",
"Epoch 15/15\n",
"176/176 [==============================] - 201s 1s/step - loss: 0.4195 - accuracy: 0.8057 - val_loss: 0.3319 - val_accuracy: 0.8536\n"
]
}
],
"source": [
"# Get the untrained model\n",
"model = create_model()\n",
"\n",
"# Train the model\n",
"# Note that this may take some time.\n",
"history = model.fit(train_generator,\n",
" epochs=15,\n",
" verbose=1,\n",
" validation_data=validation_generator)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VGsaDMc-GMd4"
},
"source": [
"Once training has finished, you can run the following cell to check the training and validation accuracy achieved at the end of each epoch.\n",
"\n",
"**To pass this assignment, your model should achieve a training and validation accuracy of at least 80% and the final testing accuracy should be either higher than the training one or have a 5% difference at maximum**. If your model didn't achieve these thresholds, try training again with a different model architecture, remember to use at least 3 convolutional layers or try tweaking the image augmentation process.\n",
"\n",
"You might wonder why the training threshold to pass this assignment is significantly lower compared to last week's assignment. Image augmentation does help with overfitting but usually this comes at the expense of requiring more training time. To keep the training time reasonable, the same number of epochs as in the previous assignment are kept. \n",
"\n",
"However, as an optional exercise you are encouraged to try training for more epochs and to achieve really good training and validation accuracies."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "MWZrJN4-65RC",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 546
},
"outputId": "bb1cb450-3b9d-4072-8480-c6333f9ad9e3"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
}
],
"source": [
"#-----------------------------------------------------------\n",
"# Retrieve a list of list results on training and test data\n",
"# sets for each training epoch\n",
"#-----------------------------------------------------------\n",
"acc=history.history['accuracy']\n",
"val_acc=history.history['val_accuracy']\n",
"loss=history.history['loss']\n",
"val_loss=history.history['val_loss']\n",
"\n",
"epochs=range(len(acc)) # Get number of epochs\n",
"\n",
"#------------------------------------------------\n",
"# Plot training and validation accuracy per epoch\n",
"#------------------------------------------------\n",
"plt.plot(epochs, acc, 'r', \"Training Accuracy\")\n",
"plt.plot(epochs, val_acc, 'b', \"Validation Accuracy\")\n",
"plt.title('Training and validation accuracy')\n",
"plt.show()\n",
"print(\"\")\n",
"\n",
"#------------------------------------------------\n",
"# Plot training and validation loss per epoch\n",
"#------------------------------------------------\n",
"plt.plot(epochs, loss, 'r', \"Training Loss\")\n",
"plt.plot(epochs, val_loss, 'b', \"Validation Loss\")\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NYIaqsN2pav6"
},
"source": [
"You will probably encounter that the model is overfitting, which means that it is doing a great job at classifying the images in the training set but struggles with new data. This is perfectly fine and you will learn how to mitigate this issue in the upcomming week.\n",
"\n",
"Before closing the assignment, be sure to also download the `history.pkl` file which contains the information of the training history of your model. You can download this file by running the cell below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "yWcrc9nZTsHj",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 17
},
"outputId": "554362a0-0886-40f0-e548-a234659c089d"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"application/javascript": [
"\n",
" async function download(id, filename, size) {\n",
" if (!google.colab.kernel.accessAllowed) {\n",
" return;\n",
" }\n",
" const div = document.createElement('div');\n",
" const label = document.createElement('label');\n",
" label.textContent = `Downloading \"${filename}\": `;\n",
" div.appendChild(label);\n",
" const progress = document.createElement('progress');\n",
" progress.max = size;\n",
" div.appendChild(progress);\n",
" document.body.appendChild(div);\n",
"\n",
" const buffers = [];\n",
" let downloaded = 0;\n",
"\n",
" const channel = await google.colab.kernel.comms.open(id);\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
"\n",
" for await (const message of channel.messages) {\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
" if (message.buffers) {\n",
" for (const buffer of message.buffers) {\n",
" buffers.push(buffer);\n",
" downloaded += buffer.byteLength;\n",
" progress.value = downloaded;\n",
" }\n",
" }\n",
" }\n",
" const blob = new Blob(buffers, {type: 'application/binary'});\n",
" const a = document.createElement('a');\n",
" a.href = window.URL.createObjectURL(blob);\n",
" a.download = filename;\n",
" div.appendChild(a);\n",
" a.click();\n",
" div.remove();\n",
" }\n",
" "
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"application/javascript": [
"download(\"download_18f50ab6-831e-41ab-b7c4-9175ac5e4e59\", \"history_augmented.pkl\", 628)"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {}
}
],
"source": [
"def download_history():\n",
" import pickle\n",
" from google.colab import files\n",
"\n",
" with open('history_augmented.pkl', 'wb') as f:\n",
" pickle.dump(history.history, f)\n",
"\n",
" files.download('history_augmented.pkl')\n",
"\n",
"download_history()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "yEj7UVe0OgMq"
},
"source": [
"You will also need to submit this notebook for grading. To download it, click on the `File` tab in the upper left corner of the screen then click on `Download` -> `Download .ipynb`. You can name it anything you want as long as it is a valid `.ipynb` (jupyter notebook) file."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "joAaZSWWpbOI"
},
"source": [
"**Congratulations on finishing this week's assignment!**\n",
"\n",
"You have successfully implemented a convolutional neural network that classifies images of cats and dogs, along with the helper functions needed to pre-process the images!\n",
"\n",
"**Keep it up!**"
]
}
],
"metadata": {
"accelerator": "GPU",
"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.4"
},
"colab": {
"name": "C2W2_Assignment.ipynb",
"provenance": [],
"include_colab_link": true
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment