{"cells":[{"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, Conv2D, MaxPooling2D, Dense, Activation,\n                          BatchNormalization, Concatenate)\nfrom keras.optimizers import SGD, RMSprop, Adam\nfrom keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\n\nfrom keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img\nfrom keras.utils import np_utils\nfrom sklearn.model_selection import train_test_split\nimport os, cv2, random, re, csv\nfrom tqdm import tqdm\n\nimport time\nt_start = time.time()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1061c8a7081db39401d034506d388ef1f270f50a"},"cell_type":"markdown","source":"# 1 - Preparing the data"},{"metadata":{"trusted":true,"_uuid":"fb2c08b9d93aaaf8c9a7d6911d8a1f11ba20ec8d"},"cell_type":"code","source":"TRAIN_DIR = '../input/train'\nTEST_DIR = '../input/test'\n\n# Initial size is 224\nROWS = 224\nCOLS = 224\nCHANNELS = 1\n\n# because of the limited resources we have, we have to adapt the BATCH_SIZE \n# With image size and complexity of the model (nb params)\nBATCH_SIZE=40\nEPOCHS=40\n\nkfold_splits = 3","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2af2a3c27b43e1eb616fbc81e11ca348b80d094b"},"cell_type":"code","source":"# Separating cats and dogs for exploratory analysis\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#### For testing purposes\ntrain_images = train_dogs[:4000] + train_cats[:4000]\ntest_images = test_images[:1000]\nrandom.shuffle(train_images)\n\ndef read_image(file_path):\n    img= cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)\n    return cv2.resize(img, (ROWS, COLS), interpolation=cv2.INTER_CUBIC)\n\ndef prep_data(images):\n    X = [] # images as arrays\n    y = [] # labels\n    for image_file in tqdm(images):\n        try:\n            image = read_image(image_file)\n            X.append(image)\n            if 'dog' in image_file: y.append(1)\n            elif 'cat' in image_file: y.append(0)\n        except:\n            pass #print(image_file)\n    X = np.array(X)\n    X = np.expand_dims(X, axis=3)\n    y = np.array(y)\n    return X, y\n\nprint(\"Processing Train images\")\nX_train, y_train = prep_data(train_images)\n\nprint(\"Train: {} images with shape {}\".format(len(X_train),X_train[0].shape))\nprint(\"Test: {} images\".format(len(test_images)))","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 = [1 if 'dog' in l else 0 for l in train_images]\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\nfor idx in range(2):\n    idx = idx + np.random.randint(low=1, high=100); # To randomize images\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=(15, 5))\n    f = plt.imshow(pair)\n    f.axes.get_xaxis().set_visible(False)\n    f.axes.get_yaxis().set_visible(False)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e68eda8965dbba477b0a3dc2341ea4a696b5850c"},"cell_type":"markdown","source":"# 4 - CatdogNet"},{"metadata":{"trusted":true,"_uuid":"9adaab5e9634d86b41640eff6de4cf8107b1a70f","scrolled":false},"cell_type":"code","source":"def convBatchActivMax_block(_input, N_Filters, N, kernel, blockNumber):\n    # N is used to Variate number of filters for each block\n    x = Conv2D(N_Filters* N, kernel_size=kernel, padding='same', activation='relu', name='block{}_conv{}_{}'.format(blockNumber, 1, kernel))(_input)\n    x = Conv2D(N_Filters* N, kernel_size=kernel, padding='same', name='block{}_conv{}_{}'.format(blockNumber, 2, kernel))(x)\n    x = BatchNormalization(name=\"block{}_BatchNorm_{}\".format(blockNumber,kernel))(x)\n    x = Activation('relu')(x)\n    \n    x = MaxPooling2D((2,2), strides=(2,2), name='block{}_pool_{}'.format(blockNumber, kernel))(x)\n    return x\n\ndef build_model(N_Filters=32):\n    input_layer = Input((ROWS, COLS, CHANNELS), name=\"InputLayer\")\n    \n    #----- Branch 1-------\n    ######################\n    # Block 1\n    x1 = convBatchActivMax_block(input_layer, N_Filters, 1, 3, 1)\n    # Block 2\n    x1 = convBatchActivMax_block(x1, N_Filters, 2, 3, 2)\n    # Block 3\n    x1 = convBatchActivMax_block(x1, N_Filters, 3, 3, 3)\n    \n    #----- Branch 2-------\n    ######################\n    # Block 1\n    x2 = convBatchActivMax_block(input_layer, N_Filters, 1, 5, 1)\n    # Block 2\n    x2 = convBatchActivMax_block(x2, N_Filters, 2, 5, 2)\n    # Block 3\n    x2 = convBatchActivMax_block(x2, N_Filters, 3, 5, 3)\n    \n    OutConcat = Concatenate()([x1,x2])\n    x = Conv2D(N_Filters*3, 1, activation='relu')(OutConcat)\n    \n    x = Flatten(name='flatten')(x)\n    x = Dense(N_Filters*10, activation='relu', name='fc1')(x)\n    x = Dropout(0.5)(x)\n    x = Dense(N_Filters*4, 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\nmodel = build_model()\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1e99530bae6008a8dc5ff85a8667eecda3067800","scrolled":false},"cell_type":"code","source":"#################\n# Plot The Model\n#################\nfrom keras.utils import plot_model \nplot_model(model, to_file='keras-baseline-architecture.png')\n\nfrom IPython.display import Image\nImage(filename='keras-baseline-architecture.png') ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c83df4e2395fd1bbc60831ba600dcf9aabbd3897"},"cell_type":"code","source":"###############################\n# Train & Validation Generators\n###############################\n# Augmentation configuration to use for training and validation\ntrain_datagen = ImageDataGenerator(\n        rescale=1. / 255,\n        rotation_range=40,\n        width_shift_range=0.2,\n        shear_range=0.2,\n        zoom_range=0.2,\n        horizontal_flip=True\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f5f60ee3fae7ce6f9fe63a7fa2a1c93a2186e0a2"},"cell_type":"code","source":"#############\n# Callbacks:\n############\ndef get_callbacks(name_weights, patience_lr):\n    erl_stop = EarlyStopping(monitor='val_loss', mode = 'min',patience=patience_lr*2, verbose=1)\n    mcp_save = ModelCheckpoint(name_weights, monitor='val_loss', mode='min', save_best_only=True, verbose=1)\n    reduce_lr_loss = ReduceLROnPlateau(monitor='val_loss', mode='min', factor=0.1, patience=patience_lr, min_lr=0.0001, verbose=1)\n    return [erl_stop, mcp_save, reduce_lr_loss]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a6f9d5784b82460a7fb247cc1b8c9bd77d7c406f","scrolled":false},"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\n\n# Instantiate the cross validator\nskf = StratifiedKFold(n_splits=kfold_splits, shuffle=True)\nhists = []\n# Loop through the indices the split() method returns\nfor index, (train_idx, val_idx) in enumerate(skf.split(X_train, y_train)):\n    print(\"___________________________________________\")\n    print(\"Training on fold (\" + str(index+1) + \"/\" + str(kfold_splits) + \")\")\n    print(\"___________________________________________\")\n    \n    # Generate batches from indices\n    xtrain = X_train[train_idx]\n    xval = X_train[val_idx] / 255\n    ytrain = y_train[train_idx]\n    yval = y_train[val_idx]\n    \n    name_weights = \"final_model_fold\" + str(index+1) + \"_weights.h5\"\n    Callbacks = get_callbacks(name_weights = name_weights, patience_lr=3)\n    \n    # Prepare generators for training and validation sets\n    train_generator = train_datagen.flow(xtrain, ytrain, batch_size=BATCH_SIZE)\n\n    # Clear model, and create it\n    model = None\n    model = build_model()\n    history = model.fit_generator(\n        train_generator, \n        steps_per_epoch = len(xtrain) // BATCH_SIZE,\n        callbacks = Callbacks,\n        epochs = EPOCHS,\n        shuffle=True,\n        validation_data = [xval, yval],\n        validation_steps = len(xval) // BATCH_SIZE,\n        verbose = 1\n    )\n    \n    hists.append(history)\n    accuracy_history = history.history['loss']\n    val_accuracy_history = history.history['val_loss']\n    print(\"\\nLast training Loss: \", str(accuracy_history[-1]), \", last validation accuracy: \", str(val_accuracy_history[-1]))\n    \n    # Debug message\n    print(\"\\nTraining new iteration on \", str(xtrain.shape[0]), \" training samples, \", str(xval.shape[0]), \" validation samples\\n\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b858ea4f1f838c04055b2ffbc886d107232027fd"},"cell_type":"markdown","source":"# 5 - Plot Loss Trend"},{"metadata":{"trusted":true,"_uuid":"8c04888e85b9aa04eea12888731b2b6bb7c4cd17","scrolled":false},"cell_type":"code","source":"colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']\n\ndef plot_accuracy_and_loss(idx, hist):\n    plt.plot(hists[idx].history['loss'], label='Train Loss Fold {}'.format(idx+1), color=colors[idx])\n    plt.plot(hists[idx].history['val_loss'], label='Val Loss Fold {}'.format(idx+1), color=colors[idx], linestyle = \"dashdot\")\n\nplt.figure(figsize=(22, 10))\nplt.title('Train Accuracy vs Val Accuracy')\nfor idx, hist in enumerate(hists):\n    plot_accuracy_and_loss(idx, hist)\n\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"26d6cb3dc8908ddf4a9b0b491825892a0cbdc760"},"cell_type":"code","source":"t_finish = time.time()\nprint(f\"Kernel run time = {(t_finish-t_start)/3600} hours\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e56d2ffb39a54456732a370aae0053b779747d94"},"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}