{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"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":"none","dataSources":[{"sourceId":9988,"databundleVersionId":868324,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfrom PIL import Image\nimport os\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision.datasets as datasets\nimport torchvision.transforms as transforms","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-13T08:39:51.715995Z","iopub.execute_input":"2025-07-13T08:39:51.716342Z","iopub.status.idle":"2025-07-13T08:39:51.721756Z","shell.execute_reply.started":"2025-07-13T08:39:51.716312Z","shell.execute_reply":"2025-07-13T08:39:51.720645Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dir = \"/kaggle/input/airbus-ship-detection/test_v2/\"\ntrain_dir = \"/kaggle/input/airbus-ship-detection/train_v2/\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-13T08:39:53.360002Z","iopub.execute_input":"2025-07-13T08:39:53.360301Z","iopub.status.idle":"2025-07-13T08:39:53.365021Z","shell.execute_reply.started":"2025-07-13T08:39:53.360277Z","shell.execute_reply":"2025-07-13T08:39:53.363774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_files = os.listdir(train_dir)[:10]\n\nfig, axes = plt.subplots(2, 5, figsize=(20, 8))\naxes = axes.flatten()\nfor i, filename in enumerate(train_files):\n    img = Image.open(os.path.join(train_dir, filename))\n    axes[i].imshow(img)\n    axes[i].set_title(f\"File {filename}\")\n    axes[i].axis(\"off\")\n\nplt.suptitle(\"Train files\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-13T08:40:29.047704Z","iopub.execute_input":"2025-07-13T08:40:29.048079Z","iopub.status.idle":"2025-07-13T08:40:32.476587Z","shell.execute_reply.started":"2025-07-13T08:40:29.048050Z","shell.execute_reply":"2025-07-13T08:40:32.475005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ShipDataset(Dataset):\n    def __init__(self, csv_file, img_dir, transform = None):\n        self.df = pd.read_csv(csv_file)\n        self.img_dir = img_dir\n        self.transform = transform\n\n        self.image_ids = self.df[\"ImageId\"].unique()\n\n    def __len__(self) -> float:\n        return len(self.image_ids)\n\n    def rle_decode(self, mask_rle, shape=(768, 768)):\n        if pd.isna(mask_rle):\n            return np.zeros(shape, np.uint8)\n\n        s = mask_rle.split()\n        starts = np.asarray(s[0::2], dtype=int) - 1\n        lengths = np.asarray(s[1::2], dtype=int)\n\n        mask = np.zeros(shape[0] * shape[1], np.uint8)\n        for start, length in zip(starts, lengths):\n            mask[start:start+length] = 1\n\n        return mask.reshape(shape).T\n\n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n\n        masks = self.df[self.df[\"ImageId\"] == image_id][\"EncodedPixels\"].values\n        combined_mask = np.zeros((768, 768), dtype=np.uint8)\n        for mask_rle in masks:\n            if pd.notna(mask_rle):\n                decoded_mask = self.rle_decode(mask_rle)\n                combined_mask = np.maximum(combined_mask, decoded_mask)\n\n        image = Image.open(os.path.join(self.img_dir, image_id)).convert(\"RGB\")\n        mask = Image.fromarray(combined_mask)\n        \n        if self.transform:\n            image = self.transform(image)\n            mask = self.transform(mask)\n\n        return image, mask","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ShipDataset(Dataset):\n    def __init__(self, image_dir, csv_file, transform = None):\n        self.image_dir = image_dir\n        self.df = pd.read_csv(csv_file)\n        self.transform = transform\n        self.image_ids = self.df[\"ImageId\"].unique()\n\n    def __len__(self) -> int:\n        return len(self.image_ids)\n\n    def rle_decode(self, mask_rle, shape = (768, 768)):\n        if pd.isna(mask_rle):\n            return np.zeros(shape, dtype=np.uint8)\n\n        info = mask_rle.split()\n        starts = np.asarray(info[0::2], dtype=int) - 1\n        lengths = np.asarray(info[1::2], dtype=int)\n\n        mask = np.zeros(shape[0] * shape[1], dtype=np.uint8)\n        for start, length in zip(starts, lengths):\n            mask[start:start+length] = 1\n\n        return mask.reshape(shape).T\n\n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n\n        image = Image.open(os.path.join(self.image_dir, image_id)).convert(\"RGB\")\n\n        all_masks = self.df[self.df[\"ImageId\"] == image_id][\"EncodedPixels\"].values\n        combined_mask = np.zeros((768, 768), dtype=np.uint8)\n        for mask_rle in all_masks:\n            if pd.notna(mask_rle):  \n                mask = self.rle_decode(mask_rle)\n                combined_mask = np.maximum(combined_mask, mask)\n                \n        mask = Image.fromarray(combined_mask)\n        \n        if self.transform:\n            image = self.transform(image)\n            mask = self.transform(mask)\n\n        return image, mask","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.Resize((256, 256)\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\ndataset = ShipDataset(\n    train_dir,\n    \"/kaggle/input/airbus-ship-detection/train_ship_segmentations_v2.csv\",\n    transform\n)\n\ntrain_size = 0.8 * len(dataset)\nval_size = len(dataset) - train_size\ntrain_dataset, val_dataset = torch.utils.data.random_split(dataset, [train_size, val_size])\n\nbatch_size = 16\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=batch_size,\n    num_workers=2,\n    shuffle=True\n)\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=batch_size,\n    num_workers=2,\n    shuffle=False\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SegmentationModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.encoder = nn.Sequential(\n            nn.Conv2d(3, 64, 3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(64, 128, 3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(128, 256, 3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),\n            \n            nn.Conv2d(256, 512, 3, padding=1),\n            nn.BatchNorm2d(512),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2)\n        )\n\n        self.decoder = nn.Sequential(\n            nn.BatchNorm2d(256),\n            nn.ReLU(inplace=True),\n            \n            nn.ConvTranspose2d(256, 128, 2, stride=2),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n            \n            nn.ConvTranspose2d(128, 64, 2, stride=2),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n            \n            nn.ConvTranspose2d(64, 1, 2, stride=2),\n        )\n\n    def forward(self, x):\n        x = self.encoder(x)\n        x = self.decoder(x)\n        return x","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Trainer:\n    def __init__(self, model, device):\n        self.device = torch.device(device)\n        self.model = model.to(self.device)\n\n        self.criterion = BCEWithLogitsLoss()\n        self.optimizer = optim.AdamW(\n            self.model.parameters(),\n            lr = 0.001,\n            weight_decay = 0.001\n        )\n        self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(\n            self.optimizer,\n            mode='min',\n            patience = 5,\n            factor = 0.5\n        )\n        \n        self.train_losses = []\n        self.val_losses = []\n        self.best_val_loss = float('inf')\n\n    def calculate_iou(self, pred, target):\n        pred = torch.sigmoid(pred) > 0.5\n        target = target > 0.5\n\n        intersection = (pred & target).float().sum()\n        union = (pred | target).float().sum()\n\n        if union == 0.0:\n            return 1.0\n\n        return (intersection / union).item()\n\n    def save_checkpoint(self, epoch: int, file_path: str):\n        checkpoint = {\n            \"epoch\": epoch,\n            \"model\": self.model.state_dict(),\n            \"optimizer\": self.optimizer.state_dict(),\n            \"scheduler\": self.scheduler.state_dict(),\n            \"best_val_loss\": self.best_val_loss,\n        }\n\n        torch.save(checkpoint, file_path)\n\n    def load_checkpoint(self, file_path: str) -> int:\n        checkpoint = torch.load(file_path)\n\n        epoch = checkpoint[\"epoch\"]\n        self.model.load_state_dict(checkpoint[\"model\"])\n        self.optimizer.load_state_dict(checkpoint[\"optimizer\"])\n        self.scheduler.load_state_dict(checkpoint[\"scheduler\"])\n        self.best_val_loss = checkpoint[\"best_val_loss\"]\n\n        return epoch\n\n    def train_epoch(self, train_loader):\n        self.model.train()\n        \n        total_iou = 0.0\n        total_loss = 0.0\n        for item, target in train_loader:\n            item = item.to(self.device)\n            target = target.to(self.device)\n\n            if isinstance(target, list):\n                target = torch.stack([transforms.ToTensor()(t) for t in target])\n            target = F.interpolate(target.unsqueeze(1), size=(256, 256), mode='nearest').squeeze(1)\n\n            self.optimizer.zero_grad()\n            output = self.model(item)\n            loss = self.criterion(output.squeeze(1), target)\n\n            loss.backward()\n\n            self.optimizer.step()\n\n            iou = self.calculate_iou(output.squeeze(1), target)\n            total_iou += iou\n\n            total_loss += loss.item()\n\n        avg_loss = total_loss / len(train_loader)\n        avg_iou = total_iou / len(train_loader)\n        print(f\"TRAIN EPOCH LOSS: {avg_loss:.4f}, IoU: {avg_iou:.4f}\")\n        return avg_loss\n\n    def val_epoch(self, val_loader):\n        self.model.eval()\n\n        total_iou = 0.0\n        total_loss = 0.0\n\n        with torch.no_grad():\n            for item, target in val_loader:\n                item, target = item.to(self.device), target.to(self.device)\n\n                if isinstance(target, list):\n                    target = torch.stack([transforms.ToTensor()(t) for t in target])\n                target = F.interpolate(target.unsqueeze(1), size=(256, 256), mode='nearest').squeeze(1)\n\n                output = self.model(item)\n                loss = self.criterion(output.squeeze(1), target)\n\n                total_loss += loss.item()\n                total_iou += self.calculate_iou(output.squeeze(1), target)\n\n        avg_loss = total_loss / len(val_loader)\n        avg_iou = total_iou / len(val_loader)\n        print(f\"VAL IoU: {avg_iou:.4f}; VAL LOSS: {avg_loss:.4f}\")\n        return avg_loss\n\n    def train(self, train_loader, val_loader, n_epochs):\n        for epoch in range(n_epochs):\n            train_loss = self.train_epoch(train_loader)\n\n            val_loss = self.val_epoch(val_loader)\n\n            self.train_losses.append(train_loss)\n            self.val_losses.append(val_loss)\n            self.scheduler.step(val_loss)\n\n            if val_loss < self.best_val_loss:\n                self.best_val_loss = val_loss\n                self.save_checkpoint(epoch, \"path\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = SegmentationModel()\ntrainer = SegmentationTrainer(model, device)\ntrainer.train(train_loader, val_loader, 50)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}