{"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":"# CIFAR-10 -> ResNet18 (random init) -> early-stop at 0.88 -> submission.csv\n# Fast: AMP + auto batch-size tuning + efficient DataLoader\n\n!apt -yq install -qq libarchive-dev\n!pip -q install -q libarchive\n\nimport os, math, glob, csv, random, time, warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport torch, torch.nn as nn\nfrom torch.cuda.amp import autocast, GradScaler\nfrom torch.utils.data import DataLoader\nfrom torchvision import datasets, transforms, models\nfrom torchvision.transforms import RandAugment\nfrom PIL import Image\n\n# ---------------- Config ----------------\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nTARGET_ACC = 0.88\nMAX_EPOCHS = 80          # will stop earlier if TARGET_ACC is hit\nWARMUP_EPOCHS = 2\nBASE_LR = 0.35           # slightly hot for fast convergence\nWORKERS = 2\nseed = 42\nrandom.seed(seed); torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)\ntorch.backends.cudnn.benchmark = True\ntorch.set_float32_matmul_precision(\"high\")\n\n# --------------- Data -------------------\nMEAN=(0.4914,0.4822,0.4465); STD=(0.2470,0.2435,0.2616)\ntrain_tfms = transforms.Compose([\n    transforms.RandomCrop(32, padding=4, padding_mode=\"reflect\"),\n    transforms.RandomHorizontalFlip(),\n    RandAugment(num_ops=2, magnitude=6),           # good accuracy, still fast\n    transforms.ToTensor(),\n    transforms.Normalize(MEAN, STD),\n])\ntest_tfms = transforms.Compose([transforms.ToTensor(), transforms.Normalize(MEAN, STD)])\n\ntrainset = datasets.CIFAR10(\"/kaggle/working\", train=True,  download=True, transform=train_tfms)\nvalset   = datasets.CIFAR10(\"/kaggle/working\", train=False, download=True, transform=test_tfms)\nclasses = trainset.classes\n\ndef make_loader(bs):\n    return (DataLoader(trainset, batch_size=bs, shuffle=True,\n                       num_workers=WORKERS, pin_memory=True,\n                       persistent_workers=True, prefetch_factor=4, drop_last=True),\n            DataLoader(valset, batch_size=bs, shuffle=False,\n                       num_workers=WORKERS, pin_memory=True,\n                       persistent_workers=True, prefetch_factor=4))\n\n# --------- Auto-tune the biggest safe batch ---------\ndef find_max_batch(start=256):\n    bs = start\n    while bs >= 32:\n        try:\n            tl, vl = make_loader(bs)\n            x, y = next(iter(tl))\n            x = x.to(DEVICE); y = y.to(DEVICE)\n            m = models.resnet18(weights=None, num_classes=10).to(DEVICE)\n            with autocast(enabled=(DEVICE==\"cuda\")):\n                _ = m(x); del m; torch.cuda.empty_cache()\n            return bs\n        except Exception as e:\n            bs //= 2\n            torch.cuda.empty_cache()\n    return 32\n\nBATCH = find_max_batch(256)\nprint(\"Using batch size:\", BATCH)\ntrain_loader, val_loader = make_loader(BATCH)\n\n# --------------- Model (NO pretrained) ---------------\nmodel = models.resnet18(weights=None, num_classes=10)\nmodel.fc = nn.Sequential(nn.Dropout(0.2), nn.Linear(model.fc.in_features, 10))\nmodel = model.to(DEVICE)\n\n# --------------- Loss/Opt/Schedule -------------------\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.05)\noptimizer = torch.optim.SGD(model.parameters(), lr=BASE_LR, momentum=0.9, weight_decay=5e-4, nesterov=True)\nscaler = GradScaler(enabled=(DEVICE==\"cuda\"))\n\nsteps_per_epoch = len(train_loader)\ndef lr_mult(step):\n    if step < WARMUP_EPOCHS * steps_per_epoch:\n        return step / float(WARMUP_EPOCHS * steps_per_epoch)\n    p = (step - WARMUP_EPOCHS * steps_per_epoch) / float((MAX_EPOCHS - WARMUP_EPOCHS) * steps_per_epoch)\n    return 0.5 * (1 + math.cos(math.pi * p))\n\n@torch.no_grad()\ndef evaluate():\n    model.eval(); correct=total=0; loss_sum=0.0\n    with autocast(enabled=(DEVICE==\"cuda\")):\n        for x,y in val_loader:\n            x=x.to(DEVICE, non_blocking=True); y=y.to(DEVICE, non_blocking=True)\n            out=model(x); loss=criterion(out,y)\n            loss_sum += loss.item()*x.size(0)\n            pred = out.argmax(1); correct += (pred==y).sum().item(); total += x.size(0)\n    return loss_sum/total, correct/total\n\n# --------------- Train (early-stop @ 0.88) ---------------\nbest = 0.0; step=0; t0=time.time()\nfor ep in range(1, MAX_EPOCHS+1):\n    model.train()\n    for x,y in train_loader:\n        x=x.to(DEVICE, non_blocking=True); y=y.to(DEVICE, non_blocking=True)\n        lr = BASE_LR * lr_mult(step)\n        for pg in optimizer.param_groups: pg[\"lr\"] = lr\n\n        optimizer.zero_grad(set_to_none=True)\n        with autocast(enabled=(DEVICE==\"cuda\")):\n            out=model(x); loss=criterion(out,y)\n        scaler.scale(loss).backward()\n        scaler.step(optimizer); scaler.update()\n        step += 1\n\n    vl, va = evaluate()\n    best = max(best, va)\n    print(f\"Epoch {ep:02d} | val_acc={va:.4f} (best {best:.4f}) | elapsed {int(time.time()-t0)}s\")\n    torch.cuda.empty_cache()\n    if va >= TARGET_ACC:\n        print(f\"Early stop: val_acc {va:.4f} >= {TARGET_ACC}\")\n        break\n\n# --------------- Extract Kaggle test.7z ----------------\nimport libarchive.public\nif not os.path.exists(\"test\"):\n    assert os.path.exists(\"/kaggle/input/cifar-10/test.7z\"), \"Attach competition data (Add Data → CIFAR-10).\"\n    os.makedirs(\"test\", exist_ok=True)\n    for i,_ in enumerate(libarchive.public.file_pour('/kaggle/input/cifar-10/test.7z'),1):\n        if i%10000==0: print(\"extracted:\", i)\n\n# --------------- Inference + light TTA -----------------\ntest_paths = sorted(glob.glob(\"test/*.png\"))\nclass TestSet(torch.utils.data.Dataset):\n    def __init__(self, paths, tfm): self.paths=paths; self.tfm=tfm\n    def __len__(self): return len(self.paths)\n    def __getitem__(self, idx):\n        p=self.paths[idx]; img=Image.open(p).convert(\"RGB\")\n        return self.tfm(img), os.path.basename(p).split(\".\")[0]\n\ntest_tfms = transforms.Compose([transforms.ToTensor(), transforms.Normalize(MEAN, STD)])\ntest_ds = TestSet(test_paths, test_tfms)\ntest_loader = DataLoader(test_ds, batch_size=BATCH, shuffle=False,\n                         num_workers=WORKERS, pin_memory=True,\n                         persistent_workers=True, prefetch_factor=4)\n\nmodel.eval()\nprobs = torch.zeros((len(test_ds),10), device=DEVICE, dtype=torch.float32); ids=[]\nwith torch.no_grad(), autocast(enabled=(DEVICE==\"cuda\")):\n    i0=0\n    for x,ids_batch in test_loader:\n        x=x.to(DEVICE, non_blocking=True)\n        p1=model(x).softmax(1)\n        p2=model(torch.flip(x,dims=[3])).softmax(1)\n        pm=0.5*(p1+p2); b=x.size(0)\n        probs[i0:i0+b]=pm; ids.extend(ids_batch); i0+=b\n\npred = probs.argmax(1).tolist()\nlabels=[classes[i] for i in pred]\nwith open(\"submission.csv\",\"w\",newline=\"\") as f:\n    w=csv.writer(f); w.writerow([\"id\",\"label\"])\n    for i,lab in zip(ids,labels): w.writerow([i,lab])\nprint(\"submission.csv written with\", len(ids), \"rows\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-24T18:11:01.584496Z","iopub.execute_input":"2025-09-24T18:11:01.584887Z","iopub.status.idle":"2025-09-24T18:49:25.659523Z","shell.execute_reply.started":"2025-09-24T18:11:01.584857Z","shell.execute_reply":"2025-09-24T18:49:25.658433Z"}},"outputs":[],"execution_count":null}]}