{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"Inspiration for this notebook comes from this [Keras Blog](https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html) and the [VGG ConvNet paper](https://arxiv.org/pdf/1409.1556.pdf)"},{"metadata":{"trusted":true,"_uuid":"aa7cdc2729c16965787492a8461bb075a6e21079"},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom keras.models import Model\nfrom keras.layers import Input, Dropout, Flatten, Convolution2D, MaxPooling2D, Dense, Activation \nfrom keras.optimizers import SGD, RMSprop\nfrom keras.callbacks import ModelCheckpoint, Callback, EarlyStopping\nfrom keras.utils import np_utils\nimport os, cv2, random\n\n# %matplotlib inline\n# import keras\n# keras.backend.backend() #=> Tensorflow","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1061c8a7081db39401d034506d388ef1f270f50a"},"cell_type":"markdown","source":"# 1 - Preparing the data"},{"metadata":{"trusted":true,"_uuid":"2af2a3c27b43e1eb616fbc81e11ca348b80d094b","scrolled":false},"cell_type":"code","source":"# This function resizes the images to 64x64 and samples 2000 images (8%) of the data.\n# I also separated cats and dogs for exploratory analysis\n\nTRAIN_DIR = '../input/train'\nTEST_DIR = '../input/test'\nROWS = 64\nCOLS = 64\nCHANNELS = 3\n\ntrain_images = [TRAIN_DIR+\"/\"+i for i in os.listdir(TRAIN_DIR)]\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# train_images = train_dogs[:1000] + train_cats[:1000]\n# random.shuffle(train_images)\n# test_images = test_images[:25]\n\ndef read_image(file_path):\n    img= cv2.imread(file_path, cv2.IMREAD_COLOR)\n    return cv2.resize(img, (ROWS, COLS), interpolation=cv2.INTER_CUBIC)\n\ndef prep_data(images):\n    count = len(images)\n    data = np.ndarray((count, ROWS, COLS, CHANNELS), dtype=np.uint8)\n    \n    for i, image_file in enumerate(images):\n        image = read_image(image_file)\n        data[i] = image\n        if i%1500 == 0: print('Processed {} of {}'.format(i, count))\n    return data\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":{"trusted":true,"_uuid":"1d4463e2f7cbab63fc98277d263fa302e3c9c4e4"},"cell_type":"markdown","source":"# 2 - Generating the labels"},{"metadata":{"trusted":true,"_uuid":"61d19f207e0e3af2dbda7f715ae8a49938fab42f"},"cell_type":"code","source":"# We're dealing with classification problem here - (1) dogs (0) cats\nlabels = []\nfor i in train_images:\n    if 'dog' in i:\n        labels.append(1)\n    else:\n        labels.append(0)\n        \nsns.countplot(labels)\nplt.title('Cats and Dogs');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d4483e0a00a89c6e94bd7119627143ffd5a6feca"},"cell_type":"markdown","source":"# 3 - Checking out Cats and Dogs"},{"metadata":{"trusted":true,"_uuid":"a8afe20840cca426831c5b9b697fcc78d5e2d34d"},"cell_type":"code","source":"# A quick side-by-side comparison of the animals\ndef show_cats_and_dogs(idx):\n    cat = read_image(train_images[idx])\n    dog = read_image(train_dogs[idx])\n    pair = np.concatenate((cat, dog), axis=1)\n    plt.figure(figsize=(15, 5))\n    plt.imshow(pair)\n    plt.show()\n\nfor idx in range(2):\n    show_cats_and_dogs(idx)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e68eda8965dbba477b0a3dc2341ea4a696b5850c"},"cell_type":"markdown","source":"# 4 - CatdogNet-16\nA scaled down version of the VGG-16, with a few notable changes.\n- Number of convolution filters cut in half, fully connected (dense) layers scaled down\n- Optimizer changed to RMSprop\n- Output layer activation set to sigmooid for binary crossentropy\n- Some layers commented out for efficiency"},{"metadata":{"trusted":true,"_uuid":"9adaab5e9634d86b41640eff6de4cf8107b1a70f"},"cell_type":"code","source":"def build_model(N_Filters=32):\n    input_layer = Input((ROWS, COLS, CHANNELS), name=\"InputLayer\")\n    # Block 1\n    x = Convolution2D(N_Filters*1, (3,3), padding='same', activation='relu', name='block1_conv1')(input_layer)\n    x = Convolution2D(N_Filters*1, (3,3), padding='same', activation='relu', name='block1_conv2')(x)\n    x = MaxPooling2D((2,2), strides=(2,2), name='block1_pool')(x)\n    \n    # Block 2\n    x = Convolution2D(N_Filters*2, (3,3), padding='same', activation='relu', name='block2_conv1')(x)\n    x = Convolution2D(N_Filters*2, (3,3), padding='same', activation='relu', name='block2_conv2')(x)\n    x = MaxPooling2D((2,2), strides=(2,2), name='block2_pool')(x)\n    \n    # Block 3\n    x = Convolution2D(N_Filters*4, (3,3), padding='same', activation='relu', name='block3_conv1')(x)\n    x = Convolution2D(N_Filters*4, (3,3), padding='same', activation='relu', name='block3_conv2')(x)\n    x = MaxPooling2D((2,2), strides=(2,2), name='block3_pool')(x)\n    \n    # Block 4\n    x = Convolution2D(N_Filters*8, (3,3), padding='same', activation='relu', name='block4_conv1')(x)\n    x = Convolution2D(N_Filters*8, (3,3), padding='same', activation='relu', name='block4_conv2')(x)\n    x = MaxPooling2D((2,2), strides=(2,2), name='block4_pool')(x)\n    \n    x = Flatten(name='flatten')(x)\n    x = Dense(N_Filters*8, activation='relu', name='fc1')(x)\n    x = Dropout(0.5)(x)\n    x = Dense(N_Filters*8, activation='relu', name='fc2')(x)\n    x = Dropout(0.5)(x)\n    \n    output = Dense(1, activation='sigmoid')(x)\n    \n    model = Model(input_layer, output)\n    model.compile(optimizer=RMSprop(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy'])\n    return model\nbuild_model().summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"67acef950bbaf67b4b7cb5c626ff9c0c3d79181f","scrolled":false},"cell_type":"code","source":"## Callback for loss loggingper epoch\nbatch_size=16; epochs=10\nmodel = build_model()\nclass LossHistory(Callback):\n    def on_train_begin(self, logs={}):\n        self.losses = []\n        self.val_losses = []\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')\ndef run_catdog(batch_size=16, epochs=20):\n    history = LossHistory()\n    model.fit(train, labels, batch_size=batch_size, epochs=epochs,\n              validation_split=0.25, verbose=1, shuffle=True, callbacks=[history, early_stopping])\n    predictions = model.predict(test, verbose=1)\n    return predictions, history\npredictions, history = run_catdog()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b858ea4f1f838c04055b2ffbc886d107232027fd"},"cell_type":"markdown","source":"# 5 - Plot Loss Trend"},{"metadata":{"trusted":true,"_uuid":"8c04888e85b9aa04eea12888731b2b6bb7c4cd17"},"cell_type":"code","source":"loss = history.losses\nval_loss = history.val_losses\n\nplt.plot(loss, 'blue', label='Training Loss')\nplt.plot(val_loss, 'green', label='Validation Loss')\nplt.title('VGG-16 Loss Trend')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.xticks(range(0,epochs)[0::2])\nplt.legend();","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0c8fa937f1262f844119da7df11c331a1f224351"},"cell_type":"markdown","source":"# 6 - How'd We Do ?\nI'm pretty sure I can distinquich a cat from a dog 100% of time, but how confident is the model ?.\n\n<u> Tip : Run on the full dataset with GPU for LB logloss of ~0.4 and accuracy at approx 90%"},{"metadata":{"trusted":true,"_uuid":"6014fb75cc39e1930de9ec471a747ba55a3d9728"},"cell_type":"code","source":"for i in range(4):\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    plt.imshow(test[i])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f645df9b9434d4c1080eb9e96fb9b7ad4bb3babb"},"cell_type":"markdown","source":"# 7 - Save model"},{"metadata":{"trusted":true,"_uuid":"d53404f15e9e99c382f98732cd8e51d550ff92d7"},"cell_type":"code","source":"model.save_weights('model_wieghts.h5')\nmodel.save('model_keras.h5')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"84db2039f379c8c65dd1925029b3fdaac495423f"},"cell_type":"markdown","source":"# 8 - Generate .csv for submission"},{"metadata":{"trusted":true,"_uuid":"d1ac62f8e8d9c4d1be99aeb81a98f12c67ab7427"},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}