diff --git a/Course 2: Convolutional Neural Networks in Tensorflow/README.md b/Course 2: Convolutional Neural Networks in Tensorflow/README.md index e69de29..cbe0dd4 100644 --- a/Course 2: Convolutional Neural Networks in Tensorflow/README.md +++ b/Course 2: Convolutional Neural Networks in Tensorflow/README.md @@ -0,0 +1,80 @@ +# Course 2: Convolutional Neural Networks in TensorFlow + +**_Course Link_**: [Convolutional Neural Networks in TensorFlow](https://www.coursera.org/learn/convolutional-neural-networks-tensorflow) + +## Achievement goal: + +
+ +
+ +## Solution + +### Week 1 + +- Quiz: ++ +
+ ++ +
+ ++ +
+ +- [Programming assignment](). + +### Week 2 + +- Quiz: ++ +
+ ++ +
+ ++ +
+ +- [Programming assignment](). + +### Week 3 + +- Quiz: ++ +
+ ++ +
+ +- [Programming assignment](). + +### Week 4 + +- Quiz: ++ +
+ ++ +
+ ++ +
+ +- [Programming assignment](). + +## Contributors: + +- 🐮 [@honghanhh](https://github.com/honghanhh) +- 🐔 [@tiena2cva](https://github.com/tiena2cva) diff --git a/Course 2: Convolutional Neural Networks in Tensorflow/Week 1/Exercise_1_Cats_vs_Dogs_Question-FINAL.ipynb b/Course 2: Convolutional Neural Networks in Tensorflow/Week 1/Exercise_1_Cats_vs_Dogs_Question-FINAL.ipynb new file mode 100644 index 0000000..5963b5c --- /dev/null +++ b/Course 2: Convolutional Neural Networks in Tensorflow/Week 1/Exercise_1_Cats_vs_Dogs_Question-FINAL.ipynb @@ -0,0 +1,479 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "dn-6c02VmqiN" + }, + "outputs": [], + "source": [ + "# ATTENTION: Please do not alter any of the provided code in the exercise. Only add your own code where indicated\n", + "# ATTENTION: Please do not add or remove any cells in the exercise. The grader will check specific cells based on the cell position.\n", + "# ATTENTION: Please use the provided epoch values when training.\n", + "\n", + "# In this exercise you will train a CNN on the FULL Cats-v-dogs dataset\n", + "# This will require you doing a lot of data preprocessing because\n", + "# the dataset isn't split into training and validation for you\n", + "# This code block has all the required inputs\n", + "import os\n", + "import zipfile\n", + "import random\n", + "import tensorflow as tf\n", + "import shutil\n", + "from tensorflow.keras.optimizers import RMSprop\n", + "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n", + "from tensorflow.keras.models import Sequential\n", + "from tensorflow.keras.layers import Flatten, Dense, Conv2D, MaxPooling2D\n", + "from tensorflow.keras.callbacks import Callback\n", + "from shutil import copyfile\n", + "from os import getcwd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3sd9dQWa23aj" + }, + "outputs": [], + "source": [ + "path_cats_and_dogs = f\"{getcwd()}/../tmp2/cats-and-dogs.zip\"\n", + "shutil.rmtree('/tmp')\n", + "\n", + "local_zip = path_cats_and_dogs\n", + "zip_ref = zipfile.ZipFile(local_zip, 'r')\n", + "zip_ref.extractall('/tmp')\n", + "zip_ref.close()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gi3yD62a6X3S" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1500\n", + "1500\n" + ] + } + ], + "source": [ + "print(len(os.listdir('/tmp/PetImages/Cat/')))\n", + "print(len(os.listdir('/tmp/PetImages/Dog/')))\n", + "\n", + "# Expected Output:\n", + "# 1500\n", + "# 1500" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "F-QkLjxpmyK2" + }, + "outputs": [], + "source": [ + "# Use os.mkdir to create your directories\n", + "# 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'\n", + "try:\n", + " #YOUR CODE GOES HERE\n", + " main_dir = \"/tmp/cats-v-dogs/\"\n", + " \n", + " train_dir = os.path.join(main_dir, \"training\")\n", + " test_dir = os.path.join(main_dir, \"testing\")\n", + " \n", + " cats_train = os.path.join(train_dir, \"cats\")\n", + " dogs_train = os.path.join(train_dir, \"dogs\")\n", + " \n", + " cats_test = os.path.join(test_dir, \"cats\")\n", + " dogs_test = os.path.join(test_dir, \"dogs\")\n", + " \n", + " os.mkdir(main_dir)\n", + " \n", + " os.mkdir(train_dir)\n", + " os.mkdir(test_dir)\n", + " \n", + " os.mkdir(cats_train)\n", + " os.mkdir(dogs_train)\n", + " \n", + " os.mkdir(cats_test)\n", + " os.mkdir(dogs_test)\n", + " \n", + "except OSError:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zvSODo0f9LaU" + }, + "outputs": [], + "source": [ + "# Write a python function called split_data which takes\n", + "# a SOURCE directory containing the files\n", + "# a TRAINING directory that a portion of the files will be copied to\n", + "# a TESTING directory that a portion of the files will be copie to\n", + "# a SPLIT SIZE to determine the portion\n", + "# The files should also be randomized, so that the training set is a random\n", + "# X% of the files, and the test set is the remaining files\n", + "# SO, for example, if SOURCE is PetImages/Cat, and SPLIT SIZE is .9\n", + "# 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", + "# Also -- All images should be checked, and if they have a zero file length,\n", + "# they will not be copied over\n", + "#\n", + "# os.listdir(DIRECTORY) gives you a listing of the contents of that directory\n", + "# os.path.getsize(PATH) gives you the size of the file\n", + "# copyfile(source, destination) copies a file from source to destination\n", + "# random.sample(list, len(list)) shuffles a list\n", + "def split_data(SOURCE, TRAINING, TESTING, SPLIT_SIZE):\n", + "# YOUR CODE STARTS HERE\n", + " data = os.listdir(SOURCE)\n", + " data = random.sample(data, len(data)) # shuffled\n", + " for count, file in enumerate(data):\n", + " if(count < SPLIT_SIZE * len(data)) and os.path.getsize(f\"{SOURCE}/{file}\")!=0:\n", + " copyfile(f\"{SOURCE}/{file}\", f\"{TRAINING}/{file}\")\n", + " elif (count >= SPLIT_SIZE * len(data)) and os.path.getsize(f\"{SOURCE}/{file}\")!=0:\n", + " copyfile(f\"{SOURCE}/{file}\", f\"{TESTING}/{file}\")\n", + "# YOUR CODE ENDS HERE\n", + "\n", + "\n", + "CAT_SOURCE_DIR = \"/tmp/PetImages/Cat/\"\n", + "TRAINING_CATS_DIR = \"/tmp/cats-v-dogs/training/cats/\"\n", + "TESTING_CATS_DIR = \"/tmp/cats-v-dogs/testing/cats/\"\n", + "DOG_SOURCE_DIR = \"/tmp/PetImages/Dog/\"\n", + "TRAINING_DOGS_DIR = \"/tmp/cats-v-dogs/training/dogs/\"\n", + "TESTING_DOGS_DIR = \"/tmp/cats-v-dogs/testing/dogs/\"\n", + "\n", + "split_size = .9\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)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "luthalB76ufC" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1350\n", + "1350\n", + "150\n", + "150\n" + ] + } + ], + "source": [ + "print(len(os.listdir('/tmp/cats-v-dogs/training/cats/')))\n", + "print(len(os.listdir('/tmp/cats-v-dogs/training/dogs/')))\n", + "print(len(os.listdir('/tmp/cats-v-dogs/testing/cats/')))\n", + "print(len(os.listdir('/tmp/cats-v-dogs/testing/dogs/')))\n", + "\n", + "# Expected output:\n", + "# 1350\n", + "# 1350\n", + "# 150\n", + "# 150" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-BQrav4anTmj" + }, + "outputs": [], + "source": [ + "# DEFINE A KERAS MODEL TO CLASSIFY CATS V DOGS\n", + "# USE AT LEAST 3 CONVOLUTION LAYERS\n", + "model = Sequential([\n", + " # Your Code Here\n", + " Conv2D(16, (3,3), activation = 'relu', input_shape = (150,150,3)),\n", + " MaxPooling2D(2,2),\n", + " Conv2D(32, (3,3), activation = 'relu'),\n", + " MaxPooling2D(2,2),\n", + " Conv2D(64, (3,3), activation = 'relu'),\n", + " MaxPooling2D(2,2),\n", + " Flatten(),\n", + " Dense(512, activation = 'relu'),\n", + " Dense(1, activation = 'sigmoid')\n", + " ])\n", + "\n", + "model.compile(optimizer=RMSprop(lr=0.001), loss='binary_crossentropy', metrics=['acc'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# NOTE:\n", + "\n", + "In the cell below you **MUST** use a batch size of 10 (`batch_size=10`) for the `train_generator` and the `validation_generator`. Using a batch size greater than 10 will exceed memory limits on the Coursera platform." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mlNjoJ5D61N6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 2700 images belonging to 2 classes.\n", + "Found 300 images belonging to 2 classes.\n" + ] + } + ], + "source": [ + "TRAINING_DIR = train_dir#YOUR CODE HERE\n", + "train_datagen = ImageDataGenerator(rescale = 1./255) #YOUR CODE HERE\n", + "\n", + "# NOTE: YOU MUST USE A BATCH SIZE OF 10 (batch_size=10) FOR THE \n", + "# TRAIN GENERATOR.\n", + "train_generator = train_datagen.flow_from_directory(\n", + " TRAINING_DIR,\n", + " target_size = (150, 150),\n", + " batch_size = 10,\n", + " class_mode = 'binary'\n", + " )#YOUR CODE HERE\n", + "\n", + "VALIDATION_DIR = test_dir #YOUR CODE HERE\n", + "validation_datagen = ImageDataGenerator(rescale = 1./255) #YOUR CODE HERE\n", + "\n", + "# NOTE: YOU MUST USE A BACTH SIZE OF 10 (batch_size=10) FOR THE \n", + "# VALIDATION GENERATOR.\n", + "validation_generator = validation_datagen.flow_from_directory(\n", + " VALIDATION_DIR,\n", + " target_size = (150, 150),\n", + " batch_size = 10,\n", + " class_mode = 'binary'\n", + " )#YOUR CODE HERE\n", + "\n", + "# Expected Output:\n", + "# Found 2700 images belonging to 2 classes.\n", + "# Found 300 images belonging to 2 classes." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "KyS4n53w7DxC" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/2\n", + "270/270 [==============================] - 35s 128ms/step - loss: 2.5938 - acc: 0.5444 - val_loss: 0.6945 - val_acc: 0.6100\n", + "Epoch 2/2\n", + "270/270 [==============================] - 30s 113ms/step - loss: 0.6117 - acc: 0.6752 - val_loss: 0.5583 - val_acc: 0.7167\n" + ] + } + ], + "source": [ + "history = model.fit_generator(train_generator,\n", + " epochs=2,\n", + " verbose=1,\n", + " validation_data=validation_generator)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "MWZrJN4-65RC" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Training and validation loss')" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "