{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":112509,"databundleVersionId":14254895}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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-04-21T06:09:05.777001Z","iopub.execute_input":"2026-04-21T06:09:05.777672Z","iopub.status.idle":"2026-04-21T06:09:05.977671Z","shell.execute_reply.started":"2026-04-21T06:09:05.777620Z","shell.execute_reply":"2026-04-21T06:09:05.976561Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/competitions/csiro-biomass\"\n\nTRAIN_CSV = f\"{DATA_DIR}/train.csv\"\nTEST_CSV = f\"{DATA_DIR}/test.csv\"\n\nTRAIN_DIR = f\"{DATA_DIR}/train\"\nTEST_DIR = f\"{DATA_DIR}/test\"\n\nOUTPUT_DIR = \"/kaggle/working/output\"\n\nimport os\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\nprint(\"Train CSV:\", TRAIN_CSV)\nprint(\"Train images:\", TRAIN_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:05.979402Z","iopub.execute_input":"2026-04-21T06:09:05.979780Z","iopub.status.idle":"2026-04-21T06:09:05.985432Z","shell.execute_reply.started":"2026-04-21T06:09:05.979743Z","shell.execute_reply":"2026-04-21T06:09:05.984782Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.read_csv(TRAIN_CSV)\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:05.986536Z","iopub.execute_input":"2026-04-21T06:09:05.987002Z","iopub.status.idle":"2026-04-21T06:09:06.013702Z","shell.execute_reply.started":"2026-04-21T06:09:05.986953Z","shell.execute_reply":"2026-04-21T06:09:06.013031Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_wide = df.pivot_table(\n    index=[\"image_path\", \"Sampling_Date\", \"State\", \"Species\", \"Pre_GSHH_NDVI\", \"Height_Ave_cm\"],\n    columns=\"target_name\",\n    values=\"target\"\n).reset_index()\n\ndf_wide.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.014658Z","iopub.execute_input":"2026-04-21T06:09:06.015017Z","iopub.status.idle":"2026-04-21T06:09:06.042340Z","shell.execute_reply.started":"2026-04-21T06:09:06.014982Z","shell.execute_reply":"2026-04-21T06:09:06.041712Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# df_wide.columns.name = None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.044331Z","iopub.execute_input":"2026-04-21T06:09:06.044574Z","iopub.status.idle":"2026-04-21T06:09:06.048307Z","shell.execute_reply.started":"2026-04-21T06:09:06.044550Z","shell.execute_reply":"2026-04-21T06:09:06.047457Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/competitions/csiro-biomass\"\n\ndef fix_path(x):\n    if x.startswith(\"/kaggle\"):\n        return x  # already correct\n    return f\"{BASE_PATH}/{x}\"\n\ndf_wide[\"image_path\"] = df_wide[\"image_path\"].apply(fix_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.049334Z","iopub.execute_input":"2026-04-21T06:09:06.049818Z","iopub.status.idle":"2026-04-21T06:09:06.061675Z","shell.execute_reply.started":"2026-04-21T06:09:06.049782Z","shell.execute_reply":"2026-04-21T06:09:06.060945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(df_wide.shape)\ndf_wide.iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.062618Z","iopub.execute_input":"2026-04-21T06:09:06.062929Z","iopub.status.idle":"2026-04-21T06:09:06.077482Z","shell.execute_reply.started":"2026-04-21T06:09:06.062894Z","shell.execute_reply":"2026-04-21T06:09:06.076596Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_wide.head(1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.078475Z","iopub.execute_input":"2026-04-21T06:09:06.079235Z","iopub.status.idle":"2026-04-21T06:09:06.095518Z","shell.execute_reply.started":"2026-04-21T06:09:06.079211Z","shell.execute_reply":"2026-04-21T06:09:06.094744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:06.096314Z","iopub.execute_input":"2026-04-21T06:09:06.096510Z","iopub.status.idle":"2026-04-21T06:09:09.741377Z","shell.execute_reply.started":"2026-04-21T06:09:06.096492Z","shell.execute_reply":"2026-04-21T06:09:09.740546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch\n\n# targets = torch.tensor([\n#     row[\"Dry_Green_g\"],\n#     row[\"Dry_Dead_g\"],\n#     row[\"Dry_Clover_g\"],\n#     row[\"GDM_g\"],\n#     row[\"Dry_Total_g\"],\n# ], dtype=torch.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:09.742776Z","iopub.execute_input":"2026-04-21T06:09:09.743249Z","iopub.status.idle":"2026-04-21T06:09:09.747963Z","shell.execute_reply.started":"2026-04-21T06:09:09.743217Z","shell.execute_reply":"2026-04-21T06:09:09.746814Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_cols = [\n    \"Dry_Green_g\",\n    \"Dry_Dead_g\",\n    \"Dry_Clover_g\",\n    \"GDM_g\",\n    \"Dry_Total_g\"\n]\n\ntargets_tensor = torch.tensor(\n    df_wide[target_cols].values,\n    dtype=torch.float32\n)\n\nprint(targets_tensor.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:09.749012Z","iopub.execute_input":"2026-04-21T06:09:09.749547Z","iopub.status.idle":"2026-04-21T06:09:09.767316Z","shell.execute_reply.started":"2026-04-21T06:09:09.749515Z","shell.execute_reply":"2026-04-21T06:09:09.766415Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q timm albumentations","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:09.768431Z","iopub.execute_input":"2026-04-21T06:09:09.768610Z","iopub.status.idle":"2026-04-21T06:09:13.556564Z","shell.execute_reply.started":"2026-04-21T06:09:09.768592Z","shell.execute_reply":"2026-04-21T06:09:13.555563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport timm\nimport cv2\nimport pandas as pd\nfrom torch.utils.data import Dataset, DataLoader\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.558155Z","iopub.execute_input":"2026-04-21T06:09:13.558614Z","iopub.status.idle":"2026-04-21T06:09:13.564270Z","shell.execute_reply.started":"2026-04-21T06:09:13.558535Z","shell.execute_reply":"2026-04-21T06:09:13.563339Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BiomassDataset(Dataset):\n    def __init__(self, df, transforms=None):\n        self.df = df\n        self.transforms = transforms\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n\n        img = cv2.imread(row[\"image_path\"])\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        if self.transforms:\n            img = self.transforms(image=img)[\"image\"]\n\n        targets = torch.tensor([\n            row[\"Dry_Green_g\"],\n            row[\"Dry_Dead_g\"],\n            row[\"Dry_Clover_g\"],\n            row[\"GDM_g\"],\n            row[\"Dry_Total_g\"],\n        ], dtype=torch.float32)\n\n        return img, targets","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.568533Z","iopub.execute_input":"2026-04-21T06:09:13.569000Z","iopub.status.idle":"2026-04-21T06:09:13.579525Z","shell.execute_reply.started":"2026-04-21T06:09:13.568957Z","shell.execute_reply":"2026-04-21T06:09:13.578797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transforms = A.Compose([\n    A.Resize(640, 640),   # 🔥 SAFE for Kaggle\n    A.HorizontalFlip(p=0.5),\n    A.Normalize(),\n    ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.580589Z","iopub.execute_input":"2026-04-21T06:09:13.580953Z","iopub.status.idle":"2026-04-21T06:09:13.595636Z","shell.execute_reply.started":"2026-04-21T06:09:13.580890Z","shell.execute_reply":"2026-04-21T06:09:13.594779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DINOModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.backbone = timm.create_model(\n            \"vit_large_patch16_dinov3_qkvb.lvd1689m\",\n            pretrained=True,\n            num_classes=0\n        )\n\n        self.head = nn.Sequential(\n            nn.Linear(self.backbone.num_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 5)\n        )\n\n    def forward(self, x):\n        x = self.backbone(x)\n        x = self.head(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.596820Z","iopub.execute_input":"2026-04-21T06:09:13.597486Z","iopub.status.idle":"2026-04-21T06:09:13.610685Z","shell.execute_reply.started":"2026-04-21T06:09:13.597441Z","shell.execute_reply":"2026-04-21T06:09:13.609705Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def freeze_backbone(model):\n    for param in model.backbone.parameters():\n        param.requires_grad = False\n\ndef unfreeze_backbone(model):\n    for param in model.backbone.parameters():\n        param.requires_grad = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.612015Z","iopub.execute_input":"2026-04-21T06:09:13.612411Z","iopub.status.idle":"2026-04-21T06:09:13.631386Z","shell.execute_reply.started":"2026-04-21T06:09:13.612374Z","shell.execute_reply":"2026-04-21T06:09:13.630511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = BiomassDataset(df_wide, transforms)\n\nloader = DataLoader(\n    dataset,\n    batch_size=1,   # 🔥 MUST\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.632630Z","iopub.execute_input":"2026-04-21T06:09:13.633001Z","iopub.status.idle":"2026-04-21T06:09:13.644585Z","shell.execute_reply.started":"2026-04-21T06:09:13.632963Z","shell.execute_reply":"2026-04-21T06:09:13.643806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\"\n\nmodel = DINOModel().to(device)\n\ncriterion = nn.SmoothL1Loss()\n\noptimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)\nscaler = torch.cuda.amp.GradScaler()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:13.645649Z","iopub.execute_input":"2026-04-21T06:09:13.646157Z","iopub.status.idle":"2026-04-21T06:09:18.267627Z","shell.execute_reply.started":"2026-04-21T06:09:13.646132Z","shell.execute_reply":"2026-04-21T06:09:18.266497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accumulation_steps =  10 # simulate batch_size=4\n\nfreeze_backbone(model)\n\nfor epoch in range(10):\n\n    # 🔥 Unfreeze after 2 epochs\n    if epoch == 2:\n        unfreeze_backbone(model)\n        optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)\n\n    model.train()\n    total_loss = 0\n\n    for i, (imgs, targets) in enumerate(loader):\n        imgs = imgs.to(device)\n        targets = targets.to(device)\n\n        with torch.cuda.amp.autocast():\n            preds = model(imgs)\n            loss = criterion(preds, targets)\n            loss = loss / accumulation_steps\n\n        scaler.scale(loss).backward()\n\n        if (i + 1) % accumulation_steps == 0:\n            scaler.step(optimizer)\n            scaler.update()\n            optimizer.zero_grad()\n\n        total_loss += loss.item()\n                                                                    \n    print(f\"Epoch {epoch} Loss: {total_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:09:18.268936Z","iopub.execute_input":"2026-04-21T06:09:18.269646Z","iopub.status.idle":"2026-04-21T06:31:29.663889Z","shell.execute_reply.started":"2026-04-21T06:09:18.269618Z","shell.execute_reply":"2026-04-21T06:31:29.662878Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), \"/kaggle/working/dino_model.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:31:29.665342Z","iopub.execute_input":"2026-04-21T06:31:29.666178Z","iopub.status.idle":"2026-04-21T06:31:32.025253Z","shell.execute_reply.started":"2026-04-21T06:31:29.666146Z","shell.execute_reply":"2026-04-21T06:31:32.024372Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), f\"/kaggle/working/model_epoch_last.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:31:32.026434Z","iopub.execute_input":"2026-04-21T06:31:32.026774Z","iopub.status.idle":"2026-04-21T06:31:34.316111Z","shell.execute_reply.started":"2026-04-21T06:31:32.026738Z","shell.execute_reply":"2026-04-21T06:31:34.315092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df, val_df = train_test_split(df_wide, test_size=0.2, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:31:34.317216Z","iopub.execute_input":"2026-04-21T06:31:34.317575Z","iopub.status.idle":"2026-04-21T06:31:34.326966Z","shell.execute_reply.started":"2026-04-21T06:31:34.317549Z","shell.execute_reply":"2026-04-21T06:31:34.326036Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_dataset = BiomassDataset(val_df, transforms)\nval_loader = DataLoader(val_dataset, batch_size=1, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:31:34.328329Z","iopub.execute_input":"2026-04-21T06:31:34.328703Z","iopub.status.idle":"2026-04-21T06:31:34.341757Z","shell.execute_reply.started":"2026-04-21T06:31:34.328658Z","shell.execute_reply":"2026-04-21T06:31:34.340651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\npreds_all = []\ntargets_all = []\n\nwith torch.no_grad():\n    for imgs, targets in val_loader:\n        imgs = imgs.to(device)\n        targets = targets.to(device)\n\n        preds = model(imgs)\n\n        preds_all.append(preds.cpu())\n        targets_all.append(targets.cpu())\n\npreds_all = torch.cat(preds_all).numpy()\ntargets_all = torch.cat(targets_all).numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:31:34.342830Z","iopub.execute_input":"2026-04-21T06:31:34.343341Z","iopub.status.idle":"2026-04-21T06:32:12.701767Z","shell.execute_reply.started":"2026-04-21T06:31:34.343287Z","shell.execute_reply":"2026-04-21T06:32:12.701010Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import r2_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:32:12.702855Z","iopub.execute_input":"2026-04-21T06:32:12.703547Z","iopub.status.idle":"2026-04-21T06:32:12.707775Z","shell.execute_reply.started":"2026-04-21T06:32:12.703510Z","shell.execute_reply":"2026-04-21T06:32:12.706811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"names = [\"Dry_Green\", \"Dry_Clover\", \"GDM\", \"Dry_Total\"]\nindices = [0, 2, 3, 4]  \n\nfor name, i in zip(names, indices):\n    score = r2_score(targets_all[:, i], preds_all[:, i])\n    print(f\"{name} R2: {score:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:32:12.708882Z","iopub.execute_input":"2026-04-21T06:32:12.709528Z","iopub.status.idle":"2026-04-21T06:32:12.726393Z","shell.execute_reply.started":"2026-04-21T06:32:12.709502Z","shell.execute_reply":"2026-04-21T06:32:12.725527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import mean_squared_error\nimport numpy as np\n\nnames = [\"Dry_Green\", \"Dry_Clover\", \"GDM\", \"Dry_Total\"]\nindices = [0, 2, 3, 4]  # skip Dry_Dead\n\nfor name, i in zip(names, indices):\n    rmse = np.sqrt(mean_squared_error(targets_all[:, i], preds_all[:, i]))\n    print(f\"{name} RMSE: {rmse:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T06:32:12.727691Z","iopub.execute_input":"2026-04-21T06:32:12.728098Z","iopub.status.idle":"2026-04-21T06:32:12.743705Z","shell.execute_reply.started":"2026-04-21T06:32:12.728060Z","shell.execute_reply":"2026-04-21T06:32:12.742535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}