{"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 packages\nimport os\nimport pickle\nfrom tqdm.notebook import tqdm\nimport random\nfrom tabulate import tabulate\n\nimport cv2\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport matplotlib.pyplot as plt\nimport torchvision.transforms.v2 as t\n\nfrom sklearn.model_selection import KFold, StratifiedKFold\nfrom sklearn.metrics import accuracy_score, roc_auc_score\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom torch.optim import Adam\nfrom torchvision import models\nfrom torchvision.transforms.v2 import Resize, Compose, RandomHorizontalFlip, ColorJitter, RandomAffine, RandomErasing, ToTensor","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-08-28T01:56:31.689593Z","iopub.execute_input":"2023-08-28T01:56:31.69007Z","iopub.status.idle":"2023-08-28T01:56:36.566798Z","shell.execute_reply.started":"2023-08-28T01:56:31.69003Z","shell.execute_reply":"2023-08-28T01:56:36.565838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_IMG_PATH = '/kaggle/input/rsna-2023-atd-reduced-256-5mm/reduced_256_tickness_5'\nTEST_IMG_PATH = '/kaggle/input/rsna-2023-abdominal-trauma-detection/test_images'\nTRAIN_DF_PATH = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv'","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:56:48.300569Z","iopub.execute_input":"2023-08-28T01:56:48.301164Z","iopub.status.idle":"2023-08-28T01:56:48.306456Z","shell.execute_reply.started":"2023-08-28T01:56:48.301127Z","shell.execute_reply":"2023-08-28T01:56:48.305114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def fetch_img_paths(train_img_path):\n    img_paths = []\n    \n    print('Scanning directories...')\n    for patient in tqdm(os.listdir(train_img_path)):\n        for scan in os.listdir(os.path.join(TRAIN_IMG_PATH, patient)):\n            scans = []\n            for img in os.listdir(os.path.join(TRAIN_IMG_PATH, patient, scan)):\n                scans.append(os.path.join(TRAIN_IMG_PATH, patient, scan, img))\n            \n            img_paths.append(scans)\n            \n    return img_paths","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:56:49.02141Z","iopub.execute_input":"2023-08-28T01:56:49.021756Z","iopub.status.idle":"2023-08-28T01:56:49.02871Z","shell.execute_reply.started":"2023-08-28T01:56:49.021726Z","shell.execute_reply":"2023-08-28T01:56:49.027413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def select_elements_with_spacing(input_list, spacing):\n    \n    \"\"\"\n    Selects elements with a specified spacing from a given list.\n\n    Args:\n        input_list (list): The input list from which elements will be selected.\n        spacing (int): The spacing between selected elements.\n\n    Returns:\n        list: A list of selected elements from the input list.\n\n    Raises:\n        ValueError: If the input list does not contain at least 4 * spacing elements.\n    \"\"\"\n \n    if len(input_list) < spacing * 4:\n        raise ValueError(\"List should contain at least 4 * spacing elements.\")\n        \n        \n    # We want to select elements in the middle part of the abdomen\n    lower_bound = int(len(input_list) * 0.4)\n    upper_bound = int(len(input_list) * 0.6)\n\n    spacing = (upper_bound - lower_bound) // 3\n    \n    # start_index = random.randint(lower_bound, upper_bound)\n    \n    selected_indices = [lower_bound, lower_bound + spacing, lower_bound + (2*spacing), upper_bound]\n    \n    selected_elements = [input_list[index] for index in selected_indices]\n    \n    return selected_elements","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:57:51.185909Z","iopub.execute_input":"2023-08-28T01:57:51.186379Z","iopub.status.idle":"2023-08-28T01:57:51.193838Z","shell.execute_reply.started":"2023-08-28T01:57:51.186338Z","shell.execute_reply":"2023-08-28T01:57:51.192877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def standardize_pixel_array(dicom_image):\n    \"\"\"\n    Standardizes a DICOM pixel array by applying various transformations.\n    \n    Args:\n        dicom_path (str): Path to the DICOM image file.\n        \n    Returns:\n        np.ndarray: The standardized pixel array of the DICOM image.\n    \"\"\"\n    pixel_array = dicom_image.pixel_array\n    \n    if dicom_image.PixelRepresentation == 1:\n        bit_shift = dicom_image.BitsAllocated - dicom_image.BitsStored\n        dtype = pixel_array.dtype \n        new_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n        pixel_array = pydicom.pixel_data_handlers.util.apply_modality_lut(new_array, dicom_image)\n\n    if dicom_image.PhotometricInterpretation == \"MONOCHROME1\":\n        pixel_array = 1 - pixel_array\n\n    # transform to hounsfield units\n    intercept = dicom_image.RescaleIntercept\n    slope = dicom_image.RescaleSlope\n    pixel_array = pixel_array * slope + intercept\n\n    # windowing\n    window_center = int(dicom_image.WindowCenter)\n    window_width = int(dicom_image.WindowWidth)\n    img_min = window_center - window_width // 2\n    img_max = window_center + window_width // 2\n    pixel_array = pixel_array.copy()\n    pixel_array[pixel_array < img_min] = img_min\n    pixel_array[pixel_array > img_max] = img_max\n\n    # normalization\n    if pixel_array.max() == pixel_array.min():\n        pixel_array = np.zeros_like(pixel_array)  # Handle case of constant array\n    else:\n        pixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min())\n\n    return pixel_array","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:57:53.624816Z","iopub.execute_input":"2023-08-28T01:57:53.625513Z","iopub.status.idle":"2023-08-28T01:57:53.6365Z","shell.execute_reply.started":"2023-08-28T01:57:53.625477Z","shell.execute_reply":"2023-08-28T01:57:53.635343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess_jpeg(jpeg_path):\n    \n    img = cv2.imread(jpeg_path)\n    greyscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)/255\n    \n    return greyscale","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:57:55.247491Z","iopub.execute_input":"2023-08-28T01:57:55.248158Z","iopub.status.idle":"2023-08-28T01:57:55.253198Z","shell.execute_reply.started":"2023-08-28T01:57:55.248122Z","shell.execute_reply":"2023-08-28T01:57:55.252201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dataset\nclass AbdominalData(Dataset):\n    \"\"\"\n    Custom dataset class for handling abdominal trauma data classification.\n    \n    Args:\n        df_path (str): Path to the CSV file containing patient labels.\n        current_fold (int): The current fold for cross-validation.\n        num_fold (int, optional): Total number of folds for cross-validation. Default is 5.\n    \"\"\"\n    \n    def __init__(self, df_path, current_fold, num_fold = 5):\n        \n        super().__init__()\n        \n        # collect all the image instance paths\n        self.img_paths = fetch_img_paths(TRAIN_IMG_PATH)\n                \n        self.df = pd.read_csv(df_path)\n        \n        self.num_fold = num_fold\n        self.current_fold = current_fold\n        self.kf = KFold(n_splits=num_fold)\n        \n        self.transform = Compose([\n#                             Resize((256, 256), antialias=True),\n                            RandomHorizontalFlip(),  # Randomly flip images left-right\n                            ColorJitter(brightness=0.2),  # Randomly adjust brightness\n                            ColorJitter(contrast=0.2),  # Randomly adjust contrast\n                            RandomAffine(degrees=0, shear=10),  # Apply shear transformation\n                            RandomAffine(degrees=0, scale=(0.8, 1.2)),  # Apply zoom transformation\n                            RandomErasing(p=0.2, scale=(0.02, 0.2)), # Coarse dropout\n                            ToTensor(),\n                        ])\n    \n    def __len__(self):\n        \"\"\"\n        Returns the total number of samples in the dataset.\n        \"\"\"\n        \n        return len(self.img_paths)\n    \n    def __getitem__(self, idx):\n        \"\"\"\n        Retrieves a sample from the dataset by index.\n        \n        Args:\n            idx (int): Index of the dataset to retrieve.\n        \n        Returns:\n            dict: A dictionary containing the image data and labels for different abdominal structures.\n        \"\"\"\n        \n        # sample 4 image instances\n        dicom_images = select_elements_with_spacing(self.img_paths[idx],\n                                                    spacing = 2)\n        patient_id = dicom_images[0].split('/')[-3]\n        images = []\n        \n        for d in dicom_images:\n            image = preprocess_jpeg(d)\n            images.append(image)\n            \n        images = np.stack(images)\n        image = torch.tensor(images, dtype = torch.float).unsqueeze(dim = 1)\n        \n        image = self.transform(image).squeeze(dim = 1)\n        \n        label = self.df[self.df.patient_id == int(patient_id)].values[0][1:-1]\n        \n        # labels\n        bowel = np.argmax(label[0:2], keepdims = True)\n        extravasation = np.argmax(label[2:4], keepdims = True)\n        kidney = np.argmax(label[4:7], keepdims = False)\n        liver = np.argmax(label[7:10], keepdims = False)\n        spleen = np.argmax(label[10:], keepdims = False)\n        \n        \n        return {\n            'image': image,\n            'bowel': bowel,\n            'extravasation': extravasation,\n            'kidney': kidney,\n            'liver': liver,\n            'spleen': spleen,\n        }\n    \n    def get_splits(self):\n        \"\"\"\n        Splits the dataset into training and validation subsets based on the current fold.\n        \n        Returns:\n            tuple: A tuple containing the training and validation subsets.\n        \"\"\"\n        \n        fold_data = list(self.kf.split(self.img_paths))\n        train_indices, val_indices = fold_data[self.current_fold]\n\n        train_data = self._get_subset(train_indices)\n        val_data = self._get_subset(val_indices)\n        \n        return train_data, val_data\n\n    def _get_subset(self, indices):\n        \"\"\"\n        Returns a subset of the dataset based on the provided indices.\n        \n        Args:\n            indices (list): List of indices to include in the subset.\n        \n        Returns:\n            Subset: A subset of the dataset.\n        \"\"\"\n        return Subset(self, indices)","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:58:13.323417Z","iopub.execute_input":"2023-08-28T01:58:13.32379Z","iopub.status.idle":"2023-08-28T01:58:13.342025Z","shell.execute_reply.started":"2023-08-28T01:58:13.323738Z","shell.execute_reply":"2023-08-28T01:58:13.340981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MetricsCalculator:\n    \n    def __init__(self, mode = 'binary'):\n        \n        self.probabilities = []\n        self.predictions = []\n        self.targets = []\n        \n        self.mode = mode\n    \n    def update(self, logits, target):\n        \"\"\"\n        Update the metrics calculator with predicted values and corresponding targets.\n        \n        Args:\n            predicted (torch.Tensor): Predicted values.\n            target (torch.Tensor): Ground truth targets.\n        \"\"\"\n        if self.mode == 'binary':\n            probabilities = torch.sigmoid(logits)\n            predicted = (probabilities > 0.5)\n        else:\n            probabilities = F.softmax(logits, dim = 1)\n            predicted = torch.argmax(probabilities, dim=1)\n            \n        self.probabilities.extend(probabilities.detach().cpu().numpy())\n        self.predictions.extend(predicted.detach().cpu().numpy())\n        self.targets.extend(target.detach().cpu().numpy())\n    \n    def reset(self):\n        \"\"\"Reset the stored predictions and targets.\"\"\"\n        \n        self.probabilities = []\n        self.predictions = []\n        self.targets = []\n    \n    def compute_accuracy(self):\n        \"\"\"\n        Compute the accuracy metric.\n        \n        Returns:\n            float: Accuracy.\n        \"\"\"\n        return accuracy_score(self.targets, self.predictions)\n    \n    def compute_auc(self):\n        \"\"\"\n        Compute the AUC (Area Under the Curve) metric.\n        \n        Returns:\n            float: AUC.\n        \"\"\"\n        if self.mode == 'multi':\n            return roc_auc_score(self.targets, self.probabilities, multi_class = 'ovo', labels=[0, 1, 2])\n    \n        else:\n            return roc_auc_score(self.targets, self.probabilities)","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:58:21.457755Z","iopub.execute_input":"2023-08-28T01:58:21.458151Z","iopub.status.idle":"2023-08-28T01:58:21.469325Z","shell.execute_reply.started":"2023-08-28T01:58:21.458121Z","shell.execute_reply":"2023-08-28T01:58:21.468386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Model Architecure\nclass CNNModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        \n        self.input = nn.Conv2d(4, 3, kernel_size = 3)\n        model = models.efficientnet_b0(weights = 'IMAGENET1K_V1')\n        \n        self.features = model.features\n        self.avgpool = model.avgpool\n        \n        self.bowel = nn.Linear(1280, 1)\n        self.extravasation = nn.Linear(1280, 1)\n        self.kidney = nn.Linear(1280, 3)\n        self.liver = nn.Linear(1280,3) \n        self.spleen = nn.Linear(1280, 3)\n    \n    def forward(self, x):\n        \n        # extract features\n        x = self.input(x)\n        x = self.features(x)\n        x = self.avgpool(x)\n        x = torch.flatten(x, 1)\n        \n        # output logits\n        bowel = self.bowel(x)\n        extravsation = self.extravasation(x)\n        kidney = self.kidney(x)\n        liver = self.liver(x)\n        spleen = self.spleen(x)\n        \n        return bowel, extravsation, kidney, liver, spleen","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:58:27.815073Z","iopub.execute_input":"2023-08-28T01:58:27.815435Z","iopub.status.idle":"2023-08-28T01:58:27.827337Z","shell.execute_reply.started":"2023-08-28T01:58:27.815403Z","shell.execute_reply":"2023-08-28T01:58:27.826177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CNNModel().to('cuda')","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:58:29.447571Z","iopub.execute_input":"2023-08-28T01:58:29.44794Z","iopub.status.idle":"2023-08-28T01:58:33.125047Z","shell.execute_reply.started":"2023-08-28T01:58:29.447909Z","shell.execute_reply":"2023-08-28T01:58:33.124039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 16\nNUM_EPOCHS = 10\nLR = 1e-4","metadata":{"execution":{"iopub.status.busy":"2023-08-28T02:22:38.450531Z","iopub.execute_input":"2023-08-28T02:22:38.451011Z","iopub.status.idle":"2023-08-28T02:22:38.456404Z","shell.execute_reply.started":"2023-08-28T02:22:38.450972Z","shell.execute_reply":"2023-08-28T02:22:38.455444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data_0, val_data_0 = AbdominalData('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv', current_fold=0).get_splits()\ntrain_data_1, val_data_1 = AbdominalData('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv', current_fold=1).get_splits()\ntrain_data_2, val_data_2 = AbdominalData('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv', current_fold=2).get_splits()\ntrain_data_3, val_data_3 = AbdominalData('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv', current_fold=3).get_splits()\ntrain_data_4, val_data_4 = AbdominalData('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv', current_fold=4).get_splits()\n\ntrain_dataloader_0 = DataLoader(train_data_0,batch_size = BATCH_SIZE, shuffle = True)\nval_dataloader_0 = DataLoader(val_data_0,batch_size = BATCH_SIZE, shuffle = False)\ntrain_dataloader_1 = DataLoader(train_data_1,batch_size = BATCH_SIZE, shuffle = True)\nval_dataloader_1 = DataLoader(val_data_1,batch_size = BATCH_SIZE, shuffle = False)\ntrain_dataloader_2 = DataLoader(train_data_2,batch_size = BATCH_SIZE, shuffle = True)\nval_dataloader_2 = DataLoader(val_data_2,batch_size = BATCH_SIZE, shuffle = False)\ntrain_dataloader_3 = DataLoader(train_data_3,batch_size = BATCH_SIZE, shuffle = True)\nval_dataloader_3 = DataLoader(val_data_3,batch_size = BATCH_SIZE, shuffle = False)\ntrain_dataloader_4 = DataLoader(train_data_4,batch_size = BATCH_SIZE, shuffle = True)\nval_dataloader_4 = DataLoader(val_data_4,batch_size = BATCH_SIZE, shuffle = False)","metadata":{"execution":{"iopub.status.busy":"2023-08-28T01:58:44.679357Z","iopub.execute_input":"2023-08-28T01:58:44.679737Z","iopub.status.idle":"2023-08-28T02:01:21.500172Z","shell.execute_reply.started":"2023-08-28T01:58:44.679704Z","shell.execute_reply":"2023-08-28T02:01:21.49915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimizer = torch.optim.AdamW(model.parameters(), lr = LR)\nbce_b = nn.BCEWithLogitsLoss(pos_weight = torch.tensor([2.0]).to('cuda'))\nbce_e = nn.BCEWithLogitsLoss(pos_weight = torch.tensor([4.0]).to('cuda'))\ncce = nn.CrossEntropyLoss(label_smoothing = 0.05, weight = torch.tensor([1.0, 2.0, 4.0]).to('cuda'))\nscheduler = ReduceLROnPlateau(optimizer, mode='min', patience=5, factor=0.5, verbose=True)","metadata":{"execution":{"iopub.status.busy":"2023-08-28T02:01:21.502033Z","iopub.execute_input":"2023-08-28T02:01:21.502908Z","iopub.status.idle":"2023-08-28T02:01:21.513025Z","shell.execute_reply.started":"2023-08-28T02:01:21.502868Z","shell.execute_reply":"2023-08-28T02:01:21.512013Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# initialize metrics objects\ntrain_acc_bowel = MetricsCalculator('binary')\ntrain_acc_extravasation = MetricsCalculator('binary')\ntrain_acc_liver = MetricsCalculator('multi')\ntrain_acc_kidney = MetricsCalculator('multi')\ntrain_acc_spleen = MetricsCalculator('multi')\n\nval_acc_bowel = MetricsCalculator('binary')\nval_acc_extravasation = MetricsCalculator('binary')\nval_acc_liver = MetricsCalculator('multi')\nval_acc_kidney = MetricsCalculator('multi')\nval_acc_spleen = MetricsCalculator('multi')","metadata":{"execution":{"iopub.status.busy":"2023-08-28T02:13:37.655276Z","iopub.execute_input":"2023-08-28T02:13:37.655655Z","iopub.status.idle":"2023-08-28T02:13:37.663793Z","shell.execute_reply.started":"2023-08-28T02:13:37.655623Z","shell.execute_reply":"2023-08-28T02:13:37.662831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prev_val_best_loss = float('inf')\n\ndataloaders = [(train_dataloader_0, val_dataloader_0),\n               (train_dataloader_1, val_dataloader_1),\n               (train_dataloader_2, val_dataloader_2), \n               (train_dataloader_3, val_dataloader_3),\n               (train_dataloader_4, val_dataloader_4)]\n\nfor epoch in range(NUM_EPOCHS):\n    \n    # training\n    model.train()\n    \n    train_loss = 0.0\n    val_loss = 0.0\n    \n    print(f'Epoch: [{epoch+1}/{NUM_EPOCHS}]')\n    \n    train_dataloader, val_dataloader = dataloaders[epoch%5]\n    \n    print(f'Fold: {epoch%5}')\n    \n    for batch_idx, batch_data in enumerate(tqdm(train_dataloader)):\n        \n        inputs = batch_data['image'].to('cuda')\n        bowel = batch_data['bowel'].to('cuda')\n        extravasation = batch_data['extravasation'].to('cuda')\n        liver = batch_data['liver'].to('cuda')\n        kidney = batch_data['kidney'].to('cuda')\n        spleen = batch_data['spleen'].to('cuda')\n        \n        optimizer.zero_grad()\n        b, e, k, l, s = model(inputs)\n        b_loss = bce_b(b, bowel.float())\n        e_loss = bce_e(e, extravasation.float())\n        l_loss = cce(l, liver)\n        k_loss = cce(k, kidney)\n        s_loss = cce(s, spleen)\n        \n        total_loss = b_loss + e_loss + l_loss + k_loss + s_loss\n        total_loss.backward()\n        \n        optimizer.step()\n        \n        # calculate training metrics\n        train_loss += total_loss.item()\n        train_acc_bowel.update(b, bowel)\n        train_acc_extravasation.update(e, extravasation)\n        train_acc_liver.update(l, liver)\n        train_acc_kidney.update(k, kidney)\n        train_acc_spleen.update(s, spleen)\n    \n    train_loss = train_loss/(batch_idx+1)\n    \n    # validation\n    model.eval()\n    running_loss = 0.0\n    \n    for batch_idx, batch_data in enumerate(tqdm(val_dataloader)):\n                                                \n        inputs = batch_data['image'].to('cuda')\n        bowel = batch_data['bowel'].to('cuda')\n        extravasation = batch_data['extravasation'].to('cuda')\n        liver = batch_data['liver'].to('cuda')\n        kidney = batch_data['kidney'].to('cuda')\n        spleen = batch_data['spleen'].to('cuda')\n\n        \n        b, e, k, l, s = model(inputs)\n        b_loss = bce_b(b, bowel.float())\n        e_loss = bce_e(e, extravasation.float())\n        l_loss = cce(l, liver)\n        k_loss = cce(k, kidney)\n        s_loss = cce(s, spleen)\n        \n        total_loss = b_loss + e_loss + l_loss + k_loss + s_loss\n        \n        # calculate validation metrics\n        val_loss += total_loss.item()\n        val_acc_bowel.update(b, bowel)\n        val_acc_extravasation.update(e, extravasation)\n        val_acc_liver.update(l, liver)\n        val_acc_kidney.update(k, kidney)\n        val_acc_spleen.update(s, spleen)\n    \n    \n    val_loss = val_loss/(batch_idx+1)\n    scheduler.step(val_loss)\n    \n    if val_loss < prev_val_best_loss:\n        prev_val_best_loss = val_loss\n        print(\"Validation Loss improved, Saving Model...\")\n        torch.save(model, f'efficientnet_b0_{val_loss:.3f}.pth')\n    \n    \n    \n    # accuracy and auc data\n    metrics_data = [\n                    [\"Bowel\", \n                        train_acc_bowel.compute_accuracy(),\n                        val_acc_bowel.compute_accuracy(),\n                        train_acc_bowel.compute_auc(),\n                        val_acc_bowel.compute_auc()],\n                    [\"Extravasation\", \n                        train_acc_extravasation.compute_accuracy(),\n                        val_acc_extravasation.compute_accuracy(),\n                        train_acc_extravasation.compute_auc(),\n                        val_acc_extravasation.compute_auc()],\n                    [\"Liver\", \n                        train_acc_liver.compute_accuracy(),\n                        val_acc_liver.compute_accuracy(),\n                        train_acc_liver.compute_auc(),\n                        val_acc_liver.compute_auc()],\n                    [\"Kidney\", \n                        train_acc_kidney.compute_accuracy(),\n                        val_acc_kidney.compute_accuracy(),\n                        train_acc_kidney.compute_auc(),\n                        val_acc_kidney.compute_auc()],\n                    [\"Spleen\", \n                        train_acc_spleen.compute_accuracy(),\n                        val_acc_spleen.compute_accuracy(),\n                        train_acc_spleen.compute_auc(),\n                        val_acc_spleen.compute_auc()]\n                ]\n    \n    # verbose\n    print('')\n    print(tabulate(metrics_data, headers=[\"\", \"Train Acc\", \"Val Acc\", \"Train AUC\", \"Val AUC\"]))\n    \n    print(f'\\nMean Train Loss: {train_loss:.3f}')\n    print(f'Mean Val Loss: {val_loss:.3f}\\n')\n    \n    #reset metrics\n    train_acc_bowel.reset()\n    train_acc_extravasation.reset()\n    train_acc_liver.reset()\n    train_acc_kidney.reset()\n    train_acc_spleen.reset()\n    val_acc_bowel.reset()\n    val_acc_extravasation.reset()\n    val_acc_liver.reset()\n    val_acc_kidney.reset()\n    val_acc_spleen.reset()\n","metadata":{"execution":{"iopub.status.busy":"2023-08-28T02:22:42.80026Z","iopub.execute_input":"2023-08-28T02:22:42.800629Z","iopub.status.idle":"2023-08-28T02:41:46.83505Z","shell.execute_reply.started":"2023-08-28T02:22:42.800598Z","shell.execute_reply":"2023-08-28T02:41:46.834035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save the model\ntorch.save(model.state_dict(), MODEL_PATH)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/rsna-2023-abdominal-trauma-detection\"\nIMAGE_DIR = \"/tmp/dataset/rsna-atd\"\nINPUT_MODEL_PATH = \"/kaggle/input/tf-efficientnet/pytorch/tf-efficientnet-b8/1\"\nMODEL_PATH = \"/kaggle/input/tf-efficientnet/pytorch/tf-efficientnet-b8/1\"\nSTRIDE = 10","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config:\n    IMAGE_SIZE = [256, 256]\n    RESIZE_DIM = 256\n    BATCH_SIZE = 64\n    AUTOTUNE = tf.data.AUTOTUNE\n    TARGET_COLS  = [\"bowel_healthy\", \"bowel_injury\", \"extravasation_healthy\",\n                   \"extravasation_injury\", \"kidney_healthy\", \"kidney_low\",\n                   \"kidney_high\", \"liver_healthy\", \"liver_low\", \"liver_high\",\n                   \"spleen_healthy\", \"spleen_low\", \"spleen_high\"]\n\nconfig = Config()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! cp {INPUT_MODEL_PATH} ./\n\nmodel = keras.models.load_model(MODEL_PATH)\nmodel.summary()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df = pd.read_csv(f\"{BASE_PATH}/test_series_meta.csv\")\n\n# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = meta_df.shape[0]\nunique_patients = meta_df[\"patient_id\"].nunique()\n\nprint(f\"{num_rows=}\")\nprint(f\"{unique_patients=}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df[\"dicom_folder\"] = BASE_PATH + \"/\" + \"test_images\"\\\n                                    + \"/\" + meta_df.patient_id.astype(str)\\\n                                    + \"/\" + meta_df.series_id.astype(str)\n\ntest_folders = meta_df.dicom_folder.tolist()\ntest_paths = []\nfor folder in tqdm(test_folders):\n    test_paths += sorted(glob(os.path.join(folder, \"*dcm\")))[::STRIDE]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = pd.DataFrame(test_paths, columns=[\"dicom_path\"])\ntest_df[\"patient_id\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-3]).astype(int)\ntest_df[\"series_id\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-2]).astype(int)\ntest_df[\"instance_number\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-1].replace(\".dcm\",\"\")).astype(int)\n\ntest_df[\"image_path\"] = f\"{IMAGE_DIR}/test_images\"\\\n                    + \"/\" + test_df.patient_id.astype(str)\\\n                    + \"/\" + test_df.series_id.astype(str)\\\n                    + \"/\" + test_df.instance_number.astype(str) +\".png\"\n\ntest_df.head(2)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = test_df.shape[0]\nunique_patients = test_df[\"patient_id\"].nunique()\n\nprint(f\"{num_rows=}\")\nprint(f\"{unique_patients=}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\nfile_paths = test_df.dicom_path.tolist()\n_ = Parallel(n_jobs=2, backend=\"threading\")(\n    delayed(resize_and_save)(file_path) for file_path in tqdm(file_paths, leave=True, position=0)\n)\n\ndel _; gc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_image(image_path):\n    file_bytes = tf.io.read_file(image_path)\n    image = tf.io.decode_png(file_bytes, channels=3, dtype=tf.uint8)\n    image = tf.image.resize(image, config.IMAGE_SIZE, method=\"bilinear\")\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef build_dataset(image_paths):\n    ds = (\n        tf.data.Dataset.from_tensor_slices(image_paths)\n        .map(decode_image, num_parallel_calls=config.AUTOTUNE)\n        .shuffle(config.BATCH_SIZE * 10)\n        .batch(config.BATCH_SIZE)\n        .prefetch(config.AUTOTUNE)\n    )\n    return ds","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"paths  = test_df.image_path.tolist()\n\nds = build_dataset(paths)\nimages = next(iter(ds))\n\nimages.shape","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def post_proc(pred):\n    proc_pred = np.empty((pred.shape[0], 2*2 + 3*3), dtype=\"float32\")\n\n    # bowel, extravasation\n    proc_pred[:, 0] = pred[:, 0]\n    proc_pred[:, 1] = 1 - proc_pred[:, 0]\n    proc_pred[:, 2] = pred[:, 1]\n    proc_pred[:, 3] = 1 - proc_pred[:, 2]\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = pred[:, 2:5]\n    proc_pred[:, 7:10] = pred[:, 5:8]\n    proc_pred[:, 10:13] = pred[:, 8:11]\n\n    return proc_pred","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Getting unique patient IDs from test dataset\npatient_ids = test_df[\"patient_id\"].unique()\n\n# Initializing array to store predictions\npatient_preds = np.zeros(\n    shape=(len(patient_ids), 2*2 + 3*3),\n    dtype=\"float32\"\n)\n\n# Iterating over each patient\nfor pidx, patient_id in tqdm(enumerate(patient_ids), total=len(patient_ids), desc=\"Patients \"):\n    print(f\"Patient ID: {patient_id}\")\n    \n    # Query the dataframe for a particular patient\n    patient_df = test_df.query(\"patient_id == @patient_id\")\n    \n    # Getting image paths for a patient\n    patient_paths = patient_df.image_path.tolist()\n\n    # Building dataset for prediction\n    dtest = build_dataset(patient_paths)\n    \n    # Predicting with the model\n    pred = model.predict(dtest)\n    pred = np.concatenate(pred, axis=-1).astype(\"float32\")\n    pred = pred[:len(patient_paths), :]\n    pred = np.mean(pred.reshape(1, len(patient_paths), 11), axis=0)\n    pred = np.max(pred, axis=0, keepdims=True)\n    \n    patient_preds[pidx, :] += post_proc(pred)[0]\n    \n\n    # Deleting variables to free up memory \n    del patient_df, patient_paths, dtest, pred; gc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -rf {MODEL_PATH}","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create Submission\npred_df = pd.DataFrame({\"patient_id\":patient_ids,})\npred_df[config.TARGET_COLS] = patient_preds.astype(\"float32\")\n\n# Align with sample submission\nsub_df = pd.read_csv(f\"{BASE_PATH}/sample_submission.csv\")\nsub_df = sub_df[[\"patient_id\"]]\nsub_df = sub_df.merge(pred_df, on=\"patient_id\", how=\"left\")\n\n# Store submission\nsub_df.to_csv(\"submission.csv\",index=False)\nsub_df.head(2)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}