{"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":"markdown","source":"1.setup","metadata":{}},{"cell_type":"code","source":"import torch, torch.nn as nn, torch.nn.functional as F\nimport torchvision\nimport torchvision.transforms as T\nimport numpy as np, os, glob, time\nfrom PIL import Image\nimport pandas as pd\n\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint('Using device:', device)\ntorch.manual_seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:04:41.130634Z","iopub.execute_input":"2026-09-18T13:04:41.130944Z","iopub.status.idle":"2026-09-18T13:04:49.381919Z","shell.execute_reply.started":"2026-09-18T13:04:41.130915Z","shell.execute_reply":"2026-09-18T13:04:49.381273Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"2. Data","metadata":{}},{"cell_type":"code","source":"stats = ((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616))\n\ntrain_tfms = T.Compose([\n    T.RandomCrop(32, padding=4, padding_mode='reflect'),\n    T.RandomHorizontalFlip(),\n    T.ToTensor(),\n    T.Normalize(*stats),\n])\ntest_tfms = T.Compose([\n    T.ToTensor(),\n    T.Normalize(*stats),\n])\n\ntrain_ds = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=train_tfms)\ntest_ds  = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=test_tfms)\n\nclasses = train_ds.classes  # alphabetical: airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck\nprint(classes)\n\nBATCH = 512\ntrain_dl = torch.utils.data.DataLoader(train_ds, batch_size=BATCH, shuffle=True, num_workers=2, pin_memory=True)\ntest_dl  = torch.utils.data.DataLoader(test_ds, batch_size=BATCH, shuffle=False, num_workers=2, pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:04:49.383234Z","iopub.execute_input":"2026-09-18T13:04:49.383609Z","iopub.status.idle":"2026-09-18T13:28:29.637312Z","shell.execute_reply.started":"2026-09-18T13:04:49.383584Z","shell.execute_reply":"2026-09-18T13:28:29.636552Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"3. Model — ResNet9 (random init, no pretrained weights)","metadata":{}},{"cell_type":"code","source":"def conv_block(in_ch, out_ch, pool=False):\n    layers = [nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1, bias=False),\n              nn.BatchNorm2d(out_ch),\n              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, ch):\n        super().__init__()\n        self.b1 = conv_block(ch, ch)\n        self.b2 = conv_block(ch, ch)\n    def forward(self, x):\n        return x + self.b2(self.b1(x))\n\nclass ResNet9(nn.Module):\n    def __init__(self, num_classes=10):\n        super().__init__()\n        self.stem = conv_block(3, 64)\n        self.layer1 = conv_block(64, 128, pool=True)\n        self.res1   = Residual(128)\n        self.layer2 = conv_block(128, 256, pool=True)\n        self.layer3 = conv_block(256, 512, pool=True)\n        self.res2   = Residual(512)\n        self.pool   = nn.Sequential(nn.MaxPool2d(4), nn.Flatten())\n        self.fc     = nn.Linear(512, num_classes)\n\n    def forward(self, x):\n        x = self.stem(x)\n        x = self.layer1(x)\n        x = self.res1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.res2(x)\n        x = self.pool(x)\n        return self.fc(x)\n\nmodel = ResNet9().to(device)\nn_params = sum(p.numel() for p in model.parameters())\nprint(f'ResNet9: {n_params:,} parameters (randomly initialized)')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:28:29.638406Z","iopub.execute_input":"2026-09-18T13:28:29.638794Z","iopub.status.idle":"2026-09-18T13:28:30.013667Z","shell.execute_reply.started":"2026-09-18T13:28:29.638751Z","shell.execute_reply":"2026-09-18T13:28:30.012728Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"4. Training","metadata":{}},{"cell_type":"code","source":"EPOCHS = 25\nMAX_LR = 0.04\nWEIGHT_DECAY = 5e-4\nGRAD_CLIP = 5\n\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\noptimizer = torch.optim.SGD(model.parameters(), lr=MAX_LR, momentum=0.9,\n                             weight_decay=WEIGHT_DECAY, nesterov=True)\nscheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=MAX_LR,\n                                                 epochs=EPOCHS, steps_per_epoch=len(train_dl))\n\ndef evaluate(dl):\n    model.eval()\n    correct, total = 0, 0\n    with torch.no_grad():\n        for xb, yb in dl:\n            xb, yb = xb.to(device), yb.to(device)\n            out = model(xb)\n            correct += (out.argmax(1) == yb).sum().item()\n            total += yb.size(0)\n    return correct / total\n\nhistory = []\nfor epoch in range(EPOCHS):\n    model.train()\n    t0 = time.time()\n    running_loss = 0.0\n    for xb, yb in train_dl:\n        xb, yb = xb.to(device), yb.to(device)\n        optimizer.zero_grad()\n        out = model(xb)\n        loss = criterion(out, yb)\n        loss.backward()\n        nn.utils.clip_grad_norm_(model.parameters(), GRAD_CLIP)\n        optimizer.step()\n        scheduler.step()\n        running_loss += loss.item() * xb.size(0)\n\n    train_loss = running_loss / len(train_ds)\n    test_acc = evaluate(test_dl)\n    history.append((epoch + 1, train_loss, test_acc))\n    print(f'Epoch {epoch+1:02d}/{EPOCHS} | loss {train_loss:.4f} | '\n          f'official test acc {test_acc:.4f} | {time.time()-t0:.1f}s')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:28:30.015167Z","iopub.execute_input":"2026-09-18T13:28:30.015457Z","iopub.status.idle":"2026-09-18T13:37:41.706877Z","shell.execute_reply.started":"2026-09-18T13:28:30.015435Z","shell.execute_reply":"2026-09-18T13:37:41.70616Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Estimate the Kaggle score","metadata":{}},{"cell_type":"code","source":"final_acc = evaluate(test_dl)\nprint(f'Estimated Kaggle score (official CIFAR-10 test accuracy): {final_acc:.4f}')\n\ntorch.save(model.state_dict(), 'resnet9_cifar10.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:37:41.708342Z","iopub.execute_input":"2026-09-18T13:37:41.708737Z","iopub.status.idle":"2026-09-18T13:37:43.316723Z","shell.execute_reply.started":"2026-09-18T13:37:41.708705Z","shell.execute_reply":"2026-09-18T13:37:43.31597Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"6. Decompress the Kaggle test set (test.7z)","metadata":{}},{"cell_type":"code","source":"!pip install -q py7zr\n\nimport py7zr, glob\n\nwith py7zr.SevenZipFile('/kaggle/input/competitions/cifar-10/test.7z', mode='r') as archive:\n    archive.extractall(path='.')\n\nprint('Extracted files:', len(glob.glob('test/*')))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T13:53:02.033698Z","iopub.execute_input":"2026-09-18T13:53:02.034428Z","iopub.status.idle":"2026-09-18T13:56:41.86877Z","shell.execute_reply.started":"2026-09-18T13:53:02.034389Z","shell.execute_reply":"2026-09-18T13:56:41.86797Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"7. Predict on the Kaggle test set and write submission.csv","metadata":{}},{"cell_type":"code","source":"class KaggleTestDataset(torch.utils.data.Dataset):\n    def __init__(self, folder, n_images, transform):\n        self.folder = folder\n        self.n_images = n_images\n        self.transform = transform\n    def __len__(self):\n        return self.n_images\n    def __getitem__(self, idx):\n        img_id = idx + 1  # Kaggle ids are 1-indexed\n        img = Image.open(os.path.join(self.folder, f'{img_id}.png')).convert('RGB')\n        return self.transform(img), img_id\n\nN_TEST = len(glob.glob('test/*'))\nkaggle_test_ds = KaggleTestDataset('test', N_TEST, test_tfms)\nkaggle_test_dl = torch.utils.data.DataLoader(kaggle_test_ds, batch_size=1024, shuffle=False, num_workers=2)\n\nmodel.eval()\nall_ids, all_preds = [], []\nwith torch.no_grad():\n    for xb, ids in kaggle_test_dl:\n        xb = xb.to(device)\n        out = model(xb)\n        preds = out.argmax(1).cpu().numpy()\n        all_ids.extend(ids.numpy().tolist())\n        all_preds.extend(preds.tolist())\n\nsubmission = pd.DataFrame({'id': all_ids, 'label': [classes[p] for p in all_preds]})\nsubmission = submission.sort_values('id').reset_index(drop=True)\nsubmission.to_csv('submission.csv', index=False)\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T14:01:55.021535Z","iopub.execute_input":"2026-09-18T14:01:55.022142Z","iopub.status.idle":"2026-09-18T14:03:07.026637Z","shell.execute_reply.started":"2026-09-18T14:01:55.022108Z","shell.execute_reply":"2026-09-18T14:03:07.025557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(next(model.parameters()).sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T14:03:53.579538Z","iopub.execute_input":"2026-09-18T14:03:53.580085Z","iopub.status.idle":"2026-09-18T14:03:53.729026Z","shell.execute_reply.started":"2026-09-18T14:03:53.580047Z","shell.execute_reply":"2026-09-18T14:03:53.728038Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(submission))                       # should print 300000\nprint(submission['id'].min(), submission['id'].max())   # should be 1, 300000\nprint(submission['label'].value_counts())     # should be roughly balanced (~30,000 each across 10 classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-18T14:05:17.906578Z","iopub.execute_input":"2026-09-18T14:05:17.907206Z","iopub.status.idle":"2026-09-18T14:05:17.931449Z","shell.execute_reply.started":"2026-09-18T14:05:17.907176Z","shell.execute_reply":"2026-09-18T14:05:17.930837Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"8. Submit to Kaggle","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}