{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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"},"accelerator":"GPU"},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"aef77760-b6b8-45a0-920c-6cf795d0b60e","cell_type":"markdown","source":"# CIFAR-10 \n","metadata":{}},{"id":"fc6b7a15-31c4-4616-ad2f-393512452061","cell_type":"code","source":"# 7z extraction for the Kaggle test set (pure-Python, installs cleanly on Kaggle)\n!pip -q install py7zr","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T17:11:39.069152Z","iopub.execute_input":"2026-09-22T17:11:39.06941Z","iopub.status.idle":"2026-09-22T17:11:44.781652Z","shell.execute_reply.started":"2026-09-22T17:11:39.069387Z","shell.execute_reply":"2026-09-22T17:11:44.780854Z"}},"outputs":[],"execution_count":null},{"id":"392dfca2-31db-41f7-84d8-1e190098f1b2","cell_type":"code","source":"import os, random, glob, time\nimport numpy as np\nimport torch, torch.nn as nn, torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision\nfrom torchvision import transforms\nfrom PIL import Image\nimport pandas as pd\n\nSEED = 42\nrandom.seed(SEED); np.random.seed(SEED)\ntorch.manual_seed(SEED); torch.cuda.manual_seed_all(SEED)\ntorch.backends.cudnn.benchmark = True\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('Device:', device)\n\nEPOCHS       = 100        # ~30-40 min on a Kaggle GPU; 60 epochs already clears 0.90\nBATCH_SIZE   = 128\nLR           = 0.1\nWEIGHT_DECAY = 5e-4\nNUM_WORKERS  = 2\n\n# Auto-find test.7z wherever Kaggle mounts the competition data\nTEST_7Z = glob.glob('/kaggle/input/**/test.7z', recursive=True)[0]\nprint('Using:', TEST_7Z)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T17:11:44.783367Z","iopub.execute_input":"2026-09-22T17:11:44.784191Z","iopub.status.idle":"2026-09-22T17:11:52.982653Z","shell.execute_reply.started":"2026-09-22T17:11:44.78416Z","shell.execute_reply":"2026-09-22T17:11:52.98187Z"}},"outputs":[],"execution_count":null},{"id":"2d1d0ff9-c151-4b67-a25d-b46ed0bda013","cell_type":"code","source":"# ---- Data & augmentation ----\nMEAN = (0.4914, 0.4822, 0.4465)\nSTD  = (0.2470, 0.2435, 0.2616)\n\ntrain_tf = transforms.Compose([\n    transforms.RandomCrop(32, padding=4, padding_mode='reflect'),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(MEAN, STD),\n    transforms.RandomErasing(p=0.5),        # cutout-style regularisation\n])\ntest_tf = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(MEAN, STD),\n])\n\ntrainset = torchvision.datasets.CIFAR10('./data', train=True,  download=True, transform=train_tf)\ntestset  = torchvision.datasets.CIFAR10('./data', train=False, download=True, transform=test_tf)\nclasses  = trainset.classes    # ['airplane','automobile',...,'truck']  -> index order == label order\nprint(classes)\n\ntrain_loader = DataLoader(trainset, batch_size=BATCH_SIZE, shuffle=True,\n                          num_workers=NUM_WORKERS, pin_memory=True, drop_last=True)\ntest_loader  = DataLoader(testset,  batch_size=256, shuffle=False,\n                          num_workers=NUM_WORKERS, pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T17:11:52.983631Z","iopub.execute_input":"2026-09-22T17:11:52.984089Z","iopub.status.idle":"2026-09-22T17:51:34.822408Z","shell.execute_reply.started":"2026-09-22T17:11:52.984059Z","shell.execute_reply":"2026-09-22T17:51:34.82168Z"}},"outputs":[],"execution_count":null},{"id":"40fa7b0e-ddeb-4fad-979d-3303a5663cc6","cell_type":"code","source":"# ---- CIFAR ResNet-18 (3x3 stem, no initial max-pool; weights randomly initialised) ----\nclass BasicBlock(nn.Module):\n    expansion = 1\n    def __init__(self, in_planes, planes, stride=1):\n        super().__init__()\n        self.conv1 = nn.Conv2d(in_planes, planes, 3, stride, 1, bias=False)\n        self.bn1   = nn.BatchNorm2d(planes)\n        self.conv2 = nn.Conv2d(planes, planes, 3, 1, 1, bias=False)\n        self.bn2   = nn.BatchNorm2d(planes)\n        self.shortcut = nn.Sequential()\n        if stride != 1 or in_planes != planes * self.expansion:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_planes, planes * self.expansion, 1, stride, bias=False),\n                nn.BatchNorm2d(planes * self.expansion))\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n        out += self.shortcut(x)\n        return F.relu(out)\n\nclass ResNet(nn.Module):\n    def __init__(self, block, num_blocks, num_classes=10):\n        super().__init__()\n        self.in_planes = 64\n        self.conv1 = nn.Conv2d(3, 64, 3, 1, 1, bias=False)\n        self.bn1   = nn.BatchNorm2d(64)\n        self.layer1 = self._make_layer(block, 64,  num_blocks[0], 1)\n        self.layer2 = self._make_layer(block, 128, num_blocks[1], 2)\n        self.layer3 = self._make_layer(block, 256, num_blocks[2], 2)\n        self.layer4 = self._make_layer(block, 512, num_blocks[3], 2)\n        self.linear = nn.Linear(512 * block.expansion, num_classes)\n    def _make_layer(self, block, planes, n, stride):\n        strides = [stride] + [1] * (n - 1)\n        layers = []\n        for s in strides:\n            layers.append(block(self.in_planes, planes, s))\n            self.in_planes = planes * block.expansion\n        return nn.Sequential(*layers)\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.layer1(out); out = self.layer2(out)\n        out = self.layer3(out); out = self.layer4(out)\n        out = F.adaptive_avg_pool2d(out, 1).flatten(1)\n        return self.linear(out)\n\ndef ResNet18(): return ResNet(BasicBlock, [2, 2, 2, 2])\n\nmodel = ResNet18().to(device)\nprint('Parameters:', sum(p.numel() for p in model.parameters()) / 1e6, 'M')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T17:51:34.824096Z","iopub.execute_input":"2026-09-22T17:51:34.824729Z","iopub.status.idle":"2026-09-22T17:51:35.228406Z","shell.execute_reply.started":"2026-09-22T17:51:34.824692Z","shell.execute_reply":"2026-09-22T17:51:35.227614Z"}},"outputs":[],"execution_count":null},{"id":"a6017417-197e-4d70-b8df-840ad6eb7968","cell_type":"code","source":"# ---- Training: SGD + Nesterov, cosine LR, label smoothing, mixed precision ----\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\noptimizer = torch.optim.SGD(model.parameters(), lr=LR, momentum=0.9,\n                            weight_decay=WEIGHT_DECAY, nesterov=True)\nscheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS)\nscaler    = torch.cuda.amp.GradScaler()\n\n@torch.no_grad()\ndef evaluate(loader):\n    model.eval(); correct = total = 0\n    for x, y in loader:\n        x, y = x.to(device), y.to(device)\n        with torch.cuda.amp.autocast():\n            out = model(x)\n        correct += (out.argmax(1) == y).sum().item(); total += y.size(0)\n    return correct / total\n\nbest = 0.0\nfor epoch in range(1, EPOCHS + 1):\n    model.train(); t0 = time.time()\n    for x, y in train_loader:\n        x = x.to(device, non_blocking=True); y = y.to(device, non_blocking=True)\n        optimizer.zero_grad(set_to_none=True)\n        with torch.cuda.amp.autocast():\n            loss = criterion(model(x), y)\n        scaler.scale(loss).backward()\n        scaler.step(optimizer); scaler.update()\n    scheduler.step()\n    acc = evaluate(test_loader)\n    if acc > best:\n        best = acc\n        torch.save(model.state_dict(), 'best.pt')\n    print(f'Epoch {epoch:3d}/{EPOCHS} | test acc {acc:.4f} | best {best:.4f} | {time.time()-t0:4.1f}s')\n\nprint(f'\\nBest official CIFAR-10 test accuracy: {best:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T17:51:35.229501Z","iopub.execute_input":"2026-09-22T17:51:35.229831Z","iopub.status.idle":"2026-09-22T18:28:05.930893Z","shell.execute_reply.started":"2026-09-22T17:51:35.229804Z","shell.execute_reply":"2026-09-22T18:28:05.930122Z"}},"outputs":[],"execution_count":null},{"id":"2bdd4a54-4b53-4600-8bf3-9f6e26308415","cell_type":"markdown","source":"","metadata":{}},{"id":"0c04721c-b648-46d8-9a6a-97c19bee939a","cell_type":"code","source":"# ---- Load best checkpoint and confirm the official-test accuracy (~= your Kaggle score) ----\nmodel.load_state_dict(torch.load('best.pt'))\nfinal_acc = evaluate(test_loader)\nprint(f'Official CIFAR-10 test accuracy (estimate of Kaggle score): {final_acc:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T18:28:05.932253Z","iopub.execute_input":"2026-09-22T18:28:05.932539Z","iopub.status.idle":"2026-09-22T18:28:07.58707Z","shell.execute_reply.started":"2026-09-22T18:28:05.93251Z","shell.execute_reply":"2026-09-22T18:28:07.586292Z"}},"outputs":[],"execution_count":null},{"id":"b32cf772-8965-430e-8de2-f4fd4c954218","cell_type":"code","source":"# ---- Decompress the 300,000 Kaggle test images ----\nimport py7zr\nif not os.path.isdir('test'):\n    with py7zr.SevenZipFile(TEST_7Z, mode='r') as z:\n        z.extractall(path='.')       # creates ./test/ with the 300k PNGs\nprint('Images found:', len(glob.glob('test/*.png')))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T18:28:07.588844Z","iopub.execute_input":"2026-09-22T18:28:07.589155Z","iopub.status.idle":"2026-09-22T18:31:42.333438Z","shell.execute_reply.started":"2026-09-22T18:28:07.589125Z","shell.execute_reply":"2026-09-22T18:31:42.332727Z"}},"outputs":[],"execution_count":null},{"id":"be6deffa-f994-4ba3-8d6c-a4f2c70bef67","cell_type":"code","source":"# ---- Predict on the 300k test images and write submission.csv (with flip TTA) ----\nclass KaggleTestSet(Dataset):\n    def __init__(self, ids, folder, transform):\n        self.ids = ids; self.folder = folder; self.t = transform\n    def __len__(self): return len(self.ids)\n    def __getitem__(self, i):\n        idx = self.ids[i]\n        img = Image.open(os.path.join(self.folder, f'{idx}.png')).convert('RGB')\n        return self.t(img), idx\n\nids = list(range(1, 300001))\nkloader = DataLoader(KaggleTestSet(ids, 'test', test_tf), batch_size=512,\n                     shuffle=False, num_workers=NUM_WORKERS, pin_memory=True)\n\nUSE_TTA = True    # average logits of the image and its horizontal flip\nmodel.eval()\npred_ids, pred_lab = [], []\nwith torch.no_grad():\n    for x, idx in kloader:\n        x = x.to(device, non_blocking=True)\n        with torch.cuda.amp.autocast():\n            logits = model(x)\n            if USE_TTA:\n                logits = logits + model(torch.flip(x, dims=[3]))\n        p = logits.argmax(1).cpu().numpy()\n        pred_ids.extend(idx.numpy().tolist())\n        pred_lab.extend(classes[k] for k in p)\n\nsub = pd.DataFrame({'id': pred_ids, 'label': pred_lab}).sort_values('id')\nsub.to_csv('submission.csv', index=False)\nprint(sub.head()); print('Rows:', len(sub))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T18:31:42.334457Z","iopub.execute_input":"2026-09-22T18:31:42.334755Z","iopub.status.idle":"2026-09-22T18:33:00.616311Z","shell.execute_reply.started":"2026-09-22T18:31:42.334724Z","shell.execute_reply":"2026-09-22T18:33:00.615547Z"}},"outputs":[],"execution_count":null}]}