{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ship Detection using Faster R-CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Importing libraries\n",
    "\n",
    "The necessary libraries required for implementing the Faster R-CNN model are imported in the code block below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Checking System\n",
      "\n",
      "Tensorflow version : 2.11.0-dev20220812\n",
      "Number of replicas: 1\n",
      "2.11.0-dev20220812\n",
      "\n",
      "System check done in 0.0104 seconds\n",
      "System Checked!\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import os, random, cv2, pickle, json, itertools\n",
    "import imgaug.augmenters as iaa\n",
    "import imgaug.imgaug\n",
    "\n",
    "from IPython.display import SVG\n",
    "# from tensorflow.python import keras\n",
    "from keras.utils import plot_model, model_to_dot\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import confusion_matrix\n",
    "from collections import Counter\n",
    "from sklearn.utils import class_weight\n",
    "from tqdm import tqdm\n",
    "from sklearn.preprocessing import LabelBinarizer\n",
    "\n",
    "from keras.utils import to_categorical\n",
    "from keras.models import Sequential, Model\n",
    "from keras.layers import (Add, Input, Conv2D, Dropout, Activation, BatchNormalization, MaxPooling2D, ZeroPadding2D, AveragePooling2D, Flatten, Dense)\n",
    "from keras.optimizers import Adam, SGD\n",
    "from keras.callbacks import TensorBoard, ModelCheckpoint, Callback\n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "from keras.initializers import *\n",
    "\n",
    "\n",
    "start_time = time.perf_counter()\n",
    "print(\"Checking System\\n\")\n",
    "\n",
    "SEED = 1337\n",
    "print('Tensorflow version : {}'.format(tf.__version__))\n",
    "\n",
    "try:\n",
    "    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n",
    "    tf.config.experimental_connect_to_cluster(tpu)\n",
    "    tf.tpu.experimental.initialize_tpu_system(tpu)\n",
    "    strategy = tf.distribute.experimental.TPUStrategy(tpu)\n",
    "except ValueError:\n",
    "    strategy = tf.distribute.get_strategy() # for CPU and single GPU\n",
    "    print('Number of replicas:', strategy.num_replicas_in_sync)\n",
    "    \n",
    "print(tf.__version__)\n",
    "\n",
    "end_time = time.perf_counter()\n",
    "\n",
    "print(f\"\\nSystem check done in {end_time - start_time:0.4f} seconds\")\n",
    "print(\"System Checked!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Utility functions\n",
    "\n",
    "1. show_final_history - For plotting the loss and accuracy of the training and validation datasets\n",
    "2. plot_confusion_matrix - For plotting the percentage of true positives per class for a better feel of how the model predicted the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def show_final_history(history):\n",
    "    \n",
    "    plt.style.use(\"ggplot\")\n",
    "    fig, ax = plt.subplots(1,2,figsize=(15,5))\n",
    "    ax[0].set_title('Loss')\n",
    "    ax[1].set_title('Accuracy')\n",
    "    ax[0].plot(history.history['loss'],label='Train Loss')\n",
    "    ax[0].plot(history.history['val_loss'],label='Validation Loss')\n",
    "    ax[1].plot(history.history['accuracy'],label='Train Accuracy')\n",
    "    ax[1].plot(history.history['val_accuracy'],label='Validation Accuracy')\n",
    "    \n",
    "    ax[0].legend(loc='upper right')\n",
    "    ax[1].legend(loc='lower right')\n",
    "    plt.show();\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_confusion_matrix(cm,classes,title='Confusion Matrix',cmap=plt.cm.Blues):\n",
    "    \n",
    "#     np.seterr(divide='ignore',invalid='ignore')\n",
    "    cm = cm.astype('float')/cm.sum(axis=1)[:,np.newaxis]\n",
    "    plt.figure(figsize=(10,10))\n",
    "    plt.imshow(cm,interpolation='nearest',cmap=cmap)\n",
    "    plt.title(title)\n",
    "    plt.colorbar()\n",
    "    tick_marks = np.arange(len(classes))\n",
    "    plt.xticks(tick_marks, classes,rotation=45)\n",
    "    plt.yticks(tick_marks, classes)\n",
    "    \n",
    "    fmt = '.2f'\n",
    "    thresh = cm.max()/2.\n",
    "    for i,j in itertools.product(range(cm.shape[0]),range(cm.shape[1])):\n",
    "        plt.text(j,i,format(cm[i,j],fmt),\n",
    "                horizontalalignment=\"center\",\n",
    "                color=\"white\" if cm[i,j] > thresh else \"black\")\n",
    "        pass\n",
    "    \n",
    "    plt.ylabel('True Label')\n",
    "    plt.xlabel('Predicted Label')\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loading the images\n",
    "\n",
    "The images from the SatelliteImageryofShips is loaded into numpy arrays, with labels [0,1] corresponding to the classes no-ship and ship. The data was loaded into numpy arrays as data augmentation and upsampling/downsampling is easier to perform."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'no-ship': 0, 'ship': 1}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "datasets = ['./SatelliteImageryofShips/']\n",
    "\n",
    "class_names = [\"no-ship\",\"ship\"]\n",
    "\n",
    "class_name_labels = {class_name:i for i,class_name in enumerate(class_names)}\n",
    "\n",
    "num_classes = len(class_names)\n",
    "class_name_labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data():\n",
    "    images, labels = [], []\n",
    "    \n",
    "    for dataset in datasets:\n",
    "        \n",
    "        for folder in os.listdir(dataset):\n",
    "            label = class_name_labels[folder]\n",
    "            \n",
    "            for file in tqdm(os.listdir(os.path.join(dataset,folder))):\n",
    "                \n",
    "                img_path = os.path.join(dataset,folder,file)\n",
    "                \n",
    "                img = cv2.imread(img_path)\n",
    "                img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n",
    "                img = cv2.resize(img, (48,48))\n",
    "                \n",
    "                images.append(img)\n",
    "                labels.append(label)\n",
    "                pass\n",
    "            pass\n",
    "        \n",
    "        images = np.array(images,dtype=np.float32)/255.0\n",
    "        labels = np.array(labels,dtype=np.float32)\n",
    "        pass\n",
    "    \n",
    "    return (images, labels)\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3000/3000 [00:05<00:00, 572.93it/s]\n",
      "100%|██████████| 1000/1000 [00:01<00:00, 566.53it/s]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "((4000, 48, 48, 3), (4000,))"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(images, labels) = load_data()\n",
    "images.shape, labels.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## EDA of dataset\n",
    "\n",
    "1. bar-plot - Bar plot is made to find the count of images per class\n",
    "2. pie-plot - Pie plot is drawn to find the percentage of class distribution in the dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Class-Label</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>no-ship</th>\n",
       "      <td>3000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ship</th>\n",
       "      <td>1000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             Count\n",
       "Class-Label       \n",
       "no-ship       3000\n",
       "ship          1000"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_labels = labels.shape[0]\n",
    "\n",
    "_, count = np.unique(labels, return_counts=True)\n",
    "\n",
    "df = pd.DataFrame(data = count)\n",
    "df['Class Label'] = class_names\n",
    "df.columns = ['Count','Class-Label']\n",
    "df.set_index('Class-Label',inplace=True)\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10224 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.plot.bar(rot=0)\n",
    "plt.title(\"distribution of images per class\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 9004 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.pie(count,\n",
    "        explode=(0,0),\n",
    "        labels=class_names,\n",
    "        autopct=\"%1.2f%%\")\n",
    "plt.axis('equal');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Due to imbalance in dataset, upsampling is done on the minority class, by randomly duplicating images until the 2 classes have comparable distribution in the dataset. After this is done, the dataset will be split into the training, testing and validation sets by randomly shuffling them and then splitting.\n",
    "\n",
    "Another way is to introduce class weights for each specific class. Each class is penalised with the specific class weight. Higher the class weight, greater the penalty. Classes with lower percentage have a higher penalty. This allows for the model to penalise itself heavily if class detected is incorrect."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Augmenting Images of Minority Class\n",
    "\n",
    "The images present in the ship class are augmented and then stored in the dataset, so that there is an equal representation of the classes. The current ratio of classes is 1:3, meaning that for every image present in the ship class there are 3 images present in the no-ship class. This will be countered by producing 2 augmented images per original image of the ship class. This will make the dataset balanced.\n",
    "\n",
    "If augmentation of dataset is required then set AUGMENTATION to True. This will balance the dataset via augmentation of minority classes. To train via class weights, then set AUGMENTATION to False."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "AUGMENTATION = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def augment_add(images, seq, labels):\n",
    "    \n",
    "    augmented_images, augmented_labels = [],[]\n",
    "    for idx,img in tqdm(enumerate(images)):\n",
    "        \n",
    "        if labels[idx] == 1:\n",
    "            image_aug_1 = seq.augment_image(image=img)\n",
    "            image_aug_2 = seq.augment_image(image=img)\n",
    "            augmented_images.append(image_aug_1)\n",
    "            augmented_images.append(image_aug_2)\n",
    "            augmented_labels.append(labels[idx])\n",
    "            augmented_labels.append(labels[idx])\n",
    "        pass\n",
    "    \n",
    "    augmented_images = np.array(augmented_images, dtype=np.float32)\n",
    "    augmented_labels = np.array(augmented_labels, dtype=np.float32)\n",
    "    \n",
    "    return (augmented_images, augmented_labels)\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "seq = iaa.Sequential([\n",
    "    iaa.Fliplr(0.5),\n",
    "    iaa.Crop(percent=(0,0.1)),\n",
    "    iaa.LinearContrast((0.75,1.5)),\n",
    "    iaa.Multiply((0.8,1.2), per_channel=0.2),\n",
    "    iaa.Affine(\n",
    "        scale={'x':(0.8,1.2), \"y\":(0.8,1.2)},\n",
    "        translate_percent={\"x\":(-0.2,0.2),\"y\":(-0.2,0.2)},\n",
    "        rotate=(-25,25),\n",
    "        shear=(-8,8)\n",
    "    )\n",
    "], random_order=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "4000it [00:16, 235.65it/s]  \n"
     ]
    }
   ],
   "source": [
    "if AUGMENTATION:\n",
    "    (aug_images, aug_labels) = augment_add(images, seq, labels)\n",
    "    images = np.concatenate([images, aug_images])\n",
    "    labels = np.concatenate([labels, aug_labels])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((6000, 48, 48, 3), (6000,))"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "images.shape, labels.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 8412 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "if AUGMENTATION:\n",
    "    _, count = np.unique(labels, return_counts=True)\n",
    "\n",
    "    plt.pie(count,\n",
    "            explode=(0,0),\n",
    "            labels=class_names,\n",
    "            autopct=\"%1.2f%%\")\n",
    "    plt.axis('equal');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## One Hot Encoding Variables\n",
    "\n",
    "The labels numpy array is one hot encoded using to_categorical from keras. This removes any uncessary bias in the dataset, by keeping the class at equal footing, with respect to labels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "labels = to_categorical(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training, Validation and Testing\n",
    "\n",
    "Training, Validation and Testing\n",
    "Instead of using train_test_split the images and labels arrays are randomly shuffled using the same seed value set at 42. This allows the images and their corresponding labels to remain linked even after shuffling.\n",
    "\n",
    "This method allows the user to make all 3 datasets. The training and validation dataset is used for training the model while the testing dataset is used for testing the model on unseen data. Unseen data is used for simulating real-world prediction, as the model has not seen this data before. It allows the developers to see how robust the model is."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(42)\n",
    "np.random.shuffle(images)\n",
    "\n",
    "np.random.seed(42)\n",
    "np.random.shuffle(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Spliting of data\n",
    "\n",
    "1. 70% - Training\n",
    "2. 20% - Validation\n",
    "3. 10% - Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((4200, 48, 48, 3), (1200, 48, 48, 3), (600, 48, 48, 3))"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "total_count = len(images)\n",
    "total_count\n",
    "\n",
    "train = int(0.7*total_count)\n",
    "val = int(0.2*total_count)\n",
    "test = int(0.1*total_count)\n",
    "\n",
    "train_images, train_labels = images[:train], labels[:train]\n",
    "val_images, val_labels = images[train:(val+train)], labels[train:(val+train)]\n",
    "test_images, test_labels = images[-test:], labels[-test:]\n",
    "\n",
    "train_images.shape, val_images.shape, test_images.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If AUGMENTATION is set True, then the number of images per class is balanced. If AUGMENTATION is set to False, then compute the class weights given below and accordingly change the fit function of the Keras API when training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not AUGMENTATION:\n",
    "    count_labels = train_labels.sum(axis=0)\n",
    "\n",
    "    classTotals = train_labels.sum(axis=0)\n",
    "    classWeight = {}\n",
    "\n",
    "    for i in range(0,len(classTotals)):\n",
    "        classWeight[i] = classTotals.max()/classTotals[i]\n",
    "        pass\n",
    "    print(classWeight)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creation of model\n",
    "\n",
    "1. conv_block - This function contains the convulotional layer, batch normalization and activation layers. The number of filters, kernel_size, strides to be taken are defined by the developer. This allows a developer to make the model without having to repeat the same lines continuously many times. It also uses the OOPs concepts of Python which is recommended instead of coding like it is C.\n",
    "\n",
    "2. basic_model - This function creates the model using the aforementioned function, max pooling layers and dropouts. After the specified number of convolutional layers, a flatten layer is introduced, along with dense layers so that the image can be classified. The flatten layer converts the feature map produced by the convolutional layers into a single column for classification.\n",
    "\n",
    "For image detection in images using Faster R-CNN the feature map produced by the convolutional layers is used, i.e., the model drops the layers after the final convolutional block and passes the generated feature map to a Regional Proposal Network, which can either uses a vanilla CNN model containing fully connected layers or a Logistic Regression, Support Vector machines or Random forests. Advised to use vanilla CNN as it uses less CPU and memory, plus slightly faster and capable of giving multiple outputs if required."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Explanation of features\n",
    "\n",
    "1. Conv2D - This is a 2 dimensional convolutional layer, the number of filters decide what the convolutional layer learns. Greater the number of filters, greater the amount of information obtained.\n",
    "2. MaxPooling2D - This reduces the spatial dimensions of the feature map produced by the convolutional layer without losing any range information. This allows a model to become slightly more robust\n",
    "3. Dropout - This removes a user-defined percentage of links between neurons of consecutive layers. This allows the model to be robust. It can be used in both fully convolutional layers and fully connected layers.\n",
    "4. BatchNormalization - This layer normalises the values present in the hidden part of the neural network. This is similar to MinMax/Standard scaling applied in machine learning algorithms\n",
    "5. Padding- This pads the feature map/input image with zeros allowing border features to stay."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def conv_block(X,k,filters,stage,block,s=2):\n",
    "    \n",
    "    conv_base_name = 'conv_' + str(stage)+block+'_branch'\n",
    "    bn_base_name = 'bn_'+str(stage)+block+\"_branch\"\n",
    "    \n",
    "    F1 = filters\n",
    "    \n",
    "    X = Conv2D(filters=F1, kernel_size=(k,k), strides=(s,s),\n",
    "                padding='same',name=conv_base_name+'2a')(X)\n",
    "    X = BatchNormalization(name=bn_base_name+'2a')(X)\n",
    "    X = Activation('relu')(X)\n",
    "    \n",
    "    return X\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creation of Model\n",
    "\n",
    "### Block 1\n",
    "\n",
    "1. An input layer is initialised using the Input Keras layer, this defines the number of neurons present in the input layer\n",
    "2. ZeroPadding is applied to the input image, so that boundary features are not lost.\n",
    "\n",
    "### Block 2\n",
    "\n",
    "First Convolutioanl Layer, it starts with 16 filters and kernel size with (3,3) and strides (2,2). Padding is maintaned same, so the image does not chaneg spatially, until the next block in which MaxPooling occurs\n",
    "\n",
    "### Block 3 - 4\n",
    "\n",
    "Similar structure in both with a convolutional layer followed by a MaxPooling and Dropout layers.\n",
    "\n",
    "### Output Block\n",
    "\n",
    "The feature map produced by the previous convolutional layers is converted into a single column using Flatten Layer and the classified using a Dense layer(output layer) with the number of classes present in the dataset, and sigmoid as activation function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def basic_model(input_shape,classes):\n",
    "    \n",
    "    X_input = tf.keras.Input(input_shape)\n",
    "    \n",
    "    X = ZeroPadding2D((5,5))(X_input)\n",
    "    \n",
    "    X = Conv2D(16,(3,3),strides=(2,2),name='conv1',padding=\"same\")(X)\n",
    "    X = BatchNormalization(name='bn_conv1')(X)\n",
    "    \n",
    "    # stage 2\n",
    "    X = conv_block(X,3,32,2,block='A',s=1)\n",
    "    X = MaxPooling2D((2,2))(X)\n",
    "    X = Dropout(0.25)(X)\n",
    "\n",
    "#     Stage 3\n",
    "    X = conv_block(X,5,32,3,block='A',s=2)\n",
    "    X = MaxPooling2D((2,2))(X)\n",
    "    X = Dropout(0.25)(X)\n",
    "    \n",
    "#     Stage 4\n",
    "    X = conv_block(X,3,64,4,block='A',s=1)\n",
    "    X = MaxPooling2D((2,2))(X)\n",
    "    X = Dropout(0.25)(X)\n",
    "    \n",
    "#   Output Layer\n",
    "    X = Flatten()(X)\n",
    "    X = Dense(64)(X)\n",
    "    X = Dropout(0.5)(X)\n",
    "    \n",
    "    X = Dense(128)(X)\n",
    "    X = Activation(\"relu\")(X)\n",
    "    \n",
    "    X = Dense(classes,activation=\"softmax\",name=\"fc\"+str(classes))(X)\n",
    "    \n",
    "    model = Model(inputs=X_input,outputs=X,name='Feature_Extraction_and_FC')\n",
    "    \n",
    "    return model\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'tf' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32mf:\\Machine Learning\\ML Project\\ShipsSatelliteImageClassification\\main.ipynb Cell 36\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> <a href='vscode-notebook-cell:/f%3A/Machine%20Learning/ML%20Project/ShipsSatelliteImageClassification/main.ipynb#X50sZmlsZQ%3D%3D?line=0'>1</a>\u001b[0m model \u001b[39m=\u001b[39m basic_model(input_shape\u001b[39m=\u001b[39;49m(\u001b[39m48\u001b[39;49m,\u001b[39m48\u001b[39;49m,\u001b[39m3\u001b[39;49m),classes\u001b[39m=\u001b[39;49m\u001b[39m2\u001b[39;49m)\n",
      "\u001b[1;32mf:\\Machine Learning\\ML Project\\ShipsSatelliteImageClassification\\main.ipynb Cell 36\u001b[0m in \u001b[0;36mbasic_model\u001b[1;34m(input_shape, classes)\u001b[0m\n\u001b[0;32m      <a href='vscode-notebook-cell:/f%3A/Machine%20Learning/ML%20Project/ShipsSatelliteImageClassification/main.ipynb#X50sZmlsZQ%3D%3D?line=0'>1</a>\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mbasic_model\u001b[39m(input_shape,classes):\n\u001b[1;32m----> <a href='vscode-notebook-cell:/f%3A/Machine%20Learning/ML%20Project/ShipsSatelliteImageClassification/main.ipynb#X50sZmlsZQ%3D%3D?line=2'>3</a>\u001b[0m     X_input \u001b[39m=\u001b[39m tf\u001b[39m.\u001b[39mkeras\u001b[39m.\u001b[39mInput(input_shape)\n\u001b[0;32m      <a href='vscode-notebook-cell:/f%3A/Machine%20Learning/ML%20Project/ShipsSatelliteImageClassification/main.ipynb#X50sZmlsZQ%3D%3D?line=4'>5</a>\u001b[0m     X \u001b[39m=\u001b[39m ZeroPadding2D((\u001b[39m5\u001b[39m,\u001b[39m5\u001b[39m))(X_input)\n\u001b[0;32m      <a href='vscode-notebook-cell:/f%3A/Machine%20Learning/ML%20Project/ShipsSatelliteImageClassification/main.ipynb#X50sZmlsZQ%3D%3D?line=6'>7</a>\u001b[0m     X \u001b[39m=\u001b[39m Conv2D(\u001b[39m16\u001b[39m,(\u001b[39m3\u001b[39m,\u001b[39m3\u001b[39m),strides\u001b[39m=\u001b[39m(\u001b[39m2\u001b[39m,\u001b[39m2\u001b[39m),name\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mconv1\u001b[39m\u001b[39m'\u001b[39m,padding\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39msame\u001b[39m\u001b[39m\"\u001b[39m)(X)\n",
      "\u001b[1;31mNameError\u001b[0m: name 'tf' is not defined"
     ]
    }
   ],
   "source": [
    "model = basic_model(input_shape=(48,48,3),classes=2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.13 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "5819c1eaf6d552792a1bbc5e8998e6c2149ab26a1973a0d78107c0d9954e5ba0"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
