{"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":2214915,"sourceType":"datasetVersion","datasetId":1330115},{"sourceId":9098127,"sourceType":"datasetVersion","datasetId":5490768},{"sourceId":268942,"sourceType":"modelInstanceVersion","modelInstanceId":230141,"modelId":251887},{"sourceId":714180,"sourceType":"modelInstanceVersion","modelInstanceId":540408,"modelId":553604}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ============================================================================\n# CSIRO BIOMASS PREDICTION - COMPLETE ENHANCED NOTEBOOK (OFFLINE)\n# ============================================================================\n# v7 - FIXED submission format: ID1001187975__Dry_Clover_g\n\nimport os\nimport gc\nimport random\nimport warnings\nimport numpy as np\nimport pandas as pd\nimport cv2\nfrom pathlib import Path\nfrom PIL import Image\nfrom tqdm.auto import tqdm\nfrom dataclasses import dataclass\n\n# ML Libraries\nfrom sklearn.model_selection import KFold\nfrom sklearn.ensemble import (\n    GradientBoostingRegressor, \n    HistGradientBoostingRegressor,\n)\nfrom sklearn.decomposition import PCA\nfrom sklearn.cross_decomposition import PLSRegression\nfrom sklearn.mixture import GaussianMixture\n\n# Boosting Libraries\nfrom catboost import CatBoostRegressor\nfrom lightgbm import LGBMRegressor\n\n# Deep Learning\nimport torch\nfrom transformers import AutoModel, AutoImageProcessor\n\nwarnings.filterwarnings('ignore')\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\ntqdm.pandas()\n\n# ============================================================================\n# CONFIGURATION\n# ============================================================================\n\n@dataclass\nclass Config:\n    DATA_PATH: Path = Path('/kaggle/input/csiro-biomass')\n    TRAIN_CSV: Path = Path('/kaggle/input/csiro-biomass/train.csv')\n    TEST_CSV: Path = Path('/kaggle/input/csiro-biomass/test.csv')\n    TRAIN_IMAGE_DIR: Path = Path('/kaggle/input/csiro-biomass/train')\n\n    SIGLIP_PATH: str = '/kaggle/input/google-siglip-so400m-patch14-384/transformers/default/1'\n\n    SEED: int = 42\n    DEVICE: str = 'cuda' if torch.cuda.is_available() else 'cpu'\n    PATCH_SIZE: int = 520\n    OVERLAP: int = 16\n\n    # Target names as they appear AFTER pivot (with underscores)\n    # These match the sample_submission.csv format\n    TARGET_NAMES = ['__Dry_Clover_g', '__Dry_Dead_g', '__Dry_Green_g', '__Dry_Total_g', '__GDM_g']\n\ncfg = Config()\n\ndef seeding(SEED):\n    np.random.seed(SEED)\n    random.seed(SEED)\n    os.environ['PYTHONHASHSEED'] = str(SEED)\n    torch.manual_seed(SEED)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed(SEED)\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False\n\nseeding(cfg.SEED)\n\n\n# ============================================================================\n# DATA LOADING\n# ============================================================================\n\ndef load_train_data():\n    print(\"Loading train.csv...\")\n    df = pd.read_csv(cfg.TRAIN_CSV)\n    print(f\"Raw shape: {df.shape}\")\n    print(f\"Sample sample_id: {df['sample_id'].iloc[0]}\")\n\n    # Extract image_id from sample_id (e.g., \"ID1001187975__Dry_Clover_g\" -> \"ID1001187975\")\n    df['image_id'] = df['sample_id'].str.extract(r'(ID\\d+)')\n\n    # Extract target_name (e.g., \"__Dry_Clover_g\")\n    df['target_name'] = df['sample_id'].str.replace(r'ID\\d+', '', regex=True)\n\n    print(f\"Unique target_names: {df['target_name'].unique().tolist()}\")\n\n    # Pivot\n    df_pivot = df.pivot(index='image_id', columns='target_name', values='target').reset_index()\n    df_pivot.columns.name = None\n\n    print(f\"Pivoted columns: {df_pivot.columns.tolist()}\")\n\n    df_pivot['image_path'] = df_pivot['image_id'].apply(\n        lambda x: str(cfg.TRAIN_IMAGE_DIR / f\"{x}.jpg\")\n    )\n\n    return df_pivot\n\n\ndef load_test_data():\n    print(\"Loading test.csv...\")\n    df = pd.read_csv(cfg.TEST_CSV)\n\n    print(f\"Test sample_id example: {df['sample_id'].iloc[0]}\")\n\n    df['image_id'] = df['sample_id'].str.extract(r'(ID\\d+)')\n    df_unique = df[['image_id', 'image_path']].drop_duplicates().reset_index(drop=True)\n    df_unique = df_unique.copy()\n    df_unique['image_path'] = df_unique['image_path'].apply(lambda p: str(cfg.DATA_PATH / p))\n\n    print(f\"Unique test images: {len(df_unique)}\")\n    return df_unique\n\n\n# ============================================================================\n# IMAGE PROCESSING & EMBEDDINGS\n# ============================================================================\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)\n            x2 = min(x + patch_size, w)\n            y1 = max(0, y2 - patch_size)\n            x1 = max(0, x2 - patch_size)\n            patch = image[y1:y2, x1:x2, :]\n            patches.append(patch)\n    return patches\n\n\ndef compute_embeddings(model_path, df):\n    print(f\"Computing Embeddings for {len(df)} images...\")\n\n    model = AutoModel.from_pretrained(model_path, local_files_only=True).eval().to(cfg.DEVICE)\n    processor = AutoImageProcessor.from_pretrained(model_path, local_files_only=True)\n\n    EMBEDDINGS = []\n    for _, row in tqdm(df.iterrows(), total=len(df)):\n        try:\n            img = cv2.imread(row['image_path'])\n            if img is None:\n                raise ValueError(f\"Image not found\")\n            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n            patches = split_image(img, patch_size=cfg.PATCH_SIZE, overlap=cfg.OVERLAP)\n            images = [Image.fromarray(p) for p in patches]\n\n            inputs = processor(images=images, return_tensors=\"pt\").to(cfg.DEVICE)\n            with torch.no_grad():\n                features = model.get_image_features(**inputs)\n\n            avg_embed = features.mean(dim=0).cpu().numpy()\n            EMBEDDINGS.append(avg_embed)\n        except Exception as e:\n            print(f\"Error: {e}\")\n            EMBEDDINGS.append(np.zeros(1152))\n\n    return np.array(EMBEDDINGS)\n\n\n# ============================================================================\n# FEATURE ENGINEERING\n# ============================================================================\n\nclass SupervisedEmbeddingEngine:\n    def __init__(self, n_pca=0.80, n_pls=8, n_gmm=6):\n        self.n_pca = n_pca\n        self.n_pls = n_pls\n        self.n_gmm = n_gmm\n        self.pca = None\n        self.pls_models = {}\n        self.gmm = None\n\n    def fit(self, X, y, target_names):\n        self.pca = PCA(n_components=self.n_pca, random_state=42)\n        X_pca = self.pca.fit_transform(X)\n\n        for i, name in enumerate(target_names):\n            n_comp = min(self.n_pls, X_pca.shape[1], X_pca.shape[0] - 1)\n            if n_comp > 0:\n                pls = PLSRegression(n_components=n_comp)\n                pls.fit(X_pca, y[:, i])\n                self.pls_models[name] = pls\n\n        n_gmm = min(self.n_gmm, X_pca.shape[0])\n        self.gmm = GaussianMixture(n_components=n_gmm, random_state=42)\n        self.gmm.fit(X_pca)\n        return self\n\n    def transform(self, X):\n        X_pca = self.pca.transform(X)\n        pls_features = [pls.transform(X_pca) for pls in self.pls_models.values()]\n        X_pls = np.hstack(pls_features) if pls_features else np.zeros((X_pca.shape[0], 0))\n        X_gmm = self.gmm.predict_proba(X_pca)\n        return np.hstack([X_pca, X_pls, X_gmm])\n\n    def fit_transform(self, X, y, target_names):\n        self.fit(X, y, target_names)\n        return self.transform(X)\n\n\n# ============================================================================\n# CROSS-VALIDATION\n# ============================================================================\n\ndef cross_validate_predict(model_cls, model_params, train_labels, sem_train, sem_test, \n                           target_idx, n_splits=5):\n    kf = KFold(n_splits=n_splits, shuffle=True, random_state=cfg.SEED)\n    test_preds = np.zeros(len(sem_test))\n\n    for fold, (train_idx, val_idx) in enumerate(kf.split(sem_train)):\n        print(f\"    Fold {fold}...\")\n        X_train = sem_train[train_idx]\n        y_train = train_labels[train_idx, target_idx]\n\n        model = model_cls(**model_params)\n        model.fit(X_train, y_train)\n        test_preds += model.predict(sem_test) / n_splits\n\n    return test_preds\n\n\n# ============================================================================\n# MAIN PIPELINE\n# ============================================================================\n\ndef main():\n    print(\"=\" * 70)\n    print(\"CSIRO BIOMASS PREDICTION - v7 (CORRECT SUBMISSION FORMAT)\")\n    print(\"=\" * 70)\n\n    # Load Data\n    train_df = load_train_data()\n    test_df = load_test_data()\n\n    # Find target columns (everything except image_id, image_path)\n    non_target_cols = ['image_id', 'image_path']\n    target_cols = [c for c in train_df.columns if c not in non_target_cols]\n\n    print(f\"\\nTarget columns: {target_cols}\")\n    print(f\"Train samples: {len(train_df)}, Test samples: {len(test_df)}\")\n\n    # Compute Embeddings\n    train_embeddings = compute_embeddings(cfg.SIGLIP_PATH, train_df)\n    test_embeddings = compute_embeddings(cfg.SIGLIP_PATH, test_df)\n\n    print(f\"Train embeddings shape: {train_embeddings.shape}\")\n    print(f\"Test embeddings shape: {test_embeddings.shape}\")\n\n    # Prepare labels\n    train_labels = train_df[target_cols].values\n    print(f\"Train labels shape: {train_labels.shape}\")\n\n    # Show training data stats\n    print(\"\\nTraining data statistics:\")\n    for i, col in enumerate(target_cols):\n        vals = train_labels[:, i]\n        nonzero = np.sum(vals > 0)\n        print(f\"  {col}: mean={vals.mean():.2f}, max={vals.max():.2f}, non-zero={nonzero}/{len(vals)}\")\n\n    # Generate Semantic Features\n    print(\"\\nGenerating Semantic Features...\")\n    feat_engine = SupervisedEmbeddingEngine(n_pca=0.80, n_pls=8, n_gmm=6)\n    sem_train = feat_engine.fit_transform(train_embeddings, train_labels, target_cols[:3])\n    sem_test = feat_engine.transform(test_embeddings)\n\n    print(f\"Train Features Shape: {sem_train.shape}\")\n    print(f\"Test Features Shape: {sem_test.shape}\")\n\n    # Model Parameters\n    params_cat = {\n        'iterations': 1900, 'learning_rate': 0.045, 'depth': 4,\n        'l2_leaf_reg': 0.56, 'random_strength': 0.045, 'bagging_temperature': 0.98,\n        'verbose': 0, 'random_state': 42, 'allow_writing_files': False\n    }\n    params_lgbm = {\n        'n_estimators': 807, 'learning_rate': 0.014, 'num_leaves': 48,\n        'min_child_samples': 19, 'subsample': 0.745, 'colsample_bytree': 0.745,\n        'reg_alpha': 0.21, 'reg_lambda': 3.78, 'verbose': -1, 'random_state': 42\n    }\n    params_hist = {\n        'max_iter': 300, 'learning_rate': 0.05, 'max_depth': None,\n        'l2_regularization': 0.44, 'random_state': 42\n    }\n    params_gb = {\n        'n_estimators': 500, 'learning_rate': 0.05, 'max_depth': 4,\n        'subsample': 0.8, 'random_state': 42\n    }\n\n    # Train ALL targets\n    print(\"\\n\" + \"=\" * 50)\n    print(\"TRAINING MODELS\")\n    print(\"=\" * 50)\n\n    all_preds = {}\n\n    for k, target_col in enumerate(target_cols):\n        print(f\"\\n--- {target_col} ---\")\n\n        print(\"  Training HistGB...\")\n        pred_hist = cross_validate_predict(\n            HistGradientBoostingRegressor, params_hist,\n            train_labels, sem_train, sem_test, k\n        )\n\n        print(\"  Training CatBoost...\")\n        pred_cat = cross_validate_predict(\n            CatBoostRegressor, params_cat,\n            train_labels, sem_train, sem_test, k\n        )\n\n        print(\"  Training LightGBM...\")\n        pred_lgbm = cross_validate_predict(\n            LGBMRegressor, params_lgbm,\n            train_labels, sem_train, sem_test, k\n        )\n\n        print(\"  Training GradientBoosting...\")\n        pred_gb = cross_validate_predict(\n            GradientBoostingRegressor, params_gb,\n            train_labels, sem_train, sem_test, k\n        )\n\n        ensemble_pred = (pred_hist + pred_cat + pred_lgbm + pred_gb) / 4.0\n        ensemble_pred = np.clip(ensemble_pred, 0, None)\n\n        all_preds[target_col] = ensemble_pred\n        print(f\"  -> Pred: min={ensemble_pred.min():.2f}, max={ensemble_pred.max():.2f}, mean={ensemble_pred.mean():.2f}\")\n\n    # Build predictions dataframe\n    print(\"\\n\" + \"=\" * 50)\n    print(\"POST-PROCESSING & SUBMISSION\")\n    print(\"=\" * 50)\n\n    test_df = test_df.copy()\n    for target_col in target_cols:\n        test_df[target_col] = all_preds[target_col]\n\n    # Apply threshold (small values -> 0)\n    print(\"\\nApplying threshold (< 0.09 -> 0)...\")\n    for col in target_cols:\n        if 'Clover' in col or 'Green' in col or 'Dead' in col:\n            test_df[col] = test_df[col].apply(lambda x: 0.0 if x < 0.09 else x)\n\n    # Mass balance: GDM = Green + Clover, Total = GDM + Dead\n    print(\"Applying mass balance...\")\n    green_col = [c for c in target_cols if 'Green' in c][0]\n    dead_col = [c for c in target_cols if 'Dead' in c][0]\n    clover_col = [c for c in target_cols if 'Clover' in c][0]\n    gdm_col = [c for c in target_cols if 'GDM' in c][0]\n    total_col = [c for c in target_cols if 'Total' in c][0]\n\n    test_df[gdm_col] = test_df[green_col] + test_df[clover_col]\n    test_df[total_col] = test_df[gdm_col] + test_df[dead_col]\n\n    # Create submission in EXACT format: sample_id = \"ID1001187975__Dry_Clover_g\"\n    print(\"\\nCreating submission...\")\n    submission_rows = []\n\n    for _, row in test_df.iterrows():\n        image_id = row['image_id']\n        for target_col in target_cols:\n            # sample_id format: ID + target_col (e.g., \"ID1001187975__Dry_Clover_g\")\n            sample_id = f\"{image_id}{target_col}\"\n            prediction = row[target_col]\n            submission_rows.append({'sample_id': sample_id, 'target': prediction})\n\n    sub_df = pd.DataFrame(submission_rows)\n\n    output_path = 'submission.csv'\n    sub_df.to_csv(output_path, index=False)\n\n    print(f\"\\nSubmission saved to {output_path}\")\n    print(f\"Submission shape: {sub_df.shape}\")\n    print(\"\\nFirst 10 rows:\")\n    print(sub_df.head(10))\n\n    print(\"\\nFinal Prediction Statistics:\")\n    for col in target_cols:\n        vals = test_df[col].values\n        print(f\"  {col}: mean={vals.mean():.2f}, std={vals.std():.2f}\")\n\n    return sub_df\n\n\nif __name__ == '__main__':\n    submission = main()\n    print(\"\\nDone!\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}