{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os, cv2, random\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\nfrom matplotlib import ticker\nimport seaborn as sns\n%matplotlib inline \n\nfrom keras.models import Sequential\nfrom keras.layers import Input, Dropout, Flatten, Convolution2D, MaxPooling2D, Dense, Activation\nfrom keras.optimizers import RMSprop\nfrom keras.callbacks import ModelCheckpoint, Callback, EarlyStopping\nfrom keras.utils import np_utils","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true,"collapsed":true},"cell_type":"code","source":"# Unzip the dataset\n!unzip ../input/dogs-vs-cats-redux-kernels-edition/train.zip -d train\n!unzip ../input/dogs-vs-cats-redux-kernels-edition/test.zip -d test","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preparing the Data"},{"metadata":{},"cell_type":"markdown","source":"#### This function can be resized to the images to 64x64 and samples 2000 images (8%) of the data to run efficiently as a Kaggle Kernel, but in this kernel I won't do that. I also separated cats and dogs for exploratory analysis."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"TRAIN_DIR = '../working/train/train/'\nTEST_DIR = '../working/test/test/'\n\n\nROWS = 64\nCOLS = 64\nCHANNELS = 3\n\n\ntrain_images = [TRAIN_DIR+i for i in os.listdir(TRAIN_DIR)] # use this for full dataset\ntrain_dogs =   [TRAIN_DIR+i for i in os.listdir(TRAIN_DIR) if 'dog' in i]\ntrain_cats =   [TRAIN_DIR+i for i in os.listdir(TRAIN_DIR) if 'cat' in i]\n\ntest_images =  [TEST_DIR+i for i in os.listdir(TEST_DIR)]\n\n'''\n# slice datasets for memory efficiency on Kaggle Kernels, delete if using full dataset\ntrain_images = train_dogs[:1000] + train_cats[:1000]\nrandom.shuffle(train_images)\ntest_images =  test_images[:25]\n'''\n\ndef read_image(file_path):\n    img = cv2.imread(file_path, cv2.IMREAD_COLOR) #cv2.IMREAD_GRAYSCALE\n    return cv2.resize(img, (ROWS, COLS), interpolation=cv2.INTER_CUBIC)\n\n\ndef prep_data(images):\n    count = len(images)\n    data = np.ndarray((count, CHANNELS, ROWS, COLS), dtype=np.uint8)\n\n    for i, image_file in enumerate(images):\n        image = read_image(image_file)\n        data[i] = image.T\n        if i%250 == 0: print('Processed {} of {}'.format(i, count))\n    \n    return data\n\ntrain = prep_data(train_images)\ntest = prep_data(test_images)\n\nprint(\"Train shape: {}\".format(train.shape))\nprint(\"Test shape: {}\".format(test.shape))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Generating the labels"},{"metadata":{},"cell_type":"markdown","source":"#### We're dealing with a binary classification problem here - (1) dog (0) cat. The lables can be created by looping over the file names in the train directory. It's nice to see the training data is perfectly balanced."},{"metadata":{"trusted":true},"cell_type":"code","source":"labels = []\nfor i in train_images:\n    if 'dog' in i:\n        labels.append(1)\n    else:\n        labels.append(0)\n\n# See the ratio of labels\nsns.countplot(labels ).set_title(\"Cat and Dogs\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## Checking out Cats and Dogs\n#### A quick side-by-side comparison of the animals.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_cats_and_dogs(idx):\n    cat = read_image(train_cats[idx])\n    dog = read_image(train_dogs[idx])\n    pair = np.concatenate((cat, dog), axis=1)\n    plt.figure(figsize=(10,5))\n    plt.imshow(pair)\n    plt.show()\n    \nfor idx in range(0,5):\n    show_cats_and_dogs(idx)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Your Average Cat and Dog Photo\n#### Just for fun, the mean pixel values for cats and dogs. I can almost see a resemblance, a ghost, if you will..."},{"metadata":{"trusted":true},"cell_type":"code","source":"dog_avg = np.array([dog[0].T for i, dog in enumerate(train) if labels[i]==1]).mean(axis=0)\nplt.imshow(dog_avg)\nplt.title('Your Average Dog')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cat_avg = np.array([cat[0].T for i, cat in enumerate(train) if labels[i]==0]).mean(axis=0)\nplt.imshow(cat_avg)\nplt.title('Your Average Cat')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## CatdogNet-16\n#### A scaled down version of the VGG-16, with a few notable changes.\n#### 1. Number of convolution filters cut in half, fully connected (dense) layers scaled down. \n#### 2. Optimizer changed to RMSprop. \n#### 3. Output layer activation set to sigmoid for binary crossentropy. \n#### 4. Some layers commented out for efficiency.\n#### The full network takes about 80s per epoch on a GTX1070 (or 2hr+ on CPU) on the full dataset. (This script only trains on 8% of the 25K images. )"},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = RMSprop(lr=1e-4)\nobjective = 'binary_crossentropy'\n\n\ndef catdog():\n    \n    model = Sequential()\n\n    model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=(3, ROWS, COLS), activation='relu'))\n    model.add(Convolution2D(32, 3, 3, border_mode='same', activation='relu'))\n    model.add(MaxPooling2D(pool_size=(1, 1)))\n\n    model.add(Convolution2D(64, 3, 3, border_mode='same', activation='relu'))\n    model.add(Convolution2D(64, 3, 3, border_mode='same', activation='relu'))\n    model.add(MaxPooling2D(pool_size=(1, 1)))\n    \n    model.add(Convolution2D(128, 3, 3, border_mode='same', activation='relu'))\n    model.add(Convolution2D(128, 3, 3, border_mode='same', activation='relu'))\n    model.add(MaxPooling2D(pool_size=(1, 1)))\n    \n    model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n    model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n#     model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n    model.add(MaxPooling2D(pool_size=(1, 1)))\n\n#     model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n#     model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n#     model.add(Convolution2D(256, 3, 3, border_mode='same', activation='relu'))\n#     model.add(MaxPooling2D(pool_size=(2, 2)))\n\n    model.add(Flatten())\n    model.add(Dense(256, activation='relu'))\n    model.add(Dropout(0.5))\n    \n    model.add(Dense(256, activation='relu'))\n    model.add(Dropout(0.5))\n\n    model.add(Dense(1))\n    model.add(Activation('sigmoid'))\n\n    model.compile(loss=objective, optimizer=optimizer, metrics=['accuracy'])\n    return model\n\n\nmodel = catdog()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Train and Predict\n#### I'm using Keras's early stopping callback to end training when the validation loss stops improving, otherwise the model will overfit. I will also be tracking the loss history on each epoch to visualize the overfitting trend.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"nb_epoch = 10\nbatch_size = 16\n\n## Callback for loss logging per epoch\nclass LossHistory(Callback):\n    def on_train_begin(self, logs={}):\n        self.losses = []\n        self.val_losses = []\n        \n    def on_epoch_end(self, batch, logs={}):\n        self.losses.append(logs.get('loss'))\n        self.val_losses.append(logs.get('val_loss'))\n\nearly_stopping = EarlyStopping(monitor='val_loss', patience=3, verbose=1, mode='auto')        \n        \ndef run_catdog():\n    \n    history = LossHistory()\n    model.fit(train, labels, batch_size=batch_size, nb_epoch=nb_epoch,\n              validation_split=0.25, verbose=0, shuffle=True, callbacks=[history, early_stopping])\n    \n\n    predictions = model.predict(test, verbose=0)\n    return predictions, history\n\npredictions, history = run_catdog()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot the lossess\n\nloss = history.losses\nval_loss = history.val_losses\n\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.title('VGG-16 Loss Trend')\nplt.plot(loss, 'blue', label='Training Loss')\nplt.plot(val_loss, 'green', label='Validation Loss')\nplt.xticks(range(0,nb_epoch)[0::2])\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## How'd We Do?\n#### I'm pretty sure I can distinguish a cat from a dog 100% of the time, but how confident is the model?..."},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(0,20):\n    if predictions[i, 0] >= 0.5: \n        print('I am {:.2%} sure this is a Dog'.format(predictions[i][0]))\n    else: \n        print('I am {:.2%} sure this is a Cat'.format(1-predictions[i][0]))\n        \n    plt.imshow(test[i].T)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Export to Output"},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv('../input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv')\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Import the Result\nsubmission[\"label\"] = predictions[:,0]\n\n# Round to binary\ndef round_to_int(row):\n    if (row[\"label\"]>= 0.5) :\n        return 1\n    else:\n        return 0\n    \nsubmission[\"label\"] = submission.apply(lambda row: round_to_int(row), axis = 1)\n\n# Check the data\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Export Submission\nexport_csv = submission.to_csv('ans.csv', index= False)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"# If you want to download the file directly\nfrom IPython.display import FileLink\nFileLink('ans.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}