{"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":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":291313552,"sourceType":"kernelVersion"},{"sourceId":292772100,"sourceType":"kernelVersion"},{"sourceId":293281528,"sourceType":"kernelVersion"},{"sourceId":725643,"sourceType":"modelInstanceVersion","modelInstanceId":551464,"modelId":564057}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =========================================================================================\n# 🏆 CSIRO BIOMASS: PLATINUM V45 (Double-Blind TTA + V15 Champion)\n# =========================================================================================\nimport os, gc, sys, cv2, glob, random, math, warnings\nimport numpy as np, pandas as pd\nimport torch, torch.nn as nn, timm\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom tqdm.auto import tqdm\nfrom pathlib import Path\nfrom copy import deepcopy\n\n# --- Imports ---\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.decomposition import PCA\nfrom sklearn.cross_decomposition import PLSRegression\nfrom sklearn.mixture import GaussianMixture\nfrom sklearn.model_selection import GroupKFold\n\n# PROVEN V15 SQUAD (Best Baseline)\nfrom sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor\nfrom catboost import CatBoostRegressor\nfrom xgboost import XGBRegressor\n\nfrom transformers import AutoModel, AutoImageProcessor, AutoTokenizer\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nwarnings.filterwarnings('ignore')\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n\nclass CFG:\n    SEED = 42\n    DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n    DATA_ROOT = Path(\"/kaggle/input/csiro-biomass\")\n    SIGLIP_PATH = \"/kaggle/input/google-siglip-so400m-patch14-384\"\n    if not os.path.exists(SIGLIP_PATH):\n        for root, dirs, files in os.walk(\"/kaggle/input\"):\n            if \"config.json\" in files and \"siglip\" in root.lower(): SIGLIP_PATH = root; break\n    \n    DINO_WEIGHTS_DIR = \"/kaggle/input/dino-512/pytorch/default/4\" \n    \n    IMG_SIZE = 512\n    BATCH_SIZE = 4\n    PATCH_SIZE = 520\n    OVERLAP = 16\n    TARGETS = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']\n    \n    # REQUIRED: V15 Scaling\n    TARGET_MAX = {\n        \"Dry_Clover_g\": 71.7865, \"Dry_Dead_g\": 83.8407, \"Dry_Green_g\": 157.9836,\n        \"Dry_Total_g\": 185.70, \"GDM_g\": 157.9836\n    }\n\ndef seed_everything(seed):\n    random.seed(seed); os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed); torch.manual_seed(seed); torch.cuda.manual_seed(seed)\nseed_everything(CFG.SEED)\n\n# =========================================================================================\n# PART A: SIGLIP + TTA EMBEDDINGS (THE UPGRADE)\n# =========================================================================================\nprint(\"\\n🔹 PART A: SIGLIP + TTA EMBEDDINGS\")\n\ndef split_image(image, patch_size=520, overlap=16):\n    h, w, c = image.shape\n    stride = patch_size - overlap\n    patches = []\n    for y in range(0, h, stride):\n        for x in range(0, w, stride):\n            y2 = min(y + patch_size, h); x2 = min(x + patch_size, w)\n            y1 = max(0, y2 - patch_size); x1 = max(0, x2 - patch_size)\n            patches.append(image[y1:y2, x1:x2, :])\n    return patches\n\ndef compute_embeddings_tta(model_path, df):\n    # >>> TTA UPGRADE: Compute Embeddings for Original AND Flipped <<<\n    print(f\"   Embeddings (TTA): {len(df)} images...\")\n    model = AutoModel.from_pretrained(model_path).eval().to(CFG.DEVICE)\n    processor = AutoImageProcessor.from_pretrained(model_path)\n    \n    EMBEDDINGS = []\n    for _, row in tqdm(df.iterrows(), total=len(df), leave=False):\n        try:\n            # 1. Load Image\n            img_orig = cv2.imread(row['image_path'])\n            if img_orig is None: raise ValueError\n            img_orig = cv2.cvtColor(img_orig, cv2.COLOR_BGR2RGB)\n            \n            # 2. Create Flip\n            img_flip = cv2.flip(img_orig, 1) # Horizontal Flip\n            \n            # 3. Process Both\n            emb_list = []\n            for img in [img_orig, img_flip]:\n                patches = split_image(img, patch_size=CFG.PATCH_SIZE, overlap=CFG.OVERLAP)\n                images = [Image.fromarray(p) for p in patches]\n                inputs = processor(images=images, return_tensors=\"pt\").to(CFG.DEVICE)\n                with torch.no_grad(): \n                    features = model.get_image_features(**inputs)\n                    emb_list.append(features.mean(dim=0).cpu().numpy())\n            \n            # 4. Average the Embeddings (Robustness)\n            avg_emb = (emb_list[0] + emb_list[1]) / 2.0\n            EMBEDDINGS.append(avg_emb)\n            \n        except: \n            EMBEDDINGS.append(np.zeros(1152)) \n            \n    return np.stack(EMBEDDINGS)\n\ndef generate_semantic_features(embeddings, model_path):\n    print(\"   Semantic Features...\")\n    model = AutoModel.from_pretrained(model_path).to(CFG.DEVICE)\n    tokenizer = AutoTokenizer.from_pretrained(model_path)\n    concepts = {\n        \"bare\": [\"bare soil\", \"dirt\"], \"dense\": [\"dense pasture\", \"thick grass\"],\n        \"green\": [\"lush green grass\", \"fresh growth\"], \"dead\": [\"dry dead grass\", \"yellow straw\"],\n        \"clover\": [\"white clover\", \"trifolium repens\"], \"grass\": [\"ryegrass\", \"fescue\"]\n    }\n    vecs = {}\n    with torch.no_grad():\n        for n, p in concepts.items():\n            inputs = tokenizer(p, padding=\"max_length\", return_tensors=\"pt\").to(CFG.DEVICE)\n            vecs[n] = model.get_text_features(**inputs).mean(dim=0, keepdim=True)\n            vecs[n] /= vecs[n].norm(p=2, dim=-1, keepdim=True)\n    img_tensor = torch.tensor(embeddings, dtype=torch.float32).to(CFG.DEVICE)\n    img_tensor /= img_tensor.norm(p=2, dim=-1, keepdim=True)\n    scores = {k: torch.matmul(img_tensor, v.T).cpu().numpy().flatten() for k, v in vecs.items()}\n    return pd.DataFrame(scores).values\n\nclass SupervisedEmbeddingEngine:\n    def __init__(self):\n        self.scaler = StandardScaler(); self.pca = PCA(n_components=0.80)\n        self.pls = PLSRegression(n_components=8)\n        self.gmm = GaussianMixture(n_components=6, covariance_type='diag', random_state=42)\n    def fit(self, X, y=None):\n        X_sc = self.scaler.fit_transform(X); self.pca.fit(X_sc)\n        try: self.gmm.fit(X_sc)\n        except: pass\n        if y is not None: self.pls.fit(X_sc, y)\n        return self\n    def transform(self, X, X_sem=None):\n        X_sc = self.scaler.transform(X)\n        feats = [self.pca.transform(X_sc)]\n        try: feats.append(self.gmm.predict_proba(X_sc))\n        except: pass\n        try: feats.insert(1, self.pls.transform(X_sc))\n        except: pass\n        if X_sem is not None: feats.append((X_sem - X_sem.mean(0))/(X_sem.std(0)+1e-6))\n        return np.hstack(feats)\n\ndef run_tabular_models():\n    train_raw = pd.read_csv(CFG.DATA_ROOT / \"train.csv\")\n    train_df = train_raw.pivot_table(index=['image_path', 'Sampling_Date'], columns='target_name', values='target').reset_index()\n    path_map = {}\n    for root, dirs, files in os.walk(CFG.DATA_ROOT / 'train'):\n        for f in files: path_map[f] = os.path.join(root, f)\n    train_df['image_path'] = train_df['image_path'].apply(lambda x: path_map.get(os.path.basename(x), x))\n    gkf = GroupKFold(n_splits=5)\n    train_df['fold'] = -1\n    for i, (train_idx, val_idx) in enumerate(gkf.split(train_df, groups=train_df['Sampling_Date'])):\n        train_df.loc[val_idx, 'fold'] = i\n    test_raw = pd.read_csv(CFG.DATA_ROOT / 'test.csv')\n    test_raw['target'] = 0.0 \n    test_df = test_raw.pivot_table(index='image_path', columns='target_name', values='target', aggfunc='mean').reset_index()\n    test_df['image_path'] = test_df['image_path'].apply(lambda p: str(CFG.DATA_ROOT / p))\n    \n    # Use TTA version here\n    tr_emb = compute_embeddings_tta(CFG.SIGLIP_PATH, train_df)\n    te_emb = compute_embeddings_tta(CFG.SIGLIP_PATH, test_df)\n    \n    all_sem = generate_semantic_features(np.vstack([tr_emb, te_emb]), CFG.SIGLIP_PATH)\n    sem_tr, sem_te = all_sem[:len(tr_emb)], all_sem[len(tr_emb):]\n    preds_accum = np.zeros((len(test_df), 5))\n    target_max = np.array([CFG.TARGET_MAX[t] for t in CFG.TARGETS])\n    \n    # V15 CHAMPIONS (Equal Weights)\n    models = [\n        (CatBoostRegressor(iterations=1000, learning_rate=0.05, depth=4, verbose=0, random_state=42), \"CatBoost\"),\n        (XGBRegressor(n_estimators=1000, learning_rate=0.05, max_depth=4, random_state=42), \"XGBoost\"),\n        (HistGradientBoostingRegressor(max_iter=300, learning_rate=0.05, random_state=42), \"HistGB\"),\n        (RandomForestRegressor(n_estimators=300, max_depth=8, random_state=42), \"RandomForest\") \n    ]\n    \n    print(f\"   Training TTA Ensemble...\")\n    for fold in range(5):\n        tr_mask = train_df['fold'] != fold\n        X_tr, y_tr = tr_emb[tr_mask], train_df.loc[tr_mask, CFG.TARGETS].values / target_max\n        S_tr = sem_tr[tr_mask]\n        eng = SupervisedEmbeddingEngine().fit(X_tr, y_tr)\n        X_tr_eng = eng.transform(X_tr, S_tr)\n        X_te_eng = eng.transform(te_emb, sem_te)\n        fold_preds = np.zeros_like(preds_accum)\n        for i, target in enumerate(CFG.TARGETS):\n            sub_pred = np.zeros(len(test_df))\n            for model_cls, name in models:\n                model = deepcopy(model_cls)\n                model.fit(X_tr_eng, y_tr[:, i])\n                sub_pred += model.predict(X_te_eng)\n            fold_preds[:, i] = (sub_pred / len(models)) * target_max[i]\n        preds_accum += fold_preds\n    df_sig = pd.DataFrame(preds_accum / 5, columns=CFG.TARGETS)\n    df_sig['image_path'] = test_df['image_path']\n    return df_sig\n\ndf_siglip = run_tabular_models()\nprint(\"✅ SigLIP TTA Ready.\")\n\n# =========================================================================================\n# PART B: DINO CHAMPION (V15 ENGINE)\n# =========================================================================================\nprint(\"\\n🔹 PART B: DINO CHAMPION\")\n\nclass LocalMambaBlock(nn.Module):\n    def __init__(self, dim, kernel_size=5, dropout=0.1):\n        super().__init__()\n        self.norm = nn.LayerNorm(dim)\n        self.dwconv = nn.Conv1d(dim, dim, kernel_size=kernel_size, padding=kernel_size//2, groups=dim)\n        self.gate = nn.Linear(dim, dim); self.proj = nn.Linear(dim, dim); self.drop = nn.Dropout(dropout)\n    def forward(self, x):\n        shortcut = x; x = self.norm(x); g = torch.sigmoid(self.gate(x)); x = x * g\n        x = self.dwconv(x.transpose(1, 2)).transpose(1, 2); return shortcut + self.drop(self.proj(x))\n\nclass BiomassDinoModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.backbone = timm.create_model('vit_huge_plus_patch16_dinov3.lvd1689m', pretrained=False, num_classes=0)\n        nf = self.backbone.num_features\n        self.fusion = nn.Sequential(LocalMambaBlock(nf), LocalMambaBlock(nf))\n        self.pool_attn = nn.Sequential(nn.Linear(nf, 128), nn.Tanh(), nn.Linear(128, 1), nn.Softmax(dim=1)) \n        self.head_green = nn.Linear(nf, 1)\n        self.head_dead = nn.Linear(nf, 1)\n        self.head_clover = nn.Linear(nf, 1)\n        \n    def forward(self, left, right):\n        x_l = self.backbone.forward_features(left); x_r = self.backbone.forward_features(right)\n        x_fused = self.fusion(torch.cat([x_l, x_r], dim=1))\n        w = self.pool_attn(x_fused)\n        x_pool = (x_fused * w).sum(dim=1)\n        return torch.cat([self.head_clover(x_pool), self.head_dead(x_pool), self.head_green(x_pool)], dim=1)\n\nclass DinoDataset(Dataset):\n    def __init__(self, df, img_size=512):\n        self.df = df\n        self.transform = A.Compose([A.Resize(img_size, img_size), A.Normalize(), ToTensorV2()])\n    def __len__(self): return len(self.df)\n    def __getitem__(self, idx):\n        path = self.df.iloc[idx]['image_path']\n        img = cv2.imread(path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if img is not None else np.zeros((1000, 2000, 3), dtype=np.uint8)\n        h, w = img.shape[:2]; left = img[0:h, 0:h]; right = img[0:h, w-h:w]\n        left = self.transform(image=left)['image']; right = self.transform(image=right)['image']\n        return left, right\n\ndef run_dino_inference():\n    test_raw = pd.read_csv(CFG.DATA_ROOT / 'test.csv')\n    test_unique = test_raw[['image_path']].drop_duplicates().reset_index(drop=True)\n    test_unique['image_path'] = test_unique['image_path'].apply(lambda x: str(CFG.DATA_ROOT / x))\n    \n    all_files = glob.glob(f\"{CFG.DINO_WEIGHTS_DIR}/*.pth\")\n    # Robust fallback\n    weight_files = [f for f in all_files if \"fold\" in f] \n    \n    print(f\"   Using Champion Model: {[os.path.basename(f) for f in weight_files]}\")\n    comp_preds = np.zeros((len(test_unique), 3))\n    loader = DataLoader(DinoDataset(test_unique, CFG.IMG_SIZE), batch_size=CFG.BATCH_SIZE, num_workers=2)\n    \n    for w_path in weight_files:\n        model = BiomassDinoModel().to(CFG.DEVICE)\n        try: model.load_state_dict(torch.load(w_path, map_location=CFG.DEVICE), strict=True)\n        except: model.load_state_dict(torch.load(w_path, map_location=CFG.DEVICE), strict=False)\n        model.eval()\n        \n        fold_preds = []\n        with torch.no_grad():\n            for l, r in tqdm(loader, leave=False):\n                l, r = l.to(CFG.DEVICE), r.to(CFG.DEVICE)\n                p1 = model(l, r)\n                p2 = model(torch.flip(l, [3]), torch.flip(r, [3]))\n                p3 = model(torch.flip(l, [2]), torch.flip(r, [2]))\n                p4 = model(torch.flip(l, [2, 3]), torch.flip(r, [2, 3]))\n                avg = (p1 + p2 + p3 + p4) / 4\n                fold_preds.append(np.expm1(avg.cpu().numpy())) \n        comp_preds += np.concatenate(fold_preds) / len(weight_files)\n        del model; torch.cuda.empty_cache()\n        \n    df_dino = pd.DataFrame()\n    df_dino['Dry_Clover_g'] = comp_preds[:, 0]\n    df_dino['Dry_Dead_g'] = comp_preds[:, 1]\n    df_dino['Dry_Green_g'] = comp_preds[:, 2]\n    df_dino['GDM_g'] = df_dino['Dry_Green_g'] + df_dino['Dry_Clover_g']\n    df_dino['Dry_Total_g'] = df_dino['GDM_g'] + df_dino['Dry_Dead_g']\n    df_dino['image_path'] = test_unique['image_path']\n    return df_dino\n\ndf_dino = run_dino_inference()\nprint(\"✅ Dino Ready.\")\n\n# =========================================================================================\n# PART C: CHAMPION BLEND (70/30) + HARD PHYSICS\n# =========================================================================================\nprint(\"\\n🔹 PART C: CHAMPION BLEND + HARD PHYSICS (V15 Logic)\")\n\ndf_siglip['key'] = df_siglip['image_path'].apply(os.path.basename)\ndf_dino['key'] = df_dino['image_path'].apply(os.path.basename)\nmerged = pd.merge(df_dino, df_siglip, on='key', suffixes=('_dino', '_sig'), how='left')\n\nfinal_df = pd.DataFrame()\nfinal_df['image_path'] = df_dino['image_path']\n\n# Use V15 Weights (proven 0.73)\nW_DINO = 0.70\nW_SIG = 0.30\n\n# 1. Base Predictions\nfor t in ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'GDM_g', 'Dry_Total_g']:\n    final_df[t] = (merged[f'{t}_dino'] * W_DINO) + (merged[f'{t}_sig'] * W_SIG)\n\n# 2. >>> HARD PHYSICS (V15) <<<\n# Overwrite Totals with Sums. This is what scored 0.73.\nfinal_df['GDM_g'] = final_df['Dry_Green_g'] + final_df['Dry_Clover_g']\nfinal_df['Dry_Total_g'] = final_df['GDM_g'] + final_df['Dry_Dead_g']\n\n# SAFETY CLIP\nfinal_df[final_df.select_dtypes(include=np.number).columns] = \\\n    final_df.select_dtypes(include=np.number).clip(lower=0.0)\n\ntest_raw = pd.read_csv(CFG.DATA_ROOT / 'test.csv')\npred_map = final_df.set_index(final_df['image_path'].apply(os.path.basename)).to_dict('index')\ntest_raw['target'] = test_raw.apply(lambda r: pred_map[os.path.basename(r['image_path'])][r['target_name']], axis=1)\ntest_raw['target'] = test_raw['target'].fillna(0.0)\ntest_raw[['sample_id', 'target']].to_csv(\"submission.csv\", index=False)\nprint(\"\\n✅✅ SUCCESS! Platinum V45 (Double-Blind TTA).\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}