{"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":4537,"sourceType":"modelInstanceVersion","modelInstanceId":3329,"modelId":986}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\n\nimport torch\nfrom transformers import AutoImageProcessor, AutoModel\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom catboost import CatBoostRegressor\n\n# CONFIG\nDINO_MODEL_ID = \"/kaggle/input/dinov2/pytorch/giant/1\"   \nBASE = \"/kaggle/input/csiro-biomass\"                     \nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nBATCH_SIZE = 16\n\nprint(\"Device:\", DEVICE)\n\n# LOAD DINOv2 MODEL\nprint(\"Loading DINOv2 model...\")\nprocessor = AutoImageProcessor.from_pretrained(DINO_MODEL_ID, local_files_only=True)\nmodel = AutoModel.from_pretrained(DINO_MODEL_ID, local_files_only=True).to(DEVICE)\nmodel.eval()\n\n# FUNCTION: DINOv2 image embeddings (with de-dup)\ndef get_dino_embeds(unique_image_paths, base_dir, batch_size=16):\n    \"\"\"\n    unique_image_paths: list of unique paths (relative to base_dir)\n    returns: dict {image_path: embedding (np.array)}\n    \"\"\"\n    embed_dict = {}\n    for i in tqdm(range(0, len(unique_image_paths), batch_size),\n                  desc=\"Extracting DINOv2 embeddings\"):\n        batch_paths = unique_image_paths[i:i + batch_size]\n        images = []\n        for p in batch_paths:\n            full_path = os.path.join(base_dir, p)\n            try:\n                img = Image.open(full_path).convert(\"RGB\")\n            except Exception as e:\n                print(f\"Error loading {p}: {e}, using blank image instead.\")\n                img = Image.new(\"RGB\", (224, 224))\n            images.append(img)\n\n        inputs = processor(images=images, return_tensors=\"pt\", padding=True).to(DEVICE)\n\n        with torch.no_grad():\n            outputs = model(**inputs)\n            # DINOv2 in HF exposes pooler_output; if not, use last_hidden_state.mean(dim=1)\n            if hasattr(outputs, \"pooler_output\") and outputs.pooler_output is not None:\n                emb = outputs.pooler_output\n            else:\n                emb = outputs.last_hidden_state.mean(dim=1)\n\n            # L2-normalise embeddings\n            emb = emb / emb.norm(dim=-1, keepdim=True)\n\n        emb_np = emb.cpu().numpy()\n        for path, vec in zip(batch_paths, emb_np):\n            embed_dict[path] = vec\n\n    return embed_dict\n\n# LOAD CSVs\nprint(\"Loading data...\")\ndftr = pd.read_csv(os.path.join(BASE, \"train.csv\"))\ndfts = pd.read_csv(os.path.join(BASE, \"test.csv\"))\n\n\ntrain_image_paths = dftr[\"image_path\"].tolist()\ntest_image_paths = dfts[\"image_path\"].tolist()\n\nprint(f\"Train rows: {len(dftr)}\")\nprint(f\"Test rows : {len(dfts)}\")\n\n# GET UNIQUE IMAGE EMBEDDINGS (train + test)\nall_image_paths = sorted(set(train_image_paths) | set(test_image_paths))\nprint(f\"Unique images (train+test): {len(all_image_paths)}\")\n\nembed_dict = get_dino_embeds(all_image_paths, BASE, batch_size=BATCH_SIZE)\n\nprint(\"\\nBuilding embedding matrices...\")\n\ntrain_embeds = np.vstack([embed_dict[p] for p in train_image_paths])\ntest_embeds = np.vstack([embed_dict[p] for p in test_image_paths])\n\nprint(f\"train_embeds shape: {train_embeds.shape}\")\nprint(f\"test_embeds  shape: {test_embeds.shape}\")\n\n# ENCODE target_name\nprint(\"\\nEncoding target_name...\")\nle = LabelEncoder()\ndftr[\"target_name_enc\"] = le.fit_transform(dftr[\"target_name\"])\ndfts[\"target_name_enc\"] = le.transform(dfts[\"target_name\"])\n\nprint(\"\\nBuilding final feature matrices...\")\nX_train = np.hstack([\n    train_embeds,\n    dftr[[\"target_name_enc\"]].values\n])\nX_test = np.hstack([\n    test_embeds,\n    dfts[[\"target_name_enc\"]].values\n])\ny_train = dftr[\"target\"].values\n\nprint(f\"X_train shape: {X_train.shape}\")\nprint(f\"X_test  shape: {X_test.shape}\")\nprint(f\"y_train shape: {y_train.shape}\")\n\n# TRAIN CATBOOST REGRESSOR\nprint(\"\\nTraining CatBoostRegressor on all training data...\")\n\nmodel_cb = CatBoostRegressor(\n    iterations=8000,          \n    learning_rate=0.03,       \n    depth=8,\n    loss_function=\"RMSE\",\n    task_type=\"GPU\" if DEVICE == \"cuda\" else \"CPU\",\n    verbose=200\n)\n\nmodel_cb.fit(X_train, y_train)\n\n# PREDICT & SAVE SUBMISSION\nprint(\"\\nGenerating predictions on test set...\")\npreds = model_cb.predict(X_test)\n\n# Biomass cannot be negative\npreds = np.clip(preds, 0.0, None)\n\nsub = pd.DataFrame({\n    \"sample_id\": dfts[\"sample_id\"],\n    \"target\": preds\n})\nsub.to_csv(\"submission.csv\", index=False)\n\nprint(f\"Prediction range: [{preds.min():.4f}, {preds.max():.4f}]\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-14T12:34:06.999531Z","iopub.execute_input":"2025-11-14T12:34:06.999745Z","iopub.status.idle":"2025-11-14T12:56:45.076165Z","shell.execute_reply.started":"2025-11-14T12:34:06.999725Z","shell.execute_reply":"2025-11-14T12:56:45.075362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}