{"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":"# Sir's method: extract /kaggle/input/cifar-10/test.7z using libarchive\n!apt -y install -qq libarchive-dev\n!pip -q install libarchive\n\nimport os, glob, time\nimport libarchive.public\n\nos.chdir(\"/kaggle/working\")  # extract into /kaggle/working\nsrc = \"/kaggle/input/cifar-10/test.7z\"\n\nt0 = time.time()\ncnt = 0\nfor _ in libarchive.public.file_pour(src):  # creates ./test with PNGs\n    cnt += 1\n    if cnt % 5000 == 0:\n        print(f\"[extract] entries processed: {cnt} | elapsed: {int(time.time()-t0)}s\")\n\nprint(\"[done] entries processed:\", cnt)\nprint(\"Top-level PNGs in ./test:\", len(glob.glob('test/*.png')))\nprint(\"Peek:\", sorted([p.split('/')[-1] for p in glob.glob('test/*.png')])[:3],\n             \"...\",\n             sorted([p.split('/')[-1] for p in glob.glob('test/*.png')])[-3:])\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-06T08:06:41.711260Z","iopub.execute_input":"2025-10-06T08:06:41.711491Z","iopub.status.idle":"2025-10-06T08:08:00.581057Z","shell.execute_reply.started":"2025-10-06T08:06:41.711469Z","shell.execute_reply":"2025-10-06T08:08:00.580301Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Sir's method for train.7z\nimport os, glob, time\nimport libarchive.public\n\nos.chdir(\"/kaggle/working\")\nsrc = \"/kaggle/input/cifar-10/train.7z\"\n\nt0 = time.time()\ncnt = 0\nfor _ in libarchive.public.file_pour(src):  # creates ./train with PNGs\n    cnt += 1\n    if cnt % 2000 == 0:\n        print(f\"[extract] entries processed: {cnt} | elapsed: {int(time.time()-t0)}s\")\n\nprint(\"[done] entries processed:\", cnt)\nprint(\"Top-level PNGs in ./train:\", len(glob.glob('train/*.png')))\nprint(\"Peek:\", sorted([p.split('/')[-1] for p in glob.glob('train/*.png')])[:3],\n             \"...\",\n             sorted([p.split('/')[-1] for p in glob.glob('train/*.png')])[-3:])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T08:08:00.582033Z","iopub.execute_input":"2025-10-06T08:08:00.582270Z","iopub.status.idle":"2025-10-06T08:08:11.479286Z","shell.execute_reply.started":"2025-10-06T08:08:00.582245Z","shell.execute_reply":"2025-10-06T08:08:11.478493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===== Step: build train/val loaders from extracted PNGs =====\nfrom pathlib import Path\nfrom PIL import Image\nimport pandas as pd\nfrom sklearn.model_selection import StratifiedShuffleSplit\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nTRAIN_DIR = Path(\"/kaggle/working/train\")\nCSV_PATH  = Path(\"/kaggle/input/cifar-10/trainLabels.csv\")\n\n# 1) id -> file path (top-level PNGs)\nid2path = {int(p.stem): p for p in TRAIN_DIR.glob(\"*.png\")}\nprint(\"train png ids found:\", len(id2path))\n\n# 2) labels dataframe\ndf = pd.read_csv(CSV_PATH)  # columns: id,label\nclasses = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\nlab2idx  = {c:i for i,c in enumerate(classes)}\n\n# 3) stratified split 90/10\nsss = StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=42)\ntr_idx, va_idx = next(sss.split(df['id'], df['label']))\ndf_tr, df_va = df.iloc[tr_idx].reset_index(drop=True), df.iloc[va_idx].reset_index(drop=True)\nprint(\"train size:\", len(df_tr), \"| val size:\", len(df_va))\n\n# 4) transforms \nMEAN = (0.4914, 0.4822, 0.4465)\nSTD  = (0.2470, 0.2435, 0.2616)\ntf_train = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(MEAN, STD),\n])\ntf_val = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(MEAN, STD),\n])\n\n# 5) dataset wrapping the id->path map\nclass CIFARTrainPNG(Dataset):\n    def __init__(self, frame, id2path, tf):\n        self.f, self.id2path, self.tf = frame, id2path, tf\n    def __len__(self): return len(self.f)\n    def __getitem__(self, i):\n        r = self.f.iloc[i]\n        img = Image.open(self.id2path[int(r['id'])]).convert(\"RGB\")\n        return self.tf(img), lab2idx[r['label']]\n\nBATCH_SIZE = 128\ntrain_loader = DataLoader(CIFARTrainPNG(df_tr, id2path, tf_train),\n                          batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)\nval_loader   = DataLoader(CIFARTrainPNG(df_va, id2path, tf_val),\n                          batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n\n# quick sanity: fetch one batch\nxb, yb = next(iter(train_loader))\nprint(\"Batch:\", xb.shape, yb.shape, \"| device:\", DEVICE)\nprint(\"Label sample (first 10):\", [classes[i] for i in yb[:10].tolist()])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T08:08:11.480296Z","iopub.execute_input":"2025-10-06T08:08:11.480829Z","iopub.status.idle":"2025-10-06T08:08:16.478388Z","shell.execute_reply.started":"2025-10-06T08:08:11.480806Z","shell.execute_reply":"2025-10-06T08:08:16.477417Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================================\n# CIFAR-10 (Kaggle) — Train CNN with CutMix + SWA\n# ==========================================================\n\nimport os, math, time\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\nfrom sklearn.model_selection import StratifiedShuffleSplit\nimport torchvision.transforms as T\nfrom torch.optim.lr_scheduler import LambdaLR\nfrom torch.optim.swa_utils import AveragedModel, update_bn\nfrom torch.amp import autocast, GradScaler\n\n# -------------------- Setup --------------------\nSEED = 42\ntorch.manual_seed(SEED); np.random.seed(SEED)\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Device:\", DEVICE)\n\n# ---------- Paths ----------\nTRAIN_DIR = Path(\"/kaggle/working/train\")\nCSV_PATH  = Path(\"/kaggle/input/cifar-10/trainLabels.csv\")\n\n# ---------- Classes / normalization ----------\nCLASSES = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\nlab2idx = {c:i for i,c in enumerate(CLASSES)}\nMEAN = (0.4914, 0.4822, 0.4465)\nSTD  = (0.2023, 0.1994, 0.2010)\n\n# ---------- Build id -> path map ----------\nid2path = {int(p.stem): p for p in TRAIN_DIR.glob(\"*.png\")}\nprint(\"train png ids found:\", len(id2path))\ndf = pd.read_csv(CSV_PATH)\nassert set(df['id']).issubset(set(id2path.keys()))\n\n# ---------- Split ----------\nsss = StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=SEED)\ntr_idx, va_idx = next(sss.split(df['id'], df['label']))\ndf_tr, df_va = df.iloc[tr_idx].reset_index(drop=True), df.iloc[va_idx].reset_index(drop=True)\nprint(\"train size:\", len(df_tr), \"| val size:\", len(df_va))\n\n# ---------- Transforms ----------\ntf_train = T.Compose([\n    T.RandomCrop(32, padding=4),\n    T.RandomHorizontalFlip(),\n    T.ToTensor(),\n    T.Normalize(MEAN, STD),\n])\ntf_val = T.Compose([\n    T.ToTensor(),\n    T.Normalize(MEAN, STD),\n])\n\n# ---------- Dataset ----------\nclass CIFARTrainPNG(Dataset):\n    def __init__(self, frame, id2path, tf):\n        self.f, self.id2path, self.tf = frame, id2path, tf\n    def __len__(self): return len(self.f)\n    def __getitem__(self, i):\n        r = self.f.iloc[i]\n        img = Image.open(self.id2path[int(r['id'])]).convert(\"RGB\")\n        return self.tf(img), lab2idx[r['label']]\n\n# ---------- Loaders ----------\nBATCH = 128\ntrain_loader = DataLoader(CIFARTrainPNG(df_tr, id2path, tf_train),\n                          batch_size=BATCH, shuffle=True,  num_workers=2, pin_memory=True)\nval_loader   = DataLoader(CIFARTrainPNG(df_va, id2path, tf_val),\n                          batch_size=BATCH, shuffle=False, num_workers=2, pin_memory=True)\n\n# ---------- Model ----------\nclass CustomCNN(nn.Module):\n    def __init__(self, num_classes=10):\n        super().__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 64, 3, padding=1, bias=False), nn.BatchNorm2d(64), nn.ReLU(),\n            nn.Conv2d(64, 64, 3, padding=1, bias=False), nn.BatchNorm2d(64), nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(64, 128, 3, padding=1, bias=False), nn.BatchNorm2d(128), nn.ReLU(),\n            nn.Conv2d(128, 128, 3, padding=1, bias=False), nn.BatchNorm2d(128), nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(128, 256, 3, padding=1, bias=False), nn.BatchNorm2d(256), nn.ReLU(),\n            nn.Conv2d(256, 256, 3, padding=1, bias=False), nn.BatchNorm2d(256), nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(256, 512, 3, padding=1, bias=False), nn.BatchNorm2d(512), nn.ReLU(),\n            nn.Conv2d(512, 512, 3, padding=1, bias=False), nn.BatchNorm2d(512), nn.ReLU(),\n            nn.AdaptiveAvgPool2d(1),\n        )\n        self.head = nn.Sequential(nn.Dropout(0.3), nn.Linear(512, num_classes))\n    def forward(self, x):\n        x = self.features(x)\n        x = torch.flatten(x, 1)\n        return self.head(x)\n\nmodel = CustomCNN().to(DEVICE)\n\n# ---------- Optimizer, scheduler, loss ----------\nbase_lr = 1e-3\noptimizer = optim.AdamW(model.parameters(), lr=base_lr, weight_decay=1e-4)\nsteps_per_epoch = len(train_loader)\nEPOCHS = 200\nwarmup_steps = 500\ntotal_steps = EPOCHS * steps_per_epoch\n\ndef lr_lambda(step):\n    if step < warmup_steps:\n        return step / max(1, warmup_steps)\n    t = (step - warmup_steps) / max(1, total_steps - warmup_steps)\n    return 0.5 * (1.0 + math.cos(math.pi * t))\n\nscheduler = LambdaLR(optimizer, lr_lambda)\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\nscaler = GradScaler(device=\"cuda\" if DEVICE==\"cuda\" else \"cpu\")\n\n# ---------- CutMix ----------\ndef rand_bbox(size, lam):\n    W, H = size[2], size[3]\n    cut_rat = math.sqrt(1. - lam)\n    cut_w, cut_h = int(W*cut_rat), int(H*cut_rat)\n    cx, cy = np.random.randint(W), np.random.randint(H)\n    x1, y1 = max(cx-cut_w//2,0), max(cy-cut_h//2,0)\n    x2, y2 = min(cx+cut_w//2,W), min(cy+cut_h//2,H)\n    return x1,y1,x2,y2\n\ndef cutmix_data(x, y, alpha=1.0):\n    lam = np.random.beta(alpha, alpha)\n    idx = torch.randperm(x.size(0), device=x.device)\n    ya, yb = y, y[idx]\n    x1,y1,x2,y2 = rand_bbox(x.size(), lam)\n    xm = x.clone()\n    xm[:,:,x1:x2, y1:y2] = x[idx,:,x1:x2, y1:y2]\n    lam = 1 - ((x2-x1)*(y2-y1) / (x.size(-1)*x.size(-2)))\n    return xm, ya, yb, lam\n\n# ---------- SWA ----------\nswa_start = 180\nswa_model = AveragedModel(model)\n\n# ---------- Training ----------\nbest_acc = 0.0\nfor epoch in range(1, EPOCHS+1):\n    model.train()\n    t0 = time.time()\n    run_loss, correct, total = 0.0, 0, 0\n\n    for xb, yb in train_loader:\n        xb, yb = xb.to(DEVICE), yb.to(DEVICE)\n        optimizer.zero_grad(set_to_none=True)\n        with autocast(device_type=\"cuda\", enabled=(DEVICE==\"cuda\")):\n            if np.random.rand() < 0.3:\n                xm, ya, yb2, lam = cutmix_data(xb, yb, alpha=1.0)\n                logits = model(xm)\n                loss = lam * criterion(logits, ya) + (1 - lam) * criterion(logits, yb2)\n            else:\n                logits = model(xb)\n                loss = criterion(logits, yb)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n        scheduler.step()\n        run_loss += loss.item()\n\n    model.eval()\n    v_correct = v_total = v_loss_sum = 0.0\n    with torch.no_grad(), autocast(device_type=\"cuda\", enabled=(DEVICE==\"cuda\")):\n        for vx, vy in val_loader:\n            vx, vy = vx.to(DEVICE), vy.to(DEVICE)\n            out = model(vx)\n            v_loss = criterion(out, vy)\n            v_loss_sum += v_loss.item() * vy.size(0)\n            v_correct += out.argmax(1).eq(vy).sum().item()\n            v_total += vy.size(0)\n\n    val_acc = v_correct / max(1, v_total)\n    val_loss = v_loss_sum / max(1, v_total)\n    if epoch >= swa_start: swa_model.update_parameters(model)\n    if val_acc > best_acc:\n        best_acc = val_acc\n        torch.save(model.state_dict(), \"best_model.pth\")\n\n    print(f\"Epoch {epoch:03d}/{EPOCHS} | val loss {val_loss:.4f} acc {val_acc:.4f} | best {best_acc:.4f} | time {time.time()-t0:.1f}s\")\n\nif EPOCHS >= swa_start:\n    update_bn(train_loader, swa_model, device=DEVICE)\n    torch.save(swa_model.state_dict(), \"best_swa.pth\")\n\nprint(\"✅ Training complete. Model files saved.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T08:08:16.479695Z","iopub.execute_input":"2025-10-06T08:08:16.480316Z","iopub.status.idle":"2025-10-06T09:07:36.292459Z","shell.execute_reply.started":"2025-10-06T08:08:16.480280Z","shell.execute_reply":"2025-10-06T09:07:36.291406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================================\n# Predict + Generate submission.csv\n# ==========================================================\n\n# ==========================================================\n# Predict + Generate submission.csv\n# ==========================================================\n\nimport os\nimport torch\nimport pandas as pd\nfrom pathlib import Path\nfrom collections import OrderedDict\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom torch.utils.data import DataLoader, Dataset\nimport torchvision.transforms as T\n\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nTEST_DIR  = Path(\"/kaggle/working/test\")\n\nCLASSES = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\nidx2lab = CLASSES\nMEAN = (0.4914, 0.4822, 0.4465)\nSTD  = (0.2023, 0.1994, 0.2010)\n\ntf_test = T.Compose([\n    T.ToTensor(),\n    T.Normalize(MEAN, STD),\n])\n\nclass TestDataset(Dataset):\n    def __init__(self, files, tf):\n        self.files, self.tf = files, tf\n    def __len__(self): return len(self.files)\n    def __getitem__(self, i):\n        p = self.files[i]\n        img = Image.open(p).convert(\"RGB\")\n        return self.tf(img), int(Path(p).stem)\n\nclass CustomCNN(torch.nn.Module):\n    def __init__(self, num_classes=10):\n        super().__init__()\n        self.features = torch.nn.Sequential(\n            torch.nn.Conv2d(3, 64, 3, padding=1, bias=False), torch.nn.BatchNorm2d(64), torch.nn.ReLU(),\n            torch.nn.Conv2d(64, 64, 3, padding=1, bias=False), torch.nn.BatchNorm2d(64), torch.nn.ReLU(),\n            torch.nn.MaxPool2d(2),\n            torch.nn.Conv2d(64, 128, 3, padding=1, bias=False), torch.nn.BatchNorm2d(128), torch.nn.ReLU(),\n            torch.nn.Conv2d(128, 128, 3, padding=1, bias=False), torch.nn.BatchNorm2d(128), torch.nn.ReLU(),\n            torch.nn.MaxPool2d(2),\n            torch.nn.Conv2d(128, 256, 3, padding=1, bias=False), torch.nn.BatchNorm2d(256), torch.nn.ReLU(),\n            torch.nn.Conv2d(256, 256, 3, padding=1, bias=False), torch.nn.BatchNorm2d(256), torch.nn.ReLU(),\n            torch.nn.MaxPool2d(2),\n            torch.nn.Conv2d(256, 512, 3, padding=1, bias=False), torch.nn.BatchNorm2d(512), torch.nn.ReLU(),\n            torch.nn.Conv2d(512, 512, 3, padding=1, bias=False), torch.nn.BatchNorm2d(512), torch.nn.ReLU(),\n            torch.nn.AdaptiveAvgPool2d(1),\n        )\n        self.head = torch.nn.Sequential(torch.nn.Dropout(0.3), torch.nn.Linear(512, num_classes))\n    def forward(self, x):\n        x = self.features(x)\n        x = torch.flatten(x, 1)\n        return self.head(x)\n\nckpt_path = \"best_swa.pth\" if os.path.exists(\"best_swa.pth\") else \"best_model.pth\"\nprint(f\"Loading checkpoint: {ckpt_path}\")\n\nstate = torch.load(ckpt_path, map_location=DEVICE)\nmodel = CustomCNN().to(DEVICE)\n\nif any(k.startswith(\"module.\") for k in state.keys()):\n    new_state = OrderedDict()\n    for k, v in state.items():\n        if k.startswith(\"module.\"):\n            new_state[k[len(\"module.\"):]] = v\n        elif k != \"n_averaged\":\n            new_state[k] = v\n    state = new_state\n\nmodel.load_state_dict(state, strict=True)\nmodel.eval()\n\ntest_files = sorted(list(TEST_DIR.glob(\"*.png\")), key=lambda x: int(x.stem))\ntest_loader = DataLoader(TestDataset(test_files, tf_test),\n                         batch_size=256, shuffle=False, num_workers=2, pin_memory=True)\n\npreds = []\nwith torch.no_grad(), torch.amp.autocast(device_type=\"cuda\", enabled=(DEVICE==\"cuda\")):\n    for xb, ids in tqdm(test_loader, desc=\"Predicting\"):\n        xb = xb.to(DEVICE)\n        out = model(xb)\n        preds.extend(zip(ids.numpy(), out.argmax(1).cpu().numpy()))\n\nsub = pd.DataFrame(preds, columns=[\"id\", \"label_idx\"])\nsub[\"label\"] = sub[\"label_idx\"].apply(lambda x: idx2lab[x])\nsub = sub[[\"id\", \"label\"]]\nsub.to_csv(\"submission.csv\", index=False)\nprint(\"✅ submission.csv saved successfully. Rows:\", len(sub))\nsub.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T09:07:36.293824Z","iopub.execute_input":"2025-10-06T09:07:36.294089Z","iopub.status.idle":"2025-10-06T09:08:44.660338Z","shell.execute_reply.started":"2025-10-06T09:07:36.294061Z","shell.execute_reply":"2025-10-06T09:08:44.659488Z"}},"outputs":[],"execution_count":null}]}