{"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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ----------------------------- CELL 1: setup -----------------------------\nimport os, glob, time\nimport numpy as np, pandas as pd\nimport torch, torch.nn as nn\nimport torchvision, torchvision.transforms as T\nfrom torch.utils.data import DataLoader, Dataset\nfrom PIL import Image\n\ntorch.manual_seed(0)\ndevice = \"cuda\"\ntorch.backends.cudnn.benchmark = True\nprint(torch.__version__, torch.cuda.get_device_name(0))\n","metadata":{"_uuid":"3143d410-fc42-4701-8988-59359ae63b3f","_cell_guid":"96d8fc73-845a-48dd-a692-c9380d8459a0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-09-23T09:49:16.646002Z","iopub.execute_input":"2026-09-23T09:49:16.646648Z","iopub.status.idle":"2026-09-23T09:49:26.811139Z","shell.execute_reply.started":"2026-09-23T09:49:16.646609Z","shell.execute_reply":"2026-09-23T09:49:26.810123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------- CELL 2: data ------------------------------\n# Official CIFAR-10 train (50k) for training, official test (10k) to estimate the Kaggle score.\nMEAN, STD = (0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)\ntrain_tf = T.Compose([\n    T.RandomCrop(32, padding=4, padding_mode=\"reflect\"),\n    T.RandomHorizontalFlip(),\n    T.ToTensor(),\n    T.Normalize(MEAN, STD),\n])\ntest_tf = T.Compose([T.ToTensor(), T.Normalize(MEAN, STD)])\n\ntrain_ds = torchvision.datasets.CIFAR10(\"/tmp/data\", train=True,  download=True, transform=train_tf)\nval_ds   = torchvision.datasets.CIFAR10(\"/tmp/data\", train=False, download=True, transform=test_tf)\ntrain_dl = DataLoader(train_ds, batch_size=512, shuffle=True, num_workers=4,\n                      pin_memory=True, drop_last=True, persistent_workers=True)\nval_dl   = DataLoader(val_ds, batch_size=1024, num_workers=4, pin_memory=True)\n\nCLASSES = train_ds.classes   # same order/names as Kaggle labels\nprint(CLASSES)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-23T09:49:51.664815Z","iopub.execute_input":"2026-09-23T09:49:51.665486Z","iopub.status.idle":"2026-09-23T10:35:47.062985Z","shell.execute_reply.started":"2026-09-23T09:49:51.665454Z","shell.execute_reply":"2026-09-23T10:35:47.062021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------- CELL 3: model -----------------------------\n# ResNet-9 (Myrtle.ai / David Page design). PyTorch default init = random weights.\ndef conv_bn(c_in, c_out, pool=False):\n    layers = [nn.Conv2d(c_in, c_out, 3, padding=1, bias=False),\n              nn.BatchNorm2d(c_out), nn.ReLU(inplace=True)]\n    if pool:\n        layers.append(nn.MaxPool2d(2))\n    return nn.Sequential(*layers)\n\nclass Residual(nn.Module):\n    def __init__(self, c):\n        super().__init__()\n        self.block = nn.Sequential(conv_bn(c, c), conv_bn(c, c))\n    def forward(self, x):\n        return x + self.block(x)\n\ndef ResNet9(num_classes=10):\n    return nn.Sequential(\n        conv_bn(3, 64),\n        conv_bn(64, 128, pool=True),  Residual(128),\n        conv_bn(128, 256, pool=True),\n        conv_bn(256, 512, pool=True), Residual(512),\n        nn.AdaptiveMaxPool2d(1), nn.Flatten(),\n        nn.Linear(512, num_classes),\n    )\n\nmodel = ResNet9().to(device).to(memory_format=torch.channels_last)\nprint(f\"Parameters: {sum(p.numel() for p in model.parameters())/1e6:.2f}M\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-23T10:35:57.480972Z","iopub.execute_input":"2026-09-23T10:35:57.481933Z","iopub.status.idle":"2026-09-23T10:35:57.907924Z","shell.execute_reply.started":"2026-09-23T10:35:57.481889Z","shell.execute_reply":"2026-09-23T10:35:57.906945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ------------------------ CELL 4: helpers + training ---------------------\n@torch.no_grad()\ndef predict(dl, tta=True):\n    \"\"\"Returns predicted class indices. TTA = average with horizontally flipped images.\"\"\"\n    model.eval()\n    preds = []\n    for x, *_ in dl:\n        x = x.to(device, non_blocking=True).to(memory_format=torch.channels_last)\n        with torch.autocast(\"cuda\", dtype=torch.float16):\n            logits = model(x)\n            if tta:\n                logits = logits + model(torch.flip(x, dims=[3]))\n        preds.append(logits.argmax(1).cpu())\n    return torch.cat(preds)\n\ndef val_accuracy(tta=True):\n    return (predict(val_dl, tta) == torch.tensor(val_ds.targets)).float().mean().item()\n\nEPOCHS = 40\nopt = torch.optim.SGD(model.parameters(), lr=0.2, momentum=0.9,\n                      nesterov=True, weight_decay=5e-4)\nsched = torch.optim.lr_scheduler.OneCycleLR(opt, max_lr=0.2, epochs=EPOCHS,\n                                            steps_per_epoch=len(train_dl), pct_start=0.25)\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\nscaler = torch.amp.GradScaler(\"cuda\")\n\nfor epoch in range(EPOCHS):\n    model.train()\n    t0, total_loss, n = time.time(), 0.0, 0\n    for x, y in train_dl:\n        x = x.to(device, non_blocking=True).to(memory_format=torch.channels_last)\n        y = y.to(device, non_blocking=True)\n        with torch.autocast(\"cuda\", dtype=torch.float16):\n            loss = criterion(model(x), y)\n        opt.zero_grad(set_to_none=True)\n        scaler.scale(loss).backward()\n        scaler.step(opt)\n        scaler.update()\n        sched.step()\n        total_loss += loss.item() * x.size(0); n += x.size(0)\n    acc = val_accuracy(tta=False)\n    print(f\"epoch {epoch+1:2d}/{EPOCHS}  loss {total_loss/n:.4f}  \"\n          f\"val_acc {acc:.4f}  ({time.time()-t0:.0f}s)\")\n\nprint(f\"\\nFinal official-test accuracy (with flip TTA): {val_accuracy(tta=True):.4f}\")\ntorch.save(model.state_dict(), \"/kaggle/working/resnet9_cifar10.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-23T10:36:01.728697Z","iopub.execute_input":"2026-09-23T10:36:01.729485Z","iopub.status.idle":"2026-09-23T10:45:45.009963Z","shell.execute_reply.started":"2026-09-23T10:36:01.729454Z","shell.execute_reply":"2026-09-23T10:45:45.008982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------- CELL 5: extract Kaggle test set -----------------\n# Needs the competition data attached (+ Add Input -> Competitions -> cifar-10).\nTEST_7Z = glob.glob(\"/kaggle/input/**/test.7z\", recursive=True)[0]\nprint(TEST_7Z)\nos.system(\"apt-get install -y -q p7zip-full > /dev/null 2>&1\")\nos.system(f\"7z x '{TEST_7Z}' -o/tmp -y > /dev/null\")\nprint(\"Test images:\", len(glob.glob(\"/tmp/test/*.png\")))   # should print 300000\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-23T11:31:46.57556Z","iopub.execute_input":"2026-09-23T11:31:46.576035Z","iopub.status.idle":"2026-09-23T11:43:47.955822Z","shell.execute_reply.started":"2026-09-23T11:31:46.576005Z","shell.execute_reply":"2026-09-23T11:43:47.954956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------- CELL 6: predict + submission --------------------\nclass KaggleTest(Dataset):\n    def __init__(self, root, n=300000):\n        self.root, self.ids = root, list(range(1, n + 1))\n    def __len__(self):\n        return len(self.ids)\n    def __getitem__(self, i):\n        img = Image.open(f\"{self.root}/{self.ids[i]}.png\").convert(\"RGB\")\n        return test_tf(img), self.ids[i]\n\ntest_ds = KaggleTest(\"/tmp/test\")\ntest_dl = DataLoader(test_ds, batch_size=1024, num_workers=4, pin_memory=True)\ntest_preds = predict(test_dl, tta=True).numpy()\n\nsub = pd.DataFrame({\"id\": test_ds.ids, \"label\": [CLASSES[p] for p in test_preds]})\nsub.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(sub.shape)          # (300000, 2)\nprint(sub.head())\nprint(sub.label.value_counts())","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}