{"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":"none","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:53:31.792264Z","iopub.execute_input":"2025-09-20T17:53:31.792552Z","iopub.status.idle":"2025-09-20T17:53:32.051952Z","shell.execute_reply.started":"2025-09-20T17:53:31.792521Z","shell.execute_reply":"2025-09-20T17:53:32.051338Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model to file","metadata":{}},{"cell_type":"code","source":"%%writefile cifar10_resnet_baseline.py\n\"\"\"\nAssignment 4 — CIFAR10 Residual CNN (PyTorch) Baseline\n(random init, residual connections)\n\"\"\"\n\nimport argparse\nimport csv\nimport os\nimport random\nfrom pathlib import Path\nfrom typing import Tuple, List\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nfrom torch.utils.data import DataLoader, Subset, Dataset\nfrom torchvision import datasets, transforms\nfrom PIL import Image\nfrom tqdm import tqdm\n\n# -----------------------------\n# Utils\n# -----------------------------\n\nCIFAR10_CLASSES = [\n    \"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\",\n    \"dog\", \"frog\", \"horse\", \"ship\", \"truck\",\n]\n\ndef set_seed(seed: int = 42):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n\n# -----------------------------\n# Model: Basic ResNet for CIFAR-10\n# -----------------------------\n\nclass BasicBlock(nn.Module):\n    expansion = 1\n    def __init__(self, in_planes, planes, stride=1, drop_prob=0.0):\n        super().__init__()\n        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride,\n                               padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(planes)\n        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1,\n                               padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(planes)\n        self.shortcut = nn.Sequential()\n        if stride != 1 or in_planes != planes:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes),\n            )\n        self.drop_prob = drop_prob\n\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n        if self.drop_prob > 0.0:\n            out = F.dropout(out, p=self.drop_prob, training=self.training)\n        out += self.shortcut(x)\n        out = F.relu(out)\n        return out\n\nclass SmallResNet(nn.Module):\n    \"\"\"A compact ResNet designed for CIFAR-10 (32x32).\"\"\"\n    def __init__(self, block: nn.Module, num_blocks: List[int], num_classes: int = 10, base: int = 64,\n                 drop_prob: float = 0.0):\n        super().__init__()\n        self.in_planes = base\n        self.conv1 = nn.Conv2d(3, base, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(base)\n        self.layer1 = self._make_layer(block, base, num_blocks[0], stride=1, drop_prob=drop_prob)\n        self.layer2 = self._make_layer(block, base * 2, num_blocks[1], stride=2, drop_prob=drop_prob)\n        self.layer3 = self._make_layer(block, base * 4, num_blocks[2], stride=2, drop_prob=drop_prob)\n        self.layer4 = self._make_layer(block, base * 4, num_blocks[3], stride=2, drop_prob=drop_prob)\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(base * 4 * block.expansion, num_classes)\n\n        # Kaiming init to keep it purely random-init (no pretrain)\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\")\n            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):\n                nn.init.ones_(m.weight)\n                nn.init.zeros_(m.bias)\n\n    def _make_layer(self, block, planes, num_blocks, stride, drop_prob):\n        strides = [stride] + [1] * (num_blocks - 1)\n        layers = []\n        for s in strides:\n            layers.append(block(self.in_planes, planes, s, drop_prob))\n            self.in_planes = planes * block.expansion\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.layer1(out)\n        out = self.layer2(out)\n        out = self.layer3(out)\n        out = self.layer4(out)\n        out = self.avgpool(out)\n        out = torch.flatten(out, 1)\n        out = self.fc(out)\n        return out\n\ndef resnet18tiny(num_classes=10, base=64, drop_prob=0.0):\n    # 2-2-2-2 blocks: light variant for CIFAR-10\n    return SmallResNet(BasicBlock, [2, 2, 2, 2], num_classes=num_classes, base=base, drop_prob=drop_prob)\n\n# -----------------------------\n# Data\n# -----------------------------\n\ndef get_transforms():\n    train_tf = 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((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),\n    ])\n    test_tf = transforms.Compose([\n        transforms.ToTensor(),\n        transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),\n    ])\n    return train_tf, test_tf\n\nclass KaggleCIFAR10Train(Dataset):\n    \"\"\"Use Kaggle competition bundle (train.7z + trainLabels.csv).\"\"\"\n    def __init__(self, train_dir: str, labels_csv: str, transform, id_filter: List[int] | None = None):\n        super().__init__()\n        import csv as _csv\n        id2label = {}\n        with open(labels_csv, \"r\") as f:\n            r = _csv.reader(f)\n            next(r)  # header\n            for row in r:\n                _id = int(row[0]); _lab = row[1]\n                id2label[_id] = CIFAR10_CLASSES.index(_lab)\n        self.id2label = id2label\n\n        all_ids = [int(p.stem) for p in Path(train_dir).glob(\"*.png\")]\n        all_ids.sort()\n        if id_filter is None:\n            self.ids = all_ids\n        else:\n            filt = set(id_filter)\n            self.ids = [i for i in all_ids if i in filt]\n        self.train_dir = Path(train_dir)\n        self.transform = transform\n\n    def __len__(self): return len(self.ids)\n\n    def __getitem__(self, idx):\n        _id = self.ids[idx]\n        img = Image.open(self.train_dir / f\"{_id}.png\").convert(\"RGB\")\n        x = self.transform(img)\n        y = self.id2label[_id]\n        return x, y\n\ndef _ensure_extract_7z(src_7z: str, out_dir: str):\n    \"\"\"Extracts 7z to out_dir if not already extracted (handles nested /train/train layout later).\"\"\"\n    out = Path(out_dir)\n    # Skip if we already see PNGs either directly under out_dir or under out_dir/train\n    if out.exists() and (any(out.glob(\"*.png\")) or any((out / \"train\").glob(\"*.png\"))):\n        return\n    out.mkdir(parents=True, exist_ok=True)\n    os.system(f\"7z x {src_7z} -o{out_dir} -y > /dev/null\")\n\ndef _resolve_image_dir(root_dir: str) -> Path:\n    \"\"\"Handle Kaggle 7z layout: sometimes files land under train/train/*.png.\"\"\"\n    p = Path(root_dir)\n    if any(p.glob(\"*.png\")):\n        return p\n    if (p / \"train\").exists() and any((p / \"train\").glob(\"*.png\")):\n        return p / \"train\"\n    return p  # fallback\n\ndef build_dataloaders(data_root: str, batch_size: int, num_workers: int, val_split: int,\n                      use_kaggle_bundle: bool = False, kaggle_input_root: str = \"/kaggle/input/cifar-10\") -> Tuple[DataLoader, DataLoader]:\n    train_tf, test_tf = get_transforms()\n\n    if use_kaggle_bundle:\n        train_7z = Path(kaggle_input_root) / \"train.7z\"\n        labels_csv = Path(kaggle_input_root) / \"trainLabels.csv\"\n        extract_root = Path(\"/kaggle/working/train\")\n        _ensure_extract_7z(str(train_7z), str(extract_root))\n        train_dir = _resolve_image_dir(str(extract_root))\n\n        all_ids = sorted([int(p.stem) for p in train_dir.glob(\"*.png\")])\n        if len(all_ids) == 0:\n            raise RuntimeError(f\"No training PNGs found under {train_dir}. Check extraction.\")\n        rng = np.random.default_rng(42)\n        rng.shuffle(all_ids)\n        if val_split > 0:\n            val_ids = all_ids[:val_split]\n            tr_ids = all_ids[val_split:]\n            train_ds = KaggleCIFAR10Train(str(train_dir), str(labels_csv), transform=train_tf, id_filter=tr_ids)\n            val_ds = KaggleCIFAR10Train(str(train_dir), str(labels_csv), transform=test_tf, id_filter=val_ids)\n        else:\n            train_ds = KaggleCIFAR10Train(str(train_dir), str(labels_csv), transform=train_tf)\n            val_ds = KaggleCIFAR10Train(str(train_dir), str(labels_csv), transform=test_tf, id_filter=all_ids[:5000])\n    else:\n        # Fallback: official torchvision CIFAR-10\n        train_full = datasets.CIFAR10(root=data_root, train=True, download=True, transform=train_tf)\n        if val_split > 0:\n            indices = list(range(len(train_full)))\n            np.random.shuffle(indices)\n            val_idx = indices[:val_split]\n            train_idx = indices[val_split:]\n            train_ds = Subset(train_full, train_idx)\n            val_ds = Subset(datasets.CIFAR10(root=data_root, train=True, download=False, transform=test_tf), val_idx)\n        else:\n            train_ds = train_full\n            val_ds = datasets.CIFAR10(root=data_root, train=False, download=True, transform=test_tf)\n\n    train_loader = DataLoader(\n        train_ds, batch_size=batch_size, shuffle=True,\n        num_workers=num_workers, pin_memory=True,\n        persistent_workers=num_workers > 0,\n    )\n    val_loader = DataLoader(\n        val_ds, batch_size=batch_size, shuffle=False,\n        num_workers=num_workers, pin_memory=True,\n        persistent_workers=num_workers > 0,\n    )\n    return train_loader, val_loader\n\n# -----------------------------\n# Training & Evaluation\n# -----------------------------\n\ndef accuracy(output, target):\n    with torch.no_grad():\n        pred = output.argmax(dim=1)\n        return (pred == target).float().mean().item()\n\ndef train_one_epoch(model, loader, optimizer, scaler, device, label_smoothing=0.0):\n    model.train(); total_loss=0.0; total_acc=0.0; n=0\n    for x, y in tqdm(loader, desc=\"train\", leave=False):\n        x, y = x.to(device, non_blocking=True), y.to(device, non_blocking=True)\n        optimizer.zero_grad(set_to_none=True)\n        with torch.cuda.amp.autocast(enabled=scaler is not None):\n            logits = model(x)\n            if label_smoothing > 0:\n                n_classes = logits.size(1)\n                with torch.no_grad():\n                    true_dist = torch.zeros_like(logits)\n                    true_dist.fill_(label_smoothing / (n_classes - 1))\n                    true_dist.scatter_(1, y.data.unsqueeze(1), 1 - label_smoothing)\n                loss = torch.mean(torch.sum(-true_dist * F.log_softmax(logits, dim=1), dim=1))\n            else:\n                loss = F.cross_entropy(logits, y)\n        if scaler is not None:\n            scaler.scale(loss).backward(); scaler.step(optimizer); scaler.update()\n        else:\n            loss.backward(); optimizer.step()\n        total_loss += loss.item()*x.size(0)\n        total_acc += accuracy(logits, y)*x.size(0)\n        n += x.size(0)\n    return total_loss/n, total_acc/n\n\ndef evaluate(model, loader, device):\n    model.eval(); total_loss=0.0; total_acc=0.0; n=0\n    with torch.no_grad():\n        for x, y in tqdm(loader, desc=\"val\", leave=False):\n            x, y = x.to(device), y.to(device)\n            logits = model(x); loss = F.cross_entropy(logits, y)\n            total_loss += loss.item()*x.size(0)\n            total_acc += accuracy(logits, y)*x.size(0)\n            n += x.size(0)\n    return total_loss/n, total_acc/n\n\n# -----------------------------\n# Inference on Kaggle test dir\n# -----------------------------\n\nclass KaggleCIFAR10Test(Dataset):\n    def __init__(self, test_dir: str, transform):\n        self.paths = sorted(list(Path(test_dir).glob(\"*.png\")), key=lambda p: int(p.stem))\n        self.transform = transform\n        assert len(self.paths) > 0, \"No PNG files found in test_dir.\"\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.transform(img), int(p.stem)\n\ndef predict_and_write_csv(model, test_dir: str, submission_path: str, batch_size: int, num_workers: int, device):\n    _, test_tf = get_transforms()\n    ds = KaggleCIFAR10Test(test_dir, test_tf)\n    loader = DataLoader(ds, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)\n    model.eval(); ids=[]; labels=[]\n    with torch.no_grad():\n        for x, idxs in tqdm(loader, desc=\"predict\"):\n            x = x.to(device)\n            logits = model(x)\n            preds = logits.argmax(dim=1).cpu().numpy().tolist()\n            ids.extend(idxs.numpy().tolist())\n            labels.extend([CIFAR10_CLASSES[p] for p in preds])\n    with open(submission_path, \"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])\n    print(f\"Saved submission to {submission_path} with {len(ids)} rows.\")\n\n# -----------------------------\n# Main\n# -----------------------------\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--data-root\", type=str, default=\"./data\")\n    parser.add_argument(\"--epochs\", type=int, default=60)\n    parser.add_argument(\"--batch-size\", type=int, default=128)\n    parser.add_argument(\"--lr\", type=float, default=0.1)\n    parser.add_argument(\"--wd\", type=float, default=5e-4)\n    parser.add_argument(\"--momentum\", type=float, default=0.9)\n    parser.add_argument(\"--val-split\", type=int, default=5000)\n    parser.add_argument(\"--num-workers\", type=int, default=4)\n    parser.add_argument(\"--seed\", type=int, default=42)\n    parser.add_argument(\"--model\", type=str, default=\"resnet18tiny\")\n    parser.add_argument(\"--dropout\", type=float, default=0.0)\n    parser.add_argument(\"--label-smoothing\", type=float, default=0.1)\n    parser.add_argument(\"--predict\", action=\"store_true\")\n    parser.add_argument(\"--test-dir\", type=str, default=\"./test\")\n    parser.add_argument(\"--ckpt\", type=str, default=\"./checkpoints/best.pt\")\n    parser.add_argument(\"--submission\", type=str, default=\"./submission.csv\")\n    parser.add_argument(\"--use-kaggle-bundle\", action=\"store_true\")\n    parser.add_argument(\"--kaggle-input-root\", type=str, default=\"/kaggle/input/cifar-10\")\n    parser.add_argument(\"--auto-extract-test\", action=\"store_true\",\n                        help=\"If set with --predict and --use-kaggle-bundle, auto-extract test.7z to /kaggle/working/test\")\n    args, _ = parser.parse_known_args()\n\n    set_seed(args.seed)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    torch.backends.cudnn.benchmark = True\n\n    # Build model\n    if args.model == \"resnet18tiny\":\n        model = resnet18tiny(base=64, drop_prob=args.dropout)\n    else:\n        raise ValueError(\"Unknown model\")\n    model = model.to(device)\n\n    if args.predict:\n        if args.auto_extract_test and args.use_kaggle_bundle:\n            test_7z = Path(args.kaggle_input_root) / \"test.7z\"\n            out = Path(\"/kaggle/working/test\")\n            if not out.exists() or not any(out.glob(\"*.png\")):\n                out.mkdir(parents=True, exist_ok=True)\n                os.system(f\"7z x {test_7z} -o{out} -y > /dev/null\")\n        assert os.path.isfile(args.ckpt), f\"Checkpoint not found: {args.ckpt}\"\n        state = torch.load(args.ckpt, map_location=device)\n        model.load_state_dict(state[\"model\"])\n        predict_and_write_csv(model, args.test_dir, args.submission, args.batch_size, args.num_workers, device)\n        return\n\n    # Data\n    train_loader, val_loader = build_dataloaders(\n        args.data_root, args.batch_size, args.num_workers, args.val_split,\n        use_kaggle_bundle=args.use_kaggle_bundle, kaggle_input_root=args.kaggle_input_root\n    )\n    print(f\"Data ready: {len(train_loader.dataset)} train / {len(val_loader.dataset)} val\")\n\n    # Optim & sched\n    optimizer = torch.optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.wd, nesterov=True)\n    scheduler = CosineAnnealingLR(optimizer, T_max=args.epochs)\n    scaler = torch.cuda.amp.GradScaler(enabled=torch.cuda.is_available())\n\n    best_acc = 0.0\n    ckpt_dir = Path(\"./checkpoints\"); ckpt_dir.mkdir(parents=True, exist_ok=True)\n\n    for epoch in range(1, args.epochs + 1):\n        print(f\"\\nEpoch {epoch}/{args.epochs}\")\n        tr_loss, tr_acc = train_one_epoch(model, train_loader, optimizer, scaler, device, args.label_smoothing)\n        val_loss, val_acc = evaluate(model, val_loader, device)\n        scheduler.step()\n        print(f\"train loss {tr_loss:.4f} acc {tr_acc:.4f} | val loss {val_loss:.4f} acc {val_acc:.4f}\")\n        if val_acc > best_acc:\n            best_acc = val_acc\n            torch.save({\"model\": model.state_dict(), \"acc\": best_acc, \"epoch\": epoch}, ckpt_dir / \"best.pt\")\n            print(f\"Saved new best with acc={best_acc:.4f}\")\n\n    print(f\"Best val acc: {best_acc:.4f}. Checkpoint at {ckpt_dir/'best.pt'}\")\n\nif __name__ == \"__main__\":\n    main()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-19T18:20:43.506596Z","iopub.execute_input":"2025-09-19T18:20:43.507199Z","iopub.status.idle":"2025-09-19T18:20:43.521231Z","shell.execute_reply.started":"2025-09-19T18:20:43.507174Z","shell.execute_reply":"2025-09-19T18:20:43.520634Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Check Available Cuda ","metadata":{}},{"cell_type":"code","source":"import torch, subprocess, textwrap\nprint(\"CUDA available:\", torch.cuda.is_available())\nprint(\"Device:\", torch.cuda.get_device_name(0) if torch.cuda.is_available() else \"CPU\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:58:42.766786Z","iopub.execute_input":"2025-09-20T17:58:42.767189Z","iopub.status.idle":"2025-09-20T17:58:42.771757Z","shell.execute_reply.started":"2025-09-20T17:58:42.767168Z","shell.execute_reply":"2025-09-20T17:58:42.770883Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# remove any previous extraction\n# !rm -rf /kaggle/working/test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T10:41:31.386654Z","iopub.execute_input":"2025-09-20T10:41:31.387207Z","iopub.status.idle":"2025-09-20T10:41:34.605405Z","shell.execute_reply.started":"2025-09-20T10:41:31.387170Z","shell.execute_reply":"2025-09-20T10:41:34.604073Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Extract Manually Test Data ","metadata":{}},{"cell_type":"markdown","source":"Decompressing test data manually.","metadata":{}},{"cell_type":"code","source":"# !mkdir -p /kaggle/working/test\n# !7z x -mmt=8 -aos /kaggle/input/cifar-10/test.7z -o/kaggle/working/test -y\n!find /kaggle/working/test/test -maxdepth 1 -name \"*.png\" | wc -l","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:57:42.351846Z","iopub.execute_input":"2025-09-20T17:57:42.352164Z","iopub.status.idle":"2025-09-20T17:57:42.843783Z","shell.execute_reply.started":"2025-09-20T17:57:42.352137Z","shell.execute_reply":"2025-09-20T17:57:42.842869Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"!python -u cifar10_resnet_baseline.py \\\n  --use-kaggle-bundle --kaggle-input-root /kaggle/input/cifar-10 \\\n  --epochs 60 --batch-size 128 --val-split 5000 --num-workers 4\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-19T18:21:07.750976Z","iopub.execute_input":"2025-09-19T18:21:07.751783Z","iopub.status.idle":"2025-09-19T18:41:32.058874Z","shell.execute_reply.started":"2025-09-19T18:21:07.751755Z","shell.execute_reply":"2025-09-19T18:41:32.058134Z"},"jupyter":{"outputs_hidden":true},"collapsed":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# confirm GPU & a few files exist\nimport torch, glob, os\nprint(\"CUDA available:\", torch.cuda.is_available())\nprint(\"GPU:\", torch.cuda.get_device_name(0) if torch.cuda.is_available() else \"CPU\")\nprint(\"Some test files:\", len(glob.glob(\"/kaggle/working/test/*.png\")))\nprint(\"Nested test files:\", len(glob.glob(\"/kaggle/working/test/test/*.png\")))\nprint(\"CKPT exists:\", os.path.isfile(\"/kaggle/working/checkpoints/best.pt\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:57:52.366324Z","iopub.execute_input":"2025-09-20T17:57:52.366951Z","iopub.status.idle":"2025-09-20T17:57:54.564678Z","shell.execute_reply.started":"2025-09-20T17:57:52.366920Z","shell.execute_reply":"2025-09-20T17:57:54.563758Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Testing","metadata":{}},{"cell_type":"code","source":"\n# run prediction (unbuffered so you see the tqdm bar)\n!python -u cifar10_resnet_baseline.py --predict \\\n  --test-dir /kaggle/working/test/test \\\n  --ckpt /kaggle/working/checkpoints/best.pt \\\n  --submission /kaggle/working/submission.csv \\\n  --batch-size 512 --num-workers 4\n\n!head -n 5 /kaggle/working/submission.csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:58:53.358485Z","iopub.execute_input":"2025-09-20T17:58:53.359090Z","iopub.status.idle":"2025-09-20T17:59:49.210067Z","shell.execute_reply.started":"2025-09-20T17:58:53.359068Z","shell.execute_reply":"2025-09-20T17:59:49.209354Z"}},"outputs":[],"execution_count":null}]}