{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport shutil\nfrom tqdm import tqdm\nimport random\nimport numpy as np\nfrom tensorflow import keras\nimport tensorflow as tf\nimport pathlib\nimport matplotlib.pyplot as plt\nimport pandas as pd","metadata":{"id":"wN82KFbp42EW","execution":{"iopub.status.busy":"2021-08-28T15:07:49.292720Z","iopub.execute_input":"2021-08-28T15:07:49.293084Z","iopub.status.idle":"2021-08-28T15:07:53.324732Z","shell.execute_reply.started":"2021-08-28T15:07:49.293000Z","shell.execute_reply":"2021-08-28T15:07:53.323830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### a) Download the dataset from kaggle and arrange images as per requirnment, \nIf you are executing on colab","metadata":{"id":"TJSpghyO4pG3"}},{"cell_type":"markdown","source":"Let's download dataset from kaggle to drive and have look at Procedure you will need to follow  \n>1) Download kaggle.json from kaggle/username/account , In that json you will get username and key.  \n>2) Replace key in the code below and execute \n```\n>import os  \n>os.environ['KAGGLE_USERNAME'] = \"usenrname\" # username from the json file  \n>os.environ['KAGGLE_KEY'] = \"key\" # key from the json file  \n```\n>3) Now copy API command from kaggle, You can get that in Data tab of the competition, It will be like this.   \n*(Attention : Please don't use double quotes to the path)*  \n```\n>*!kaggle competitions download -c \"compitition name seperated with (-)\" -p \"folder path where you want to download dataset\"*  \n```","metadata":{"id":"U0SDcC-03-0w"}},{"cell_type":"code","source":"# Uncomment this if you are executing this code on colab\n\n# os.environ['KAGGLE_USERNAME'] = \"username\" # username from the json file\n# os.environ['KAGGLE_KEY'] = \"key\" # key from the json file\n\n# !kaggle competitions download -c dogs-vs-cats-redux-kernels-edition -p /content/drive/MyDrive/Projects/Image_search_engine/train_zip/","metadata":{"id":"nXceaB6gVfUp"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Unzip file","metadata":{}},{"cell_type":"markdown","source":"You will need to unzip file you downloaded from kaggle  \nThere are multiple way to unzip the file  \n- 1) Using zipfile liabrary , We get flexibility to deal with the zipfile\n- 2) Using command line, Sometime I prefer to use command line as shown below    \n```\n!unzip zip_file_path -d destination_folder\n\n```\n\nHold a cup of coffee for a minute till it unzip","metadata":{"id":"PyujCP--5fLA"}},{"cell_type":"code","source":"train_zip_path = r'/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip'\ntest_zip_path = r'/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip'\nbase_dir = '/kaggle/working/Data/'\nunzip_dir = '/kaggle/working/data/'\nno_of_images = 6000 #No of images we want to take for training and validation, for validation set we will take 80% of training","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:42:51.379137Z","iopub.execute_input":"2021-08-28T15:42:51.379504Z","iopub.status.idle":"2021-08-28T15:42:51.384375Z","shell.execute_reply.started":"2021-08-28T15:42:51.379468Z","shell.execute_reply":"2021-08-28T15:42:51.383206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if not os.path.exists(unzip_dir):\n    os.makedirs(unzip_dir)\n    print(f'Directory ceated at {unzip_dir}')","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:08:14.380024Z","iopub.execute_input":"2021-08-28T15:08:14.380353Z","iopub.status.idle":"2021-08-28T15:08:14.385794Z","shell.execute_reply.started":"2021-08-28T15:08:14.380324Z","shell.execute_reply":"2021-08-28T15:08:14.384819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"shutil.rmtree(unzip_dir)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:08:16.856383Z","iopub.execute_input":"2021-08-28T15:08:16.856790Z","iopub.status.idle":"2021-08-28T15:08:16.861268Z","shell.execute_reply.started":"2021-08-28T15:08:16.856760Z","shell.execute_reply":"2021-08-28T15:08:16.860290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import zipfile\nwith zipfile.ZipFile(train_zip_path) as f:\n    f.extractall(unzip_dir)\nf.close","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:08:18.992097Z","iopub.execute_input":"2021-08-28T15:08:18.992405Z","iopub.status.idle":"2021-08-28T15:08:32.913268Z","shell.execute_reply.started":"2021-08-28T15:08:18.992376Z","shell.execute_reply":"2021-08-28T15:08:32.912478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with zipfile.ZipFile(test_zip_path) as f:\n    f.extractall(unzip_dir)\nf.close","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:43:27.099921Z","iopub.execute_input":"2021-08-28T15:43:27.100255Z","iopub.status.idle":"2021-08-28T15:43:35.170875Z","shell.execute_reply.started":"2021-08-28T15:43:27.100224Z","shell.execute_reply":"2021-08-28T15:43:35.170058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Arrange unzip images in the folder structure below","metadata":{"id":"jzTK5oR06zPs"}},{"cell_type":"code","source":"# Let's see how many files we have in unziped folder\nlen(os.listdir(unzip_dir + 'train/'))","metadata":{"id":"Xtbwm4Xe-w3w","execution":{"iopub.status.busy":"2021-08-28T12:09:53.119200Z","iopub.execute_input":"2021-08-28T12:09:53.119540Z","iopub.status.idle":"2021-08-28T12:09:53.140552Z","shell.execute_reply.started":"2021-08-28T12:09:53.119506Z","shell.execute_reply":"2021-08-28T12:09:53.139623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# If we have base directory in path we are going to remove the directory\nif os.path.exists(base_dir):\n      shutil.rmtree(base_dir )\n\n# Creating typical folder structure used in image classification where images are stoored in respective classes \nos.makedirs(base_dir + 'train/dog')\nos.makedirs(base_dir + 'train/cat')\nos.makedirs(base_dir + 'val/dog')\nos.makedirs(base_dir + 'val/cat')","metadata":{"id":"v352LKS5T7Im","execution":{"iopub.status.busy":"2021-08-28T15:12:26.797305Z","iopub.execute_input":"2021-08-28T15:12:26.797755Z","iopub.status.idle":"2021-08-28T15:12:26.808373Z","shell.execute_reply.started":"2021-08-28T15:12:26.797717Z","shell.execute_reply":"2021-08-28T15:12:26.806579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now i am saving dog and cat images name in list, so that i can copy those images to the folder structure we need\ncat_filename = []\ndog_filename = []\n\nfor i in tqdm(os.listdir(unzip_dir + 'train/')):\n    if i.startswith('dog'):\n        dog_filename.append(i)\n    else:\n        cat_filename.append(i)","metadata":{"id":"vv0t97Pjko4E","outputId":"59729d25-481d-4c23-a49f-3a1baacb97d0","execution":{"iopub.status.busy":"2021-08-28T15:12:27.820485Z","iopub.execute_input":"2021-08-28T15:12:27.820871Z","iopub.status.idle":"2021-08-28T15:12:27.874041Z","shell.execute_reply.started":"2021-08-28T15:12:27.820841Z","shell.execute_reply":"2021-08-28T15:12:27.872907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# source = '/content/drive/MyDrive/Projects/Image_search_engine/data/train/'\n\nfor id_, image  in tqdm(enumerate(random.sample(dog_filename,no_of_images))):\n    if id_<int(0.8 * no_of_images):\n        shutil.copy2(unzip_dir + 'train/' + image, base_dir + 'train/' + 'dog/' )\n    else:\n        shutil.copy2(unzip_dir + 'train/'  + image, base_dir + 'val/' + 'dog/' )\n        \nfor id_, image  in tqdm(enumerate(random.sample(cat_filename,no_of_images))):\n    if id_<int(0.8 * no_of_images):\n        shutil.copy2(unzip_dir + 'train/' + image, base_dir + 'train/' + 'cat/' )\n    else:\n        shutil.copy2(unzip_dir + 'train/' + image, base_dir + 'val/' + 'cat/' )","metadata":{"id":"_GwnNUqtmmYo","outputId":"ae0d287f-6661-46b0-ee66-993d7b80a8c9","execution":{"iopub.status.busy":"2021-08-28T15:12:28.537363Z","iopub.execute_input":"2021-08-28T15:12:28.537682Z","iopub.status.idle":"2021-08-28T15:12:29.978972Z","shell.execute_reply.started":"2021-08-28T15:12:28.537652Z","shell.execute_reply":"2021-08-28T15:12:29.977872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### b) Data Preparation \nTo have faster communication while training we will take advantage by prefetching images using tensorflow, This is procedure we can follow for every dataset in this format, This will drastically decrease the training time ","metadata":{"id":"T7Wc3ztfApyH"}},{"cell_type":"code","source":"TRAIN_DATA_DIR = base_dir + 'train/'\nVALIDATION_DATA_DIR = base_dir + 'val/'\nbatch_size = 32","metadata":{"id":"mTDBHRFcmqlm","execution":{"iopub.status.busy":"2021-08-28T15:12:29.980808Z","iopub.execute_input":"2021-08-28T15:12:29.981188Z","iopub.status.idle":"2021-08-28T15:12:29.986154Z","shell.execute_reply.started":"2021-08-28T15:12:29.981148Z","shell.execute_reply":"2021-08-28T15:12:29.985105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# pathlib.Path, will listing of subdirectories in the \"TRAIN_DATA_DIR\"\ntrain_data_dir = pathlib.Path(TRAIN_DATA_DIR)\n\n# .glob will find all files by using pattern we provided in the paranthessis of glob, Here all files in subfolders which is\n# Having extension as .jpg\nimage_count_train = len(list(train_data_dir.glob('*/*.jpg')))\nprint(image_count_train)","metadata":{"id":"RnqCDOpCpv3_","outputId":"44fdf1e8-d9dc-4130-fe43-ee22b2cf0cbb","execution":{"iopub.status.busy":"2021-08-28T15:12:29.988262Z","iopub.execute_input":"2021-08-28T15:12:29.988685Z","iopub.status.idle":"2021-08-28T15:12:30.187841Z","shell.execute_reply.started":"2021-08-28T15:12:29.988646Z","shell.execute_reply":"2021-08-28T15:12:30.187001Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Similarly we will do for VALIDATION_DATA_DIR\nvalid_data_dir = pathlib.Path(VALIDATION_DATA_DIR)\nimage_count_valid= len(list(valid_data_dir.glob('*/*.jpg')))\nprint(image_count_valid)","metadata":{"id":"d-Z9pFvIlqpd","outputId":"217bb57d-202d-4513-f5b2-90d89f2cf68c","execution":{"iopub.status.busy":"2021-08-28T15:12:30.410169Z","iopub.execute_input":"2021-08-28T15:12:30.410448Z","iopub.status.idle":"2021-08-28T15:12:30.426363Z","shell.execute_reply.started":"2021-08-28T15:12:30.410421Z","shell.execute_reply":"2021-08-28T15:12:30.425397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# A dataset of all files matching one or more glob patterns metioned in \"tf.data.Dataset.list_files(pattern)\"\" \ntrain_list_ds = tf.data.Dataset.list_files(str(train_data_dir/'*/*'), shuffle=False)\n\n# We are shuffling the dataset with buffer size as image_count_train, Which is the best way to shuffle dataset,\n# But sometimes we can't fit that much of data in ram at a time, so I try to use 10% of data \ntrain_list_ds = train_list_ds.shuffle(image_count_train, reshuffle_each_iteration=False)\n\n# Similarly we will do this for validation data\nvalid_list_ds = tf.data.Dataset.list_files(str(valid_data_dir/'*/*'), shuffle=False)\nvalid_list_ds = valid_list_ds.shuffle(image_count_valid, reshuffle_each_iteration=False)","metadata":{"id":"OibruKpcg6KI","execution":{"iopub.status.busy":"2021-08-28T15:12:30.816973Z","iopub.execute_input":"2021-08-28T15:12:30.817280Z","iopub.status.idle":"2021-08-28T15:12:32.873681Z","shell.execute_reply.started":"2021-08-28T15:12:30.817251Z","shell.execute_reply":"2021-08-28T15:12:32.872824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# As we can see from the result we have stored path of each image in \"train_list_ds\"\nfor f in train_list_ds.take(10):\n    print(f)","metadata":{"id":"b7l5YHrXg6Px","outputId":"be310411-b12e-4464-d34e-a3f24b57e229","execution":{"iopub.status.busy":"2021-08-28T15:12:32.876900Z","iopub.execute_input":"2021-08-28T15:12:32.877158Z","iopub.status.idle":"2021-08-28T15:12:32.936874Z","shell.execute_reply.started":"2021-08-28T15:12:32.877131Z","shell.execute_reply":"2021-08-28T15:12:32.936106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now, Need to know how many classes we have ? , Which are stored inside the \"class_names\" variable as list of strings\nclass_names= [i.name for i in train_data_dir.glob('*')]\nclass_names","metadata":{"id":"Cvu3tcCepLYA","outputId":"974d842b-bd6a-435f-d1a2-5973c81a24b6","execution":{"iopub.status.busy":"2021-08-28T15:12:32.939853Z","iopub.execute_input":"2021-08-28T15:12:32.940093Z","iopub.status.idle":"2021-08-28T15:12:32.945728Z","shell.execute_reply.started":"2021-08-28T15:12:32.940068Z","shell.execute_reply":"2021-08-28T15:12:32.944775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_label(file_path):\n    # split the path by seperator \"/\"\n    parts = tf.strings.split(file_path,  os.path.sep)\n\n    # eg. let's assume value inside the \"one_hot\" will be [1, 0], this means that this file is belong to of class \n    # dog from list of class_names ['dog', 'cat'] \n    one_hot = parts[-2] == class_names\n\n    return tf.argmax(one_hot) # Return 1 or 0","metadata":{"id":"jtdrFF1vpLVJ","execution":{"iopub.status.busy":"2021-08-28T15:12:32.947189Z","iopub.execute_input":"2021-08-28T15:12:32.947732Z","iopub.status.idle":"2021-08-28T15:12:32.955861Z","shell.execute_reply.started":"2021-08-28T15:12:32.947696Z","shell.execute_reply":"2021-08-28T15:12:32.955051Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_image(file_path):\n\n    # read image in the string format\n    image = tf.io.read_file(file_path)\n\n    # decode image, this will decode string into array\n    image = tf.io.decode_jpeg(image)\n\n    return tf.image.resize(image, size =[224, 224]) # resize the image [224,224]","metadata":{"id":"QDIZbOpxrKjw","execution":{"iopub.status.busy":"2021-08-28T15:12:32.958245Z","iopub.execute_input":"2021-08-28T15:12:32.958509Z","iopub.status.idle":"2021-08-28T15:12:32.965992Z","shell.execute_reply.started":"2021-08-28T15:12:32.958484Z","shell.execute_reply":"2021-08-28T15:12:32.965081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process_img(file_path):\n\n    # using \"get_label\" function we will get label\n    label = get_label(file_path)\n\n    # using \"decode_label\" function we will get image in format of array\n    image = decode_image(file_path)\n\n    return  image, label # returning image and label","metadata":{"id":"QkAhPKO8n92g","execution":{"iopub.status.busy":"2021-08-28T15:12:33.256213Z","iopub.execute_input":"2021-08-28T15:12:33.256512Z","iopub.status.idle":"2021-08-28T15:12:33.261331Z","shell.execute_reply.started":"2021-08-28T15:12:33.256485Z","shell.execute_reply":"2021-08-28T15:12:33.260482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# prefetch base on the memory available\nAUTOTUNE = tf.data.AUTOTUNE\n\n# Finally we will call \"process_img\" function, using map, This will store image and label in train_ds. \ntrain_ds = train_list_ds.map(process_img, AUTOTUNE )\n\n# Similarly for valid dataset\nvalid_ds = valid_list_ds.map(process_img, AUTOTUNE)","metadata":{"id":"rzil3n8Mn95u","execution":{"iopub.status.busy":"2021-08-28T15:12:33.657145Z","iopub.execute_input":"2021-08-28T15:12:33.657418Z","iopub.status.idle":"2021-08-28T15:12:33.867209Z","shell.execute_reply.started":"2021-08-28T15:12:33.657392Z","shell.execute_reply":"2021-08-28T15:12:33.866310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def configure_for_performance(ds):\n\n    #  To train a model with this dataset you will want the data:\n    #  To be well shuffled.\n    #  To be batched.\n    #  Batches to be available as soon as possible.\n    ds = ds.cache()\n    ds = ds.shuffle(buffer_size=1000)\n    ds = ds.batch(batch_size)\n    ds = ds.prefetch(buffer_size = AUTOTUNE)\n    return ds","metadata":{"id":"4QDZzSrssFCZ","execution":{"iopub.status.busy":"2021-08-28T15:12:34.207765Z","iopub.execute_input":"2021-08-28T15:12:34.208049Z","iopub.status.idle":"2021-08-28T15:12:34.214155Z","shell.execute_reply.started":"2021-08-28T15:12:34.208022Z","shell.execute_reply":"2021-08-28T15:12:34.213266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# configure the performance\ntrain_ds = configure_for_performance(train_ds)\nvalid_ds  = configure_for_performance(valid_ds)","metadata":{"id":"Tk3P6btesFFZ","execution":{"iopub.status.busy":"2021-08-28T15:12:34.604130Z","iopub.execute_input":"2021-08-28T15:12:34.604401Z","iopub.status.idle":"2021-08-28T15:12:34.611997Z","shell.execute_reply.started":"2021-08-28T15:12:34.604374Z","shell.execute_reply":"2021-08-28T15:12:34.611157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's visualize the dataset\nimage_batch, label_batch = next(iter(train_ds))\n\nplt.figure(figsize=(10, 10))\nfor i in range(9):\n    ax = plt.subplot(3, 3, i + 1)\n    plt.imshow(image_batch[i].numpy().astype(\"uint8\"))\n\n    label = label_batch[i]\n    plt.title(class_names[label])\n    plt.axis(\"off\")","metadata":{"id":"EA4MmFtIsFIN","outputId":"d422d959-da5f-4682-9978-d7ce88423639","execution":{"iopub.status.busy":"2021-08-28T15:12:35.124232Z","iopub.execute_input":"2021-08-28T15:12:35.124546Z","iopub.status.idle":"2021-08-28T15:12:37.116247Z","shell.execute_reply.started":"2021-08-28T15:12:35.124503Z","shell.execute_reply":"2021-08-28T15:12:37.115421Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### c) Build model withiout finetuning\n\nWe will use Mobilenet pretrained model","metadata":{"id":"HyamZr8MONRa"}},{"cell_type":"code","source":"base_model = keras.applications.MobileNet(input_shape=(224,224,3),weights = 'imagenet', include_top=False)\n# base_model.summary()","metadata":{"id":"VIuXlQW1rwJV","execution":{"iopub.status.busy":"2021-08-28T15:23:35.487561Z","iopub.execute_input":"2021-08-28T15:23:35.487902Z","iopub.status.idle":"2021-08-28T15:23:35.992361Z","shell.execute_reply.started":"2021-08-28T15:23:35.487872Z","shell.execute_reply":"2021-08-28T15:23:35.991524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set base model trainable as false\nbase_model.trainable = True\nfor layer in base_model.layers[:-25]:\n    layer.trainable =False","metadata":{"id":"_GGmlE6YsaHH","execution":{"iopub.status.busy":"2021-08-28T15:23:35.993915Z","iopub.execute_input":"2021-08-28T15:23:35.994242Z","iopub.status.idle":"2021-08-28T15:23:36.003285Z","shell.execute_reply.started":"2021-08-28T15:23:35.994205Z","shell.execute_reply":"2021-08-28T15:23:36.002249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# keras input with image shape (224, 224 , 3)\ninputs = keras.Input(shape = (224,224,3))\n\n# Preprocessing of image\n  # rescaling by dividing 255.0\nx = keras.layers.experimental.preprocessing.Rescaling(1./255)(inputs)\n\n  # RandomRotation by 20\nx = keras.layers.experimental.preprocessing.RandomRotation(20)(x)\n  # RandomHeight by 0.2\nx = keras.layers.experimental.preprocessing.RandomHeight(0.2)(x)\n  # RandomWidth by 0.2\nx = keras.layers.experimental.preprocessing.RandomWidth(0.2)(x)\n\n# Let's pass the model to base model\nx = base_model(x)\n\n# Global average pooling for passing to dense layer \nx = keras.layers.GlobalAveragePooling2D()(x)\n\n# Dense layer with 64 neuron\nx = keras.layers.Dense(64, activation ='relu')(x)\n\n# Dropout 20% of neuron\nx = keras.layers.Dropout(0.2)(x)\n\n# Let's predict\noutputs= keras.layers.Dense(2, activation='softmax')(x)\n\n# finally we are here to create model\nmodel = keras.Model(inputs, outputs)","metadata":{"id":"kRchm29Nr8Vw","execution":{"iopub.status.busy":"2021-08-28T15:23:36.494973Z","iopub.execute_input":"2021-08-28T15:23:36.495295Z","iopub.status.idle":"2021-08-28T15:23:36.755303Z","shell.execute_reply.started":"2021-08-28T15:23:36.495265Z","shell.execute_reply":"2021-08-28T15:23:36.754540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Compile the model\n\n # We use SparseCategoricalCrossentropy because our label are in integet format\nmodel.compile(loss = keras.losses.SparseCategoricalCrossentropy(),\n             optimizer = keras.optimizers.Adam(learning_rate=0.001),\n             metrics = ['accuracy'])","metadata":{"id":"yvkjSWqWsVDz","execution":{"iopub.status.busy":"2021-08-28T15:23:38.345634Z","iopub.execute_input":"2021-08-28T15:23:38.345948Z","iopub.status.idle":"2021-08-28T15:23:38.360128Z","shell.execute_reply.started":"2021-08-28T15:23:38.345919Z","shell.execute_reply":"2021-08-28T15:23:38.359214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# We will stop training of model if there is no change in val loss upto 3 iteration\nearly_stopping = keras.callbacks.EarlyStopping(monitor='val_loss', patience = 3)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:23:42.127243Z","iopub.execute_input":"2021-08-28T15:23:42.127572Z","iopub.status.idle":"2021-08-28T15:23:42.131229Z","shell.execute_reply.started":"2021-08-28T15:23:42.127522Z","shell.execute_reply":"2021-08-28T15:23:42.130314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fit the model \nhistory = model.fit(train_ds, epochs = 20,\n          validation_data = valid_ds,\n         batch_size = 32, callbacks = [early_stopping])","metadata":{"id":"HOvWDCSJsY2A","outputId":"0369aaa7-f932-4512-d8c3-f3b01f3133bd","execution":{"iopub.status.busy":"2021-08-28T15:23:43.086499Z","iopub.execute_input":"2021-08-28T15:23:43.086885Z","iopub.status.idle":"2021-08-28T15:25:46.698352Z","shell.execute_reply.started":"2021-08-28T15:23:43.086852Z","shell.execute_reply":"2021-08-28T15:25:46.697602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\npd.DataFrame(history.history).plot()","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:26:10.811981Z","iopub.execute_input":"2021-08-28T15:26:10.812373Z","iopub.status.idle":"2021-08-28T15:26:11.022626Z","shell.execute_reply.started":"2021-08-28T15:26:10.812330Z","shell.execute_reply":"2021-08-28T15:26:11.021627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Model_path= '/kaggle/working/Model/'","metadata":{"id":"thr9NXIuQ-fw","execution":{"iopub.status.busy":"2021-08-28T15:26:18.185698Z","iopub.execute_input":"2021-08-28T15:26:18.186019Z","iopub.status.idle":"2021-08-28T15:26:18.190449Z","shell.execute_reply.started":"2021-08-28T15:26:18.185990Z","shell.execute_reply":"2021-08-28T15:26:18.189418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check is model path exists or not, otherwise create one\nif not os.path.exists(Model_path):\n    os.makedirs(Model_path)\n    print(f'New dir created at {Model_path}')","metadata":{"id":"0BLVdiM1sf3I","outputId":"d6583be9-9da8-4fe4-ba87-0de57f4af74e","execution":{"iopub.status.busy":"2021-08-28T15:26:18.831639Z","iopub.execute_input":"2021-08-28T15:26:18.831960Z","iopub.status.idle":"2021-08-28T15:26:18.836910Z","shell.execute_reply.started":"2021-08-28T15:26:18.831932Z","shell.execute_reply":"2021-08-28T15:26:18.835953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save(Model_path + '/cat_dog.h5')","metadata":{"id":"ccbUMT-LUqpk","execution":{"iopub.status.busy":"2021-08-28T15:26:19.420342Z","iopub.execute_input":"2021-08-28T15:26:19.420676Z","iopub.status.idle":"2021-08-28T15:26:19.624760Z","shell.execute_reply.started":"2021-08-28T15:26:19.420644Z","shell.execute_reply":"2021-08-28T15:26:19.623910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = keras.models.load_model(Model_path + '/cat_dog.h5')","metadata":{"id":"jFSKQtwrkMgg","execution":{"iopub.status.busy":"2021-08-28T15:26:19.927946Z","iopub.execute_input":"2021-08-28T15:26:19.928256Z","iopub.status.idle":"2021-08-28T15:26:21.087579Z","shell.execute_reply.started":"2021-08-28T15:26:19.928229Z","shell.execute_reply":"2021-08-28T15:26:21.086646Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_image(img_path = None):\n    # This functions, will return img array, \n    # if image path is not given it will choose randomly from the val directory, either from dog or cat class\n    if img_path == None:\n        random_class = str(random.sample(class_names,1)[0])\n        img_path = os.path.join(base_dir + 'val/' +random_class + '/'+ random.sample(os.listdir(base_dir + 'val/'+ random_class + '/'),1)[0])\n    else:\n        img = tf.io.read_file(img_path)\n        img = tf.image.decode_image(img, channels=3)\n        img = tf.image.resize(img, size = [224,224])\n        img = tf.expand_dims(img.numpy(), axis = 0)\n    return img","metadata":{"id":"UMNa6XNgVTkb","execution":{"iopub.status.busy":"2021-08-28T15:26:21.154381Z","iopub.execute_input":"2021-08-28T15:26:21.154677Z","iopub.status.idle":"2021-08-28T15:26:21.161863Z","shell.execute_reply.started":"2021-08-28T15:26:21.154648Z","shell.execute_reply":"2021-08-28T15:26:21.160850Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### d) Evaluate model by visualizing","metadata":{"id":"2t0fHQfvOYz8"}},{"cell_type":"markdown","source":" Let's evaluate our dataset on \n> Some questions we need to answer\n>> 1) Which images are the most confident?    \n>> 2) Which images are the list confident?    \n>> 3) Which images got high confidence instead of high probability?  ","metadata":{"id":"PHYNq6l0xplz"}},{"cell_type":"code","source":"pred_prob_0= [] # prob of class 0\npred_prob_1= [] # prob of class 1\npred_class = [] # pred class by our model\nactual_class = [] # Actualc class\npath_of_image = [] # path of ptredicted image\n\nfor cat_name in class_names:\n    for im_g in tqdm(os.listdir(base_dir + 'val/' + cat_name + '/')):  \n        pred = model.predict(get_image(img_path=base_dir + 'val/' + cat_name + '/' + im_g))\n        pred_prob_0.append(pred[0][0])\n        pred_prob_1.append(pred[0][1])\n        pred_class.append(np.argmax(pred))\n        actual_class.append(class_names.index(cat_name))\n        path_of_image.append(base_dir + 'val/' + cat_name + '/' + im_g)\n  ","metadata":{"id":"Y45CeiNQyQn9","outputId":"eac66c38-d904-42eb-8986-b3c1a349b630","execution":{"iopub.status.busy":"2021-08-28T15:26:24.023221Z","iopub.execute_input":"2021-08-28T15:26:24.023554Z","iopub.status.idle":"2021-08-28T15:28:02.042477Z","shell.execute_reply.started":"2021-08-28T15:26:24.023506Z","shell.execute_reply":"2021-08-28T15:28:02.041659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's store all data we collected inside a dataframe\ndf = pd.DataFrame(columns = ['pred_prob_0', 'pred_prob_1', 'pred_class', 'actual_class'])\ndf['pred_prob_0'] = pred_prob_0\ndf['pred_prob_1'] = pred_prob_1\ndf['pred_class'] = pred_class\ndf['actual_class'] = actual_class\ndf['path_of_image'] = path_of_image\n# df['pred_class'] = df['pred_class'].astype('int64')\n# df['actual_class'] = df['actual_class'].astype('int64')\ndf.sample(10)","metadata":{"id":"R6WNyO3M0T4I","outputId":"d2ac909f-7484-4164-9ec9-f7448d86d8dd","execution":{"iopub.status.busy":"2021-08-28T15:28:02.044299Z","iopub.execute_input":"2021-08-28T15:28:02.044662Z","iopub.status.idle":"2021-08-28T15:28:02.089663Z","shell.execute_reply.started":"2021-08-28T15:28:02.044624Z","shell.execute_reply":"2021-08-28T15:28:02.088653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Analysis of dog class\n\nIntentionally i have written this code again and again to have big picture what we are doing","metadata":{"id":"hKdn8xsg2siy"}},{"cell_type":"code","source":"# Images with predicted class as dog with highest probability\nx = df[df['pred_class']==0].sort_values(by = ['pred_prob_0'], ascending = False, axis = 0).iloc[:9]\nplt.figure(figsize=(12,12))\nfor idx in range(9):\n    plt.subplot(3,3, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob dog {round(x.iloc[idx][\"pred_prob_0\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"V4aQOh094ke5","outputId":"39cfdd80-a9e2-41c4-9e29-7e28025aded9","execution":{"iopub.status.busy":"2021-08-28T15:28:02.094173Z","iopub.execute_input":"2021-08-28T15:28:02.094513Z","iopub.status.idle":"2021-08-28T15:28:03.045594Z","shell.execute_reply.started":"2021-08-28T15:28:02.094479Z","shell.execute_reply":"2021-08-28T15:28:03.042339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now lets see images predicted as dog with least probability\n\nx = df[df['pred_class']==0].sort_values(by = ['pred_prob_0'], ascending = True, axis = 0).iloc[:9]\nplt.figure(figsize=(10,10))\nfor idx in range(9):\n    plt.subplot(3,3, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob dog {round(x.iloc[idx][\"pred_prob_0\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"nve_tsLEqbme","outputId":"58b434cd-94a3-4cef-da37-8d43f77ef9fa","execution":{"iopub.status.busy":"2021-08-28T15:28:03.047558Z","iopub.execute_input":"2021-08-28T15:28:03.047898Z","iopub.status.idle":"2021-08-28T15:28:04.221581Z","shell.execute_reply.started":"2021-08-28T15:28:03.047862Z","shell.execute_reply":"2021-08-28T15:28:04.218240Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's see those which are confident about dog inspite of cats\n\nx = df[(df['pred_class']==0) & (df['actual_class']==1)].sort_values(by = ['pred_prob_0'], ascending = False, axis = 0).iloc[:9]\nle = len(x)\nplt.figure(figsize=(10,10))\nfor idx in range(4):\n    plt.subplot(2,2, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob as dog {round(x.iloc[idx][\"pred_prob_0\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"Pw8aFhx6xKmj","outputId":"cf5e8a8d-31a6-4870-98ab-6b221dd0e463","execution":{"iopub.status.busy":"2021-08-28T15:28:04.223183Z","iopub.execute_input":"2021-08-28T15:28:04.223687Z","iopub.status.idle":"2021-08-28T15:28:04.693604Z","shell.execute_reply.started":"2021-08-28T15:28:04.223648Z","shell.execute_reply":"2021-08-28T15:28:04.690232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Analysis of cat class","metadata":{"id":"n7DwF3Y42z73"}},{"cell_type":"code","source":"# Images with predicted class as cat with highest probability\nx = df[df['pred_class']==1].sort_values(by = ['pred_prob_1'], ascending = False, axis = 0).iloc[:9]\nplt.figure(figsize=(12,12))\nfor idx in range(9):\n    plt.subplot(3,3, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob cat {round(x.iloc[idx][\"pred_prob_1\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"LYCFjjiP17-i","outputId":"d6aea0b9-7978-4270-9fb4-e4d076295123","execution":{"iopub.status.busy":"2021-08-28T15:28:04.694968Z","iopub.execute_input":"2021-08-28T15:28:04.695410Z","iopub.status.idle":"2021-08-28T15:28:05.519514Z","shell.execute_reply.started":"2021-08-28T15:28:04.695349Z","shell.execute_reply":"2021-08-28T15:28:05.518633Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now lets see images predicted as cat with least probability\nx = df[df['pred_class']==1].sort_values(by = ['pred_prob_1'], ascending = True, axis = 0).iloc[:9]\nplt.figure(figsize=(10,10))\nfor idx in range(9):\n    plt.subplot(3,3, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob cat {round(x.iloc[idx][\"pred_prob_1\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"ptjyorz927zu","outputId":"3eb9c0bf-cbef-46aa-a8ba-caa5779e6671","execution":{"iopub.status.busy":"2021-08-28T15:28:05.520660Z","iopub.execute_input":"2021-08-28T15:28:05.521095Z","iopub.status.idle":"2021-08-28T15:28:06.265633Z","shell.execute_reply.started":"2021-08-28T15:28:05.521057Z","shell.execute_reply":"2021-08-28T15:28:06.264523Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's see those which are confident about cat inspite of dog\n\nx = df[(df['pred_class']==1) & (df['actual_class']==0)].sort_values(by = ['pred_prob_1'], ascending = False, axis = 0).iloc[:9]\nplt.figure(figsize=(10,10))\nfor idx in range(4):\n    plt.subplot(2,2, idx+1)\n    img = get_image(x.iloc[idx]['path_of_image'])\n    plt.imshow(tf.squeeze(img, axis=0)/255.0)\n    plt.title(label = f'Pred_prob as cat {round(x.iloc[idx][\"pred_prob_1\"], 2)} \\n Actual image {class_names[x.iloc[idx][\"actual_class\"]]}')\n    plt.axis('off')","metadata":{"id":"p8SDl9u3272g","outputId":"d9d29925-bd9b-4ade-ffd8-f43cf99e5f9c","execution":{"iopub.status.busy":"2021-08-28T15:28:06.268051Z","iopub.execute_input":"2021-08-28T15:28:06.268378Z","iopub.status.idle":"2021-08-28T15:28:06.990775Z","shell.execute_reply.started":"2021-08-28T15:28:06.268342Z","shell.execute_reply":"2021-08-28T15:28:06.982885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### e) evaluation matrics ","metadata":{"id":"eHF-s4a3PYX8"}},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\nfrom sklearn.metrics import recall_score","metadata":{"id":"52eTheHIUeSZ","execution":{"iopub.status.busy":"2021-08-28T15:28:23.522600Z","iopub.execute_input":"2021-08-28T15:28:23.522927Z","iopub.status.idle":"2021-08-28T15:28:23.529276Z","shell.execute_reply.started":"2021-08-28T15:28:23.522899Z","shell.execute_reply":"2021-08-28T15:28:23.528334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 1) Classification accuracy\n\n","metadata":{"id":"ejnONjYOPxxF"}},{"cell_type":"markdown","source":"\n$Accuracy = \\frac{\\text{Number of correct prediction }}{\\text{ No of all prediction }}$","metadata":{"id":"fwzXv6DMRnP4"}},{"cell_type":"code","source":"print(f\"Accuracy of our model is {accuracy_score(df['actual_class'], df['pred_class'])}\")","metadata":{"id":"RsB94i9f3tCp","outputId":"eeb4faff-cbcb-4c9d-b628-82f8dd8f1f7e","execution":{"iopub.status.busy":"2021-08-28T15:28:25.980477Z","iopub.execute_input":"2021-08-28T15:28:25.980872Z","iopub.status.idle":"2021-08-28T15:28:25.988896Z","shell.execute_reply.started":"2021-08-28T15:28:25.980840Z","shell.execute_reply":"2021-08-28T15:28:25.987913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Pie chart\n\nlabels = ['Correctly predicted', 'Incorrectly predicted']\nsizes = [accuracy_score(df['actual_class'], df['pred_class']), 1-accuracy_score(df['actual_class'], df['pred_class'])]\nexplode = (0, 0.1)  # only \"explode\" the 2nd slice\n\nfig1, ax1 = plt.subplots()\nax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',\n        shadow=True, startangle=90)\nax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.\n\nplt.show()\n\n# it's look really cool","metadata":{"id":"E0SQiOspVQ9h","outputId":"b9c4a83e-0ee8-4543-b26e-84033bb8c3fb","execution":{"iopub.status.busy":"2021-08-28T15:28:27.038338Z","iopub.execute_input":"2021-08-28T15:28:27.038675Z","iopub.status.idle":"2021-08-28T15:28:27.157034Z","shell.execute_reply.started":"2021-08-28T15:28:27.038644Z","shell.execute_reply":"2021-08-28T15:28:27.156072Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 2) Confusion Matrix","metadata":{"id":"MNcqIxVAU78X"}},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns","metadata":{"id":"bRrSuToDUNfI","execution":{"iopub.status.busy":"2021-08-28T15:28:28.949883Z","iopub.execute_input":"2021-08-28T15:28:28.950347Z","iopub.status.idle":"2021-08-28T15:28:29.077020Z","shell.execute_reply.started":"2021-08-28T15:28:28.950310Z","shell.execute_reply":"2021-08-28T15:28:29.076228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cm = confusion_matrix(df['actual_class'], df['pred_class'])\ncm","metadata":{"id":"wv6g06Q_UzdQ","execution":{"iopub.status.busy":"2021-08-28T15:28:38.064095Z","iopub.execute_input":"2021-08-28T15:28:38.064425Z","iopub.status.idle":"2021-08-28T15:28:38.076218Z","shell.execute_reply.started":"2021-08-28T15:28:38.064394Z","shell.execute_reply":"2021-08-28T15:28:38.075212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This doesent look good , we will try to plot","metadata":{"id":"6t1Z5UL4XRgF"}},{"cell_type":"code","source":"ax = sns.heatmap(cm, annot=True, fmt='g');\n\n## set title, X/Y Labels.\nax.set_title('Seaborn Confusion Matrix');\nax.set_xlabel('Predicted Class')\nax.set_ylabel('Actual Class');\n## For the Tick Labels, the labels should be in Alphabetical order\nax.xaxis.set_ticklabels(class_names)\nax.yaxis.set_ticklabels(class_names)","metadata":{"id":"KA1URifrWjJh","outputId":"8764d03f-b565-49c2-d3c0-82eaab2a08ca","execution":{"iopub.status.busy":"2021-08-28T15:28:42.979065Z","iopub.execute_input":"2021-08-28T15:28:42.979403Z","iopub.status.idle":"2021-08-28T15:28:43.208906Z","shell.execute_reply.started":"2021-08-28T15:28:42.979375Z","shell.execute_reply":"2021-08-28T15:28:43.208127Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 3) Precision\n\nThis will tell us how many predicted positive are correcly classified  \n$ Precision = \\frac{\\text{TP}}{\\text{TP + FP}} $\n\n","metadata":{"id":"UHhJ3BNvYI5-"}},{"cell_type":"code","source":"TP = cm[0][0]\nFP = cm[1][0]\nFN = cm[0][1]\nTN = cm[1][1]","metadata":{"execution":{"iopub.status.busy":"2021-08-28T15:30:29.496443Z","iopub.execute_input":"2021-08-28T15:30:29.496835Z","iopub.status.idle":"2021-08-28T15:30:29.502272Z","shell.execute_reply.started":"2021-08-28T15:30:29.496803Z","shell.execute_reply":"2021-08-28T15:30:29.501358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''comment :-\n      We are predicting correcly a positive class with 97% times, among positive prediction\n      '''\nprecision = TP/(TP+FN)\nprecision","metadata":{"id":"OVDyj-gdb474","outputId":"54b8e36c-9471-4ffd-cf46-66e16162dc59","execution":{"iopub.status.busy":"2021-08-28T15:30:50.802045Z","iopub.execute_input":"2021-08-28T15:30:50.802374Z","iopub.status.idle":"2021-08-28T15:30:50.808058Z","shell.execute_reply.started":"2021-08-28T15:30:50.802345Z","shell.execute_reply":"2021-08-28T15:30:50.807260Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 4) Recall\nThis will tell us, How many predicted positive among all positive in our dataset.  \n\n$ Recall = \\frac{\\text{TP}}{\\text{TP + FN}} $\n\neg. as we can see from the confusion matrix, 1184 of dog class are predicted correctly and 16 predicted as cat though the image is of dog.","metadata":{"id":"PXXFkpxgaceF"}},{"cell_type":"code","source":"''' comment:\n 97% times we are classifying positive image correctly\n'''\nRecall = TP/(TP + FN)\nRecall","metadata":{"id":"ZYpZFgXHXs3k","outputId":"987a6897-650c-4d61-8b69-1585ebe72b45","execution":{"iopub.status.busy":"2021-08-28T15:31:04.906188Z","iopub.execute_input":"2021-08-28T15:31:04.906529Z","iopub.status.idle":"2021-08-28T15:31:04.914179Z","shell.execute_reply.started":"2021-08-28T15:31:04.906497Z","shell.execute_reply":"2021-08-28T15:31:04.913172Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 5) F1 Score\n\n$\\text{F1 score} = 2 * \\frac{\\text{precision} *  \\text{recall}}{\\text{precision} +  \\text{recall}}$","metadata":{"id":"08w5hzAHctxv"}},{"cell_type":"code","source":"# It will take into account both precicion and recall, \n# Best value for f1 score is 1 and the worst 0\n\nF1_score = 2* ((precision * Recall)/(precision + Recall))\nF1_score","metadata":{"id":"6Sze35JSctfY","outputId":"ea5789a4-6663-44d2-e3b0-debee6a7d0e0","execution":{"iopub.status.busy":"2021-08-28T15:31:26.203140Z","iopub.execute_input":"2021-08-28T15:31:26.203478Z","iopub.status.idle":"2021-08-28T15:31:26.209681Z","shell.execute_reply.started":"2021-08-28T15:31:26.203447Z","shell.execute_reply":"2021-08-28T15:31:26.208843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 6) Specificity\n\nProportion of negative class that is correctly predicted negative  , We can say this as recall for negative class\n\n$ Specificity = \\frac{\\text{TN}}{\\text{TN + FP}} $","metadata":{"id":"RhO2J4CQd385"}},{"cell_type":"code","source":"specificity = (TN/(TN+FP))\nspecificity","metadata":{"id":"9vx2Yg0GcqqY","outputId":"dac0aaef-90a5-4188-c3c8-be96b4cf0686","execution":{"iopub.status.busy":"2021-08-28T15:31:39.309350Z","iopub.execute_input":"2021-08-28T15:31:39.309699Z","iopub.status.idle":"2021-08-28T15:31:39.315333Z","shell.execute_reply.started":"2021-08-28T15:31:39.309668Z","shell.execute_reply":"2021-08-28T15:31:39.314411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# If you come along to the end of this notebook, Please do comment for any improvement, Thank you :) ","metadata":{"id":"Dwvbqf6JbYOk"},"execution_count":null,"outputs":[]}]}