{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-09-15T16:29:24.681709Z","iopub.execute_input":"2021-09-15T16:29:24.684319Z","iopub.status.idle":"2021-09-15T16:30:01.072741Z","shell.execute_reply.started":"2021-09-15T16:29:24.684084Z","shell.execute_reply":"2021-09-15T16:30:01.066825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Capstone Project - CV - Pneumonia Detection on XRay DICOM Images","metadata":{}},{"cell_type":"markdown","source":"## Imports and Initializations","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"#","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:26.776627Z","iopub.execute_input":"2021-09-15T16:42:26.776960Z","iopub.status.idle":"2021-09-15T16:42:26.781989Z","shell.execute_reply.started":"2021-09-15T16:42:26.776929Z","shell.execute_reply":"2021-09-15T16:42:26.781179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#!pip install pydicom","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:28.093070Z","iopub.execute_input":"2021-09-15T16:42:28.093349Z","iopub.status.idle":"2021-09-15T16:42:28.099172Z","shell.execute_reply.started":"2021-09-15T16:42:28.093323Z","shell.execute_reply":"2021-09-15T16:42:28.097832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Imports\nimport os\nimport cv2\nimport pydicom\n\nimport numpy as np \nimport pandas as pd \nimport seaborn as sns\nimport matplotlib.pylab as plt\nimport tensorflow as tf\n%matplotlib inline\n\n#from google.colab import drive\n\nfrom tensorflow.keras.applications.mobilenet import MobileNet\nfrom tensorflow.keras.layers import Concatenate, UpSampling2D, Conv2D, Reshape, BatchNormalization\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.losses import binary_crossentropy\nfrom tensorflow.keras.optimizers import Adam\n\nimport skimage\nfrom skimage.transform import resize\nfrom skimage import feature, filters\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import classification_report\nimport random\nimport pickle","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:28.705502Z","iopub.execute_input":"2021-09-15T16:42:28.705828Z","iopub.status.idle":"2021-09-15T16:42:37.744767Z","shell.execute_reply.started":"2021-09-15T16:42:28.705796Z","shell.execute_reply":"2021-09-15T16:42:37.743988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:37.746345Z","iopub.execute_input":"2021-09-15T16:42:37.747402Z","iopub.status.idle":"2021-09-15T16:42:37.751922Z","shell.execute_reply.started":"2021-09-15T16:42:37.747353Z","shell.execute_reply":"2021-09-15T16:42:37.751283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(pydicom.__version__)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:37.753911Z","iopub.execute_input":"2021-09-15T16:42:37.754340Z","iopub.status.idle":"2021-09-15T16:42:37.790592Z","shell.execute_reply.started":"2021-09-15T16:42:37.754286Z","shell.execute_reply":"2021-09-15T16:42:37.789737Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"### Kaggle paths\n\ninput_path = '../input/rsna-pneumonia-detection-challenge/'\noutput_path = './'\ntrain_path = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images/'\n\n# Copy previous output files to output directory\nif (os.path.isfile('../input/patient-data/patient_df.pb')):\n    !cp '../input/patient-data/patient_df.pb' './'\n\nif (os.path.isfile('../input/basic_unet_i128_b8_e6/basic_unet_i64_b64_e6_seg.h5')):\n    !cp '../input/basic_unet_i128_b8_e6/basic_unet_i64_b64_e6_seg.h5' './'\n\n\n# Pickled dataframe path\npatient_df_path = output_path + 'patient_df.pb'\n\n# output weights filepaths\n#cust_unet_path = './cust_unet_seg.h5'\n#mobilenet_unet_path = './umobilnet_seg.h5'\n#basic_cnn_path = './basic_cnn_seg.h5'\nbasic_unet_i64_b64_e6_path = './basic_unet_i64_b64_e6_seg.h5'\n\n# Pickle output files\n#basic_cnn_history = output_path + 'BasicCNN_history.pickle'\n#basic_unet_history = output_path + 'BasicUnet_history.pickle'\n#unet_mobnet_history = output_path + 'unet_mobnet_history.pickle'\nbasic_unet_i64_b64_e6_history = output_path + 'basic_unet_i64_b64_e6_history.pickle'\n\n#basic_cnn_creport = output_path +\"BasicCNN_creport.pickle\"\n#basic_unet_creport = output_path + \"BasicUnet_creport.pickle\"\n#unet_mobnet_creport = output_path +'unet_mobnet_creport.pickle\"\nbasic_unet_i64_b64_e6_creport = output_path + 'basic_unet_i64_b64_e6_creport.pickle'\n\n# Model filepaths\n#basic_cnn_model = output_path + 'BasicCNN_model.h5'\n#basic_unet_model = output_path + \"BasicUnet_model.h5\"\n#unet_mobnet_model = output_path + \"unet_mobnet_model.h5\"\nbasic_unet_i64_b64_e6_model_path = output_path + 'basic_unet_i64_b64_e6_model.h5'\n","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:58.961038Z","iopub.execute_input":"2021-09-15T16:42:58.961943Z","iopub.status.idle":"2021-09-15T16:42:58.973923Z","shell.execute_reply.started":"2021-09-15T16:42:58.961893Z","shell.execute_reply":"2021-09-15T16:42:58.972800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_SHAPE = [1024,1024]\nIMG_PATH = train_path\nINPUT_SIZE_64 = [64,64,3]\nBATCH_SIZE_8 = 8\nBATCH_SIZE_32 = 32\nBATCH_SIZE_64 = 64\nEPOCH_SIZE = 6","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:42:59.223302Z","iopub.execute_input":"2021-09-15T16:42:59.224322Z","iopub.status.idle":"2021-09-15T16:42:59.230266Z","shell.execute_reply.started":"2021-09-15T16:42:59.224237Z","shell.execute_reply":"2021-09-15T16:42:59.229174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Utility Functions**","metadata":{}},{"cell_type":"markdown","source":"**Functions for image visualization**","metadata":{}},{"cell_type":"code","source":"def get_img(img_path, patientId):\n    '''Function to get an pixel array image from dcm file'''\n\n    dcm_filename = img_path + patientId + '.dcm'\n    dcm_img = pydicom.read_file(dcm_filename)\n    \n    img = dcm_img.pixel_array\n    return dcm_filename,img\n\ndef get_mask(bboxes, input_size):\n    '''Function to get mask given the bounding box co-ordinates and the image_size'''\n    \n    # add 1's at the location of pneumonia\n    mask = np.zeros(IMAGE_SHAPE) # Black background\n    for bbox in bboxes:\n        if ( not np.isnan(bbox).any()):\n            xmin, ymin, width, height = [int(i) for i in bbox] # Get box co-ordinates\n            mask[ymin:ymin+height, xmin:xmin+width] = 1 # Color Highlight the pneumonia area\n        \n    mask = resize(mask,input_size, mode='reflect') # resize output mask to  input size\n    return (mask)\n\ndef draw_box(image, box):\n    '''Function to draw a rectangle on an image'''\n\n    # Convert coordinates to integers\n    box = map(int, box)\n    \n    # Extract coordinates for rectangle\n    x, y, width, height = box\n    x1 = x + width\n    y1 = y + height\n   \n    color = (255, 0, 0) \n    thickness = 8\n    \n    # Draw a rectangle with line borders of thickness of 8 px \n    image = cv2.rectangle(image, (x,y), (x1,y1), color, thickness) \n\n    return image\n\ndef get_boxes(patient_df, patientId):\n    ''' Return bboxes for the given patientId'''\n    \n    return patient_df[patient_df['patientId'] == patientId]['bboxes'].values[0]\n\n\ndef draw_bounded_image(img_path, patient_df, patientId):\n    '''Bound the image with boxes covering the affected pneumonia areas'''\n\n    # Get pixel image array\n    file,image = get_img(img_path, patientId)\n\n    # Get box co-ordinates for this patient\n    boxes = get_boxes(patient_df, patientId)\n\n    # Add the boxes to image\n    for box in boxes:\n        if ( np.isnan(np.sum(boxes)).any() ) : # Check if image is of a person without penumonia i.e box = NaN)\n            break;\n        else:\n            image = draw_box(image=image, box=box) # Overlay the box on the image\n\n    return file,image","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:43:01.807585Z","iopub.execute_input":"2021-09-15T16:43:01.808414Z","iopub.status.idle":"2021-09-15T16:43:01.823949Z","shell.execute_reply.started":"2021-09-15T16:43:01.808347Z","shell.execute_reply":"2021-09-15T16:43:01.822869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def printTwoImgs(img1, img2, title1, title2):\n    \n    fig, ax = plt.subplots(1, 2, figsize=(10,10))\n\n    ax[0].imshow(img1, cmap=\"gray\")\n    ax[1].imshow(img2, cmap=\"gray\")\n    \n    ax[0].set_title(title1)\n    ax[1].set_title(title2)\n    \n    ax[0].axis('off')\n    ax[1].axis('off')","metadata":{"execution":{"iopub.status.busy":"2021-09-15T16:43:04.021659Z","iopub.execute_input":"2021-09-15T16:43:04.022209Z","iopub.status.idle":"2021-09-15T16:43:04.028435Z","shell.execute_reply.started":"2021-09-15T16:43:04.022156Z","shell.execute_reply":"2021-09-15T16:43:04.027783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Functions for data preprocessing**","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"def get_single_df(patient_df_df,classes_df,df_path, images_path):\n    ''' Function to add relevant dicom image metadata columns into dataframe'''\n\n    # If the session has a prestored dataframe pickle use it.\n    if (os.path.isfile(df_path)):\n        print(\"Reading prestored dataframe object\")\n        patient_df = pd.read_pickle(df_path)\n        return patient_df\n        \n    bboxes = [patient_df_df[patient_df_df['patientId'] == patientId].loc[:,'x':'height'].values \n                  for patientId in patient_df_df['patientId']] # Get list of bboxes for each patientId\n    \n    patient_df = patient_df_df.copy()\n    patient_df.insert(1, 'bboxes', bboxes ) # Add new bboxes column\n    patient_df.insert(2, 'class', classes_df['class']) #  Add class column\n\n    # Remove x,y, width, height columns and drop the duplicate rows of patientIds \n    patient_df = patient_df.drop(columns=['x','y','width','height']).drop_duplicates(subset=['patientId'], ignore_index=True)\n    \n    # Initialize columns for DICOM metadata\n    patient_df['gender'] = np.nan\n    patient_df['age'] = np.nan\n    patient_df['viewpos'] = np.nan\n    patient_df['dimx'] = np.nan\n    patient_df['dimy'] = np.nan\n\n    for patientId in patient_df['patientId']:    \n        # Get dcm filename\n        dcm_file = images_path + patientId + '.dcm'\n        ds = pydicom.dcmread(dcm_file)\n    \n        # Get the row indices of patientId in dataframe\n        indices = patient_df.index[patient_df['patientId'] == patientId].tolist()\n    \n        # Add the sex, age, viewpos, image dimensions data to the  dataframe\n        patient_df.at[indices,'gender'] = ds.PatientSex\n        patient_df.at[indices,'age'] = ds.PatientAge\n        patient_df.at[indices , 'viewpos'] = ds[0x0018, 0x5101].value\n        patient_df.at[indices , 'dimx']    = ds[0x0028, 0x0010].value\n        patient_df.at[indices , 'dimy']    = ds[0x0028, 0x0011].value\n\n    # Store the updated patient_df dataframe as an object for faster retrieval in subsequent sessions\n    patient_df.to_pickle(df_path) # (not using to_csv because it stores bboxes lists as strings).\n    \n    return patient_df","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:05.153268Z","iopub.execute_input":"2021-09-15T15:14:05.153738Z","iopub.status.idle":"2021-09-15T15:14:05.169093Z","shell.execute_reply.started":"2021-09-15T15:14:05.153685Z","shell.execute_reply":"2021-09-15T15:14:05.168162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the preprocess input function to be called from CustomDataGen\ndef preprocess_input(X):\n  ''' Function to preprocess image. This can vary if we use imported model architectures'''\n  return X/255","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:07.741284Z","iopub.execute_input":"2021-09-15T15:14:07.741603Z","iopub.status.idle":"2021-09-15T15:14:07.746231Z","shell.execute_reply.started":"2021-09-15T15:14:07.741568Z","shell.execute_reply":"2021-09-15T15:14:07.745306Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CustomDataGen(tf.keras.utils.Sequence):\n    '''Define Custom Data Generator'''\n    # Keras ImageDataGenerator cannot be used because of not able to handle DICOM images easily\n    # as well as to create masks in batches, to save memory\n\n    # Called on initialization\n    def __init__(self, df,\n                 batch_size,\n                 input_size=INPUT_SIZE_64,\n                 shuffle=True,\n                 augment = 'no',\n                ):\n        \n        self.df = df.copy()\n        self.batch_size = batch_size\n        self.input_size = input_size\n        self.shuffle = shuffle\n        self.augment = augment\n        self.n = len(self.df)\n\n    # Private method called by __getdata. Creates masks for an image\n    def __getmask(self, bboxes, input_size):\n\n        # add 1's at the location of pneumonia\n        mask = np.zeros(IMAGE_SHAPE) # Black background\n        for bbox in bboxes:\n            if ( not np.isnan(bbox).any()):\n                xmin, ymin, width, height = [int(i) for i in bbox] # Get box co-ordinates\n                mask[ymin:ymin+height, xmin:xmin+width] = 1 # Color Highlight the pneumonia area\n        \n        mask = resize(mask,self.input_size, mode='reflect') # resize output mask to  input size\n        return (mask)\n\n    # Private method called by __getdata. Gets pixel image array and preprocesses it (UNET backbone)\n    def __getimage(self, path, image_size):\n        image_arr = pydicom.read_file(path).pixel_array\n        image_arr = np.stack((image_arr,)*3, axis=-1) # Expand grayscale image to contain 3 channels\n        image_arr = resize(image_arr,self.input_size, mode='reflect')\n        return (image_arr)\n    \n    # Private method called by __getitem__. Gets X and y for a batch\n    def __getdata(self, batches):\n        # Generates data containing batch_size samples of features(image pixels array) and targets(mask and label)\n\n        batch_paths = [IMG_PATH + patientId + '.dcm' for patientId in batches['patientId']]\n        batch_bboxes = batches['bboxes'].values\n\n        X_batch = np.asarray([self.__getimage(path, self.input_size) for path in batch_paths])\n\n        y_batch = np.asarray([self.__getmask( bbox, self.input_size) \n                               for bbox in batch_bboxes])\n        return X_batch, y_batch\n\n    # Mandatory Sequence class method, called to yield the next batch of data at index \n    def __getitem__(self, index):\n        batch_start = index * self.batch_size\n        batch_end = (index + 1) * self.batch_size\n        batches = self.df[batch_start:batch_end]\n        X_batch, y_batch = self.__getdata(batches) \n        X_batch = preprocess_input(X_batch)\n        return X_batch, y_batch\n    \n    # Mandatory Sequence class method. Get number of steps in an epoch that runs all batches to update the model\n    def __len__(self):\n        return self.n // self.batch_size\n\n    # Optional Sequence class method, called at the end of every epoch during model run. \n    def on_epoch_end(self):\n        # Shuffle data at end of every epoch                       \n        if self.shuffle:\n            self.df = self.df.sample(frac=1).reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:10.728706Z","iopub.execute_input":"2021-09-15T15:14:10.729426Z","iopub.status.idle":"2021-09-15T15:14:10.747904Z","shell.execute_reply.started":"2021-09-15T15:14:10.729368Z","shell.execute_reply":"2021-09-15T15:14:10.746777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Functions for Model Building**","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"def conv2d_block(input_tensor, n_filters=16):\n    \"\"\"Function to add 2 convolutional layers with the parameters passed to it\"\"\"\n\n    # first convolution\n    x = tf.keras.layers.Conv2D(n_filters, (3,3), kernel_initializer = 'he_normal', padding = 'same')(input_tensor)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.Activation('relu')(x)\n    \n  \n    # second convolution\n    x = tf.keras.layers.Conv2D(n_filters, (3,3), kernel_initializer = 'he_normal', padding = 'same')(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.Activation('relu')(x)\n    \n    return x","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:15.008777Z","iopub.execute_input":"2021-09-15T15:14:15.010123Z","iopub.status.idle":"2021-09-15T15:14:15.022333Z","shell.execute_reply.started":"2021-09-15T15:14:15.01006Z","shell.execute_reply":"2021-09-15T15:14:15.021357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_cnnmodel(input_size=INPUT_SIZE_64):\n    \n    #Define input layer\n    input_tensor = tf.keras.layers.Input(input_size, name='input_layer')\n    \n    #First double convolution block\n    n_filters = 16\n    c1 = conv2d_block(input_tensor, n_filters*1)\n    c2 = conv2d_block(c1, n_filters*2)\n    c4 = conv2d_block(c2, n_filters*4)\n\n    #Build the Output layer\n    outputs = tf.keras.layers.Conv2D(1, kernel_size=1, activation='sigmoid')(c4) \n\n    #Build the model using different layers\n    model = tf.keras.Model(inputs=[input_tensor], outputs=[outputs])\n    return model\n    ","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:16.495067Z","iopub.execute_input":"2021-09-15T15:14:16.4956Z","iopub.status.idle":"2021-09-15T15:14:16.505693Z","shell.execute_reply.started":"2021-09-15T15:14:16.495563Z","shell.execute_reply":"2021-09-15T15:14:16.504765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def down_sample(tensor, dropout):\n    d = tf.keras.layers.MaxPooling2D((2, 2))(tensor) \n    return tf.keras.layers.Dropout(dropout)(d) \n    \ndef up_sample(tensor, n_filters):\n    return tf.keras.layers.Conv2DTranspose(n_filters, (3, 3), \n                                         strides = (2, 2), \n                                         padding = 'same')(tensor)\ndef concat(tensor1, tensor2, dropout):\n    c = tf.keras.layers.concatenate([tensor1, tensor2])\n    return tf.keras.layers.Dropout(dropout)(c) \n    \ndef create_custom_unet(input_size=INPUT_SIZE_64, n_filters=16, dropout=0.4):\n    ''' Function to build a custom UNET model from scratch using CNN'''\n    \n    #Define input layer\n    input_tensor = tf.keras.layers.Input(input_size, name='input_layer')\n\n    #ENCODER - DOWNSAMPLE the image\n\n    #First Block of Encoder\n    c1 = conv2d_block(input_tensor, n_filters*1) # 64*64*16\n    e1 = down_sample(c1, dropout)\n\n    #Second Block of Encoder\n    c2 = conv2d_block(e1, n_filters*2) # 32 *32 * 32\n    e2 = down_sample(c2, dropout)\n\n    #Central Block of Encoder\n    c3 = conv2d_block(e2, n_filters*4) # 16 * 16 * 64\n\n    #We now have output of Encoder\n\n    #DECODER - UPSAMPLE the feature to generate mask\n\n    # Central Block of Decoder\n    d3 = conv2d_block(c3, n_filters*4) # 16 * 16 * 64\n    \n    # First Block of Decoder\n    d2 = up_sample(d3, n_filters*4) \n    d2 = concat(d2,c2, dropout) #First Block of Decoder - connected to Second block on Encoder side\n    c4 = conv2d_block(d2, n_filters * 2)# 32 * 32 * 32\n\n    # Second Block of Decoder \n    d1 = up_sample(c4, n_filters*2) \n    d1 = concat(d1,c1,dropout) #Second Block of Decoder - connected to First block on Encoder side\n    c5 = conv2d_block(d1, n_filters*1)# 64 * 64 * 16\n    \n    #Build the Output layer\n    outputs = tf.keras.layers.Conv2D(1, kernel_size=1, activation='sigmoid')(c5) \n\n    #Build the model using different layers\n    model = tf.keras.Model(inputs=[input_tensor], outputs=[outputs])\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:18.301283Z","iopub.execute_input":"2021-09-15T15:14:18.303539Z","iopub.status.idle":"2021-09-15T15:14:18.317969Z","shell.execute_reply.started":"2021-09-15T15:14:18.303488Z","shell.execute_reply":"2021-09-15T15:14:18.316711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_backbone_unet(backbone, input_size=INPUT_SIZE_64, dropout=0.4):\n  '''Function to create UNET model from mobilenet backbone'''\n    \n  #Freeze Encoder layers\n  for layer in backbone.layers:\n    layer.trainable = False\n  \n  model =  backbone\n    \n  # Build Decoder\n  block1 = model.get_layer(\"conv_pw_1_relu\").output \n  block2 = model.get_layer(\"conv_pw_3_relu\").output\n  block3 = model.get_layer(\"conv_pw_5_relu\").output\n  block6 = model.get_layer(\"conv_pw_11_relu\").output\n  block7 = model.get_layer(\"conv_pw_13_relu\").output\n\n  n_filters = 32\n  x = Concatenate()([UpSampling2D()(block7), block6])\n  x = conv2d_block(x,n_filters * 8)\n\n  x = Concatenate()([UpSampling2D()(x), block3])\n  x = conv2d_block(x,n_filters * 4)\n\n  x = Concatenate()([UpSampling2D()(x), block2])\n  x = conv2d_block(x,n_filters * 2)\n\n  x = Concatenate()([UpSampling2D()(x), block1])\n  x = conv2d_block(x,n_filters)\n\n  x =  UpSampling2D()(x)\n  x = conv2d_block(x,n_filters)\n\n  # Output layer\n  x = Conv2D(1, kernel_size=1, kernel_initializer='he_normal', padding='same')(x)\n  x = tf.keras.layers.Activation('sigmoid')(x)\n\n  return Model(inputs=model.input, outputs=x)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:20.14172Z","iopub.execute_input":"2021-09-15T15:14:20.142018Z","iopub.status.idle":"2021-09-15T15:14:20.153828Z","shell.execute_reply.started":"2021-09-15T15:14:20.14198Z","shell.execute_reply":"2021-09-15T15:14:20.152447Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_unet_model(backbone='mobilenet', input_size=INPUT_SIZE_64, dropout=0.4):\n    ''' Creates UNET model from different backbone, including no backbone(custom UNET from scratch)'''\n\n    model = MobileNet(input_shape=input_size, include_top=False, alpha=1.0, weights = 'imagenet', dropout=0.4)\n    model = create_backbone_unet(model)\n\n    if (backbone == 'None'):\n      model = create_custom_unet()\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:22.46538Z","iopub.execute_input":"2021-09-15T15:14:22.465695Z","iopub.status.idle":"2021-09-15T15:14:22.4714Z","shell.execute_reply.started":"2021-09-15T15:14:22.465658Z","shell.execute_reply":"2021-09-15T15:14:22.4707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Custom metric and loss functions for validation\n\ndef dice_coefficient(y_true, y_pred):\n    numerator = 2 * tf.reduce_sum(y_true * y_pred)\n    denominator = tf.reduce_sum(y_true + y_pred)\n\n    return numerator / (denominator + tf.keras.backend.epsilon())\n  \ndef loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) - tf.keras.backend.log(dice_coefficient(y_true, y_pred) + tf.keras.backend.epsilon())","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:24.512367Z","iopub.execute_input":"2021-09-15T15:14:24.513347Z","iopub.status.idle":"2021-09-15T15:14:24.521745Z","shell.execute_reply.started":"2021-09-15T15:14:24.51328Z","shell.execute_reply":"2021-09-15T15:14:24.520319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def PlotMetrics(history):\n    plt.figure(figsize=(8, 5))\n    plt.grid(True)\n    plt.plot(history.history['dice_coefficient'],     label='Train Dice-Coef',      color = \"green\" );\n    plt.plot(history.history['val_dice_coefficient'], label='Val Dice-Coef',  color = \"yellow\");\n    plt.plot(history.history['loss'],         label='Train Loss',          color = \"red\"   );\n    plt.plot(history.history['val_loss'],     label='Val Loss',      color = \"orange\");\n    plt.title(\"Validation and Training - Loss and Dice Coefficient vs Epoch\")\n    plt.xlabel(\"Epoch\")\n    plt.legend();","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:26.539388Z","iopub.execute_input":"2021-09-15T15:14:26.539688Z","iopub.status.idle":"2021-09-15T15:14:26.547052Z","shell.execute_reply.started":"2021-09-15T15:14:26.539651Z","shell.execute_reply":"2021-09-15T15:14:26.545915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict_imagemask(model, patientId, patient_df, train_path, threshold=0.5, input_size=INPUT_SIZE_64):\n    '''Function to predict a mask for an image'''\n\n    # Get image pixel array\n    _,image = get_img(train_path, patientId)\n    image = resize(image,input_size, mode='reflect')\n\n    # Get bboxes and expected mask\n    bboxes = patient_df[patient_df['patientId'] == patientId]['bboxes'].values[0]\n    mask = get_mask(bboxes, input_size)\n\n    # Prepare the image for feeding into model for \n    image_rescaled = preprocess_input(np.expand_dims(image,0))\n\n    # Predict mask for image\n    pred_mask = model.predict(image_rescaled)\n\n    # remove the batch dimension\n    pred_mask = pred_mask[0]\n\n    # Mask contains probabilities from sigmoid function. Convert to 0 or 1 values for the pixels, by using a threshold\n    pred_mask = (pred_mask > threshold) * 1.0\n\n    return (image,mask,pred_mask)\n","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:28.510474Z","iopub.execute_input":"2021-09-15T15:14:28.510777Z","iopub.status.idle":"2021-09-15T15:14:28.518146Z","shell.execute_reply.started":"2021-09-15T15:14:28.510744Z","shell.execute_reply":"2021-09-15T15:14:28.51737Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_predictions(model,val_df,train_path, input_size):\n    y_pred = []\n    for patientId in val_df['patientId']:\n        image,mask,pred_mask = predict_imagemask(model,patientId, val_df, train_path, input_size=input_size)\n        y_pred.append((1 in pred_mask) * 1)\n    y_true = val_df['Target']\n    return y_true, y_pred","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:30.526358Z","iopub.execute_input":"2021-09-15T15:14:30.526722Z","iopub.status.idle":"2021-09-15T15:14:30.532921Z","shell.execute_reply.started":"2021-09-15T15:14:30.526683Z","shell.execute_reply":"2021-09-15T15:14:30.532124Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_confusion_matrix(y_true, y_pred):\n    '''Function to print confusion_matrix'''\n\n    # Get confusion matrix array\n    array = confusion_matrix(y_true, y_pred)    \n    df_cm = pd.DataFrame(array, range(2), range(2))\n\n    print(\"Total samples = \", len(val_df))\n\n    # Plot heatmap and get sns heatmap values\n    sns.set(font_scale=1.4); # for label size\n    result = sns.heatmap(df_cm, annot=True, annot_kws={\"size\": 16}, fmt='g', cbar=False); \n\n    # Add labels to heatmap\n    labels = ['TN=','FP=','FN=','TP=']\n    i=0\n    for t in result.texts:\n        t.set_text(labels[i] + t.get_text())\n        i += 1\n    \n    plt.xlabel(\"Predicted Values\")\n    plt.ylabel('True Values')\n    plt.show()\n    return\n","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:32.528571Z","iopub.execute_input":"2021-09-15T15:14:32.528912Z","iopub.status.idle":"2021-09-15T15:14:32.538515Z","shell.execute_reply.started":"2021-09-15T15:14:32.528881Z","shell.execute_reply":"2021-09-15T15:14:32.537704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_image_mask(image, mask, pred_mask):\n    \n    # Plot three images side by side\n    fig, ax = plt.subplots(1, 3, figsize=(10,10))\n    ax[0].imshow(image, cmap=plt.cm.gist_gray) # Show the image\n    ax[0].axis('off') # Remove axis\n    ax[0].set_title(\"IMAGE\")\n\n    ax[1].imshow(mask*image, cmap=plt.cm.gist_gray)\n    ax[1].set_title(\"Expected Mask\")\n    ax[1].axis('off')\n        \n    # Visualize mask superimposed on image \n    masked = mask * image\n    ax[2].imshow(masked)\n    ax[2].imshow(pred_mask * image)\n    ax[2].set_title(\"Predicted Mask\")\n    ax[2].axis('off')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:34.156736Z","iopub.execute_input":"2021-09-15T15:14:34.157372Z","iopub.status.idle":"2021-09-15T15:14:34.165881Z","shell.execute_reply.started":"2021-09-15T15:14:34.157329Z","shell.execute_reply":"2021-09-15T15:14:34.165086Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1. Exploratory Data Analysis\n<li>Exploring the given Data files, classes and images of different classes.<br>\n<li>Dealing with missing valuesVisualizationof different classes<br> \n<li>Analysis from the visualizationof different classes.","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"labels_file = input_path + \"stage_2_train_labels.csv\"\nclass_file = input_path + \"stage_2_detailed_class_info.csv\"\n\nlabels_df = pd.read_csv(labels_file)\nclasses_df = pd.read_csv(class_file)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:38.220972Z","iopub.execute_input":"2021-09-15T15:14:38.22139Z","iopub.status.idle":"2021-09-15T15:14:38.3594Z","shell.execute_reply.started":"2021-09-15T15:14:38.221357Z","shell.execute_reply":"2021-09-15T15:14:38.358207Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1.3 Load and Examine MetaData","metadata":{}},{"cell_type":"code","source":"# Get a single dataframe by merging the labels_df and classes_df dataframes and adding relevant DICOM metadata columnspath = patient_df_path\ndf_path = patient_df_path # dataframe pickle object file to store or retrieve to/from disk\nimages_path = train_path # path where DICOM images are stored\npatient_df = get_single_df(labels_df,classes_df,patient_df_path, images_path)\npatient_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:14:42.578087Z","iopub.execute_input":"2021-09-15T15:14:42.578388Z","iopub.status.idle":"2021-09-15T15:26:42.767912Z","shell.execute_reply.started":"2021-09-15T15:14:42.578359Z","shell.execute_reply":"2021-09-15T15:26:42.767217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check shape of merged df\npatient_df.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check if size of images are uniform\nprint('X dimension of images:' , patient_df['dimx'].value_counts())\nprint('Y dimension of images:', patient_df['dimy'].value_counts())","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:31.598937Z","iopub.execute_input":"2021-09-15T14:12:31.599255Z","iopub.status.idle":"2021-09-15T14:12:31.620836Z","shell.execute_reply.started":"2021-09-15T14:12:31.59922Z","shell.execute_reply":"2021-09-15T14:12:31.619838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<font color = blue>As seen above, all 26684 images have the same dimension of 1024 * 1024","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"## 2. Data preprocessing","metadata":{}},{"cell_type":"markdown","source":"### 2.1 Split dataframe into train and validation dataframes","metadata":{}},{"cell_type":"code","source":"# To split the dataframe form an array of all the indices\nindices = range( len(patient_df))\ntarget = patient_df['Target'] # for stratification split\n\n# First split to get 0.2 of the dataset to use as total samples to input for model\nsample_indices, _, sample_target,_ = train_test_split(indices,target, test_size=0.8, random_state=42, stratify=target)\n\n# Split those indices further into train and val indices\ntrain_indices, val_indices, _,_ = train_test_split(sample_indices, sample_target, test_size=0.2, random_state=42, stratify=sample_target)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:38.586047Z","iopub.execute_input":"2021-09-15T14:12:38.586333Z","iopub.status.idle":"2021-09-15T14:12:38.654314Z","shell.execute_reply.started":"2021-09-15T14:12:38.586305Z","shell.execute_reply":"2021-09-15T14:12:38.653636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the train and validation dataframes from the split indices\npatient_data = patient_df[['patientId','bboxes','Target']]\ntrain_df = patient_data.loc[train_indices]\nval_df = patient_data.loc[val_indices]\n","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:39.65509Z","iopub.execute_input":"2021-09-15T14:12:39.655534Z","iopub.status.idle":"2021-09-15T14:12:39.676559Z","shell.execute_reply.started":"2021-09-15T14:12:39.655499Z","shell.execute_reply":"2021-09-15T14:12:39.675681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Total Samples: \",len(patient_df) )\ntotal_sample_size = len(train_df) + len(val_df) \nprint(\"Total samples selected for model training:\", total_sample_size)\nprint(\"Train samples:\", len(train_df))\nprint(\"Validation samples:\", len(val_df))\nprint(\"Ratio of train to validation samples: %0.1f : %0.1f\" %(len(train_df)/total_sample_size, len(val_df)/len(sample_indices) ))\nprint(\"Positive samples in original dataset: %0.0f%%\" %((len(patient_df[patient_df['Target'] == 1])/ len(patient_df))*100))\nprint(\"Positive samples in train dataset: %0.0f%%\" %((len(train_df[train_df['Target'] == 1]) /len(train_df)) * 100))\nprint(\"Positive samples in test dataset:%0.0f%%\" %((len(val_df[val_df['Target'] == 1])/len(val_df))*100))","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:41.803043Z","iopub.execute_input":"2021-09-15T14:12:41.803317Z","iopub.status.idle":"2021-09-15T14:12:41.823714Z","shell.execute_reply.started":"2021-09-15T14:12:41.803289Z","shell.execute_reply":"2021-09-15T14:12:41.822618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Build and Train Model","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"### 3.3 Basic UNET Model Training","metadata":{}},{"cell_type":"code","source":"BACKBONE = 'None'\nbasic_unet_i64_b64_e6_model = create_unet_model(BACKBONE, input_size=INPUT_SIZE_64)\n\nif (os.path.isfile(basic_unet_i64_b64_e6_path)):\n  print(\"LOADING WEIGHTS FROM PREVIOUS MODEL\\n\")\n  basic_unet_i64_b64_e6_model.load_weights(basic_unet_i64_b64_e6_path, by_name=True)\n\nbasic_unet_i64_b64_e6_model.summary()","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:48.038085Z","iopub.execute_input":"2021-09-15T14:12:48.038382Z","iopub.status.idle":"2021-09-15T14:12:50.444093Z","shell.execute_reply.started":"2021-09-15T14:12:48.038345Z","shell.execute_reply":"2021-09-15T14:12:50.443043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Number of layers in the model = \", len(basic_unet_i64_b64_e6_model.layers))","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:53.550328Z","iopub.execute_input":"2021-09-15T14:12:53.550634Z","iopub.status.idle":"2021-09-15T14:12:53.556686Z","shell.execute_reply.started":"2021-09-15T14:12:53.550606Z","shell.execute_reply":"2021-09-15T14:12:53.555794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimizer = Adam(learning_rate=1e-4, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)\nbasic_unet_i64_b64_e6_model.compile(loss= loss, optimizer=optimizer, metrics=[dice_coefficient])","metadata":{"execution":{"iopub.status.busy":"2021-09-15T14:12:55.325514Z","iopub.execute_input":"2021-09-15T14:12:55.325791Z","iopub.status.idle":"2021-09-15T14:12:55.348232Z","shell.execute_reply.started":"2021-09-15T14:12:55.325763Z","shell.execute_reply":"2021-09-15T14:12:55.347587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"traingen = CustomDataGen(train_df,batch_size=BATCH_SIZE_64, input_size=INPUT_SIZE_64)\nvalgen = CustomDataGen(val_df,batch_size=BATCH_SIZE_64, input_size=INPUT_SIZE_64)\n\n#Saving the best model using model checkpoint callback.  \noutfile = basic_unet_i64_b64_e6_path # update with new weights\nmodel_checkpoint=tf.keras.callbacks.ModelCheckpoint(outfile, \n                                                    save_best_only=True, \n                                                    monitor='val_dice_coefficient',\n                                                    mode='max', \n                                                    verbose=1)\n\n#es = tf.keras.callbacks.EarlyStopping(monitor='val_dice_coefficient', mode='max', verbose=1, patience=3)\nhistory = basic_unet_i64_b64_e6_model.fit(traingen,\n                epochs=EPOCH_SIZE,\n                validation_data=valgen,\n                callbacks=[model_checkpoint],\n                use_multiprocessing=True,\n                workers=4,\n                shuffle=True,\n                verbose=1)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T15:09:00.794064Z","iopub.execute_input":"2021-09-15T15:09:00.794562Z","iopub.status.idle":"2021-09-15T15:09:00.87799Z","shell.execute_reply.started":"2021-09-15T15:09:00.794353Z","shell.execute_reply":"2021-09-15T15:09:00.876796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.4 Basic UNET Model Evaluation\n\n\n","metadata":{}},{"cell_type":"markdown","source":"#### 3.4.1 Basic UNET Model : Image Segmentation Results","metadata":{}},{"cell_type":"markdown","source":"<font color=blue> <b>Basic UNET: Best Dice Coefficient : 0.240","metadata":{}},{"cell_type":"code","source":"history.history","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:10:58.905236Z","iopub.execute_input":"2021-09-15T10:10:58.913858Z","iopub.status.idle":"2021-09-15T10:10:58.922274Z","shell.execute_reply.started":"2021-09-15T10:10:58.913636Z","shell.execute_reply":"2021-09-15T10:10:58.921533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Basic UNET Model\\n\")\nPlotMetrics(history)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:18:59.343213Z","iopub.execute_input":"2021-09-15T10:18:59.34367Z","iopub.status.idle":"2021-09-15T10:18:59.657843Z","shell.execute_reply.started":"2021-09-15T10:18:59.343637Z","shell.execute_reply":"2021-09-15T10:18:59.656989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<font color=blue> val_dice-oefficient value is very low and pretty much a flat curve, indicating underfitting indicating model has not learnt sufficiently. It is steadily increasing, not sufficient training(more epochs needed).  \n\n<li>We have used only 20% of the samples for model training and validation, in order to save time during initial evaluation.  Needs more samples for training to resolve underfitting.\n\n<li>We have used an image size of 64x64 as against the original size of 1024x1024. Using a higher resolution, could also improve training capacity \n\n<li>Hyper-parameter tuning, image_augmentation, using different architectures will help in increasing model performance and generalization.","metadata":{}},{"cell_type":"code","source":"# Pickle dump the history dictionary\nfilename = basic_unet_i64_b64_e6_history\nfilehandle = open(filename, \"wb\")\npickle.dump(history.history, filehandle)\nfilehandle.close()","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:19:02.285082Z","iopub.execute_input":"2021-09-15T10:19:02.285751Z","iopub.status.idle":"2021-09-15T10:19:02.29308Z","shell.execute_reply.started":"2021-09-15T10:19:02.285705Z","shell.execute_reply":"2021-09-15T10:19:02.292122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 3.4.2 Basic UNET Model:  Image Clasification Results","metadata":{}},{"cell_type":"code","source":"#! cp '../input/kaggle-capstone-sep11/cust_unet_seg.h5' './cust_unet_seg.h5'\n#basic_unet_model = create_unet_model('None')\n\n# Load the best weights ---  basic_unet_i64_b64_e6_model\nbasic_unet_i64_b64_e6_model.load_weights(basic_unet_i64_b64_e6_path)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:19:05.016778Z","iopub.execute_input":"2021-09-15T10:19:05.01716Z","iopub.status.idle":"2021-09-15T10:19:05.08912Z","shell.execute_reply.started":"2021-09-15T10:19:05.017119Z","shell.execute_reply":"2021-09-15T10:19:05.088108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get predictions for Validation dataset and evaluate model performance\ny_true,y_pred = get_predictions(basic_unet_i64_b64_e6_model, val_df, train_path, input_size=INPUT_SIZE_64)\n\nprint(\"BASIC UNET MODEL - CONFUSION MATRIX\\n\")\nprint_confusion_matrix(y_true, y_pred)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:19:06.444377Z","iopub.execute_input":"2021-09-15T10:19:06.444727Z","iopub.status.idle":"2021-09-15T10:23:19.715228Z","shell.execute_reply.started":"2021-09-15T10:19:06.444691Z","shell.execute_reply":"2021-09-15T10:23:19.714147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"BASIC UNET MODEL : CLASSIFICATION REPORT\\n\")\nprint(classification_report(y_true, y_pred))","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:25:21.595341Z","iopub.execute_input":"2021-09-15T10:25:21.595785Z","iopub.status.idle":"2021-09-15T10:25:21.615577Z","shell.execute_reply.started":"2021-09-15T10:25:21.595746Z","shell.execute_reply":"2021-09-15T10:25:21.613291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<font color=blue> \n    <li>Recall socre is quite high for pneumonia class(target = 1), even for a very low dice-coefficient score for image mask prediction. This is a good score, because it indicates that 99% of patients who are positive are detected correctly by this model. \n    <li>Precision score is however low indicating only 24% of the predictions are correct. This is largely due to a high amount of false positives, as indicated in the confusion matrix.\n    <li>The accuracy score is very low less than 0.31, which is largely again due to the large number of False Positives. This probably can improve as the model trains with more number of samples, that will help it to distinguish non-pneumonia images better.","metadata":{}},{"cell_type":"code","source":"# Pickle dump the report dictionary\nreport = classification_report(y_true, y_pred, output_dict=True)\nfilename = basic_unet_i64_b64_e6_creport\nfilehandle = open(filename, \"wb\")\npickle.dump(report, filehandle)\nfilehandle.close()","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:25:25.096312Z","iopub.execute_input":"2021-09-15T10:25:25.096611Z","iopub.status.idle":"2021-09-15T10:25:25.108747Z","shell.execute_reply.started":"2021-09-15T10:25:25.096584Z","shell.execute_reply":"2021-09-15T10:25:25.107766Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load the best weights and save using model.save to make sure the model can be retrieved later\nbest_weights = basic_unet_i64_b64_e6_path\nbasic_unet_i64_b64_e6_model.load_weights(best_weights)\nbest_model_path  = basic_unet_i64_b64_e6_model_path\nbasic_unet_i64_b64_e6_model.save(best_model_path)","metadata":{"execution":{"iopub.status.busy":"2021-09-15T10:25:28.179838Z","iopub.execute_input":"2021-09-15T10:25:28.180139Z","iopub.status.idle":"2021-09-15T10:25:28.34879Z","shell.execute_reply.started":"2021-09-15T10:25:28.180112Z","shell.execute_reply":"2021-09-15T10:25:28.347748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}