{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\n\nfrom sklearn.model_selection import train_test_split\n\nimport tensorflow as tf\nprint(tf.__version__)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preparing data"},{"metadata":{"trusted":true},"cell_type":"code","source":"# preparing data\n\nTRAIN_DIR = \"../input/dogs-vs-cats/train/train\"\nTEST_DIR = \"../input/dogs-vs-cats/test1/test1\"\n\nTRAIN_SIZE = len([name for name in os.listdir(TRAIN_DIR)])\nTEST_SIZE = len([name for name in os.listdir(TEST_DIR)])\nprint(\"Number of training images:\", TRAIN_SIZE)\nprint(\"Number of test images:\", TEST_SIZE)\n\nVALID_FRACTION = 0.2\nBATCH_SIZE = 100\nEPOCHS = 50\n\nIMAGE_WIDTH = IMAGE_HEIGHT = 150\n\n# creating df with train labels\ntrain_filenames = os.listdir(TRAIN_DIR)\ntrain_labels = []\nfor filename in train_filenames:\n    label = filename.split('.')[0]\n    train_labels.append(label)\n\ntrain_df = pd.DataFrame({\n    'id': train_filenames,\n    'label': train_labels\n})\n\n# splitting to train & valid\ntrain_df, valid_df = train_test_split(train_df, test_size=VALID_FRACTION)\n\n# augmentation settings, for now just normalizing\ntrain_datagen = tf.keras.preprocessing.image.ImageDataGenerator(    \n    rotation_range=40,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    rescale=1./255.,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    fill_mode='nearest'\n    )\n\n# not doing any data augmentation on validation test set\nvalid_datagen  = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255.)\n\n# creating train and valid generators (not using valid_split to avoid doing data augmentation on validation set)\ntrain_generator = train_datagen.flow_from_dataframe(\n    train_df, \n    TRAIN_DIR, \n    x_col='id',\n    y_col='label',\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    class_mode='binary',\n    batch_size=BATCH_SIZE\n)\n\nvalid_generator = valid_datagen.flow_from_dataframe(\n    valid_df, \n    TRAIN_DIR, \n    x_col='id',\n    y_col='label',\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    class_mode='binary',\n    batch_size=BATCH_SIZE\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating model"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = tf.keras.models.Sequential([\n    # the images were resized by ImageDataGenerator 150x150 with 3 bytes color\n    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 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.Dropout(0.5),\n    tf.keras.layers.Flatten(), \n    # 512 neuron hidden layer\n    tf.keras.layers.Dense(512, activation='relu'),\n    # since we have only 2 classes to predict we can use 1 neuron and sigmoid\n    tf.keras.layers.Dense(1, activation='sigmoid')  \n])\n\nmodel.summary()\n\nmodel.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.001),\n    loss='binary_crossentropy',\n    metrics = ['accuracy'])\n\nes = tf.keras.callbacks.EarlyStopping(monitor='val_loss',\n    mode='min',\n    restore_best_weights=True, \n    verbose=1,\n    patience=5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tf.keras.utils.plot_model(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\n# training\nhistory = model.fit_generator(train_generator,\n    validation_data=valid_generator,\n    steps_per_epoch=round(TRAIN_SIZE*(1.-VALID_FRACTION)/BATCH_SIZE),\n    validation_steps=round(TRAIN_SIZE*VALID_FRACTION/BATCH_SIZE),\n    epochs=EPOCHS,\n    callbacks=[es],\n    verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#plotting\n\nimport matplotlib.pyplot as plt\n\n#-----------------------------------------------------------\n# Retrieve a list of list results on training and test data\n# sets for each training epoch\n#-----------------------------------------------------------\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)) # Get number of epochs\n\n#------------------------------------------------\n# Plot training and validation accuracy per epoch\n#------------------------------------------------\nplt.plot(epochs, acc)\nplt.plot(epochs, val_acc)\nplt.title('Training and validation accuracy')\nplt.figure()\n\n#------------------------------------------------\n# Plot training and validation loss per epoch\n#------------------------------------------------\nplt.plot(epochs, loss)\nplt.plot(epochs, val_loss)\nplt.title('Training and validation loss')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generating predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\n# preparing testing data\ntest_filenames = os.listdir(TEST_DIR)\ntest_df = pd.DataFrame({\n    'id': test_filenames\n})\n\ntest_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale = 1.0/255.)\n\ntest_generator = test_datagen.flow_from_dataframe(\n    test_df, \n    TEST_DIR, \n    x_col='id',\n    y_col=None,\n    class_mode=None,\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\nyhat = model.predict_generator(test_generator, steps=np.ceil(TEST_SIZE/BATCH_SIZE))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating submission file"},{"metadata":{"trusted":true},"cell_type":"code","source":"# sigmoid returns probability between 0 and 1, need to convert it to an integer class\nyhat = [1 if y > 0.5 else 0 for y in yhat]\n\ntest_df['label'] = yhat\n\n# restoring back to class names (dog|cat)\nlabel_map = dict((v,k) for k,v in train_generator.class_indices.items())\ntest_df['label'] = test_df['label'].replace(label_map)\n\n# encoding according to submission format, 1 = dog, 0 = cat\ntest_df['label'] = test_df['label'].replace({ 'dog': 1, 'cat': 0 })\n\ntest_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.head()","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":1}