{"cells":[{"metadata":{},"cell_type":"markdown","source":"Our aim is to write an algorithm to classify whether images contain either a dog (1) or a cat (0). This is an example of Binary Classification problem. We have been given the Asirra (Animal Species Image Recognition for Restricting Access) dataset.\nIn this notebook we will be training our model from scratch.\n\n# 1. Importing Libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport zipfile \nimport random\nimport numpy as np \nimport pandas as pd \nimport seaborn as sns\n\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\n\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"np.random.seed(9)\ntf.random.set_seed(9)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2. Extracting Training Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(os.listdir(\"../input/dogs-vs-cats\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The training data is in train.zip file. Lets extract this zip file into ../kaggle/working/train_unzip/"},{"metadata":{"trusted":true},"cell_type":"code","source":"with zipfile.ZipFile(\"../input/dogs-vs-cats/train.zip\",\"r\") as z:\n    z.extractall(\"../kaggle/working/train_unzip\")\n    \nprint(f\"We have total {len(os.listdir('../kaggle/working/train_unzip/train'))} images in our training data.\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. Exploring Training Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f\"First 12 filenames: \\n {os.listdir('../kaggle/working/train_unzip/train')[:12]}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Among the filenames first 3 characters are either 'dog' or 'cat'. Using these we can label our data as 'cat' for cat images and 'dog' for dog images. Lets also include image height, width and channels in the dataframe so that we can explore it's distribution further. And finally lets also include file size since we don't want any file with size 0."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path = '../kaggle/working/train_unzip/train/'\nfilenames = os.listdir(train_path)\n\nlabels, heights, widths, channels, filesize = [], [], [], [], []\n\nfor fname in filenames:\n    labels.append(str(fname)[:3])\n    img_shape = mpimg.imread(train_path+fname).shape\n    heights.append(img_shape[0])\n    widths.append(img_shape[1])\n    channels.append(img_shape[2])\n    filesize.append(os.path.getsize(train_path+fname))\n\ntrain_df = pd.DataFrame({'filename': filenames, 'label': labels, 'height': heights, 'width': widths, 'channels': channels, 'filesize': filesize})\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":" Note that in this dataset not all images have the same height and width. Channels have value 3 indicating RGB images."},{"metadata":{"trusted":true},"cell_type":"code","source":"print((train_df['label']).value_counts())\ndogsVScats_count = train_df['label'].value_counts().plot.bar(title='Number of Dog vs Cat Images in Training Data')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can see that the training data consists of 12500 images of Dogs and 12500 images of Cats.\n\nLets now look at few images from our dataset:"},{"metadata":{"trusted":true},"cell_type":"code","source":"nrows = 3\nncols = 3\n\nfig = plt.gcf()\nfig.set_size_inches(ncols*4, nrows*4)\n\nfor i in range(nrows*ncols):\n    sample = np.random.choice(filenames)\n    img_path = \"../kaggle/working/train_unzip/train/\"+sample\n    sp = plt.subplot(nrows, ncols, i + 1)\n    sp.axis('Off')\n    img = mpimg.imread(img_path)\n    plt.imshow(img)\n    plt.title(sample[:3])\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"They look adorable :)\n\n> # 3.1 Image Height and Width Distribution in the Training Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(9, 5))\n\nplt.subplot(1, 2, 1)\nsns.distplot(train_df['height'], kde=False)\nplt.title('Distribution of Image HEIGHTs\\nthroughout training data')\n\nplt.subplot(1, 2, 2)\nsns.distplot(train_df['width'], kde=False)\nplt.title('Distribution of Image WIDTHs\\nthroughout training data')\n\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(9, 5))\n\nplt.subplot(1, 2, 1)\nsns.distplot(train_df[train_df['label']=='dog']['height'], label='dog')\nsns.distplot(train_df[train_df['label']=='cat']['height'], label='cat')\nplt.title('Cats vs Dogs image \\nHEIGHT distribution')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nsns.distplot(train_df[train_df['label']=='dog']['width'], label='dog')\nsns.distplot(train_df[train_df['label']=='cat']['width'], label='cat')\nplt.title('Cats vs Dogs image \\nWIDTH distribution')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> # 3.2 Training DataFrame Summary"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. Splitting Training Data\n\nWe will use 2 columns 'filename' and 'label' from train_df as our training data. This will be split into train_set_df and dev_set_df. (Note dev set is also referred as development / validation set)"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_set_df, dev_set_df = train_test_split(train_df[['filename', 'label']], test_size=0.3, random_state = 42, shuffle=True, stratify=train_df['label'])\nprint(train_set_df.shape, dev_set_df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train_set_df['label'].value_counts())\ntrain_set_plot = train_set_df['label'].value_counts().plot.bar(title='Number of Dog vs Cat Images in train set')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(dev_set_df['label'].value_counts())\ndev_set_plot = dev_set_df['label'].value_counts().plot.bar(title='Number of Dog vs Cat Images in dev set')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5. Image Augmentation\n\nImage Augmentation is a very powerful tool to help us avoid overfitting our data. Let’s initialize Keras’ ImageDataGenerator class:"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator( rescale = 1.0/255,\n                                    rotation_range=40,\n                                    shear_range=0.2,\n                                    zoom_range=0.2,\n                                    horizontal_flip=True,\n                                    fill_mode='nearest' )\n\nvalidation_datagen  = ImageDataGenerator( rescale = 1.0/255 )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's see how ImageDataGenerator works using a sample image:"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = train_df.sample(n=1)\nsample_generator = train_datagen.flow_from_dataframe(\n    sample, \n    \"../kaggle/working/train_unzip/train/\", \n    x_col='filename',\n    y_col='label',\n    target_size=(150,150),\n    class_mode='categorical'\n)\n\nplt.figure(figsize=(10, 10))\nfor i in range(0, 9):\n    plt.subplot(3, 3, i+1)\n    for x_batch, y_batch in sample_generator:\n        image = x_batch[0]\n        plt.imshow(image)\n        plt.axis('Off')\n        break\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The flow_from_dataframe() method takes the Pandas DataFrame and the path to a directory and generates batches of augmented images. Let’s now initialize our training and validation generator. We will be using batch size as 32 and image target size as 150x150."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator = train_datagen.flow_from_dataframe(\n    train_set_df, \n    directory=\"../kaggle/working/train_unzip/train/\", \n    x_col='filename',\n    y_col='label',\n    target_size=(150, 150),\n    class_mode='binary',\n    batch_size=32,\n    validate_filenames=False \n)\n\nvalidation_generator = validation_datagen.flow_from_dataframe(\n    dev_set_df, \n    directory=\"../kaggle/working/train_unzip/train/\", \n    x_col='filename',\n    y_col='label',\n    target_size=(150, 150),\n    class_mode='binary',\n    batch_size=32,\n    validate_filenames=False \n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 6. Building CNN Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = tf.keras.models.Sequential([\n    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 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.25),\n    tf.keras.layers.Flatten(),\n    tf.keras.layers.Dense(512, activation='relu'),\n    tf.keras.layers.Dropout(0.25),\n    tf.keras.layers.Dense(1, activation='sigmoid')  \n])\n\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> # 6.1 Model Compilation"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer='adam',\n              loss='binary_crossentropy',\n              metrics = ['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> # 6.2 Model Fitting"},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_generator,\n                    validation_data=validation_generator,\n                    steps_per_epoch=100,\n                    epochs=40,\n                    validation_steps=50\n                   )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 7. Accuracy and Loss Curves"},{"metadata":{"trusted":true},"cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs   = range(len(acc))\n\nplt.plot(epochs, acc, label=\"Training accuracy\")\nplt.plot(epochs, val_acc, label=\"Validation accuracy\")\nplt.title('Training and validation accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epochs, loss, label=\"Training loss\")\nplt.plot(epochs, val_loss, label=\"Validation loss\")\nplt.title('Training and validation loss')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 8. Model Evaluation\nLet's evaluate our model with dev set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"loss, accuracy = model.evaluate_generator(validation_generator)\nprint(\"Test: accuracy = %f  ;  loss = %f \" % (accuracy, loss))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dev_true = dev_set_df['label'].map({'dog': 1, \"cat\": 0})\ndev_predictions =  model.predict_generator(validation_generator)\ndev_set_df['pred'] = np.where(dev_predictions>0.5, 1, 0)\ndev_pred = dev_set_df['pred']\ndev_set_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dev_set_predictions_plot = dev_set_df['pred'].value_counts().plot.bar(title='Predicted number of Dog vs Cat Images in dev set')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> # 8.1 Confusion Matrix\n> Let's compute confusion matrix to evaluate the accuracy of classification."},{"metadata":{"trusted":true},"cell_type":"code","source":"confusion_mtx = confusion_matrix(dev_true, dev_pred) \n\nf,ax = plt.subplots(figsize=(8, 8))\nsns.heatmap(confusion_mtx, annot=True, linewidths=0.01,cmap=\"Blues\",linecolor=\"gray\", fmt= '.1f',ax=ax)\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"True Label\")\nplt.title(\"Confusion Matrix\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In the notebook [Cats vs Dogs 2 | Transfer Learning](https://www.kaggle.com/sejalkshirsagar/cats-vs-dogs-2-transfer-learning) we will apply Transfer Learning and see how we can get better results faster."}],"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}