{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":"# import libraries\nimport os\nimport kagglehub\nimport shutil\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torch.utils.data import DataLoader, Subset, Dataset\nimport torchvision.datasets as datasets\nfrom PIL import Image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-13T11:29:27.229479Z","iopub.execute_input":"2026-08-13T11:29:27.230093Z","iopub.status.idle":"2026-08-13T11:29:27.235340Z","shell.execute_reply.started":"2026-08-13T11:29:27.230061Z","shell.execute_reply":"2026-08-13T11:29:27.234794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Inspect dataset directory structure and file paths\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:03:09.252113Z","iopub.execute_input":"2026-08-12T21:03:09.252836Z","iopub.status.idle":"2026-08-12T21:03:09.260618Z","shell.execute_reply.started":"2026-08-12T21:03:09.252803Z","shell.execute_reply":"2026-08-12T21:03:09.259793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Install p7zip\n!apt-get install p7zip-full -y -qq\n\n# Let's extract the training data\n!7za x /kaggle/input/competitions/cifar-10/train.7z -o/kaggle/working/ -y -bd > /dev/null\n\nprint(\"Successfully extracted\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:03:32.224086Z","iopub.execute_input":"2026-08-12T21:03:32.224512Z","iopub.status.idle":"2026-08-12T21:10:14.014572Z","shell.execute_reply.started":"2026-08-12T21:03:32.224486Z","shell.execute_reply":"2026-08-12T21:10:14.013618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# competition labels\ndf = pd.read_csv('/kaggle/input/competitions/cifar-10/trainLabels.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:12:12.093429Z","iopub.execute_input":"2026-08-12T21:12:12.093883Z","iopub.status.idle":"2026-08-12T21:12:12.130419Z","shell.execute_reply.started":"2026-08-12T21:12:12.093851Z","shell.execute_reply":"2026-08-12T21:12:12.129515Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# PyTorch's ImageFolder expects images organized in class directories.\n# Below, we reorganize the images into separate folders per category.\nfor _, row in df.iterrows():\n    img_id, label = row['id'], row['label']\n    \n    # Create target directory for the class\n    target_dir = f\"/kaggle/working/train_organized/{label}\"\n    os.makedirs(target_dir, exist_ok=True)\n    \n    # Move the image to its class folder\n    shutil.move(f\"/kaggle/working/train/{img_id}.png\", f\"{target_dir}/{img_id}.png\")\n\nprint(\"Images successfully reorganized\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:12:21.630801Z","iopub.execute_input":"2026-08-12T21:12:21.631274Z","iopub.status.idle":"2026-08-12T21:12:26.307224Z","shell.execute_reply.started":"2026-08-12T21:12:21.631211Z","shell.execute_reply":"2026-08-12T21:12:26.306603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Temp dataset\ntemp_ds = datasets.ImageFolder('/kaggle/working/train_organized', transform=transforms.ToTensor())\nloader = DataLoader(temp_ds, batch_size=256)\n\nmean, std, total = torch.zeros(3), torch.zeros(3), 0\nfor imgs, _ in loader:\n    b = imgs.size(0)\n    imgs = imgs.view(b, 3, -1)\n    mean += imgs.mean(2).sum(0)\n    std += imgs.std(2).sum(0)\n    total += b\n\nmean_calc = (mean / total).tolist()\nstd_calc = (std / total).tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:15:52.686791Z","iopub.execute_input":"2026-08-12T21:15:52.687440Z","iopub.status.idle":"2026-08-12T21:16:10.048535Z","shell.execute_reply.started":"2026-08-12T21:15:52.687409Z","shell.execute_reply":"2026-08-12T21:16:10.047836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mean_calc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:21:50.810093Z","iopub.execute_input":"2026-08-12T21:21:50.810661Z","iopub.status.idle":"2026-08-12T21:21:50.816942Z","shell.execute_reply.started":"2026-08-12T21:21:50.810631Z","shell.execute_reply":"2026-08-12T21:21:50.816307Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"std_calc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:21:53.079440Z","iopub.execute_input":"2026-08-12T21:21:53.079708Z","iopub.status.idle":"2026-08-12T21:21:53.084642Z","shell.execute_reply.started":"2026-08-12T21:21:53.079687Z","shell.execute_reply":"2026-08-12T21:21:53.084001Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define transforms\ntrain_transform = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize((0.4914,0.4822,0.4465),(0.2023,0.1994,0.2010))\n    ])\n\neval_transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.4914,0.4822,0.4465),(0.2023,0.1994,0.2010))\n    ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:22:03.840442Z","iopub.execute_input":"2026-08-12T21:22:03.840814Z","iopub.status.idle":"2026-08-12T21:22:03.845465Z","shell.execute_reply.started":"2026-08-12T21:22:03.840787Z","shell.execute_reply":"2026-08-12T21:22:03.844779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load and split dataset\ntrain_dataset = datasets.ImageFolder(root='/kaggle/working/train_organized', transform=train_transform)\nval_dataset = datasets.ImageFolder(root='/kaggle/working/train_organized', transform=eval_transform)\n\nindices = torch.randperm(len(train_dataset)).tolist()\ntrainset = Subset(train_dataset, indices[:40000])\nvalset = Subset(val_dataset, indices[40000:])\n\n# Dataloader\ntrainloader = DataLoader(trainset, batch_size=64, shuffle=True)\nvalloader = DataLoader(valset, batch_size=64, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:22:40.685648Z","iopub.execute_input":"2026-08-12T21:22:40.686047Z","iopub.status.idle":"2026-08-12T21:22:40.911494Z","shell.execute_reply.started":"2026-08-12T21:22:40.686019Z","shell.execute_reply":"2026-08-12T21:22:40.910663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, labels = next(iter(trainloader))\nprint(images.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:23:12.278592Z","iopub.execute_input":"2026-08-12T21:23:12.279336Z","iopub.status.idle":"2026-08-12T21:23:12.326623Z","shell.execute_reply.started":"2026-08-12T21:23:12.279310Z","shell.execute_reply":"2026-08-12T21:23:12.325890Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class convnet(nn.Module):\n    def __init__(self):\n        super().__init__()\n        # conv layer 1 + conv layer 2 and maxpool\n        # from [64, 3, 32, 32] to [64, 64, 32, 32]:\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride = 1, padding=1)\n        self.batch1 = nn.BatchNorm2d(64)\n        # from [64, 64, 32, 32] to [64, 64, 32, 32]:\n        self.conv2 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride = 1, padding=1)\n        self.batch2 = nn.BatchNorm2d(64)\n        # from [64, 64, 32, 32] to [64, 64, 16, 16]:\n        self.pool_a = nn.MaxPool2d(kernel_size=2) # stride = kernel_size\n        # conv layer 3 + conv layer 4 and maxpool\n        # from [64, 64, 16, 16] to [64, 128, 16, 16]:\n        self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride = 1, padding=1)\n        self.batch3 = nn.BatchNorm2d(128)\n        # from [64, 128, 16, 16] to [64, 128, 16, 16]:\n        self.conv4 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride = 1, padding=1)\n        self.batch4 = nn.BatchNorm2d(128)\n        # from [64, 128, 16, 16] to [64, 128, 8, 8]:\n        self.pool_b = nn.MaxPool2d(kernel_size=2) # stride = kernel_size\n        # conv layer 5\n        # from [64, 128, 8, 8] to [64, 256, 8, 8]:\n        self.conv5 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride = 1, padding=1)\n        self.batch5 = nn.BatchNorm2d(256)\n        # conv layer 6 and gap\n        # from [64, 256, 8, 8] to [64, 256, 8, 8]:\n        self.conv6 = nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride = 1, padding=1)\n        self.batch6 = nn.BatchNorm2d(256)\n        # from [64, 256, 8, 8] to [64, 256, 1, 1]:\n        self.gap = nn.AdaptiveAvgPool2d((1, 1))\n        self.dropout = nn.Dropout(p=0.3)\n        self.fc = nn.Linear(256, 10)\n       \n    def forward(self, x):\n        x = F.relu(self.batch1(self.conv1(x)))\n        x = F.relu(self.batch2(self.conv2(x)))\n        x = self.pool_a(x)\n        x = F.relu(self.batch3(self.conv3(x)))\n        x = F.relu(self.batch4(self.conv4(x)))\n        x = self.pool_b(x)\n        x = F.relu(self.batch5(self.conv5(x)))\n        x = F.relu(self.batch6(self.conv6(x)))\n        x = self.gap(x)\n        x = torch.flatten(x, 1)\n        x = self.dropout(x)\n        x = self.fc(x)\n        \n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:39:17.166920Z","iopub.execute_input":"2026-08-12T21:39:17.167738Z","iopub.status.idle":"2026-08-12T21:39:17.176900Z","shell.execute_reply.started":"2026-08-12T21:39:17.167706Z","shell.execute_reply":"2026-08-12T21:39:17.176027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = convnet().to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:42:57.275595Z","iopub.execute_input":"2026-08-12T21:42:57.276090Z","iopub.status.idle":"2026-08-12T21:42:57.902106Z","shell.execute_reply.started":"2026-08-12T21:42:57.276057Z","shell.execute_reply":"2026-08-12T21:42:57.901174Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 30\npatience = 8\npatience_counter = 0\nbest_val_loss = float('inf')\n\nprint_every_batches = 250\n\nscheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)\n\ntrain_losses, val_losses, val_accuracies = [], [], []\nfor e in range(epochs):\n    running_loss = 0\n    model.train()\n    print(f\"\\n--- Epoch {e + 1}/{epochs} ---\")\n    \n    for batch_idx, (images, labels) in enumerate(trainloader):\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n        \n        if (batch_idx + 1) % print_every_batches == 0:\n            print(f\"[Epoch {e + 1}, Batch {batch_idx + 1:4d}] Loss: {loss.item():.4f}\")\n        \n    else:\n        val_loss = 0\n        accuracy = 0\n        correct_samples = 0\n        total_samples = 0\n        \n        # Turn off gradients for validation, saves memory and computations\n        with torch.no_grad():\n            model.eval()\n            for images, labels in valloader:\n                images, labels = images.to(device), labels.to(device)\n                \n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                val_loss += loss.item()\n    \n                _, top_class = outputs.topk(1, dim=1)\n                equals = top_class == labels.view(*top_class.shape)\n                correct_samples += equals.sum().item()\n                total_samples += labels.size(0)\n        \n        train_losses.append(running_loss/len(trainloader))\n        val_losses.append(val_loss/len(valloader))\n        val_accuracies.append((correct_samples / total_samples) * 100)\n        \n        scheduler.step()\n        \n        print(\"Epoch: {}/{}.. \".format(e+1, epochs),\n              \"Training Loss: {:.3f}.. \".format(train_losses[-1]),\n              \"Valuation Loss: {:.3f}.. \".format(val_losses[-1]),\n              \"Valuation Accuracy: {:.2f}%\".format(val_accuracies[-1]))\n        # early stopping\n        if val_losses[-1] < best_val_loss:\n            best_val_loss = val_losses[-1]\n            patience_counter = 0\n            torch.save(model.state_dict(), \"cifar10_convnet.pth\")\n            print(\"Best Model saved\")\n        else:\n            patience_counter += 1\n            print(f\"No improvement ({patience_counter}/{patience})\")\n            if patience_counter >= patience:\n                print(\"Early Stopping: Training interruption\")\n                break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T21:45:38.519756Z","iopub.execute_input":"2026-08-12T21:45:38.520157Z","iopub.status.idle":"2026-08-12T22:05:35.429697Z","shell.execute_reply.started":"2026-08-12T21:45:38.520129Z","shell.execute_reply":"2026-08-12T22:05:35.428990Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epoch_range = range(1, len(train_losses) + 1)\n\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))\n\nax1.plot(epoch_range, train_losses, 'b-o', label='Training Loss', linewidth=2)\nax1.plot(epoch_range, val_losses, 'r-s', label='Validation Loss', linewidth=2)\nax1.set_title('Training vs Validation Loss', fontsize=14)\nax1.set_xlabel('Epoch', fontsize=12)\nax1.set_ylabel('Loss', fontsize=12)\nax1.legend(fontsize=11)\n\nax2.plot(epoch_range, val_accuracies, 'g-^', label='Validation Accuracy', linewidth=2)\nax2.set_title('Validation Accuracy', fontsize=14)\nax2.set_xlabel('Epoch', fontsize=12)\nax2.set_ylabel('Accuracy (%)', fontsize=12)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T22:09:50.252638Z","iopub.execute_input":"2026-08-12T22:09:50.253406Z","iopub.status.idle":"2026-08-12T22:09:50.616183Z","shell.execute_reply.started":"2026-08-12T22:09:50.253374Z","shell.execute_reply":"2026-08-12T22:09:50.615461Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# load best model saved\nmodel.load_state_dict(torch.load(\"cifar10_convnet.pth\"))\nmodel.to(device)\n\n# evaluation\nmodel.eval()\ncorrect = 0\ntotal = 0\n\nwith torch.no_grad():\n    for images, labels in valloader:\n        images, labels = images.to(device), labels.to(device)\n        \n        outputs = model(images)\n        _, top_class = outputs.topk(1, dim=1)\n        equals = top_class == labels.view(*top_class.shape)\n        \n        correct += equals.sum().item()\n        total += labels.size(0)\n\nprint(f\"Accuracy of the best model saved: {100 * (correct / total):.2f}%\\n\")\n\n# View prediction\nclasses = sorted(df['label'].unique().tolist())\ndef denormalize(tensor, mean=mean_calc, std=std_calc):\n    tensor = tensor.clone()\n    for t, m, s in zip(tensor, mean, std):\n        t.mul_(s).add_(m)\n    return tensor.clamp(0, 1)\n\nimages, labels = next(iter(valloader))\n\nwith torch.no_grad():\n    outputs = model(images.to(device))\n    _, preds = torch.max(outputs, 1)\n\nfig = plt.figure(figsize=(12, 6))\n\nfor idx in range(8):\n    ax = fig.add_subplot(2, 4, idx + 1, xticks=[], yticks=[])\n    img = denormalize(images[idx]).cpu().numpy().transpose((1, 2, 0))\n    ax.imshow(img)\n    \n    pred_label = classes[preds[idx]]\n    true_label = classes[labels[idx]]\n    \n    color = 'green' if pred_label == true_label else 'red'\n    ax.set_title(f\"Pred: {pred_label}\\nActual: {true_label}\", color=color)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T22:10:09.299666Z","iopub.execute_input":"2026-08-12T22:10:09.300463Z","iopub.status.idle":"2026-08-12T22:10:15.049938Z","shell.execute_reply.started":"2026-08-12T22:10:09.300434Z","shell.execute_reply":"2026-08-12T22:10:15.049310Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's extract the test data\n!7za x /kaggle/input/competitions/cifar-10/test.7z -o/kaggle/working/ -y -bd > /dev/null\n\nprint(\"Successfully extracted\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-12T22:16:32.278868Z","iopub.execute_input":"2026-08-12T22:16:32.279282Z","iopub.status.idle":"2026-08-12T23:11:47.584220Z","shell.execute_reply.started":"2026-08-12T22:16:32.279221Z","shell.execute_reply":"2026-08-12T23:11:47.583308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class KaggleTestDataset(Dataset):\n    def __init__(self, img_dir, transform=None):\n        self.img_dir = img_dir\n        self.img_names = sorted(os.listdir(img_dir), key=lambda x: int(x.split('.')[0]))\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.img_names)\n\n    def __getitem__(self, idx):\n        img_name = self.img_names[idx]\n        img_path = os.path.join(self.img_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n            \n        img_id = int(img_name.split('.')[0])\n        return image, img_id\n\n# Load\ntest_dir = '/kaggle/working/test'\ntest_dataset = KaggleTestDataset(img_dir=test_dir, transform=eval_transform)\ntest_loader = DataLoader(test_dataset, batch_size=128, shuffle=False, num_workers=2)\n\n# Inference\nids = []\npredictions = []\n\nwith torch.no_grad():\n    for images, batch_ids in test_loader:\n        images = images.to(device)\n        outputs = model(images)\n        _, preds = torch.max(outputs, 1)\n        \n        ids.extend(batch_ids.numpy())\n        predictions.extend([classes[p] for p in preds.cpu().numpy()])\n\n# Save file\nsubmission_df = pd.DataFrame({\n    'id': ids,\n    'label': predictions\n})\n\nsubmission_df.to_csv('submission.csv', index=False)\nprint(\"File 'submission.csv' ready\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-13T11:21:55.024396Z","iopub.execute_input":"2026-08-13T11:21:55.025092Z","iopub.status.idle":"2026-08-13T11:21:55.035689Z","shell.execute_reply.started":"2026-08-13T11:21:55.025059Z","shell.execute_reply":"2026-08-13T11:21:55.034633Z"}},"outputs":[],"execution_count":null}]}