{"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":3175,"databundleVersionId":44352,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, zipfile\n\nbase = \"/kaggle/input/galaxy-zoo-the-galaxy-challenge\"\nwork = \"/kaggle/working\"\n\n# Unzip training images\nwith zipfile.ZipFile(os.path.join(base, \"images_training_rev1.zip\"), \"r\") as z:\n    z.extractall(work)\n\n# Unzip test images\nwith zipfile.ZipFile(os.path.join(base, \"images_test_rev1.zip\"), \"r\") as z:\n    z.extractall(work)\n\n# Unzip labels\nwith zipfile.ZipFile(os.path.join(base, \"training_solutions_rev1.zip\"), \"r\") as z:\n    z.extractall(work)\n\n# Unzip zero benchmark (submission template)\nwith zipfile.ZipFile(os.path.join(base, \"all_zeros_benchmark.zip\"), \"r\") as z:\n    z.extractall(work)\n\nprint(os.listdir(work))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T08:59:06.846158Z","iopub.execute_input":"2025-11-29T08:59:06.846378Z","iopub.status.idle":"2025-11-29T08:59:34.909816Z","shell.execute_reply.started":"2025-11-29T08:59:06.846348Z","shell.execute_reply":"2025-11-29T08:59:34.908845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Full corrected script for EffB4 with 9-view TTA (fixes empty-param error)\n# Requires timm: uncomment and run `!pip install -q timm` on Kaggle if missing\n\nimport os, cv2, random\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport timm\n\nfrom sklearn.model_selection import train_test_split\nfrom torch.cuda.amp import autocast, GradScaler\n\n# ---------------- CONFIG ----------------\nIMG_SIZE   = 256\nBATCH_SIZE = 16\nEPOCHS     = 70\nLR         = 2e-4\nDEVICE     = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nTRAIN_DIR = \"/kaggle/working/images_training_rev1\"\nTEST_DIR  = \"/kaggle/working/images_test_rev1\"\nLABELS    = \"/kaggle/working/training_solutions_rev1.csv\"\nBENCH     = \"/kaggle/working/all_zeros_benchmark.csv\"\nBEST_CKPT = \"/kaggle/working/effb4_T10_best.pth\"\n\nIMAGENET_MEAN = [0.485, 0.456, 0.406]\nIMAGENET_STD  = [0.229, 0.224, 0.225]\n\n# ---------------- SEEDING ----------------\ndef seed_everything(seed: int = 1337):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n\nseed_everything(1337)\n\n# ---------------- TRANSFORMS ----------------\ntrain_tf = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.RandomResizedCrop(IMG_SIZE, scale=(0.6, 1.0)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(360),\n    transforms.ColorJitter(0.3, 0.3, 0.3, 0.1),\n    transforms.ToTensor(),\n    transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD),\n    transforms.RandomErasing(p=0.5)\n])\n\nval_tf = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD),\n])\n\n# ---------------- DATASET (fixed dunders) ----------------\nclass GalaxyDataset(Dataset):\n    def __init__(self, galaxy_ids, img_dir, labels=None, transform=None):\n        self.galaxy_ids = list(galaxy_ids)\n        self.img_dir = img_dir\n        self.labels = None if labels is None else np.asarray(labels, dtype=np.float32)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.galaxy_ids)\n\n    def __getitem__(self, idx):\n        gid = self.galaxy_ids[idx]\n        img_path = os.path.join(self.img_dir, f\"{gid}.jpg\")\n        img_bgr = cv2.imread(img_path)\n        if img_bgr is None:\n            # fallback zero image\n            img = np.zeros((IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8)\n        else:\n            img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        if self.labels is not None:\n            label = torch.from_numpy(self.labels[idx]).float()\n            return img, label\n        return img\n\n# ---------------- LOAD DATA ----------------\ndf = pd.read_csv(LABELS)\nall_ids = df[\"GalaxyID\"].values\ntargets = df.drop(\"GalaxyID\", axis=1).values  # shape (N,37)\n\nX_train_ids, X_val_ids, y_train, y_val = train_test_split(\n    all_ids, targets, test_size=0.1, random_state=42\n)\n\ntrain_ds = GalaxyDataset(X_train_ids, TRAIN_DIR, y_train, transform=train_tf)\nval_ds   = GalaxyDataset(X_val_ids,   TRAIN_DIR, y_val,   transform=val_tf)\n\ntrain_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True)\nval_loader   = DataLoader(val_ds,   batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\n\n# ---------------- MODEL (timm) ----------------\nclass StrongEffB4(nn.Module):\n    def __init__(self, pretrained=True, num_classes=37):\n        super().__init__()\n        # timm reliably lets you set num_classes\n        self.model = timm.create_model(\"efficientnet_b4\", pretrained=pretrained, num_classes=num_classes)\n\n    def forward(self, x):\n        return self.model(x)\n\nmodel = StrongEffB4(pretrained=True, num_classes=37).to(DEVICE)\n\n# ------------- SANITY CHECK BEFORE OPTIMIZER -------------\ntotal_params = sum(p.numel() for p in model.parameters())\ntrainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\nprint(\"Model params:\", total_params, \"Trainable:\", trainable_params)\nassert total_params > 0, \"Model has zero parameters — constructor didn't run correctly. Re-run class cell.\"\n\n# ---------------- OPTIM / SCHED / LOSS / AMP ----------------\noptimizer = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=1e-4)\nscheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS)\ncriterion = nn.MSELoss()\nscaler = GradScaler()\n\n# ---------------- TRAINING LOOP ----------------\nbest_val_loss = float(\"inf\")\n\nfor epoch in range(EPOCHS):\n    model.train()\n    train_loss_sum = 0.0\n    n_train = 0\n\n    for imgs, labels in tqdm(train_loader, desc=f\"[EffB4-T10] Epoch {epoch+1}/{EPOCHS} - train\"):\n        imgs, labels = imgs.to(DEVICE, non_blocking=True), labels.to(DEVICE, non_blocking=True)\n\n        optimizer.zero_grad()\n        # autocast with explicit device type\n        if DEVICE == \"cuda\":\n            with torch.amp.autocast(device_type=\"cuda\"):\n                outputs = model(imgs)\n                loss = criterion(outputs, labels)\n        else:\n            with torch.amp.autocast(device_type=\"cpu\"):\n                outputs = model(imgs)\n                loss = criterion(outputs, labels)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        train_loss_sum += loss.item() * imgs.size(0)\n        n_train += imgs.size(0)\n\n    scheduler.step()\n    train_loss = train_loss_sum / max(1, n_train)\n\n    # validation\n    model.eval()\n    val_loss_sum = 0.0\n    n_val = 0\n    with torch.no_grad():\n        for imgs, labels in tqdm(val_loader, desc=f\"[EffB4-T10] Epoch {epoch+1}/{EPOCHS} - val\"):\n            imgs, labels = imgs.to(DEVICE, non_blocking=True), labels.to(DEVICE, non_blocking=True)\n            if DEVICE == \"cuda\":\n                with torch.amp.autocast(device_type=\"cuda\"):\n                    outputs = model(imgs)\n                    loss = criterion(outputs, labels)\n            else:\n                with torch.amp.autocast(device_type=\"cpu\"):\n                    outputs = model(imgs)\n                    loss = criterion(outputs, labels)\n\n            val_loss_sum += loss.item() * imgs.size(0)\n            n_val += imgs.size(0)\n\n    val_loss = val_loss_sum / max(1, n_val)\n    print(f\"[EffB4-T10] Epoch {epoch+1}/{EPOCHS} | Train: {train_loss:.6f} | Val: {val_loss:.6f}\")\n\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        torch.save(model.state_dict(), BEST_CKPT)\n        print(f\"  💾 New best model saved with val_loss={best_val_loss:.6f}\")\n\n# ---------------- LOAD BEST ----------------\nprint(f\"\\nLoading best checkpoint from {BEST_CKPT}\")\nmodel.load_state_dict(torch.load(BEST_CKPT, map_location=DEVICE))\nmodel.eval()\n\n# ---------------- 9-VIEW TTA PREDICTION ----------------\ndef tta_9view(model, imgs):\n    # imgs: (B,C,H,W) on device\n    aug_imgs = [\n        imgs,\n        torch.flip(imgs, [3]),                    # horiz\n        torch.flip(imgs, [2]),                    # vert\n        torch.rot90(imgs, 1, [2,3]),\n        torch.rot90(imgs, 2, [2,3]),\n        torch.rot90(imgs, 3, [2,3]),\n        torch.flip(torch.rot90(imgs, 1, [2,3]), [3]),\n        torch.flip(torch.rot90(imgs, 2, [2,3]), [3]),\n        torch.flip(torch.rot90(imgs, 3, [2,3]), [3]),\n    ]\n    outs = []\n    for a in aug_imgs:\n        # use autocast for inference as well\n        if DEVICE == \"cuda\":\n            with torch.amp.autocast(device_type=\"cuda\"):\n                outs.append(model(a))\n        else:\n            with torch.amp.autocast(device_type=\"cpu\"):\n                outs.append(model(a))\n    return torch.stack(outs).mean(0)\n\nsub_template = pd.read_csv(BENCH)\ntest_ids = sub_template[\"GalaxyID\"].values\ntest_ds = GalaxyDataset(test_ids, TEST_DIR, labels=None, transform=val_tf)\ntest_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\n\nall_preds = []\nwith torch.no_grad():\n    for imgs in tqdm(test_loader, desc=\"[EffB4-T10] TTA predicting\"):\n        # DataLoader returns images only because labels=None\n        if isinstance(imgs, (list, tuple)):\n            imgs = imgs[0]\n        imgs = imgs.to(DEVICE, non_blocking=True)\n        preds = tta_9view(model, imgs)\n        all_preds.append(preds.cpu().numpy())\n\npreds = np.vstack(all_preds)\n\n# ---------------- GROUP NORMALIZATION ----------------\npreds = np.clip(preds, 0.0, 1.0)\ngroups = [\n    (0,3),(3,5),(5,8),(8,11),(11,15),\n    (15,18),(18,25),(25,28),(28,31),(31,37)\n]\nfor s,e in groups:\n    g = preds[:, s:e].sum(axis=1, keepdims=True)\n    g[g == 0] = 1.0\n    preds[:, s:e] /= g\n\nsub = sub_template.copy()\nsub.iloc[:, 1:] = preds\nsub.to_csv(\"submission_effb4_T10.csv\", index=False)\nprint(\"\\n✅ Saved submission_effb4_T10.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T09:02:58.423754Z","iopub.execute_input":"2025-11-29T09:02:58.424308Z","execution_failed":"2025-11-29T20:58:09.857Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nBEST_CKPT = \"/kaggle/working/effb4_T10_best.pth\"\n\n# 1) Does the file exist?\nprint(\"exists:\", os.path.exists(BEST_CKPT))\n\n# 2) File size (helps check it's not empty)\nif os.path.exists(BEST_CKPT):\n    print(\"size (MB):\", os.path.getsize(BEST_CKPT) / 1024**2)\n\n# 3) List recent files in working dir\nprint(\"working dir contents (sample):\", sorted(os.listdir(\"/kaggle/working\"))[-20:])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:03:31.252286Z","iopub.execute_input":"2025-11-29T21:03:31.253154Z","iopub.status.idle":"2025-11-29T21:03:31.264764Z","shell.execute_reply.started":"2025-11-29T21:03:31.253117Z","shell.execute_reply":"2025-11-29T21:03:31.263955Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# show current working directory and disk usage\nimport os, sys, subprocess\nprint(\"pwd:\", os.getcwd())\nprint(\"cwd contents:\", sorted(os.listdir(\".\"))[:200])\n\n# show /kaggle/working contents (where we save)\nwk = \"/kaggle/working\"\nprint(\"/kaggle/working exists:\", os.path.exists(wk))\nif os.path.exists(wk):\n    print(\"working dir files (full):\", sorted(os.listdir(wk))[:200])\n\n# show free disk space (MB)\nstat = os.statvfs(wk if os.path.exists(wk) else \".\")\nfree_mb = stat.f_bavail * stat.f_frsize / 1024**2\nprint(\"free space (MB):\", round(free_mb,2))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T21:04:31.171618Z","iopub.execute_input":"2025-11-29T21:04:31.171905Z","iopub.status.idle":"2025-11-29T21:04:31.178833Z","shell.execute_reply.started":"2025-11-29T21:04:31.171883Z","shell.execute_reply":"2025-11-29T21:04:31.178030Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}