{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":6927,"databundleVersionId":45059,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision import transforms\n\nfrom tqdm import tqdm\n\n\n\ndef seed_everything(seed):\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n\nseed_everything(42)\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# ========================\n# CUSTOM DATASET\n# ========================\nclass GenData(Dataset):\n    def __init__(self, image_dir, mask_dir, transform=None):\n        self.image_dir = image_dir\n        self.mask_dir = mask_dir\n        self.transform = transform\n        self.image_files = sorted(os.listdir(image_dir))\n\n    def __len__(self):\n        return len(self.image_files)\n\n    def __getitem__(self, idx):\n        img_name = self.image_files[idx]\n        img_path = os.path.join(self.image_dir, img_name)\n        mask_path = os.path.join(self.mask_dir, img_name.replace('.jpg', '_mask.gif'))\n\n        # Check if mask file exists\n        if not os.path.exists(mask_path):\n            raise FileNotFoundError(f\"Mask not found for image: {img_name}\")\n\n        image = Image.open(img_path).convert(\"RGB\")\n        mask = Image.open(mask_path).convert(\"L\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        mask = transforms.Resize((224, 224))(mask)\n        mask = transforms.ToTensor()(mask)\n        mask = (mask > 0.5).long().squeeze(0)  # Binary mask for 2 classes\n\n        return image, mask\n\n\n\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\n\n\ndataset = GenData(\n    image_dir=\"/kaggle/working/carvana/train/train\",\n    mask_dir=\"/kaggle/working/carvana/train_masks/train_masks\",\n    transform=transform\n)\n\nprint(f\"Total samples in dataset: {len(dataset)}\")\nprint(f\"Sample filenames: {dataset.image_files[:3]}\")\n\ntrain_size = int(0.9 * len(dataset))\nval_size = len(dataset) - train_size\n\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\ntrain_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=8, shuffle=False)\n\n\n\nclass DoubleConv(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_channels, out_channels, 3, padding=1),\n            nn.ReLU(inplace=True)\n        )\n\n    def forward(self, x):\n        return self.conv(x)\n\nclass DownSample(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.block = DoubleConv(in_channels, out_channels)\n        self.pool = nn.MaxPool2d(2)\n\n    def forward(self, x):\n        conv = self.block(x)\n        pooled = self.pool(conv)\n        return conv, pooled\n\nclass UpSample(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, 2, stride=2)\n        self.conv = DoubleConv(in_channels, out_channels)\n\n    def forward(self, x1, x2):\n        x1 = self.up(x1)\n        x = torch.cat([x2, x1], dim=1)\n        return self.conv(x)\n\n\n\nclass UNet(nn.Module):\n    def __init__(self, in_channels=3, out_classes=2):\n        super().__init__()\n        self.d1 = DownSample(in_channels, 64)\n        self.d2 = DownSample(64, 128)\n        self.d3 = DownSample(128, 256)\n        self.d4 = DownSample(256, 512)\n\n        self.bottleneck = DoubleConv(512, 1024)\n\n        self.u1 = UpSample(1024, 512)\n        self.u2 = UpSample(512, 256)\n        self.u3 = UpSample(256, 128)\n        self.u4 = UpSample(128, 64)\n\n        self.final = nn.Conv2d(64, out_classes, kernel_size=1)\n\n    def forward(self, x):\n        c1, p1 = self.d1(x)\n        c2, p2 = self.d2(p1)\n        c3, p3 = self.d3(p2)\n        c4, p4 = self.d4(p3)\n\n        b = self.bottleneck(p4)\n\n        u1 = self.u1(b, c4)\n        u2 = self.u2(u1, c3)\n        u3 = self.u3(u2, c2)\n        u4 = self.u4(u3, c1)\n\n        return self.final(u4)\n\n\n\nmodel = UNet().to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0001)\nscheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)\n\nepochs = 20\ntrain_loss, val_loss = [], []\ntrain_acc, val_acc = [], []\n\n\nfor epoch in tqdm(range(epochs), desc='Training'):\n    model.train()\n    running_loss = 0\n    correct, total = 0, 0\n\n    for x_batch, y_batch in train_loader:\n        x_batch, y_batch = x_batch.to(device), y_batch.to(device)\n        optimizer.zero_grad()\n        output = model(x_batch)\n\n        loss = criterion(output, y_batch)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n        preds = torch.argmax(output, dim=1)\n        correct += (preds == y_batch).sum().item()\n        total += torch.numel(y_batch)\n\n    train_loss.append(running_loss / len(train_loader))\n    train_acc.append(correct / total)\n\n    # Validation\n    model.eval()\n    val_running_loss = 0\n    val_correct, val_total = 0, 0\n    with torch.no_grad():\n        for x_val, y_val in val_loader:\n            x_val, y_val = x_val.to(device), y_val.to(device)\n            val_output = model(x_val)\n            val_loss_i = criterion(val_output, y_val)\n            val_running_loss += val_loss_i.item()\n\n            val_preds = torch.argmax(val_output, dim=1)\n            val_correct += (val_preds == y_val).sum().item()\n            val_total += torch.numel(y_val)\n\n    val_loss.append(val_running_loss / len(val_loader))\n    val_acc.append(val_correct / val_total)\n\n    scheduler.step()\n\n    print(f\"Epoch [{epoch+1}/{epochs}] \"\n          f\"| Train Loss: {train_loss[-1]:.4f}, Acc: {train_acc[-1]*100:.2f}% \"\n          f\"| Val Loss: {val_loss[-1]:.4f}, Acc: {val_acc[-1]*100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-14T11:26:11.479678Z","iopub.execute_input":"2025-07-14T11:26:11.480280Z","iopub.status.idle":"2025-07-14T12:44:53.343740Z","shell.execute_reply.started":"2025-07-14T11:26:11.480257Z","shell.execute_reply":"2025-07-14T12:44:53.342644Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(train_loss, label='Train Loss')\nplt.plot(val_loss, label='Val Loss')\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.show()\n\nplt.plot(train_acc, label='Train Acc')\nplt.plot(val_acc, label='Val Acc')\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")1\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-14T12:44:59.966037Z","iopub.execute_input":"2025-07-14T12:44:59.966304Z","iopub.status.idle":"2025-07-14T12:45:00.325447Z","shell.execute_reply.started":"2025-07-14T12:44:59.966286Z","shell.execute_reply":"2025-07-14T12:45:00.324713Z"}},"outputs":[],"execution_count":null}]}