{"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":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":14369415,"sourceType":"datasetVersion","datasetId":8618494}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport cv2\nimport timm\nimport torch\nimport lightgbm as lgb\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom torchvision import transforms\nfrom tqdm import tqdm\nfrom sklearn.model_selection import KFold\nimport warnings\n\nwarnings.filterwarnings('ignore')\nprint(\"Initializing Strategy #3: Hybrid Fusion...\")\n\nclass CFG:\n    BASE_PATH = '/kaggle/input/csiro-biomass'\n    TRAIN_CSV = os.path.join(BASE_PATH, 'train.csv')\n    TEST_CSV = os.path.join(BASE_PATH, 'test.csv')\n    \n    # Using Pretrained Weights\n    WEIGHTS_PATH = '/kaggle/input/offline-efficientnet/pretrained_models'\n    DL_BACKBONE = 'efficientnet_b0'\n    DL_BACKBONE_WEIGHTS = os.path.join(WEIGHTS_PATH, 'efficientnet_b0_state_dict.pth')\n    \n    IMAGE_SIZE = 224\n    BATCH_SIZE = 32\n    DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n    N_SPLITS = 5\n    RANDOM_STATE = 42\n\n# --- 1. FEATURE EXTRACTION ---\n# A. Maths Features\ndef get_math_feats(image_path):\n    try:\n        img = cv2.imread(image_path)\n        if img is None: return [0, 0, 0]\n        img = cv2.resize(img, (224, 224))\n        b, g, r = cv2.split(img.astype(float))\n        # ExG (Greenness)\n        exg = 2*g - r - b\n        # CIVE (Complex Color Index)\n        cive = 0.441*r - 0.811*g + 0.385*b + 18.787\n        # Texture (Laplacian)\n        gray = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2GRAY)\n        tex = cv2.Laplacian(gray, cv2.CV_64F).var()\n        return [np.mean(exg), np.mean(cive), tex]\n    except: return [0, 0, 0]\n\n# B. Deep Features\nclass FeatureDataset(Dataset):\n    def __init__(self, paths):\n        self.paths = paths\n        self.transform = transforms.Compose([\n            transforms.Resize((CFG.IMAGE_SIZE, CFG.IMAGE_SIZE)),\n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n        ])\n    def __len__(self): return len(self.paths)\n    def __getitem__(self, idx): return self.transform(Image.open(self.paths[idx]).convert('RGB'))\n\n# --- 2. EXECUTION ---\ntrain_df = pd.read_csv(CFG.TRAIN_CSV)\ntrain_pivot = train_df.pivot_table(index='image_path', columns='target_name', values='target').reset_index()\ntrain_pivot['full_path'] = train_pivot['image_path'].apply(lambda x: os.path.join(CFG.BASE_PATH, x))\n\ntest_df = pd.read_csv(CFG.TEST_CSV)\ntest_unique = test_df[['image_path']].drop_duplicates().reset_index(drop=True)\ntest_unique['full_path'] = test_unique['image_path'].apply(lambda x: os.path.join(CFG.BASE_PATH, x))\n\nall_paths = np.concatenate([train_pivot['full_path'].values, test_unique['full_path'].values])\n\n# Extract Maths\nprint(\"Extracting Math Features...\")\nmath_feats = np.array([get_math_feats(p) for p in tqdm(all_paths)])\n\n# Extract Deep Features\nprint(\"Extracting Deep Features...\")\nmodel = timm.create_model(CFG.DL_BACKBONE, pretrained=False, num_classes=0).to(CFG.DEVICE)\nif os.path.exists(CFG.DL_BACKBONE_WEIGHTS):\n    model.load_state_dict(torch.load(CFG.DL_BACKBONE_WEIGHTS), strict=False)\nmodel.eval()\n\ndl_loader = DataLoader(FeatureDataset(all_paths), batch_size=CFG.BATCH_SIZE, shuffle=False, num_workers=2)\ndeep_feats = []\nwith torch.no_grad():\n    for imgs in tqdm(dl_loader):\n        deep_feats.append(model(imgs.to(CFG.DEVICE)).cpu().numpy())\ndeep_feats = np.vstack(deep_feats)\n\n# Combine\nX_all = np.hstack([math_feats, deep_feats])\nX_train = X_all[:len(train_pivot)]\nX_test = X_all[len(train_pivot):]\nY = train_pivot[['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'GDM_g', 'Dry_Total_g']]\n\n# --- 3. TRAINING ---\nprint(\"\\nTraining Hybrid Model...\")\nkf = KFold(n_splits=CFG.N_SPLITS, shuffle=True, random_state=CFG.RANDOM_STATE)\nfinal_preds = np.zeros((len(test_unique), 5))\noof_preds = np.zeros(Y.shape)\n\nfor i, col in enumerate(Y.columns):\n    y = Y[col].values\n    valid_mask = ~np.isnan(y)\n    X_curr = X_train[valid_mask]\n    y_curr = y[valid_mask]\n    \n    fold_test_preds = np.zeros(len(test_unique))\n    for t_idx, v_idx in kf.split(X_curr):\n        # Increased regularization\n        model = lgb.LGBMRegressor(\n            objective='regression_l1', n_estimators=1500, learning_rate=0.03,\n            num_leaves=31, colsample_bytree=0.7, verbose=-1\n        )\n        model.fit(X_curr[t_idx], y_curr[t_idx])\n        oof_preds[np.where(valid_mask)[0][v_idx], i] = model.predict(X_curr[v_idx])\n        fold_test_preds += model.predict(X_test) / CFG.N_SPLITS\n        \n    final_preds[:, i] = fold_test_preds\n\n# Score Check\nfrom sklearn.metrics import r2_score\ndef get_score(y_true, y_pred):\n    weights = [0.1, 0.1, 0.1, 0.2, 0.5]\n    scores = []\n    for i in range(5):\n        mask = ~np.isnan(y_true.iloc[:, i])\n        if mask.sum() > 0:\n            scores.append(r2_score(y_true.loc[mask].iloc[:, i], y_pred[mask, i]) * weights[i])\n        else: scores.append(0)\n    return sum(scores)\n\ncv_score = get_score(Y, oof_preds)\nprint(f\"\\nFinal CV Score: {cv_score:.4f}\")\n\n# Submission\nsub_rows = []\nfor i, row in test_unique.iterrows():\n    img_id = row['image_path'].split('/')[-1].split('.')[0]\n    for j, col in enumerate(Y.columns):\n        val = max(0, final_preds[i, j])\n        sub_rows.append({'sample_id': f\"{img_id}__{col}\", 'target': val})\n\npd.DataFrame(sub_rows).to_csv('submission.csv', index=False)\nprint(\"Submission Created.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-02T08:44:30.077923Z","iopub.execute_input":"2026-01-02T08:44:30.078476Z","execution_failed":"2026-01-02T08:48:31.409Z"}},"outputs":[],"execution_count":null}]}