{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":120401,"databundleVersionId":14399764}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\ninp_path = \"/kaggle/input/competitions/flood-image-segmentation-ml-4-im-2025-2026/Training\"\nos.listdir(inp_path)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:41.187150Z","iopub.execute_input":"2026-04-04T08:01:41.187499Z","iopub.status.idle":"2026-04-04T08:01:41.194020Z","shell.execute_reply.started":"2026-04-04T08:01:41.187472Z","shell.execute_reply":"2026-04-04T08:01:41.193282Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\nimport torch\nimport numpy as np\nimport pandas as pd \nimport cv2\n\nclass ImgSegDataset(Dataset):\n\n    def __init__(self, root: str, indices: list[int], transform = None):\n\n        super().__init__()\n        \n        self.mask_path = os.path.join(root, \"Masks\")\n        self.image_path = os.path.join(root, \"Images\")\n        self.image_names = [x.split('.')[0] for x in sorted(os.listdir(self.image_path))]\n        self.image_ftypes = [\".\"+  x.split('.')[1] for x in sorted(os.listdir(self.image_path))]\n        self.mask_ftypes = [\".\"+ x.split('.')[1] for x in sorted(os.listdir(self.mask_path))]\n        self.indices = indices\n        self.transform = transform\n        \n    def __len__(self) -> int:\n        return len(self.indices)\n\n    def __getitem__(self, index: int) -> tuple[torch.Tensor, torch.Tensor] | tuple[np.ndarray, np.ndarray]:\n\n        index = self.indices[index]\n        image_path: str = os.path.join(self.image_path, self.image_names[index] + self.image_ftypes[index])\n        mask_path: str = os.path.join(self.mask_path, self.image_names[index] + self.mask_ftypes[index])\n        \n        image= cv2.imread(image_path, cv2.IMREAD_COLOR_RGB)\n        mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)\n        \n        if self.transform:\n            argmt = self.transform(image = image, mask = mask)\n            image = argmt[\"image\"]\n            mask  = argmt[\"mask\"]\n            mask = (mask > 0).float()\n            \n        return image, mask","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:41.195522Z","iopub.execute_input":"2026-04-04T08:01:41.195829Z","iopub.status.idle":"2026-04-04T08:01:41.210536Z","shell.execute_reply.started":"2026-04-04T08:01:41.195795Z","shell.execute_reply":"2026-04-04T08:01:41.209799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nroot_path = \"/kaggle/input/competitions/flood-image-segmentation-ml-4-im-2025-2026/Training\"\ntotal_size = len(os.listdir( os.path.join(root_path + \"/Images\")))\nprint(f\"Total Dataset Size: {total_size}\")\nall_indices = list(range(total_size))\ntrain_indices, val_indices, _ , _ = train_test_split(all_indices, \n                                                     all_indices, \n                                                     train_size = 0.8 , \n                                                     test_size = 0.2, \n                                                     shuffle = True, \n                                                     random_state = 13)\nprint(f\"Train indices peak: {train_indices[:5]}, size: {len(train_indices)}\")\nprint(f\"Validation indices peak: {val_indices[:5]}, size: {len(val_indices)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:41.211592Z","iopub.execute_input":"2026-04-04T08:01:41.212178Z","iopub.status.idle":"2026-04-04T08:01:41.234228Z","shell.execute_reply.started":"2026-04-04T08:01:41.212153Z","shell.execute_reply":"2026-04-04T08:01:41.233345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TrainingDataset: ImgSegDataset = ImgSegDataset(root = root_path, indices = train_indices, transform = None)\nValidationDataset: ImgSegDataset = ImgSegDataset(root = root_path, indices = val_indices, transform = None)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:41.235107Z","iopub.execute_input":"2026-04-04T08:01:41.235375Z","iopub.status.idle":"2026-04-04T08:01:41.254747Z","shell.execute_reply.started":"2026-04-04T08:01:41.235353Z","shell.execute_reply":"2026-04-04T08:01:41.254230Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport random \n\nshape = (2, 4)\nfig, axes = plt.subplots(shape[0], shape[1])\n\nfor j in range(shape[1]):\n    random_index = random.randint(0, len(TrainingDataset))\n    image, mask = TrainingDataset[random_index]\n    axes[0, j].imshow(image, cmap = \"viridis\")\n    axes[1, j].imshow(mask, cmap = \"grey\")\n    \nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:41.256181Z","iopub.execute_input":"2026-04-04T08:01:41.256460Z","iopub.status.idle":"2026-04-04T08:01:42.008407Z","shell.execute_reply.started":"2026-04-04T08:01:41.256439Z","shell.execute_reply":"2026-04-04T08:01:42.007487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import albumentations as A\n\ntrain_tf: A.Compose = A.Compose([\n\n    # Base Argumentations: Geometric\n    A.Resize(height = 256 , width = 256, interpolation = cv2.INTER_LINEAR, mask_interpolation = cv2.INTER_NEAREST),\n    A.RandomResizedCrop(size = (224, 224), scale = (0.6, 1.0)),\n    A.RandomRotate90(p = 0.5),\n    A.HorizontalFlip(p = 0.5),\n    A.VerticalFlip(p = 0.5),\n\n    # Some transformations that make images a little irregular, useful for regularization\n    A.OneOf([\n        A.GridDistortion(p = 0.2),\n        A.ElasticTransform(p = 0.2),\n    ], p = 1.0),\n\n    # Affine transformation , very useful for generating varied images\n    A.Affine(scale = (0.9, 1.1), \n             translate_percent = (0.07, 0.15), \n             rotate = (-20, 20), \n             border_mode = cv2.BORDER_REFLECT_101, \n             p = 0.3),\n\n    # Simple color jitter, must keep the hue low\n    A.ColorJitter(hue = (-0.1, 0.1)),\n\n    # Simulation for weather , for this dataset, it is extremely useful\n    A.OneOf([\n        A.RandomBrightnessContrast(brightness_limit = (-0.2 , 0.2), contrast_limit = (-0.2 , 0.2)),\n        A.RandomRain(blur_value = 3, p = 0.2, brightness_coefficient = 0.8),\n        A.RandomFog(p = 0.3),\n        A.RandomShadow(p = 0.2)\n    ], p = 1),\n\n    # Some bluring and noising to image, to prevent overfitting \n    A.OneOf([\n        A.GaussianBlur(blur_limit = (3, 5) , p = 1),\n        A.ISONoise(p  = 1),\n        A.GaussNoise(std_range = (0.04, 0.1), p = 1),\n    ], p = 0.3),\n\n    # Random dropout from the image, must keep the count low since in real life , we don't want models to\n    # predict the mask from previous images, instead we want model correctly masking\n    A.CoarseDropout(num_holes_range = (3 , 5), \n                    hole_height_range = (0.05, 0.1), \n                    hole_width_range = (0.05, 0.3) , \n                    p = 0.1),\n\n    # Final touch\n    A.Normalize(),\n    A.pytorch.transforms.ToTensorV2()\n])\n\ntest_tf: A.Compose = A.Compose([\n    A.Resize(height = 256 , width = 256, interpolation = cv2.INTER_LINEAR, mask_interpolation = cv2.INTER_LINEAR),\n    A.CenterCrop(height = 224, width = 224, border_mode = cv2.BORDER_WRAP),\n    A.Normalize(),\n    A.pytorch.transforms.ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.009444Z","iopub.execute_input":"2026-04-04T08:01:42.009853Z","iopub.status.idle":"2026-04-04T08:01:42.031428Z","shell.execute_reply.started":"2026-04-04T08:01:42.009829Z","shell.execute_reply":"2026-04-04T08:01:42.030625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def denorm(image: torch.Tensor) -> torch.Tensor:\n    means = torch.Tensor([[[0.485, 0.456, 0.406]]]).permute(2, 1, 0)\n    stds = torch.Tensor([[[0.229, 0.224, 0.225]]]).permute(2, 1, 0)\n    return image * stds + means\n\nrandom_index = random.randint(0, len(TrainingDataset))\n\nimage, mask = TrainingDataset[random_index]\nfig_2, axes_2 = plt.subplots(2, 2)\naxes_2[0, 0].imshow(image, cmap = \"viridis\")\naxes_2[1, 0].imshow(mask, cmap = \"grey\")\n\nargmt = train_tf(image = image , mask = mask)\nimage = argmt[\"image\"]\nmask = argmt[\"mask\"]\n\naxes_2[0, 1].imshow(denorm(image).permute(1,2,0).numpy(), cmap = \"viridis\")\naxes_2[1, 1].imshow(mask.numpy(), cmap = \"grey\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.033089Z","iopub.execute_input":"2026-04-04T08:01:42.033638Z","iopub.status.idle":"2026-04-04T08:01:42.502749Z","shell.execute_reply.started":"2026-04-04T08:01:42.033614Z","shell.execute_reply":"2026-04-04T08:01:42.501960Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TrainingDataset.transform = train_tf\nValidationDataset.transform = test_tf","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.503787Z","iopub.execute_input":"2026-04-04T08:01:42.504091Z","iopub.status.idle":"2026-04-04T08:01:42.507831Z","shell.execute_reply.started":"2026-04-04T08:01:42.504067Z","shell.execute_reply":"2026-04-04T08:01:42.507064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.models import resnext50_32x4d, ResNeXt50_32X4D_Weights\nprint(resnext50_32x4d())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.508751Z","iopub.execute_input":"2026-04-04T08:01:42.509040Z","iopub.status.idle":"2026-04-04T08:01:42.747902Z","shell.execute_reply.started":"2026-04-04T08:01:42.509018Z","shell.execute_reply":"2026-04-04T08:01:42.741105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.748875Z","iopub.status.idle":"2026-04-04T08:01:42.749305Z","shell.execute_reply.started":"2026-04-04T08:01:42.749084Z","shell.execute_reply":"2026-04-04T08:01:42.749110Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch import nn\n\nclass AttentionLayer(nn.Module):\n\n    def __init__(self, n_inp_channels: int , n_skip_channels: int, n_hidden_channels: int):\n        super().__init__()\n        self.inp_conv = nn.Sequential(\n            nn.Conv2d(n_inp_channels, n_hidden_channels, kernel_size = 1, stride = 1, padding = 0),\n            nn.BatchNorm2d(n_hidden_channels),\n        )\n        self.skip_conv = nn.Sequential(\n            nn.Conv2d(n_skip_channels, n_hidden_channels, kernel_size = 1, stride = 1, padding = 0),\n            nn.BatchNorm2d(n_hidden_channels),\n        )\n        self.final_conv = nn.Sequential(\n            nn.Conv2d(n_hidden_channels, 1, kernel_size = 1, stride = 1, padding = 0),\n            nn.BatchNorm2d(1),\n            nn.Sigmoid()\n        )\n        self.elu = nn.ELU()\n        \n    def forward(self, inp: torch.Tensor, skip: torch.Tensor) -> torch.Tensor:\n\n        _inp = self.inp_conv(inp) # no modification inplace !\n        _skip = self.skip_conv(skip)\n        weight = self.elu(_inp + _skip)\n        weight = self.final_conv(weight)\n        return skip * weight\n\nclass DecoderBlock(nn.Module):\n\n    def __init__(self, n_inp_channels: int, n_skip_channels: int , n_out_channels: int ):\n\n        super().__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(n_inp_channels + n_skip_channels, \n                      n_out_channels, \n                      kernel_size = 3, \n                      stride = 1,\n                      padding = 1, \n                      bias = False),\n            nn.BatchNorm2d(n_out_channels),\n            nn.ELU(inplace = True),\n            \n            nn.Conv2d(n_out_channels, n_out_channels, \n                      kernel_size = 3,\n                      stride = 1,\n                      padding = 1,\n                      bias = False),\n            nn.BatchNorm2d(n_out_channels),\n            nn.ELU(inplace = True)\n        )\n        self.attention = AttentionLayer(n_inp_channels, \n                                        n_skip_channels, \n                                        (n_inp_channels + n_skip_channels)//2)\n        \n    def forward(self, inp: torch.Tensor, skip: torch.Tensor) -> torch.Tensor:\n\n        skip = self.attention(inp, skip)\n        inp = torch.cat([inp, skip], dim = 1)\n        inp = self.conv(inp)\n        return inp\n\n\nclass AttentionUnet(nn.Module):\n\n    def __init__(self):\n        super().__init__()\n        weights = ResNeXt50_32X4D_Weights.DEFAULT\n        self.backbone = resnext50_32x4d(weights = weights)\n        \n        self.backbone.layer4 = nn.Identity()\n        self.backbone.fc = nn.Identity()\n        \n        for param in self.backbone.parameters():\n            param.requires_grad = False\n            \n        for param in self.backbone.layer3.parameters():\n            param.requires_grad = True\n            \n        self.bottleneck = nn.Sequential(\n            nn.Conv2d(1024, 1024, kernel_size = 3, stride = 1, padding = 1, bias = False),\n            nn.BatchNorm2d(1024),\n            nn.ELU(inplace = True),\n            \n            nn.Conv2d(1024, 1024, kernel_size = 3, stride = 1, padding = 1, bias = False),\n            nn.BatchNorm2d(1024),\n            nn.ELU(inplace = True)\n        )\n\n        self.decoder_4 = DecoderBlock(1024, 1024, 512)\n        self.decoder_3 = DecoderBlock(512, 512, 256)\n        self.decoder_2 = DecoderBlock(256, 256, 64)\n        self.decoder_1 = DecoderBlock(64, 64, 16)\n\n        self.fc = nn.Conv2d(16, 1, kernel_size = 3, stride = 1, padding = 1, bias = True)\n        self.upsample = nn.Upsample(scale_factor = 2, mode = \"bilinear\", align_corners = True)\n\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n\n        b1 = self.backbone.conv1(x)   # 224 x 224 -> 112 x 112\n        b1 = self.backbone.bn1(b1)\n        b1 = self.backbone.relu(b1)\n        p1 = self.backbone.maxpool(b1) # 112 x 112 -> 56 x 56\n        \n        b2 = self.backbone.layer1(p1)  # 56 x 56 -> 56 x 56\n        b3 = self.backbone.layer2(b2)  # 56 x 56 -> 28 x 28\n        b4 = self.backbone.layer3(b3)  # 28 x 28 -> 14 x 14\n\n\n        x = self.bottleneck(b4)        # x.shape = 14 x 14\n\n        x = self.decoder_4(x, b4)\n        x = self.upsample(x)          # x.shape = 28 x 28\n        x = self.decoder_3(x, b3)\n        x = self.upsample(x)          # x.shape = 56 x 56\n        x = self.decoder_2(x, b2)\n        x = self.upsample(x)         # x.shape = 112 x 112\n        x = self.decoder_1(x, b1)\n        x = self.upsample(x)         # x.shape = 224 x 224\n        \n        return self.fc(x)\n        \ndef _DefaultSampleWeighter(masks: torch.Tensor) -> torch.Tensor:\n\n    with torch.no_grad():\n\n        weights = masks.shape[1]/masks.sum(dim = 1)\n        weights = torch.clamp(weights, min = 1, max = 10.0)\n    return weights\nclass SegmentLoss(nn.Module):\n\n    def __init__(self, gamma: float = 2.0,\n                       smoothing: float = 1.0,\n                       focal_weight: float = 2.0,\n                       iou_weight: float = 2.0,\n                       sample_weighter = _DefaultSampleWeighter):\n        super().__init__()\n        self.gamma = gamma\n        self.smoothing = smoothing\n        self.focal_weight = focal_weight\n        self.iou_weight = iou_weight\n        self.sample_weighter = sample_weighter\n\n        self.flatten = nn.Flatten()\n        self.sig     = nn.Sigmoid()\n        self.bce     = nn.BCEWithLogitsLoss(reduction = \"none\")\n\n    def forward(self, preds: torch.Tensor, targets: torch.Tensor) -> tuple[torch.Tensor, float, float]:\n\n        with torch.amp.autocast(\"cuda\", dtype = torch.float32):\n\n            # First of all, we need to flatten the targets and the preds: \n            # (B, 1, H, W) -> (B, H * W)\n            preds = self.flatten(preds)\n            targets = self.flatten(targets)\n\n            # Secondly, we will find the bce (reduction = \"none\")\n            bce  = self.bce(preds, targets)\n            pt = torch.exp(-bce)\n            focal_loss = ((1 - pt)**self.gamma * bce).mean(dim = 1)\n\n            # Turn logits into porbabilities (via sigmoid act func: 0..1)\n            preds = self.sig(preds)\n            alpha = torch.log(torch.abs((preds > 0.5).float() - targets).sum(dim = 1) + 1.0)\n            alpha = torch.clamp(alpha, min = 1.0, max = 5.0)\n\n            # IoU loss calculation\n            intersection = (preds * targets).sum(dim = 1)\n            total        = (preds + targets).sum(dim = 1)\n            union        = total - intersection\n            iou_loss     = 1 - (intersection + self.smoothing)/(union + self.smoothing)\n\n            # Track the raw losses , useful for later visualization\n            raw_focal    = focal_loss.mean().item()\n            raw_iou      = iou_loss.mean().item()\n\n            # Weighten the losses\n            focal_loss   *= self.focal_weight * alpha\n            iou_loss     *= self.iou_weight \n\n            # Find sample wise weights\n            if self.sample_weighter is not None:\n                weights      = self.sample_weighter(targets)\n            else:\n                weights      = torch.ones_like(focal_loss, device = device)\n            # Final weightening\n            loss         = ((focal_loss + iou_loss) * weights).mean()\n            \n            return loss, raw_iou, raw_focal","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.754330Z","iopub.status.idle":"2026-04-04T08:01:42.754743Z","shell.execute_reply.started":"2026-04-04T08:01:42.754525Z","shell.execute_reply":"2026-04-04T08:01:42.754550Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.nn.init import xavier_uniform_, zeros_\nfrom torch.optim import AdamW\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nfrom torch.amp import GradScaler\n\nnum_epochs = 100\ndef initLayer(layer: nn.Module) -> None:\n\n    if isinstance(layer, nn.Conv2d):\n        xavier_uniform_(layer.weight)\n        if layer.bias is not None:\n            zeros_(layer.bias)\n\nmodel: AttentionUnet = AttentionUnet()\nparam_group = [\n    {\"params\": model.backbone.layer3.parameters(), 'lr': 2e-5},\n    {\"params\": model.decoder_1.parameters(), 'lr': 5e-4},\n    {\"params\": model.decoder_2.parameters(), 'lr': 5e-4},\n    {\"params\": model.decoder_3.parameters(), 'lr': 5e-4},\n    {\"params\": model.decoder_4.parameters(), 'lr': 5e-4},\n    {\"params\": model.fc.parameters(), 'lr': 1e-3},\n]\noptimizer: AdamW = AdamW(param_group, lr = 1e-4, weight_decay = 0.003)\ncriterion: SegmentLoss = SegmentLoss()\nevaluator: SegmentLoss = SegmentLoss(sample_weighter = None)\nscheduler: CosineAnnealingLR = CosineAnnealingLR(optimizer, T_max = num_epochs, eta_min = 2e-5)\nscaler: GradScaler = GradScaler(\"cuda\")\n\nmodel.to(device)\ncriterion.to(device)\nevaluator.to(device)\n\nmodel.decoder_1.apply(initLayer)\nmodel.decoder_2.apply(initLayer)\nmodel.decoder_3.apply(initLayer)\nmodel.decoder_4.apply(initLayer)\nmodel.fc.apply(initLayer)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.755552Z","iopub.status.idle":"2026-04-04T08:01:42.760855Z","shell.execute_reply.started":"2026-04-04T08:01:42.760568Z","shell.execute_reply":"2026-04-04T08:01:42.760599Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\nbatch_size: int = 64\nTrainingLoader = DataLoader(TrainingDataset, \n                            batch_size = batch_size,\n                            num_workers = 4,\n                            pin_memory = True,\n                            shuffle = True,\n                            persistent_workers = True)\n\nValidationLoader = DataLoader(ValidationDataset,\n                             batch_size = batch_size,\n                             num_workers = 4,\n                             pin_memory = True,\n                             shuffle = True,\n                             persistent_workers = True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.762388Z","iopub.status.idle":"2026-04-04T08:01:42.762766Z","shell.execute_reply.started":"2026-04-04T08:01:42.762552Z","shell.execute_reply":"2026-04-04T08:01:42.762574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm as ProgressBar\n\nEpochHistory: dict = {\n    \"Train:Iou\": [],\n    \"Train:Focal\": [],\n    \"Train:WeightedLoss\": [],\n\n    \"Val:Iou\": [],\n    \"Val:Focal\": [],\n    \"Val:WeightedLoss\": []\n}\nbest_loss: float = float(\"inf\")\n\nfor epoch_no in range(num_epochs):\n\n    print(\"\\n---------------------------------------------\\n\")\n    print(f\"Epoch no. {epoch_no} started! With reference lr(model.fc): {optimizer.param_groups[5]['lr']}\")\n    model.train(True)\n    pbar_t = ProgressBar(TrainingLoader)\n    \n    train_iou: float = 0.0\n    train_focal: float = 0.0\n    train_wloss: float = 0.0\n\n    for batch_idx , (imgs, masks) in enumerate(pbar_t):\n        \n        optimizer.zero_grad()\n        imgs = imgs.to(device)\n        masks = masks.to(device)\n        \n        with torch.amp.autocast(\"cuda\", dtype = torch.float16):\n            preds = model(imgs)\n            loss, iou, focal = criterion(preds, masks)\n            \n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n            \n        train_iou += iou/len(TrainingLoader)\n        train_focal += focal/len(TrainingLoader)\n        train_wloss += loss.item()/len(TrainingLoader)\n        \n        pbar_t.set_postfix({\n            \"Batch No.\": batch_idx,\n            \"Focal\": f\"{focal: .3f}\",\n            \"Iou\": f\"{iou: .3f}\",\n            \"Weighted\": f\"{loss.item(): .3f}\",\n        })\n        \n    EpochHistory[\"Train:Iou\"].append(train_iou)\n    EpochHistory[\"Train:Focal\"].append(train_focal)\n    EpochHistory[\"Train:WeightedLoss\"].append(train_wloss)\n    print(f\"Training finished with training iou: {train_iou: .3f}, focal: {train_focal: .3f}, wloss: {train_wloss: .3f}\")\n    model.eval()\n    pbar_v = ProgressBar(ValidationLoader)\n    \n    val_iou: float = 0.0\n    val_focal: float = 0.0\n    val_wloss: float = 0.0\n\n    with torch.no_grad():\n         for batch_idx, (imgs, masks) in enumerate(pbar_v):\n             \n             imgs = imgs.to(device)\n             masks = masks.to(device)\n             \n             with torch.amp.autocast(\"cuda\", dtype = torch.float16):\n                 preds = model(imgs)\n                 loss, iou, focal = evaluator(preds, masks)\n\n             val_iou += iou/len(ValidationLoader)\n             val_focal += focal/len(ValidationLoader)\n             val_wloss += loss.item()/len(ValidationLoader)\n             \n             pbar_v.set_postfix({\n                \"Batch No.\": batch_idx,\n                \"Focal\": f\"{focal: .3f}\",\n                \"Iou\": f\"{iou: .3f}\",\n                \"Weighted\": f\"{loss.item(): .3f}\",\n             })\n             \n         EpochHistory[\"Val:Iou\"].append(val_iou)\n         EpochHistory[\"Val:Focal\"].append(val_focal)\n         EpochHistory[\"Val:WeightedLoss\"].append(val_wloss)\n         print(f\"Validation finished with training iou: {val_iou: .3f}, focal: {val_focal: .3f}, wloss: {val_wloss: .3f}\")\n         if best_loss > val_wloss:\n             best_loss = val_wloss\n             checkpoint: dict = {\n                 \"model_state_dict\": model.state_dict(),\n                 \"optimizer_state_dict\": optimizer.state_dict(),\n                 \"scheduler_state_dict\": scheduler.state_dict(),\n                 \"epoch_no\": epoch_no,\n                 \"loss\" : best_loss\n             }\n             torch.save(checkpoint, \"best_model.pth\")\n             print(\"New best model found!\")\n    scheduler.step()\n    print(\"\\n---------------------------------------------\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:01:42.767628Z","iopub.status.idle":"2026-04-04T08:01:42.768369Z","shell.execute_reply.started":"2026-04-04T08:01:42.768152Z","shell.execute_reply":"2026-04-04T08:01:42.768179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\n\nfig_3, axes_3 = plt.subplots(2, 2, facecolor = '#1e1e1e')\nxVals = list(range(num_epochs))\n\n\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Train:Iou\",\n    size = 15,\n    color= \"blue\",\n    ax = axes_3[0, 0],\n    zorder = 3\n)\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Val:Iou\",\n    size = 15,\n    color=  \"#ff5f03\",\n    ax = axes_3[0, 0],\n    zorder = 3\n)\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Train:Focal\",\n    size = 15,\n    color= \"blue\",\n    ax = axes_3[1, 0],\n    zorder = 3\n)\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Val:Focal\",\n    size = 15,\n    color=  \"#ff5f03\",\n    ax = axes_3[1, 0],\n    zorder = 3\n)\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Train:WeightedLoss\",\n    size = 15,\n    color= \"blue\",\n    ax = axes_3[0, 1],\n    zorder = 3\n)\nsns.lineplot(\n    data = EpochHistory,\n    x    = xVals,\n    y    = \"Val:WeightedLoss\",\n    size = 15,\n    color=  \"#ff5f03\",\n    ax = axes_3[0, 1],\n    zorder = 3\n)\n\nfor i in range(2):\n    for j in range(2):\n        axes_3[i, j].grid(True, zorder = 2, color = \"white\", linestyle = '-', linewidth = 0.8)\n        axes_3[i, j].set_facecolor(\"#d6d4d4\")\n        axes_3[i, j].tick_params(axis = \"x\", colors = \"#c7c7c7\")\n        axes_3[i, j].tick_params(axis = \"y\", colors = \"#c7c7c7\")\n\naxes_3[0, 0].set_xlabel(\"Epoch\", color = \"cyan\")\naxes_3[0, 0].set_ylabel(\"Iou Loss\", color = \"cyan\")\n\naxes_3[1, 0].set_xlabel(\"Epoch\", color = \"cyan\")\naxes_3[1, 0].set_ylabel(\"Focal Loss\", color = \"cyan\")\n\naxes_3[0, 1].set_xlabel(\"Epoch\", color = \"cyan\")\naxes_3[0, 1].set_ylabel(\"Weighted Loss\", color = \"cyan\")\n\nplt.tight_layout()\nplt.savefig(\"epoch_history.png\")\nplt.show()\n\ntorch.save(EpochHistory, \"epoch_history.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:25:01.234128Z","iopub.execute_input":"2026-04-04T08:25:01.234866Z","iopub.status.idle":"2026-04-04T08:25:01.929096Z","shell.execute_reply.started":"2026-04-04T08:25:01.234835Z","shell.execute_reply":"2026-04-04T08:25:01.928259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"checkpoint = torch.load(\"best_model.pth\")\n\neval_model: AttentionUnet = AttentionUnet()\neval_model.load_state_dict(checkpoint[\"model_state_dict\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:30:33.866478Z","iopub.execute_input":"2026-04-04T08:30:33.866932Z","iopub.status.idle":"2026-04-04T08:30:34.921828Z","shell.execute_reply.started":"2026-04-04T08:30:33.866902Z","shell.execute_reply":"2026-04-04T08:30:34.921251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eval_model.to(device)\neval_model.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:30:48.005062Z","iopub.execute_input":"2026-04-04T08:30:48.005466Z","iopub.status.idle":"2026-04-04T08:30:48.075005Z","shell.execute_reply.started":"2026-04-04T08:30:48.005440Z","shell.execute_reply":"2026-04-04T08:30:48.074387Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\ntest_root_path = \"/kaggle/input/competitions/flood-image-segmentation-ml-4-im-2025-2026/Testing/Images\"\nimage_paths = sorted(os.listdir(test_root_path))\n\nimage_ids: list[int] = []\npreds    : list[np.ndarray] = []\nfor image_path in tqdm(image_paths):\n    image_id = int(image_path.split('.')[0])\n    image_ids.append(image_ids)\n    img = cv2.imread(os.path.join(test_root_path, image_path), cv2.IMREAD_COLOR_RGB)\n    img = test_tf(image = img)[\"image\"].unsqueeze(0)\n    img = img.to(device)\n     \n    pred = eval_model(img)\n    pred = (pred > 0.5).cpu().squeeze(dim = (0, 1)).numpy().astype(np.uint8)\n    pred = cv2.resize(pred, (512, 512), interpolation = cv2.INTER_NEAREST_EXACT)\n    preds.append(pred)\nprint(f\"Shape of the first element in preds: {preds[0].shape}\")\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:59:49.981446Z","iopub.execute_input":"2026-04-04T08:59:49.982246Z","iopub.status.idle":"2026-04-04T08:59:57.539218Z","shell.execute_reply.started":"2026-04-04T08:59:49.982203Z","shell.execute_reply":"2026-04-04T08:59:57.538333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\ndef rle_encode(image: np.ndarray) -> str:\n    \"\"\"\n    Encode a binary mask image using Run-Length Encoding (RLE) as used in Kaggle competitions.\n    Args:\n        image (np.ndarray): 2D binary mask (1 for mask, 0 for background)\n    Returns:\n        str: RLE encoded string\n    \"\"\"\n    # Flatten the image column-wise (For Kaggle masks)\n    pixels = image.T.flatten()\n    # Pad with zeros at start and end\n    pixels = np.concatenate([[0], pixels, [0]])\n    # Find where pixel values change\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    # Compute run lengths\n    runs[1::2] -= runs[::2]\n    # Convert to space-separated string\n    return ' '.join(str(x) for x in runs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T09:10:00.513397Z","iopub.execute_input":"2026-04-04T09:10:00.513776Z","iopub.status.idle":"2026-04-04T09:10:00.519382Z","shell.execute_reply.started":"2026-04-04T09:10:00.513675Z","shell.execute_reply":"2026-04-04T09:10:00.518732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rle_encoded = []\nfor pred in tqdm(preds):\n    rle_encoded.append(rle_encode(pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T09:10:02.640502Z","iopub.execute_input":"2026-04-04T09:10:02.641358Z","iopub.status.idle":"2026-04-04T09:10:02.954858Z","shell.execute_reply.started":"2026-04-04T09:10:02.641295Z","shell.execute_reply":"2026-04-04T09:10:02.954091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result = pd.DataFrame({\n    \"id\": image_ids,\n    \"rle\": rle_encoded\n})\n\nresult.to_csv(\"result.csv\", index = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T09:11:01.928570Z","iopub.execute_input":"2026-04-04T09:11:01.929418Z","iopub.status.idle":"2026-04-04T09:11:02.034328Z","shell.execute_reply.started":"2026-04-04T09:11:01.929387Z","shell.execute_reply":"2026-04-04T09:11:02.033614Z"}},"outputs":[],"execution_count":null}]}