{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# Data\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport random\nfrom sklearn.model_selection import train_test_split\n\n# Image Processing\nimport cv2 # OpenCV\nfrom keras import layers\nfrom keras import models\nfrom keras import optimizers\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.preprocessing.image import img_to_array, load_img\nfrom keras.models import load_model\nfrom keras.applications import InceptionResNetV2\n\n# Visualization\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n%matplotlib inline \nimport seaborn as sns\n# Inline make plot appears on notebook\n\n# Evaluation\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import f1_score\n\n# ETC\nimport os\nimport gc # Gabage collector","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have 25,000 images in each class (dog and cat), but we want to try use small amounts of data and use transfer learning later, so we choose 2000 images from each class."},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_dir = '../input/train'\ntest_dir = '../input/test'\n\ntrain_dogs = ['../input/train/{}'.format(i) for i in os.listdir(train_dir) if 'dog' in i]\ntrain_cats = ['../input/train/{}'.format(i) for i in os.listdir(train_dir) if 'cat' in i]\n\ntest_imgs = ['../input/test/{}'.format(i) for i in os.listdir(test_dir)]\n\ntrain_imgs = train_dogs[:2000] + train_cats[:2000]  # Slice the dataset and use 2000 images from each class\nrandom.shuffle(train_imgs)\n\n# Clear unused data\ndel train_dogs\ndel train_cats\ngc.collect() # Collect gabage to save memory","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Visualize our training data"},{"metadata":{"trusted":true},"cell_type":"code","source":"for ima in train_imgs[0:3]:\n    img = mpimg.imread(ima)\n    imgplot = plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Resize images using OpenCV (we chose 150x150)"},{"metadata":{"trusted":true},"cell_type":"code","source":"nrows = 150\nncols = 150\nchannels = 3 # 3 for colored images (red, green, blue), 1 for greyscale images","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# A function to read and process images to the format usable by our model\ndef read_and_process_image(list_of_images):\n    \"\"\"\n        Returns 2 arrays:\n            X is an array of resized images\n            y is an array of labels\n    \"\"\"\n    X = []\n    y = []\n    \n    for image in list_of_images:\n        X.append(cv2.resize(cv2.imread(image, cv2.IMREAD_COLOR), (nrows, ncols), interpolation=cv2.INTER_CUBIC)) # Read the image\n        # Get the label\n        if 'dog' in image:\n            y.append(1)\n        elif 'cat' in image:\n            y.append(0)\n    return X, y\n\nX, y = read_and_process_image(train_imgs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del train_imgs\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Lets check the first 5 images. We have to use imshow() because the images are now array of pixels, not raw jpg (so we cannot use matplotlib.image anymore here)"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(20, 10))\ncolumns = 5\nfor i in range(columns):\n    plt.subplot(5 / columns + 1, columns, i + 1)\n    plt.imshow(X[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Convert lists to numpy arrays\nX = np.array(X)\ny = np.array(y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.countplot(y)\nplt.title(\"Labels for Cats and Dogs\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Shape of training data:\", X.shape)  # Batch size, Height, Width, Channel\nprint(\"Shape of labels:\", y.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Split training data into training data and validation data"},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.20, random_state=2)\n\nprint(\"Shape of X_train:\", X_train.shape)\nprint(\"Shape of X_val:\", X_val.shape)\nprint(\"Shape of y_train:\", y_train.shape)\nprint(\"Shape of y_val:\", y_val.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del X\ndel y\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Get length of training set and validation set\nntrain = len(X_train)\nnval = len(X_val)\n\n# We  will use batch size of 32, batch size should be a factor of 2, 4, 8, 16, 32, 64, ...\nbatch_size = 32","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use pretrained model from Keras"},{"metadata":{"trusted":true},"cell_type":"code","source":"conv_base = InceptionResNetV2(weights='imagenet', include_top=False, input_shape=(150, 150, 3))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use weights which are trained on imagenet. <br/>\nAssign include_top=False to not to download the fully connected layers (top layers), because I am going to implement it by myself.  \n\nLets check the pre-trained model I just loaded."},{"metadata":{"trusted":true},"cell_type":"code","source":"conv_base.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use CNN on Keras (Tensorflow backend) and try use small version of an architecture called VGGnet <br>(output filter size of each layer are 32, 64, 128, 512, 1).\n\nInput shape matched with size of both X.\n\n(3, 3) is kernel size (that 3x3 pixels size box which scan through image)."},{"metadata":{"trusted":true},"cell_type":"code","source":"model = models.Sequential()\nmodel.add(conv_base)  # Add pre-trained model here\nmodel.add(layers.Flatten())\n#model.add(layers.Dropout(0.5))\nmodel.add(layers.Dense(256, activation='relu'))\nmodel.add(layers.Dense(1, activation='sigmoid'))  # Use sigmoid activation, because output has only 2 classes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Freeze pre-trained model and only train our own."},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Number of trainable weights BEFORE freezing the conv_base', len(model.trainable_weights))\nconv_base.trainable = False\nprint('Number of trainable weights AFTER freezing the conv_base', len(model.trainable_weights))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Try change learning rate from 0.0001 to 0.0002 (compares to previous model)\nmodel.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=2e-4), metrics=['acc'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Use rescaling and image augmentation on training dataset\ntrain_datagen = ImageDataGenerator(rescale=1./255,  # Normalize image to have mean = 0 and standard deviation = 1, helps model learn and update parameters efficiently\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# We do not perform augmentation on validation dataset, we only do rescaling\nval_datagen = ImageDataGenerator(rescale=1./255)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create the image generator\ntrain_generator = train_datagen.flow(X_train, y_train, batch_size=batch_size)\nval_generator = val_datagen.flow(X_val, y_val, batch_size=batch_size)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Train for 20 epochs (try reduces it from 64 epochs this time)"},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit_generator(train_generator,\n                              steps_per_epoch=ntrain // batch_size,  \n                              epochs=20,                             \n                              validation_data=val_generator,\n                              validation_steps=nval // batch_size,)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Plot accuracy and loss"},{"metadata":{"trusted":true},"cell_type":"code","source":"acc = history.history['acc']\nval_acc  = history.history['val_acc']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(1, len(acc) + 1)\n\nplt.plot(epochs, acc, 'b', label='Training Accuracy')\nplt.plot(epochs, val_acc, 'r', label='Validation Accuracy')\nplt.title('Training and Validation Accuracy')\nplt.legend()\nplt.figure()\n\nplt.plot(epochs, loss, 'b', label='Training Loss')\nplt.plot(epochs, val_loss, 'r', label='Validation Loss')\nplt.title('Training and Validation Loss')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Predicts the first 10 images from test dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"X_test, y_test = read_and_process_image(test_imgs[0:10])  # Outputted y_test will be empty\nX_test = np.array(X_test)\ntest_datagen = ImageDataGenerator(rescale=1./255)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 0\ntext_labels = []\nplt.figure(figsize=(30, 20))\nfor batch in test_datagen.flow(X_test, batch_size=1):\n    pred = model.predict(batch)\n    if pred > 0.5:\n        text_labels.append('dog')\n    else:\n        text_labels.append('cat')\n    plt.subplot(5 / columns + 1, columns, i + 1)\n    plt.title('This is a ' + text_labels[i])\n    imgplot = plt.imshow(batch[0])\n    i += 1\n    if i % 10 == 0:\n        break\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Check confusion matrix and F1 score on validation dataset (should have do it on test dataset, but Kaggle does not have labels for test dataset)."},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 0\ny_pred = []\nfor batch in val_datagen.flow(X_val, batch_size=1):\n    pred = model.predict(batch)\n    if pred > 0.5:\n        y_pred.append(1)  # Dog\n    else:\n        y_pred.append(0)  # Cat\n    i += 1\n    if i == len(X_val):\n        break\ny_pred = np.array(y_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"confusion_matrix(y_val, y_pred, labels=None)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f1_score(y_val, y_pred, average='weighted') ","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}