{"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":"# Cats and Dogs Classifier using Keras and CNN","metadata":{}},{"cell_type":"markdown","source":"## Define Variables for Path and Stuff","metadata":{}},{"cell_type":"code","source":"INPUT_DIR = \"/kaggle/input/dogs-vs-cats/\"\nWORK_DIR = \"/kaggle/working/\"\nclasses = [\"cat\", \"dog\"]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Preparation\n#### Extract Training and Test Data to folders","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport os, shutil\n\nzipitems = [\"test1.zip\",\"train.zip\"]\n\nfor zipitem in zipitems:\n    with zipfile.ZipFile(os.path.join(INPUT_DIR, zipitem) , mode=\"r\") as archive:\n        archive.extractall(WORK_DIR)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_TEST = WORK_DIR + zipitems[0].split('.')[0]\nBASE_TRAIN =  WORK_DIR + zipitems[1].split('.')[0]\n\n# Print for debugging\nprint(\"BASE_TEST = \", BASE_TEST)\nprint(\"BASE_TRAIN = \", BASE_TRAIN)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This is a lazy, hard-coded approach, FIXME please :)\n# I think it can be improved further still\nVALIDATION_DIR_ROOT = os.path.join(WORK_DIR, 'validation')\nTRAIN_DIR_ROOT = os.path.join(WORK_DIR, 'training')\n\nif not os.path.exists(VALIDATION_DIR_ROOT):\n    os.mkdir(VALIDATION_DIR_ROOT)\nif not os.path.exists(TRAIN_DIR_ROOT):\n    os.mkdir(TRAIN_DIR_ROOT)\n\nfor cat_dog in classes:\n    if not os.path.exists(os.path.join(VALIDATION_DIR_ROOT, cat_dog)):\n        os.mkdir(os.path.join(VALIDATION_DIR_ROOT, cat_dog))\n    if not os.path.exists(os.path.join(TRAIN_DIR_ROOT, cat_dog)):\n        os.mkdir(os.path.join(TRAIN_DIR_ROOT, cat_dog))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Separate Cats and Dogs in Train Folder","metadata":{}},{"cell_type":"code","source":"# Copy first 2000 Images to Validation Set and use rest of the images as Training Set\n\ncat_count = 0\ndog_count = 0\nVAL_SIZE = 2000\n\nDST_DIR_CAT = VALIDATION_DIR_ROOT\nDST_DIR_DOG = VALIDATION_DIR_ROOT\n\nfor file in os.listdir(BASE_TRAIN):\n    if file.startswith(\"cat\"):\n        if(cat_count >= VAL_SIZE):\n            DST_DIR_CAT = TRAIN_DIR_ROOT     # Switch to training set\n        src = os.path.join(BASE_TRAIN, file)\n        dst = os.path.join(DST_DIR_CAT, classes[0], file)\n        shutil.copyfile(src, dst)\n        cat_count = cat_count + 1\n    else:\n        if(dog_count >= VAL_SIZE):\n            DST_DIR_DOG = TRAIN_DIR_ROOT     # Switch to training set\n        src = os.path.join(BASE_TRAIN, file)\n        dst = os.path.join(DST_DIR_DOG, classes[1], file)\n        shutil.copyfile(src, dst)\n        dog_count = dog_count + 1\n        \n# Verify number of files copied\nprint(len(os.listdir(os.path.join(TRAIN_DIR_ROOT, classes[0]))))\nprint(len(os.listdir(os.path.join(TRAIN_DIR_ROOT, classes[1]))))\nprint(len(os.listdir(os.path.join(VALIDATION_DIR_ROOT, classes[0]))))\nprint(len(os.listdir(os.path.join(VALIDATION_DIR_ROOT, classes[1]))))\n\nprint(\"cats = \",cat_count)\nprint(\"dogs = \", dog_count)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Begin Model Creation\n#### Import Libraries","metadata":{}},{"cell_type":"code","source":"# Need to run this cell twice to remove error/warning\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nfrom tensorflow import keras\nfrom keras import layers\nfrom keras import models\nfrom keras import optimizers\nfrom keras.preprocessing import image\nfrom keras.preprocessing.image import ImageDataGenerator","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Create Conv2D Network\n\nInput will be a 150 x 150 size image (size is selected arbitrarily)","metadata":{}},{"cell_type":"code","source":"model = models.Sequential()\nmodel.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(128, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(128, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Flatten())\nmodel.add(layers.Dense(512, activation='relu'))\nmodel.add(layers.Dense(1, activation='sigmoid'))\n\nmodel.summary()\n\nmodel.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(learning_rate=1e-4), metrics=['acc'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Data Pre-processing","metadata":{}},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(rescale=1./255)\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow_from_directory(\n    TRAIN_DIR_ROOT,\n    target_size=(150, 150),\n    batch_size=20,\n    class_mode='binary')\n\nvalidation_generator = test_datagen.flow_from_directory(\n    VALIDATION_DIR_ROOT,\n    target_size=(150, 150),\n    batch_size=20,\n    class_mode='binary')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Train Model","metadata":{}},{"cell_type":"code","source":"# Make Generators yield \"Total Size / Batch Size\" items before declaring an epoch over\nstep_size_train = (len(os.listdir(os.path.join(TRAIN_DIR_ROOT, classes[0])))) / 20\nstep_size_val = (len(os.listdir(os.path.join(VALIDATION_DIR_ROOT, classes[0])))) / 20\n\n# Begin Training\nhistory = model.fit(\n    train_generator,\n    steps_per_epoch=step_size_train,\n    epochs=30,\n    validation_data=validation_generator,\n    validation_steps=step_size_val)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Training vs Validation Plot","metadata":{}},{"cell_type":"code","source":"acc = history.history['acc']\nval_acc = history.history['val_acc']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\nepochs = range(1, len(acc) + 1)\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()\nplt.figure()\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()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define a New Model with Dropout for better accuracy\n#### Data Augmentation\n\nFollowing code is an example of how to make Augmented Generators\n```py\ndatagen = 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    ```","metadata":{}},{"cell_type":"markdown","source":"#### Compile New Model","metadata":{}},{"cell_type":"code","source":"model = models.Sequential()\nmodel.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(64, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(128, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Conv2D(128, (3, 3), activation='relu'))\nmodel.add(layers.MaxPooling2D((2, 2)))\nmodel.add(layers.Flatten())\nmodel.add(layers.Dropout(0.5))\nmodel.add(layers.Dense(512, activation='relu'))\nmodel.add(layers.Dense(1, activation='sigmoid'))\n\nmodel.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(learning_rate=1e-4), metrics=['acc'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Create Augmentation Generators","metadata":{}},{"cell_type":"code","source":"train_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\n# Do not augment Validation Set\nval_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow_from_directory(\n    TRAIN_DIR_ROOT,\n    target_size=(150, 150),\n    batch_size=32,\n    class_mode='binary')\n\nvalidation_generator = val_datagen.flow_from_directory(\n    VALIDATION_DIR_ROOT,\n    target_size=(150, 150),\n    batch_size=32,\n    class_mode='binary')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Train New Model","metadata":{}},{"cell_type":"code","source":"history = model.fit(\n    train_generator,\n    steps_per_epoch=step_size_train,\n    epochs=100,\n    validation_data=validation_generator,\n    validation_steps=step_size_val)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### New Training vs Validation Plot","metadata":{}},{"cell_type":"code","source":"acc = history.history['acc']\nval_acc = history.history['val_acc']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\nepochs = range(1, len(acc) + 1)\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()\nplt.figure()\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()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}