{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":9988,"databundleVersionId":868324,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport cv2\nfrom sklearn.model_selection import train_test_split\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimport torch.nn.functional as F\nfrom torch.optim.swa_utils import AveragedModel, SWALR\nimport torchvision.models as models\nfrom torch.cuda.amp import autocast, GradScaler","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:17.219286Z","iopub.execute_input":"2025-02-23T07:09:17.219619Z","iopub.status.idle":"2025-02-23T07:09:26.022511Z","shell.execute_reply.started":"2025-02-23T07:09:17.219573Z","shell.execute_reply":"2025-02-23T07:09:26.021487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def set_seed(seed=42):\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    \nset_seed()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.023622Z","iopub.execute_input":"2025-02-23T07:09:26.024037Z","iopub.status.idle":"2025-02-23T07:09:26.033142Z","shell.execute_reply.started":"2025-02-23T07:09:26.024013Z","shell.execute_reply":"2025-02-23T07:09:26.032159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Config:\n    TRAIN_PATH = '/kaggle/input/airbus-ship-detection/train_v2'\n    TEST_PATH = '/kaggle/input/airbus-ship-detection/test_v2'\n    TRAIN_CSV = '/kaggle/input/airbus-ship-detection/train_ship_segmentations_v2.csv'\n    \n    DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    \n    # Training parameters\n    BATCH_SIZE = 8\n    EPOCHS = 70\n    LEARNING_RATE = 0.001\n    IMG_SIZE = 384\n    NUM_WORKERS = 4\n    \n    # Validation split\n    VAL_SPLIT = 0.2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.034918Z","iopub.execute_input":"2025-02-23T07:09:26.035156Z","iopub.status.idle":"2025-02-23T07:09:26.102326Z","shell.execute_reply.started":"2025-02-23T07:09:26.035134Z","shell.execute_reply":"2025-02-23T07:09:26.101669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def rle_encode(img):\n    \"\"\"Convert mask to run-length encoding.\"\"\"\n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.104059Z","iopub.execute_input":"2025-02-23T07:09:26.104466Z","iopub.status.idle":"2025-02-23T07:09:26.1195Z","shell.execute_reply.started":"2025-02-23T07:09:26.104422Z","shell.execute_reply":"2025-02-23T07:09:26.118724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def rle_decode(mask_rle, shape=(768, 768)):\n    \"\"\"Decode RLE encoded mask.\"\"\"\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0] * shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.120211Z","iopub.execute_input":"2025-02-23T07:09:26.120463Z","iopub.status.idle":"2025-02-23T07:09:26.134531Z","shell.execute_reply.started":"2025-02-23T07:09:26.120442Z","shell.execute_reply":"2025-02-23T07:09:26.133745Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ShipDataset(Dataset):\n    def __init__(self, df, img_path, transform=None, train=True):\n        self.df = df\n        self.img_path = img_path\n        self.transform = transform\n        self.train = train\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        img_id = self.df.iloc[idx]['ImageId']\n        img_path = os.path.join(self.img_path, img_id)\n        \n        # Load image\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        if self.train:\n            # Create mask\n            mask_rle = self.df.iloc[idx]['EncodedPixels']\n            if isinstance(mask_rle, str):\n                mask = rle_decode(mask_rle)\n            else:\n                mask = np.zeros((768, 768))\n                \n            # Apply transformations\n            if self.transform:\n                augmented = self.transform(image=img, mask=mask)\n                img = augmented['image']\n                mask = augmented['mask']\n            \n            return img, mask.float()\n        else:\n            if self.transform:\n                augmented = self.transform(image=img)\n                img = augmented['image']\n            return img\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.135335Z","iopub.execute_input":"2025-02-23T07:09:26.135625Z","iopub.status.idle":"2025-02-23T07:09:26.150648Z","shell.execute_reply.started":"2025-02-23T07:09:26.135603Z","shell.execute_reply":"2025-02-23T07:09:26.149841Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_training_augmentation():\n    return A.Compose([\n        A.RandomResizedCrop(Config.IMG_SIZE, Config.IMG_SIZE, scale=(0.8, 1.0)),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.RandomRotate90(p=0.5),\n        A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.1, rotate_limit=45, p=0.5),\n        A.OneOf([\n            A.ElasticTransform(alpha=120, sigma=120 * 0.05, alpha_affine=120 * 0.03),\n            A.GridDistortion(),\n            A.OpticalDistortion(distort_limit=1, shift_limit=0.5),\n        ], p=0.3),\n        A.OneOf([\n            A.GaussNoise(),\n            A.RandomBrightnessContrast(),\n            A.RandomGamma(),\n        ], p=0.3),\n        A.Normalize(),\n        ToTensorV2(),\n    ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.151398Z","iopub.execute_input":"2025-02-23T07:09:26.151678Z","iopub.status.idle":"2025-02-23T07:09:26.167973Z","shell.execute_reply.started":"2025-02-23T07:09:26.151658Z","shell.execute_reply":"2025-02-23T07:09:26.167112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_validation_augmentation():\n    return A.Compose([\n        A.Resize(Config.IMG_SIZE, Config.IMG_SIZE),\n        A.Normalize(),\n        ToTensorV2(),\n    ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.170376Z","iopub.execute_input":"2025-02-23T07:09:26.170597Z","iopub.status.idle":"2025-02-23T07:09:26.182043Z","shell.execute_reply.started":"2025-02-23T07:09:26.170578Z","shell.execute_reply":"2025-02-23T07:09:26.181319Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def prepare_data():\n    # Read CSV\n    df = pd.read_csv(Config.TRAIN_CSV)\n    \n    # Group by ImageId to handle multiple ships\n    df_grouped = df.groupby('ImageId')['EncodedPixels'].agg(lambda x: list(x)).reset_index()\n    df_grouped['has_ship'] = df_grouped['EncodedPixels'].map(lambda x: 1 if isinstance(x[0], str) else 0)\n    \n    # Balance dataset\n    df_with_ships = df_grouped[df_grouped['has_ship'] == 1]\n    df_without_ships = df_grouped[df_grouped['has_ship'] == 0].sample(n=len(df_with_ships))\n    df_balanced = pd.concat([df_with_ships, df_without_ships]).reset_index(drop=True)\n    \n    # Split data\n    train_df, val_df = train_test_split(\n        df_balanced, \n        test_size=Config.VAL_SPLIT, \n        stratify=df_balanced['has_ship'],\n        random_state=42\n    )\n    \n    return train_df, val_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.183686Z","iopub.execute_input":"2025-02-23T07:09:26.183884Z","iopub.status.idle":"2025-02-23T07:09:26.19901Z","shell.execute_reply.started":"2025-02-23T07:09:26.183867Z","shell.execute_reply":"2025-02-23T07:09:26.198313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AverageMeter:\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n        \n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n        \n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.19993Z","iopub.execute_input":"2025-02-23T07:09:26.200213Z","iopub.status.idle":"2025-02-23T07:09:26.213139Z","shell.execute_reply.started":"2025-02-23T07:09:26.200185Z","shell.execute_reply":"2025-02-23T07:09:26.212499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_metrics(pred, target):\n    pred_binary = (pred > 0.5).float()\n    \n    # Calculate Dice score\n    intersection = (pred_binary * target).sum()\n    dice = (2. * intersection) / (pred_binary.sum() + target.sum() + 1e-6)\n    \n    # Calculate IoU\n    union = pred_binary.sum() + target.sum() - intersection\n    iou = intersection / (union + 1e-6)\n    \n    return {\n        'dice': dice.item(),\n        'iou': iou.item()\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.213814Z","iopub.execute_input":"2025-02-23T07:09:26.214029Z","iopub.status.idle":"2025-02-23T07:09:26.226303Z","shell.execute_reply.started":"2025-02-23T07:09:26.21401Z","shell.execute_reply":"2025-02-23T07:09:26.225589Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MetricsTracker:\n    def __init__(self):\n        self.train_losses = []\n        self.val_losses = []\n        self.train_dice_scores = []\n        self.val_dice_scores = []\n        self.train_ious = []\n        self.val_ious = []\n        \n    def update(self, train_metrics, val_metrics):\n        self.train_losses.append(train_metrics['loss'])\n        self.val_losses.append(val_metrics['loss'])\n        self.train_dice_scores.append(train_metrics['dice'])\n        self.val_dice_scores.append(val_metrics['dice'])\n        self.train_ious.append(train_metrics['iou'])\n        self.val_ious.append(val_metrics['iou'])\n        \n    def plot_metrics(self):\n        epochs = range(1, len(self.train_losses) + 1)\n        \n        fig, axes = plt.subplots(1, 3, figsize=(20, 5))\n        \n        # Plot losses\n        axes[0].plot(epochs, self.train_losses, 'b-', label='Training Loss')\n        axes[0].plot(epochs, self.val_losses, 'r-', label='Validation Loss')\n        axes[0].set_title('Training and Validation Loss')\n        axes[0].set_xlabel('Epoch')\n        axes[0].set_ylabel('Loss')\n        axes[0].legend()\n        \n        # Plot Dice scores\n        axes[1].plot(epochs, self.train_dice_scores, 'b-', label='Training Dice')\n        axes[1].plot(epochs, self.val_dice_scores, 'r-', label='Validation Dice')\n        axes[1].set_title('Training and Validation Dice Score')\n        axes[1].set_xlabel('Epoch')\n        axes[1].set_ylabel('Dice Score')\n        axes[1].legend()\n        \n        # Plot IoU scores\n        axes[2].plot(epochs, self.train_ious, 'b-', label='Training IoU')\n        axes[2].plot(epochs, self.val_ious, 'r-', label='Validation IoU')\n        axes[2].set_title('Training and Validation IoU')\n        axes[2].set_xlabel('Epoch')\n        axes[2].set_ylabel('IoU Score')\n        axes[2].legend()\n        \n        plt.tight_layout()\n        plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.227117Z","iopub.execute_input":"2025-02-23T07:09:26.227388Z","iopub.status.idle":"2025-02-23T07:09:26.241543Z","shell.execute_reply.started":"2025-02-23T07:09:26.227359Z","shell.execute_reply":"2025-02-23T07:09:26.240944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MixedLoss(nn.Module):\n    def __init__(self, bce_weight=0.5, dice_weight=0.3, focal_weight=0.2, gamma=2.0):\n        super(MixedLoss, self).__init__()\n        self.bce_weight = bce_weight\n        self.dice_weight = dice_weight\n        self.focal_weight = focal_weight\n        self.gamma = gamma\n        \n    def forward(self, pred, target):\n        if pred.shape[1] == 1:\n            pred = pred.squeeze(1)\n            \n        # BCE Loss\n        bce_loss = F.binary_cross_entropy_with_logits(pred, target)\n        \n        # Dice Loss\n        pred_sigmoid = torch.sigmoid(pred)\n        intersection = (pred_sigmoid * target).sum()\n        union = pred_sigmoid.sum() + target.sum()\n        dice_loss = 1 - (2 * intersection + 1) / (union + 1)\n        \n        # Focal Loss\n        pt = torch.exp(-bce_loss)\n        focal_loss = ((1 - pt) ** self.gamma) * bce_loss\n        \n        return (self.bce_weight * bce_loss + \n                self.dice_weight * dice_loss + \n                self.focal_weight * focal_loss)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.242307Z","iopub.execute_input":"2025-02-23T07:09:26.242588Z","iopub.status.idle":"2025-02-23T07:09:26.261764Z","shell.execute_reply.started":"2025-02-23T07:09:26.242546Z","shell.execute_reply":"2025-02-23T07:09:26.261142Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AttentionBlock(nn.Module):\n    def __init__(self, in_channels, num_heads=8):\n        super(AttentionBlock, self).__init__()\n        self.num_heads = num_heads\n        self.head_dim = in_channels // num_heads\n        assert self.head_dim * num_heads == in_channels, \"in_channels must be divisible by num_heads\"\n        \n        self.qkv = nn.Conv2d(in_channels, in_channels * 3, kernel_size=1)\n        self.proj = nn.Conv2d(in_channels, in_channels, kernel_size=1)\n        self.scale = self.head_dim ** -0.5\n        \n    def forward(self, x):\n        B, C, H, W = x.shape\n        \n        # Generate Q, K, V\n        qkv = self.qkv(x).reshape(B, 3, self.num_heads, self.head_dim, H * W)\n        q, k, v = qkv.unbind(1)\n        \n        # Scaled dot-product attention\n        attn = (q @ k.transpose(-2, -1)) * self.scale\n        attn = attn.softmax(dim=-1)\n        \n        # Apply attention to V\n        x = (attn @ v).reshape(B, C, H, W)\n        x = self.proj(x)\n        \n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.26247Z","iopub.execute_input":"2025-02-23T07:09:26.262731Z","iopub.status.idle":"2025-02-23T07:09:26.281517Z","shell.execute_reply.started":"2025-02-23T07:09:26.262711Z","shell.execute_reply":"2025-02-23T07:09:26.280806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SkipConnectionBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, stride=1):\n        super(SkipConnectionBlock, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n        \n        if in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_channels, out_channels, kernel_size=1),\n                nn.BatchNorm2d(out_channels)\n            )\n        else:\n            self.shortcut = nn.Identity()\n            \n    def forward(self, x):\n        identity = self.shortcut(x)\n        \n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n        out += identity\n        out = F.relu(out)\n        \n        return out\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.282138Z","iopub.execute_input":"2025-02-23T07:09:26.2824Z","iopub.status.idle":"2025-02-23T07:09:26.29506Z","shell.execute_reply.started":"2025-02-23T07:09:26.282379Z","shell.execute_reply":"2025-02-23T07:09:26.294313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DualPathBlock(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(DualPathBlock, self).__init__()\n        \n        self.main_path = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_channels),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_channels)\n        )\n        \n        self.residual_path = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, kernel_size=1),\n            nn.BatchNorm2d(out_channels)\n        )\n        \n        self.attention = AttentionBlock(out_channels)\n        \n    def forward(self, x):\n        main = self.main_path(x)\n        residual = self.residual_path(x)\n        combined = main + residual\n        attended = self.attention(combined)\n        return F.relu(attended)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.295919Z","iopub.execute_input":"2025-02-23T07:09:26.296227Z","iopub.status.idle":"2025-02-23T07:09:26.313449Z","shell.execute_reply.started":"2025-02-23T07:09:26.296173Z","shell.execute_reply":"2025-02-23T07:09:26.312494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PyramidPoolingModule(nn.Module):\n    def __init__(self, in_channels):\n        super(PyramidPoolingModule, self).__init__()\n        self.pools = nn.ModuleList([\n            nn.AdaptiveAvgPool2d(bin_size) for bin_size in [(1, 1), (2, 2), (3, 3), (6, 6)]\n        ])\n        # Reduce output channels to maintain proper dimensionality\n        self.convs = nn.ModuleList([\n            nn.Conv2d(in_channels, in_channels // 8, kernel_size=1) for _ in range(4)\n        ])\n        \n    def forward(self, x):\n        features = [x]\n        h, w = x.shape[2:]\n        \n        for pool, conv in zip(self.pools, self.convs):\n            feat = pool(x)\n            feat = conv(feat)\n            feat = F.interpolate(feat, size=(h, w), mode='bilinear', align_corners=True)\n            features.append(feat)\n            \n        return torch.cat(features, dim=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.314334Z","iopub.execute_input":"2025-02-23T07:09:26.31459Z","iopub.status.idle":"2025-02-23T07:09:26.328454Z","shell.execute_reply.started":"2025-02-23T07:09:26.314569Z","shell.execute_reply":"2025-02-23T07:09:26.327629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class UNet(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1):\n        super(UNet, self).__init__()\n        \n        # Encoder\n        self.enc1 = DualPathBlock(in_channels, 64)\n        self.enc2 = DualPathBlock(64, 128)\n        self.enc3 = DualPathBlock(128, 256)\n        self.enc4 = DualPathBlock(256, 512)\n        \n        # Bridge with PyramidPoolingModule\n        self.bridge = PyramidPoolingModule(512)\n        bridge_channels = 512 + (512 // 8) * 4  # Main features + 4 pooling branches\n        \n        # Decoder with corrected channel dimensions\n        self.dec1 = DualPathBlock(bridge_channels + 256, 256)\n        self.dec2 = DualPathBlock(256 + 128, 128)  # Fixed: Changed from 256 * 2\n        self.dec3 = DualPathBlock(128 + 64, 64)    # Fixed: Changed from 128 * 2\n        \n        self.final = nn.Conv2d(64, out_channels, kernel_size=1)\n        self.dropout = nn.Dropout2d(0.5)\n        \n    def forward(self, x):\n        # Encoder path\n        e1 = self.enc1(x)\n        e2 = self.enc2(F.max_pool2d(e1, 2))\n        e3 = self.enc3(F.max_pool2d(e2, 2))\n        e4 = self.enc4(F.max_pool2d(e3, 2))\n        \n        # Bridge\n        bridge = self.bridge(e4)\n        \n        # Decoder path with skip connections\n        # Upsample bridge and concatenate with e3\n        d1 = F.interpolate(bridge, size=e3.shape[2:], mode='bilinear', align_corners=True)\n        d1 = torch.cat([d1, e3], dim=1)\n        d1 = self.dec1(d1)\n        \n        # Upsample d1 and concatenate with e2\n        d2 = F.interpolate(d1, size=e2.shape[2:], mode='bilinear', align_corners=True)\n        d2 = torch.cat([d2, e2], dim=1)\n        d2 = self.dec2(d2)\n        \n        # Upsample d2 and concatenate with e1\n        d3 = F.interpolate(d2, size=e1.shape[2:], mode='bilinear', align_corners=True)\n        d3 = torch.cat([d3, e1], dim=1)\n        d3 = self.dec3(d3)\n        \n        return self.final(self.dropout(d3))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.329292Z","iopub.execute_input":"2025-02-23T07:09:26.329521Z","iopub.status.idle":"2025-02-23T07:09:26.346902Z","shell.execute_reply.started":"2025-02-23T07:09:26.329502Z","shell.execute_reply":"2025-02-23T07:09:26.346161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResNet(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1):\n        super(ResNet, self).__init__()\n        \n        # Initial conv\n        self.initial = nn.Sequential(\n            nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),\n            nn.BatchNorm2d(64),\n            nn.ReLU()\n        )\n        \n        # ResNet blocks\n        self.layer1 = self._make_layer(64, 64, 3)\n        self.layer2 = self._make_layer(64, 128, 4, stride=2)\n        self.layer3 = self._make_layer(128, 256, 6, stride=2)\n        \n        # Attention blocks\n        self.attention1 = AttentionBlock(256)\n        self.attention2 = AttentionBlock(128)\n        \n        # Decoder blocks\n        self.decoder1 = SkipConnectionBlock(256, 128)\n        self.decoder2 = SkipConnectionBlock(128, 64)\n        \n        # Final layer\n        self.final = nn.Conv2d(64, out_channels, kernel_size=1)\n        \n    def _make_layer(self, in_channels, out_channels, blocks, stride=1):\n        layers = [SkipConnectionBlock(in_channels, out_channels, stride)]\n        for _ in range(1, blocks):\n            layers.append(SkipConnectionBlock(out_channels, out_channels))\n        return nn.Sequential(*layers)\n        \n    def forward(self, x):\n        # Initial conv\n        x = self.initial(x)\n        \n        # ResNet blocks\n        x1 = self.layer1(x)\n        x2 = self.layer2(x1)\n        x3 = self.layer3(x2)\n        \n        # Apply attention\n        x3 = self.attention1(x3)\n        \n        # Decoder with attention\n        x = F.interpolate(x3, scale_factor=2)\n        x = self.decoder1(x)\n        x = self.attention2(x)\n        \n        x = F.interpolate(x, scale_factor=2)\n        x = self.decoder2(x)\n        \n        x = F.interpolate(x, scale_factor=2)\n        return self.final(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.34769Z","iopub.execute_input":"2025-02-23T07:09:26.347927Z","iopub.status.idle":"2025-02-23T07:09:26.36396Z","shell.execute_reply.started":"2025-02-23T07:09:26.347898Z","shell.execute_reply":"2025-02-23T07:09:26.363193Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DenseNet(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1, growth_rate=32):\n        super(DenseNet, self).__init__()\n        \n        # Initial convolution\n        self.features = nn.Sequential(\n            nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),\n            nn.BatchNorm2d(64),\n            nn.ReLU()\n        )\n        \n        # Dense blocks\n        self.denseblock1 = self._make_dense_block(64, growth_rate, 6)\n        self.denseblock2 = self._make_dense_block(64 + 6 * growth_rate, growth_rate, 12)\n        self.denseblock3 = self._make_dense_block(64 + 18 * growth_rate, growth_rate, 24)\n        \n        # Attention blocks\n        self.attention1 = AttentionBlock(64 + 42 * growth_rate)\n        self.attention2 = AttentionBlock(64 + 18 * growth_rate)\n        \n        # Decoder blocks\n        self.decoder1 = SkipConnectionBlock(64 + 42 * growth_rate, 256)\n        self.decoder2 = SkipConnectionBlock(256, 128)\n        self.decoder3 = SkipConnectionBlock(128, 64)\n        \n        # Final layer\n        self.final = nn.Conv2d(64, out_channels, kernel_size=1)\n        \n    def _make_dense_block(self, in_channels, growth_rate, num_layers):\n        layers = []\n        for i in range(num_layers):\n            layers.append(\n                nn.Sequential(\n                    nn.BatchNorm2d(in_channels + i * growth_rate),\n                    nn.ReLU(),\n                    nn.Conv2d(in_channels + i * growth_rate, growth_rate, kernel_size=3, padding=1)\n                )\n            )\n        return nn.ModuleList(layers)\n        \n    def forward(self, x):\n        features = self.features(x)\n        \n        # Dense blocks with skip connections\n        skip1 = features\n        for layer in self.denseblock1:\n            features = torch.cat([features, layer(features)], dim=1)\n            \n        skip2 = features\n        for layer in self.denseblock2:\n            features = torch.cat([features, layer(features)], dim=1)\n            \n        for layer in self.denseblock3:\n            features = torch.cat([features, layer(features)], dim=1)\n            \n        # Apply attention\n        features = self.attention1(features)\n        \n        # Decoder with attention\n        x = self.decoder1(features)\n        x = self.attention2(x)\n        x = self.decoder2(x)\n        x = self.decoder3(x)\n        \n        return self.final(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.364698Z","iopub.execute_input":"2025-02-23T07:09:26.364924Z","iopub.status.idle":"2025-02-23T07:09:26.376761Z","shell.execute_reply.started":"2025-02-23T07:09:26.364905Z","shell.execute_reply":"2025-02-23T07:09:26.376003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DeepLabV3Plus(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1):\n        super(DeepLabV3Plus, self).__init__()\n        \n        # Use ResNet50 as backbone\n        backbone = models.resnet50(pretrained=True)\n        self.initial = nn.Sequential(*list(backbone.children())[:4])\n        self.layer1 = backbone.layer1\n        self.layer2 = backbone.layer2\n        self.layer3 = backbone.layer3\n        self.layer4 = backbone.layer4\n        \n        # ASPP module\n        self.aspp = PyramidPoolingModule(2048)\n        # Calculate correct number of channels after ASPP\n        aspp_channels = 2048 + (2048 // 8) * 4  # Main features + 4 pooling branches\n        \n        # Adjust attention block to match ASPP output channels\n        self.attention = AttentionBlock(aspp_channels)\n        \n        # Decoder with adjusted channels\n        self.decoder = nn.Sequential(\n            DualPathBlock(aspp_channels, 256),\n            DualPathBlock(256, 128),\n            DualPathBlock(128, 64)\n        )\n        \n        self.final = nn.Conv2d(64, out_channels, kernel_size=1)\n        \n    def forward(self, x):\n        # Backbone\n        x = self.initial(x)\n        low_level = self.layer1(x)\n        x = self.layer2(low_level)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        \n        # ASPP\n        x = self.aspp(x)  # This increases the number of channels\n        \n        # Attention with matched channels\n        x = self.attention(x)\n        \n        # Decoder\n        x = F.interpolate(x, scale_factor=4, mode='bilinear', align_corners=True)\n        x = self.decoder(x)\n        x = F.interpolate(x, scale_factor=4, mode='bilinear', align_corners=True)\n        \n        return self.final(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.377503Z","iopub.execute_input":"2025-02-23T07:09:26.377719Z","iopub.status.idle":"2025-02-23T07:09:26.39342Z","shell.execute_reply.started":"2025-02-23T07:09:26.377691Z","shell.execute_reply":"2025-02-23T07:09:26.392625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self):\n        super(Model, self).__init__()\n        \n        # Multiple model architectures\n        self.unet = UNet()\n        self.deeplabv3 = DeepLabV3Plus()\n        # self.resnet = ResNet()  \n        # self.densenet = DenseNet()  \n        \n        # Learnable weights for ensemble\n        self.weights = nn.Parameter(torch.ones(2) / 2)\n        \n        # Feature fusion module\n        self.fusion = nn.Sequential(\n            nn.Conv2d(2, 16, kernel_size=3, padding=1), \n            nn.BatchNorm2d(16),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(16, 1, kernel_size=1)\n        )\n        \n        # Loss function\n        self.criterion = MixedLoss()\n        \n    def forward(self, x):\n        # Get predictions from each model\n        pred1 = self.unet(x)\n        pred2 = self.deeplabv3(x)\n        # pred3 = self.resnet(x)\n        # pred4 = self.densenet(x)\n        \n        pred2 = F.interpolate(pred2, \n                            size=pred1.shape[2:],  \n                            mode='bilinear', \n                            align_corners=True)\n        \n        stacked = torch.cat([pred1, pred2], dim=1)  \n        \n        # Apply softmax to weights\n        weights = F.softmax(self.weights, dim=0)\n        \n        # Apply weights \n        weighted = stacked * weights.view(1, -1, 1, 1)\n        \n        # Feature fusion\n        fused = self.fusion(weighted)\n        \n        return fused","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.394213Z","iopub.execute_input":"2025-02-23T07:09:26.394506Z","iopub.status.idle":"2025-02-23T07:09:26.410777Z","shell.execute_reply.started":"2025-02-23T07:09:26.394473Z","shell.execute_reply":"2025-02-23T07:09:26.41009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Trainer:\n    def __init__(self, model, train_loader, val_loader, optimizer, device='cuda'):\n        self.model = model.to(device)\n        self.train_loader = train_loader\n        self.val_loader = val_loader\n        self.optimizer = optimizer\n        self.device = device\n        self.scaler = GradScaler()  \n        \n    def evaluate_epoch(self, loader, is_training=False):\n        metrics = {\n            'loss': 0.0,\n            'accuracy': 0.0,\n            'dice': 0.0\n        }\n        \n        self.model.train(is_training)\n        total_batches = len(loader)\n        phase = \"Training\" if is_training else \"Validation\"\n        \n        with torch.set_grad_enabled(is_training):\n            for data, target in tqdm(loader, desc=f'{phase} Evaluation'):\n                data, target = data.to(self.device), target.to(self.device)\n                \n                if is_training:\n                    self.optimizer.zero_grad()\n                    \n                    # Use autocast for mixed precision\n                    with autocast():\n                        output = self.model(data)\n                        loss = self.model.criterion(output, target)\n                    \n                    # Scale loss and backpropagate\n                    self.scaler.scale(loss).backward()\n                    self.scaler.step(self.optimizer)\n                    self.scaler.update()\n                else:\n                    with autocast():\n                        output = self.model(data)\n                        loss = self.model.criterion(output, target)\n                \n                # Update metrics (using float32 for accuracy)\n                with torch.cuda.amp.autocast(enabled=False):\n                    metrics['loss'] += loss.item()\n                    pred = output.argmax(dim=1) if output.shape[1] > 1 else (output > 0.5).float()\n                    metrics['accuracy'] += (pred == target).float().mean().item()\n                    metrics['dice'] += self.calculate_dice(pred, target).item()\n        \n        # Average metrics\n        for key in metrics:\n            metrics[key] /= total_batches\n            \n        return metrics\n    \n    @staticmethod\n    def calculate_dice(pred, target):\n        smooth = 1e-5\n        intersection = (pred * target).sum()\n        return (2. * intersection + smooth) / (pred.sum() + target.sum() + smooth)\n    \n    def train(self, epochs):\n        best_val_loss = float('inf')\n        patience = 10\n        patience_counter = 0\n        \n        for epoch in range(epochs):\n            # Training phase\n            train_metrics = self.evaluate_epoch(self.train_loader, is_training=True)\n            \n            # Validation phase\n            val_metrics = self.evaluate_epoch(self.val_loader, is_training=False)\n            \n            # Print metrics\n            print(f\"\\nEpoch {epoch+1}/{epochs}\")\n            print(f\"Training Loss: {train_metrics['loss']:.4f}, Accuracy: {train_metrics['accuracy']:.4f}, Dice: {train_metrics['dice']:.4f}\")\n            print(f\"Validation Loss: {val_metrics['loss']:.4f}, Accuracy: {val_metrics['accuracy']:.4f}, Dice: {val_metrics['dice']:.4f}\")\n            \n            # Early stopping check\n            if val_metrics['loss'] < best_val_loss:\n                best_val_loss = val_metrics['loss']\n                patience_counter = 0\n                # Save best model\n                torch.save({\n                    'epoch': epoch,\n                    'model_state_dict': self.model.state_dict(),\n                    'optimizer_state_dict': self.optimizer.state_dict(),\n                    'scaler_state_dict': self.scaler.state_dict(),  # Save scaler state\n                    'loss': best_val_loss,\n                }, 'best_model.pth')\n            else:\n                patience_counter += 1\n                if patience_counter >= patience:\n                    print(f\"\\nEarly stopping triggered after {epoch+1} epochs\")\n                    break\n        \n        return self.model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.413214Z","iopub.execute_input":"2025-02-23T07:09:26.413436Z","iopub.status.idle":"2025-02-23T07:09:26.427964Z","shell.execute_reply.started":"2025-02-23T07:09:26.413417Z","shell.execute_reply":"2025-02-23T07:09:26.427196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestPredictor:\n    def __init__(self, model, test_loader, device):\n        self.model = model\n        self.test_loader = test_loader\n        self.device = device\n    \n    def predict_test_set(self):\n        self.model.eval()\n        predictions = []\n        image_ids = []\n        \n        with torch.no_grad():\n            for images, ids in tqdm(self.test_loader, desc='Predicting Test Set'):\n                images = images.to(self.device)\n                outputs = self.model(images)\n                pred_masks = (torch.sigmoid(outputs) > 0.5).float().cpu().numpy()\n                \n                for mask, img_id in zip(pred_masks, ids):\n                    rle = rle_encode(mask[0])\n                    predictions.append(rle)\n                    image_ids.append(img_id)\n        \n        return pd.DataFrame({\n            'ImageId': image_ids,\n            'EncodedPixels': predictions\n        })\n    \n    def create_submission(self, output_path='/kaggle/working/submission.csv'):\n        print(\"Creating submission file...\")\n        submission_df = self.predict_test_set()\n        submission_df.to_csv(output_path, index=False)\n        print(f\"Submission file saved to {output_path}\")\n        return submission_df\n    \n    def visualize_predictions(self, num_samples=5):\n        self.model.eval()\n        fig, axes = plt.subplots(num_samples, 2, figsize=(10, 5*num_samples))\n        \n        with torch.no_grad():\n            for i, (images, _) in enumerate(self.test_loader):\n                if i >= num_samples:\n                    break\n                    \n                images = images.to(self.device)\n                outputs = self.model(images)\n                pred_masks = torch.sigmoid(outputs).cpu()\n                \n                # Plot original image\n                axes[i, 0].imshow(images[0].cpu().permute(1, 2, 0))\n                axes[i, 0].set_title('Original Image')\n                axes[i, 0].axis('off')\n                \n                # Plot prediction\n                axes[i, 1].imshow(pred_masks[0, 0], cmap='jet')\n                axes[i, 1].set_title('Predicted Mask')\n                axes[i, 1].axis('off')\n        \n        plt.tight_layout()\n        plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.4289Z","iopub.execute_input":"2025-02-23T07:09:26.429191Z","iopub.status.idle":"2025-02-23T07:09:26.447516Z","shell.execute_reply.started":"2025-02-23T07:09:26.429169Z","shell.execute_reply":"2025-02-23T07:09:26.446598Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def main():\n    device = Config.DEVICE\n    \n    print(\"Preparing data...\")\n    train_df, val_df = prepare_data()\n    \n    # Create datasets and dataloaders\n    train_dataset = ShipDataset(\n        train_df, \n        Config.TRAIN_PATH,\n        transform=get_training_augmentation()\n    )\n    \n    val_dataset = ShipDataset(\n        val_df,\n        Config.TRAIN_PATH,\n        transform=get_validation_augmentation()\n    )\n    \n    train_loader = DataLoader(\n        train_dataset,\n        batch_size=Config.BATCH_SIZE,\n        shuffle=True,\n        num_workers=Config.NUM_WORKERS\n    )\n    \n    val_loader = DataLoader(\n        val_dataset,\n        batch_size=Config.BATCH_SIZE,\n        shuffle=False,\n        num_workers=Config.NUM_WORKERS\n    )\n    \n    print(\"Initializing model...\")\n    model = Model()\n    \n    # Initialize optimizer\n    optimizer = torch.optim.AdamW(model.parameters(), lr=Config.LEARNING_RATE)\n    \n    # Create trainer with correct parameter order\n    trainer = Trainer(\n        model=model,\n        train_loader=train_loader,\n        val_loader=val_loader,\n        optimizer=optimizer,  # Add the missing optimizer\n        device=device\n    )\n    \n    # Train model\n    trained_model = trainer.train(epochs=30)\n    \n    # Initialize test predictor\n    predictor = TestPredictor(trained_model, test_loader, device)\n    \n    # Create submission file\n    submission_df = predictor.create_submission()\n    \n    # Visualize some test predictions\n    predictor.visualize_predictions()\n    \n    return submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.448504Z","iopub.execute_input":"2025-02-23T07:09:26.44881Z","iopub.status.idle":"2025-02-23T07:09:26.46656Z","shell.execute_reply.started":"2025-02-23T07:09:26.448781Z","shell.execute_reply":"2025-02-23T07:09:26.4657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    submission_df = main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-23T07:09:26.467537Z","iopub.execute_input":"2025-02-23T07:09:26.468017Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}