{"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":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":14113251,"sourceType":"datasetVersion","datasetId":8990263},{"sourceId":14145231,"sourceType":"datasetVersion","datasetId":9004317},{"sourceId":289705324,"sourceType":"kernelVersion"}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<div style=\"background-color:#F0F8FF; border-radius:15px; border:1px solid #4682B4; padding: 25px;\">\n    <div style=\"text-align:center;\">\n        <h1 style=\"color:#2E8B57; font-family:sans-serif; font-weight:800; font-size:32px;\">🚀 CSIRO Inference: Pure Vision SOTA benchmark</h1>\n        <h3 style=\"color:#4682B4; font-family:sans-serif; font-weight:600;\">ViT-Base/Small + Tiling + TTA (No Metadata Leakage)</h3>\n    </div>\n</div>","metadata":{}},{"cell_type":"code","source":"import sys\nimport os\nimport gc\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom tqdm.auto import tqdm\nfrom PIL import Image\nimport cv2\nimport re\n\n# Deep Learning\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nimport pytorch_lightning as pl\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport timm\nfrom sklearn.preprocessing import RobustScaler, MinMaxScaler\n\n# --- CONFIG ---\nclass CFG:\n    # ⚠️ IMPORTANT: Update this path to where you uploaded your .ckpt files\n    MODEL_DIR = \"/kaggle/input/csiro-nb/models/\" \n    \n    # Competition Data Paths\n    TEST_CSV = \"/kaggle/input/csiro-biomass/test.csv\"\n    TEST_DIR = \"/kaggle/input/csiro-biomass/test\"\n    TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n    \n    # Model Architecture\n    MODEL_NAME = \"vit_small_patch14_dinov2.lvd142m\"\n    \n    # Tiling Settings\n    TILE_SIZE = 518       \n    IMG_LOAD_SIZE = 1036\n    \n    BATCH_SIZE = 8\n    NUM_WORKERS = 2\n    \n    # Targets\n    TARGET_COLS = ['Dry_Total_g', 'GDM_g', 'Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g']\n    AUX_COLS = ['Height_Ave_cm', 'Pre_GSHH_NDVI'] ","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-02T21:50:09.789943Z","iopub.execute_input":"2026-01-02T21:50:09.790252Z","iopub.status.idle":"2026-01-02T21:50:09.796495Z","shell.execute_reply.started":"2026-01-02T21:50:09.790229Z","shell.execute_reply":"2026-01-02T21:50:09.795792Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🧬 Model Classes","metadata":{}},{"cell_type":"code","source":"class TiledDINO(pl.LightningModule):\n    def __init__(self):\n        super().__init__()\n        # Load Backbone (Pretrained=False for Offline)\n        self.backbone = timm.create_model(\n            CFG.MODEL_NAME, \n            pretrained=False, \n            num_classes=0,\n            img_size=CFG.TILE_SIZE \n        )\n        \n        embed_dim = self.backbone.num_features\n        \n        # Heads\n        self.head_main = nn.Sequential(\n            nn.Linear(embed_dim, 512),\n            nn.BatchNorm1d(512),\n            nn.SiLU(),\n            nn.Dropout(0.1),\n            nn.Linear(512, 5)\n        )\n        \n        self.head_aux = nn.Sequential(\n            nn.Linear(embed_dim, 256),  \n            nn.BatchNorm1d(256),        \n            nn.SiLU(),                  \n            nn.Linear(256, 2)           \n        )\n    def forward(self, x):\n        # x: [Batch, 4, 3, 518, 518]\n        b, n_tiles, c, h, w = x.shape\n        x_flat = x.view(b * n_tiles, c, h, w)\n        \n        feats = self.backbone(x_flat) \n        feats = feats.view(b, n_tiles, -1)\n        global_feat = torch.mean(feats, dim=1) \n        \n        return self.head_main(global_feat), self.head_aux(global_feat)\n\ndef predict_tta(model_list, img_batch, meta_batch):\n    # Pass meta_batch to the forward function combining the predictions across all models (ensemble)\n    return np.mean([m(img_batch, meta_batch)[0].cpu().numpy() for m in model_list], axis=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-02T21:50:11.789658Z","iopub.execute_input":"2026-01-02T21:50:11.790405Z","iopub.status.idle":"2026-01-02T21:50:11.796970Z","shell.execute_reply.started":"2026-01-02T21:50:11.790378Z","shell.execute_reply":"2026-01-02T21:50:11.796261Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 📂 Data Handling & Processing","metadata":{}},{"cell_type":"code","source":"class TiledDataset(Dataset):\n    def __init__(self, df, transforms=None):\n        self.df = df.reset_index(drop=True)\n        self.paths = df['full_path'].values\n        self.transforms = transforms\n\n    def __len__(self):\n        return len(self.df)\n    \n    def tile_image(self, image):\n        H, W = CFG.IMG_LOAD_SIZE, CFG.IMG_LOAD_SIZE\n        image = cv2.resize(image, (W, H))\n        \n        t_sz = CFG.TILE_SIZE\n        tiles = []\n        for i in range(2):\n            for j in range(2):\n                tile = image[i*t_sz:(i+1)*t_sz, j*t_sz:(j+1)*t_sz, :]\n                tiles.append(tile)\n        return tiles\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        try:\n            image = np.array(Image.open(path).convert(\"RGB\"))\n        except:\n            image = np.zeros((CFG.IMG_LOAD_SIZE, CFG.IMG_LOAD_SIZE, 3), dtype=np.uint8)\n            \n        tiles = self.tile_image(image)\n        \n        processed_tiles = []\n        for t in tiles:\n            if self.transforms:\n                t = self.transforms(image=t)['image']\n            processed_tiles.append(t)\n            \n        img_tensor = torch.stack(processed_tiles)\n        return img_tensor\n\ndef get_transforms():\n    return A.Compose([\n        A.Resize(CFG.TILE_SIZE, CFG.TILE_SIZE),\n        A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n        ToTensorV2(),\n    ])\n\ndef get_processor():\n    print(\"Fitting Scaler on Training Data...\")\n    train_df = pd.read_csv(CFG.TRAIN_CSV)\n    train_df['image_id'] = train_df['sample_id'].str.split('__').str[0]\n    \n    # Pivot logic to match training\n    meta_cols = ['image_path', 'Sampling_Date']\n    df_wide = train_df.pivot_table(\n        index=['image_id'] + meta_cols,\n        columns='target_name',\n        values='target',\n        aggfunc='first'\n    ).reset_index()\n    \n    main_scaler = RobustScaler()\n    main_scaler.fit(df_wide[CFG.TARGET_COLS])\n    print(\"✅ Scaler fitted.\")\n    return main_scaler\n\ndef predict_tta(model_list, img_batch):\n    return np.mean([m(img_batch)[0].cpu().numpy() for m in model_list], axis=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-02T21:50:12.049571Z","iopub.execute_input":"2026-01-02T21:50:12.050191Z","iopub.status.idle":"2026-01-02T21:50:12.059169Z","shell.execute_reply.started":"2026-01-02T21:50:12.050170Z","shell.execute_reply":"2026-01-02T21:50:12.058635Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🚀 Main Inference Pipeline\nIncludes **Metadata Encoding** setup and **TTA Loop**.","metadata":{}},{"cell_type":"code","source":"print(\"🚀 Starting Pure Vision Inference...\")\n\npattern = r\"vit_small_patch14_dinov2\\.lvd142m/fold_\\d/.*\\.ckpt$\"\n\n# 1. Load Test Data\ntest_df = pd.read_csv(CFG.TEST_CSV)\ntest_df['image_id'] = test_df['sample_id'].str.split('__').str[0]\n\n# Detect Extension\ntry:\n    sample_file = [f for f in os.listdir(CFG.TEST_DIR) if not f.startswith('.')][0]\n    ext = os.path.splitext(sample_file)[1]\nexcept:\n    ext = \".png\"\n\ntest_df['full_path'] = test_df['image_id'].apply(lambda x: os.path.join(CFG.TEST_DIR, f\"{x}{ext}\"))\n\n# Unique images only\ndf_unique = test_df[['image_id', 'full_path']].drop_duplicates(subset=['image_id']).reset_index(drop=True)\n\ntest_ds = TiledDataset(df_unique, get_transforms())\nloader = DataLoader(test_ds, batch_size=CFG.BATCH_SIZE, shuffle=False, num_workers=CFG.NUM_WORKERS)\n\n# 2. Load Models\nmodels = []\nckpt_files = [\n    str(p) for p in Path(CFG.MODEL_DIR).rglob(\"*.ckpt\") \n    if re.search(pattern, str(p))\n]\nprint(f\"📂 Found {len(ckpt_files)} checkpoints.\")\n\nfor p in ckpt_files:\n    try:\n        model = TiledDINO.load_from_checkpoint(str(p))\n        model.eval().cuda()\n        models.append(model)\n        print(f\"   ✅ Loaded {p}\")\n    except Exception as e:\n        print(f\"   ⚠️ Skipping {p}: {e}\")\n\nif not models:\n    raise RuntimeError(\"No models loaded! Check CFG.MODEL_DIR\")\n\n# 3. Inference with TTA\nprocessor = get_processor()\nall_preds = []\n\nprint(\"🔥 Running Inference (x3 TTA)...\")\n\nwith torch.no_grad():\n    for i, img in tqdm(enumerate(loader), desc=\"Predicting\"):\n        img = img.cuda()\n\n        # TTA 1: Original\n        p1 = predict_tta(models, img)\n        \n        # TTA 2: Horizontal Flip\n        img_h = torch.flip(img, dims=[4]) \n        p2 = predict_tta(models, img_h)\n        \n        # TTA 3: Vertical Flip\n        img_v = torch.flip(img, dims=[3]) \n        p3 = predict_tta(models, img_v)\n        \n        # TTA 4: Rotate 90 degrees\n        img_r1 = torch.rot90(img, 1, [3, 4])\n        p4 = predict_tta(models, img_r1)\n        \n        # TTA 5: Rotate 270 degrees\n        img_r3 = torch.rot90(img, 3, [3, 4])\n        p5 = predict_tta(models, img_r3)\n\n        # Average all 5 TTA predictions\n        batch_preds = (p1 + p2 + p3 + p4 + p5) / 5.0\n        all_preds.append(batch_preds)\n\n# 4. Post-Processing & Submission\nall_preds = np.vstack(all_preds)\nall_preds = processor.inverse_transform(all_preds) # Scale back to grams\nall_preds = np.maximum(0, all_preds) # Clip negatives\n\nprint(\"Writing Submission file...\")\npred_map = {img_id: preds for img_id, preds in zip(df_unique['image_id'].values, all_preds)}\ntarget_idx = {name: i for i, name in enumerate(CFG.TARGET_COLS)}\n\nfinal_values = []\nsub_template = pd.read_csv(CFG.TEST_CSV)\n\nfor _, row in sub_template.iterrows():\n    img_id = row['sample_id'].split('__')[0]\n    t_name = row['target_name']\n    \n    if img_id in pred_map:\n        val = pred_map[img_id][target_idx[t_name]]\n        final_values.append(val)\n    else:\n        final_values.append(0.0)\n\nsub_template['target'] = final_values\nsub_template[['sample_id', 'target']].to_csv(\"submission.csv\", index=False)\nprint(\"🎉 Done! submission.csv created successfully.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-02T21:50:16.078538Z","iopub.execute_input":"2026-01-02T21:50:16.079269Z","iopub.status.idle":"2026-01-02T21:50:20.605009Z","shell.execute_reply.started":"2026-01-02T21:50:16.079241Z","shell.execute_reply":"2026-01-02T21:50:20.604290Z"}},"outputs":[],"execution_count":null}]}