{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!ls -lah /kaggle/input/cifar-10","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:08:49.177734Z","iopub.execute_input":"2025-09-24T08:08:49.178395Z","iopub.status.idle":"2025-09-24T08:08:49.301254Z","shell.execute_reply.started":"2025-09-24T08:08:49.178351Z","shell.execute_reply":"2025-09-24T08:08:49.300414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# A) imports, seed, device\nimport os, math, random, glob\nfrom pathlib import Path\nimport numpy as np, pandas as pd\nfrom PIL import Image\n\nimport torch, torch.nn as nn\nfrom torch.utils.data import DataLoader, random_split\nfrom torchvision import transforms\nfrom torchvision.models import resnet18\nfrom torch.cuda.amp import autocast, GradScaler\n\nSEED = 42\nrandom.seed(SEED); np.random.seed(SEED); torch.manual_seed(SEED)\ntorch.backends.cudnn.benchmark = True\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Device:\", DEVICE)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:08:49.303107Z","iopub.execute_input":"2025-09-24T08:08:49.303847Z","iopub.status.idle":"2025-09-24T08:08:56.735740Z","shell.execute_reply.started":"2025-09-24T08:08:49.303813Z","shell.execute_reply":"2025-09-24T08:08:56.735064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# B) extract Kaggle train.7z and build train/val loaders\n!apt -yq update >/dev/null\n!apt -yq install libarchive-dev >/dev/null\n!pip -q install libarchive >/dev/null\n\nimport libarchive.public\n\n# constants\nCIFAR10_MEAN = (0.4914, 0.4822, 0.4465)\nCIFAR10_STD  = (0.2470, 0.2435, 0.2616)\nCLASSES = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\nclass_to_idx = {c:i for i,c in enumerate(CLASSES)}\n\n# extract train set to /kaggle/working/train (only once)\ntrain_out = \"/kaggle/working/train\"\nos.makedirs(train_out, exist_ok=True)\nif len(glob.glob(train_out + \"/*\")) < 1000:\n    for _ in libarchive.public.file_pour('/kaggle/input/cifar-10/train.7z'):\n        pass\n\n# labels\nlabels_df = pd.read_csv('/kaggle/input/cifar-10/trainLabels.csv')  # id,label\nlabels_df['path'] = labels_df['id'].astype(str).apply(lambda i: f\"{train_out}/{i}.png\")\nprint(\"Rows in labels:\", len(labels_df))\n\n# transforms\ntrain_tfms = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),\n    transforms.RandomHorizontalFlip(),\n    transforms.ColorJitter(0.2, 0.2, 0.2, 0.1),\n    transforms.ToTensor(),\n    transforms.Normalize(CIFAR10_MEAN, CIFAR10_STD),\n    transforms.RandomErasing(p=0.25, scale=(0.02,0.15), ratio=(0.3,3.3), value='random')\n])\ntest_tfms = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(CIFAR10_MEAN, CIFAR10_STD),\n])\n\n# dataset\nclass KaggleTrainDataset(torch.utils.data.Dataset):\n    def __init__(self, df, tfm):\n        self.df = df.reset_index(drop=True)\n        self.tfm = tfm\n    def __len__(self): return len(self.df)\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img = Image.open(row['path']).convert(\"RGB\")\n        y = class_to_idx[row['label']]\n        return self.tfm(img), y\n\nfull_ds = KaggleTrainDataset(labels_df, train_tfms)\n\n# split train/val\nval_size = 5000\ntrain_size = len(full_ds) - val_size\ntrain_ds, val_ds = random_split(full_ds, [train_size, val_size],\n                                generator=torch.Generator().manual_seed(SEED))\n\nBS = 128   # if OOM later, set 64 (also change in H)\ntrain_loader = DataLoader(train_ds, batch_size=BS, shuffle=True,  num_workers=2, pin_memory=True)\nval_loader   = DataLoader(val_ds,   batch_size=BS, shuffle=False, num_workers=2, pin_memory=True)\n\nprint(\"train/val sizes:\", len(train_ds), len(val_ds))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:08:56.736747Z","iopub.execute_input":"2025-09-24T08:08:56.737170Z","iopub.status.idle":"2025-09-24T08:09:27.832497Z","shell.execute_reply.started":"2025-09-24T08:08:56.737149Z","shell.execute_reply":"2025-09-24T08:09:27.831742Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# C) ResNet-18 adapted for CIFAR-10 (random init)\ndef resnet18_cifar10(num_classes=10):\n    m = resnet18(weights=None)   # IMPORTANT: no pre-trained weights\n    m.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n    m.maxpool = nn.Identity()\n    m.fc = nn.Linear(m.fc.in_features, num_classes)\n    return m\n\nmodel = resnet18_cifar10().to(DEVICE)\nprint(\"Model ready (weights=None).\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:09:27.833532Z","iopub.execute_input":"2025-09-24T08:09:27.833777Z","iopub.status.idle":"2025-09-24T08:09:28.197857Z","shell.execute_reply.started":"2025-09-24T08:09:27.833750Z","shell.execute_reply":"2025-09-24T08:09:28.197196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# D) training setup\nEPOCHS = 40          # quick first run; raise to 80+ later for higher score\nBASE_LR = 0.1\n\noptimizer = torch.optim.SGD(model.parameters(), lr=BASE_LR, momentum=0.9,\n                            weight_decay=5e-4, nesterov=True)\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\nscaler = GradScaler()\n\nwarmup_epochs = 5\ntotal_steps = EPOCHS * len(train_loader)\n\ndef cosine_lr(step, total_steps, warmup_steps, base_lr):\n    if step < warmup_steps:\n        return base_lr * (step / warmup_steps)\n    progress = (step - warmup_steps) / (total_steps - warmup_steps)\n    return 0.5 * base_lr * (1.0 + math.cos(math.pi * progress))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:09:28.199573Z","iopub.execute_input":"2025-09-24T08:09:28.199762Z","iopub.status.idle":"2025-09-24T08:09:28.205865Z","shell.execute_reply.started":"2025-09-24T08:09:28.199746Z","shell.execute_reply":"2025-09-24T08:09:28.205123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# E) train loop + validation\ndef evaluate(loader):\n    model.eval()\n    correct, total, loss_sum = 0, 0, 0.0\n    with torch.no_grad():\n        for x, y in loader:\n            x, y = x.to(DEVICE, non_blocking=True), y.to(DEVICE, non_blocking=True)\n            with autocast():\n                logits = model(x)\n                loss = criterion(logits, y)\n            loss_sum += loss.item() * x.size(0)\n            pred = logits.argmax(1)\n            correct += (pred == y).sum().item()\n            total += x.size(0)\n    return loss_sum/total, correct/total\n\nbest_val = 0.0\nglobal_step = 0\n\nfor epoch in range(1, EPOCHS+1):\n    model.train()\n    for x, y in train_loader:\n        x, y = x.to(DEVICE, non_blocking=True), y.to(DEVICE, non_blocking=True)\n        lr = cosine_lr(global_step, total_steps, warmup_epochs*len(train_loader), BASE_LR)\n        for g in optimizer.param_groups: g['lr'] = lr\n        optimizer.zero_grad(set_to_none=True)\n        with autocast():\n            logits = model(x)\n            loss = criterion(logits, y)\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n        global_step += 1\n\n    val_loss, val_acc = evaluate(val_loader)\n    print(f\"Epoch {epoch:03d} | val_acc={val_acc:.4f} | val_loss={val_loss:.4f} | lr={lr:.5f}\")\n    if val_acc > best_val:\n        best_val = val_acc\n        torch.save(model.state_dict(), \"best_model.pth\")\n\nprint(\"Best val acc:\", best_val)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:09:28.206543Z","iopub.execute_input":"2025-09-24T08:09:28.206776Z","iopub.status.idle":"2025-09-24T08:31:06.955330Z","shell.execute_reply.started":"2025-09-24T08:09:28.206754Z","shell.execute_reply":"2025-09-24T08:31:06.954471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# G) extract competition test set to /kaggle/working/test\nimport libarchive.public\nout_dir = \"/kaggle/working/test\"\nos.makedirs(out_dir, exist_ok=True)\nif len(glob.glob(out_dir + \"/*\")) < 1000:\n    for _ in libarchive.public.file_pour('/kaggle/input/cifar-10/test.7z'):\n        pass\nprint(\"Files in /kaggle/working/test:\", len(glob.glob(out_dir + \"/*\")))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:31:06.956413Z","iopub.execute_input":"2025-09-24T08:31:06.956673Z","iopub.status.idle":"2025-09-24T08:32:11.389396Z","shell.execute_reply.started":"2025-09-24T08:31:06.956644Z","shell.execute_reply":"2025-09-24T08:32:11.388611Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# H) predict & write submission.csv\ntest_tfms = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(CIFAR10_MEAN, CIFAR10_STD),\n])\n\nclass ImageFolderFromDir(torch.utils.data.Dataset):\n    def __init__(self, folder, tfm):\n        self.paths = sorted(glob.glob(os.path.join(folder, \"*.png\")), key=lambda p: int(Path(p).stem))\n        self.tfm = tfm\n    def __len__(self): return len(self.paths)\n    def __getitem__(self, idx):\n        p = self.paths[idx]\n        img = Image.open(p).convert(\"RGB\")\n        return self.tfm(img), int(Path(p).stem)\n\nkaggle_test_ds = ImageFolderFromDir(\"/kaggle/working/test\", test_tfms)\nkaggle_test_loader = DataLoader(kaggle_test_ds, batch_size=BS, shuffle=False, num_workers=2, pin_memory=True)\n\nidx_to_label = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\n\nmodel.load_state_dict(torch.load(\"best_model.pth\", map_location=DEVICE))\nmodel.eval()\nids, labels = [], []\nwith torch.no_grad():\n    for x, img_ids in kaggle_test_loader:\n        x = x.to(DEVICE, non_blocking=True)\n        with autocast():\n            logits = model(x)\n        pred = logits.argmax(1).cpu().numpy()\n        ids.extend(img_ids)\n        labels.extend([idx_to_label[i] for i in pred])\n\nsub = pd.DataFrame({\"id\": ids, \"label\": labels}).sort_values(\"id\")\nsub.to_csv(\"submission.csv\", index=False)\nprint(sub.shape)   # should be (300000, 2)\nsub.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:32:11.390411Z","iopub.execute_input":"2025-09-24T08:32:11.390764Z","iopub.status.idle":"2025-09-24T08:33:56.427639Z","shell.execute_reply.started":"2025-09-24T08:32:11.390739Z","shell.execute_reply":"2025-09-24T08:33:56.426803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# F-exact: evaluate on the official CIFAR-10 test set (requires Internet = On)\nfrom torchvision.datasets import CIFAR10\nfrom torch.utils.data import DataLoader\n\ntest_official = CIFAR10(root=\"/kaggle/working/data\", train=False, download=True, transform=test_tfms)\ntest_loader_official = DataLoader(test_official, batch_size=BS, shuffle=False, num_workers=2, pin_memory=True)\n\nmodel.load_state_dict(torch.load(\"best_model.pth\", map_location=DEVICE))\ntl, ta = evaluate(test_loader_official)\nprint(\"Official CIFAR-10 test accuracy (≈ Kaggle score):\", ta)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T08:47:35.077425Z","iopub.execute_input":"2025-09-24T08:47:35.078216Z","iopub.status.idle":"2025-09-24T08:47:51.039844Z","shell.execute_reply.started":"2025-09-24T08:47:35.078191Z","shell.execute_reply":"2025-09-24T08:47:51.038943Z"}},"outputs":[],"execution_count":null}]}