{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":52279,"databundleVersionId":5822112,"sourceType":"competition"},{"sourceId":8380012,"sourceType":"datasetVersion","datasetId":4983219},{"sourceId":8554110,"sourceType":"datasetVersion","datasetId":5072025},{"sourceId":8564800,"sourceType":"datasetVersion","datasetId":5120279}],"dockerImageVersionId":30698,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"!pip install -U segmentation-models","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-05-30T18:22:04.075828Z","iopub.execute_input":"2024-05-30T18:22:04.076109Z","iopub.status.idle":"2024-05-30T18:22:18.094133Z","shell.execute_reply.started":"2024-05-30T18:22:04.076086Z","shell.execute_reply":"2024-05-30T18:22:18.093191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import cv2\nimport pickle\nimport json, math\nimport numpy as np\nimport random, time\nimport pandas as pd\nfrom PIL import Image\nimport seaborn as sns\nimport shutil, sys, os, gc\nfrom datetime import datetime\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\n\n\nimport tensorflow as tf\nimport albumentations as A\nfrom scipy.optimize import minimize\nfrom tensorflow.keras import layers, Model\nfrom tensorflow.keras.utils import Sequence\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.callbacks import ModelCheckpoint\n\n\nos.environ['SM_FRAMEWORK'] = 'tf.keras'\nimport segmentation_models as sm","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:11:07.066748Z","iopub.execute_input":"2024-05-30T19:11:07.067832Z","iopub.status.idle":"2024-05-30T19:11:07.075220Z","shell.execute_reply.started":"2024-05-30T19:11:07.067788Z","shell.execute_reply":"2024-05-30T19:11:07.074173Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configs","metadata":{}},{"cell_type":"code","source":"class CFG:\n    # PATHS\n    tile_fpath = \"/kaggle/input/hubmap-human-vasculature-dataset-512512/HuPMap/kidney_tiles.csv\"\n    masks_dir = \"/kaggle/input/hubmap-human-vasculature-dataset-512512/HuPMap/masks/\"\n    images_dir = \"/kaggle/input/hubmap-human-vasculature-dataset-512512/HuPMap/images/\"\n    \n    unetsm_weights = \"/kaggle/input/unet-weights/Unet_efficientnetb5_weights.keras\"\n    fcn_weights = \"/kaggle/input/hupmap-models/fcn/fcn_weights.weights.keras\"\n    unet_weights = \"/kaggle/input/hupmap-models/unet/unet_weights.weights.keras\"\n    linknetsm_weights = \"/kaggle/input/hupmap-models/linknet/linknet_efficientnetb5_weights.keras\"\n\n    # Seeding for reproducibility\n    seed = 42\n\n    # Image, Mask\n    _shape = 512\n    mask_channel = 1 # 1 for blood_vessels only, 2 for unsure as well, any other value returns 512, 512, 3 mask\n    image_size = (_shape, _shape)","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:23:07.106011Z","iopub.execute_input":"2024-05-30T18:23:07.106391Z","iopub.status.idle":"2024-05-30T18:23:07.111862Z","shell.execute_reply.started":"2024-05-30T18:23:07.106362Z","shell.execute_reply":"2024-05-30T18:23:07.110954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utilities","metadata":{}},{"cell_type":"code","source":"def read_mask(index, channel=CFG.mask_channel):\n    # loading mask\n    mask = np.load(f\"{CFG.masks_dir}{index}.npy\")\n    \n    # Select the specified channels\n    if channel == 1:\n        mask = mask[:, :, 0] # to consider only blood vessels\n    elif channel == 2:\n        selected_channels = mask[:, :, [0, 2]]\n        mask = np.sum(selected_channels, axis=2)\n    else:\n        pass\n    \n    # expanding dimension\n    if len(mask.shape) != 3:\n        mask = np.expand_dims(mask, axis=-1)\n        mask = np.where(mask > 0, 1, 0).astype(np.uint8)\n\n    return mask\n\n# to read data\ndef read_image_mask(df):\n    # Loading data\n    indexes = df['id'].to_numpy()\n    x = np.array([np.load(CFG.images_dir + index + \".npy\") for index in tqdm(indexes, desc=\"Loading images\")])\n    y = np.array([read_mask(index) for index in tqdm(indexes, desc=\"Loading masks\")])    \n    gc.collect()\n    \n    return x, y","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:23:18.787089Z","iopub.execute_input":"2024-05-30T18:23:18.787827Z","iopub.status.idle":"2024-05-30T18:23:18.795999Z","shell.execute_reply.started":"2024-05-30T18:23:18.787792Z","shell.execute_reply":"2024-05-30T18:23:18.794950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def calculate_confidence(y_pred, threshold):\n    binary_mask = y_pred > threshold\n    confidence_scores = y_pred.flatten()\n    binary_mask_flat = binary_mask.flatten()\n    blood_vessel_confidences = confidence_scores[binary_mask_flat]\n\n    return np.mean(blood_vessel_confidences)\n\ndef calculate_metrics(y_true, y_pred, threshold):\n    y_pred_binary = (y_pred > threshold).astype(np.uint8)\n\n    # True Positives, False Positives, False Negatives, True Negatives\n    TP = np.sum((y_true == 1) & (y_pred_binary == 1))\n    FP = np.sum((y_true == 0) & (y_pred_binary == 1))\n    TN = np.sum((y_true == 0) & (y_pred_binary == 0))\n    FN = np.sum((y_true == 1) & (y_pred_binary == 0))\n\n    # Dice coefficient\n    dice_denominator = 2 * TP + FP + FN\n    dice = (2 * TP) / dice_denominator if dice_denominator != 0 else 1\n\n    # Intersection over Union (IoU)\n    iou_denominator = TP + FP + FN\n    iou = TP / iou_denominator if iou_denominator != 0 else 1\n\n    # Precision\n    precision = TP / (TP + FP) if (TP + FP) != 0 else 1\n\n    # Recall\n    recall = TP / (TP + FN) if (TP + FN) != 0 else 1\n\n    # Confidence\n    confidence = calculate_confidence(y_pred, threshold)\n\n    return dice, iou, precision, recall, confidence\n\ndef metrics_dataframe(Y, Y_hat, threshold, show_progress=True):\n    n_val = len(Y)\n    df_object = {}\n    df_object['dice'] = []\n    df_object['iou'] = []\n    df_object['precision'] = []\n    df_object['recall'] = []\n    df_object['confidence'] = []\n    df_object['threshold'] = threshold\n\n    for i in tqdm(range(n_val), total=n_val, disable=not show_progress):\n        metrics = calculate_metrics(Y[i], Y_hat[i], threshold)\n        df_object['dice'].append(metrics[0])\n        df_object['iou'].append(metrics[1])\n        df_object['precision'].append(metrics[2])\n        df_object['recall'].append(metrics[3])\n        df_object['confidence'].append(metrics[4])\n        \n    return pd.DataFrame(df_object)\n\ndef get_best_thresh(Y, Y_hat, metrics_dataframe=metrics_dataframe):\n    thresh = 0\n    best_score = -1\n    for i in range(10, 100, 5):\n        # Colculate metrics\n        metrics = metrics_dataframe(Y, Y_hat, threshold=i/100, show_progress=False).mean()\n        \n        if metrics['dice'] > best_score:\n            best_score = metrics['dice']\n            thresh = i/100\n            \n    return thresh","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:55:51.556325Z","iopub.execute_input":"2024-05-30T18:55:51.556662Z","iopub.status.idle":"2024-05-30T18:55:51.571528Z","shell.execute_reply.started":"2024-05-30T18:55:51.556638Z","shell.execute_reply":"2024-05-30T18:55:51.570491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_result(X, Y_true, Y_pred, cutoff, model='model'):\n    \n    N = X.shape[0]\n    \n    fig, ax = plt.subplots(N,4, figsize = (10, 3*N))\n    \n    for k in range(N):\n        \n        cutoff_img1 = (Y_pred[k,:,:,0] > cutoff).astype(int)\n\n        true_img = np.zeros((512, 512, 3), dtype = np.uint8)\n        true_img[:,:,1] = Y_true[k,:,:,0]*200\n        \n        cutoff1 = np.zeros((512, 512, 3), dtype = np.uint8)\n        \n        cutoff1[:,:,0] = cutoff_img1*230\n        \n        cutoff1[:,:,1] = cutoff_img1*50\n        cutoff1[:,:,2] = cutoff_img1*50\n        \n        diff_photo1 = cutoff1.copy()\n        diff_photo1[:,:,1] += (Y_true[k,:,:,0]*200).astype(np.uint8)\n        \n        ax[k, 0].imshow(X[k])\n        ax[k, 1].imshow(true_img, cmap = \"gray\")\n        ax[k, 2].imshow(cutoff1, cmap = \"gray\")\n        ax[k, 3].imshow(diff_photo1)\n        \n        for j in range(4):\n            ax[k,j].set_xticks([])\n            ax[k,j].set_yticks([])\n    \n        if k == 0:\n            ax[k, 0].set_title(\"kidney tissue\")\n            ax[k, 1].set_title(\"true label\")\n            ax[k, 2].set_title(f\"{model} (cutoff at {cutoff})\")\n            ax[k, 3].set_title(\"Compare (Y:tp)\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:41:43.337482Z","iopub.execute_input":"2024-05-30T18:41:43.338159Z","iopub.status.idle":"2024-05-30T18:41:43.350535Z","shell.execute_reply.started":"2024-05-30T18:41:43.338123Z","shell.execute_reply":"2024-05-30T18:41:43.349589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load dataframe","metadata":{}},{"cell_type":"code","source":"# read csv file\ntile_df = pd.read_csv(CFG.tile_fpath)\n\n# ignore blank masks, and select some features\ntile_df = tile_df[tile_df['annotated'] == 1]\nif CFG.mask_channel == 1:\n    tile_df = tile_df[tile_df['blood_vessel'] > 0]\ntile_df = tile_df[['id', 'source_wsi', 'dataset', 'dataset_wsi', 'blood_vessel', 'glomerulus', 'unsure']]\n\nprint(tile_df.shape)\ntile_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:23:22.190621Z","iopub.execute_input":"2024-05-30T18:23:22.191323Z","iopub.status.idle":"2024-05-30T18:23:22.266114Z","shell.execute_reply.started":"2024-05-30T18:23:22.191293Z","shell.execute_reply":"2024-05-30T18:23:22.265110Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data preparation","metadata":{}},{"cell_type":"code","source":"# spliting data and perserving the same ratio of each class\nX_train, X_val = train_test_split(tile_df, test_size=0.2, random_state=CFG.seed, stratify=tile_df['dataset_wsi'])\nprint(X_train.shape, X_val.shape)","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:23:31.659890Z","iopub.execute_input":"2024-05-30T18:23:31.660638Z","iopub.status.idle":"2024-05-30T18:23:31.671737Z","shell.execute_reply.started":"2024-05-30T18:23:31.660603Z","shell.execute_reply":"2024-05-30T18:23:31.670959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# loading validation data\nX, Y = read_image_mask(X_val)","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:23:32.185709Z","iopub.execute_input":"2024-05-30T18:23:32.186369Z","iopub.status.idle":"2024-05-30T18:23:47.124833Z","shell.execute_reply.started":"2024-05-30T18:23:32.186337Z","shell.execute_reply":"2024-05-30T18:23:47.124069Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Models","metadata":{}},{"cell_type":"markdown","source":"## Unet Segmentation Model","metadata":{}},{"cell_type":"code","source":"# Defining unet model\nclass UnetSegmentationModel:\n    def __init__(self, backbone='efficientnetb5', weights_path=CFG.unetsm_weights):\n        self.BACKBONE = backbone\n        self.preprocess_input = sm.get_preprocessing(self.BACKBONE)\n        self.model = sm.Unet(self.BACKBONE, encoder_weights=None)\n\n        if weights_path and os.path.exists(weights_path):\n            self.model.load_weights(weights_path)\n\n    def preprocess_image(self, image):\n        preprocessed_image = self.preprocess_input(image)\n        return preprocessed_image\n\n    def predict(self, image, threshold=None):\n        # Preprocess the input image\n        preprocessed_image = self.preprocess_image(image)\n\n        # Ensure the input image has the right shape for prediction\n        if len(preprocessed_image.shape) == 3:\n            preprocessed_image = np.expand_dims(preprocessed_image, axis=0)  # Add batch dimension if needed\n\n        # Predict the probabilities for the input image\n        prediction = self.model.predict(preprocessed_image)\n\n        # Process predictions as needed\n        if threshold:\n            prediction = (prediction > threshold).astype(np.uint8)\n\n        return prediction","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:24:31.575808Z","iopub.execute_input":"2024-05-30T18:24:31.576779Z","iopub.status.idle":"2024-05-30T18:24:31.584275Z","shell.execute_reply.started":"2024-05-30T18:24:31.576738Z","shell.execute_reply":"2024-05-30T18:24:31.583451Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making a prediction\nunet_sm = UnetSegmentationModel()\nY_hat_unet_sm = unet_sm.predict(X)\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:24:33.570122Z","iopub.execute_input":"2024-05-30T18:24:33.570775Z","iopub.status.idle":"2024-05-30T18:26:52.352923Z","shell.execute_reply.started":"2024-05-30T18:24:33.570742Z","shell.execute_reply":"2024-05-30T18:26:52.352033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluating results\nmetrics_dataframe(Y, Y_hat_unet_sm, 0.5).mean()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:26:52.354623Z","iopub.execute_input":"2024-05-30T18:26:52.354951Z","iopub.status.idle":"2024-05-30T18:26:52.810338Z","shell.execute_reply.started":"2024-05-30T18:26:52.354898Z","shell.execute_reply":"2024-05-30T18:26:52.809464Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## LinknetSegmentationModel","metadata":{}},{"cell_type":"code","source":"class LinknetSegmentationModel:\n    def __init__(self, backbone='efficientnetb5', weights_path=CFG.linknetsm_weights):\n        self.BACKBONE = backbone\n        self.preprocess_input = sm.get_preprocessing(self.BACKBONE)\n        self.model = sm.Linknet(self.BACKBONE, encoder_weights=None)\n\n        if weights_path and os.path.exists(weights_path):\n            self.model.load_weights(weights_path)\n\n    def preprocess_image(self, image):\n        preprocessed_image = self.preprocess_input(image)\n        return preprocessed_image\n\n    def predict(self, image, threshold=None):\n        # Preprocess the input image\n        preprocessed_image = self.preprocess_image(image)\n\n        # Ensure the input image has the right shape for prediction\n        if len(preprocessed_image.shape) == 3:\n            preprocessed_image = np.expand_dims(preprocessed_image, axis=0)  # Add batch dimension if needed\n\n        # Predict the probabilities for the input image\n        prediction = self.model.predict(preprocessed_image)\n\n        # Process predictions as needed\n        if threshold:\n            prediction = (prediction > threshold).astype(np.uint8)\n\n        return prediction","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making a prediction\nlinknet_sm = LinknetSegmentationModel()\nY_hat_linknet_sm = linknet_sm.predict(X)\ngc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluating results\nmetrics_dataframe(Y, Y_hat_linknet_sm, 0.5).mean()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Unet","metadata":{}},{"cell_type":"code","source":"# Defining unet model\nclass UNet:\n    def __init__(self, input_shape=(512, 512, 3), initial_channels=16, kernel_size=3,\n                 num_classes=1, weights_path=CFG.unet_weights):\n        self.input_shape = input_shape\n        self.num_classes = num_classes\n        self.kernel_size = kernel_size\n        self.initial_channels = initial_channels\n        self.model = self.build_model()\n\n        if weights_path and os.path.exists(weights_path):\n            self.model.load_weights(weights_path)\n\n    def LeftBlock(self, channel, X, ksize=3, downsample=True):\n        if downsample:\n            X = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(X)\n\n        X = layers.Conv2D(channel, kernel_size=ksize, strides=1, padding=\"same\")(X)\n        X = layers.BatchNormalization()(X)\n        X = layers.ReLU()(X)\n\n        # Add more convolutional layers to increase model capacity\n        for _ in range(3):\n            X = layers.Conv2D(channel, kernel_size=ksize, strides=1, padding=\"same\")(X)\n            X = layers.BatchNormalization()(X)\n            X = layers.ReLU()(X)\n\n        return X\n\n    def RightBlock(self, channel, X, ksize=3, X_skip=None, upsample=True):\n        if upsample:\n            X = layers.Conv2DTranspose(channel, kernel_size=3, strides=2, padding=\"same\")(X)\n\n        if X_skip is not None:\n            X = layers.Concatenate()([X, X_skip])\n\n        X = layers.Conv2D(channel, kernel_size=ksize, strides=1, padding=\"same\")(X)\n        X = layers.BatchNormalization()(X)\n        X = layers.ReLU()(X)\n\n        # Add more convolutional layers to increase model capacity\n        for _ in range(3):\n            X = layers.Conv2D(channel, kernel_size=ksize, strides=1, padding=\"same\")(X)\n            X = layers.BatchNormalization()(X)\n            X = layers.ReLU()(X)\n\n        return X\n\n    def build_model(self):\n        Input = layers.Input(shape=self.input_shape)\n\n        X0 = layers.Rescaling(scale=1. / 127.5, offset=-1)(Input)\n\n        KS = self.kernel_size\n        channel = self.initial_channels\n\n        X1 = self.LeftBlock(channel, X0, ksize=KS, downsample=False)  # 512\n\n        channel *= 2\n        X2 = self.LeftBlock(channel, X1, ksize=KS, downsample=True)  # 256\n\n        channel *= 2\n        X3 = self.LeftBlock(channel, X2, ksize=KS, downsample=True)  # 128\n\n        channel *= 2\n        X4 = self.LeftBlock(channel, X3, ksize=KS, downsample=True)  # 64\n\n        XR = self.RightBlock(channel, X4, ksize=KS, X_skip=X3, upsample=True)  # 128\n\n        channel = int(channel / 2)\n        XR = self.RightBlock(channel, XR, ksize=KS, X_skip=X2, upsample=True)  # 256\n\n        channel = int(channel / 2)\n        XR = self.RightBlock(channel, XR, ksize=KS, X_skip=X1, upsample=True)  # 512\n\n        channel = self.num_classes\n        XR = layers.Conv2D(channel, kernel_size=1, strides=1, padding=\"same\")(XR)\n        model = Model(inputs=Input, outputs=XR)\n\n        return model\n\n    def predict(self, image, threshold=None):\n        # Ensure the input image has the right shape for prediction\n        if len(image.shape) == 3:\n            image = np.expand_dims(image, axis=0)  # Add batch dimension if needed\n\n        # Predict the probabilities for the input image\n        prediction = self.model.predict(image)\n\n        # Process predictions as needed\n        if threshold:\n            prediction = (prediction > threshold).astype(np.uint8)\n\n        return prediction","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:26:52.811408Z","iopub.execute_input":"2024-05-30T18:26:52.811659Z","iopub.status.idle":"2024-05-30T18:26:52.831698Z","shell.execute_reply.started":"2024-05-30T18:26:52.811637Z","shell.execute_reply":"2024-05-30T18:26:52.830732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making a prediction\nunet = UNet()\nY_hat_unet = unet.predict(X)\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:33:53.661366Z","iopub.execute_input":"2024-05-30T18:33:53.661725Z","iopub.status.idle":"2024-05-30T18:34:02.837450Z","shell.execute_reply.started":"2024-05-30T18:33:53.661695Z","shell.execute_reply":"2024-05-30T18:34:02.836540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluating results\nY_hat_unet = tf.keras.activations.sigmoid(Y_hat_unet).numpy()\nmetrics_dataframe(Y, Y_hat_unet, 0.5).mean()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:34:02.838891Z","iopub.execute_input":"2024-05-30T18:34:02.839210Z","iopub.status.idle":"2024-05-30T18:34:04.182594Z","shell.execute_reply.started":"2024-05-30T18:34:02.839185Z","shell.execute_reply":"2024-05-30T18:34:04.181649Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## FCN","metadata":{}},{"cell_type":"code","source":"# Defining FCN model\nclass FCN:\n    def __init__(self, input_shape=(512, 512, 3), num_classes=1, weights_path=CFG.fcn_weights):\n        self.input_shape = input_shape\n        self.num_classes = num_classes\n        self.model = self.build_model()\n\n        if weights_path and os.path.exists(weights_path):\n            self.model.load_weights(weights_path)\n\n    def conv_block(self, channel, X, ksize=3, downsample=True):\n        if downsample:\n            X = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(X)\n\n        X = layers.Conv2D(channel, kernel_size=ksize, strides=1, padding=\"same\")(X)\n        X = layers.BatchNormalization()(X)\n        X = layers.ReLU()(X)\n\n        return X\n\n    def upsample_block(self, channel, X, ksize, stride):\n        X = layers.Conv2DTranspose(channel, kernel_size=ksize, strides=stride, padding=\"same\")(X)\n        return X\n\n    def build_model(self):\n        Input = layers.Input(shape=self.input_shape)\n        X = layers.Rescaling(scale=1. / 127.5, offset=-1)(Input)\n\n        KS = 3\n        channel = 64\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)  # 512\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n\n        channel *= 2\n        X = self.conv_block(channel, X, ksize=KS, downsample=True)  # 256\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n\n        channel *= 2\n        X = self.conv_block(channel, X, ksize=KS, downsample=True)  # 128\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n\n        channel *= 2\n        X = self.conv_block(channel, X, ksize=KS, downsample=True)  # 64\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n        X = self.conv_block(channel, X, ksize=KS, downsample=False)\n\n        X = self.upsample_block(self.num_classes, X, ksize=8, stride=8)\n        model = Model(inputs=Input, outputs=X)\n\n        return model\n\n    def predict(self, image, threshold=None):\n        # Ensure the input image has the right shape for prediction\n        if len(image.shape) == 3:\n            image = np.expand_dims(image, axis=0)  # Add batch dimension if needed\n\n        # Predict the probabilities for the input image\n        prediction = self.model.predict(image)\n\n        # Process predictions as needed\n        if threshold:\n            prediction = (prediction > threshold).astype(np.uint8)\n\n        return prediction","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:27:49.446577Z","iopub.execute_input":"2024-05-30T18:27:49.446987Z","iopub.status.idle":"2024-05-30T18:27:49.462684Z","shell.execute_reply.started":"2024-05-30T18:27:49.446956Z","shell.execute_reply":"2024-05-30T18:27:49.461736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making a prediction\nfcn = FCN()\nY_hat_fcn = fcn.predict(X)\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:33:23.511708Z","iopub.execute_input":"2024-05-30T18:33:23.512090Z","iopub.status.idle":"2024-05-30T18:33:31.336050Z","shell.execute_reply.started":"2024-05-30T18:33:23.512061Z","shell.execute_reply":"2024-05-30T18:33:31.335175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluating results\nY_hat_fcn = tf.keras.activations.sigmoid(Y_hat_fcn).numpy()\nmetrics_dataframe(Y, Y_hat_fcn, 0.5).mean()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:33:31.337744Z","iopub.execute_input":"2024-05-30T18:33:31.338049Z","iopub.status.idle":"2024-05-30T18:33:32.694964Z","shell.execute_reply.started":"2024-05-30T18:33:31.338024Z","shell.execute_reply":"2024-05-30T18:33:32.694068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Ensemble\n## Ensembling predictions\n- Average Ensembling\n- Weighted Average Ensembling\n- Majority Voting Ensembling\n- Maximum Probability Ensembling","metadata":{}},{"cell_type":"code","source":"CFG.THRESHOLD = 0.5\nmodels = [\"Unet SM\", \"Linknet SM\", \"UNet\", \"FCN\"]\nY_hat_ensemble = [Y_hat_unet_sm, Y_hat_linknet_sm, Y_hat_unet, Y_hat_fcn]\nn_hat_ensemble = len(Y_hat_ensemble)","metadata":{"execution":{"iopub.status.busy":"2024-05-30T18:48:05.982284Z","iopub.execute_input":"2024-05-30T18:48:05.983059Z","iopub.status.idle":"2024-05-30T18:48:05.987740Z","shell.execute_reply.started":"2024-05-30T18:48:05.983024Z","shell.execute_reply":"2024-05-30T18:48:05.986729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# generate samples for ploting\nn_val = X_val.shape[0]\nnp.random.seed(CFG.seed + 20)\nval_sample = np.random.choice(n_val, 5)\n\n# ploting prediction of each model\nfor n in tqdm(range(n_hat_ensemble), total=n_hat_ensemble):\n    CFG.THRESHOLD = get_best_thresh(Y[val_sample], Y_hat_ensemble[n][val_sample])\n    plot_result(X[val_sample], Y[val_sample], Y_hat_ensemble[n][val_sample], cutoff=CFG.THRESHOLD, model=models[n])","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:25:55.107901Z","iopub.execute_input":"2024-05-30T19:25:55.108643Z","iopub.status.idle":"2024-05-30T19:26:01.509335Z","shell.execute_reply.started":"2024-05-30T19:25:55.108610Z","shell.execute_reply":"2024-05-30T19:26:01.508482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Average Ensembling","metadata":{}},{"cell_type":"code","source":"# Stack the predictions to create a new dimension\nY_hat_stack = np.stack(Y_hat_ensemble, axis=-1)\n\n# Compute the ensembled prediction\nensembled_pred = np.mean(Y_hat_stack, axis=-1)\n\n# Colculate metrics\nCFG.THRESHOLD = get_best_thresh(Y, ensembled_pred)\nmetrics = metrics_dataframe(Y, ensembled_pred, threshold=CFG.THRESHOLD).mean()\nprint(f\"[{CFG.THRESHOLD*100}%] Thresh - {metrics['dice'] * 100}% Dice - {metrics['iou'] * 100}% IOU\")\nprint(f\"{metrics['precision'] * 100}% Precision - {metrics['recall'] * 100}% Recall - {metrics['confidence'] * 100}% Confidence\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:27:12.197151Z","iopub.execute_input":"2024-05-30T19:27:12.197539Z","iopub.status.idle":"2024-05-30T19:27:22.536612Z","shell.execute_reply.started":"2024-05-30T19:27:12.197510Z","shell.execute_reply":"2024-05-30T19:27:22.535710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_result(X[val_sample], Y[val_sample], ensembled_pred[val_sample], cutoff=CFG.THRESHOLD, model=\"AE\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:27:55.670413Z","iopub.execute_input":"2024-05-30T19:27:55.671235Z","iopub.status.idle":"2024-05-30T19:27:57.463348Z","shell.execute_reply.started":"2024-05-30T19:27:55.671207Z","shell.execute_reply":"2024-05-30T19:27:57.462462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del Y_hat_stack\ndel ensembled_pred\ndel metrics\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:27:57.465233Z","iopub.execute_input":"2024-05-30T19:27:57.465643Z","iopub.status.idle":"2024-05-30T19:27:58.026971Z","shell.execute_reply.started":"2024-05-30T19:27:57.465613Z","shell.execute_reply":"2024-05-30T19:27:58.025971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Weighted Average Ensembling","metadata":{}},{"cell_type":"code","source":"# Objective function to minimize the negative Dice score\ndef objective(weights, y_preds, y_true):\n    weights = np.array(weights)\n    weights = weights / np.sum(weights)  # Normalize weights\n    weights = weights.reshape(1, 1, 1, 1, -1)\n    ensembled_pred_wavg = np.sum(y_preds * weights, axis=-1)\n    dice = metrics_dataframe(y_true, ensembled_pred_wavg, threshold=0.5, show_progress=False).mean()['dice']\n    return -dice  # We minimize the negative Dice score to maximize Dice\n\n# Initial weights\ninitial_weights = [1/n_hat_ensemble] * n_hat_ensemble\n\n# Stack the predictions to create a new dimension\nY_hat_stack = np.stack(Y_hat_ensemble, axis=-1)\n\n# Optimization\nresult = minimize(objective, initial_weights, args=(Y_hat_stack, Y), method='SLSQP', bounds=[(0,1)]*n_hat_ensemble)\n\n# Normalized optimal weights\noptimal_weights = result.x / np.sum(result.x)\nprint(\"Optimal Weights:\", optimal_weights)\n\n# Apply optimal weights to compute the final ensemble prediction\nweights = optimal_weights.reshape(1, 1, 1, 1, -1)\nensembled_pred = np.sum(Y_hat_stack * weights, axis=-1)\n\n# Colculate metrics\nCFG.THRESHOLD = get_best_thresh(Y, ensembled_pred)\nmetrics = metrics_dataframe(Y, ensembled_pred, threshold=CFG.THRESHOLD).mean()\nprint(f\"[{CFG.THRESHOLD*100}%] Thresh - {metrics['dice'] * 100}% Dice - {metrics['iou'] * 100}% IOU\")\nprint(f\"{metrics['precision'] * 100}% Precision - {metrics['recall'] * 100}% Recall - {metrics['confidence'] * 100}% Confidence\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:13.454532Z","iopub.execute_input":"2024-05-30T19:28:13.454813Z","iopub.status.idle":"2024-05-30T19:28:43.710514Z","shell.execute_reply.started":"2024-05-30T19:28:13.454790Z","shell.execute_reply":"2024-05-30T19:28:43.709610Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_result(X[val_sample], Y[val_sample], ensembled_pred[val_sample], cutoff=CFG.THRESHOLD, model=\"WAEnsembling\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:43.713191Z","iopub.execute_input":"2024-05-30T19:28:43.713470Z","iopub.status.idle":"2024-05-30T19:28:45.522661Z","shell.execute_reply.started":"2024-05-30T19:28:43.713445Z","shell.execute_reply":"2024-05-30T19:28:45.521781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del metrics\ndel ensembled_pred\ndel weights\ndel optimal_weights\ndel result\ndel Y_hat_stack\ndel initial_weights\ndel objective\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:45.523987Z","iopub.execute_input":"2024-05-30T19:28:45.524298Z","iopub.status.idle":"2024-05-30T19:28:46.074872Z","shell.execute_reply.started":"2024-05-30T19:28:45.524271Z","shell.execute_reply":"2024-05-30T19:28:46.073957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Majority Voting Ensembling","metadata":{}},{"cell_type":"code","source":"# Convert probabilities to binary masks\nCFG.THRESHOLD = 0.2\nbinary_masks = [(pred > CFG.THRESHOLD).astype(np.uint8) for pred in Y_hat_ensemble]\n\n# Stack the binary masks to create a new dimension\nbinary_masks_stack = np.stack(binary_masks, axis=-1)  # Shape will be (327, 512, 512, 1, 6)\n\n# Compute majority vote\nensembled_pred = np.sum(binary_masks_stack, axis=-1) > (len(Y_hat_ensemble) / 2)\n\n# Colculate metrics\nCFG.THRESHOLD = 0.01\nmetrics = metrics_dataframe(Y, ensembled_pred, threshold=CFG.THRESHOLD).mean()\nprint(f\"[{CFG.THRESHOLD*100}%] Thresh - {metrics['dice'] * 100}% Dice - {metrics['iou'] * 100}% IOU\")\nprint(f\"{metrics['precision'] * 100}% Precision - {metrics['recall'] * 100}% Recall - {metrics['confidence'] * 100}% Confidence\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:46.076198Z","iopub.execute_input":"2024-05-30T19:28:46.076619Z","iopub.status.idle":"2024-05-30T19:28:49.095566Z","shell.execute_reply.started":"2024-05-30T19:28:46.076584Z","shell.execute_reply":"2024-05-30T19:28:49.094655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_result(X[val_sample], Y[val_sample], ensembled_pred[val_sample],cutoff=CFG.THRESHOLD, model=\"MVEnsembling\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:49.096704Z","iopub.execute_input":"2024-05-30T19:28:49.097003Z","iopub.status.idle":"2024-05-30T19:28:50.921900Z","shell.execute_reply.started":"2024-05-30T19:28:49.096977Z","shell.execute_reply":"2024-05-30T19:28:50.920820Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del metrics\ndel ensembled_pred\ndel binary_masks_stack\ndel binary_masks\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:50.923263Z","iopub.execute_input":"2024-05-30T19:28:50.923550Z","iopub.status.idle":"2024-05-30T19:28:51.489682Z","shell.execute_reply.started":"2024-05-30T19:28:50.923524Z","shell.execute_reply":"2024-05-30T19:28:51.488668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Maximum Probability Ensembling","metadata":{}},{"cell_type":"code","source":"# Stack the predictions to create a new dimension\nY_hat_stack = np.stack(Y_hat_ensemble, axis=-1)  # Shape will be (327, 512, 512, 1, 6)\n\n# Compute maximum probability\nensembled_pred = np.max(Y_hat_stack, axis=-1)\n\n# Colculate metrics\nCFG.THRESHOLD = get_best_thresh(Y, ensembled_pred)\nmetrics = metrics_dataframe(Y, ensembled_pred, threshold=CFG.THRESHOLD).mean()\nprint(f\"[{CFG.THRESHOLD*100}%] Thresh - {metrics['dice'] * 100}% Dice - {metrics['iou'] * 100}% IOU\")\nprint(f\"{metrics['precision'] * 100}% Precision - {metrics['recall'] * 100}% Recall - {metrics['confidence'] * 100}% Confidence\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:28:51.491127Z","iopub.execute_input":"2024-05-30T19:28:51.491900Z","iopub.status.idle":"2024-05-30T19:29:03.896969Z","shell.execute_reply.started":"2024-05-30T19:28:51.491866Z","shell.execute_reply":"2024-05-30T19:29:03.895949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_result(X[val_sample], Y[val_sample], ensembled_pred[val_sample],cutoff=CFG.THRESHOLD, model=\"MVEnsembling\")","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:29:03.898239Z","iopub.execute_input":"2024-05-30T19:29:03.898548Z","iopub.status.idle":"2024-05-30T19:29:05.842174Z","shell.execute_reply.started":"2024-05-30T19:29:03.898521Z","shell.execute_reply":"2024-05-30T19:29:05.841185Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del metrics\ndel ensembled_pred\ndel Y_hat_stack\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-05-30T19:29:05.844650Z","iopub.execute_input":"2024-05-30T19:29:05.844966Z","iopub.status.idle":"2024-05-30T19:29:06.406845Z","shell.execute_reply.started":"2024-05-30T19:29:05.844940Z","shell.execute_reply":"2024-05-30T19:29:06.405899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}