{"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":6927,"databundleVersionId":45059}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nfrom pathlib import Path\n\ndef print_dataset_structure(root_path, indent=0, max_files=3):\n    \"\"\"\n    Prints the folder structure and a few sample files to understand \n    naming conventions and extensions.\n    \"\"\"\n    root = Path(root_path)\n    \n    # Sort to keep folders at the top\n    for path in sorted(root.iterdir(), key=lambda x: (not x.is_dir(), x.name)):\n        spacing = '    ' * indent\n        if path.is_dir():\n            print(f\"{spacing}📁 {path.name}/\")\n            # Recursive call for subdirectories\n            print_dataset_structure(path, indent + 1, max_files)\n        else:\n            # Only print the first few files to avoid a wall of text\n            file_count = len(list(root.glob(f\"*{path.suffix}\")))\n            if list(root.glob(f\"*{path.suffix}\")).index(path) < max_files:\n                print(f\"{spacing}📄 {path.name}\")\n            elif list(root.glob(f\"*{path.suffix}\")).index(path) == max_files:\n                print(f\"{spacing}... and {file_count - max_files} more {path.suffix} files.\")\n\n# Replace this with your actual path (e.g., '/kaggle/input/carvana-image-masking-challenge')\ndataset_path = '/kaggle/input/competitions/carvana-image-masking-challenge' \n\nif os.path.exists(dataset_path):\n    print(f\"Structure for: {dataset_path}\")\n    print_dataset_structure(dataset_path)\nelse:\n    print(f\"Path '{dataset_path}' not found. Please update the dataset_path variable.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-30T00:49:07.316166Z","iopub.execute_input":"2026-03-30T00:49:07.316435Z","iopub.status.idle":"2026-03-30T00:49:07.342530Z","shell.execute_reply.started":"2026-03-30T00:49:07.316412Z","shell.execute_reply":"2026-03-30T00:49:07.341652Z"},"jupyter":{"source_hidden":true,"outputs_hidden":true},"collapsed":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"****UNZIP IMAGES****","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport os\n\n# Create directories to extract to\nos.makedirs('train_images', exist_ok=True)\nos.makedirs('train_masks', exist_ok=True)\n\n# Unzip images\nwith zipfile.ZipFile('/kaggle/input/competitions/carvana-image-masking-challenge/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('train_images')\n\n# Unzip masks\nwith zipfile.ZipFile('/kaggle/input/competitions/carvana-image-masking-challenge/train_masks.zip', 'r') as zip_ref:\n    zip_ref.extractall('train_masks')\n\nprint(f\"Extracted {len(os.listdir('train_images/train'))} images and {len(os.listdir('train_masks/train_masks'))} masks.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T18:48:06.037386Z","iopub.execute_input":"2026-04-01T18:48:06.037773Z","iopub.status.idle":"2026-04-01T18:48:14.965579Z","shell.execute_reply.started":"2026-04-01T18:48:06.037739Z","shell.execute_reply":"2026-04-01T18:48:14.964687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport random\nfrom PIL import Image\n\ndef visualize_sample(image_dir, mask_dir):\n    # Get all image filenames\n    image_files = [f for f in os.listdir(image_dir) if f.endswith('.jpg')]\n    random_img = image_files[3]\n    \n    # Define paths (Carvana masks have the '_mask' suffix and are .gif)\n    img_path = os.path.join(image_dir, random_img)\n    mask_path = os.path.join(mask_dir, random_img.replace('.jpg', '_mask.gif'))\n    \n    # Load images\n    image = np.array(Image.open(img_path))\n    mask = np.array(Image.open(mask_path).convert(\"L\")) # Convert to grayscale\n\n    # Create the visualization\n    fig, ax = plt.subplots(1, 3, figsize=(18, 6))\n\n    # 1. Original Image\n    ax[0].imshow(image)\n    ax[0].set_title(f\"Original Image\\n{random_img}\", fontsize=12)\n    ax[0].axis(\"off\")\n\n    # 2. Mask (Ground Truth)\n    ax[1].imshow(mask, cmap=\"gray\")\n    ax[1].set_title(\"Ground Truth Mask\\n(What the AI should predict)\", fontsize=12)\n    ax[1].axis(\"off\")\n\n    # 3. Overlay (Mask on Image)\n    ax[2].imshow(image)\n    # We use 'alpha' to make the mask semi-transparent\n    ax[2].imshow(mask, cmap=\"jet\", alpha=0.4) \n    ax[2].set_title(\"Overlay\\n(Visualizing the 'Cutout')\", fontsize=12)\n    ax[2].axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\n# Run the visualization\n# Note: Since the zip extraction created subfolders, adjust paths if necessary\nvisualize_sample('train_images/train', 'train_masks/train_masks')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-30T01:06:33.340853Z","iopub.execute_input":"2026-03-30T01:06:33.341108Z","iopub.status.idle":"2026-03-30T01:06:34.883309Z","shell.execute_reply.started":"2026-03-30T01:06:33.341082Z","shell.execute_reply":"2026-03-30T01:06:34.882527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass SimpleUNet(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1):\n        super(SimpleUNet, self).__init__()\n\n        # 1. Downward Path (Encoder)\n        self.enc1 = self.conv_block(in_channels, 64)\n        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.enc2 = self.conv_block(64, 128)\n        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)\n\n        # 2. The Bottom (Bottleneck)\n        self.bottleneck = self.conv_block(128, 256)\n\n        # 3. Upward Path (Decoder)\n        self.up2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)\n        self.dec2 = self.conv_block(256, 128) # 256 because of skip connection!\n        \n        self.up1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)\n        self.dec1 = self.conv_block(128, 64) # 128 because of skip connection!\n\n        # 4. Final Output Layer (Pixel-wise classification)\n        self.final_conv = nn.Conv2d(64, out_channels, kernel_size=1)\n\n    def conv_block(self, in_c, out_c):\n        return nn.Sequential(\n            nn.Conv2d(in_c, out_c, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_c), # <--- Adds stability!\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_c, out_c, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_c), # <--- Adds stability!\n            nn.ReLU(inplace=True)\n        )\n\n    def forward(self, x):\n        # Encoder\n        s1 = self.enc1(x)\n        p1 = self.pool1(s1)\n        s2 = self.enc2(p1)\n        p2 = self.pool2(s2)\n\n        # Bottleneck\n        b = self.bottleneck(p2)\n\n        # Decoder + Skip Connections\n        d2 = self.up2(b)\n        d2 = torch.cat((d2, s2), dim=1) # The \"Bridge\" connection\n        d2 = self.dec2(d2)\n\n        d1 = self.up1(d2)\n        d1 = torch.cat((d1, s1), dim=1) # The \"Bridge\" connection\n        d1 = self.dec1(d1)\n\n        return self.final_conv(d1)\n\n# Quick check: Create a dummy image (Batch=1, Channels=3, H=160, W=160)\nmodel = SimpleUNet()\ndummy_input = torch.randn((1, 3, 160, 160))\noutput = model(dummy_input)\nprint(f\"Input shape: {dummy_input.shape}\")\nprint(f\"Output shape: {output.shape}\") # Should be (1, 1, 160, 160)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T19:31:59.055321Z","iopub.execute_input":"2026-04-01T19:31:59.056090Z","iopub.status.idle":"2026-04-01T19:31:59.271831Z","shell.execute_reply.started":"2026-04-01T19:31:59.056054Z","shell.execute_reply":"2026-04-01T19:31:59.271183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\nimport torch.optim as optim\n\nclass DiceBCELoss(nn.Module):\n    def __init__(self, weight=None, size_average=True):\n        super(DiceBCELoss, self).__init__()\n\n    def forward(self, inputs, targets, smooth=1):\n        # Flatten label and prediction tensors\n        inputs = torch.sigmoid(inputs).view(-1)\n        targets = targets.view(-1)\n        \n        intersection = (inputs * targets).sum()                            \n        dice_loss = 1 - (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)  \n        BCE = nn.functional.binary_cross_entropy(inputs, targets, reduction='mean')\n        \n        return BCE + dice_loss\n\n# Initialize Model, Loss, and Optimizer\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nmodel = SimpleUNet().to(device)\ncriterion = DiceBCELoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T19:32:00.036466Z","iopub.execute_input":"2026-04-01T19:32:00.037330Z","iopub.status.idle":"2026-04-01T19:32:00.069133Z","shell.execute_reply.started":"2026-04-01T19:32:00.037291Z","shell.execute_reply":"2026-04-01T19:32:00.068308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np            # For math/arrays\nfrom PIL import Image         # For opening images\nimport torch                  # The AI engine\nfrom torch.utils.data import Dataset, DataLoader\nimport albumentations as A    # For resizing and flipping\nfrom albumentations.pytorch import ToTensorV2\n\n# 1. Define the Recipe (The Class)\nclass CarvanaDataset(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.images = os.listdir(image_dir)\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, index):\n        img_path = os.path.join(self.image_dir, self.images[index])\n        mask_path = os.path.join(self.mask_dir, self.images[index].replace(\".jpg\", \"_mask.gif\"))\n\n        image = np.array(Image.open(img_path).convert(\"RGB\"))\n        mask = np.array(Image.open(mask_path).convert(\"L\"), dtype=np.float32) \n        mask[mask == 255.0] = 1.0 # Normalize to 0 and 1\n\n        if self.transform is not None:\n            augmentations = self.transform(image=image, mask=mask)\n            image = augmentations[\"image\"]\n            mask = augmentations[\"mask\"]\n\n        return image, mask\n\n# 2. Define the Ingredients (Transforms & Paths)\ntrain_transform = A.Compose([\n    A.Resize(height=160, width=240),\n    A.HorizontalFlip(p=0.5),\n    A.Normalize(mean=[0.0, 0.0, 0.0], std=[1.0, 1.0, 1.0], max_pixel_value=255.0),\n    ToTensorV2(),\n])\n\n# 3. Create the Chef (The Loader)\ntrain_ds = CarvanaDataset(\n    image_dir=\"train_images/train\",\n    mask_dir=\"train_masks/train_masks\",\n    transform=train_transform,\n)\n\ntrain_loader = DataLoader(\n    train_ds,\n    batch_size=16,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True,\n)\n\nprint(f\"✅ Ready! 'train_loader' is now active with {len(train_ds)} samples.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T19:32:04.192564Z","iopub.execute_input":"2026-04-01T19:32:04.193321Z","iopub.status.idle":"2026-04-01T19:32:04.208346Z","shell.execute_reply.started":"2026-04-01T19:32:04.193290Z","shell.execute_reply":"2026-04-01T19:32:04.207677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import random_split\n\n# 1. Calculate the sizes (80/10/10 split)\ntotal_size = len(train_ds)\ntrain_size = int(0.8 * total_size)\nval_size = int(0.1 * total_size)\ntest_size = total_size - train_size - val_size\n\n# 2. Split the dataset\n# We use a 'generator' with a seed so the split is the same every time you run it\ntrain_subset, val_subset, test_subset = random_split(\n    train_ds, [train_size, val_size, test_size], \n    generator=torch.Generator().manual_seed(42)\n)\n\n# 3. Create the Loaders\ntrain_loader = DataLoader(train_subset, batch_size=16, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_subset, batch_size=16, shuffle=False, num_workers=2)\ntest_loader = DataLoader(test_subset, batch_size=1, shuffle=False) # Batch 1 for easy testing\n\nprint(f\"📊 Dataset Split Complete:\")\nprint(f\"🏠 Training: {len(train_subset)} images\")\nprint(f\"📝 Validation: {len(val_subset)} images\")\nprint(f\"🎓 Testing: {len(test_subset)} images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T18:48:36.745017Z","iopub.execute_input":"2026-04-01T18:48:36.745424Z","iopub.status.idle":"2026-04-01T18:48:36.753801Z","shell.execute_reply.started":"2026-04-01T18:48:36.745398Z","shell.execute_reply":"2026-04-01T18:48:36.752894Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\ndef visualize_progress(model, loader, device, epoch):\n    model.eval()\n    # Grab one batch from validation to show progress\n    data, targets = next(iter(loader))\n    data, targets = data.to(device), targets.to(device)\n    \n    with torch.no_grad():\n        preds = torch.sigmoid(model(data))\n        preds = (preds > 0.5).float()\n\n    # Plotting\n    plt.figure(figsize=(12, 4))\n    \n    # Input Image (Denormalize for display)\n    plt.subplot(1, 3, 1)\n    plt.imshow(data[7].cpu().permute(1, 2, 0))\n    plt.title(f\"Input (Epoch {epoch+1})\")\n    plt.axis(\"off\")\n\n    # Real Mask\n    plt.subplot(1, 3, 2)\n    plt.imshow(targets[7].cpu().squeeze(), cmap=\"gray\")\n    plt.title(\"Ground Truth\")\n    plt.axis(\"off\")\n\n    # AI Prediction\n    plt.subplot(1, 3, 3)\n    plt.imshow(preds[7].cpu().squeeze(), cmap=\"gray\")\n    plt.title(\"AI Prediction\")\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\ndef train_model(epochs=10):\n    for epoch in range(epochs):\n        # --- TRAINING PHASE ---\n        model.train()\n        train_loop = tqdm(train_loader)\n        train_loss = 0\n        for data, targets in train_loop:\n            data, targets = data.to(device), targets.float().unsqueeze(1).to(device)\n            \n            predictions = model(data)\n            loss = criterion(predictions, targets)\n            \n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n            \n            train_loss += loss.item()\n            train_loop.set_description(f\"Epoch [{epoch+1}/{epochs}] - Training\")\n        \n        # --- VALIDATION PHASE ---\n        model.eval()\n        val_loss = 0\n        with torch.no_grad():\n            for data, targets in val_loader:\n                data, targets = data.to(device), targets.float().unsqueeze(1).to(device)\n                predictions = model(data)\n                loss = criterion(predictions, targets)\n                val_loss += loss.item()\n        \n        avg_train = train_loss / len(train_loader)\n        avg_val = val_loss / len(val_loader)\n        \n        print(f\"Epoch {epoch+1}: Train Loss: {avg_train:.4f} | Val Loss: {avg_val:.4f}\")\n        \n        # --- VISUALIZATION STEP ---\n        # Show what the model learned after this epoch\n        visualize_progress(model, val_loader, device, epoch)\n\n# Start training!\ntrain_model()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-01T19:32:08.366590Z","iopub.execute_input":"2026-04-01T19:32:08.366870Z","iopub.status.idle":"2026-04-01T19:51:52.546973Z","shell.execute_reply.started":"2026-04-01T19:32:08.366846Z","shell.execute_reply":"2026-04-01T19:51:52.545752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def final_exam():\n    model.eval()\n    # Pull one image from the Test Set\n    data, targets = next(iter(test_loader))\n    data = data.to(device)\n    \n    with torch.no_grad():\n        output = torch.sigmoid(model(data))\n        prediction = (output > 0.5).float()\n\n    plt.figure(figsize=(12, 4))\n    plt.subplot(1, 3, 1); plt.imshow(data[0].cpu().permute(1,2,0)); plt.title(\"Test Image (Unseen)\")\n    plt.subplot(1, 3, 2); plt.imshow(targets[0].cpu(), cmap=\"gray\"); plt.title(\"True Mask\")\n    plt.subplot(1, 3, 3); plt.imshow(prediction[0].cpu().squeeze(), cmap=\"gray\"); plt.title(\"AI Prediction\")\n    plt.show()\n\n# Run this after training:\nfinal_exam()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-30T01:33:47.291928Z","iopub.execute_input":"2026-03-30T01:33:47.292314Z","iopub.status.idle":"2026-03-30T01:33:47.595979Z","shell.execute_reply.started":"2026-03-30T01:33:47.292283Z","shell.execute_reply":"2026-03-30T01:33:47.595153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}