{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"dockerImageVersionId":31192,"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:35.579607Z","iopub.execute_input":"2025-12-17T08:25:35.579971Z","iopub.status.idle":"2025-12-17T08:25:37.903771Z","shell.execute_reply.started":"2025-12-17T08:25:35.579938Z","shell.execute_reply":"2025-12-17T08:25:37.902783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = pd.read_csv('/kaggle/input/csiro-biomass/train.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:37.905378Z","iopub.execute_input":"2025-12-17T08:25:37.905901Z","iopub.status.idle":"2025-12-17T08:25:37.940701Z","shell.execute_reply.started":"2025-12-17T08:25:37.905879Z","shell.execute_reply":"2025-12-17T08:25:37.939696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:45.092532Z","iopub.execute_input":"2025-12-17T08:25:45.093028Z","iopub.status.idle":"2025-12-17T08:25:45.124546Z","shell.execute_reply.started":"2025-12-17T08:25:45.092997Z","shell.execute_reply":"2025-12-17T08:25:45.123595Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import re\ndata['sample_id'] = data['sample_id'].astype(str).str.split('__').str[0]\ndata","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:45.509599Z","iopub.execute_input":"2025-12-17T08:25:45.509974Z","iopub.status.idle":"2025-12-17T08:25:45.538830Z","shell.execute_reply.started":"2025-12-17T08:25:45.509946Z","shell.execute_reply":"2025-12-17T08:25:45.537676Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(data.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:47.613295Z","iopub.execute_input":"2025-12-17T08:25:47.614071Z","iopub.status.idle":"2025-12-17T08:25:47.619358Z","shell.execute_reply.started":"2025-12-17T08:25:47.614038Z","shell.execute_reply":"2025-12-17T08:25:47.618258Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data.drop(columns=['Sampling_Date'], inplace=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:48.170380Z","iopub.execute_input":"2025-12-17T08:25:48.170742Z","iopub.status.idle":"2025-12-17T08:25:48.178271Z","shell.execute_reply.started":"2025-12-17T08:25:48.170714Z","shell.execute_reply":"2025-12-17T08:25:48.177463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data.drop(columns=['State'], inplace=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:50.101687Z","iopub.execute_input":"2025-12-17T08:25:50.102005Z","iopub.status.idle":"2025-12-17T08:25:50.108114Z","shell.execute_reply.started":"2025-12-17T08:25:50.101980Z","shell.execute_reply":"2025-12-17T08:25:50.107095Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(data.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:50.419298Z","iopub.execute_input":"2025-12-17T08:25:50.419625Z","iopub.status.idle":"2025-12-17T08:25:50.428319Z","shell.execute_reply.started":"2025-12-17T08:25:50.419599Z","shell.execute_reply":"2025-12-17T08:25:50.427381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torchvision import models, transforms\nfrom PIL import Image\nimport os\nimport pandas as pd\nfrom tqdm import tqdm\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:25:52.012488Z","iopub.execute_input":"2025-12-17T08:25:52.012834Z","iopub.status.idle":"2025-12-17T08:26:01.409536Z","shell.execute_reply.started":"2025-12-17T08:25:52.012809Z","shell.execute_reply":"2025-12-17T08:26:01.408680Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nresnet = models.resnet50(weights=None)\nresnet.fc = nn.Identity()   # remove classification head\nresnet = resnet.to(device)\nresnet.eval()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:26:01.411046Z","iopub.execute_input":"2025-12-17T08:26:01.411960Z","iopub.status.idle":"2025-12-17T08:26:01.923512Z","shell.execute_reply.started":"2025-12-17T08:26:01.411931Z","shell.execute_reply":"2025-12-17T08:26:01.922357Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:26:04.144316Z","iopub.execute_input":"2025-12-17T08:26:04.144605Z","iopub.status.idle":"2025-12-17T08:26:04.150289Z","shell.execute_reply.started":"2025-12-17T08:26:04.144586Z","shell.execute_reply":"2025-12-17T08:26:04.149183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embedding_cache={}\ndef extract_embedding(image_path):\n    if image_path in embedding_cache:\n        return embedding_cache[image_path]\n\n    img = Image.open(image_path).convert(\"RGB\")\n    img = transform(img).unsqueeze(0).to(device)  # [1, 3, H, W]\n\n    with torch.no_grad():\n        emb = resnet(img)                          # [1, 2048]\n\n    emb = emb.detach()  # keep on device\n    embedding_cache[image_path] = emb\n\n    return emb\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:26:08.216767Z","iopub.execute_input":"2025-12-17T08:26:08.217115Z","iopub.status.idle":"2025-12-17T08:26:08.223779Z","shell.execute_reply.started":"2025-12-17T08:26:08.217090Z","shell.execute_reply":"2025-12-17T08:26:08.222445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = data[['image_path', 'target_name', 'target']].copy()\n\nunique_images = df['image_path'].unique()\n\nimage_embeddings = {}\n\nfor img_path in tqdm(unique_images):\n    image_embeddings[img_path] = extract_embedding('/kaggle/input/csiro-biomass/'+img_path)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:26:09.692872Z","iopub.execute_input":"2025-12-17T08:26:09.693181Z","iopub.status.idle":"2025-12-17T08:27:26.668258Z","shell.execute_reply.started":"2025-12-17T08:26:09.693159Z","shell.execute_reply":"2025-12-17T08:27:26.667338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# df['embedding'] = df['image_path'].map(image_embeddings)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T06:11:25.537040Z","iopub.execute_input":"2025-12-17T06:11:25.537349Z","iopub.status.idle":"2025-12-17T06:11:25.542097Z","shell.execute_reply.started":"2025-12-17T06:11:25.537325Z","shell.execute_reply":"2025-12-17T06:11:25.540673Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# df['embedding']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T06:11:27.265762Z","iopub.execute_input":"2025-12-17T06:11:27.266092Z","iopub.status.idle":"2025-12-17T06:11:27.271898Z","shell.execute_reply.started":"2025-12-17T06:11:27.266066Z","shell.execute_reply":"2025-12-17T06:11:27.269933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# X = torch.cat(df['embedding'].tolist(),dim=0)  # [N, 2048]\n# y = torch.tensor(df['target'].values, dtype=torch.float32)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T06:11:29.272531Z","iopub.execute_input":"2025-12-17T06:11:29.272848Z","iopub.status.idle":"2025-12-17T06:11:29.278379Z","shell.execute_reply.started":"2025-12-17T06:11:29.272824Z","shell.execute_reply":"2025-12-17T06:11:29.276777Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# print(X.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T06:11:29.916236Z","iopub.execute_input":"2025-12-17T06:11:29.916600Z","iopub.status.idle":"2025-12-17T06:11:29.920828Z","shell.execute_reply.started":"2025-12-17T06:11:29.916573Z","shell.execute_reply":"2025-12-17T06:11:29.919901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom tqdm import tqdm\n\ndf = data[['image_path', 'target_name', 'target']].copy()\nunique_images = df['image_path'].unique()\n\nimage_embeddings = {}\n\nfor img_path in tqdm(unique_images):\n    image_embeddings[img_path] = extract_embedding('/kaggle/input/csiro-biomass/' + img_path)\n\n# Prepare arrays for all samples\nemb_list = []\ntarget_list = []\ntarget_name_list = []\n\nfor _, row in df.iterrows():\n    emb_list.append(image_embeddings[row['image_path']])\n    target_list.append(row['target'])\n    target_name_list.append(row['target_name'])\n\n# Stack embeddings to a tensor\nX = torch.cat(emb_list, dim=0)  # shape: [num_samples, emb_dim]\ny = torch.tensor(target_list, dtype=torch.float32)\n\n# Save all\ntorch.save({\n    \"embeddings\": X,\n    \"targets\": y,\n    \"target_names\": target_name_list\n}, \"image_embeddings.pt\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:27:45.754854Z","iopub.execute_input":"2025-12-17T08:27:45.755228Z","iopub.status.idle":"2025-12-17T08:27:45.884428Z","shell.execute_reply.started":"2025-12-17T08:27:45.755205Z","shell.execute_reply":"2025-12-17T08:27:45.883197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:27:51.801382Z","iopub.execute_input":"2025-12-17T08:27:51.801758Z","iopub.status.idle":"2025-12-17T08:27:51.807463Z","shell.execute_reply.started":"2025-12-17T08:27:51.801731Z","shell.execute_reply":"2025-12-17T08:27:51.806224Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = torch.load(\"/kaggle/working/image_embeddings.pt\",weights_only = False)\n# import torch\n\n# data = torch.load(\"image_embeddings.pt\")\nX = data[\"embeddings\"]        # [num_samples, emb_dim]\ny = data[\"targets\"]           # [num_samples]\ntarget_names = data[\"target_names\"]\nunique_targets = list(set(target_names))\ntarget_to_idx = {t: i for i, t in enumerate(unique_targets)}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:27:55.630673Z","iopub.execute_input":"2025-12-17T08:27:55.631402Z","iopub.status.idle":"2025-12-17T08:27:55.649958Z","shell.execute_reply.started":"2025-12-17T08:27:55.631370Z","shell.execute_reply":"2025-12-17T08:27:55.648712Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nunique_targets = sorted(set(target_names))\ntarget_to_idx = {t: i for i, t in enumerate(unique_targets)}\n\ntarget_idx = torch.tensor([target_to_idx[t] for t in target_names])\nnum_targets = len(unique_targets)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:27:57.838707Z","iopub.execute_input":"2025-12-17T08:27:57.839152Z","iopub.status.idle":"2025-12-17T08:27:57.845038Z","shell.execute_reply.started":"2025-12-17T08:27:57.839112Z","shell.execute_reply":"2025-12-17T08:27:57.844049Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# target_stats = {}\n\n# for t in unique_targets:\n#     vals = df.loc[df.target_name == t, 'target'].values\n#     target_stats[t] = {\n#         \"mean\": vals.mean(),\n#         \"std\": vals.std() + 1e-6\n#     }\n\n# y_norm = torch.tensor([\n#     (v - target_stats[t][\"mean\"]) / target_stats[t][\"std\"]\n#     for v, t in zip(df.target.values, df.target_name.values)\n# ], dtype=torch.float32)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:28:00.862225Z","iopub.execute_input":"2025-12-17T08:28:00.862502Z","iopub.status.idle":"2025-12-17T08:28:00.867952Z","shell.execute_reply.started":"2025-12-17T08:28:00.862482Z","shell.execute_reply":"2025-12-17T08:28:00.866546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\n\nclass EmbeddingDataset(Dataset):\n    def __init__(self, X, target_names, y, target_to_idx):\n        self.X = X\n        self.y = y\n        self.t_idx = torch.tensor([target_to_idx[t] for t in target_names], dtype=torch.long)\n\n    def __len__(self):\n        return len(self.X)\n\n    def __getitem__(self, idx):\n        return self.X[idx], self.t_idx[idx], self.y[idx]\n\ndataset = EmbeddingDataset(X, target_names, y, target_to_idx)\nloader = DataLoader(dataset, batch_size=64, shuffle=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:28:01.572418Z","iopub.execute_input":"2025-12-17T08:28:01.573093Z","iopub.status.idle":"2025-12-17T08:28:01.585798Z","shell.execute_reply.started":"2025-12-17T08:28:01.573038Z","shell.execute_reply":"2025-12-17T08:28:01.584687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch.nn as nn\n\n# class RegressionModel(nn.Module):\n#     def __init__(self, emb_dim, num_targets):\n#         super().__init__()\n#         self.target_emb = nn.Embedding(num_targets, 32)\n#         self.regressor = nn.Sequential(\n#             nn.Linear(emb_dim + 32, 512),\n#             nn.ReLU(),\n#             nn.Linear(512, 1)\n#         )\n\n#     def forward(self, img_emb, target_idx):\n#         t_emb = self.target_emb(target_idx)\n#         x = torch.cat([img_emb, t_emb], dim=1)\n#         return self.regressor(x).squeeze(1)\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass RegressionModel(nn.Module):\n    def __init__(self, emb_dim, num_targets, hidden_dims=[1024, 512]):\n        super().__init__()\n        self.target_emb = nn.Embedding(num_targets, 128)  # target embedding\n\n        # Define MLP layers\n        input_dim = emb_dim + 128\n        layers = []\n        for h_dim in hidden_dims:\n            layers.append(nn.Linear(input_dim, h_dim))\n            layers.append(nn.ReLU())\n            layers.append(nn.Dropout(0.2))\n            input_dim = h_dim\n\n        layers.append(nn.Linear(input_dim, 1))  # final regression output\n        self.regressor = nn.Sequential(*layers)\n\n    def forward(self, img_emb, target_idx):\n        t_emb = self.target_emb(target_idx)         # [batch, 128]\n        x = torch.cat([img_emb, t_emb], dim=1)      # [batch, emb_dim+128]\n        return self.regressor(x).squeeze(1)         # [batch]\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:28:04.847289Z","iopub.execute_input":"2025-12-17T08:28:04.847609Z","iopub.status.idle":"2025-12-17T08:28:04.855777Z","shell.execute_reply.started":"2025-12-17T08:28:04.847584Z","shell.execute_reply":"2025-12-17T08:28:04.854716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nmodel = RegressionModel(\n    emb_dim=X.shape[1],\n    num_targets=len(unique_targets),\n    hidden_dims=[1024, 512]\n).to(device)\n\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\ncriterion = nn.MSELoss()\n\n# Keep same DataLoader and training loop\n\n\nepochs = 100\n\nfor epoch in range(epochs):\n    model.train()\n    total_loss = 0\n\n    for img_emb, t_idx, target in loader:\n        img_emb = img_emb.to(device)\n        t_idx = t_idx.to(device)\n        target = target.to(device)\n\n        pred = model(img_emb, t_idx)\n        loss = criterion(pred, 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(loader):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:40:54.561332Z","iopub.execute_input":"2025-12-17T08:40:54.561703Z","iopub.status.idle":"2025-12-17T08:42:44.094751Z","shell.execute_reply.started":"2025-12-17T08:40:54.561668Z","shell.execute_reply":"2025-12-17T08:42:44.093899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save({\n    \"model_state\": model.state_dict(),\n    \"target_to_idx\": target_to_idx\n}, \"regression_model.pt\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:42:47.585664Z","iopub.execute_input":"2025-12-17T08:42:47.585979Z","iopub.status.idle":"2025-12-17T08:42:47.612950Z","shell.execute_reply.started":"2025-12-17T08:42:47.585958Z","shell.execute_reply":"2025-12-17T08:42:47.611987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def run_csv_inference(csv_path, output_csv=None):\n    df = pd.read_csv(csv_path)\n    predictions = []\n\n    for _, row in tqdm(df.iterrows(), total=len(df)):\n        img_path = '/kaggle/input/csiro-biomass/' + row['image_path']\n        target_name = row['target_name']\n        sample_id = row['sample_id']\n\n        img_emb = extract_embedding(img_path).to(device)  # use precomputed embeddings if available\n        t_idx = torch.tensor([target_to_idx[target_name]], device=device)\n\n        model.eval()\n        with torch.no_grad():\n            pred = model(img_emb, t_idx).item()\n\n        predictions.append((sample_id, pred))\n\n    # Create submission DataFrame after loop\n    submission = pd.DataFrame(predictions, columns=[\"sample_id\", \"target\"])\n\n    # Save CSV\n    if output_csv is None:\n        output_csv = \"/kaggle/working/submission.csv\"\n    submission.to_csv(output_csv, index=False)\n    print(f\"✅ Submission saved to {output_csv}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:42:48.644521Z","iopub.execute_input":"2025-12-17T08:42:48.645674Z","iopub.status.idle":"2025-12-17T08:42:48.652596Z","shell.execute_reply.started":"2025-12-17T08:42:48.645610Z","shell.execute_reply":"2025-12-17T08:42:48.651537Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"run_csv_inference(\n    csv_path=\"/kaggle/input/csiro-biomass/test.csv\",\n    output_csv=\"submission.csv\"\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T08:42:50.315985Z","iopub.execute_input":"2025-12-17T08:42:50.316371Z","iopub.status.idle":"2025-12-17T08:42:50.341384Z","shell.execute_reply.started":"2025-12-17T08:42:50.316347Z","shell.execute_reply":"2025-12-17T08:42:50.340374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}