{"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":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":14352836,"sourceType":"datasetVersion","datasetId":9164678},{"sourceId":4537,"sourceType":"modelInstanceVersion","modelInstanceId":3329,"modelId":986},{"sourceId":575836,"sourceType":"modelInstanceVersion","modelInstanceId":430757,"modelId":447692},{"sourceId":704554,"sourceType":"modelInstanceVersion","modelInstanceId":534786,"modelId":548410},{"sourceId":704558,"sourceType":"modelInstanceVersion","modelInstanceId":534789,"modelId":548412},{"sourceId":704735,"sourceType":"modelInstanceVersion","modelInstanceId":534943,"modelId":548558},{"sourceId":705850,"sourceType":"modelInstanceVersion","modelInstanceId":535866,"modelId":549376},{"sourceId":705900,"sourceType":"modelInstanceVersion","modelInstanceId":535905,"modelId":549413},{"sourceId":705930,"sourceType":"modelInstanceVersion","modelInstanceId":535924,"modelId":549429},{"sourceId":706249,"sourceType":"modelInstanceVersion","modelInstanceId":536183,"modelId":549662},{"sourceId":706842,"sourceType":"modelInstanceVersion","modelInstanceId":536702,"modelId":550148},{"sourceId":707042,"sourceType":"modelInstanceVersion","modelInstanceId":536854,"modelId":550288},{"sourceId":710513,"sourceType":"modelInstanceVersion","modelInstanceId":539775,"modelId":552977},{"sourceId":713287,"sourceType":"modelInstanceVersion","modelInstanceId":541993,"modelId":555185},{"sourceId":713584,"sourceType":"modelInstanceVersion","modelInstanceId":542234,"modelId":555434},{"sourceId":714135,"sourceType":"modelInstanceVersion","modelInstanceId":542688,"modelId":555874},{"sourceId":714288,"sourceType":"modelInstanceVersion","modelInstanceId":542810,"modelId":555987},{"sourceId":714928,"sourceType":"modelInstanceVersion","modelInstanceId":543349,"modelId":556506},{"sourceId":717008,"sourceType":"modelInstanceVersion","modelInstanceId":545097,"modelId":558130},{"sourceId":717126,"sourceType":"modelInstanceVersion","modelInstanceId":545200,"modelId":558226},{"sourceId":717769,"sourceType":"modelInstanceVersion","modelInstanceId":545729,"modelId":558741},{"sourceId":718964,"sourceType":"modelInstanceVersion","modelInstanceId":546734,"modelId":559638},{"sourceId":720667,"sourceType":"modelInstanceVersion","modelInstanceId":548149,"modelId":560960},{"sourceId":721045,"sourceType":"modelInstanceVersion","modelInstanceId":548465,"modelId":561210},{"sourceId":722571,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":549749,"modelId":562420}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:29:51.481953Z","iopub.execute_input":"2026-01-15T14:29:51.482350Z","iopub.status.idle":"2026-01-15T14:29:53.623143Z","shell.execute_reply.started":"2026-01-15T14:29:51.482307Z","shell.execute_reply":"2026-01-15T14:29:53.621823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models, transforms\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\nimport xgboost as xgb\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:29:53.625604Z","iopub.execute_input":"2026-01-15T14:29:53.626120Z","iopub.status.idle":"2026-01-15T14:30:07.128711Z","shell.execute_reply.started":"2026-01-15T14:29:53.626072Z","shell.execute_reply":"2026-01-15T14:30:07.126871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# IMG_SIZE = 224\n# BATCH_SIZE = 32\n# EMB_DIM = 2048   # ResNet50\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.130886Z","iopub.execute_input":"2026-01-15T14:30:07.131412Z","iopub.status.idle":"2026-01-15T14:30:07.138319Z","shell.execute_reply.started":"2026-01-15T14:30:07.131380Z","shell.execute_reply":"2026-01-15T14:30:07.137136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# class ImageDataset(Dataset):\n#     def __init__(self, df, img_dir, target_col=None, transform=None, task=\"regression\"):\n#         self.df = df.reset_index(drop=True)\n#         self.img_dir = img_dir\n#         self.target_col = target_col\n#         self.transform = transform\n#         self.task = task\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_dir, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n\n#         if self.transform:\n#             image = self.transform(image)\n\n#         if self.target_col is None:\n#             return image\n\n#         target = self.df.loc[idx, self.target_col]\n\n#         if self.task == \"classification\":\n#             target = torch.tensor(target, dtype=torch.long)\n#         else:\n#             target = torch.tensor(target, dtype=torch.float32)\n\n#         return image, target\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.139562Z","iopub.execute_input":"2026-01-15T14:30:07.139904Z","iopub.status.idle":"2026-01-15T14:30:07.173889Z","shell.execute_reply.started":"2026-01-15T14:30:07.139875Z","shell.execute_reply":"2026-01-15T14:30:07.172354Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(mean=[0.485, 0.456, 0.406],\n#                          std=[0.229, 0.224, 0.225])\n# ])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.175153Z","iopub.execute_input":"2026-01-15T14:30:07.175667Z","iopub.status.idle":"2026-01-15T14:30:07.204382Z","shell.execute_reply.started":"2026-01-15T14:30:07.175535Z","shell.execute_reply":"2026-01-15T14:30:07.203161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.205733Z","iopub.execute_input":"2026-01-15T14:30:07.206136Z","iopub.status.idle":"2026-01-15T14:30:07.225898Z","shell.execute_reply.started":"2026-01-15T14:30:07.206093Z","shell.execute_reply":"2026-01-15T14:30:07.224512Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# class ClassifierHead(nn.Module):\n#     def __init__(self, in_dim, num_classes):\n#         super().__init__()\n#         self.fc = nn.Linear(in_dim, num_classes)\n\n#     def forward(self, x):\n#         return self.fc(x)\n\n\n# class RegressionHead(nn.Module):\n#     def __init__(self, in_dim):\n#         super().__init__()\n#         self.fc = nn.Linear(in_dim, 1)\n\n#     def forward(self, x):\n#         return self.fc(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.228656Z","iopub.execute_input":"2026-01-15T14:30:07.228958Z","iopub.status.idle":"2026-01-15T14:30:07.255606Z","shell.execute_reply.started":"2026-01-15T14:30:07.228932Z","shell.execute_reply":"2026-01-15T14:30:07.254225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def train_model(backbone, head, loader, criterion, optimizer, epochs=5):\n#     backbone.train()\n#     head.train()\n\n#     for epoch in range(epochs):\n#         total_loss = 0\n#         for x, y in loader:\n#             x, y = x.to(DEVICE), y.to(DEVICE)\n\n#             optimizer.zero_grad()\n#             feats = backbone(x)\n#             out = head(feats).squeeze()\n#             loss = criterion(out, y)\n#             loss.backward()\n#             optimizer.step()\n\n#             total_loss += loss.item()\n\n#         print(f\"Epoch {epoch+1}: Loss {total_loss / len(loader):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.257096Z","iopub.execute_input":"2026-01-15T14:30:07.257654Z","iopub.status.idle":"2026-01-15T14:30:07.284120Z","shell.execute_reply.started":"2026-01-15T14:30:07.257612Z","shell.execute_reply":"2026-01-15T14:30:07.282143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n# sub_df = train_df.iloc[::5].reset_index(drop=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.285898Z","iopub.execute_input":"2026-01-15T14:30:07.286419Z","iopub.status.idle":"2026-01-15T14:30:07.326298Z","shell.execute_reply.started":"2026-01-15T14:30:07.286384Z","shell.execute_reply":"2026-01-15T14:30:07.323771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# le_species = LabelEncoder()\n# sub_df[\"species_enc\"] = le_species.fit_transform(sub_df[\"Species\"])\n\n# dataset = ImageDataset(sub_df, \"/kaggle/input/csiro-biomass/\", \"species_enc\", transform, 'classification')\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)\n\n# backbone1 = ResNetBackbone().to(DEVICE)\n# head = ClassifierHead(EMB_DIM, sub_df[\"species_enc\"].nunique()).to(DEVICE)\n\n# criterion = nn.CrossEntropyLoss()\n# optimizer = torch.optim.Adam(list(backbone1.parameters()) + list(head.parameters()), lr=1e-4)\n\n# train_model(backbone1, head, loader, criterion, optimizer)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.328947Z","iopub.execute_input":"2026-01-15T14:30:07.329898Z","iopub.status.idle":"2026-01-15T14:30:07.357936Z","shell.execute_reply.started":"2026-01-15T14:30:07.329859Z","shell.execute_reply":"2026-01-15T14:30:07.355903Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# dataset = ImageDataset(sub_df, \"/kaggle/input/csiro-biomass/\", \"Pre_GSHH_NDVI\", transform)\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)\n\n# reg_head = RegressionHead(EMB_DIM).to(DEVICE)\n# criterion = nn.MSELoss()\n# optimizer = torch.optim.Adam(reg_head.parameters(), lr=1e-4)\n# backbone2 = ResNetBackbone().to(DEVICE)\n# train_model(backbone2, reg_head, loader, criterion, optimizer)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.361247Z","iopub.execute_input":"2026-01-15T14:30:07.361704Z","iopub.status.idle":"2026-01-15T14:30:07.389235Z","shell.execute_reply.started":"2026-01-15T14:30:07.361670Z","shell.execute_reply":"2026-01-15T14:30:07.387683Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# dataset = ImageDataset(sub_df, \"/kaggle/input/csiro-biomass/\", \"Height_Ave_cm\", transform)\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)\n\n# reg_head = RegressionHead(EMB_DIM).to(DEVICE)\n# criterion = nn.MSELoss()\n# optimizer = torch.optim.Adam(reg_head.parameters(), lr=1e-4)\n# backbone3 = ResNetBackbone().to(DEVICE)\n# train_model(backbone3, reg_head, loader, criterion, optimizer)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.390833Z","iopub.execute_input":"2026-01-15T14:30:07.391433Z","iopub.status.idle":"2026-01-15T14:30:07.416664Z","shell.execute_reply.started":"2026-01-15T14:30:07.391388Z","shell.execute_reply":"2026-01-15T14:30:07.415503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# le_state = LabelEncoder()\n# sub_df[\"state_enc\"] = le_state.fit_transform(sub_df[\"State\"])\n\n# dataset = ImageDataset(\n#     sub_df,\n#     \"/kaggle/input/csiro-biomass/\",\n#     \"state_enc\",\n#     transform,\n#     task=\"classification\"\n# )\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)\n# backbone4 = ResNetBackbone().to(DEVICE)\n\n# head = ClassifierHead(EMB_DIM, sub_df[\"state_enc\"].nunique()).to(DEVICE)\n\n# criterion = nn.CrossEntropyLoss()\n# optimizer = torch.optim.Adam(\n#     list(backbone4.parameters()) + list(head.parameters()),\n#     lr=1e-4\n# )\n\n# train_model(backbone4, head, loader, criterion, optimizer)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.417906Z","iopub.execute_input":"2026-01-15T14:30:07.418239Z","iopub.status.idle":"2026-01-15T14:30:07.447853Z","shell.execute_reply.started":"2026-01-15T14:30:07.418210Z","shell.execute_reply":"2026-01-15T14:30:07.446107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_embeddings(backbone, df, img_dir):\n    backbone.eval()\n    dataset = ImageDataset(df, img_dir, transform=transform)\n    loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False)\n\n    embeddings = []\n    with torch.no_grad():\n        for x in loader:\n            x = x.to(DEVICE)\n            feats = backbone(x).cpu().numpy()\n            embeddings.append(feats)\n\n    return np.vstack(embeddings)\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.449493Z","iopub.execute_input":"2026-01-15T14:30:07.450220Z","iopub.status.idle":"2026-01-15T14:30:07.475682Z","shell.execute_reply.started":"2026-01-15T14:30:07.450184Z","shell.execute_reply":"2026-01-15T14:30:07.474146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# torch.save(backbone, \"backbone_full.pt\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.477127Z","iopub.execute_input":"2026-01-15T14:30:07.477471Z","iopub.status.idle":"2026-01-15T14:30:07.502518Z","shell.execute_reply.started":"2026-01-15T14:30:07.477442Z","shell.execute_reply":"2026-01-15T14:30:07.501087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# from sklearn.model_selection import train_test_split\n# from sklearn.metrics import mean_squared_error\n\n# le_target = LabelEncoder()\n# train_df[\"target_enc\"] = le_target.fit_transform(train_df[\"target_name\"])\n\n# e1 = extract_embeddings(backbone1, train_df, \"/kaggle/input/csiro-biomass/\")\n# e2 = extract_embeddings(backbone2, train_df, \"/kaggle/input/csiro-biomass/\")\n# e3 = extract_embeddings(backbone3, train_df, \"/kaggle/input/csiro-biomass/\")\n# e4 = extract_embeddings(backbone4, train_df, \"/kaggle/input/csiro-biomass/\")\n\n\n# X = np.hstack([\n#     e1,e2,e3,e4,\n#     train_df[[\"target_enc\"]].values\n# ])\n# y = train_df[\"target\"].values\n\n# X_train, X_val, y_train, y_val = train_test_split(\n#     X, y, test_size=0.2, random_state=42\n# )\n\n# xgb_model = xgb.XGBRegressor(\n#     n_estimators=500,\n#     max_depth=8,\n#     learning_rate=0.05,\n#     subsample=0.8,\n#     colsample_bytree=0.8,\n#     objective=\"reg:squarederror\",\n#     random_state=42\n# )\n\n# xgb_model.fit(\n#     X_train,\n#     y_train,\n#     eval_set=[(X_val, y_val)],\n#     verbose=True\n# )\n\n# xgb_model.save_model(\"xgb_model.json\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.504388Z","iopub.execute_input":"2026-01-15T14:30:07.504823Z","iopub.status.idle":"2026-01-15T14:30:07.536442Z","shell.execute_reply.started":"2026-01-15T14:30:07.504780Z","shell.execute_reply":"2026-01-15T14:30:07.535313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =========================\n# # 1. Imports\n# # =========================\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n# import xgboost as xgb\n# import joblib\n\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# from sklearn.preprocessing import LabelEncoder\n\n# import torch\n# import torch.nn as nn\n# from torchvision import models\n\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         model = models.resnet50(pretrained=False)\n#         model.fc = nn.Identity()\n#         self.model = model\n\n#     def forward(self, x):\n#         return self.model(x)\n\n\n# # =========================\n# # 2. Config\n# # =========================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n\n# # =========================\n# # 3. Load CNN Model (.pt)\n# # =========================\n# cnn_model = torch.load(\"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\", map_location=DEVICE, weights_only= False)\n# cnn_model.eval()\n\n# # =========================\n# # 4. Image Dataset (Inference)\n# # =========================\n# class InferenceDataset(Dataset):\n#     def __init__(self, df, img_root, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         return self.transform(image)\n\n# # =========================\n# # 5. Image Transforms\n# # =========================\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # =========================\n# # 6. Load CSVs\n# # =========================\n# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")   # used ONLY to rebuild encoder\n# test_df = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # =========================\n# # 7. Rebuild LabelEncoder (CRITICAL)\n# # =========================\n# le_target = LabelEncoder()\n# le_target.fit(train_df[\"target_name\"])\n\n# # Safe transform (no unseen labels assumed)\n# test_df[\"target_enc\"] = le_target.transform(test_df[\"target_name\"])\n\n# # =========================\n# # 8. Extract Embeddings\n# # =========================\n# dataset = InferenceDataset(test_df, IMG_ROOT, transform)\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False)\n\n# embeddings = []\n\n# with torch.no_grad():\n#     for images in loader:\n#         images = images.to(DEVICE)\n#         emb = cnn_model(images)\n#         embeddings.append(emb.cpu().numpy())\n\n# test_embeddings = np.vstack(embeddings)\n\n# # =========================\n# # 9. Load XGBoost Model\n# # =========================\n# xgb_model = xgb.XGBRegressor()\n# xgb_model.load_model(\"/kaggle/input/xgboost/scikitlearn/default/1/xgb_model.json\")\n\n# # =========================\n# # 10. Predict\n# # =========================\n# X_test = np.hstack([\n#     test_embeddings,\n#     test_df[[\"target_enc\"]].values\n# ])\n\n# test_preds = xgb_model.predict(X_test)\n\n# # =========================\n# # 11. Create Submission\n# # =========================\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": test_preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"✅ submission.csv created successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.538451Z","iopub.execute_input":"2026-01-15T14:30:07.539286Z","iopub.status.idle":"2026-01-15T14:30:07.580271Z","shell.execute_reply.started":"2026-01-15T14:30:07.539228Z","shell.execute_reply":"2026-01-15T14:30:07.577906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # CNN EMBEDDINGS + MLP REGRESSOR (TRAIN + INFERENCE)\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# from sklearn.preprocessing import LabelEncoder\n\n# import torch.nn as nn\n# import torch.optim as optim\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n# EPOCHS = 15\n# LR = 1e-3\n\n# torch.backends.cudnn.benchmark = True\n\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         model = models.resnet50(pretrained=False)\n#         model.fc = nn.Identity()\n#         self.model = model\n\n#     def forward(self, x):\n#         return self.model(x)\n\n\n# # ============================================================\n# # LOAD CNN BACKBONE (FROZEN)\n# # ============================================================\n\n# cnn_model = torch.load(\n#     \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n# cnn_model = cnn_model.to(DEVICE)\n# cnn_model.eval()\n\n# for p in cnn_model.parameters():\n#     p.requires_grad = False\n\n# EMB_DIM = 2048  # resnet50 output\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n\n# class RegressionDataset(Dataset):\n#     def __init__(self, df, img_root, transform, le):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n#         self.le = le\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         target_enc = self.le.transform([self.df.loc[idx, \"target_name\"]])[0]\n#         target_val = self.df.loc[idx, \"target\"] if \"target\" in self.df else -1\n\n#         return image, target_enc, target_val\n\n# # ============================================================\n# # LOAD DATA\n# # ============================================================\n\n# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n# test_df  = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # LabelEncoder (CRITICAL)\n# le_target = LabelEncoder()\n# le_target.fit(train_df[\"target_name\"])\n\n# train_ds = RegressionDataset(train_df, IMG_ROOT, transform, le_target)\n# test_ds  = RegressionDataset(test_df, IMG_ROOT, transform, le_target)\n\n# train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)\n# test_loader  = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n\n# # ============================================================\n# # MLP REGRESSOR\n# # ============================================================\n\n# class MLPRegressor(nn.Module):\n#     def __init__(self, emb_dim, num_targets):\n#         super().__init__()\n#         self.net = nn.Sequential(\n#             nn.Linear(emb_dim + 1, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.3),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, emb, target_enc):\n#         x = torch.cat([emb, target_enc.unsqueeze(1)], dim=1)\n#         return self.net(x).squeeze(1)\n\n# mlp = MLPRegressor(\n#     emb_dim=EMB_DIM,\n#     num_targets=len(le_target.classes_)\n# ).to(DEVICE)\n\n# # ============================================================\n# # TRAINING SETUP\n# # ============================================================\n\n# criterion = nn.MSELoss()\n# optimizer = optim.AdamW(mlp.parameters(), lr=LR)\n\n# # ============================================================\n# # TRAIN LOOP\n# # ============================================================\n\n# mlp.train()\n\n# for epoch in range(EPOCHS):\n#     total_loss = 0.0\n\n#     for images, target_enc, target in train_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_enc = target_enc.to(DEVICE)\n#         target = target.float().to(DEVICE)\n\n#         with torch.no_grad():\n#             emb = cnn_model(images)\n\n#         preds = mlp(emb, target_enc)\n\n#         loss = criterion(preds, target)\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total_loss += loss.item()\n\n#     print(f\"Epoch [{epoch+1}/{EPOCHS}] | Loss: {total_loss/len(train_loader):.4f}\")\n\n# torch.save(mlp.state_dict(), \"mlp_regressor.pt\")\n# print(\"✅ MLP training complete\")\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# mlp.eval()\n# preds = []\n\n# with torch.no_grad():\n#     for images, target_enc, _ in test_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_enc = target_enc.to(DEVICE)\n\n#         emb = cnn_model(images)\n#         out = mlp(emb, target_enc)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv created\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.581916Z","iopub.execute_input":"2026-01-15T14:30:07.582368Z","iopub.status.idle":"2026-01-15T14:30:07.620165Z","shell.execute_reply.started":"2026-01-15T14:30:07.582326Z","shell.execute_reply":"2026-01-15T14:30:07.618598Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # CNN EMBEDDINGS + MLP REGRESSOR (TRAIN + INFERENCE)\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# from sklearn.preprocessing import LabelEncoder\n\n# import torch.nn as nn\n# import torch.optim as optim\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n# EPOCHS = 15\n# LR = 1e-3\n\n# torch.backends.cudnn.benchmark = True\n\n# # ============================================================\n# # LOAD CNN BACKBONE (FROZEN)\n# # ============================================================\n\n# cnn_model = torch.load(\n#     \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n# cnn_model = cnn_model.to(DEVICE)\n# cnn_model.eval()\n\n# for p in cnn_model.parameters():\n#     p.requires_grad = False\n\n# EMB_DIM = 2048  # resnet50 output\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n\n# class RegressionDataset(Dataset):\n#     def __init__(self, df, img_root, transform, le):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n#         self.le = le\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         target_enc = self.le.transform([self.df.loc[idx, \"target_name\"]])[0]\n#         target_val = self.df.loc[idx, \"target\"] if \"target\" in self.df else -1\n\n#         return image, target_enc, target_val\n\n# # ============================================================\n# # LOAD DATA\n# # ============================================================\n\n# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n# test_df  = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # LabelEncoder (CRITICAL)\n# le_target = LabelEncoder()\n# le_target.fit(train_df[\"target_name\"])\n\n# train_ds = RegressionDataset(train_df, IMG_ROOT, transform, le_target)\n# test_ds  = RegressionDataset(test_df, IMG_ROOT, transform, le_target)\n\n# train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)\n# test_loader  = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n\n# # ============================================================\n# # MLP REGRESSOR\n# # ============================================================\n\n# class MLPRegressor(nn.Module):\n#     def __init__(self, emb_dim, num_targets):\n#         super().__init__()\n#         self.net = nn.Sequential(\n#             nn.Linear(emb_dim + 1, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.3),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, emb, target_enc):\n#         x = torch.cat([emb, target_enc.unsqueeze(1)], dim=1)\n#         return self.net(x).squeeze(1)\n\n# mlp = MLPRegressor(\n#     emb_dim=EMB_DIM,\n#     num_targets=len(le_target.classes_)\n# ).to(DEVICE)\n\n# # ============================================================\n# # TRAINING SETUP\n# # ============================================================\n\n# criterion = nn.MSELoss()\n# optimizer = optim.AdamW(mlp.parameters(), lr=LR)\n\n# # ============================================================\n# # TRAIN LOOP\n# # ============================================================\n\n# mlp.train()\n\n# for epoch in range(EPOCHS):\n#     total_loss = 0.0\n\n#     for images, target_enc, target in train_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_enc = target_enc.to(DEVICE)\n#         target = target.float().to(DEVICE)\n\n#         with torch.no_grad():\n#             emb = cnn_model(images)\n\n#         preds = mlp(emb, target_enc)\n\n#         loss = criterion(preds, target)\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total_loss += loss.item()\n\n#     print(f\"Epoch [{epoch+1}/{EPOCHS}] | Loss: {total_loss/len(train_loader):.4f}\")\n\n# torch.save(mlp.state_dict(), \"mlp_regressor.pt\")\n# print(\"✅ MLP training complete\")\n\n# checkpoint = {\n#     \"epoch\": epoch + 1,               \n#     \"model_state_dict\": mlp.state_dict(),\n#     \"optimizer_state_dict\": optimizer.state_dict(),\n#     \"loss\": total_loss / len(train_loader),\n# }\n\n# torch.save(checkpoint, \"mlp_checkpoint.pt\")\n# print(\"✅ Checkpoint saved\")\n\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# mlp.eval()\n# preds = []\n\n# with torch.no_grad():\n#     for images, target_enc, _ in test_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_enc = target_enc.to(DEVICE)\n\n#         emb = cnn_model(images)\n#         out = mlp(emb, target_enc)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv created\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.621940Z","iopub.execute_input":"2026-01-15T14:30:07.623431Z","iopub.status.idle":"2026-01-15T14:30:07.657178Z","shell.execute_reply.started":"2026-01-15T14:30:07.623253Z","shell.execute_reply":"2026-01-15T14:30:07.655855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # INFERENCE ONLY — CNN EMBEDDINGS + MLP REGRESSOR\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# from sklearn.preprocessing import LabelEncoder\n\n# import torch.nn as nn\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n\n# # ============================================================\n# # LOAD CNN BACKBONE (FROZEN)\n# # ============================================================\n\n# cnn_model = torch.load(\n#     \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n# cnn_model = cnn_model.to(DEVICE)\n# cnn_model.eval()\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n\n# class InferenceDataset(Dataset):\n#     def __init__(self, df, img_root, transform, le):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n#         self.le = le\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         target_enc = self.le.transform(\n#             [self.df.loc[idx, \"target_name\"]]\n#         )[0]\n\n#         return image, target_enc\n\n\n# class RegressionDataset(Dataset):\n#     def __init__(self, df, img_root, transform, le):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n#         self.le = le\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         target_enc = self.le.transform([self.df.loc[idx, \"target_name\"]])[0]\n#         target_val = self.df.loc[idx, \"target\"] if \"target\" in self.df else -1\n\n#         return image, target_enc, target_val\n\n# # ============================================================\n# # LOAD CSVs\n# # ============================================================\n\n# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n# test_df  = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # ============================================================\n# # REBUILD LABEL ENCODER (CRITICAL)\n# # ============================================================\n\n# le_target = LabelEncoder()\n# le_target.fit(train_df[\"target_name\"])\n\n# # ============================================================\n# # DATALOADER\n# # ============================================================\n\n# test_ds = InferenceDataset(test_df, IMG_ROOT, transform, le_target)\n\n# test_loader = DataLoader(\n#     test_ds,\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # ============================================================\n# # MLP REGRESSOR DEFINITION (MUST MATCH TRAINING)\n# # ============================================================\n\n# class MLPRegressor(nn.Module):\n#     def __init__(self, emb_dim):\n#         super().__init__()\n#         self.net = nn.Sequential(\n#             nn.Linear(emb_dim + 1, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.3),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, emb, target_enc):\n#         x = torch.cat([emb, target_enc.unsqueeze(1)], dim=1)\n#         return self.net(x).squeeze(1)\n\n# EMB_DIM = 2048  # resnet50\n\n# mlp = MLPRegressor(EMB_DIM).to(DEVICE)\n# mlp.load_state_dict(\n#     torch.load(\n#         \"/kaggle/input/mlp/pytorch/default/1/mlp_regressor.pt\",\n#         map_location=DEVICE\n#     )\n# )\n# mlp.eval()\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# preds = []\n\n# with torch.no_grad():\n#     for images, target_enc in test_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_enc = target_enc.to(DEVICE)\n\n#         emb = cnn_model(images)\n#         out = mlp(emb, target_enc)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission2.csv generated successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.658554Z","iopub.execute_input":"2026-01-15T14:30:07.658940Z","iopub.status.idle":"2026-01-15T14:30:07.690153Z","shell.execute_reply.started":"2026-01-15T14:30:07.658901Z","shell.execute_reply":"2026-01-15T14:30:07.689116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # INFERENCE — CNN + TARGET EMBEDDING + MLP\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n\n# import torch.nn as nn\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # LOAD TEST CSV\n# # ============================================================\n\n# test_df = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # ============================================================\n# # LOAD CHECKPOINT\n# # ============================================================\n\n# ckpt = torch.load(\n#     \"/kaggle/input/cnn-mlp/pytorch/default/1/cnn_retrained_with_target_embedding.pt\",\n#     map_location=DEVICE, weights_only = False\n# )\n\n# label_classes = ckpt[\"label_encoder_classes\"]\n\n# # Build mapping: target_name -> index\n# target_to_idx = {name: i for i, name in enumerate(label_classes)}\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n\n# class InferenceDataset(Dataset):\n#     def __init__(self, df, img_root, transform, target_to_idx):\n#         self.df = df.reset_index(drop=True)\n#         self.img_root = img_root\n#         self.transform = transform\n#         self.target_to_idx = target_to_idx\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img_path = os.path.join(self.img_root, self.df.loc[idx, \"image_path\"])\n#         image = Image.open(img_path).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         target_idx = self.target_to_idx[self.df.loc[idx, \"target_name\"]]\n\n#         return image, target_idx\n\n# # ============================================================\n# # MODEL DEFINITION (MUST MATCH TRAINING)\n# # ============================================================\n\n# class CNNWithTargetEmbedding(nn.Module):\n#     def __init__(self, num_targets, target_emb_dim=16):\n#         super().__init__()\n\n#         # CNN backbone\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_emb_dim = 2048\n\n#         # Target embedding\n#         self.target_emb = nn.Embedding(num_targets, target_emb_dim)\n\n#         # Regression head\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_emb_dim + target_emb_dim, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         x = torch.cat([img_emb, tgt_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# # ============================================================\n# # LOAD MODEL\n# # ============================================================\n\n# model = CNNWithTargetEmbedding(\n#     num_targets=len(label_classes),\n#     target_emb_dim=16\n# ).to(DEVICE)\n\n# model.load_state_dict(ckpt[\"model_state_dict\"])\n# model.eval()\n\n# # ============================================================\n# # DATALOADER\n# # ============================================================\n\n# test_loader = DataLoader(\n#     InferenceDataset(test_df, IMG_ROOT, transform, target_to_idx),\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# preds = []\n\n# with torch.no_grad():\n#     for images, target_idx in test_loader:\n#         images = images.to(DEVICE, non_blocking=True)\n#         target_idx = target_idx.to(DEVICE)\n\n#         out = model(images, target_idx)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv generated successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.692191Z","iopub.execute_input":"2026-01-15T14:30:07.692486Z","iopub.status.idle":"2026-01-15T14:30:07.725825Z","shell.execute_reply.started":"2026-01-15T14:30:07.692458Z","shell.execute_reply":"2026-01-15T14:30:07.724751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # ENSEMBLE INFERENCE — XGBOOST + CNN-MLP\n# # ============================================================\n\n# import os\n# import torch\n# import joblib\n# import xgboost as xgb\n# import pandas as pd\n# import numpy as np\n\n# from PIL import Image\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import torch.nn as nn\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n\n# ALPHA = 0.5   # weight for XGBoost (0.5 = equal ensemble)\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # LOAD TEST DATA\n# # ============================================================\n\n# test_df = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# # ============================================================\n# # LOAD CNN BACKBONE (SHARED)\n# # ============================================================\n\n# cnn_backbone = torch.load(\n#     \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n# cnn_backbone.eval()\n\n# # ============================================================\n# # ---------- XGBOOST INFERENCE ----------\n# # ============================================================\n\n# target_groups = joblib.load(\"/kaggle/input/target-grps/target_groups.pkl\")\n\n# xgb_models = {}\n# for g in target_groups:\n#     m = xgb.XGBRegressor()\n#     m.load_model(f\"/kaggle/input/xgb/other/default/1/xgb_{g}.json\")\n#     xgb_models[g] = m\n\n# def embed_single(path):\n#     img = Image.open(path).convert(\"RGB\")\n#     x = transform(img).unsqueeze(0).to(DEVICE)\n#     with torch.no_grad():\n#         return cnn_backbone(x).cpu().numpy()\n\n# xgb_preds = []\n\n# for _, row in test_df.iterrows():\n#     emb = embed_single(os.path.join(IMG_ROOT, row[\"image_path\"]))\n#     pred = xgb_models[row[\"target_name\"]].predict(emb)[0]\n#     xgb_preds.append(pred)\n\n# xgb_preds = np.array(xgb_preds)\n\n# # ============================================================\n# # ---------- DEEP LEARNING INFERENCE ----------\n# # ============================================================\n\n# ckpt = torch.load(\n#     \"/kaggle/input/cnn-mlp/pytorch/default/1/cnn_retrained_with_target_embedding.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n\n# label_classes = ckpt[\"label_encoder_classes\"]\n# target_to_idx = {name: i for i, name in enumerate(label_classes)}\n\n# class InferenceDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img = Image.open(\n#             os.path.join(IMG_ROOT, self.df.loc[idx, \"image_path\"])\n#         ).convert(\"RGB\")\n\n#         img = transform(img)\n#         target_idx = target_to_idx[self.df.loc[idx, \"target_name\"]]\n\n#         return img, target_idx\n\n# class CNNWithTargetEmbedding(nn.Module):\n#     def __init__(self, num_targets, target_emb_dim=16):\n#         super().__init__()\n\n#         # CNN backbone\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_emb_dim = 2048\n\n#         # Target embedding\n#         self.target_emb = nn.Embedding(num_targets, target_emb_dim)\n\n#         # Regression head\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_emb_dim + target_emb_dim, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         x = torch.cat([img_emb, tgt_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# model = CNNWithTargetEmbedding(\n#     num_targets=len(label_classes),\n#     target_emb_dim=16\n# ).to(DEVICE)\n\n# model.load_state_dict(ckpt[\"model_state_dict\"])\n# model.eval()\n\n# loader = DataLoader(\n#     InferenceDataset(test_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=False\n# )\n\n# dl_preds = []\n\n# with torch.no_grad():\n#     for images, target_idx in loader:\n#         images = images.to(DEVICE)\n#         target_idx = target_idx.to(DEVICE)\n#         out = model(images, target_idx)\n#         dl_preds.extend(out.cpu().numpy())\n\n# dl_preds = np.array(dl_preds)\n\n# # ============================================================\n# # ---------- ENSEMBLE ----------\n# # ============================================================\n\n# final_preds = ALPHA * xgb_preds + (1 - ALPHA) * dl_preds\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": final_preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 Ensemble submission.csv generated\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.730360Z","iopub.execute_input":"2026-01-15T14:30:07.730790Z","iopub.status.idle":"2026-01-15T14:30:07.758202Z","shell.execute_reply.started":"2026-01-15T14:30:07.730759Z","shell.execute_reply":"2026-01-15T14:30:07.756518Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # COMPLETE TRAINING + INFERENCE SCRIPT\n# # CNN EMBEDDINGS + ExG + GNDVI → XGBOOST\n# # ============================================================\n\n# import os\n# import torch\n# import joblib\n# import xgboost as xgb\n# import pandas as pd\n# import numpy as np\n\n# from PIL import Image\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import models, transforms\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n\n# BATCH_SIZE = 32\n\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n\n\n# # ============================================================\n# # CNN BACKBONE (EMBEDDING MODEL)\n# # ============================================================\n\n# cnn_model = torch.load(\n#     \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#     map_location=DEVICE,\n#     weights_only=False\n# )\n# cnn_model.eval()\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n\n# class ImgDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         img = Image.open(\n#             os.path.join(IMG_ROOT, self.df.loc[idx, \"image_path\"])\n#         ).convert(\"RGB\")\n#         return transform(img)\n\n# # ============================================================\n# # CNN EMBEDDING EXTRACTION\n# # ============================================================\n\n# def extract_embeddings(df):\n#     loader = DataLoader(\n#         ImgDataset(df),\n#         batch_size=BATCH_SIZE,\n#         shuffle=False\n#     )\n\n#     feats = []\n#     with torch.no_grad():\n#         for x in loader:\n#             x = x.to(DEVICE)\n#             feats.append(cnn_model(x).cpu().numpy())\n\n#     return np.vstack(feats)   # [N, 2048]\n\n# # ============================================================\n# # GREEN FEATURE EXTRACTION\n# # ============================================================\n\n# def compute_green_features(img_path):\n#     img = np.array(Image.open(img_path).convert(\"RGB\")).astype(np.float32)\n\n#     R = img[..., 0]\n#     G = img[..., 1]\n#     B = img[..., 2]\n\n#     # ExG\n#     exg = 2 * G - R - B\n#     exg_mean = exg.mean()\n#     exg_std = exg.std()\n#     exg_top10 = exg[exg >= np.percentile(exg, 90)].mean()\n\n#     # GNDVI proxy\n#     eps = 1e-6\n#     gndvi = (G - R) / (G + R + eps)\n#     gndvi_mean = gndvi.mean()\n\n#     return np.array(\n#         [exg_mean, exg_std, exg_top10, gndvi_mean],\n#         dtype=np.float32\n#     )\n\n# def extract_green_features(df):\n#     feats = []\n#     for p in df[\"image_path\"]:\n#         feats.append(\n#             compute_green_features(os.path.join(IMG_ROOT, p))\n#         )\n#     return np.vstack(feats)   # [N, 4]\n\n# # ============================================================\n# # ===================== TRAINING =============================\n# # ============================================================\n\n# train_df = pd.read_csv(TRAIN_CSV)\n\n# print(\"🔍 Extracting CNN embeddings...\")\n# train_img_emb = extract_embeddings(train_df)\n\n# print(\"🌿 Extracting green features...\")\n# train_green = extract_green_features(train_df)\n\n# # Final feature vector\n# X_full = np.hstack([train_img_emb, train_green])  # [N, 2052]\n\n# # Train separate model per target_name\n# target_groups = train_df[\"target_name\"].unique()\n# joblib.dump(target_groups.tolist(), \"target_groups2.pkl\")\n\n# for g in target_groups:\n#     idx = train_df[\"target_name\"] == g\n\n#     X = X_full[idx]\n#     y = train_df.loc[idx, \"target\"].values\n\n#     model = xgb.XGBRegressor(\n#         n_estimators=600,\n#         max_depth=8,\n#         learning_rate=0.05,\n#         subsample=0.85,\n#         colsample_bytree=0.85,\n#         objective=\"reg:squarederror\",\n#         n_jobs=-1\n#     )\n\n#     model.fit(X, y)\n#     model.save_model(f\"xgb_{g}.json\")\n\n# print(\"✅ XGBoost training completed\")\n\n# # ============================================================\n# # ===================== INFERENCE =============================\n# # ============================================================\n\n# # Load trained models\n# target_groups = joblib.load(\"target_groups2.pkl\")\n\n# xgb_models = {}\n# for g in target_groups:\n#     base_g = g[::-1].replace(\"_\", \"\", 1)[::-1]\n#     m = xgb.XGBRegressor()\n#     m.load_model(f\"/kaggle/input/xgboost-with-color/scikitlearn/default/1/xgb_{base_g}.json\")\n#     xgb_models[g] = m\n\n# def extract_features_single(img_path):\n#     img = Image.open(img_path).convert(\"RGB\")\n#     x = transform(img).unsqueeze(0).to(DEVICE)\n\n#     with torch.no_grad():\n#         cnn_emb = cnn_model(x).cpu().numpy()\n\n#     green_feat = compute_green_features(img_path).reshape(1, -1)\n\n#     return np.hstack([cnn_emb, green_feat])\n\n# test_df = pd.read_csv(TEST_CSV)\n# preds = []\n\n# for _, row in test_df.iterrows():\n#     feat = extract_features_single(\n#         os.path.join(IMG_ROOT, row[\"image_path\"])\n#     )\n\n#     model = xgb_models[row[\"target_name\"]]\n#     preds.append(model.predict(feat)[0])\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv generated successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.759598Z","iopub.execute_input":"2026-01-15T14:30:07.759980Z","iopub.status.idle":"2026-01-15T14:30:07.792393Z","shell.execute_reply.started":"2026-01-15T14:30:07.759938Z","shell.execute_reply":"2026-01-15T14:30:07.790682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # TRAINING + INFERENCE — CNN + TARGET EMB + GREEN EMB (IMAGE)\n# # ============================================================\n\n# import os\n# import torch\n# import joblib\n# import pandas as pd\n# import numpy as np\n# import xgboost as xgb\n\n# from PIL import Image\n# from sklearn.cluster import KMeans\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import torch.nn as nn\n# import torch.optim as optim\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# BATCH_SIZE = 32\n# EPOCHS = 10\n# LR = 1e-4\n# GREEN_BINS = 16\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # GREEN FEATURE EXTRACTION (IMAGE-ONLY)\n# # ============================================================\n\n# def compute_green_features(img):\n#     arr = np.array(img).astype(np.float32) / 255.0\n#     R, G, B = arr[..., 0], arr[..., 1], arr[..., 2]\n#     eps = 1e-6\n\n#     green_ratio = G / (R + G + B + eps)\n#     exg = 2 * G - R - B\n#     gr_ratio = G / (R + eps)\n\n#     return np.array([\n#         green_ratio.mean(),\n#         exg.mean(),\n#         gr_ratio.mean()\n#     ], dtype=np.float32)\n\n# # ============================================================\n# # BUILD GREEN BINS (TRAIN SET ONLY)\n# # ============================================================\n\n# def build_green_kmeans(df):\n#     feats = []\n#     for p in df[\"image_path\"]:\n#         img = Image.open(os.path.join(IMG_ROOT, p)).convert(\"RGB\")\n#         feats.append(compute_green_features(img))\n#     feats = np.stack(feats)\n#     km = KMeans(n_clusters=GREEN_BINS, random_state=42)\n#     km.fit(feats)\n#     return km\n\n# def green_to_bin(img, kmeans):\n#     feat = compute_green_features(img)\n#     return int(kmeans.predict(feat[None])[0])\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n\n\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, target_to_idx, green_kmeans):\n#         self.df = df.reset_index(drop=True)\n#         self.target_to_idx = target_to_idx\n#         self.green_kmeans = green_kmeans\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         row = self.df.loc[idx]\n\n#         img = Image.open(\n#             os.path.join(IMG_ROOT, row[\"image_path\"])\n#         ).convert(\"RGB\")\n\n#         green_idx = green_to_bin(img, self.green_kmeans)\n#         img = transform(img)\n\n#         target_idx = self.target_to_idx[row[\"target_name\"]]\n#         y = row[\"target\"]\n\n#         return img, target_idx, green_idx, torch.tensor(y, dtype=torch.float32)\n\n# # ============================================================\n# # MODEL\n# # ============================================================\n\n# class CNNWithTargetAndGreenEmbedding(nn.Module):\n#     def __init__(self, num_targets, num_green_bins):\n#         super().__init__()\n\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_dim = 2048\n\n#         self.target_emb = nn.Embedding(num_targets, 16)\n#         self.green_emb = nn.Embedding(num_green_bins, 8)\n\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_dim + 16 + 8, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx, green_idx):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         grn_emb = self.green_emb(green_idx)\n#         x = torch.cat([img_emb, tgt_emb, grn_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# # ============================================================\n# # TRAINING\n# # ============================================================\n\n# train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n\n# target_classes = sorted(train_df[\"target_name\"].unique())\n# target_to_idx = {t: i for i, t in enumerate(target_classes)}\n\n# green_kmeans = build_green_kmeans(train_df)\n\n# train_ds = BiomassDataset(train_df, target_to_idx, green_kmeans)\n# train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n\n# model = CNNWithTargetAndGreenEmbedding(\n#     num_targets=len(target_classes),\n#     num_green_bins=GREEN_BINS\n# ).to(DEVICE)\n\n# criterion = nn.MSELoss()\n# optimizer = optim.AdamW(model.parameters(), lr=LR)\n\n# model.train()\n# for epoch in range(EPOCHS):\n#     total_loss = 0\n#     for imgs, tgt_idx, grn_idx, y in train_loader:\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         grn_idx = grn_idx.to(DEVICE)\n#         y = y.to(DEVICE)\n\n#         preds = model(imgs, tgt_idx, grn_idx)\n#         loss = criterion(preds, y)\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total_loss += loss.item()\n\n#     print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {total_loss/len(train_loader):.4f}\")\n\n# # ============================================================\n# # SAVE\n# # ============================================================\n\n# torch.save(\n#     {\n#         \"model_state_dict\": model.state_dict(),\n#         \"label_encoder_classes\": target_classes,\n#         \"green_kmeans\": green_kmeans\n#     },\n#     \"cnn_with_target_and_green_embedding.pt\"\n# )\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# test_df = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n\n# ckpt = torch.load(\"cnn_with_target_and_green_embedding.pt\", map_location=DEVICE)\n# green_kmeans = ckpt[\"green_kmeans\"]\n\n# model.load_state_dict(ckpt[\"model_state_dict\"])\n# model.eval()\n\n# class TestDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         row = self.df.loc[idx]\n\n#         img = Image.open(\n#             os.path.join(IMG_ROOT, row[\"image_path\"])\n#         ).convert(\"RGB\")\n\n#         green_idx = green_to_bin(img, green_kmeans)\n#         img = transform(img)\n#         tgt_idx = target_to_idx[row[\"target_name\"]]\n\n#         return img, tgt_idx, green_idx\n\n# test_loader = DataLoader(TestDataset(test_df), batch_size=BATCH_SIZE)\n\n# preds = []\n# with torch.no_grad():\n#     for imgs, tgt_idx, grn_idx in test_loader:\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         grn_idx = grn_idx.to(DEVICE)\n#         out = model(imgs, tgt_idx, grn_idx)\n#         preds.extend(out.cpu().numpy())\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# # })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv generated\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.793919Z","iopub.execute_input":"2026-01-15T14:30:07.794359Z","iopub.status.idle":"2026-01-15T14:30:07.833758Z","shell.execute_reply.started":"2026-01-15T14:30:07.794317Z","shell.execute_reply":"2026-01-15T14:30:07.832228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # CNN + TARGET EMBEDDING + GREEN FEATURE EMBEDDING\n# # TRAINING + INFERENCE (NO CLUSTERING)\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from PIL import Image\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import torch.nn as nn\n# from tqdm import tqdm\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n\n# BATCH_SIZE = 32\n# EPOCHS = 50\n# LR = 3e-4\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # GREEN FEATURE EXTRACTION (CONTINUOUS)\n# # ============================================================\n\n# def extract_green_features(img: Image.Image):\n#     arr = np.asarray(img).astype(np.float32) / 255.0\n#     R, G, B = arr[..., 0], arr[..., 1], arr[..., 2]\n\n#     eps = 1e-6\n#     g_ratio  = np.mean(G / (R + G + B + eps))\n#     exg      = np.mean(2 * G - R - B)\n#     gr_ratio = np.mean(G / (R + eps))\n\n#     return np.array([g_ratio, exg, gr_ratio], dtype=np.float32)\n\n# # ============================================================\n# # DATASETS\n# # ============================================================\n\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, training=True):\n#         self.df = df.reset_index(drop=True)\n#         self.training = training\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         row = self.df.loc[idx]\n\n#         img_path = os.path.join(IMG_ROOT, row[\"image_path\"])\n#         img = Image.open(img_path).convert(\"RGB\")\n\n#         green_feat = extract_green_features(img)\n#         img = transform(img)\n\n#         target_idx = target_to_idx[row[\"target_name\"]]\n\n#         if self.training:\n#             return img, target_idx, green_feat, row[\"target\"]\n#         else:\n#             return img, target_idx, green_feat\n\n# # ============================================================\n# # MODEL\n# # ============================================================\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n\n\n# class CNNWithTargetAndGreenEmbedding(nn.Module):\n#     def __init__(self, num_targets, target_emb_dim=16, green_emb_dim=8):\n#         super().__init__()\n\n#         # CNN backbone (TRAINABLE)\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_emb_dim = 2048\n\n#         # Target embedding\n#         self.target_emb = nn.Embedding(num_targets, target_emb_dim)\n\n#         # Green feature embedding (MLP, not clustering)\n#         self.green_mlp = nn.Sequential(\n#             nn.Linear(3, 32),\n#             nn.ReLU(),\n#             nn.Linear(32, green_emb_dim)\n#         )\n\n#         # Regression head\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_emb_dim + target_emb_dim + green_emb_dim, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx, green_feat):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         green_emb = self.green_mlp(green_feat)\n\n#         x = torch.cat([img_emb, tgt_emb, green_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# # ============================================================\n# # LOAD DATA\n# # ============================================================\n\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# label_classes = sorted(train_df[\"target_name\"].unique())\n# target_to_idx = {n: i for i, n in enumerate(label_classes)}\n\n# train_ds = BiomassDataset(train_df, training=True)\n# test_ds  = BiomassDataset(test_df, training=False)\n\n# train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n# test_loader  = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)\n\n# # ============================================================\n# # TRAINING\n# # ============================================================\n\n# model = CNNWithTargetAndGreenEmbedding(\n#     num_targets=len(label_classes)\n# ).to(DEVICE)\n\n# optimizer = torch.optim.AdamW(model.parameters(), lr=LR)\n# criterion = nn.MSELoss()\n\n# model.train()\n\n# for epoch in range(EPOCHS):\n#     total_loss = 0\n\n#     for imgs, tgt_idx, green_feat, y in tqdm(train_loader):\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         green_feat = green_feat.to(DEVICE)\n#         y = y.to(DEVICE).float()\n\n#         optimizer.zero_grad()\n#         preds = model(imgs, tgt_idx, green_feat)\n#         loss = criterion(preds, y)\n#         loss.backward()\n#         optimizer.step()\n\n#         total_loss += loss.item()\n\n#     print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {total_loss / len(train_loader):.4f}\")\n\n# # ============================================================\n# # SAVE CHECKPOINT\n# # ============================================================\n\n# torch.save({\n#     \"model_state_dict\": model.state_dict(),\n#     \"label_encoder_classes\": label_classes\n# }, \"cnn_green_target_model.pt\")\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# model.eval()\n# preds = []\n\n# with torch.no_grad():\n#     for imgs, tgt_idx, green_feat in test_loader:\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         green_feat = green_feat.to(DEVICE)\n\n#         out = model(imgs, tgt_idx, green_feat)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv generated\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.835255Z","iopub.execute_input":"2026-01-15T14:30:07.835900Z","iopub.status.idle":"2026-01-15T14:30:07.877783Z","shell.execute_reply.started":"2026-01-15T14:30:07.835864Z","shell.execute_reply":"2026-01-15T14:30:07.875961Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # CNN + TARGET EMBEDDING + GREEN FEATURE EMBEDDING\n# # TRAINING + INFERENCE (NO CLUSTERING)\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from PIL import Image\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import torch.nn as nn\n# from tqdm import tqdm\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n\n# BATCH_SIZE = 32\n# EPOCHS = 10\n# LR = 3e-4\n\n# # ============================================================\n# # IMAGE TRANSFORMS\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # GREEN FEATURE EXTRACTION (CONTINUOUS)\n# # ============================================================\n\n# def extract_green_features(img: Image.Image):\n#     arr = np.asarray(img).astype(np.float32) / 255.0\n#     R, G, B = arr[..., 0], arr[..., 1], arr[..., 2]\n\n#     eps = 1e-6\n#     g_ratio  = np.mean(G / (R + G + B + eps))\n#     exg      = np.mean(2 * G - R - B)\n#     gr_ratio = np.mean(G / (R + eps))\n\n#     return np.array([g_ratio, exg, gr_ratio], dtype=np.float32)\n\n# # ============================================================\n# # DATASETS\n# # ============================================================\n\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, training=True):\n#         self.df = df.reset_index(drop=True)\n#         self.training = training\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         row = self.df.loc[idx]\n\n#         img_path = os.path.join(IMG_ROOT, row[\"image_path\"])\n#         img = Image.open(img_path).convert(\"RGB\")\n\n#         green_feat = extract_green_features(img)\n#         img = transform(img)\n\n#         target_idx = target_to_idx[row[\"target_name\"]]\n\n#         if self.training:\n#             return img, target_idx, green_feat, row[\"target\"]\n#         else:\n#             return img, target_idx, green_feat\n\n# # ============================================================\n# # MODEL\n# # ============================================================\n\n# class CNNWithTargetAndGreenEmbedding(nn.Module):\n#     def __init__(self, num_targets, target_emb_dim=16, green_emb_dim=8):\n#         super().__init__()\n\n#         # CNN backbone (TRAINABLE)\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_emb_dim = 2048\n\n#         # Target embedding\n#         self.target_emb = nn.Embedding(num_targets, target_emb_dim)\n\n#         # Green feature embedding (MLP, not clustering)\n#         self.green_mlp = nn.Sequential(\n#             nn.Linear(3, 32),\n#             nn.ReLU(),\n#             nn.Linear(32, green_emb_dim)\n#         )\n\n#         # Regression head\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_emb_dim + target_emb_dim + green_emb_dim, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx, green_feat):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         green_emb = self.green_mlp(green_feat)\n\n#         x = torch.cat([img_emb, tgt_emb, green_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# # ============================================================\n# # LOAD DATA\n# # ============================================================\n\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# label_classes = sorted(train_df[\"target_name\"].unique())\n# target_to_idx = {n: i for i, n in enumerate(label_classes)}\n\n# train_ds = BiomassDataset(train_df, training=True)\n# test_ds  = BiomassDataset(test_df, training=False)\n\n# train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n# test_loader  = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)\n\n# # ============================================================\n# # TRAINING\n# # ============================================================\n\n# model = CNNWithTargetAndGreenEmbedding(\n#     num_targets=len(label_classes)\n# ).to(DEVICE)\n\n# optimizer = torch.optim.AdamW(model.parameters(), lr=LR)\n# criterion = nn.MSELoss()\n\n# model.train()\n\n# for epoch in range(EPOCHS):\n#     total_loss = 0\n\n#     for imgs, tgt_idx, green_feat, y in tqdm(train_loader):\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         green_feat = green_feat.to(DEVICE)\n#         y = y.to(DEVICE).float()\n\n#         optimizer.zero_grad()\n#         preds = model(imgs, tgt_idx, green_feat)\n#         loss = criterion(preds, y)\n#         loss.backward()\n#         optimizer.step()\n\n#         total_loss += loss.item()\n\n#     print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {total_loss / len(train_loader):.4f}\")\n\n# # ============================================================\n# # SAVE CHECKPOINT\n# # ============================================================\n\n# torch.save({\n#     \"model_state_dict\": model.state_dict(),\n#     \"label_encoder_classes\": label_classes\n# }, \"cnn_green_target_model.pt\")\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# model.eval()\n# preds = []\n\n# with torch.no_grad():\n#     for imgs, tgt_idx, green_feat in test_loader:\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         green_feat = green_feat.to(DEVICE)\n\n#         out = model(imgs, tgt_idx, green_feat)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"📤 submission.csv generated\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.879848Z","iopub.execute_input":"2026-01-15T14:30:07.880260Z","iopub.status.idle":"2026-01-15T14:30:07.920979Z","shell.execute_reply.started":"2026-01-15T14:30:07.880229Z","shell.execute_reply":"2026-01-15T14:30:07.919184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # CNN + TARGET EMBEDDING + GREEN FEATURE EMBEDDING\n# # INFERENCE ONLY\n# # ============================================================\n\n# import os\n# import torch\n# import pandas as pd\n# import numpy as np\n\n# from PIL import Image\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import torch.nn as nn\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_ROOT = \"/kaggle/input/csiro-biomass/\"\n# TEST_CSV = \"/kaggle/input/csiro-biomass/test.csv\"\n\n# CKPT_PATH = \"/kaggle/input/cnn-with-green/pytorch/default/1/cnn_green_target_model.pt\"\n\n# BATCH_SIZE = 32\n\n# # ============================================================\n# # IMAGE TRANSFORMS (SAME AS TRAINING)\n# # ============================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # GREEN FEATURE EXTRACTION (RAW RGB)\n# # ============================================================\n\n# def extract_green_features(img: Image.Image):\n#     arr = np.asarray(img).astype(np.float32) / 255.0\n#     R, G, B = arr[..., 0], arr[..., 1], arr[..., 2]\n\n#     eps = 1e-6\n#     g_ratio  = np.mean(G / (R + G + B + eps))\n#     exg      = np.mean(2 * G - R - B)\n#     gr_ratio = np.mean(G / (R + eps))\n\n#     return torch.tensor([g_ratio, exg, gr_ratio], dtype=torch.float32)\n\n# # ============================================================\n# # DATASET\n# # ============================================================\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n\n# class InferenceDataset(Dataset):\n#     def __init__(self, df, target_to_idx):\n#         self.df = df.reset_index(drop=True)\n#         self.target_to_idx = target_to_idx\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         row = self.df.loc[idx]\n\n#         img_path = os.path.join(IMG_ROOT, row[\"image_path\"])\n#         img = Image.open(img_path).convert(\"RGB\")\n\n#         green_feat = extract_green_features(img)\n#         img = transform(img)\n\n#         target_idx = self.target_to_idx[row[\"target_name\"]]\n\n#         return img, target_idx, green_feat\n\n# # ============================================================\n# # MODEL DEFINITION (MUST MATCH TRAINING)\n# # ============================================================\n\n# class CNNWithTargetAndGreenEmbedding(nn.Module):\n#     def __init__(self, num_targets, target_emb_dim=16, green_emb_dim=8):\n#         super().__init__()\n\n#         self.cnn = torch.load(\n#             \"/kaggle/input/embedding/pytorch/default/1/backbone_full.pt\",\n#             map_location=\"cpu\",\n#             weights_only=False\n#         )\n#         self.cnn.fc = nn.Identity()\n#         self.img_emb_dim = 2048\n\n#         self.target_emb = nn.Embedding(num_targets, target_emb_dim)\n\n#         self.green_mlp = nn.Sequential(\n#             nn.Linear(3, 32),\n#             nn.ReLU(),\n#             nn.Linear(32, green_emb_dim)\n#         )\n\n#         self.head = nn.Sequential(\n#             nn.Linear(self.img_emb_dim + target_emb_dim + green_emb_dim, 1024),\n#             nn.BatchNorm1d(1024),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n\n#             nn.Linear(1024, 256),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n\n#             nn.Linear(256, 1)\n#         )\n\n#     def forward(self, images, target_idx, green_feat):\n#         img_emb = self.cnn(images)\n#         tgt_emb = self.target_emb(target_idx)\n#         green_emb = self.green_mlp(green_feat)\n\n#         x = torch.cat([img_emb, tgt_emb, green_emb], dim=1)\n#         return self.head(x).squeeze(1)\n\n# # ============================================================\n# # LOAD CHECKPOINT\n# # ============================================================\n\n# ckpt = torch.load(CKPT_PATH, map_location=DEVICE)\n\n# label_classes = ckpt[\"label_encoder_classes\"]\n# target_to_idx = {name: i for i, name in enumerate(label_classes)}\n\n# model = CNNWithTargetAndGreenEmbedding(\n#     num_targets=len(label_classes)\n# ).to(DEVICE)\n\n# model.load_state_dict(ckpt[\"model_state_dict\"])\n# model.eval()\n\n# # ============================================================\n# # LOAD TEST DATA\n# # ============================================================\n\n# test_df = pd.read_csv(TEST_CSV)\n\n# test_ds = InferenceDataset(test_df, target_to_idx)\n# test_loader = DataLoader(\n#     test_ds,\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # ============================================================\n# # INFERENCE\n# # ============================================================\n\n# preds = []\n\n# with torch.no_grad():\n#     for imgs, tgt_idx, green_feat in test_loader:\n#         imgs = imgs.to(DEVICE)\n#         tgt_idx = tgt_idx.to(DEVICE)\n#         green_feat = green_feat.to(DEVICE)\n\n#         out = model(imgs, tgt_idx, green_feat)\n#         preds.extend(out.cpu().numpy())\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n\n# submission = pd.DataFrame({\n#     \"sample_id\": test_df[\"sample_id\"],\n#     \"target\": preds\n# })\n\n# submission.to_csv(\"submission.csv\", index=False)\n# print(\"✅ submission.csv generated successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.923859Z","iopub.execute_input":"2026-01-15T14:30:07.925090Z","iopub.status.idle":"2026-01-15T14:30:07.959297Z","shell.execute_reply.started":"2026-01-15T14:30:07.924785Z","shell.execute_reply":"2026-01-15T14:30:07.957768Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ============================================================\n# # COMPLETE TRAINING + INFERENCE USING EMBEDDING MODELS\n# # ============================================================\n\n# import os\n# import numpy as np\n# import pandas as pd\n# import torch\n# import torch.nn as nn\n# import torchvision.transforms as T\n# import xgboost as xgb\n\n# from PIL import Image\n# from sklearn.model_selection import train_test_split\n# from sklearn.preprocessing import LabelEncoder\n# from sklearn.metrics import mean_squared_error\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n\n# TRAIN_IMG_DIR = \"/kaggle/input/csiro-biomass/\"\n# TEST_IMG_DIR  = \"/kaggle/input/csiro-biomass/\"\n\n# EMB_DIR = \"/kaggle/input/embeddings-new/pytorch/default/1\"\n\n# EMB_FILES = [\n#     \"backbone_full1.pt\",\n#     \"backbone_full2.pt\",\n#     \"backbone_full3.pt\",\n#     \"backbone_full4.pt\",\n# ]\n\n# class ResNetBackbone(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = models.resnet50(pretrained=True)\n#         self.model.fc = nn.Identity()  # remove classifier\n\n#     def forward(self, x):\n#         return self.model(x)\n\n# IMAGE_COL = \"image_path\"   # <-- change if needed\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# # ============================================================\n# # LOAD CSVs\n# # ============================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# # ============================================================\n# # IMAGE TRANSFORMS (MUST MATCH TRAINING)\n# # ============================================================\n# transform = T.Compose([\n#     T.Resize((224, 224)),\n#     T.ToTensor(),\n#     T.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n\n# # ============================================================\n# # 1. LOAD EMBEDDING MODELS\n# # ============================================================\n# def load_models(files, emb_dir):\n#     models = []\n#     for f in files:\n#         model = torch.load(\n#             os.path.join(emb_dir, f),\n#             map_location=DEVICE,\n#             weights_only = False\n#         )\n#         model.eval()\n#         models.append(model)\n#     return models\n\n\n# models = load_models(EMB_FILES, EMB_DIR)\n\n# # ============================================================\n# # 2. EXTRACT EMBEDDINGS (ORDER-SAFE)\n# # ============================================================\n# def extract_embeddings(df, img_dir, models):\n#     all_rows = []\n\n#     for img_name in df[IMAGE_COL].values:\n#         img_path = os.path.join(img_dir, img_name)\n#         img = Image.open(img_path).convert(\"RGB\")\n#         img = transform(img).unsqueeze(0).to(DEVICE)\n\n#         embs = []\n\n#         with torch.no_grad():\n#             for model in models:\n#                 emb = model(img)          # (1, D)\n#                 embs.append(emb)\n\n#         # concat features\n#         row = torch.cat(embs, dim=1)     # (1, D_total)\n#         all_rows.append(row.cpu())\n\n#     # concat samples\n#     X = torch.cat(all_rows, dim=0)       # (N, D_total)\n#     return X.numpy()\n\n\n# # ============================================================\n# # 3. BUILD FEATURE MATRICES\n# # ============================================================\n# # X_train_all = extract_embeddings(train_df, TRAIN_IMG_DIR, models)\n# X_test_all  = extract_embeddings(test_df,  TEST_IMG_DIR,  models)\n\n# # ============================================================\n# # 4. TARGET PROCESSING\n# # ============================================================\n# le_target = LabelEncoder()\n# train_df[\"target_enc\"] = le_target.fit_transform(train_df[\"target_name\"])\n\n# y_all = train_df[\"target\"].values\n# target_names = train_df[\"target_name\"].values\n\n# # ============================================================\n# # 5. TRAIN ONE XGBOOST PER target_name\n# # ============================================================\n# # xgb_models = {}\n\n# # for tname in np.unique(target_names):\n# #     print(f\"\\nTraining XGBoost for target_name = {tname}\")\n\n#     # idx = np.where(target_names == tname)[0]\n#     # X_t = X_train_all[idx]\n#     # y_t = y_all[idx]\n\n \n\n#     # model = xgb.XGBRegressor(\n#     #     n_estimators=500,\n#     #     max_depth=8,\n#     #     learning_rate=0.05,\n#     #     subsample=0.8,\n#     #     colsample_bytree=0.8,\n#     #     objective=\"reg:squarederror\",\n#     #     random_state=42\n#     # )\n\n#     # model.fit(\n#     #     X_t,\n#     #     y_t,\n      \n#     #     verbose=False\n#     # )\n\n#     # # preds = model.predict(X_val)\n#     # # rmse = mean_squared_error(y_val, preds, squared=False)\n#     # # print(f\"RMSE for {tname}: {rmse:.4f}\")\n#     # print(f\"done\")\n\n#     # safe_name = tname.replace(\"_\", \"\")\n#     # model.save_model(f\"xgb_{safe_name}.json\")\n\n#     # xgb_models[tname] = model\n\n# # ============================================================\n# # 6. INFERENCE\n# # ============================================================\n# test_target_names = test_df[\"target_name\"].values\n# test_preds = np.zeros(len(test_df))\n\n# for tname in np.unique(test_target_names):\n#     idx = np.where(test_target_names == tname)[0]\n\n#     model = xgb.XGBRegressor()\n#     safe_name = tname.replace(\"_\", \"\")\n#     model.load_model(f\"/kaggle/input/advance-xg/pytorch/default/1/xgb_{safe_name}.json\")\n\n#     test_preds[idx] = model.predict(X_test_all[idx])\n\n# # ============================================================\n# # 7. SUBMISSION\n# # ============================================================\n# submission = test_df[[\"sample_id\"]].copy()\n# submission[\"target\"] = test_preds\n# submission.to_csv(\"submission.csv\", index=False)\n\n# print(\"✅ End-to-end pipeline completed successfully.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.961372Z","iopub.execute_input":"2026-01-15T14:30:07.961889Z","iopub.status.idle":"2026-01-15T14:30:07.996080Z","shell.execute_reply.started":"2026-01-15T14:30:07.961854Z","shell.execute_reply":"2026-01-15T14:30:07.994845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import numpy as np\n# import xgboost as xgb\n\n# # ============================================================\n# # CONFIG\n# # ============================================================\n# MODEL_DIR = \"/kaggle/input/advance-xg/pytorch/default/1\"\n\n# # test_df must already be loaded\n# # X_test_all must already be computed (shape: N x D)\n\n# test_target_names = test_df[\"target_name\"].values\n# test_preds = np.zeros(len(test_df))\n\n# # ============================================================\n# # INFERENCE PER target_name\n# # ============================================================\n# for tname in np.unique(test_target_names):\n#     print(f\"Running inference for target_name = {tname}\")\n\n#     idx = np.where(test_target_names == tname)[0]\n\n#     safe_name = tname.replace(\"_\", \"\")\n#     model_path = f\"{MODEL_DIR}/xgb_{safe_name}.json\"\n\n#     model = xgb.XGBRegressor()\n#     model.load_model(model_path)\n\n#     test_preds[idx] = model.predict(X_test_all[idx])\n\n# # ============================================================\n# # SUBMISSION\n# # ============================================================\n# submission = test_df[[\"id\"]].copy()\n# submission[\"target\"] = test_preds\n# submission.to_csv(\"submission.csv\", index=False)\n\n# print(\"✅ Inference completed. submission.csv saved.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:07.997792Z","iopub.execute_input":"2026-01-15T14:30:07.998239Z","iopub.status.idle":"2026-01-15T14:30:08.029807Z","shell.execute_reply.started":"2026-01-15T14:30:07.998210Z","shell.execute_reply":"2026-01-15T14:30:08.028136Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**DINO+ VIT**","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport timm\nimport numpy as np\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:08.031385Z","iopub.execute_input":"2026-01-15T14:30:08.031833Z","iopub.status.idle":"2026-01-15T14:30:14.808693Z","shell.execute_reply.started":"2026-01-15T14:30:08.031786Z","shell.execute_reply":"2026-01-15T14:30:14.806897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # BIOMASS PREDICTION – FINAL VERSION\n# # Image + Target-Name Conditioning\n# # Local DINO ViT-S/16 Weights (No Internet)\n# # =====================================================\n\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import timm\n# import pandas as pd\n# from pathlib import Path\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_SIZE = 224\n# BATCH_SIZE = 8\n# EPOCHS = 10\n# LR = 1e-4\n\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # TARGET NAME VOCAB (EXPLICIT & FIXED)\n# # =====================================================\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Clover_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Green_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n# NUM_TARGET_NAMES = len(TARGET_NAME_TO_ID)\n\n# # =====================================================\n# # LOAD DATA\n# # =====================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# # =====================================================\n# # PATCHIFY\n# # =====================================================\n# class Patchify(nn.Module):\n#     def __init__(self, patch_size):\n#         super().__init__()\n#         self.patch_size = patch_size\n\n#     def forward(self, x):\n#         B, C, H, W = x.shape\n#         P = self.patch_size\n\n#         x = x.unfold(2, P, P).unfold(3, P, P)\n#         x = x.permute(0, 2, 3, 1, 4, 5).contiguous()\n#         return x.view(B, -1, C, P, P)  # (B, N, C, P, P)\n\n# # =====================================================\n# # GREEN FEATURE EXTRACTOR\n# # =====================================================\n# class GreenFeatureExtractor(nn.Module):\n#     def forward(self, x):\n#         R, G, B = x[:, 0], x[:, 1], x[:, 2]\n#         exg = 2 * G - R - B\n#         green_ratio = (G > R).float().mean(dim=[1, 2])\n\n#         return torch.stack(\n#             [\n#                 G.mean(dim=[1, 2]),\n#                 exg.mean(dim=[1, 2]),\n#                 green_ratio,\n#             ],\n#             dim=1,\n#         )\n\n# # =====================================================\n# # CROSS ATTENTION\n# # =====================================================\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # BIOMASS MODEL\n# # =====================================================\n# class BiomassModel(nn.Module):\n#     def __init__(\n#         self,\n#         patch_size=32,\n#         embed_dim=384,\n#         num_heads=6,\n#         num_layers=4,\n#         target_emb_dim=128,\n#         num_species=15,\n#         num_states=4,\n#     ):\n#         super().__init__()\n\n#         # ---- DINO ViT-S/16 (LOCAL WEIGHTS) ----\n#         self.dino = timm.create_model(\n#             \"vit_small_patch16_224\",\n#             pretrained=False,\n#             num_classes=0,\n#         )\n\n#         ckpt = torch.load(DINO_CKPT, map_location=\"cpu\")\n#         self.dino.load_state_dict(ckpt, strict=False)\n\n#         for p in self.dino.parameters():\n#             p.requires_grad = False\n\n#         self.patchify = Patchify(patch_size)\n\n#         self.pos_embed = nn.Parameter(torch.randn(1, 196, embed_dim))\n\n#         encoder_layer = nn.TransformerEncoderLayer(\n#             d_model=embed_dim,\n#             nhead=num_heads,\n#             batch_first=True,\n#         )\n#         self.transformer = nn.TransformerEncoder(\n#             encoder_layer, num_layers=num_layers\n#         )\n\n#         # ---- Green Features ----\n#         self.green_extractor = GreenFeatureExtractor()\n#         self.green_proj = nn.Linear(3, embed_dim)\n\n#         self.cross_attn = CrossAttention(embed_dim, num_heads)\n\n#         # ---- Target Name Embedding ----\n#         self.target_embedding = nn.Embedding(NUM_TARGET_NAMES, target_emb_dim)\n#         self.target_proj = nn.Linear(target_emb_dim, embed_dim)\n\n#         fused_dim = embed_dim * 2\n\n#         # ---- Heads ----\n#         self.biomass_head = nn.Sequential(\n#             nn.Linear(fused_dim, 256),\n#             nn.ReLU(),\n#             nn.Linear(256, 1),\n#         )\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, num_species)\n#         self.state_head   = nn.Linear(fused_dim, num_states)\n\n#     def forward(self, image, target_name_id):\n#         B = image.size(0)\n\n#         patches = self.patchify(image)\n#         B, N, C, P, _ = patches.shape\n\n#         patches = patches.view(B * N, C, P, P)\n#         tokens = self.dino(patches)\n#         tokens = tokens.view(B, N, -1)\n\n#         tokens = tokens + self.pos_embed[:, :N]\n#         tokens = self.transformer(tokens)\n\n#         green = self.green_extractor(image)\n#         green = self.green_proj(green).unsqueeze(1)\n\n#         img_ctx = self.cross_attn(green, tokens).squeeze(1)\n\n#         tgt = self.target_embedding(target_name_id)\n#         tgt = self.target_proj(tgt)\n\n#         fused = torch.cat([img_ctx, tgt], dim=1)\n\n#         return {\n#             \"biomass\": self.biomass_head(fused).squeeze(1),\n#             \"height\":  self.height_head(fused).squeeze(1),\n#             \"aux\":     self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\":   self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASETS\n# # =====================================================\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n\n#         image = Image.open(os.path.join(\"/kaggle/input/csiro-biomass\", r[\"image_path\"])).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         return {\n#             \"image\": image,\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"]),\n#             \"state\": torch.tensor(r[\"State\"]),\n#             \"height\": torch.tensor(r[\"Height_Ave_cm\"], dtype=torch.float32),\n#             \"aux\": torch.tensor(r[\"Pre_GSHH_NDVI\"], dtype=torch.float32),\n#         }\n\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n\n#         image = Image.open(os.path.join(\"/kaggle/input/csiro-biomass\", r[\"image_path\"])).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         return {\n#             \"image\": image,\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"id\": r[\"sample_id\"],\n#         }\n\n# # =====================================================\n# # TRAINING\n# # =====================================================\n# LOSS_W = dict(biomass=1.0, height=0.3, aux=0.2, species=0.1, state=0.1)\n\n# def train_one_epoch(model, loader, optimizer):\n#     model.train()\n#     total = 0\n\n#     for b in loader:\n#         img = b[\"image\"].to(DEVICE)\n#         tgt = b[\"target\"].to(DEVICE)\n#         tn  = b[\"target_name\"].to(DEVICE)\n\n#         out = model(img, tn)\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * F.mse_loss(out[\"biomass\"], tgt) +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total += loss.item()\n\n#     return total / len(loader)\n\n# # =====================================================\n# # SUBMISSION\n# # =====================================================\n# @torch.no_grad()\n# def generate_submission(model, loader, out_path=\"submission.csv\"):\n#     model.eval()\n#     ids, preds = [], []\n\n#     for b in loader:\n#         out = model(b[\"image\"].to(DEVICE), b[\"target_name\"].to(DEVICE))\n#         ids.extend(b[\"id\"])\n#         preds.extend(out[\"biomass\"].cpu().numpy())\n\n#     pd.DataFrame({\"id\": ids, \"biomass\": preds}).to_csv(out_path, index=False)\n#     print(f\"✅ Saved {out_path}\")\n\n# # =====================================================\n# # RUN\n# # =====================================================\n# transform = transforms.Compose([\n#     transforms.Resize((224, 224)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=[0.485, 0.456, 0.406],\n#         std=[0.229, 0.224, 0.225]\n#     )\n# ])\n# train_loader = DataLoader(\n#     BiomassDataset(train_df, transform),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n# )\n\n# test_loader = DataLoader(\n#     BiomassTestDataset(test_df, transform),\n#     batch_size=BATCH_SIZE,\n# )\n\n# model = BiomassModel().to(DEVICE)\n# optimizer = torch.optim.AdamW(model.parameters(), lr=LR)\n\n# for e in range(EPOCHS):\n#     loss = train_one_epoch(model, train_loader, optimizer)\n#     print(f\"Epoch {e+1}/{EPOCHS} | Loss: {loss:.4f}\")\n\n# torch.save(model.state_dict(), \"biomass_model.pth\")\n# print(\"✅ Model saved\")\n\n# generate_submission(model, test_loader)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.810181Z","iopub.execute_input":"2026-01-15T14:30:14.810783Z","iopub.status.idle":"2026-01-15T14:30:14.823390Z","shell.execute_reply.started":"2026-01-15T14:30:14.810731Z","shell.execute_reply":"2026-01-15T14:30:14.822146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import timm\n# model = timm.create_model(\n#     \"deit_small_patch16_224\",\n#     pretrained=False,\n#     num_classes=0\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.824797Z","iopub.execute_input":"2026-01-15T14:30:14.825238Z","iopub.status.idle":"2026-01-15T14:30:14.858816Z","shell.execute_reply.started":"2026-01-15T14:30:14.825207Z","shell.execute_reply":"2026-01-15T14:30:14.856935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # BIOMASS INFERENCE – FIXED DINO VERSION\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import timm\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# BATCH_SIZE = 8\n# IMG_SIZE = 224\n\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n# MODEL_CKPT = \"/kaggle/input/biomass/pytorch/default/1/biomass_model2.pth\"  \n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # TARGET NAME VOCAB (MUST MATCH TRAINING)\n# # =====================================================\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Clover_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Green_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n# NUM_TARGET_NAMES = len(TARGET_NAME_TO_ID)\n\n# # =====================================================\n# # GREEN FEATURE EXTRACTOR\n# # =====================================================\n# class GreenFeatureExtractor(nn.Module):\n#     def forward(self, x):\n#         R, G, B = x[:, 0], x[:, 1], x[:, 2]\n#         exg = 2 * G - R - B\n#         green_ratio = (G > R).float().mean(dim=[1, 2])\n#         return torch.stack(\n#             [G.mean(dim=[1, 2]), exg.mean(dim=[1, 2]), green_ratio],\n#             dim=1,\n#         )\n\n\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n\n# class BiomassModel(nn.Module):\n#     def __init__(\n#         self,\n#         embed_dim=384,\n#         num_heads=6,\n#         num_layers=4,\n#         target_emb_dim=128,\n#         num_species=1,\n#         num_states=1,\n#     ):\n#         super().__init__()\n\n#         self.dino = timm.create_model(\n#             \"vit_small_patch16_224\",\n#             pretrained=False,\n#             num_classes=0,\n#         )\n#         ckpt = torch.load(DINO_CKPT, map_location=\"cpu\")\n#         self.dino.load_state_dict(ckpt, strict=False)\n\n#         for p in self.dino.parameters():\n#             p.requires_grad = False\n\n#         self.pos_embed = nn.Parameter(torch.randn(1, 196, embed_dim))\n\n#         encoder_layer = nn.TransformerEncoderLayer(\n#             d_model=embed_dim,\n#             nhead=num_heads,\n#             batch_first=True,\n#         )\n#         self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)\n\n#         self.green_extractor = GreenFeatureExtractor()\n#         self.green_proj = nn.Linear(3, embed_dim)\n#         self.cross_attn = CrossAttention(embed_dim, num_heads)\n\n#         self.target_embedding = nn.Embedding(NUM_TARGET_NAMES, target_emb_dim)\n#         self.target_proj = nn.Linear(target_emb_dim, embed_dim)\n\n#         fused_dim = embed_dim * 2\n\n#         self.biomass_head = nn.Sequential(\n#             nn.Linear(fused_dim, 256),\n#             nn.ReLU(),\n#             nn.Linear(256, 1),\n#         )\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, num_species)\n#         self.state_head   = nn.Linear(fused_dim, num_states)\n\n#     def forward(self, image, target_name_id):\n#         tokens = self.dino.forward_features(image)  # (B, 197, D)\n#         tokens = tokens[:, 1:, :]                   # drop CLS → (B, 196, D)\n\n#         tokens = tokens + self.pos_embed\n#         tokens = self.transformer(tokens)\n\n#         green = self.green_proj(self.green_extractor(image)).unsqueeze(1)\n#         img_ctx = self.cross_attn(green, tokens).squeeze(1)\n\n#         tgt = self.target_proj(self.target_embedding(target_name_id))\n#         fused = torch.cat([img_ctx, tgt], dim=1)\n\n#         return self.biomass_head(fused).squeeze(1)\n\n# # =====================================================\n# # TEST DATASET\n# # =====================================================\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(\n#             os.path.join(DATA_ROOT, r[\"image_path\"])\n#         ).convert(\"RGB\")\n\n#         return {\n#             \"image\": self.transform(img),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"id\": r[\"sample_id\"],\n#         }\n\n# # =====================================================\n# # INFERENCE\n# # =====================================================\n# @torch.no_grad()\n# def run_inference():\n#     test_df = pd.read_csv(TEST_CSV)\n\n#     transform = transforms.Compose([\n#         transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#         transforms.ToTensor(),\n#     ])\n\n#     loader = DataLoader(\n#         BiomassTestDataset(test_df, transform),\n#         batch_size=BATCH_SIZE,\n#         shuffle=False,\n#     )\n\n#     model = BiomassModel().to(DEVICE)\n#     checkpoint = torch.load(MODEL_CKPT, map_location=DEVICE)\n#     model.load_state_dict(checkpoint)\n#     model.eval()\n\n#     ids, preds = [], []\n\n#     for b in loader:\n#         out = model(\n#             b[\"image\"].to(DEVICE),\n#             b[\"target_name\"].to(DEVICE),\n#         )\n#         ids.extend(b[\"id\"])\n#         preds.extend(out.cpu().numpy())\n\n#     pd.DataFrame({\"sample_id\": ids, \"target\": preds}).to_csv(\n#         \"submission.csv\", index=False\n#     )\n#     print(\"✅ submission.csv generated\");\n\n\n# run_inference()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.860968Z","iopub.execute_input":"2026-01-15T14:30:14.861442Z","iopub.status.idle":"2026-01-15T14:30:14.888223Z","shell.execute_reply.started":"2026-01-15T14:30:14.861398Z","shell.execute_reply":"2026-01-15T14:30:14.886851Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import timm\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_SIZE = 224\n# BATCH_SIZE = 8\n# EPOCHS = 10\n# LR = 1e-4\n\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # TARGET NAME VOCAB\n# # =====================================================\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Clover_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Green_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n# NUM_TARGET_NAMES = len(TARGET_NAME_TO_ID)\n\n# # =====================================================\n# # LOAD DATA\n# # =====================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# # =====================================================\n# # LABEL ENCODING\n# # =====================================================\n# species_encoder = LabelEncoder()\n# state_encoder   = LabelEncoder()\n\n# train_df[\"Species\"] = species_encoder.fit_transform(train_df[\"Species\"])\n# train_df[\"State\"]   = state_encoder.fit_transform(train_df[\"State\"])\n\n# if \"Species\" in test_df.columns:\n#     test_df[\"Species\"] = species_encoder.transform(test_df[\"Species\"])\n# if \"State\" in test_df.columns:\n#     test_df[\"State\"] = state_encoder.transform(test_df[\"State\"])\n\n# NUM_SPECIES = train_df[\"Species\"].nunique()\n# NUM_STATES  = train_df[\"State\"].nunique()\n\n# print(f\"✅ Species classes: {NUM_SPECIES}, State classes: {NUM_STATES}\")\n\n# # =====================================================\n# # GREEN FEATURE EXTRACTOR\n# # =====================================================\n# class GreenFeatureExtractor(nn.Module):\n#     def forward(self, x):\n#         R, G, B = x[:, 0], x[:, 1], x[:, 2]\n#         exg = 2 * G - R - B\n#         green_ratio = (G > R).float().mean(dim=[1, 2])\n#         return torch.stack(\n#             [G.mean(dim=[1, 2]), exg.mean(dim=[1, 2]), green_ratio], dim=1\n#         )\n\n# # =====================================================\n# # CROSS ATTENTION\n# # =====================================================\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # BIOMASS MODEL (FIXED)\n# # =====================================================\n# class BiomassModel(nn.Module):\n#     def __init__(\n#         self,\n#         embed_dim=384,\n#         num_heads=6,\n#         num_layers=4,\n#         target_emb_dim=128,\n#         num_species=1,\n#         num_states=1,\n#     ):\n#         super().__init__()\n\n#         # ---- DINO ViT-S/16 ----\n#         self.dino = timm.create_model(\n#             \"vit_small_patch16_224\",\n#             pretrained=False,\n#             num_classes=0,\n#         )\n#         ckpt = torch.load(DINO_CKPT, map_location=\"cpu\")\n#         self.dino.load_state_dict(ckpt, strict=False)\n\n#         for p in self.dino.parameters():\n#             p.requires_grad = False\n\n#         # 196 tokens (14x14)\n#         self.pos_embed = nn.Parameter(torch.randn(1, 196, embed_dim))\n\n#         encoder_layer = nn.TransformerEncoderLayer(\n#             d_model=embed_dim,\n#             nhead=num_heads,\n#             batch_first=True,\n#         )\n#         self.transformer = nn.TransformerEncoder(\n#             encoder_layer, num_layers=num_layers\n#         )\n\n#         # ---- Green Features ----\n#         self.green_extractor = GreenFeatureExtractor()\n#         self.green_proj = nn.Linear(3, embed_dim)\n#         self.cross_attn = CrossAttention(embed_dim, num_heads)\n\n#         # ---- Target Name Conditioning ----\n#         self.target_embedding = nn.Embedding(NUM_TARGET_NAMES, target_emb_dim)\n#         self.target_proj = nn.Linear(target_emb_dim, embed_dim)\n\n#         fused_dim = embed_dim * 2\n\n#         # ---- Heads ----\n#         self.biomass_head = nn.Sequential(\n#             nn.Linear(fused_dim, 256),\n#             nn.ReLU(),\n#             nn.Linear(256, 1),\n#         )\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, num_species)\n#         self.state_head   = nn.Linear(fused_dim, num_states)\n\n#     def forward(self, image, target_name_id):\n#         # ---- DINO token extraction ----\n#         tokens = self.dino.forward_features(image)  # (B, 197, D)\n#         tokens = tokens[:, 1:, :]                   # remove CLS → (B, 196, D)\n\n#         tokens = tokens + self.pos_embed\n#         tokens = self.transformer(tokens)\n\n#         # ---- Green conditioning ----\n#         green = self.green_extractor(image)\n#         green = self.green_proj(green).unsqueeze(1)\n#         img_ctx = self.cross_attn(green, tokens).squeeze(1)\n\n#         # ---- Target conditioning ----\n#         tgt = self.target_proj(self.target_embedding(target_name_id))\n\n#         fused = torch.cat([img_ctx, tgt], dim=1)\n\n#         return {\n#             \"biomass\": self.biomass_head(fused).squeeze(1),\n#             \"height\":  self.height_head(fused).squeeze(1),\n#             \"aux\":     self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\":   self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASETS\n# # =====================================================\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         image = Image.open(\n#             os.path.join(DATA_ROOT, r[\"image_path\"])\n#         ).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         return {\n#             \"image\": image,\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"], dtype=torch.long),\n#             \"state\": torch.tensor(r[\"State\"], dtype=torch.long),\n#             \"height\": torch.tensor(r[\"Height_Ave_cm\"], dtype=torch.float32),\n#             \"aux\": torch.tensor(r[\"Pre_GSHH_NDVI\"], dtype=torch.float32),\n#         }\n\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df, transform):\n#         self.df = df.reset_index(drop=True)\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         image = Image.open(\n#             os.path.join(DATA_ROOT, r[\"image_path\"])\n#         ).convert(\"RGB\")\n#         image = self.transform(image)\n\n#         return {\n#             \"image\": image,\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"id\": r[\"sample_id\"],\n#         }\n\n# # =====================================================\n# # TRAINING\n# # =====================================================\n# LOSS_W = dict(biomass=1.0, height=0.3, aux=0.2, species=0.1, state=0.1)\n\n# def train_one_epoch(model, loader, optimizer):\n#     model.train()\n#     total = 0.0\n\n#     for b in loader:\n#         img = b[\"image\"].to(DEVICE)\n#         tgt = b[\"target\"].to(DEVICE)\n#         tn  = b[\"target_name\"].to(DEVICE)\n\n#         out = model(img, tn)\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * F.mse_loss(out[\"biomass\"], tgt) +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total += loss.item()\n\n#     return total / len(loader)\n\n# # =====================================================\n# # SUBMISSION\n# # =====================================================\n# @torch.no_grad()\n# def generate_submission(model, loader, out_path=\"submission.csv\"):\n#     model.eval()\n#     ids, preds = [], []\n\n#     with torch.no_grad():\n#         for b in loader:\n#             out = model(\n#                 b[\"image\"].to(DEVICE),\n#                 b[\"target_name\"].to(DEVICE)\n#             )\n\n#             biomass = out[\"biomass\"]\n\n#             # ✅ only negatives → 0.5\n#             biomass = torch.where(\n#                 biomass < 0,\n#                 torch.tensor(0.5, device=biomass.device),\n#                 biomass\n#             )\n\n#             ids.extend(b[\"id\"])\n#             preds.extend(biomass.cpu().numpy())\n\n#     pd.DataFrame({\"sample_id\": ids, \"target\": preds}).to_csv(out_path, index=False)\n#     print(f\"✅ Saved {out_path}\")\n\n\n# # =====================================================\n# # RUN\n# # =====================================================\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n\n# train_loader = DataLoader(\n#     BiomassDataset(train_df, transform),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n# )\n\n# test_loader = DataLoader(\n#     BiomassTestDataset(test_df, transform),\n#     batch_size=BATCH_SIZE,\n# )\n\n# model = BiomassModel(\n#     num_species=NUM_SPECIES,\n#     num_states=NUM_STATES,\n# ).to(DEVICE)\n\n\n# check = torch.load(\"/kaggle/input/biomass30/pytorch/default/1/biomass_model_epoch_30.pth\", map_location = DEVICE)\n# model.load_state_dict(check['model_state_dict'])\n\n# optimizer = torch.optim.AdamW(model.parameters(), lr=LR)\n\n# EPOCHS = 50\n# SAVE_EVERY = 10\n\n# # for e in range(EPOCHS):\n# #     loss = train_one_epoch(model, train_loader, optimizer)\n# #     print(f\"Epoch {e+1}/{EPOCHS} | Loss: {loss:.4f}\")\n\n# #     if (e + 1) % SAVE_EVERY == 0:\n# #         ckpt_path = f\"biomass_model_epoch_{e+1}.pth\"\n# #         torch.save({\n# #             \"epoch\": e + 1,\n# #             \"model_state_dict\": model.state_dict(),\n# #             \"optimizer_state_dict\": optimizer.state_dict(),\n# #             \"loss\": loss,\n# #         }, ckpt_path)\n# #         print(f\"💾 Saved checkpoint: {ckpt_path}\")\n\n# # print(\"✅ Training complete\")\n\n\n# generate_submission(model, test_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.889678Z","iopub.execute_input":"2026-01-15T14:30:14.890007Z","iopub.status.idle":"2026-01-15T14:30:14.914691Z","shell.execute_reply.started":"2026-01-15T14:30:14.889938Z","shell.execute_reply":"2026-01-15T14:30:14.913332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # FULL BIOMASS PIPELINE – DINOv2 GIANT (SINGLE BLOCK)\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n# from transformers import Dinov2Model\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_SIZE   = 224\n# BATCH_SIZE = 1            # DINOv2-Giant SAFE\n# LR         = 1e-4\n\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n\n# DINOv2_PATH = \"/kaggle/input/dinov2/pytorch/giant/1\"\n\n# # =====================================================\n# # LOSS WEIGHTS\n# # =====================================================\n# LOSS_W = {\n#     \"biomass\": 1.0,\n#     \"height\":  0.2,\n#     \"aux\":     0.2,\n#     \"species\": 0.1,\n#     \"state\":   0.1,\n# }\n\n# # =====================================================\n# # TARGET NAME VOCAB\n# # =====================================================\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Clover_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Green_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n# NUM_TARGET_NAMES = len(TARGET_NAME_TO_ID)\n\n# # =====================================================\n# # LOAD DATA\n# # =====================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# species_encoder = LabelEncoder()\n# state_encoder   = LabelEncoder()\n\n# train_df[\"Species\"] = species_encoder.fit_transform(train_df[\"Species\"])\n# train_df[\"State\"]   = state_encoder.fit_transform(train_df[\"State\"])\n\n# NUM_SPECIES = train_df[\"Species\"].nunique()\n# NUM_STATES  = train_df[\"State\"].nunique()\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n# # =====================================================\n# # PATCH TILING\n# # =====================================================\n# def patch_to_tiles(x, tile_h, tile_w):\n#     B, H, W, D = x.shape\n#     x = x.view(\n#         B,\n#         H // tile_h, tile_h,\n#         W // tile_w, tile_w,\n#         D\n#     )\n#     x = x.mean(dim=(2, 4))\n#     return x.view(B, -1, D)\n\n# # =====================================================\n# # VEGETATION INDEX (ExG)\n# # =====================================================\n# class VegetationIndex(nn.Module):\n#     def forward(self, img):\n#         R, G, B = img[:, 0], img[:, 1], img[:, 2]\n#         exg = 2 * G - R - B\n#         exg = F.interpolate(\n#             exg.unsqueeze(1),\n#             size=(16, 16),\n#             mode=\"bilinear\",\n#             align_corners=False\n#         )\n#         return exg.squeeze(1)\n\n# # =====================================================\n# # CROSS ATTENTION\n# # =====================================================\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # BIOMASS MODEL – DINOv2 GIANT\n# # =====================================================\n# class BiomassModel(nn.Module):\n#     def __init__(self, embed_dim=1536, num_heads=24, target_emb_dim=256):\n#         super().__init__()\n\n#         # ---- DINOv2 Giant ----\n#         self.dino = Dinov2Model.from_pretrained(\n#             DINOv2_PATH,\n#             local_files_only=True\n#         )\n#         for p in self.dino.parameters():\n#             p.requires_grad = False\n\n#         # ---- Vegetation ----\n#         self.veg = VegetationIndex()\n\n#         # ---- Projections ----\n#         self.local_proj = nn.Sequential(\n#             nn.Linear(embed_dim + 1, embed_dim),\n#             nn.ReLU(),\n#             nn.LayerNorm(embed_dim),\n#         )\n#         self.global_proj = nn.Sequential(\n#             nn.Linear(embed_dim + 1, embed_dim),\n#             nn.ReLU(),\n#             nn.LayerNorm(embed_dim),\n#         )\n\n#         # ---- Cross-scale Attention ----\n#         self.local_attn  = CrossAttention(embed_dim, num_heads)\n#         self.global_attn = CrossAttention(embed_dim, num_heads)\n\n#         # ---- Token Pooling ----\n#         self.token_pool = nn.Linear(embed_dim, 1)\n\n#         # ---- Target Conditioning ----\n#         self.target_embedding = nn.Embedding(NUM_TARGET_NAMES, target_emb_dim)\n#         self.target_proj = nn.Linear(target_emb_dim, embed_dim)\n\n#         fused_dim = embed_dim * 3 + embed_dim\n\n#         # ---- Heads ----\n#         self.biomass_head = nn.Sequential(\n#             nn.Linear(fused_dim, 1024),\n#             nn.ReLU(),\n#             nn.Linear(1024, 1),\n#         )\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, NUM_SPECIES)\n#         self.state_head   = nn.Linear(fused_dim, NUM_STATES)\n\n#     def forward(self, image, target_name_id):\n#         B = image.size(0)\n\n#         # ---- DINOv2 Tokens ----\n#         out = self.dino(pixel_values=image)\n#         tokens = out.last_hidden_state[:, 1:, :]   # remove CLS\n#         tokens = tokens.view(B, 16, 16, -1)\n\n#         # ---- Tiling ----\n#         local_tokens  = patch_to_tiles(tokens, 2, 2)   # 64\n#         global_tokens = patch_to_tiles(tokens, 8, 8)   # 4\n\n#         # ---- Vegetation ----\n#         exg = self.veg(image).unsqueeze(-1)\n#         exg_local  = patch_to_tiles(exg, 2, 2)\n#         exg_global = patch_to_tiles(exg, 8, 8)\n\n#         local_tokens  = self.local_proj(torch.cat([local_tokens, exg_local], dim=-1))\n#         global_tokens = self.global_proj(torch.cat([global_tokens, exg_global], dim=-1))\n\n#         # ---- Cross Attention ----\n#         global_tokens = self.global_attn(global_tokens, local_tokens)\n#         local_tokens  = self.local_attn(local_tokens, global_tokens)\n\n#         # ---- Multi Pooling ----\n#         all_tokens = torch.cat([global_tokens, local_tokens], dim=1)\n\n#         w = F.softmax(self.token_pool(all_tokens), dim=1)\n#         cls_like = (w * all_tokens).sum(dim=1)\n#         avg_pool = all_tokens.mean(dim=1)\n#         max_pool = all_tokens.max(dim=1)[0]\n\n#         img_ctx = torch.cat([cls_like, avg_pool, max_pool], dim=1)\n\n#         # ---- Target Conditioning ----\n#         tgt = self.target_proj(self.target_embedding(target_name_id))\n#         fused = torch.cat([img_ctx, tgt], dim=1)\n\n#         return {\n#             \"biomass\": self.biomass_head(fused).squeeze(1),\n#             \"height\":  self.height_head(fused).squeeze(1),\n#             \"aux\":     self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\":   self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASETS\n# # =====================================================\n# class BiomassDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         img = transform(img)\n\n#         return {\n#             \"image\": img,\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"]),\n#             \"state\": torch.tensor(r[\"State\"]),\n#             \"height\": torch.tensor(r.get(\"Height_Ave_cm\", 0.0), dtype=torch.float32),\n#             \"aux\": torch.tensor(r.get(\"Pre_GSHH_NDVI\", 0.0), dtype=torch.float32),\n#         }\n\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         img = transform(img)\n\n#         return {\n#             \"image\": img,\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"id\": r[\"sample_id\"],\n#         }\n\n# # =====================================================\n# # INFERENCE\n# # =====================================================\n# @torch.no_grad()\n# def generate_submission(model, loader, out_path=\"submission.csv\"):\n#     model.eval()\n#     ids, preds = [], []\n\n#     for b in loader:\n#         out = model(\n#             b[\"image\"].to(DEVICE),\n#             b[\"target_name\"].to(DEVICE)\n#         )\n#         biomass = torch.clamp(out[\"biomass\"], min=0)\n#         ids.extend(b[\"id\"])\n#         preds.extend(biomass.cpu().numpy())\n\n#     pd.DataFrame({\"sample_id\": ids, \"target\": preds}).to_csv(out_path, index=False)\n#     print(f\"✅ Saved {out_path}\")\n\n# # =====================================================\n# # RUN\n# # =====================================================\n# model = BiomassModel().to(DEVICE)\n\n# optimizer = torch.optim.AdamW(model.parameters(), lr=LR)\n# def train_one_epoch(model, loader, optimizer):\n#     model.train()\n#     total = 0.0\n\n#     for b in loader:\n#         img = b[\"image\"].to(DEVICE)\n#         tgt = b[\"target\"].to(DEVICE)\n#         tn  = b[\"target_name\"].to(DEVICE)\n\n#         out = model(img, tn)\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * F.mse_loss(out[\"biomass\"], tgt) +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n\n#         total += loss.item()\n\n#     return total / len(loader)\n\n\n# train_loader = DataLoader(\n#     BiomassDataset(train_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n# )\n\n# EPOCHS = 50\n# SAVE_EVERY = 10\n\n# for e in range(EPOCHS):\n#     loss = train_one_epoch(model, train_loader, optimizer)\n#     print(f\"Epoch {e+1}/{EPOCHS} | Loss: {loss:.4f}\")\n\n#     if (e + 1) % SAVE_EVERY == 0:\n#         ckpt_path = f\"biomass_model_epoch_{e+1}.pth\"\n#         torch.save({\n#             \"epoch\": e + 1,\n#             \"model_state_dict\": model.state_dict(),\n#             \"optimizer_state_dict\": optimizer.state_dict(),\n#             \"loss\": loss,\n#         }, ckpt_path)\n#         print(f\"💾 Saved checkpoint: {ckpt_path}\")\n\n# print(\"✅ Training complete\")\n\n\n# test_loader = DataLoader(\n#     BiomassTestDataset(test_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# generate_submission(model, test_loader)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.916816Z","iopub.execute_input":"2026-01-15T14:30:14.917323Z","iopub.status.idle":"2026-01-15T14:30:14.952373Z","shell.execute_reply.started":"2026-01-15T14:30:14.917291Z","shell.execute_reply":"2026-01-15T14:30:14.951039Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # FULL HIERARCHICAL BIOMASS MODEL\n# # LEFT–RIGHT + TOKEN-SPACE TILING (SINGLE BLOCK)\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torchvision import transforms\n# from torch.utils.data import Dataset, DataLoader\n# from PIL import Image\n# import timm\n# import pandas as pd\n# import numpy as np\n# from sklearn.preprocessing import LabelEncoder\n# import warnings\n\n# warnings.filterwarnings('ignore')\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_SIZE = 224\n# BATCH_SIZE = 8 \n# LR = 1e-4\n# EPOCHS = 50 \n# SAVE_INTERVAL = 10  # Save checkpoint every 10 epochs\n\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# TRAIN_CSV = f\"{DATA_ROOT}/train.csv\"\n# TEST_CSV = f\"{DATA_ROOT}/test.csv\"\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# TARGET_NAMES = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\", \"Dry_Total_g\", \"GDM_g\"]\n# TARGET_NAME_TO_ID = {name: i for i, name in enumerate(TARGET_NAMES)}\n# NUM_TARGETS = len(TARGET_NAMES)\n\n# LOSS_W = dict(biomass=1.0, height=0.3, green=0.2, species=0.1, state=0.1)\n\n# # =====================================================\n# # DATA PREPARATION\n# # =====================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df = pd.read_csv(TEST_CSV)\n\n# species_enc = LabelEncoder()\n# state_enc   = LabelEncoder()\n\n# train_df[\"Species_idx\"] = species_enc.fit_transform(train_df[\"Species\"])\n# train_df[\"State_idx\"]   = state_enc.fit_transform(train_df[\"State\"])\n\n# NUM_SPECIES = len(species_enc.classes_)\n# NUM_STATES  = len(state_enc.classes_)\n\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))\n# ])\n\n# # =====================================================\n# # LAYERS\n# # =====================================================\n# class FiLM(nn.Module):\n#     def __init__(self, feat_dim):\n#         super().__init__()\n#         self.mlp = nn.Sequential(\n#             nn.Linear(feat_dim, feat_dim // 2),\n#             nn.ReLU(inplace=True),\n#             nn.Linear(feat_dim // 2, feat_dim * 2)\n#         )\n\n#     def forward(self, context):\n#         gamma_beta = self.mlp(context)\n#         gamma, beta = torch.chunk(gamma_beta, 2, dim=-1)\n#         return gamma, beta\n\n\n\n# class GreenFeatureExtractor(nn.Module):\n#     def forward(self, x):\n#         R, G, B = x[:, 0], x[:, 1], x[:, 2]\n#         exg = 2 * G - R - B\n#         green_ratio = (G > R).float().mean(dim=[1, 2])\n#         return torch.stack([G.mean(dim=[1, 2]), exg.mean(dim=[1, 2]), green_ratio], dim=1)\n\n# GREEN_EXTRACTOR = GreenFeatureExtractor()\n\n# def extract_stats(tokens):\n#     cls  = tokens[:, 0]\n#     mean = tokens[:, 1:].mean(dim=1)\n#     maxv = tokens[:, 1:].max(dim=1)[0]\n#     return torch.cat([cls, mean, maxv], dim=1)\n\n# def token_tile(tokens, tile):\n#     B, N, D = tokens.shape\n#     S = int(N ** 0.5)\n#     tokens = tokens.view(B, S, S, D)\n#     tokens = tokens.view(B, S // tile, tile, S // tile, tile, D).mean(dim=(2, 4))\n#     return tokens.view(B, -1, D)\n\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads=6):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # MODEL\n# # =====================================================\n# class HierarchicalBiomassModel(nn.Module):\n#     def __init__(self, embed_dim=384):\n#         super().__init__()\n#         self.dino = timm.create_model(\"vit_small_patch16_224\", pretrained=False, num_classes=0)\n#         if os.path.exists(DINO_CKPT):\n#             self.dino.load_state_dict(torch.load(DINO_CKPT, map_location=\"cpu\"), strict=False)\n#         for p in self.dino.parameters(): p.requires_grad = False\n\n#         feat_dim = embed_dim * 3 \n#         self.film = FiLM(feat_dim)\n#         self.cross_small_large = CrossAttention(feat_dim)\n#         self.self_lr = nn.TransformerEncoder(\n#             nn.TransformerEncoderLayer(feat_dim, 6, batch_first=True), num_layers=2\n#         )\n#         self.cross_full = CrossAttention(feat_dim)\n\n#         self.biomass_heads = nn.ModuleList([nn.Linear(feat_dim, 1) for _ in range(NUM_TARGETS)])\n#         self.height_head  = nn.Linear(feat_dim, 1)\n#         self.green_head   = nn.Linear(feat_dim, 3)\n#         self.species_head = nn.Linear(feat_dim, NUM_SPECIES)\n#         self.state_head   = nn.Linear(feat_dim, NUM_STATES)\n\n#     def process_side(self, img):\n#         tokens = self.dino.forward_features(img)[:, 1:, :] \n#         small = extract_stats(token_tile(tokens, tile=2))\n#         large = extract_stats(token_tile(tokens, tile=7))\n#         return self.cross_small_large(small.unsqueeze(1), large.unsqueeze(1)).squeeze(1)\n\n#     def forward(self, left, right, full, target_name=None):\n#         left_feat = self.process_side(left)\n#         right_feat = self.process_side(right)\n        \n#         full_tokens = self.dino.forward_features(full)\n#         full_stats = extract_stats(full_tokens)\n\n#         # FiLM Modulation using Full Image Context\n#         gamma, beta = self.film(full_stats)\n#         left_feat = left_feat * (1 + gamma) + beta\n#         right_feat = right_feat * (1 + gamma) + beta\n\n#         lr_feats = torch.stack([left_feat, right_feat], dim=1)\n#         lr_feats = self.self_lr(lr_feats).mean(dim=1)\n\n#         fused = self.cross_full(lr_feats.unsqueeze(1), full_stats.unsqueeze(1)).squeeze(1)\n\n#         all_biomass = torch.stack([head(fused).squeeze(1) for head in self.biomass_heads], dim=1)\n\n#         biomass_out = torch.zeros(left.size(0), device=left.device)\n#         if target_name is not None:\n#             for i in range(NUM_TARGETS):\n#                 mask = (target_name == i)\n#                 if mask.any(): biomass_out[mask] = all_biomass[mask, i]\n\n#         return {\n#             \"biomass\": biomass_out, \"all_biomass\": all_biomass,\n#             \"height\": self.height_head(fused).squeeze(1),\n#             \"green\": self.green_head(fused), \"species\": self.species_head(fused),\n#             \"state\": self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASET\n# # =====================================================\n# class BiomassDataset(Dataset):\n#     def __init__(self, df, is_test=False):\n#         self.df = df.reset_index(drop=True)\n#         self.is_test = is_test\n\n#     def __len__(self): return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         w, h = img.size\n        \n#         left_t = transform(img.crop((0, 0, w//2, h)))\n#         right_t = transform(img.crop((w//2, 0, w, h)))\n#         full_t = transform(img)\n\n#         data = {\"left\": left_t, \"right\": right_t, \"full\": full_t}\n#         if not self.is_test:\n#             data.update({\n#                 \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#                 \"height\": torch.tensor(r[\"Height_Ave_cm\"], dtype=torch.float32),\n#                 \"green\": GREEN_EXTRACTOR(full_t.unsqueeze(0)).squeeze(0),\n#                 \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#                 \"species\": torch.tensor(r[\"Species_idx\"], dtype=torch.long),\n#                 \"state\": torch.tensor(r[\"State_idx\"], dtype=torch.long),\n#             })\n#         return data\n\n# # =====================================================\n# # TRAINING FUNCTION WITH CHECKPOINTS\n# # =====================================================\n# def train_model():\n#     train_loader = DataLoader(BiomassDataset(train_df), batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n#     model = HierarchicalBiomassModel().to(DEVICE)\n#     optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)\n\n#     print(f\"Starting training for {EPOCHS} epochs...\")\n\n#     for e in range(EPOCHS):\n#         model.train()\n#         total_loss = 0.0\n#         for b in train_loader:\n#             optimizer.zero_grad()\n#             out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE), b[\"full\"].to(DEVICE), b[\"target_name\"].to(DEVICE))\n            \n#             loss = (LOSS_W[\"biomass\"] * F.mse_loss(out[\"biomass\"], b[\"target\"].to(DEVICE)) +\n#                     LOSS_W[\"height\"] * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#                     LOSS_W[\"green\"] * F.mse_loss(out[\"green\"], b[\"green\"].to(DEVICE)) +\n#                     LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#                     LOSS_W[\"state\"] * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE)))\n            \n#             loss.backward(); optimizer.step(); total_loss += loss.item()\n        \n#         print(f\"Epoch {e+1}/{EPOCHS} | Loss: {total_loss/len(train_loader):.4f}\")\n        \n#         # --- Checkpoint Saving Logic ---\n#         if (e + 1) % SAVE_INTERVAL == 0:\n#             ckpt_path = f\"hierarchical_model_epoch_{e+1}.pth\"\n#             torch.save(model.state_dict(), ckpt_path)\n#             print(f\"💾 Checkpoint saved: {ckpt_path}\")\n\n#     return model\n\n# # =====================================================\n# # INFERENCE FUNCTION\n# # =====================================================\n# def run_inference(model):\n#     unique_test_df = test_df.drop_duplicates('image_path').reset_index(drop=True)\n#     test_loader = DataLoader(BiomassDataset(unique_test_df, is_test=True), batch_size=BATCH_SIZE, shuffle=False)\n    \n#     model.eval()\n#     results = {}\n#     with torch.no_grad():\n#         for i, b in enumerate(test_loader):\n#             out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE), b[\"full\"].to(DEVICE))\n#             preds = out[\"all_biomass\"].cpu().numpy()\n#             batch_paths = unique_test_df.iloc[i*BATCH_SIZE : i*BATCH_SIZE + len(preds)][\"image_path\"].values\n#             for path, p_val in zip(batch_paths, preds):\n#                 results[path] = p_val\n\n#     submission = []\n#     for _, row in test_df.iterrows():\n#         path, t_name = row['image_path'], row['target_name']\n#         pred_value = results[path][TARGET_NAME_TO_ID[t_name]]\n#         submission.append({\"sample_id\": row[\"sample_id\"], \"target\": max(0.0, pred_value)})\n    \n#     pd.DataFrame(submission).to_csv(\"submission.csv\", index=False)\n#     print(\"✅ Submission saved to submission.csv\")\n\n# # =====================================================\n# # RUN\n# # =====================================================\n\n# model = HierarchicalBiomassModel().to(DEVICE)\n# optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)\n\n# # 2. LOAD THE CHECKPOINT\n# checkpoint_path = \"/kaggle/input/heirarichal/pytorch/default/1/hierarchical_model_epoch_50 (2).pth\" # Ensure this file is in your working directory\n\n# if os.path.exists(checkpoint_path):\n#     print(f\"Loading weights from {checkpoint_path}...\")\n#     state_dict = torch.load(checkpoint_path, map_location=DEVICE)\n#     model.load_state_dict(state_dict)\n#     print(\"Weights loaded successfully!\")\n# else:\n#     print(\"Warning: Checkpoint not found. Starting from scratch.\")\n# # trained_model = train_model()\n# run_inference(model)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.955410Z","iopub.execute_input":"2026-01-15T14:30:14.955880Z","iopub.status.idle":"2026-01-15T14:30:14.988955Z","shell.execute_reply.started":"2026-01-15T14:30:14.955840Z","shell.execute_reply":"2026-01-15T14:30:14.987788Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # 1. Initialize Model and Optimizer as before\n# model = HierarchicalBiomassModel().to(DEVICE)\n# optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)\n\n# # 2. LOAD THE CHECKPOINT\n# checkpoint_path = \"/kaggle/input/heirarichal/pytorch/default/1/hierarchical_model_epoch_50 (2).pth\" # Ensure this file is in your working directory\n\n# if os.path.exists(checkpoint_path):\n#     print(f\"Loading weights from {checkpoint_path}...\")\n#     state_dict = torch.load(checkpoint_path, map_location=DEVICE)\n#     model.load_state_dict(state_dict)\n#     print(\"Weights loaded successfully!\")\n# else:\n#     print(\"Warning: Checkpoint not found. Starting from scratch.\")\n\n# # 3. SETUP DATA LOADER\n# train_loader = DataLoader(BiomassDataset(train_df), batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n\n# # 4. CONTINUE TRAINING LOOP\n# START_EPOCH = 50\n# EXTENSION = 50 \n\n# for e in range(START_EPOCH, START_EPOCH + EXTENSION):\n#     model.train()\n#     total_loss = 0.0\n#     for b in train_loader:\n#         optimizer.zero_grad()\n#         out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE), b[\"full\"].to(DEVICE), b[\"target_name\"].to(DEVICE))\n        \n#         loss = (LOSS_W[\"biomass\"] * F.mse_loss(out[\"biomass\"], b[\"target\"].to(DEVICE)) +\n#                 LOSS_W[\"height\"] * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#                 LOSS_W[\"green\"] * F.mse_loss(out[\"green\"], b[\"green\"].to(DEVICE)) +\n#                 LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#                 LOSS_W[\"state\"] * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE)))\n        \n#         loss.backward()\n#         optimizer.step()\n#         total_loss += loss.item()\n    \n#     print(f\"Epoch {e+1}/{START_EPOCH + EXTENSION} | Loss: {total_loss/len(train_loader):.4f}\")\n    \n#     # Save every 10 epochs\n#     if (e + 1) % SAVE_INTERVAL == 0:\n#         ckpt_path = f\"hierarchical_model_epoch_{e+1}.pth\"\n#         torch.save(model.state_dict(), ckpt_path)\n#         print(f\"💾 Checkpoint saved: {ckpt_path}\")\n\n# # Run inference with the extended model\n# run_inference(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:14.990362Z","iopub.execute_input":"2026-01-15T14:30:14.990730Z","iopub.status.idle":"2026-01-15T14:30:15.025511Z","shell.execute_reply.started":"2026-01-15T14:30:14.990691Z","shell.execute_reply":"2026-01-15T14:30:15.024271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # FULL BIOMASS PIPELINE – DINO ViT-S/16 (TIMM, SINGLE BLOCK)\n# # Domain-correct: 3 learned heads + 2 derived targets\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n# import timm\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_SIZE   = 224\n# BATCH_SIZE = 8\n# LR         = 3e-4\n# EPOCHS     = 40\n# SAVE_EVERY = 10\n\n# TRAIN_CSV = \"/kaggle/input/csiro-biomass/train.csv\"\n# TEST_CSV  = \"/kaggle/input/csiro-biomass/test.csv\"\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # LOSS WEIGHTS\n# # =====================================================\n# LOSS_W = {\n#     \"biomass\": 1.0,\n#     \"height\":  0.2,\n#     \"aux\":     0.2,\n#     \"species\": 0.1,\n#     \"state\":   0.1,\n# }\n\n# # =====================================================\n# # TARGET DEFINITIONS\n# # =====================================================\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Green_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Clover_g\": 2,\n#     \"Dry_Total_g\": 3,   # derived\n#     \"GDM_g\": 4,         # derived\n# }\n\n# LEARNED_TARGETS = [\n#     \"Dry_Green_g\",\n#     \"Dry_Dead_g\",\n#     \"Dry_Clover_g\",\n# ]\n\n# # =====================================================\n# # LOAD DATA\n# # =====================================================\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# species_encoder = LabelEncoder()\n# state_encoder   = LabelEncoder()\n\n# train_df[\"Species\"] = species_encoder.fit_transform(train_df[\"Species\"])\n# train_df[\"State\"]   = state_encoder.fit_transform(train_df[\"State\"])\n\n# NUM_SPECIES = train_df[\"Species\"].nunique()\n# NUM_STATES  = train_df[\"State\"].nunique()\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n# # =====================================================\n# # PATCH TILING\n# # =====================================================\n# def patch_to_tiles(x, tile_h, tile_w):\n#     B, H, W, D = x.shape\n#     x = x.view(\n#         B,\n#         H // tile_h, tile_h,\n#         W // tile_w, tile_w,\n#         D\n#     )\n#     x = x.mean(dim=(2, 4))\n#     return x.view(B, -1, D)\n\n# # =====================================================\n# # VEGETATION INDEX (ExG)\n# # =====================================================\n# class VegetationIndex(nn.Module):\n#     def forward(self, img):\n#         R, G, B = img[:, 0], img[:, 1], img[:, 2]\n#         exg = 2 * G - R - B\n#         exg = F.interpolate(\n#             exg.unsqueeze(1),\n#             size=(16, 16),\n#             mode=\"bilinear\",\n#             align_corners=False\n#         )\n#         return exg.squeeze(1)\n\n# # =====================================================\n# # CROSS ATTENTION\n# # =====================================================\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # BACKBONE: DINO ViT-S/16 (TIMM)\n# # =====================================================\n# class DinoViTSmall(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = timm.create_model(\n#             \"vit_small_patch16_224\",\n#             pretrained=False,\n#             num_classes=0\n#         )\n#         state = torch.load(DINO_CKPT, map_location=\"cpu\")\n#         self.model.load_state_dict(state, strict=False)\n\n#         for p in self.model.parameters():\n#             p.requires_grad = False\n\n#         self.embed_dim = 384\n\n#     def forward(self, x):\n#         tokens = self.model.forward_features(x)[:, 1:, :]   # [B,196,384]\n#         tokens = tokens.view(x.size(0), 14, 14, self.embed_dim)\n\n#         tokens = F.interpolate(\n#             tokens.permute(0, 3, 1, 2),\n#             size=(16, 16),\n#             mode=\"bilinear\",\n#             align_corners=False\n#         ).permute(0, 2, 3, 1)\n\n#         return tokens\n\n# # =====================================================\n# # BIOMASS MODEL\n# # =====================================================\n# class BiomassModel(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n\n#         self.backbone = DinoViTSmall()\n#         embed_dim = self.backbone.embed_dim\n\n#         self.veg = VegetationIndex()\n\n#         self.local_proj = nn.Sequential(\n#             nn.Linear(embed_dim + 1, embed_dim),\n#             nn.ReLU(),\n#             nn.LayerNorm(embed_dim),\n#         )\n#         self.global_proj = nn.Sequential(\n#             nn.Linear(embed_dim + 1, embed_dim),\n#             nn.ReLU(),\n#             nn.LayerNorm(embed_dim),\n#         )\n\n#         self.local_attn  = CrossAttention(embed_dim, 6)\n#         self.global_attn = CrossAttention(embed_dim, 6)\n\n#         self.token_pool = nn.Linear(embed_dim, 1)\n\n#         fused_dim = embed_dim * 3\n\n#         self.biomass_heads = nn.ModuleDict({\n#             name: nn.Sequential(\n#                 nn.Linear(fused_dim, 512),\n#                 nn.ReLU(),\n#                 nn.Linear(512, 1),\n#             )\n#             for name in LEARNED_TARGETS\n#         })\n\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, NUM_SPECIES)\n#         self.state_head   = nn.Linear(fused_dim, NUM_STATES)\n\n#     def forward(self, image):\n#         tokens = self.backbone(image)\n\n#         local_tokens  = patch_to_tiles(tokens, 2, 2)\n#         global_tokens = patch_to_tiles(tokens, 8, 8)\n\n#         exg = self.veg(image).unsqueeze(-1)\n#         exg_local  = patch_to_tiles(exg, 2, 2)\n#         exg_global = patch_to_tiles(exg, 8, 8)\n\n#         local_tokens  = self.local_proj(torch.cat([local_tokens, exg_local], dim=-1))\n#         global_tokens = self.global_proj(torch.cat([global_tokens, exg_global], dim=-1))\n\n#         global_tokens = self.global_attn(global_tokens, local_tokens)\n#         local_tokens  = self.local_attn(local_tokens, global_tokens)\n\n#         all_tokens = torch.cat([global_tokens, local_tokens], dim=1)\n\n#         w = F.softmax(self.token_pool(all_tokens), dim=1)\n#         cls_like = (w * all_tokens).sum(dim=1)\n#         avg_pool = all_tokens.mean(dim=1)\n#         max_pool = all_tokens.max(dim=1)[0]\n\n#         fused = torch.cat([cls_like, avg_pool, max_pool], dim=1)\n\n#         pred = {k: self.biomass_heads[k](fused).squeeze(1) for k in LEARNED_TARGETS}\n#         pred[\"Dry_Total_g\"] = pred[\"Dry_Green_g\"] + pred[\"Dry_Dead_g\"] + pred[\"Dry_Clover_g\"]\n#         pred[\"GDM_g\"] = pred[\"Dry_Green_g\"] + pred[\"Dry_Clover_g\"]\n\n#         return {\n#             \"biomass_all\": pred,\n#             \"height\": self.height_head(fused).squeeze(1),\n#             \"aux\": self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\": self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASETS\n# # =====================================================\n# class BiomassDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         img = transform(img)\n#         return {\n#             \"image\": img,\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"], dtype=torch.long),\n#             \"state\": torch.tensor(r[\"State\"], dtype=torch.long),\n#             \"height\": torch.tensor(r.get(\"Height_Ave_cm\", 0.0), dtype=torch.float32),\n#             \"aux\": torch.tensor(r.get(\"Pre_GSHH_NDVI\", 0.0), dtype=torch.float32),\n#         }\n\n#     def __len__(self):\n#         return len(self.df)\n\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         img = transform(img)\n#         return {\n#             \"image\": img,\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"id\": r[\"sample_id\"],\n#         }\n\n#     def __len__(self):\n#         return len(self.df)\n\n# # =====================================================\n# # TRAINING\n# # =====================================================\n# def train_one_epoch(model, loader, optimizer):\n#     model.train()\n#     total = 0.0\n\n#     for b in loader:\n#         img = b[\"image\"].to(DEVICE)\n#         tgt = b[\"target\"].to(DEVICE)\n#         tn  = b[\"target_name\"].to(DEVICE)\n\n#         out = model(img)\n\n#         biomass_loss, count = 0.0, 0\n#         for i in range(len(tn)):\n#             name = list(TARGET_NAME_TO_ID.keys())[tn[i].item()]\n#             if name in LEARNED_TARGETS:\n#                 biomass_loss += F.mse_loss(out[\"biomass_all\"][name][i], tgt[i])\n#                 count += 1\n#         biomass_loss /= max(count, 1)\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * biomass_loss +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n#         total += loss.item()\n\n#     return total / len(loader)\n\n# # =====================================================\n# # INFERENCE\n# # =====================================================\n# @torch.no_grad()\n# def generate_submission(model, loader, out_path=\"submission.csv\"):\n#     model.eval()\n#     ids, preds = [], []\n\n#     for b in loader:\n#         out = model(b[\"image\"].to(DEVICE))\n#         for i in range(len(b[\"id\"])):\n#             name = list(TARGET_NAME_TO_ID.keys())[b[\"target_name\"][i].item()]\n#             preds.append(torch.clamp(out[\"biomass_all\"][name][i], min=0).item())\n#             ids.append(b[\"id\"][i])\n\n#     pd.DataFrame({\"sample_id\": ids, \"target\": preds}).to_csv(out_path, index=False)\n#     print(f\"✅ Saved {out_path}\")\n\n# # =====================================================\n# # RUN\n# # =====================================================\n# model = BiomassModel().to(DEVICE)\n\n# check=  torch.load(\"/kaggle/input/biomass-model/pytorch/default/1/biomass_vits16_epoch_40.pth\", map_location = DEVICE);\n# model.load_state_dict(check)\n\n# optimizer = torch.optim.AdamW(\n#     filter(lambda p: p.requires_grad, model.parameters()),\n#     lr=LR\n# )\n\n# train_loader = DataLoader(\n#     BiomassDataset(train_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# test_loader = DataLoader(\n#     BiomassTestDataset(test_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # for epoch in range(EPOCHS):\n# #     loss = train_one_epoch(model, train_loader, optimizer)\n# #     print(f\"Epoch [{epoch+1}/{EPOCHS}] | Loss: {loss:.4f}\")\n\n# #     if (epoch + 1) % SAVE_EVERY == 0:\n# #         torch.save(model.state_dict(), f\"biomass_vits16_epoch_{epoch+1}.pth\")\n\n# generate_submission(model, test_loader)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:15.026950Z","iopub.execute_input":"2026-01-15T14:30:15.027398Z","iopub.status.idle":"2026-01-15T14:30:15.064941Z","shell.execute_reply.started":"2026-01-15T14:30:15.027359Z","shell.execute_reply":"2026-01-15T14:30:15.063236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # FULL BIOMASS PIPELINE – DINO ViT-S/16\n# # LEFT–RIGHT IMAGE SPLIT, NO TILING\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n# import timm\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_SIZE   = 224\n# BATCH_SIZE = 8\n# LR         = 3e-4\n# EPOCHS     = 40\n# SAVE_EVERY = 10\n\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# TRAIN_CSV = f\"{DATA_ROOT}/train.csv\"\n# TEST_CSV  = f\"{DATA_ROOT}/test.csv\"\n\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # LOSS WEIGHTS\n# # =====================================================\n\n# LOSS_W = {\n#     \"biomass\": 1.0,\n#     \"height\":  0.2,\n#     \"aux\":     0.2,\n#     \"species\": 0.1,\n#     \"state\":   0.1,\n# }\n\n# # =====================================================\n# # TARGET DEFINITIONS\n# # =====================================================\n\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Green_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Clover_g\": 2,\n#     \"Dry_Total_g\": 3,   # derived\n#     \"GDM_g\": 4,         # derived\n# }\n\n# LEARNED_TARGETS = [\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\"]\n\n# # =====================================================\n# # LOAD DATA\n# # =====================================================\n\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# species_enc = LabelEncoder()\n# state_enc   = LabelEncoder()\n\n# train_df[\"Species\"] = species_enc.fit_transform(train_df[\"Species\"])\n# train_df[\"State\"]   = state_enc.fit_transform(train_df[\"State\"])\n\n# NUM_SPECIES = train_df[\"Species\"].nunique()\n# NUM_STATES  = train_df[\"State\"].nunique()\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n# # =====================================================\n# # VEGETATION INDEX (ExG)\n# # =====================================================\n\n# class VegetationIndex(nn.Module):\n#     def forward(self, img):\n#         R, G, B = img[:, 0], img[:, 1], img[:, 2]\n#         exg = 2 * G - R - B\n#         exg = F.interpolate(\n#             exg.unsqueeze(1),\n#             size=(16, 16),\n#             mode=\"bilinear\",\n#             align_corners=False\n#         )\n#         return exg.squeeze(1)\n\n# # =====================================================\n# # CROSS ATTENTION\n# # =====================================================\n\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads=6):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# # =====================================================\n# # DINO ViT-S/16 BACKBONE\n# # =====================================================\n\n# class DinoViTSmall(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = timm.create_model(\n#             \"vit_small_patch16_224\",\n#             pretrained=False,\n#             num_classes=0\n#         )\n\n#         state = torch.load(DINO_CKPT, map_location=\"cpu\")\n#         self.model.load_state_dict(state, strict=False)\n\n#         for p in self.model.parameters():\n#             p.requires_grad = False\n\n#         self.embed_dim = 384\n\n#     def forward(self, x):\n#         tokens = self.model.forward_features(x)[:, 1:, :]  # [B,196,384]\n#         tokens = tokens.view(x.size(0), 14, 14, self.embed_dim)\n#         tokens = F.interpolate(\n#             tokens.permute(0, 3, 1, 2),\n#             size=(16, 16),\n#             mode=\"bilinear\",\n#             align_corners=False\n#         ).permute(0, 2, 3, 1)\n#         return tokens  # [B,16,16,384]\n\n# # =====================================================\n# # BIOMASS MODEL (LEFT–RIGHT, NO TILING)\n# # =====================================================\n\n# class BiomassModel(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n\n#         self.backbone = DinoViTSmall()\n#         D = self.backbone.embed_dim\n\n#         self.veg = VegetationIndex()\n\n#         self.token_proj = nn.Sequential(\n#             nn.Linear(D + 1, D),\n#             nn.ReLU(),\n#             nn.LayerNorm(D),\n#         )\n\n#         self.lr_attn = CrossAttention(D, 6)\n#         self.token_pool = nn.Linear(D, 1)\n\n#         fused_dim = D * 3\n\n#         self.biomass_heads = nn.ModuleDict({\n#             name: nn.Sequential(\n#                 nn.Linear(fused_dim, 512),\n#                 nn.ReLU(),\n#                 nn.Linear(512, 1),\n#             )\n#             for name in LEARNED_TARGETS\n#         })\n\n#         self.height_head  = nn.Linear(fused_dim, 1)\n#         self.aux_head     = nn.Linear(fused_dim, 1)\n#         self.species_head = nn.Linear(fused_dim, NUM_SPECIES)\n#         self.state_head   = nn.Linear(fused_dim, NUM_STATES)\n\n#     def encode_side(self, img):\n#         tokens = self.backbone(img)             # [B,16,16,D]\n#         B, H, W, D = tokens.shape\n#         tokens = tokens.view(B, H * W, D)       # [B,256,D]\n#         exg = self.veg(img).view(B, H * W, 1)   # [B,256,1]\n#         return self.token_proj(torch.cat([tokens, exg], dim=-1))\n\n#     def forward(self, left, right):\n#         left_tokens  = self.encode_side(left)\n#         right_tokens = self.encode_side(right)\n\n#         left_tokens  = self.lr_attn(left_tokens, right_tokens)\n#         right_tokens = self.lr_attn(right_tokens, left_tokens)\n\n#         tokens = torch.cat([left_tokens, right_tokens], dim=1)  # [B,512,D]\n\n#         w = F.softmax(self.token_pool(tokens), dim=1)\n#         cls_like = (w * tokens).sum(dim=1)\n#         avg_pool = tokens.mean(dim=1)\n#         max_pool = tokens.max(dim=1)[0]\n\n#         fused = torch.cat([cls_like, avg_pool, max_pool], dim=1)\n\n#         pred = {k: self.biomass_heads[k](fused).squeeze(1)\n#                 for k in LEARNED_TARGETS}\n\n#         pred[\"Dry_Total_g\"] = pred[\"Dry_Green_g\"] + pred[\"Dry_Dead_g\"] + pred[\"Dry_Clover_g\"]\n#         pred[\"GDM_g\"] = pred[\"Dry_Green_g\"] + pred[\"Dry_Clover_g\"]\n\n#         return {\n#             \"biomass_all\": pred,\n#             \"height\": self.height_head(fused).squeeze(1),\n#             \"aux\": self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\": self.state_head(fused),\n#         }\n\n# # =====================================================\n# # DATASET\n# # =====================================================\n\n# class BiomassDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         w, h = img.size\n\n#         left  = transform(img.crop((0, 0, w // 2, h)))\n#         right = transform(img.crop((w // 2, 0, w, h)))\n\n#         return {\n#             \"left\": left,\n#             \"right\": right,\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"], dtype=torch.long),\n#             \"state\": torch.tensor(r[\"State\"], dtype=torch.long),\n#             \"height\": torch.tensor(r.get(\"Height_Ave_cm\", 0.0), dtype=torch.float32),\n#             \"aux\": torch.tensor(r.get(\"Pre_GSHH_NDVI\", 0.0), dtype=torch.float32),\n#         }\n\n#     def __len__(self):\n#         return len(self.df)\n\n# # =====================================================\n# # TRAINING\n# # =====================================================\n\n# def train_one_epoch(model, loader, optimizer):\n#     model.train()\n#     total = 0.0\n\n#     for b in loader:\n#         out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE))\n\n#         biomass_loss, count = 0.0, 0\n#         for i in range(len(b[\"target_name\"])):\n#             name = list(TARGET_NAME_TO_ID.keys())[b[\"target_name\"][i].item()]\n#             if name in LEARNED_TARGETS:\n#                 biomass_loss += F.mse_loss(\n#                     out[\"biomass_all\"][name][i],\n#                     b[\"target\"][i].to(DEVICE)\n#                 )\n#                 count += 1\n#         biomass_loss /= max(count, 1)\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * biomass_loss +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         optimizer.zero_grad()\n#         loss.backward()\n#         optimizer.step()\n#         total += loss.item()\n\n#     return total / len(loader)\n\n# # =====================================================\n# # RUN\n# # =====================================================\n\n# model = BiomassModel().to(DEVICE)\n# optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)\n\n# train_loader = DataLoader(\n#     BiomassDataset(train_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # for epoch in range(EPOCHS):\n# #     loss = train_one_epoch(model, train_loader, optimizer)\n# #     print(f\"Epoch [{epoch+1}/{EPOCHS}] | Loss: {loss:.4f}\")\n\n# #     if (epoch + 1) % SAVE_EVERY == 0:\n# #         torch.save(model.state_dict(), f\"biomass_lr_notiling_epoch_{epoch+1}.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-15T14:30:49.205799Z","iopub.execute_input":"2026-01-15T14:30:49.206195Z","iopub.status.idle":"2026-01-15T14:30:49.829601Z","shell.execute_reply.started":"2026-01-15T14:30:49.206153Z","shell.execute_reply":"2026-01-15T14:30:49.828601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # FULL BIOMASS PIPELINE – MULTI-TASK LEARNING\n# # DINO ViT-S/16 | LEFT–RIGHT | GREEN + NDVI SUPERVISION\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn as nn\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import pandas as pd\n# from sklearn.preprocessing import LabelEncoder\n# import timm\n# import math\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# IMG_SIZE   = 224\n# BATCH_SIZE = 8\n# LR         = 3e-4\n# EPOCHS     = 40\n\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# TRAIN_CSV = f\"{DATA_ROOT}/train.csv\"\n# TEST_CSV  = f\"{DATA_ROOT}/test.csv\"\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n# # =====================================================\n# # LOSS WEIGHTS (NON-EQUAL, PRINCIPLED)\n# # =====================================================\n\n# LOSS_W = {\n#     \"biomass\": 1.5,\n#     \"green\":   0.3,\n#     \"ndvi\":    0.3,\n#     \"corr\":    0.2,\n#     \"contrast\":0.1,\n#     \"height\":  0.15,\n#     \"aux\":     0.15,\n#     \"species\": 0.05,\n#     \"state\":   0.07,\n# }\n\n# # =====================================================\n# # TARGET DEFINITIONS\n# # =====================================================\n\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Green_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Clover_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n\n# LEARNED_TARGETS = [\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\"]\n\n# ID_TO_TARGET = {v: k for k, v in TARGET_NAME_TO_ID.items()}\n\n# # =====================================================\n# # DATA\n# # =====================================================\n\n# train_df = pd.read_csv(TRAIN_CSV)\n# test_df  = pd.read_csv(TEST_CSV)\n\n# species_enc = LabelEncoder()\n# state_enc   = LabelEncoder()\n\n# train_df[\"Species\"] = species_enc.fit_transform(train_df[\"Species\"])\n# train_df[\"State\"]   = state_enc.fit_transform(train_df[\"State\"])\n\n# NUM_SPECIES = train_df[\"Species\"].nunique()\n# NUM_STATES  = train_df[\"State\"].nunique()\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n# # =====================================================\n# # GREEN / NDVI TARGETS\n# # =====================================================\n\n# def compute_exg(img):\n#     R, G, B = img[:, 0], img[:, 1], img[:, 2]\n#     exg = 2 * G - R - B\n#     return exg.mean()\n\n# def compute_ndvi_proxy(img):\n#     R, G = img[:, 0], img[:, 1]\n#     return ((G - R) / (G + R + 1e-6)).mean()\n\n# # =====================================================\n# # DATASET\n# # =====================================================\n\n# class BiomassDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         w, h = img.size\n\n#         left  = transform(img.crop((0, 0, w//2, h)))\n#         right = transform(img.crop((w//2, 0, w, h)))\n#         full  = transform(img)\n\n#         return {\n#             \"left\": left,\n#             \"right\": right,\n#             \"green\": compute_exg(full),\n#             \"ndvi\": compute_ndvi_proxy(full),\n#             \"target\": torch.tensor(r[\"target\"], dtype=torch.float32),\n#             \"target_name\": torch.tensor(TARGET_NAME_TO_ID[r[\"target_name\"]]),\n#             \"species\": torch.tensor(r[\"Species\"], dtype=torch.long),\n#             \"state\": torch.tensor(r[\"State\"], dtype=torch.long),\n#             \"height\": torch.tensor(r.get(\"Height_Ave_cm\", 0.0), dtype=torch.float32),\n#             \"aux\": torch.tensor(r.get(\"Pre_GSHH_NDVI\", 0.0), dtype=torch.float32),\n#             \"image_path\": r[\"image_path\"],\n#         }\n\n# # =====================================================\n# # MODEL COMPONENTS\n# # =====================================================\n\n# class CrossAttention(nn.Module):\n#     def __init__(self, dim, heads=6):\n#         super().__init__()\n#         self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)\n#         self.norm = nn.LayerNorm(dim)\n\n#     def forward(self, q, kv):\n#         out, _ = self.attn(q, kv, kv)\n#         return self.norm(out + q)\n\n# class DinoViTSmall(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.model = timm.create_model(\"vit_small_patch16_224\", pretrained=False, num_classes=0)\n#         self.model.load_state_dict(torch.load(DINO_CKPT, map_location=\"cpu\"), strict=False)\n#         for p in self.model.parameters():\n#             p.requires_grad = False\n#         self.embed_dim = 384\n\n#     def forward(self, x):\n#         t = self.model.forward_features(x)[:, 1:, :]\n#         t = t.view(x.size(0), 14, 14, self.embed_dim)\n#         t = F.interpolate(t.permute(0,3,1,2), size=(16,16), mode=\"bilinear\", align_corners=False)\n#         return t.permute(0,2,3,1)\n\n# # =====================================================\n# # BIOMASS MODEL\n# # =====================================================\n\n# class BiomassModel(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.backbone = DinoViTSmall()\n#         D = self.backbone.embed_dim\n\n#         self.token_proj = nn.Sequential(\n#             nn.Linear(D, D),\n#             nn.ReLU(),\n#             nn.LayerNorm(D),\n#         )\n\n#         self.lr_attn = CrossAttention(D)\n#         self.token_pool = nn.Linear(D, 1)\n\n#         fused_dim = D * 3\n\n#         self.biomass_heads = nn.ModuleDict({\n#             k: nn.Sequential(nn.Linear(fused_dim, 512), nn.ReLU(), nn.Linear(512, 1))\n#             for k in LEARNED_TARGETS\n#         })\n\n#         self.green_head  = nn.Linear(fused_dim, 1)\n#         self.ndvi_head   = nn.Linear(fused_dim, 1)\n#         self.height_head = nn.Linear(fused_dim, 1)\n#         self.aux_head    = nn.Linear(fused_dim, 1)\n#         self.species_head= nn.Linear(fused_dim, NUM_SPECIES)\n#         self.state_head  = nn.Linear(fused_dim, NUM_STATES)\n\n#     def encode(self, img):\n#         t = self.backbone(img).view(img.size(0), -1, self.backbone.embed_dim)\n#         return self.token_proj(t)\n\n#     def forward(self, left, right):\n#         L = self.encode(left)\n#         R = self.encode(right)\n\n#         L2 = self.lr_attn(L, R)\n#         R2 = self.lr_attn(R, L)\n\n#         tokens = torch.cat([L2, R2], dim=1)\n\n#         w = F.softmax(self.token_pool(tokens), dim=1)\n#         fused = torch.cat([\n#             (w * tokens).sum(1),\n#             tokens.mean(1),\n#             tokens.max(1)[0]\n#         ], dim=1)\n\n#         bio = {k: self.biomass_heads[k](fused).squeeze(1) for k in LEARNED_TARGETS}\n#         bio[\"Dry_Total_g\"] = bio[\"Dry_Green_g\"] + bio[\"Dry_Dead_g\"] + bio[\"Dry_Clover_g\"]\n#         bio[\"GDM_g\"] = bio[\"Dry_Green_g\"] + bio[\"Dry_Clover_g\"]\n\n#         return {\n#             \"biomass\": bio,\n#             \"green\": self.green_head(fused).squeeze(1),\n#             \"ndvi\": self.ndvi_head(fused).squeeze(1),\n#             \"height\": self.height_head(fused).squeeze(1),\n#             \"aux\": self.aux_head(fused).squeeze(1),\n#             \"species\": self.species_head(fused),\n#             \"state\": self.state_head(fused),\n#             \"L_repr\": L2.mean(1),\n#             \"R_repr\": R2.mean(1),\n#         }\n\n# # =====================================================\n# # TRAINING\n# # =====================================================\n\n# def train_one_epoch(model, loader, opt):\n#     model.train()\n#     total = 0\n\n#     for b in loader:\n#         out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE))\n\n#         biomass_loss = 0\n#         cnt = 0\n#         for i in range(len(b[\"target_name\"])):\n#             name = ID_TO_TARGET[b[\"target_name\"][i].item()]\n#             if name in LEARNED_TARGETS:\n#                 biomass_loss += F.mse_loss(out[\"biomass\"][name][i], b[\"target\"][i].to(DEVICE))\n#                 cnt += 1\n#         biomass_loss /= max(cnt,1)\n\n#         corr = 1 - F.cosine_similarity(\n#             out[\"green\"].unsqueeze(1),\n#             out[\"biomass\"][\"Dry_Green_g\"].unsqueeze(1)\n#         ).mean()\n\n#         contrast = F.mse_loss(out[\"L_repr\"], out[\"R_repr\"])\n\n#         loss = (\n#             LOSS_W[\"biomass\"] * biomass_loss +\n#             LOSS_W[\"green\"]   * F.mse_loss(out[\"green\"], b[\"green\"].to(DEVICE)) +\n#             LOSS_W[\"ndvi\"]    * F.mse_loss(out[\"ndvi\"], b[\"ndvi\"].to(DEVICE)) +\n#             LOSS_W[\"corr\"]    * corr +\n#             LOSS_W[\"contrast\"]* contrast +\n#             LOSS_W[\"height\"]  * F.mse_loss(out[\"height\"], b[\"height\"].to(DEVICE)) +\n#             LOSS_W[\"aux\"]     * F.mse_loss(out[\"aux\"], b[\"aux\"].to(DEVICE)) +\n#             LOSS_W[\"species\"] * F.cross_entropy(out[\"species\"], b[\"species\"].to(DEVICE)) +\n#             LOSS_W[\"state\"]   * F.cross_entropy(out[\"state\"], b[\"state\"].to(DEVICE))\n#         )\n\n#         opt.zero_grad()\n#         loss.backward()\n#         opt.step()\n#         total += loss.item()\n\n#     return total / len(loader)\n\n# # =====================================================\n# # INFERENCE (DEDUP IMAGE PATHS)\n# # =====================================================\n\n# def run_inference(model):\n#     unique_df = test_df.drop_duplicates(\"image_path\").reset_index(drop=True)\n#     loader = DataLoader(BiomassDataset(unique_df), batch_size=BATCH_SIZE, shuffle=False)\n\n#     model.eval()\n#     cache = {}\n\n#     with torch.no_grad():\n#         for b in loader:\n#             out = model(b[\"left\"].to(DEVICE), b[\"right\"].to(DEVICE))\n#             for i, p in enumerate(b[\"image_path\"]):\n#                 cache[p] = torch.stack([out[\"biomass\"][k][i] for k in TARGET_NAME_TO_ID]).cpu().numpy()\n\n#     rows = []\n#     for _, r in test_df.iterrows():\n#         val = cache[r[\"image_path\"]][TARGET_NAME_TO_ID[r[\"target_name\"]]]\n#         rows.append({\"sample_id\": r[\"sample_id\"], \"target\": max(0.0, float(val))})\n\n#     pd.DataFrame(rows).to_csv(\"submission.csv\", index=False)\n#     print(\"✅ submission.csv saved\")\n\n\n# model = BiomassModel().to(DEVICE)\n# optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)\n\n# train_loader = DataLoader(\n#     BiomassDataset(train_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=True,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# # for epoch in range(EPOCHS):\n# #     loss = train_one_epoch(model, train_loader, optimizer)\n# #     print(f\"Epoch [{epoch+1}/{EPOCHS}] | Loss: {loss:.4f}\")\n\n# #     if (epoch + 1) % 10 == 0:\n# #         torch.save(model.state_dict(), f\"biomass_notiling_epoch_{epoch+1}.pth\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # INFERENCE SCRIPT – BIOMASS MODEL (LEFT–RIGHT, NO TILING)\n# # =====================================================\n\n# import os\n# import torch\n# import torch.nn.functional as F\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# from PIL import Image\n# import pandas as pd\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# IMG_SIZE = 224\n# BATCH_SIZE = 8\n\n# DATA_ROOT = \"/kaggle/input/csiro-biomass\"\n# TEST_CSV  = f\"{DATA_ROOT}/test.csv\"\n\n# CHECKPOINT_PATH = \"/kaggle/input/dino-bst/pytorch/default/1/biomass_lr_notiling_epoch_30.pth\"  # 👈 change if needed\n# OUT_CSV = \"submission.csv\"\n\n# # =====================================================\n# # TARGET DEFINITIONS (MUST MATCH TRAINING)\n# # =====================================================\n\n# TARGET_NAME_TO_ID = {\n#     \"Dry_Green_g\": 0,\n#     \"Dry_Dead_g\": 1,\n#     \"Dry_Clover_g\": 2,\n#     \"Dry_Total_g\": 3,\n#     \"GDM_g\": 4,\n# }\n\n# LEARNED_TARGETS = [\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\"]\n\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n\n# transform = transforms.Compose([\n#     transforms.Resize((IMG_SIZE, IMG_SIZE)),\n#     transforms.ToTensor(),\n#     transforms.Normalize(\n#         mean=(0.485, 0.456, 0.406),\n#         std=(0.229, 0.224, 0.225),\n#     ),\n# ])\n\n# # =====================================================\n# # TEST DATASET (LEFT–RIGHT)\n# # =====================================================\n\n# class BiomassTestDataset(Dataset):\n#     def __init__(self, df):\n#         self.df = df.reset_index(drop=True)\n\n#     def __len__(self):\n#         return len(self.df)\n\n#     def __getitem__(self, idx):\n#         r = self.df.iloc[idx]\n#         img = Image.open(os.path.join(DATA_ROOT, r[\"image_path\"])).convert(\"RGB\")\n#         w, h = img.size\n\n#         left  = transform(img.crop((0, 0, w // 2, h)))\n#         right = transform(img.crop((w // 2, 0, w, h)))\n\n#         return {\n#             \"left\": left,\n#             \"right\": right,\n#             \"target_name\": TARGET_NAME_TO_ID[r[\"target_name\"]],\n#             \"sample_id\": r[\"sample_id\"],\n#         }\n\n# # =====================================================\n# # LOAD MODEL (MUST MATCH TRAINING DEFINITION)\n# # =====================================================\n\n# # from biomass_model import BiomassModel   # 👈 OR paste model class above\n\n# model = BiomassModel().to(DEVICE)\n# state = torch.load(CHECKPOINT_PATH, map_location=DEVICE)\n# model.load_state_dict(state)\n# model.eval()\n\n# print(f\"✅ Loaded checkpoint: {CHECKPOINT_PATH}\")\n\n# # =====================================================\n# # INFERENCE\n# # =====================================================\n\n# test_df = pd.read_csv(TEST_CSV)\n# test_loader = DataLoader(\n#     BiomassTestDataset(test_df),\n#     batch_size=BATCH_SIZE,\n#     shuffle=False,\n#     num_workers=2,\n#     pin_memory=True\n# )\n\n# ids, preds = [], []\n\n# with torch.no_grad():\n#     for b in test_loader:\n#         out = model(\n#             b[\"left\"].to(DEVICE),\n#             b[\"right\"].to(DEVICE)\n#         )\n\n#         for i in range(len(b[\"sample_id\"])):\n#             name = list(TARGET_NAME_TO_ID.keys())[b[\"target_name\"][i]]\n#             print(name)\n#             value = out[\"biomass_all\"][name][i]\n#             preds.append(torch.clamp(value, min=0).item())\n#             ids.append(b[\"sample_id\"][i])\n\n# # =====================================================\n# # SAVE SUBMISSION\n# # =====================================================\n\n# pd.DataFrame({\n#     \"sample_id\": ids,\n#     \"target\": preds\n# }).to_csv(OUT_CSV, index=False)\n\n# print(f\"✅ Submission saved to {OUT_CSV}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # 0. IMPORTS & CONFIG\n# # =====================================================\n# import os, gc, cv2, math, random\n# import numpy as np\n# import pandas as pd\n# from tqdm import tqdm\n\n# import torch\n# import torch.nn as nn\n# from torch.utils.data import Dataset, DataLoader\n\n# from sklearn.model_selection import StratifiedKFold\n# from transformers import AutoModel\n\n# import albumentations as A\n# from albumentations.pytorch import ToTensorV2\n\n\n# DINO_CKPT = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n\n\n# class CONFIG:\n#     base_path = \"/kaggle/input/csiro-biomass\"\n#     train_csv = f\"{base_path}/train.csv\"\n#     test_csv  = f\"{base_path}/test.csv\"\n#     data_dir  = base_path\n\n#     img_size = 512\n#     batch_size = 8\n#     epochs = 10\n#     lr = 1e-3\n#     device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n#     n_folds = 5\n#     seed = 42\n\n#     targets_3 = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\"]\n#     target_cols = [\n#         \"Dry_Clover_g\",\n#         \"Dry_Dead_g\",\n#         \"Dry_Green_g\",\n#         \"Dry_Total_g\",\n#         \"GDM_g\"\n#     ]\n\n# config = CONFIG()\n# torch.manual_seed(config.seed)\n# np.random.seed(config.seed)\n\n# # =====================================================\n# # 1. DATA PREPARATION\n# # =====================================================\n# def preprocess_train():\n#     df = pd.read_csv(config.train_csv)\n\n#     df[\"image_id\"] = df[\"image_path\"].apply(\n#         lambda x: os.path.splitext(os.path.basename(x))[0]\n#     )\n\n#     pivot = df.pivot(index=\"image_id\", columns=\"target_name\", values=\"target\").reset_index()\n#     meta = df.drop_duplicates(\"image_id\").drop(columns=[\"sample_id\",\"target_name\",\"target\"])\n\n#     out = meta.merge(pivot, on=\"image_id\")\n#     return out\n\n\n# # =====================================================\n# # 2. DATASET\n# # =====================================================\n# class BiomassTwoStreamDataset(Dataset):\n#     def __init__(self, df, transforms, is_test=False):\n#         self.df = df.reset_index(drop=True)\n#         self.transforms = transforms\n#         self.is_test = is_test\n\n#         if not is_test:\n#             self.targets = df[config.targets_3].values.astype(\"float32\")\n\n#     def split_two_squares(self, img):\n#         H, W, _ = img.shape\n#         left  = img[:, :H]\n#         right = img[:, W-H:]\n#         return left, right\n\n#     def __getitem__(self, idx):\n#         row = self.df.iloc[idx]\n#         img = cv2.imread(os.path.join(config.data_dir, row[\"image_path\"]))\n#         img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n#         left, right = self.split_two_squares(img)\n\n#         left  = self.transforms(image=left)[\"image\"]\n#         right = self.transforms(image=right)[\"image\"]\n\n#         if self.is_test:\n#             return left, right\n\n#         y = torch.tensor(self.targets[idx])\n#         return left, right, y\n\n#     def __len__(self):\n#         return len(self.df)\n\n\n# # =====================================================\n# # 3. MODEL (DINOv2 SMALL)\n# # =====================================================\n# class DINOv2TwoStreamRegressor(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.backbone = timm.create_model(\"vit_small_patch16_224\", pretrained=False, num_classes=0)\n#         self.backbone.load_state_dict(torch.load(DINO_CKPT, map_location=\"cpu\"), strict=False)\n\n#         # self.backbone = AutoModel.from_pretrained(\n#         #     \"facebook/dinov2-small\"\n#         # )\n#         embed_dim = 384 # 384\n\n#         self.regressor = nn.Sequential(\n#             nn.Linear(embed_dim * 6, 512),\n#             nn.GELU(),\n#             nn.Dropout(0.1),\n#             nn.Linear(512, 3)\n#         )\n\n#         for p in self.backbone.parameters():\n#             p.requires_grad = False\n\n#     def pool_tokens(self, out):\n#         tokens = out.last_hidden_state\n#         cls = tokens[:, 0]\n#         patches = tokens[:, 1:]\n\n#         avg = patches.mean(1)\n#         mx  = patches.max(1)[0]\n\n#         return torch.cat([cls, avg, mx], dim=1)\n\n#     def forward(self, xl, xr):\n#         ol = self.backbone(pixel_values=xl)\n#         orr = self.backbone(pixel_values=xr)\n\n#         fl = self.pool_tokens(ol)\n#         fr = self.pool_tokens(orr)\n\n#         fused = torch.cat([fl, fr], dim=1)\n#         return self.regressor(fused)\n\n\n# # =====================================================\n# # 4. TRANSFORMS\n# # =====================================================\n# def get_tfms(train=True):\n#     if train:\n#         return A.Compose([\n#             A.HorizontalFlip(p=0.5),\n#             A.Rotate(15, p=0.5),\n#             A.ColorJitter(0.1,0.1,0.1,0.05,p=0.5),\n#             A.Resize(config.img_size, config.img_size),\n#             A.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),\n#             ToTensorV2()\n#         ])\n#     else:\n#         return A.Compose([\n#             A.Resize(config.img_size, config.img_size),\n#             A.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),\n#             ToTensorV2()\n#         ])\n\n\n# # =====================================================\n# # 5. TRAIN / VALIDATE\n# # =====================================================\n# def train_one_epoch(model, loader, opt, loss_fn):\n#     model.train()\n#     total = 0\n\n#     for l, r, y in loader:\n#         l,r,y = l.to(config.device), r.to(config.device), y.to(config.device)\n\n#         opt.zero_grad()\n#         pred = model(l,r)\n#         loss = loss_fn(pred, y)\n#         loss.backward()\n#         opt.step()\n\n#         total += loss.item() * l.size(0)\n\n#     return total / len(loader.dataset)\n\n\n# @torch.no_grad()\n# def infer_3(model, loader):\n#     model.eval()\n#     preds = []\n\n#     for l, r in loader:\n#         l,r = l.to(config.device), r.to(config.device)\n#         preds.append(model(l,r).cpu().numpy())\n\n#     return np.concatenate(preds)\n\n\n# # =====================================================\n# # 6. EXPAND 3 → 5 TARGETS\n# # =====================================================\n# def expand_preds(preds_3):\n#     C = np.clip(preds_3[:,0],0,None)\n#     D = np.clip(preds_3[:,1],0,None)\n#     G = np.clip(preds_3[:,2],0,None)\n\n#     GDM = G + C\n#     TOT = GDM + D\n\n#     return np.stack([C,D,G,TOT,GDM],1)\n\n\n# # =====================================================\n# # 7. TRAIN + INFERENCE (5-FOLD)\n# # =====================================================\n# train_df = preprocess_train()\n\n# skf = StratifiedKFold(\n#     n_splits=config.n_folds,\n#     shuffle=True,\n#     random_state=config.seed\n# )\n\n# test_df = pd.read_csv(config.test_csv)\n# test_unique = test_df.drop_duplicates(\"image_path\")\n\n# test_ds = BiomassTwoStreamDataset(\n#     test_unique, get_tfms(False), is_test=True\n# )\n# test_loader = DataLoader(test_ds, batch_size=16, shuffle=False)\n\n# all_preds = []\n\n# for fold, (tr, va) in enumerate(skf.split(train_df, train_df[\"State\"])):\n#     print(f\"\\nFOLD {fold+1}\")\n\n#     tr_ds = BiomassTwoStreamDataset(\n#         train_df.iloc[tr], get_tfms(True)\n#     )\n#     tr_dl = DataLoader(tr_ds, batch_size=config.batch_size, shuffle=True)\n\n#     model = DINOv2TwoStreamRegressor().to(config.device)\n#     opt = torch.optim.AdamW(model.parameters(), lr=config.lr)\n#     loss_fn = nn.MSELoss()\n\n#     for e in range(config.epochs):\n#         loss = train_one_epoch(model, tr_dl, opt, loss_fn)\n#         print(f\"Epoch {e+1}: {loss:.4f}\")\n\n#     preds_3 = infer_3(model, test_loader)\n#     all_preds.append(preds_3)\n\n#     del model\n#     gc.collect()\n#     torch.cuda.empty_cache()\n\n# # =====================================================\n# # 8. SUBMISSION\n# # =====================================================\n# avg_preds_3 = np.mean(all_preds, axis=0)\n# avg_preds_5 = expand_preds(avg_preds_3)\n\n# pred_df = pd.DataFrame(avg_preds_5, columns=config.target_cols)\n# out_df = pd.concat([test_unique, pred_df], axis=1)\n\n# long_df = out_df.melt(\n#     id_vars=\"image_path\",\n#     value_vars=config.target_cols,\n#     var_name=\"target_name\",\n#     value_name=\"target\"\n# )\n\n# submission = test_df.merge(\n#     long_df,\n#     on=[\"image_path\",\"target_name\"],\n#     how=\"left\"\n# )[[\"sample_id\",\"target\"]]\n\n# submission[\"target\"] = submission[\"target\"].fillna(0).clip(0)\n# submission.to_csv(\"submission.csv\", index=False)\n\n# print(\"✅ submission.csv written\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T10:37:41.455610Z","iopub.execute_input":"2026-01-16T10:37:41.456028Z","iopub.status.idle":"2026-01-16T10:38:32.973615Z","shell.execute_reply.started":"2026-01-16T10:37:41.455995Z","shell.execute_reply":"2026-01-16T10:38:32.972421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # =====================================================\n# # COMPLETE TRAIN + SAVE + LOAD + INFERENCE PIPELINE\n# # =====================================================\n\n# import os, gc, cv2, random\n# import numpy as np\n# import pandas as pd\n# from tqdm import tqdm\n# import timm\n\n# import torch\n# import torch.nn as nn\n# from torch.utils.data import Dataset, DataLoader\n\n# from sklearn.model_selection import StratifiedKFold\n\n# import albumentations as A\n# from albumentations.pytorch import ToTensorV2\n\n# # =====================================================\n# # CONFIG\n# # =====================================================\n# class CONFIG:\n#     base_path = \"/kaggle/input/csiro-biomass\"\n#     train_csv = f\"{base_path}/train.csv\"\n#     test_csv  = f\"{base_path}/test.csv\"\n#     data_dir  = base_path\n\n#     img_size = 224\n#     batch_size = 8\n#     epochs = 50\n#     lr = 1e-3\n\n#     device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n#     n_folds = 5\n#     seed = 42\n\n#     targets_3 = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\"]\n#     target_cols = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\", \"Dry_Total_g\", \"GDM_g\"]\n\n#     dino_ckpt = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n#     ckpt_dir = \"/kaggle/input/checkpoints/pytorch/default/1\"\n\n\n# config = CONFIG()\n# os.makedirs(config.ckpt_dir, exist_ok=True)\n\n# torch.manual_seed(config.seed)\n# np.random.seed(config.seed)\n# random.seed(config.seed)\n\n# # =====================================================\n# # DATA PREP\n# # =====================================================\n# def preprocess_train():\n#     df = pd.read_csv(config.train_csv)\n#     df[\"image_id\"] = df[\"image_path\"].apply(lambda x: os.path.splitext(os.path.basename(x))[0])\n#     pivot = df.pivot(index=\"image_id\", columns=\"target_name\", values=\"target\").reset_index()\n#     meta = df.drop_duplicates(\"image_id\").drop(columns=[\"sample_id\", \"target_name\", \"target\"])\n#     return meta.merge(pivot, on=\"image_id\")\n\n# # =====================================================\n# # DATASET\n# # =====================================================\n# class BiomassTwoStreamDataset(Dataset):\n#     def __init__(self, df, transforms, is_test=False):\n#         self.df = df.reset_index(drop=True)\n#         self.transforms = transforms\n#         self.is_test = is_test\n#         if not is_test:\n#             self.targets = self.df[config.targets_3].values.astype(\"float32\")\n\n#     def split_two_squares(self, img):\n#         H, W, _ = img.shape\n#         return img[:, :H], img[:, W-H:]\n\n#     def __getitem__(self, idx):\n#         row = self.df.iloc[idx]\n#         img = cv2.cvtColor(cv2.imread(os.path.join(config.data_dir, row[\"image_path\"])), cv2.COLOR_BGR2RGB)\n#         l, r = self.split_two_squares(img)\n#         l = self.transforms(image=l)[\"image\"]\n#         r = self.transforms(image=r)[\"image\"]\n#         if self.is_test:\n#             return l, r\n#         return l, r, torch.tensor(self.targets[idx])\n\n#     def __len__(self):\n#         return len(self.df)\n\n# # =====================================================\n# # TRANSFORMS\n# # =====================================================\n# def get_tfms(train=True):\n#     tfms = [\n#         A.Resize(config.img_size, config.img_size),\n#         A.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),\n#         ToTensorV2()\n#     ]\n#     if train:\n#         tfms.insert(0, A.HorizontalFlip(p=0.5))\n#     return A.Compose(tfms)\n\n# # =====================================================\n# # MODEL\n# # =====================================================\n# class DINOv2TwoStreamRegressor(nn.Module):\n#     def __init__(self):\n#         super().__init__()\n#         self.backbone = timm.create_model(\"vit_small_patch16_224\", pretrained=False, num_classes=0)\n#         self.backbone.load_state_dict(torch.load(config.dino_ckpt, map_location=\"cpu\"), strict=False)\n\n#         self.regressor = nn.Sequential(\n#             nn.Linear(384 * 2, 512),\n#             nn.GELU(),\n#             nn.Dropout(0.1),\n#             nn.Linear(512, 3)\n#         )\n\n#         for p in self.backbone.parameters():\n#             p.requires_grad = False\n\n#     def forward(self, xl, xr):\n#         fl = self.backbone(xl)\n#         fr = self.backbone(xr)\n#         return self.regressor(torch.cat([fl, fr], dim=1))\n\n# # =====================================================\n# # TRAIN / INFER\n# # =====================================================\n# def train_one_epoch(model, loader, opt, loss_fn):\n#     model.train()\n#     tot = 0\n#     for l, r, y in loader:\n#         l, r, y = l.to(config.device), r.to(config.device), y.to(config.device)\n#         opt.zero_grad()\n#         loss = loss_fn(model(l, r), y)\n#         loss.backward()\n#         opt.step()\n#         tot += loss.item() * l.size(0)\n#     return tot / len(loader.dataset)\n\n# @torch.no_grad()\n# def infer_from_ckpt(ckpt, loader):\n#     model = DINOv2TwoStreamRegressor().to(config.device)\n#     model.load_state_dict(torch.load(ckpt, map_location=config.device))\n#     model.eval()\n#     out = []\n#     for l, r in loader:\n#         out.append(model(l.to(config.device), r.to(config.device)).cpu().numpy())\n#     del model\n#     torch.cuda.empty_cache()\n#     return np.concatenate(out)\n\n# # =====================================================\n# # EXPAND TARGETS\n# # =====================================================\n# def expand_preds(p):\n#     C, D, G = np.clip(p[:,0],0,None), np.clip(p[:,1],0,None), np.clip(p[:,2],0,None)\n#     GDM = G + C\n#     TOT = GDM + D\n#     return np.stack([C,D,G,TOT,GDM], axis=1)\n\n# # =====================================================\n# # CV TRAINING (SAVE WEIGHTS)\n# # =====================================================\n# train_df = preprocess_train()\n# skf = StratifiedKFold(config.n_folds, shuffle=True, random_state=config.seed)\n\n# # for f, (tr, _) in enumerate(skf.split(train_df, train_df[\"State\"])):\n# #     ds = BiomassTwoStreamDataset(train_df.iloc[tr], get_tfms(True))\n# #     dl = DataLoader(ds, batch_size=config.batch_size, shuffle=True)\n# #     model = DINOv2TwoStreamRegressor().to(config.device)\n# #     opt = torch.optim.AdamW(model.parameters(), lr=config.lr)\n# #     loss_fn = nn.MSELoss()\n# #     i= 0;\n# #     for _ in range(config.epochs):\n        \n        \n# #         loss = train_one_epoch(model, dl, opt, loss_fn)\n# #         print(f\"Epoch{i} {loss} \")\n        \n# #     torch.save(model.state_dict(), f\"{config.ckpt_dir}/dino_fold_{f}.pth\")\n# #     del model\n# #     gc.collect()\n# #     torch.cuda.empty_cache()\n\n# # =====================================================\n# # INFERENCE (LOAD WEIGHTS)\n# # =====================================================\n# test_df = pd.read_csv(config.test_csv)\n# test_unique = test_df.drop_duplicates(\"image_path\")\n# test_ds = BiomassTwoStreamDataset(test_unique, get_tfms(False), is_test=True)\n# test_dl = DataLoader(test_ds, batch_size=16, shuffle=False)\n\n# all_preds = []\n# for f in range(config.n_folds):\n#     all_preds.append(infer_from_ckpt(f\"{config.ckpt_dir}/dino_fold_{f}.pth\", test_dl))\n\n# avg_preds_5 = expand_preds(np.mean(all_preds, axis=0))\n\n# # =====================================================\n# # SUBMISSION\n# # =====================================================\n# pred_df = pd.DataFrame(avg_preds_5, columns=config.target_cols)\n# out_df = pd.concat([test_unique.reset_index(drop=True), pred_df], axis=1)\n\n# long_df = out_df.melt(\"image_path\", config.target_cols, \"target_name\", \"target\")\n# submission = test_df.merge(long_df, on=[\"image_path\",\"target_name\"], how=\"left\")[[\"sample_id\",\"target\"]]\n# submission[\"target\"] = submission[\"target\"].fillna(0).clip(0)\n# submission.to_csv(\"submission.csv\", index=False)\n\n# print(\"✅ submission.csv written successfully\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T14:01:59.915138Z","iopub.execute_input":"2026-01-16T14:01:59.915500Z","iopub.status.idle":"2026-01-16T14:03:00.114547Z","shell.execute_reply.started":"2026-01-16T14:01:59.915462Z","shell.execute_reply":"2026-01-16T14:03:00.113431Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, gc, cv2, random\nimport numpy as np\nimport pandas as pd\nimport timm\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.preprocessing import LabelEncoder\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\n# =====================================================\n# CONFIG\n# =====================================================\nclass CONFIG:\n    base_path = \"/kaggle/input/csiro-biomass\"\n    train_csv = f\"{base_path}/train.csv\"\n    test_csv  = f\"{base_path}/test.csv\"\n    data_dir  = base_path\n\n    img_size = 224\n    batch_size = 8\n    epochs = 30\n    lr = 1e-3\n    n_folds = 5\n    seed = 42\n\n    device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n    targets_3 = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\"]\n    target_cols = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\", \"Dry_Total_g\", \"GDM_g\"]\n\n    dino_ckpt = \"/kaggle/input/dinoembed/pytorch/default/1/dino_deitsmall16_pretrain (1).pth\"\n    ckpt_dir = \"/kaggle/input/check/pytorch/default/1\"\n\nconfig = CONFIG()\nos.makedirs(config.ckpt_dir, exist_ok=True)\n\ntorch.manual_seed(config.seed)\nnp.random.seed(config.seed)\nrandom.seed(config.seed)\n\n# =====================================================\n# DATA PREPROCESSING\n# =====================================================\ndef preprocess_train():\n    df = pd.read_csv(config.train_csv)\n    df[\"image_id\"] = df[\"image_path\"].apply(lambda x: os.path.splitext(os.path.basename(x))[0])\n\n    pivot = df.pivot(index=\"image_id\", columns=\"target_name\", values=\"target\").reset_index()\n    meta = df.drop_duplicates(\"image_id\").drop(columns=[\"sample_id\",\"target_name\",\"target\"])\n\n    out = meta.merge(pivot, on=\"image_id\")\n\n    # encode auxiliary labels\n    state_enc = LabelEncoder()\n    species_enc = LabelEncoder()\n\n    out[\"state_id\"] = state_enc.fit_transform(out[\"State\"])\n    out[\"species_id\"] = species_enc.fit_transform(out[\"Species\"])\n    \n\n    return out, state_enc, species_enc\n\ntrain_df, state_enc, species_enc = preprocess_train()\nN_STATES   = train_df[\"state_id\"].nunique()\nN_SPECIES = train_df[\"species_id\"].nunique()\n\n# =====================================================\n# DATASET\n# =====================================================\nclass BiomassTwoStreamDataset(Dataset):\n    def __init__(self, df, transforms, is_test=False):\n        self.df = df.reset_index(drop=True)\n        self.transforms = transforms\n        self.is_test = is_test\n\n        if not is_test:\n            self.biomass = df[config.targets_3].values.astype(\"float32\")\n            self.states  = df[\"state_id\"].values.astype(\"int64\")\n            self.species = df[\"species_id\"].values.astype(\"int64\")\n            self.heights = df[\"Height_Ave_cm\"].values.astype(\"float32\")\n\n    def split_two(self, img):\n        H, W, _ = img.shape\n        return img[:, :H], img[:, W-H:]\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img = cv2.cvtColor(\n            cv2.imread(os.path.join(config.data_dir, row[\"image_path\"])),\n            cv2.COLOR_BGR2RGB\n        )\n\n        l, r = self.split_two(img)\n        l = self.transforms(image=l)[\"image\"]\n        r = self.transforms(image=r)[\"image\"]\n\n        if self.is_test:\n            return l, r\n\n        return (\n            l, r,\n            torch.tensor(self.biomass[idx]),\n            torch.tensor(self.states[idx]),\n            torch.tensor(self.species[idx]),\n            torch.tensor(self.heights[idx])\n        )\n\n    def __len__(self):\n        return len(self.df)\n\n# =====================================================\n# TRANSFORMS\n# =====================================================\ndef get_tfms(train=True):\n    tfms = [\n        A.Resize(config.img_size, config.img_size),\n        A.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),\n        ToTensorV2()\n    ]\n    if train:\n        tfms.insert(0, A.HorizontalFlip(p=0.5))\n    return A.Compose(tfms)\n\n# =====================================================\n# MODEL\n# =====================================================\nclass DINOv2MultiTask(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.backbone = timm.create_model(\n            \"vit_small_patch16_224\",\n            pretrained=False,\n            num_classes=0\n        )\n        self.backbone.load_state_dict(\n            torch.load(config.dino_ckpt, map_location=\"cpu\"),\n            strict=False\n        )\n\n        feat_dim = 384 * 2\n\n        self.biomass_head = nn.Sequential(\n            nn.Linear(feat_dim, 512),\n            nn.GELU(),\n            nn.Dropout(0.1),\n            nn.Linear(512, 3)\n        )\n\n        self.state_head   = nn.Linear(feat_dim, N_STATES)\n        self.species_head = nn.Linear(feat_dim, N_SPECIES)\n        self.height_head  = nn.Linear(feat_dim, 1)\n\n        for p in self.backbone.parameters():\n            p.requires_grad = False\n\n    def forward(self, xl, xr):\n        fl = self.backbone(xl)\n        fr = self.backbone(xr)\n        f  = torch.cat([fl, fr], dim=1)\n\n        return {\n            \"biomass\": self.biomass_head(f),\n            \"state\": self.state_head(f),\n            \"species\": self.species_head(f),\n            \"height\": self.height_head(f).squeeze(1)\n        }\n\n# =====================================================\n# TRAINING\n# =====================================================\nloss_biomass = nn.MSELoss()\nloss_state   = nn.CrossEntropyLoss()\nloss_species = nn.CrossEntropyLoss()\nloss_height  = nn.L1Loss()\n\nW_STATE, W_SPECIES, W_HEIGHT = 0.1, 0.1, 0.05\n\ndef train_one_epoch(model, loader, opt):\n    model.train()\n    tot = 0\n\n    for l, r, y, s, sp, h in loader:\n        l, r = l.to(config.device), r.to(config.device)\n        y, s = y.to(config.device), s.to(config.device)\n        sp, h = sp.to(config.device), h.to(config.device)\n\n        opt.zero_grad()\n        out = model(l, r)\n\n        loss = (\n            loss_biomass(out[\"biomass\"], y)\n            + W_STATE   * loss_state(out[\"state\"], s)\n            + W_SPECIES * loss_species(out[\"species\"], sp)\n            + W_HEIGHT  * loss_height(out[\"height\"], h)\n        )\n\n        loss.backward()\n        opt.step()\n        tot += loss.item() * l.size(0)\n\n    return tot / len(loader.dataset)\n\n# =====================================================\n# TRAIN + SAVE\n# =====================================================\nskf = StratifiedKFold(config.n_folds, shuffle=True, random_state=config.seed)\n\n# for f, (tr, _) in enumerate(skf.split(train_df, train_df[\"State\"])):\n#     ds = BiomassTwoStreamDataset(train_df.iloc[tr], get_tfms(True))\n#     dl = DataLoader(ds, batch_size=config.batch_size, shuffle=True)\n\n#     model = DINOv2MultiTask().to(config.device)\n#     opt = torch.optim.AdamW(model.parameters(), lr=config.lr)\n\n#     for e in range(config.epochs):\n#         loss = train_one_epoch(model, dl, opt)\n#         print(f\"Fold {f} | Epoch {e} | Loss {loss:.4f}\")\n\n#     torch.save(model.state_dict(), f\"{config.ckpt_dir}/dino_fold_{f}.pth\")\n#     del model; gc.collect(); torch.cuda.empty_cache()\n\n# =====================================================\n# INFERENCE\n# =====================================================\n@torch.no_grad()\ndef infer(ckpt, loader):\n    model = DINOv2MultiTask().to(config.device)\n    model.load_state_dict(torch.load(ckpt, map_location=config.device))\n    model.eval()\n\n    out = []\n    for l, r in loader:\n        preds = model(l.to(config.device), r.to(config.device))[\"biomass\"]\n        out.append(preds.cpu().numpy())\n\n    return np.concatenate(out)\n\ndef expand_preds(p):\n    C, D, G = np.clip(p[:,0],0,None), np.clip(p[:,1],0,None), np.clip(p[:,2],0,None)\n    GDM = G + C\n    TOT = GDM + D\n    return np.stack([C,D,G,TOT,GDM], axis=1)\n\ntest_df = pd.read_csv(config.test_csv)\ntest_unique = test_df.drop_duplicates(\"image_path\")\ntest_ds = BiomassTwoStreamDataset(test_unique, get_tfms(False), is_test=True)\ntest_dl = DataLoader(test_ds, batch_size=16, shuffle=False)\n#/kaggle/input/check/pytorch/default/1/dino_fold_0 (1).pth\nall_preds = []\nfor f in range(config.n_folds):\n    all_preds.append(infer(f\"{config.ckpt_dir}/dino_fold_{f} (1).pth\", test_dl))\n\navg_preds_5 = expand_preds(np.mean(all_preds, axis=0))\n\n# =====================================================\n# SUBMISSION\n# =====================================================\npred_df = pd.DataFrame(avg_preds_5, columns=config.target_cols)\nout_df = pd.concat([test_unique.reset_index(drop=True), pred_df], axis=1)\n\nlong_df = out_df.melt(\"image_path\", config.target_cols, \"target_name\", \"target\")\nsubmission = test_df.merge(long_df, on=[\"image_path\",\"target_name\"], how=\"left\")[[\"sample_id\",\"target\"]]\nsubmission[\"target\"] = submission[\"target\"].fillna(0).clip(0)\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T08:02:51.195628Z","iopub.execute_input":"2026-01-17T08:02:51.195884Z","iopub.status.idle":"2026-01-17T08:02:54.062594Z","shell.execute_reply.started":"2026-01-17T08:02:51.195866Z","shell.execute_reply":"2026-01-17T08:02:54.061489Z"}},"outputs":[],"execution_count":null}]}