{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":112509,"databundleVersionId":14254895},{"sourceType":"datasetVersion","sourceId":16173781,"datasetId":10370505,"databundleVersionId":17150479},{"sourceType":"datasetVersion","sourceId":16191086,"datasetId":10381214,"databundleVersionId":17169436}],"dockerImageVersionId":31328,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\nimport os, gc, warnings\nfrom pathlib import Path\nfrom dataclasses import dataclass\nimport cv2, numpy as np, pandas as pd\nfrom tqdm.auto import tqdm\nimport torch, torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport timm\n\nwarnings.filterwarnings('ignore')\ntorch.backends.cuda.matmul.allow_tf32 = True\ntorch.backends.cudnn.benchmark = True\n\n@dataclass\nclass CFG:\n    DATA = Path(\"/kaggle/input/competitions/csiro-biomass\")\n    TRAIN_DIR = DATA / \"train\"\n    TEST_DIR = DATA / \"test\"\n    MODEL_NAME = 'vit_large_patch16_dinov3_qkvb'\n    DINO_WEIGHTS = \"/kaggle/input/datasets/geekhole/dinov3large/dinov3_large_weights.pth\"\n    FOLDS_DIR = \"/kaggle/input/datasets/geekhole/siglipfoldds\"      \n   \n    \n    DEVICE = \"cuda\"\n    IMG_SIZE = 384\n    BATCH = 8\n    NUM_WORKERS = 2\n    N_FOLDS = 5\n    TARGETS = ['Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g', 'GDM_g', 'Dry_Total_g']\n    TARGET_MAX = np.array([157.98, 83.84, 71.79, 157.98, 185.70])\n\nprint(\"=\" * 60)\nprint(\"✓ IMPORTS OK\")\nprint(f\"  PyTorch: {torch.__version__}, Timm: {timm.__version__}\")\nprint(f\"  Device: {'CUDA' if torch.cuda.is_available() else 'CPU'}\")\nprint(\"=\" * 60)\n\n\ndef clean_image(img):\n    h, w = img.shape[:2]\n    img = img[:int(h * 0.9), :]\n    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)\n    mask = cv2.inRange(hsv, (5, 100, 100), (25, 255, 255))\n    if mask.sum() > 0:\n        img = cv2.inpaint(img, cv2.dilate(mask, np.ones((3,3), np.uint8), iterations=1), 3, cv2.INPAINT_TELEA)\n    return img\n\nclass FilmLayer(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        h = max(32, dim // 2)\n        self.net = nn.Sequential(nn.Linear(dim, h), nn.ReLU(), nn.Linear(h, dim * 2))\n    def forward(self, x):\n        g, b = torch.chunk(self.net(x), 2, dim=1)\n        return g, b\n\nclass BioModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.backbone = timm.create_model(CFG.MODEL_NAME, pretrained=False, num_classes=0, global_pool='avg')\n        weights = torch.load(CFG.DINO_WEIGHTS, map_location='cpu')\n        self.backbone.load_state_dict(weights)\n        nf = self.backbone.num_features\n        self.film = FilmLayer(nf)\n        def head():\n            return nn.Sequential(\n                nn.Linear(nf*2, nf//2), nn.ReLU(), nn.Dropout(0.2),\n                nn.Linear(nf//2, nf//4), nn.ReLU(), nn.Dropout(0.2),\n                nn.Linear(nf//4, 1)\n            )\n        self.h_green = head()\n        self.h_clover = head()\n        self.h_dead = head()\n        self.sp = nn.Softplus(beta=1.0)\n    def forward(self, left, right):\n        fl = self.backbone(left)\n        fr = self.backbone(right)\n        c = (fl + fr) / 2\n        g, b = self.film(c)\n        fl = fl * (1 + g) + b\n        fr = fr * (1 + g) + b\n        x = torch.cat([fl, fr], dim=1)\n        green = self.sp(self.h_green(x))\n        clover = self.sp(self.h_clover(x))\n        dead = self.sp(self.h_dead(x))\n        return torch.cat([green, dead, clover, green+clover, green+clover+dead], dim=1)\n\nclass BioDataset(Dataset):\n    def __init__(self, df, img_dir, augs=None):\n        self.df = df.reset_index(drop=True)\n        self.dir = str(img_dir)\n        self.augs = augs\n    def __len__(self): return len(self.df)\n    def __getitem__(self, i):\n        row = self.df.iloc[i]\n        img = cv2.imread(os.path.join(self.dir, os.path.basename(row['image_path'])))\n        img = cv2.cvtColor(img if img is not None else np.zeros((1000,2000,3), np.uint8), cv2.COLOR_BGR2RGB)\n        img = clean_image(img)\n        mid = img.shape[1] // 2\n        L = self.augs(image=img[:,:mid])['image']\n        R = self.augs(image=img[:,mid:])['image']\n        return L, R\n\ndef val_augs():\n    return A.Compose([\n        A.Resize(CFG.IMG_SIZE, CFG.IMG_SIZE),\n        A.Normalize([0.485,0.456,0.406], [0.229,0.224,0.225]),\n        ToTensorV2(),\n    ])\n\nprint(\"✓ Model and Dataset defined\")\nprint(\"=\" * 60)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T17:16:22.728687Z","iopub.execute_input":"2026-05-12T17:16:22.729162Z","iopub.status.idle":"2026-05-12T17:16:22.750039Z","shell.execute_reply.started":"2026-05-12T17:16:22.729132Z","shell.execute_reply":"2026-05-12T17:16:22.749138Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport gc\n\nprint(\"Loading test data...\")\ntest_csv = pd.read_csv(CFG.DATA / \"test.csv\")\ntest = test_csv[['image_path']].drop_duplicates().reset_index(drop=True)\nprint(f\"Test images: {len(test)}\")\n\ntest_ds = BioDataset(test, CFG.TEST_DIR, val_augs())\ntest_ld = DataLoader(test_ds, batch_size=CFG.BATCH, shuffle=False, num_workers=CFG.NUM_WORKERS, pin_memory=True)\n\nfold_paths = {}\nfor fold in range(CFG.N_FOLDS):\n    fname = f'dino_fold{fold}.pth'\n    candidate = os.path.join(CFG.FOLDS_DIR, fname)\n    if os.path.exists(candidate):\n        fold_paths[fold] = candidate\n\nif len(fold_paths) < CFG.N_FOLDS:\n    print(f\"⚠ Found only {len(fold_paths)} folds out of {CFG.N_FOLDS}\")\nelse:\n    print(f\"✓ All {CFG.N_FOLDS} fold weights found\")\n\n\nall_preds = []\nfor fold in sorted(fold_paths.keys()):\n    print(f\"\\nFold {fold}...\")\n    model = BioModel().to(CFG.DEVICE)\n    model.load_state_dict(torch.load(fold_paths[fold], map_location=CFG.DEVICE))\n    model.eval()\n    \n    fold_preds = []\n    with torch.no_grad(), torch.amp.autocast('cuda', dtype=torch.bfloat16):\n        for L, R in test_ld:\n            pred = model(L.to(CFG.DEVICE), R.to(CFG.DEVICE))\n            fold_preds.append(pred.cpu().float().numpy())\n    \n    all_preds.append(np.vstack(fold_preds))\n    del model\n    torch.cuda.empty_cache()\n    gc.collect()\n\n\npreds = np.mean(all_preds, axis=0)\nprint(f\"\\n✓ Ensemble predictions: {preds.shape}\")\n\n\n\n\n\nrows = []\nfor i, row in test.iterrows():\n    img_id = Path(row['image_path']).stem\n    for j, target in enumerate(CFG.TARGETS):\n        val = max(0.0, float(preds[i, j]))\n        rows.append({'sample_id': f\"{img_id}__{target}\", 'target': val})\n\nsub = pd.DataFrame(rows).sort_values('sample_id').reset_index(drop=True)\nsub.to_csv('submission.csv', index=False)\nprint(f\"\\n✓ submission.csv saved: {len(sub)} rows\")\nprint(sub.head(10))\nprint(f\"\\nTarget means: {sub.groupby(sub['sample_id'].str.extract(r'__(.*)')[0])['target'].mean().to_dict()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T17:16:22.751502Z","iopub.execute_input":"2026-05-12T17:16:22.751801Z","execution_failed":"2026-05-12T17:14:01.184Z"}},"outputs":[],"execution_count":null}]}