{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\n# CELL 1: Import libraries and check the environment\n\nimport os\nimport torch\nimport numpy as np\nimport pandas as pd\n\n# Check GPU availability\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(\"Device:\", device)\n\nif torch.cuda.is_available():\n    print(\"GPU:\", torch.cuda.get_device_name(0))\nelse:\n    print(\"WARNING: GPU is not enabled!\")\n\n# Check CIFAR-10 dataset files\nprint(\"\\nAvailable dataset folders:\")\nprint(os.listdir(\"/kaggle/input\"))\n\nfor folder in os.listdir(\"/kaggle/input\"):\n    path = os.path.join(\"/kaggle/input\", folder)\n    if os.path.isdir(path):\n        print(f\"\\nFiles inside {folder}:\")\n        print(os.listdir(path)[:20])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:30:06.207179Z","iopub.execute_input":"2026-10-10T10:30:06.207483Z","iopub.status.idle":"2026-10-10T10:30:21.191054Z","shell.execute_reply.started":"2026-10-10T10:30:06.207447Z","shell.execute_reply":"2026-10-10T10:30:21.190168Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 2: Explore the CIFAR-10 dataset\n\nimport os\nimport pandas as pd\n\n# Find all files in the competition dataset\ndata_dir = \"/kaggle/input\"\n\nprint(\"CIFAR-10 Dataset Files:\\n\")\n\nfor root, dirs, files in os.walk(data_dir):\n    for file in files:\n        print(os.path.join(root, file))\n\n# Locate the training labels\nlabel_path = None\n\nfor root, dirs, files in os.walk(data_dir):\n    for file in files:\n        if file.lower() == \"trainlabels.csv\":\n            label_path = os.path.join(root, file)\n\n# Load and inspect the labels\nif label_path is not None:\n    train_labels = pd.read_csv(label_path)\n\n    print(\"\\nTraining labels shape:\", train_labels.shape)\n    print(\"\\nFirst 5 rows:\")\n    print(train_labels.head())\n\n    print(\"\\nNumber of images per class:\")\n    print(train_labels[\"label\"].value_counts())\nelse:\n    print(\"\\nTraining labels file not found.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:30:37.765279Z","iopub.execute_input":"2026-10-10T10:30:37.765747Z","iopub.status.idle":"2026-10-10T10:30:37.896881Z","shell.execute_reply.started":"2026-10-10T10:30:37.765673Z","shell.execute_reply":"2026-10-10T10:30:37.896031Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 3: Extract CIFAR-10 training images\n\nimport os\nimport subprocess\nimport shutil\n\n# Location of compressed training images\ntrain_archive = \"/kaggle/input/competitions/cifar-10/train.7z\"\n\n# Folder where extracted images will be stored\nextract_dir = \"/kaggle/working/cifar10\"\n\nos.makedirs(extract_dir, exist_ok=True)\n\n# Check whether the training images are already extracted\ntrain_dir = os.path.join(extract_dir, \"train\")\n\nif not os.path.exists(train_dir) or len(os.listdir(train_dir)) != 50000:\n\n    print(\"Extracting training images...\")\n\n    # Find the 7-Zip program\n    seven_zip = shutil.which(\"7z\") or shutil.which(\"7za\")\n\n    if seven_zip is None:\n        raise RuntimeError(\"7-Zip is not available.\")\n\n    # Extract the training images\n    subprocess.run(\n        [seven_zip, \"x\", train_archive, f\"-o{extract_dir}\", \"-y\"],\n        check=True,\n        stdout=subprocess.DEVNULL\n    )\n\n    print(\"Extraction completed!\")\n\nelse:\n    print(\"Training images already extracted.\")\n\n# Count extracted images\nimage_files = [\n    f for f in os.listdir(train_dir)\n    if f.lower().endswith((\".png\", \".jpg\", \".jpeg\"))\n]\n\nprint(\"\\nTotal extracted training images:\", len(image_files))\nprint(\"Example image files:\", image_files[:5])\n\nassert len(image_files) == 50000, \"Expected 50,000 training images.\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:31:40.095567Z","iopub.execute_input":"2026-10-10T10:31:40.096353Z","iopub.status.idle":"2026-10-10T10:31:56.644533Z","shell.execute_reply.started":"2026-10-10T10:31:40.096324Z","shell.execute_reply":"2026-10-10T10:31:56.643771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 4: Visualize CIFAR-10 training images\n\nimport os\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\n# Load the labels\nlabels_df = pd.read_csv(\n    \"/kaggle/input/competitions/cifar-10/trainLabels.csv\"\n)\n\n# Create a figure showing 10 images\nplt.figure(figsize=(15, 6))\n\n# Display one image from each class\nfor i, class_name in enumerate(sorted(labels_df[\"label\"].unique())):\n\n    # Find the first image belonging to this class\n    image_id = labels_df[\n        labels_df[\"label\"] == class_name\n    ][\"id\"].iloc[0]\n\n    # Locate the image\n    image_path = os.path.join(train_dir, f\"{image_id}.png\")\n\n    # Open the image\n    image = Image.open(image_path)\n\n    # Display the image\n    plt.subplot(2, 5, i + 1)\n    plt.imshow(image)\n    plt.title(class_name)\n    plt.axis(\"off\")\n\nplt.suptitle(\"CIFAR-10 Training Images\", fontsize=16)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:33:16.370186Z","iopub.execute_input":"2026-10-10T10:33:16.370875Z","iopub.status.idle":"2026-10-10T10:33:17.017751Z","shell.execute_reply.started":"2026-10-10T10:33:16.370847Z","shell.execute_reply":"2026-10-10T10:33:17.017051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 5: Prepare training and validation data\n\nfrom sklearn.model_selection import train_test_split\nimport numpy as np\n\n# Define the 10 CIFAR-10 classes\nclass_names = sorted(labels_df[\"label\"].unique())\n\n# Convert class names into numerical labels\nlabel_to_idx = {\n    name: idx for idx, name in enumerate(class_names)\n}\n\nlabels_df[\"label_idx\"] = labels_df[\"label\"].map(label_to_idx)\n\n# Split the 50,000 images into 90% training and 10% validation\ntrain_df, val_df = train_test_split(\n    labels_df,\n    test_size=0.10,\n    random_state=42,\n    stratify=labels_df[\"label_idx\"]\n)\n\n# Reset indices\ntrain_df = train_df.reset_index(drop=True)\nval_df = val_df.reset_index(drop=True)\n\n# Print results\nprint(\"Training images:\", len(train_df))\nprint(\"Validation images:\", len(val_df))\n\nprint(\"\\nClass mapping:\")\nprint(label_to_idx)\n\nprint(\"\\nTraining class distribution:\")\nprint(train_df[\"label\"].value_counts().sort_index())\n\nprint(\"\\nValidation class distribution:\")\nprint(val_df[\"label\"].value_counts().sort_index())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:35:13.103211Z","iopub.execute_input":"2026-10-10T10:35:13.103507Z","iopub.status.idle":"2026-10-10T10:35:14.12226Z","shell.execute_reply.started":"2026-10-10T10:35:13.103484Z","shell.execute_reply":"2026-10-10T10:35:14.121526Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 6: Advanced Data Augmentation\n# Cutout-style Random Erasing + CutMix\n\nimport torch\nimport numpy as np\nfrom torchvision import transforms\n\n# CIFAR-10 normalization values\nmean = (0.4914, 0.4822, 0.4465)\nstd = (0.2470, 0.2435, 0.2616)\n\n# Training transformations\ntrain_transform = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.ColorJitter(\n        brightness=0.1,\n        contrast=0.1,\n        saturation=0.1\n    ),\n    transforms.ToTensor(),\n    transforms.Normalize(mean, std),\n\n    # Advanced augmentation: Cutout-style erasing\n    transforms.RandomErasing(\n        p=0.25,\n        scale=(0.02, 0.12),\n        ratio=(0.3, 3.3),\n        value=\"random\"\n    )\n])\n\n# Validation transformations\n# No random augmentation during validation\nval_transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(mean, std)\n])\n\n# Advanced augmentation: CutMix\ndef cutmix_data(images, labels, alpha=1.0):\n\n    # Randomly choose mixing strength\n    lam = np.random.beta(alpha, alpha)\n\n    # Shuffle images within the batch\n    batch_size = images.size(0)\n    indices = torch.randperm(\n        batch_size, device=images.device\n    )\n\n    shuffled_labels = labels[indices]\n\n    # Image dimensions\n    _, _, H, W = images.shape\n\n    # Calculate random rectangle\n    cut_ratio = np.sqrt(1.0 - lam)\n    cut_w = int(W * cut_ratio)\n    cut_h = int(H * cut_ratio)\n\n    cx = np.random.randint(W)\n    cy = np.random.randint(H)\n\n    x1 = np.clip(cx - cut_w // 2, 0, W)\n    x2 = np.clip(cx + cut_w // 2, 0, W)\n    y1 = np.clip(cy - cut_h // 2, 0, H)\n    y2 = np.clip(cy + cut_h // 2, 0, H)\n\n    # Replace part of each image\n    mixed_images = images.clone()\n\n    mixed_images[:, :, y1:y2, x1:x2] = (\n        images[indices, :, y1:y2, x1:x2]\n    )\n\n    # Adjust mixing ratio to actual rectangle area\n    lam = 1.0 - (\n        (x2 - x1) * (y2 - y1) / (W * H)\n    )\n\n    return mixed_images, labels, shuffled_labels, lam\n\nprint(\"Training transformations created!\")\nprint(\"Validation transformations created!\")\nprint(\"CutMix function created!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:36:46.419503Z","iopub.execute_input":"2026-10-10T10:36:46.420244Z","iopub.status.idle":"2026-10-10T10:36:51.212767Z","shell.execute_reply.started":"2026-10-10T10:36:46.420216Z","shell.execute_reply":"2026-10-10T10:36:51.21211Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 7: Create PyTorch Dataset and DataLoaders\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport os\n\n# Custom CIFAR-10 Dataset\nclass CIFAR10CustomDataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transform=None):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.image_dir = image_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n\n        # Get image ID and class label\n        row = self.dataframe.iloc[idx]\n\n        image_id = int(row[\"id\"])\n        label = int(row[\"label_idx\"])\n\n        # Load the image\n        image_path = os.path.join(\n            self.image_dir, f\"{image_id}.png\"\n        )\n\n        image = Image.open(image_path).convert(\"RGB\")\n\n        # Apply transformations\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n\n# Create training dataset\ntrain_dataset = CIFAR10CustomDataset(\n    dataframe=train_df,\n    image_dir=train_dir,\n    transform=train_transform\n)\n\n# Create validation dataset\nval_dataset = CIFAR10CustomDataset(\n    dataframe=val_df,\n    image_dir=train_dir,\n    transform=val_transform\n)\n\n\n# DataLoader settings\nBATCH_SIZE = 128\nNUM_WORKERS = 2\n\n# Training DataLoader\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=NUM_WORKERS,\n    pin_memory=torch.cuda.is_available()\n)\n\n# Validation DataLoader\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=NUM_WORKERS,\n    pin_memory=torch.cuda.is_available()\n)\n\n\n# Check dataset sizes\nprint(\"Training images:\", len(train_dataset))\nprint(\"Validation images:\", len(val_dataset))\n\nprint(\"\\nTraining batches:\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))\n\n# Check one batch\nimages, labels = next(iter(train_loader))\n\nprint(\"\\nImage batch shape:\", images.shape)\nprint(\"Label batch shape:\", labels.shape)\n\nprint(\"\\nDataLoaders created successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:37:55.228595Z","iopub.execute_input":"2026-10-10T10:37:55.229244Z","iopub.status.idle":"2026-10-10T10:37:56.362919Z","shell.execute_reply.started":"2026-10-10T10:37:55.229217Z","shell.execute_reply":"2026-10-10T10:37:56.362057Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 8: Build Our Custom Advanced CNN\n# All weights initialized from scratch\n# No pretrained models\n\nimport torch\nimport torch.nn as nn\n\n\n# 1. Channel Attention Module\nclass ChannelAttention(nn.Module):\n\n    def __init__(self, channels, reduction=8):\n        super().__init__()\n\n        hidden = max(channels // reduction, 8)\n\n        self.attention = nn.Sequential(\n            nn.AdaptiveAvgPool2d(1),\n            nn.Conv2d(channels, hidden, 1),\n            nn.SiLU(),\n            nn.Conv2d(hidden, channels, 1),\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        weights = self.attention(x)\n        return x * weights\n\n\n# 2. Custom Residual Block\nclass CustomResidualBlock(nn.Module):\n\n    def __init__(self, in_channels, out_channels, stride=1):\n        super().__init__()\n\n        self.conv1 = nn.Conv2d(\n            in_channels, out_channels,\n            kernel_size=3, stride=stride,\n            padding=1, bias=False\n        )\n\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.act = nn.SiLU()\n\n        self.conv2 = nn.Conv2d(\n            out_channels, out_channels,\n            kernel_size=3, stride=1,\n            padding=1, bias=False\n        )\n\n        self.bn2 = nn.BatchNorm2d(out_channels)\n\n        self.attention = ChannelAttention(out_channels)\n\n        # Match input and output dimensions\n        if stride != 1 or in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(\n                    in_channels, out_channels,\n                    kernel_size=1, stride=stride,\n                    bias=False\n                ),\n                nn.BatchNorm2d(out_channels)\n            )\n        else:\n            self.shortcut = nn.Identity()\n\n    def forward(self, x):\n\n        identity = self.shortcut(x)\n\n        out = self.act(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n\n        out = self.attention(out)\n\n        out = out + identity\n        out = self.act(out)\n\n        return out\n\n\n# 3. Our Custom CIFAR-10 CNN\nclass AdvancedCIFAR10CNN(nn.Module):\n\n    def __init__(self, num_classes=10):\n        super().__init__()\n\n        # Initial feature extraction\n        self.stem = nn.Sequential(\n            nn.Conv2d(\n                3, 48, kernel_size=3,\n                stride=1, padding=1, bias=False\n            ),\n            nn.BatchNorm2d(48),\n            nn.SiLU()\n        )\n\n        # Four custom residual stages\n        self.stage1 = nn.Sequential(\n            CustomResidualBlock(48, 48),\n            CustomResidualBlock(48, 48)\n        )\n\n        self.stage2 = nn.Sequential(\n            CustomResidualBlock(48, 96, stride=2),\n            CustomResidualBlock(96, 96)\n        )\n\n        self.stage3 = nn.Sequential(\n            CustomResidualBlock(96, 192, stride=2),\n            CustomResidualBlock(192, 192)\n        )\n\n        self.stage4 = nn.Sequential(\n            CustomResidualBlock(192, 320, stride=2),\n            CustomResidualBlock(320, 320)\n        )\n\n        # Classification head\n        self.pool = nn.AdaptiveAvgPool2d(1)\n\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Dropout(p=0.2),\n            nn.Linear(320, num_classes)\n        )\n\n        # Explicit random initialization\n        self.apply(self._initialize_weights)\n\n    def _initialize_weights(self, module):\n\n        if isinstance(module, nn.Conv2d):\n            nn.init.kaiming_normal_(\n                module.weight,\n                mode=\"fan_out\",\n                nonlinearity=\"relu\"\n            )\n\n        elif isinstance(module, nn.BatchNorm2d):\n            nn.init.ones_(module.weight)\n            nn.init.zeros_(module.bias)\n\n        elif isinstance(module, nn.Linear):\n            nn.init.normal_(module.weight, 0, 0.01)\n            nn.init.zeros_(module.bias)\n\n    def forward(self, x):\n\n        x = self.stem(x)\n\n        x = self.stage1(x)\n        x = self.stage2(x)\n        x = self.stage3(x)\n        x = self.stage4(x)\n\n        x = self.pool(x)\n        x = self.classifier(x)\n\n        return x\n\n\n# 4. Create our model\nmodel = AdvancedCIFAR10CNN(\n    num_classes=10\n).to(device)\n\n\n# 5. Count trainable parameters\ntotal_params = sum(\n    p.numel() for p in model.parameters()\n)\n\ntrainable_params = sum(\n    p.numel()\n    for p in model.parameters()\n    if p.requires_grad\n)\n\nprint(\"Custom Advanced CNN Created!\")\nprint(\"Device:\", device)\nprint(f\"Total Parameters: {total_params:,}\")\nprint(f\"Trainable Parameters: {trainable_params:,}\")\n\n\n# 6. Test a small input\nmodel.eval()\n\nwith torch.no_grad():\n    dummy_input = torch.randn(\n        2, 3, 32, 32,\n        device=device\n    )\n\n    output = model(dummy_input)\n\nprint(\"Input Shape:\", dummy_input.shape)\nprint(\"Output Shape:\", output.shape)\n\nassert output.shape == (2, 10)\n\nprint(\"Model architecture test passed!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:39:05.351547Z","iopub.execute_input":"2026-10-10T10:39:05.352038Z","iopub.status.idle":"2026-10-10T10:39:07.841855Z","shell.execute_reply.started":"2026-10-10T10:39:05.352007Z","shell.execute_reply":"2026-10-10T10:39:07.840087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 9: AdamW Optimizer + Warm-up + Cosine Annealing\n\nimport torch\nimport torch.nn as nn\nimport math\n\n# 1. Training hyperparameters\nEPOCHS = 120\nWARMUP_EPOCHS = 5\n\nBASE_LR = 0.001\nMIN_LR = 0.000001\nWEIGHT_DECAY = 0.01\n\n# 2. Loss function\ncriterion = nn.CrossEntropyLoss()\n\n# 3. Advanced optimizer: AdamW\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=BASE_LR,\n    weight_decay=WEIGHT_DECAY\n)\n\n# 4. Warm-up + Cosine Annealing scheduler\ndef lr_lambda(epoch):\n\n    # Warm-up phase\n    if epoch < WARMUP_EPOCHS:\n        return (epoch + 1) / WARMUP_EPOCHS\n\n    # Cosine annealing phase\n    progress = (epoch - WARMUP_EPOCHS) / max(\n        1, EPOCHS - WARMUP_EPOCHS - 1\n    )\n\n    progress = min(progress, 1.0)\n\n    cosine = 0.5 * (1 + math.cos(math.pi * progress))\n\n    min_factor = MIN_LR / BASE_LR\n\n    return min_factor + (1 - min_factor) * cosine\n\n\nscheduler = torch.optim.lr_scheduler.LambdaLR(\n    optimizer,\n    lr_lambda=lr_lambda\n)\n\n# 5. Display configuration\nprint(\"Optimizer: AdamW\")\nprint(\"Loss Function: CrossEntropyLoss\")\nprint(\"Total Epochs:\", EPOCHS)\nprint(\"Warm-up Epochs:\", WARMUP_EPOCHS)\nprint(\"Maximum Learning Rate:\", BASE_LR)\nprint(\"Minimum Learning Rate:\", MIN_LR)\nprint(\"Weight Decay:\", WEIGHT_DECAY)\n\nprint(\"\\nCurrent Learning Rate:\",\n      optimizer.param_groups[0][\"lr\"])\n\nprint(\"\\nOptimizer and Scheduler created successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:40:12.905844Z","iopub.execute_input":"2026-10-10T10:40:12.906301Z","iopub.status.idle":"2026-10-10T10:40:12.915392Z","shell.execute_reply.started":"2026-10-10T10:40:12.906263Z","shell.execute_reply":"2026-10-10T10:40:12.914816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 10: Train Our Advanced CNN\n# AdamW + Warm-up + Cosine Annealing + CutMix\n# Automatically save the best model\n\nimport os\nimport time\nimport numpy as np\nimport torch\nfrom tqdm.auto import tqdm\n\n# Improve convolution performance\ntorch.backends.cudnn.benchmark = True\n\n# Mixed precision for faster GPU training\nUSE_AMP = (device.type == \"cuda\")\n\nscaler = torch.amp.GradScaler(\n    \"cuda\", enabled=USE_AMP\n)\n\n# Training history\nhistory = {\n    \"train_loss\": [],\n    \"val_loss\": [],\n    \"val_acc\": [],\n    \"lr\": []\n}\n\nbest_acc = 0.0\nbest_epoch = 0\n\nbest_model_path = \"/kaggle/working/best_advanced_cnn.pth\"\ncheckpoint_path = \"/kaggle/working/latest_checkpoint.pth\"\n\nprint(\"Starting Advanced CNN Training...\")\nprint(\"Device:\", device)\nprint(\"Total epochs:\", EPOCHS)\nprint(\"CutMix: Enabled\")\nprint(\"Optimizer: AdamW\")\nprint(\"Scheduler: Warm-up + Cosine Annealing\")\nprint(\"-\" * 60)\n\n# Training loop\nfor epoch in range(EPOCHS):\n\n    start_time = time.time()\n\n    # TRAINING\n    model.train()\n\n    running_loss = 0.0\n    total_train = 0\n\n    current_lr = optimizer.param_groups[0][\"lr\"]\n\n    progress = tqdm(\n        train_loader,\n        desc=f\"Epoch {epoch+1}/{EPOCHS}\",\n        leave=False\n    )\n\n    for images, labels in progress:\n\n        images = images.to(\n            device, non_blocking=True\n        )\n\n        labels = labels.to(\n            device, non_blocking=True\n        )\n\n        optimizer.zero_grad(set_to_none=True)\n\n        # Apply CutMix with 50% probability\n        use_cutmix = np.random.rand() < 0.5\n\n        if use_cutmix:\n            images, labels_a, labels_b, lam = cutmix_data(\n                images, labels, alpha=1.0\n            )\n\n        # Forward pass with mixed precision\n        with torch.amp.autocast(\n            device_type=\"cuda\",\n            enabled=USE_AMP\n        ):\n\n            outputs = model(images)\n\n            if use_cutmix:\n                loss = (\n                    lam * criterion(outputs, labels_a)\n                    + (1 - lam) * criterion(outputs, labels_b)\n                )\n            else:\n                loss = criterion(outputs, labels)\n\n        # Backpropagation\n        scaler.scale(loss).backward()\n\n        # Update weights\n        scaler.step(optimizer)\n        scaler.update()\n\n        running_loss += loss.item() * images.size(0)\n        total_train += images.size(0)\n\n        progress.set_postfix(\n            loss=f\"{loss.item():.4f}\"\n        )\n\n    train_loss = running_loss / total_train\n\n    # VALIDATION\n    model.eval()\n\n    val_loss_sum = 0.0\n    correct = 0\n    total_val = 0\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(\n                device, non_blocking=True\n            )\n\n            labels = labels.to(\n                device, non_blocking=True\n            )\n\n            with torch.amp.autocast(\n                device_type=\"cuda\",\n                enabled=USE_AMP\n            ):\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n\n            val_loss_sum += loss.item() * images.size(0)\n\n            predictions = outputs.argmax(dim=1)\n\n            correct += (\n                predictions == labels\n            ).sum().item()\n\n            total_val += labels.size(0)\n\n    val_loss = val_loss_sum / total_val\n    val_acc = 100.0 * correct / total_val\n\n    # Update learning rate for next epoch\n    scheduler.step()\n\n    # Store results\n    history[\"train_loss\"].append(train_loss)\n    history[\"val_loss\"].append(val_loss)\n    history[\"val_acc\"].append(val_acc)\n    history[\"lr\"].append(current_lr)\n\n    # Save best model\n    if val_acc > best_acc:\n\n        best_acc = val_acc\n        best_epoch = epoch + 1\n\n        torch.save(\n            model.state_dict(),\n            best_model_path\n        )\n\n        print(\"New best model saved!\")\n\n    # Save latest checkpoint for recovery\n    torch.save(\n        {\n            \"epoch\": epoch + 1,\n            \"model_state_dict\": model.state_dict(),\n            \"optimizer_state_dict\": optimizer.state_dict(),\n            \"scheduler_state_dict\": scheduler.state_dict(),\n            \"scaler_state_dict\": scaler.state_dict(),\n            \"best_acc\": best_acc,\n            \"best_epoch\": best_epoch,\n            \"history\": history\n        },\n        checkpoint_path\n    )\n\n    elapsed = time.time() - start_time\n\n    print(\n        f\"Epoch [{epoch+1}/{EPOCHS}] | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Val Acc: {val_acc:.2f}% | \"\n        f\"Best: {best_acc:.2f}% | \"\n        f\"LR: {current_lr:.6f} | \"\n        f\"Time: {elapsed:.1f}s\"\n    )\n\nprint(\"\\nTraining Completed!\")\nprint(f\"Best Validation Accuracy: {best_acc:.2f}%\")\nprint(f\"Best Epoch: {best_epoch}\")\nprint(f\"Best Model Saved: {best_model_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T10:41:51.048559Z","iopub.execute_input":"2026-10-10T10:41:51.049038Z","iopub.status.idle":"2026-10-10T11:41:04.653324Z","shell.execute_reply.started":"2026-10-10T10:41:51.04901Z","shell.execute_reply":"2026-10-10T11:41:04.652418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 11: Visualize Training Results and Load Best Model\n\nimport os\nimport torch\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# 1. Load the best saved model\nbest_model_path = \"/kaggle/working/best_advanced_cnn.pth\"\n\nassert os.path.exists(best_model_path), \"Best model file not found!\"\n\nmodel.load_state_dict(\n    torch.load(\n        best_model_path,\n        map_location=device,\n        weights_only=True\n    )\n)\n\nmodel.eval()\n\nprint(\"Best model loaded successfully!\")\nprint(f\"Best Validation Accuracy: {best_acc:.2f}%\")\nprint(f\"Best Epoch: {best_epoch}\")\n\n# 2. Prepare graph data\nepochs = np.arange(1, len(history[\"train_loss\"]) + 1)\n\n# 3. Plot Training and Validation Loss\nplt.figure(figsize=(10, 5))\n\nplt.plot(\n    epochs,\n    history[\"train_loss\"],\n    label=\"Training Loss\"\n)\n\nplt.plot(\n    epochs,\n    history[\"val_loss\"],\n    label=\"Validation Loss\"\n)\n\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training and Validation Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()\n\n# 4. Plot Validation Accuracy\nplt.figure(figsize=(10, 5))\n\nplt.plot(\n    epochs,\n    history[\"val_acc\"],\n    label=\"Validation Accuracy\"\n)\n\nplt.axhline(\n    y=94.2,\n    color=\"red\",\n    linestyle=\"--\",\n    label=\"94.2% Target\"\n)\n\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Validation Accuracy (%)\")\nplt.title(\"CNN Validation Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.show()\n\n# 5. Plot Learning Rate\nplt.figure(figsize=(10, 5))\n\nplt.plot(\n    epochs,\n    history[\"lr\"],\n    label=\"Learning Rate\"\n)\n\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Learning Rate\")\nplt.title(\"Warm-up + Cosine Annealing Schedule\")\nplt.legend()\nplt.grid(True)\nplt.show()\n\nprint(\"\\nAll training graphs generated successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T11:43:31.750723Z","iopub.execute_input":"2026-10-10T11:43:31.751247Z","iopub.status.idle":"2026-10-10T11:44:52.421282Z","shell.execute_reply.started":"2026-10-10T11:43:31.751216Z","shell.execute_reply":"2026-10-10T11:44:52.42046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 12: Extract CIFAR-10 Kaggle Test Images\n\nimport os\nimport subprocess\nimport shutil\nimport time\n\n# 1. Path to compressed test dataset\ntest_archive = \"/kaggle/input/competitions/cifar-10/test.7z\"\n\n# 2. Extract to the working directory\nextract_dir = \"/kaggle/working/cifar10\"\ntest_dir = os.path.join(extract_dir, \"test\")\n\nos.makedirs(extract_dir, exist_ok=True)\n\n# 3. Count any previously extracted images\ndef count_images(folder):\n    if not os.path.isdir(folder):\n        return 0\n\n    return sum(\n        1 for f in os.scandir(folder)\n        if f.is_file() and f.name.lower().endswith(\".png\")\n    )\n\nexisting = count_images(test_dir)\n\nprint(\"Existing test images:\", existing)\n\n# 4. Extract if necessary\nif existing != 300000:\n\n    print(\"\\nExtracting Kaggle test images...\")\n    start_time = time.time()\n\n    seven_zip = shutil.which(\"7z\") or shutil.which(\"7za\")\n\n    if seven_zip is None:\n        raise RuntimeError(\"7-Zip is not installed.\")\n\n    subprocess.run(\n        [\n            seven_zip,\n            \"x\",\n            test_archive,\n            f\"-o{extract_dir}\",\n            \"-y\"\n        ],\n        check=True,\n        stdout=subprocess.DEVNULL\n    )\n\n    print(\"Extraction completed!\")\n    print(\n        \"Extraction time:\",\n        round(time.time() - start_time, 2),\n        \"seconds\"\n    )\n\nelse:\n    print(\"Test images already extracted.\")\n\n# 5. Verify extraction\ntotal_test = count_images(test_dir)\n\nprint(\"\\nTotal Kaggle test images:\", total_test)\n\nassert total_test == 300000, \\\n    \"Expected 300,000 test images.\"\n\nprint(\"\\nTest dataset is ready for prediction!\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T11:44:52.422608Z","iopub.execute_input":"2026-10-10T11:44:52.422923Z","iopub.status.idle":"2026-10-10T11:46:01.507057Z","shell.execute_reply.started":"2026-10-10T11:44:52.422901Z","shell.execute_reply":"2026-10-10T11:46:01.506429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 13: Predict Kaggle Test Images with TTA\n\nimport os\nimport torch\nimport numpy as np\nimport pandas as pd\n\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom tqdm.auto import tqdm\n\n# 1. Load Kaggle submission template\nsample_path = (\n    \"/kaggle/input/competitions/cifar-10/\"\n    \"sampleSubmission.csv\"\n)\n\nsample_submission = pd.read_csv(sample_path)\n\ntest_ids = sample_submission[\"id\"].astype(int).tolist()\n\nprint(\"Total test images:\", len(test_ids))\n\nassert len(test_ids) == 300000\nassert sample_submission[\"id\"].is_unique\n\n\n# 2. Create test dataset\nclass CIFAR10TestDataset(Dataset):\n\n    def __init__(self, image_dir, image_ids, transform):\n        self.image_dir = image_dir\n        self.image_ids = image_ids\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_ids)\n\n    def __getitem__(self, idx):\n\n        image_id = self.image_ids[idx]\n\n        image_path = os.path.join(\n            self.image_dir,\n            f\"{image_id}.png\"\n        )\n\n        image = Image.open(image_path).convert(\"RGB\")\n\n        image = self.transform(image)\n\n        return image\n\n\n# 3. Create DataLoader\ntest_dataset = CIFAR10TestDataset(\n    image_dir=test_dir,\n    image_ids=test_ids,\n    transform=val_transform\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=256,\n    shuffle=False,\n    num_workers=0,\n    pin_memory=torch.cuda.is_available()\n)\n\nprint(\"Test batches:\", len(test_loader))\n\n\n# 4. Load best CNN weights\nmodel.load_state_dict(\n    torch.load(\n        \"/kaggle/working/best_advanced_cnn.pth\",\n        map_location=device,\n        weights_only=True\n    )\n)\n\nmodel.eval()\n\nprint(\"\\nBest model loaded!\")\nprint(\"Starting Kaggle predictions...\")\n\n\n# 5. Predict using Test-Time Augmentation\nall_predictions = []\n\nwith torch.inference_mode():\n\n    for images in tqdm(\n        test_loader,\n        desc=\"Predicting test images\"\n    ):\n\n        images = images.to(\n            device,\n            non_blocking=True\n        )\n\n        with torch.amp.autocast(\n            device_type=device.type,\n            enabled=(device.type == \"cuda\")\n        ):\n\n            # Original image prediction\n            outputs_original = model(images)\n\n            # Horizontally flipped image prediction\n            flipped_images = torch.flip(\n                images,\n                dims=[3]\n            )\n\n            outputs_flipped = model(flipped_images)\n\n            # Average both predictions\n            outputs = (\n                outputs_original + outputs_flipped\n            ) / 2\n\n        predictions = outputs.argmax(dim=1)\n\n        all_predictions.extend(\n            predictions.cpu().numpy()\n        )\n\n\n# 6. Convert predictions to NumPy\nall_predictions = np.array(\n    all_predictions,\n    dtype=np.int64\n)\n\n# Verify prediction count and class indices\nassert len(all_predictions) == 300000\nassert np.all(\n    (all_predictions >= 0) &\n    (all_predictions < 10)\n)\n\n# 7. Save predictions\nnp.save(\n    \"/kaggle/working/test_predictions.npy\",\n    all_predictions\n)\n\nprint(\"\\nPrediction completed!\")\nprint(\"Total predictions:\", len(all_predictions))\nprint(\"First 10 predictions:\", all_predictions[:10])\nprint(\"Predictions saved successfully!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T11:47:27.987614Z","iopub.execute_input":"2026-10-10T11:47:27.988391Z","iopub.status.idle":"2026-10-10T11:50:29.264054Z","shell.execute_reply.started":"2026-10-10T11:47:27.988365Z","shell.execute_reply":"2026-10-10T11:50:29.263093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# CELL 14: Generate Kaggle Submission CSV\n\nimport os\nimport numpy as np\nimport pandas as pd\n\n# 1. Load our saved predictions\npredictions_path = \"/kaggle/working/test_predictions.npy\"\n\nassert os.path.exists(predictions_path), \\\n    \"Run Cell 13 first to generate predictions.\"\n\npredictions = np.load(predictions_path)\n\n# 2. Load Kaggle submission template\nsample_path = (\n    \"/kaggle/input/competitions/cifar-10/\"\n    \"sampleSubmission.csv\"\n)\n\nsubmission = pd.read_csv(sample_path)\n\n# 3. Convert numerical predictions to class names\nidx_to_label = {\n    idx: name for name, idx in label_to_idx.items()\n}\n\npredicted_labels = [\n    idx_to_label[int(pred)]\n    for pred in predictions\n]\n\n# 4. Insert predicted class names\nsubmission[\"label\"] = predicted_labels\n\n# 5. Verify submission format\nassert len(submission) == 300000\nassert list(submission.columns) == [\"id\", \"label\"]\nassert submission[\"id\"].is_unique\nassert submission[\"label\"].notna().all()\nassert submission[\"label\"].isin(class_names).all()\nassert np.array_equal(\n    submission[\"id\"].to_numpy(),\n    sample_submission[\"id\"].to_numpy()\n)\n\n# 6. Save Kaggle submission\nsubmission_path = \"/kaggle/working/submission.csv\"\n\nsubmission.to_csv(\n    submission_path,\n    index=False\n)\n\n# 7. Display results\nprint(\"Submission file created successfully!\")\n\nprint(\"\\nSubmission shape:\", submission.shape)\n\nprint(\"\\nFirst 10 predictions:\")\nprint(submission.head(10))\n\nprint(\"\\nPredicted class distribution:\")\nprint(submission[\"label\"].value_counts())\n\nprint(\"\\nFile saved to:\")\nprint(submission_path)\n\nprint(\"\\nReady for Kaggle submission!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-10T11:51:33.942644Z","iopub.execute_input":"2026-10-10T11:51:33.943133Z","iopub.status.idle":"2026-10-10T11:51:34.401459Z","shell.execute_reply.started":"2026-10-10T11:51:33.943107Z","shell.execute_reply":"2026-10-10T11:51:34.400774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}