{"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":"code","source":"","metadata":{"id":"mu5fh2Lgi_GY"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Using 4 convolutional layers with 32, 64, 128 and 128 convolutions respectively.\n\nAlso, this will train for 100 epochs, because I want to plot the graph of loss and accuracy.","metadata":{"id":"FlVYmPLZjRpf"}},{"cell_type":"code","source":"!wget --no-check-certificate \\\n    https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip \\\n    -O /tmp/cats_and_dogs_filtered.zip\n  \nimport os\nimport zipfile\nimport tensorflow as tf\nfrom tensorflow.keras.optimizers import RMSprop\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nlocal_zip = '/tmp/cats_and_dogs_filtered.zip'\nzip_ref = zipfile.ZipFile(local_zip, 'r')\nzip_ref.extractall('/tmp')\nzip_ref.close()","metadata":{"id":"ujQpV47UjSUd","outputId":"30260c14-b30b-4daa-f657-b98ba78fc0be"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_dir = '/tmp/cats_and_dogs_filtered'\ntrain_dir = os.path.join(base_dir, 'train')\nvalidation_dir = os.path.join(base_dir, 'validation')\n\n# Directory with our training cat pictures\ntrain_cats_dir = os.path.join(train_dir, 'cats')\n\n# Directory with our training dog pictures\ntrain_dogs_dir = os.path.join(train_dir, 'dogs')\n\n# Directory with our validation cat pictures\nvalidation_cats_dir = os.path.join(validation_dir, 'cats')\n\n# Directory with our validation dog pictures\nvalidation_dogs_dir = os.path.join(validation_dir, 'dogs')","metadata":{"id":"AgU-A-BRjcTX"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**MODEL WITHOUT AUGMENTATION:**","metadata":{"id":"2H1Cfzylks2h"}},{"cell_type":"code","source":"\nmodel = tf.keras.models.Sequential([\n    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),\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(128, (3,3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Flatten(),\n    tf.keras.layers.Dense(512, activation='relu'),\n    tf.keras.layers.Dense(1, activation='sigmoid')\n])\n\nmodel.compile(loss='binary_crossentropy',\n              optimizer=RMSprop(lr=1e-4),\n              metrics=['accuracy'])\n\n# All images will be rescaled by 1./255\ntrain_datagen = ImageDataGenerator(rescale=1./255)\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\n# Flow training images in batches of 20 using train_datagen generator\ntrain_generator = train_datagen.flow_from_directory(\n        train_dir,  # This is the source directory for training images\n        target_size=(150, 150),  # All images will be resized to 150x150\n        batch_size=20,\n        # Since we use binary_crossentropy loss, we need binary labels\n        class_mode='binary')\n\n# Flow validation images in batches of 20 using test_datagen generator\nvalidation_generator = test_datagen.flow_from_directory(\n        validation_dir,\n        target_size=(150, 150),\n        batch_size=20,\n        class_mode='binary')\n\nhistory = model.fit(\n      train_generator,\n      steps_per_epoch=100,  # 2000 images = batch_size * steps\n      epochs=50,\n      validation_data=validation_generator,\n      validation_steps=50,  # 1000 images = batch_size * steps\n      verbose=2)\n","metadata":{"id":"jkdpXojFjvbz","outputId":"5765c5d8-0169-4c58-d67e-ceb903f64ece"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nacc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(len(acc))\n\nplt.plot(epochs, acc, 'b', label='Training accuracy')\nplt.plot(epochs, val_acc, 'g', label='Validation accuracy')\nplt.title('Training and validation accuracy')\nplt.legend()\nplt.figure()\n\nplt.plot(epochs, loss, 'b', label='Training Loss')\nplt.plot(epochs, val_loss, 'g', label='Validation Loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()","metadata":{"id":"3lSVQ3Nzlkix","outputId":"d6cdceb4-cfb7-4919-a80c-69d603ffc83e"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The Training Accuracy is close to 100%, and the validation accuracy is in the 70%-80% range. This is a great example of overfitting -- which in short means that it can do very well with images it has seen before, but not so well with images it hasn't. \n\nLet's see if we can do better to avoid overfitting -- and one simple method is to augment the images a bit.\n\nWhat if we tweak with the images to change this up a bit -- rotate the image, squash it, etc. That's what image augementation is all about. And there's an API that makes it easy...\n\nThere are properties on ImageGenerator that we can use to augment the image.\n\n```\n#Updated to do image augmentation\ntrain_datagen = ImageDataGenerator(\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\nThese are just a few of the options available (for more, see the Keras documentation. Let's quickly go over what we just wrote:\n\n- rotation_range is a value in degrees (0–180), a range within which to randomly rotate pictures.\n- width_shift and height_shift are ranges (as a fraction of total width or height) within which to randomly translate pictures vertically or horizontally.\n- shear_range is for randomly applying shearing transformations.\n- zoom_range is for randomly zooming inside pictures.\n- horizontal_flip is for randomly flipping half of the images horizontally. This is relevant when there are no assumptions of horizontal assymmetry (e.g. real-world pictures).\n- fill_mode is the strategy used for filling in newly created pixels, which can appear after a rotation or a width/height shift.\n","metadata":{"id":"5iT3sjQ-uQ36"}},{"cell_type":"markdown","source":"**Using Augmented Images**","metadata":{"id":"oLV9hlqtu8i8"}},{"cell_type":"code","source":"model = tf.keras.models.Sequential([\n    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),\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(128, (3,3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Flatten(),\n    tf.keras.layers.Dense(512, activation='relu'),\n    tf.keras.layers.Dense(1, activation='sigmoid')\n])\n\nmodel.compile(loss='binary_crossentropy',\n              optimizer=RMSprop(lr=1e-4),\n              metrics=['accuracy'])\n\n# This code has changed. Now instead of the ImageGenerator just rescaling\n# the image, we also rotate and do other operations\n# Updated to do image augmentation\ntrain_datagen = ImageDataGenerator(\n      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\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\n# Flow training images in batches of 20 using train_datagen generator\ntrain_generator = train_datagen.flow_from_directory(\n        train_dir,  # This is the source directory for training images\n        target_size=(150, 150),  # All images will be resized to 150x150\n        batch_size=20,\n        # Since we use binary_crossentropy loss, we need binary labels\n        class_mode='binary')\n\n# Flow validation images in batches of 20 using test_datagen generator\nvalidation_generator = test_datagen.flow_from_directory(\n        validation_dir,\n        target_size=(150, 150),\n        batch_size=20,\n        class_mode='binary')\n\nhistory = model.fit(\n      train_generator,\n      steps_per_epoch=100,  # 2000 images = batch_size * steps\n      epochs=20,\n      validation_data=validation_generator,\n      validation_steps=50,  # 1000 images = batch_size * steps\n      verbose=2)","metadata":{"id":"IlyO0bZ2tSda","outputId":"3ae1e8a6-8bd1-4d93-c917-3532c786fa28"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nacc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(len(acc))\n\nplt.plot(epochs, acc, 'b', label='Training accuracy')\nplt.plot(epochs, val_acc, 'g', label='Validation accuracy')\nplt.title('Training and validation accuracy')\n\nplt.figure()\n\nplt.plot(epochs, loss, 'b', label='Training Loss')\nplt.plot(epochs, val_loss, 'g', label='Validation Loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()","metadata":{"id":"3Dol1kSnvenp","outputId":"7ea1fa62-ceae-4bfd-8651-b60cb9f3d1f2"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"mK7NbzLKxCKb"},"execution_count":null,"outputs":[]}]}