{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":20561,"databundleVersionId":1187889,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"d63e611f-cf13-4c81-8b28-9cc03c44091e","cell_type":"code","source":"import numpy as np\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport os\nfrom pathlib import Path\nimport matplotlib.pyplot as plt","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:49.802241Z","start_time":"2025-11-09T09:29:48.675681Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:44.172187Z","iopub.execute_input":"2025-11-09T20:39:44.172503Z","iopub.status.idle":"2025-11-09T20:39:44.176869Z","shell.execute_reply.started":"2025-11-09T20:39:44.172482Z","shell.execute_reply":"2025-11-09T20:39:44.176071Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"2b0fb876-4359-466d-9263-ee7979123cde","cell_type":"code","source":"!pip install -U segmentation_models_pytorch --no-deps -q","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:50.515872Z","start_time":"2025-11-09T09:29:49.807897Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:44.546633Z","iopub.execute_input":"2025-11-09T20:39:44.54682Z","iopub.status.idle":"2025-11-09T20:39:46.054752Z","shell.execute_reply.started":"2025-11-09T20:39:44.546806Z","shell.execute_reply":"2025-11-09T20:39:46.053915Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"b19b014a-8389-4c0d-a896-c0d3a0743702","cell_type":"code","source":"prefix = '/kaggle/input/covid-segmentation'\n\nimages_radiopedia = np.load(os.path.join(prefix, 'images_radiopedia.npy')).astype(np.float32)\nmasks_radiopedia = np.load(os.path.join(prefix, 'masks_radiopedia.npy')).astype(np.int8)\nimages_medseg = np.load(os.path.join(prefix, 'images_medseg.npy')).astype(np.float32)\nmasks_medseg = np.load(os.path.join(prefix, 'masks_medseg.npy')).astype(np.int8)\n\ntest_images_medseg = np.load(os.path.join(prefix, 'test_images_medseg.npy')).astype(np.float32)\n","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:51.1525Z","start_time":"2025-11-09T09:29:50.521014Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:46.056306Z","iopub.execute_input":"2025-11-09T20:39:46.056543Z","iopub.status.idle":"2025-11-09T20:39:47.831563Z","shell.execute_reply.started":"2025-11-09T20:39:46.05652Z","shell.execute_reply":"2025-11-09T20:39:47.830402Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"699794af-a347-4761-8193-90d90ce29c23","cell_type":"code","source":"def visualize(image_batch, mask_batch=None, pred_batch=None, num_samples=8, hot_encode=True):\n    num_classes = mask_batch.shape[-1] if mask_batch is not None else 0\n    fix, ax = plt.subplots(num_classes + 1, num_samples, figsize=(num_samples * 2, (num_classes + 1) * 2))\n\n    for i in range(num_samples):\n        ax_image = ax[0, i] if num_classes > 0 else ax[i]\n        if hot_encode: \n            ax_image.imshow(image_batch[i,:,:,0], cmap='Greys')\n        else: \n            ax_image.imshow(image_batch[i,:,:])\n        ax_image.set_xticks([]) \n        ax_image.set_yticks([])\n        \n        if mask_batch is not None:\n            for j in range(num_classes):\n                if pred_batch is None:\n                    mask_to_show = mask_batch[i,:,:,j]\n                else:\n                    mask_to_show = np.zeros(shape=(*mask_batch.shape[1:-1], 3)) \n                    mask_to_show[..., 0] = pred_batch[i,:,:,j] > 0.5\n                    mask_to_show[..., 1] = mask_batch[i,:,:,j]\n                ax[j + 1, i].imshow(mask_to_show, vmin=0, vmax=1)\n                ax[j + 1, i].set_xticks([]) \n                ax[j + 1, i].set_yticks([]) \n\n    plt.tight_layout()\n    plt.show()","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:51.158306Z","start_time":"2025-11-09T09:29:51.155567Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:47.833076Z","iopub.execute_input":"2025-11-09T20:39:47.833396Z","iopub.status.idle":"2025-11-09T20:39:47.840828Z","shell.execute_reply.started":"2025-11-09T20:39:47.833369Z","shell.execute_reply":"2025-11-09T20:39:47.840121Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"d306365c-6a14-4def-83a5-bbd6264d1790","cell_type":"code","source":"visualize(images_radiopedia[30:], masks_radiopedia[30:])","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:52.670103Z","start_time":"2025-11-09T09:29:51.79271Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:47.841656Z","iopub.execute_input":"2025-11-09T20:39:47.841917Z","iopub.status.idle":"2025-11-09T20:39:49.747606Z","shell.execute_reply.started":"2025-11-09T20:39:47.841897Z","shell.execute_reply":"2025-11-09T20:39:49.746739Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"8b9ec032-ec44-40cd-b8df-32eab2a7b504","cell_type":"code","source":"def onehot_to_mask(mask, palette):\n    \"\"\"\n    Converts a mask (H, W, K) to (H, W, C)\n    \"\"\"\n    x = np.argmax(mask, axis=-1)\n    colour_codes = np.array(palette)\n    x = np.uint8(colour_codes[x.astype(np.uint8)])\n    return x\n\npalette = [[0], [1], [2],[3]]\nmasks_radiopedia_recover = onehot_to_mask(masks_radiopedia, palette).squeeze()  # shape = (H, W)\n\nmasks_medseg_recover = onehot_to_mask(masks_medseg, palette).squeeze()  # shape = (H, W)\n\nprint('Hot encoded mask size: ',masks_radiopedia.shape)\nprint('Paletted mask size:',masks_medseg_recover.shape)\n\nvisualize(masks_medseg_recover[30:],hot_encode=False)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:55.174556Z","start_time":"2025-11-09T09:29:52.71541Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:49.749186Z","iopub.execute_input":"2025-11-09T20:39:49.749684Z","iopub.status.idle":"2025-11-09T20:39:54.727651Z","shell.execute_reply.started":"2025-11-09T20:39:49.749662Z","shell.execute_reply":"2025-11-09T20:39:54.727054Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"6502b7be-1a3e-4800-a759-b8095a6afdbe","cell_type":"code","source":"def preprocess_images(images_arr, mean_std=None):\n    images_arr[images_arr > 500] = 500\n    images_arr[images_arr < -1500] = -1500\n    min_perc, max_perc = np.percentile(images_arr, 5), np.percentile(images_arr, 95)\n    images_arr_valid = images_arr[(images_arr > min_perc) & (images_arr < max_perc)]\n    mean, std = (images_arr_valid.mean(), images_arr_valid.std()) if mean_std is None else mean_std\n    images_arr = (images_arr - mean) / std\n    print(f'mean {mean}, std {std}')\n    return images_arr, (mean, std)\n\nimages_radiopedia, mean_std = preprocess_images(images_radiopedia)\nimages_medseg, _ = preprocess_images(images_medseg, mean_std)\ntest_images_medseg, _ = preprocess_images(test_images_medseg, mean_std)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:58.432385Z","start_time":"2025-11-09T09:29:55.190257Z"},"execution":{"iopub.status.busy":"2025-11-09T20:39:54.728896Z","iopub.execute_input":"2025-11-09T20:39:54.729133Z","iopub.status.idle":"2025-11-09T20:40:02.326472Z","shell.execute_reply.started":"2025-11-09T20:39:54.729117Z","shell.execute_reply":"2025-11-09T20:40:02.325566Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"87df5f7b-9dd9-47b6-a215-715bf8330d4b","cell_type":"code","source":"def plot_hists(images1, images2=None):\n    plt.hist(images1.ravel(), bins=100, density=True, color='b', alpha=1 if images2 is None else 0.5)\n    if images2 is not None:\n        plt.hist(images2.ravel(), bins=100, density=True, alpha=0.5, color='orange')\n    plt.show();\n    \nplot_hists(test_images_medseg, images_radiopedia)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:29:59.870022Z","start_time":"2025-11-09T09:29:58.444777Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:02.327413Z","iopub.execute_input":"2025-11-09T20:40:02.327628Z","iopub.status.idle":"2025-11-09T20:40:05.849927Z","shell.execute_reply.started":"2025-11-09T20:40:02.327612Z","shell.execute_reply":"2025-11-09T20:40:05.848707Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"da8b4386-b114-44e0-9fb6-7dc6d467594d","cell_type":"code","source":"masks_radiopedia_recover = onehot_to_mask(masks_radiopedia, palette).squeeze()  # shape = (H, W)\nmasks_medseg_recover = onehot_to_mask(masks_medseg, palette).squeeze()  # shape = (H, W)\n\n\nval_indexes, train_indexes = list(range(24)), list(range(24, 100))\n\ntrain_images = np.concatenate((images_medseg[train_indexes], images_radiopedia))\ntrain_masks = np.concatenate((masks_medseg_recover[train_indexes], masks_radiopedia_recover))\nval_images = images_medseg[val_indexes]\nval_masks = masks_medseg_recover[val_indexes]\n\nbatch_size = len(val_masks)\n\ndel masks_medseg_recover\ndel masks_radiopedia_recover\ndel images_radiopedia\ndel masks_radiopedia\ndel images_medseg\ndel masks_medseg","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:02.365877Z","start_time":"2025-11-09T09:29:59.887766Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:05.851891Z","iopub.execute_input":"2025-11-09T20:40:05.852538Z","iopub.status.idle":"2025-11-09T20:40:10.925796Z","shell.execute_reply.started":"2025-11-09T20:40:05.852518Z","shell.execute_reply":"2025-11-09T20:40:10.925233Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"b1d0fc3e-b20b-4e4f-91a8-f17874b668e8","cell_type":"code","source":"import albumentations as A\nimport cv2\n\nTARGET_SIZE = 256\n\ntrain_augs = A.Compose([\n    A.Resize(height=TARGET_SIZE, width=TARGET_SIZE, interpolation=cv2.INTER_NEAREST),\n    A.HorizontalFlip(p=0.5),\n])\n\nval_augs = A.Compose([\n    A.Resize(height=TARGET_SIZE, width=TARGET_SIZE, interpolation=cv2.INTER_NEAREST)\n])","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:03.286257Z","start_time":"2025-11-09T09:30:02.38253Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:10.926458Z","iopub.execute_input":"2025-11-09T20:40:10.926636Z","iopub.status.idle":"2025-11-09T20:40:10.934476Z","shell.execute_reply.started":"2025-11-09T20:40:10.926622Z","shell.execute_reply":"2025-11-09T20:40:10.933732Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"0ea97470-6c15-483a-b7af-9e413471daed","cell_type":"code","source":"from PIL import Image\nimport segmentation_models_pytorch as smp\nimport torch.nn.functional as F\nimport torch.nn as nn\nfrom tqdm.notebook import tqdm\nimport time\n\nfrom torchvision import transforms as T\nfrom torch.utils.data import Dataset, DataLoader  \nimport torch\n\ndevice = torch.device(\"cuda:1\") if torch.cuda.is_available() else torch.device('cpu')\n\nclass Dataset:   \n    def __init__(\n            self, \n            images, \n            masks,\n            augmentations=None\n    ):\n        self.images = images\n        self.masks = masks\n        self.augmentations = augmentations\n        self.mean = [0.485]\n        self.std = [0.229]\n    \n    def __getitem__(self, i):\n        image = self.images[i]\n        mask = self.masks[i]\n        \n        \n        if self.augmentations is not None:\n            sample = self.augmentations(image=image, mask=mask)\n            \n            image, mask = Image.fromarray(np.squeeze(sample['image'], axis=2)), sample['mask']\n        \n        if self.augmentations is None:\n            image = Image.fromarray(image)\n        \n        t = T.Compose([T.ToTensor(), T.Normalize(self.mean, self.std)])\n        image = t(image)\n        mask = torch.from_numpy(mask).long()\n    \n        return image, mask\n    \n    def __len__(self):\n        return len(self.images)\n    \n    def tiles(self, image, mask):\n\n        img_patches = image.unfold(1, 512, 512).unfold(2, 768, 768) \n        img_patches  = img_patches.contiguous().view(3,-1, 512, 768) \n        img_patches = img_patches.permute(1,0,2,3)\n        \n        mask_patches = mask.unfold(0, 512, 512).unfold(1, 768, 768)\n        mask_patches = mask_patches.contiguous().view(-1, 512, 768)\n        \n        return img_patches, mask_patches\n    \n                \ntrain_dataset = Dataset(train_images, train_masks, train_augs)\nval_dataset = Dataset(val_images, val_masks, val_augs)       \n\ntrain_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:03.52577Z","start_time":"2025-11-09T09:30:03.309412Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:10.935209Z","iopub.execute_input":"2025-11-09T20:40:10.935492Z","iopub.status.idle":"2025-11-09T20:40:10.951591Z","shell.execute_reply.started":"2025-11-09T20:40:10.935476Z","shell.execute_reply":"2025-11-09T20:40:10.950878Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"2cbceda6-19ed-4ef6-b69a-5d1ddbe77f18","cell_type":"code","source":"def mask_to_onehot(mask, palette):\n    \"\"\"\n    Converts a segmentation mask (H, W, C) to (H, W, K) where the last dim is a one\n    hot encoding vector, C is usually 1 or 3, and K is the number of class.\n    \"\"\"\n    semantic_map = []\n    for colour in palette:\n        #print('colour',colour)\n        equality = np.equal(mask, colour)\n        #print('equality',equality)\n        class_map = np.all(equality, axis=-1)\n        semantic_map.append(class_map)\n    semantic_map = np.stack(semantic_map, axis=-1).astype(np.float32)\n    return torch.from_numpy(semantic_map)\n\ni,train_data = next(enumerate(train_dataloader))\n\nmask_hot_encoded = mask_to_onehot(torch.unsqueeze(train_data[1],-1).numpy(),palette)\n#visualize(torch.unsqueeze(torch.squeeze(train_data[0],1),-1),mask_hot_encoded)\nvisualize(train_data[0].permute(0, 2, 3,1),mask_hot_encoded)\n","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.246095Z","start_time":"2025-11-09T09:30:03.531386Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:10.952663Z","iopub.execute_input":"2025-11-09T20:40:10.952985Z","iopub.status.idle":"2025-11-09T20:40:12.80253Z","shell.execute_reply.started":"2025-11-09T20:40:10.952967Z","shell.execute_reply":"2025-11-09T20:40:12.801687Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"4df4a6d7-6d05-4e86-8cd3-e7b52db0c5e9","cell_type":"code","source":"def pixel_accuracy(output, mask):\n    with torch.no_grad():\n        output = torch.argmax(F.softmax(output, dim=1), dim=1)\n        correct = torch.eq(output, mask).int()\n        accuracy = float(correct.sum()) / float(correct.numel())\n    return accuracy\n","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.256628Z","start_time":"2025-11-09T09:30:04.254876Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:12.803272Z","iopub.execute_input":"2025-11-09T20:40:12.803468Z","iopub.status.idle":"2025-11-09T20:40:12.807847Z","shell.execute_reply.started":"2025-11-09T20:40:12.803452Z","shell.execute_reply":"2025-11-09T20:40:12.807162Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"492180af-1f61-4faa-8547-7f14f9dddfb7","cell_type":"code","source":"def mIoU(pred_mask, mask, smooth=1e-10, n_classes=4):\n    with torch.no_grad():\n        pred_mask = F.softmax(pred_mask, dim=1)\n        pred_mask = torch.argmax(pred_mask, dim=1)\n        pred_mask = pred_mask.contiguous().view(-1)\n        mask = mask.contiguous().view(-1)\n\n        iou_per_class = []\n        for clas in range(0, n_classes): #loop per pixel class\n            true_class = pred_mask == clas\n            true_label = mask == clas\n\n            if true_label.long().sum().item() == 0: #no exist label in this loop\n                iou_per_class.append(np.nan)\n            else:\n                intersect = torch.logical_and(true_class, true_label).sum().float().item()\n                union = torch.logical_or(true_class, true_label).sum().float().item()\n\n                iou = (intersect + smooth) / (union +smooth)\n                iou_per_class.append(iou)\n        return np.nanmean(iou_per_class)\n","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.267171Z","start_time":"2025-11-09T09:30:04.264848Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:23.489331Z","iopub.execute_input":"2025-11-09T20:40:23.489598Z","iopub.status.idle":"2025-11-09T20:40:23.495498Z","shell.execute_reply.started":"2025-11-09T20:40:23.489579Z","shell.execute_reply":"2025-11-09T20:40:23.494771Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"a39b2125-030e-451d-82e1-2ae3b1ce7447","cell_type":"code","source":"def iou(pred, gt):\n    pred = pred.squeeze().cpu().data.numpy()\n    pred = ToLabel(pred)\n    gt = gt.squeeze().cpu().data.numpy()\n    agg = pred + gt\n    i = float(np.sum(agg == 2))\n    u = float(np.sum(agg > 0))\n    if u == 0:\n        result = 1\n    else:\n        result = i/u\n    return result","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.271801Z","start_time":"2025-11-09T09:30:04.270053Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:24.763953Z","iopub.execute_input":"2025-11-09T20:40:24.764871Z","iopub.status.idle":"2025-11-09T20:40:24.770189Z","shell.execute_reply.started":"2025-11-09T20:40:24.764832Z","shell.execute_reply":"2025-11-09T20:40:24.769399Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"813dcfac-f3f9-44e0-a8ca-b70605fa8765","cell_type":"code","source":"model = smp.DeepLabV3Plus(\n    encoder_name='efficientnet-b2',\n    encoder_weights='imagenet',\n    in_channels=1,\n    classes=4,\n    activation=None\n)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.328739Z","start_time":"2025-11-09T09:30:04.274502Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:25.629134Z","iopub.execute_input":"2025-11-09T20:40:25.629839Z","iopub.status.idle":"2025-11-09T20:40:25.759147Z","shell.execute_reply.started":"2025-11-09T20:40:25.629815Z","shell.execute_reply":"2025-11-09T20:40:25.758351Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"f78aae45-a912-41a9-b235-ee44e9878629","cell_type":"code","source":"import torch\nfrom torch.amp import autocast, GradScaler\n\ndevice_type = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ndevice = torch.device(device_type)\n\n# Prefer BF16 if supported (no scaler needed for bf16)\nuse_bf16 = (device_type == \"cuda\") and hasattr(torch.cuda, \"is_bf16_supported\") and torch.cuda.is_bf16_supported()\namp_dtype = torch.bfloat16 if use_bf16 else torch.float16\n\ndef get_lr(optimizer):\n    for pg in optimizer.param_groups:\n        return pg[\"lr\"]\n\ndef fit(epochs, model, train_loader, val_loader, criterion, optimizer, scheduler):\n    train_losses, test_losses = [], []\n    val_iou, val_acc, train_iou, train_acc, lrs = [], [], [], [], []\n    min_loss = float(\"inf\"); decrease = 1; not_improve = 0\n\n    model.to(device)\n\n    try:\n        scaler = GradScaler(\"cuda\", enabled=not use_bf16) if device_type == \"cuda\" else GradScaler(enabled=False)\n    except TypeError:\n        scaler = GradScaler(enabled=(device_type == \"cuda\" and not use_bf16))\n\n    fit_time = time.time()\n\n    for e in range(epochs):\n        since = time.time()\n        model.train()\n        running_loss = 0.0\n        iou_sum = 0.0\n        acc_sum = 0.0\n\n        for i, (image_tiles, mask_tiles) in enumerate(tqdm(train_loader)):\n            image = image_tiles.to(device, non_blocking=True)\n            mask  =  mask_tiles.to(device, non_blocking=True)\n\n            optimizer.zero_grad(set_to_none=True)\n\n            with autocast(device_type=device_type, dtype=amp_dtype, enabled=(device_type==\"cuda\")):\n                output = model(image)\n                loss = criterion(output, mask)\n\n            with torch.no_grad():\n                iou_sum += float(mIoU(output, mask))\n                acc_sum += float(pixel_accuracy(output, mask))\n\n            # Backward\n            if device_type == \"cuda\" and not use_bf16:\n                scaler.scale(loss).backward()\n                scaler.step(optimizer)\n                scaler.update()\n            else:\n                loss.backward()\n                optimizer.step()\n\n            scheduler.step()\n            lrs.append(get_lr(optimizer))\n            running_loss += float(loss.detach().item())\n\n            del output, loss, image, mask\n\n        model.eval()\n        test_loss = 0.0\n        val_iou_sum = 0.0\n        val_acc_sum = 0.0\n        with torch.no_grad(), autocast(device_type=device_type, dtype=amp_dtype, enabled=(device_type==\"cuda\")):\n            for image_tiles, mask_tiles in tqdm(val_loader):\n                image = image_tiles.to(device, non_blocking=True)\n                mask  =  mask_tiles.to(device, non_blocking=True)\n                out   = model(image)\n                test_loss += float(criterion(out, mask).item())\n                val_iou_sum += float(mIoU(out, mask))\n                val_acc_sum += float(pixel_accuracy(out, mask))\n                del out, image, mask\n\n        tr_loss = running_loss/len(train_loader)\n        va_loss = test_loss/len(val_loader)\n        tr_iou  = iou_sum/len(train_loader)\n        va_iou  = val_iou_sum/len(val_loader)\n        tr_acc  = acc_sum/len(train_loader)\n        va_acc  = val_acc_sum/len(val_loader)\n\n        train_losses.append(tr_loss); test_losses.append(va_loss)\n        train_iou.append(tr_iou);     val_iou.append(va_iou)\n        train_acc.append(tr_acc);     val_acc.append(va_acc)\n\n        print(f\"Epoch:{e+1}/{epochs}  \"\n              f\"Train Loss:{tr_loss:.3f}  Val Loss:{va_loss:.3f}  \"\n              f\"Train mIoU:{tr_iou:.3f}  Val mIoU:{va_iou:.3f}  \"\n              f\"Train Acc:{tr_acc:.3f}  Val Acc:{va_acc:.3f}  \"\n              f\"Time:{(time.time()-since)/60:.2f}m\")\n\n        if va_loss < min_loss:\n            print(f'Loss Decreasing.. {min_loss:.3f} >> {va_loss:.3f}')\n            min_loss = va_loss\n            decrease += 1\n            if decrease % 5 == 0:\n                print('saving model state_dict...')\n                torch.save(model.state_dict(), f'DeepLabV3Plus_efficientnet_b2_mIoU-{va_iou:.3f}.pth')\n        else:\n            not_improve += 1\n            print(f'Loss Not Decrease for {not_improve} time')\n            if not_improve == 7:\n                print('Loss not decrease for 7 times, Stop Training')\n                break\n\n        torch.cuda.empty_cache()\n\n    history = {\n        'train_loss': train_losses, 'val_loss': test_losses,\n        'train_miou': train_iou,    'val_miou': val_iou,\n        'train_acc':  train_acc,    'val_acc':  val_acc,\n        'lrs': lrs\n    }\n    print('Total time: {:.2f} m'.format((time.time()- fit_time)/60))\n    return history\n","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:30:04.338258Z","start_time":"2025-11-09T09:30:04.332395Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:28.030364Z","iopub.execute_input":"2025-11-09T20:40:28.031113Z","iopub.status.idle":"2025-11-09T20:40:28.045365Z","shell.execute_reply.started":"2025-11-09T20:40:28.031082Z","shell.execute_reply":"2025-11-09T20:40:28.044741Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"f6d2a0e7-97c8-4065-9b20-cafcd733eb83","cell_type":"code","source":"from tqdm import tqdm\n\nmax_lr = 1e-3\nepoch = 15\nweight_decay = 1e-4\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=max_lr, weight_decay=weight_decay)\nsched = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr, epochs=epoch,\n                                            steps_per_epoch=len(train_dataloader))\n\nhistory = fit(epoch, model, train_dataloader, val_dataloader, criterion, optimizer, sched)","metadata":{"ExecuteTime":{"end_time":"2025-11-09T09:36:18.797771Z","start_time":"2025-11-09T09:35:32.795447Z"},"execution":{"iopub.status.busy":"2025-11-09T20:40:32.14434Z","iopub.execute_input":"2025-11-09T20:40:32.144614Z","iopub.status.idle":"2025-11-09T21:04:44.958536Z","shell.execute_reply.started":"2025-11-09T20:40:32.144594Z","shell.execute_reply":"2025-11-09T21:04:44.957871Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"70fe0a60-3a7a-47c6-b58a-447ccfeeb462","cell_type":"code","source":"def plot_loss(history):\n    plt.plot(history['val_loss'], label='val', marker='o')\n    plt.plot( history['train_loss'], label='train', marker='o')\n    plt.title('Loss per epoch'); plt.ylabel('loss');\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()\n    \ndef plot_score(history):\n    plt.plot(history['train_miou'], label='train_mIoU', marker='*')\n    plt.plot(history['val_miou'], label='val_mIoU',  marker='*')\n    plt.title('Score per epoch'); plt.ylabel('mean IoU')\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()\n    \ndef plot_acc(history):\n    plt.plot(history['train_acc'], label='train_accuracy', marker='*')\n    plt.plot(history['val_acc'], label='val_accuracy',  marker='*')\n    plt.title('Accuracy per epoch'); plt.ylabel('Accuracy')\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:20.430822Z","iopub.execute_input":"2025-11-09T21:06:20.431238Z","iopub.status.idle":"2025-11-09T21:06:20.43769Z","shell.execute_reply.started":"2025-11-09T21:06:20.431215Z","shell.execute_reply":"2025-11-09T21:06:20.436855Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"acbf4773-7c0f-46bb-9da7-0d58f92ed87c","cell_type":"code","source":"plot_loss(history)\nplot_score(history)\nplot_acc(history)","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:20.934827Z","iopub.execute_input":"2025-11-09T21:06:20.935455Z","iopub.status.idle":"2025-11-09T21:06:21.425062Z","shell.execute_reply.started":"2025-11-09T21:06:20.935427Z","shell.execute_reply":"2025-11-09T21:06:21.424429Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"45e660bd-ce9c-4ecc-b15d-e81ebcfbfdb5","cell_type":"code","source":"def predict_image_mask_miou(model, image, mask, mean=[0.485], std=[0.229]):\n    model.eval()\n    model.to(device); image=image.to(device)\n    mask = mask.to(device)\n    \n    \n    with torch.no_grad():        \n        output = model(image)\n        a,b,c,d = output.shape\n        score = mIoU(output, mask)\n        masked = torch.argmax(output, dim=1)\n        masked = masked.cpu().squeeze(0)\n    return masked, score, output.permute(0, 2, 3,1)","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:26.75587Z","iopub.execute_input":"2025-11-09T21:06:26.756235Z","iopub.status.idle":"2025-11-09T21:06:26.761665Z","shell.execute_reply.started":"2025-11-09T21:06:26.756211Z","shell.execute_reply":"2025-11-09T21:06:26.760806Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"4fe99cbe-93f0-41f9-bbdc-29cf0e35008d","cell_type":"code","source":"def predict_image_mask_pixel(model, image, mask, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]):\n    model.eval()\n    model.to(device); image=image.to(device)\n    mask = mask.to(device)\n    with torch.no_grad():\n        \n        output = model(image)\n        acc = pixel_accuracy(output, mask)\n        masked = torch.argmax(output, dim=1)\n        masked = masked.cpu().squeeze(0)\n    return masked, acc","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:27.829443Z","iopub.execute_input":"2025-11-09T21:06:27.830141Z","iopub.status.idle":"2025-11-09T21:06:27.83471Z","shell.execute_reply.started":"2025-11-09T21:06:27.830118Z","shell.execute_reply":"2025-11-09T21:06:27.833929Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"a58f89a3-b6c0-475b-9329-fe3cd49aa56d","cell_type":"code","source":"def mask_to_onehot(mask, palette):\n    \"\"\"\n    Converts a segmentation mask (H, W, C) to (H, W, K) where the last dim is a one\n    hot encoding vector, C is usually 1 or 3, and K is the number of class.\n    \"\"\"\n    semantic_map = []\n    for colour in palette:\n        equality = np.equal(mask, colour)\n        class_map = np.all(equality, axis=-1)\n        semantic_map.append(class_map)\n    semantic_map = np.stack(semantic_map, axis=-1).astype(np.float32)\n    return torch.from_numpy(semantic_map)","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:28.361672Z","iopub.execute_input":"2025-11-09T21:06:28.362174Z","iopub.status.idle":"2025-11-09T21:06:28.366373Z","shell.execute_reply.started":"2025-11-09T21:06:28.362152Z","shell.execute_reply":"2025-11-09T21:06:28.365669Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"ebe1e8f5-d7d0-4ac4-8436-3923e2b4681a","cell_type":"code","source":"image, mask = next(iter(val_dataloader))\npred_mask, score,output = predict_image_mask_miou(model, image, mask)\nsemantic_map = mask_to_onehot(torch.unsqueeze(mask,-1).numpy(),palette)","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:28.65354Z","iopub.execute_input":"2025-11-09T21:06:28.654131Z","iopub.status.idle":"2025-11-09T21:06:29.219659Z","shell.execute_reply.started":"2025-11-09T21:06:28.65411Z","shell.execute_reply":"2025-11-09T21:06:29.219033Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"3c74abe4-fa2d-4ea0-bd89-32c2fce35026","cell_type":"code","source":"visualize(image, semantic_map.cpu(), pred_batch=output.cpu())","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:29.220727Z","iopub.execute_input":"2025-11-09T21:06:29.22097Z","iopub.status.idle":"2025-11-09T21:06:31.027062Z","shell.execute_reply.started":"2025-11-09T21:06:29.220946Z","shell.execute_reply":"2025-11-09T21:06:31.026436Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"c9c88126-c1fe-4246-9bdf-fd897c19708d","cell_type":"code","source":"def miou_score(model, test_set):\n    score_iou = []\n    for i, data in enumerate(tqdm(test_set)):\n        img, mask = data\n        pred_mask, score,output = predict_image_mask_miou(model, img, mask)\n        score_iou.append(score)\n    return score_iou","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:37.968747Z","iopub.execute_input":"2025-11-09T21:06:37.969125Z","iopub.status.idle":"2025-11-09T21:06:37.974068Z","shell.execute_reply.started":"2025-11-09T21:06:37.969091Z","shell.execute_reply":"2025-11-09T21:06:37.973271Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"81b9ccea-94ee-4a54-9f08-969f6fae4369","cell_type":"code","source":"mob_miou = miou_score(model, val_dataloader)\nmob_miou","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:38.411482Z","iopub.execute_input":"2025-11-09T21:06:38.411721Z","iopub.status.idle":"2025-11-09T21:06:38.613262Z","shell.execute_reply.started":"2025-11-09T21:06:38.411703Z","shell.execute_reply":"2025-11-09T21:06:38.612497Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"1c983b85-9f75-4209-b949-fbe1c7d22d69","cell_type":"code","source":"del train_images\ndel train_masks","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:40.069743Z","iopub.execute_input":"2025-11-09T21:06:40.070007Z","iopub.status.idle":"2025-11-09T21:06:40.073854Z","shell.execute_reply.started":"2025-11-09T21:06:40.069988Z","shell.execute_reply":"2025-11-09T21:06:40.07316Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"8c09f395-236b-4995-a7dc-994d6172cbef","cell_type":"code","source":"def test_predict(model, image, mean=[0.485], std=[0.229]):\n    t = T.Compose([T.ToTensor(), T.Normalize(mean, std)])\n    image = t(image)\n    model.eval()\n    \n    model.to(device); image=image.to(device)\n    \n    with torch.no_grad():\n        output = model(torch.unsqueeze(image,1)).cpu()\n        output = nn.Softmax(dim=1)(output)\n    return output.permute(0, 2, 3,1)","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:40.879948Z","iopub.execute_input":"2025-11-09T21:06:40.880656Z","iopub.status.idle":"2025-11-09T21:06:40.885479Z","shell.execute_reply.started":"2025-11-09T21:06:40.880631Z","shell.execute_reply":"2025-11-09T21:06:40.884715Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"eee78102-7d0e-4d5e-8f9e-e8f8a1e4d642","cell_type":"code","source":"image_batch = np.stack([val_augs(image=img)['image'] for img in test_images_medseg], axis=0)\nprint(torch.from_numpy(image_batch).shape)\nprint(image_batch[i].shape)\n#output = test_predict(model, torch.from_numpy(image_batch).permute(0, 3, 1,2))\noutput = np.zeros((10,256,256,4))\nfor i in range(10):   \n    output[i] = test_predict(model, image_batch[i])\nprint(output.shape)\ntest_masks_prediction = output > 0.5\nvisualize(image_batch, test_masks_prediction, num_samples=len(test_images_medseg))\n","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:41.797863Z","iopub.execute_input":"2025-11-09T21:06:41.798185Z","iopub.status.idle":"2025-11-09T21:06:43.996518Z","shell.execute_reply.started":"2025-11-09T21:06:41.798163Z","shell.execute_reply":"2025-11-09T21:06:43.995583Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"46e34e22-5b14-4786-98cc-ab6db27447d5","cell_type":"code","source":"import scipy\ntest_masks_prediction_original_size = scipy.ndimage.zoom(test_masks_prediction[..., :-2], (1, 2, 2, 1), order=0)\ntest_masks_prediction_original_size.shape","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:06:43.997963Z","iopub.execute_input":"2025-11-09T21:06:43.998759Z","iopub.status.idle":"2025-11-09T21:06:44.091004Z","shell.execute_reply.started":"2025-11-09T21:06:43.998727Z","shell.execute_reply":"2025-11-09T21:06:44.090451Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"01af3626-782a-4dbf-9702-e86db862cff8","cell_type":"code","source":"import pandas as pd\n\npd.DataFrame(\n             data=np.stack((np.arange(len(test_masks_prediction_original_size.ravel())), \n                            test_masks_prediction_original_size.ravel().astype(int)),\n                            axis=-1), \n             columns=['Id', 'Predicted'])\\\n.set_index('Id').to_csv('sub1.csv')","metadata":{"execution":{"iopub.status.busy":"2025-11-09T21:07:13.400786Z","iopub.execute_input":"2025-11-09T21:07:13.401457Z","iopub.status.idle":"2025-11-09T21:07:16.592904Z","shell.execute_reply.started":"2025-11-09T21:07:13.401425Z","shell.execute_reply":"2025-11-09T21:07:16.592127Z"},"trusted":true},"outputs":[],"execution_count":null},{"id":"a565c9aa-c933-4617-b942-54752dd0e37c","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}