{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":4533,"sourceType":"modelInstanceVersion","modelInstanceId":3325,"modelId":986},{"sourceId":722097,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":549350,"modelId":562044}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nos.environ[\"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION\"] = \"python\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:26:26.464497Z","iopub.execute_input":"2025-12-12T17:26:26.464782Z","iopub.status.idle":"2025-12-12T17:26:26.471494Z","shell.execute_reply.started":"2025-12-12T17:26:26.464738Z","shell.execute_reply":"2025-12-12T17:26:26.470754Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import gc\nimport pandas as pd\nimport torch\nfrom tqdm import tqdm\nfrom PIL import Image\nfrom transformers import AutoModel\nimport torch.nn.functional as F\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.transforms import Compose, Resize, Normalize, RandomRotation, RandomHorizontalFlip, ToTensor ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:46:36.414670Z","iopub.execute_input":"2025-12-12T17:46:36.415377Z","iopub.status.idle":"2025-12-12T17:46:36.420085Z","shell.execute_reply.started":"2025-12-12T17:46:36.415343Z","shell.execute_reply":"2025-12-12T17:46:36.419298Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Model Building \nclass CompBlock(nn.Module):\n    def __init__(self, in_dim):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(in_dim, 512),\n            nn.ReLU(),\n            nn.Linear(512, in_dim)\n        )\n        self.ln = nn.LayerNorm(in_dim)\n        \n    def forward(self, x): \n        out = x + self.net(x)\n        out = self.ln(out)\n        return out # B, in_dim\n    \nclass Net(nn.Module):\n    def __init__(self, n_in = 384, n_comps_out = 5, n_height_out = 1, n_ndvi_out = 1, n_species_out = 15, n_state = 4):\n        super().__init__()\n        self.pred_height1 = nn.Sequential(\n            nn.Linear(n_in, 512),\n            nn.ReLU(),\n            nn.Linear(512, n_in)\n        )\n        self.pred_height2 = nn.Sequential(\n            nn.Linear(n_in, 512),\n            nn.ReLU(),\n            nn.Linear(512, 1)\n        )\n\n        self.pred_ndvi = nn.Sequential(\n            nn.Linear(n_in, 512),\n            nn.ReLU(),\n            nn.Linear(512, 1)\n        )\n\n        self.pred_species = nn.Sequential(\n            nn.Linear(n_in, 256),\n            nn.ReLU(),\n            nn.Linear(256, n_species_out)\n        )\n        self.species_emb = nn.Parameter(torch.randn((15, 16), dtype = torch.float32))\n\n        self.pred_state = nn.Sequential(\n            nn.Linear(n_in, 256),\n            nn.ReLU(),\n            nn.Linear(256, n_state)\n        )\n        self.state_emb = nn.Parameter(torch.randn((n_state, 16), dtype = torch.float32))\n\n        self.pred_date = nn.Sequential(\n            nn.Linear(n_in, 512),\n            nn.ReLU(),\n            nn.Linear(512, 5) \n        )\n\n        self.proj = nn.Linear(423, 423)\n\n        self.comp_blocks = nn.ModuleList([CompBlock(423) for _ in range(3)])\n        \n        self.comp_head = nn.Sequential(\n            nn.Linear(423, 512),\n            nn.ReLU(),\n            nn.Linear(512, n_comps_out)\n        )\n        \n    def forward(\n        self,\n        x\n    ):\n        # -------------------------\n        # Height\n        # -------------------------\n        h_feat = x + self.pred_height1(x)\n        h_feat = self.pred_height2(h_feat)\n    \n        # -------------------------\n        # NDVI (bounded approx)\n        # -------------------------\n        ndvi_feat = torch.sigmoid(self.pred_ndvi(x))\n    \n        # -------------------------\n        # Species\n        # -------------------------\n        species = self.pred_species(x)\n        species_probs = F.softmax(species, dim=-1)\n        species_ix = species_probs.argmax(dim = -1)\n        species_feat = self.species_emb[species_ix] # B, 8, 16\n    \n        # -------------------------\n        # State \n        # -------------------------\n        state = self.pred_state(x)\n        state_probs = F.softmax(state, dim=-1)\n        # state_emb = state_probs @ self.state_emb\n        state_ix = state_probs.argmax(dim = -1) \n        state_feat = self.state_emb[state_ix] # B, 8, 16\n\n        # -------------------------\n        # Date Prediction\n        # -------------------------\n        date_feat = self.pred_date(x)   # B, 8, 16\n    \n        # -------------------------\n        # Biomass components\n        # -------------------------\n        x = torch.cat(\n            [x, h_feat, ndvi_feat, species_feat, state_feat, date_feat],\n            dim=-1\n        )  \n    \n        x = self.proj(x)\n        for block in self.comp_blocks:\n            x = block(x)\n    \n        comp = self.comp_head(x) # B, 8, 5\n        comp = comp.mean(1) # B. 5\n    \n        return comp","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def custom_collate(batch):\n    img_path = [item[0] for item in batch]\n    group_df   = [item[1] for item in batch]\n    return img_path, group_df\n\nclass TestDataset(Dataset):\n    def __init__(self, group_df):\n        self.group_df = list(group_df)\n\n    def __len__(self):\n        return len(self.group_df)\n\n    def __getitem__(self, idx):\n        img_path, df = self.group_df[idx]\n        return img_path, df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:42:20.367245Z","iopub.execute_input":"2025-12-12T17:42:20.367858Z","iopub.status.idle":"2025-12-12T17:42:20.372686Z","shell.execute_reply.started":"2025-12-12T17:42:20.367833Z","shell.execute_reply":"2025-12-12T17:42:20.371921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(\"Device: \", device)\n\n# Load image encoder\ndino = '/kaggle/input/dinov2/pytorch/small/1'\nimg_encoder = AutoModel.from_pretrained(dino)\nimg_encoder = img_encoder.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:27:08.027537Z","iopub.execute_input":"2025-12-12T17:27:08.027928Z","iopub.status.idle":"2025-12-12T17:27:20.437063Z","shell.execute_reply.started":"2025-12-12T17:27:08.027902Z","shell.execute_reply":"2025-12-12T17:27:20.436269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load model\nmodel_path = \"/kaggle/input/meta-model-jan16-26/pytorch/default/1/meta_model.pth\"\nstate_dict = torch.load(model_path)\nmodel = Net()\nmodel.load_state_dict(state_dict)\nmodel.eval()\nmodel = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:27:20.438356Z","iopub.execute_input":"2025-12-12T17:27:20.438717Z","iopub.status.idle":"2025-12-12T17:27:20.690483Z","shell.execute_reply.started":"2025-12-12T17:27:20.438690Z","shell.execute_reply":"2025-12-12T17:27:20.689701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data Collection\nroot_dir = '/kaggle/input/csiro-biomass'\ntest_df = pd.read_csv(os.path.join(root_dir, 'test.csv'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:27:49.643920Z","iopub.execute_input":"2025-12-12T17:27:49.644567Z","iopub.status.idle":"2025-12-12T17:27:49.660199Z","shell.execute_reply.started":"2025-12-12T17:27:49.644538Z","shell.execute_reply":"2025-12-12T17:27:49.659288Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data Preprocessing\n## Augmentations\nmean = [0.485, 0.456, 0.406]\nstd = [0.229, 0.224, 0.225]\nrotate1 = RandomRotation(degrees = (0, 90))\nrotate2 = RandomRotation(degrees = (90, 180))\nrotate3 = RandomRotation(degrees = (180, 270))\nresize = Resize((224, 224)) # DinoV2 resize required\nhflip = RandomHorizontalFlip(p=1.0)\nnorm = Normalize(mean, std)\naugs = [\n       Compose([resize, ToTensor(), norm]), # Original\n       Compose([resize, hflip, ToTensor(), norm]),\n       Compose([resize, rotate1, ToTensor(), norm]),\n       Compose([resize, rotate2, ToTensor(), norm]),\n       Compose([resize, rotate3, ToTensor(), norm]),\n       Compose([resize, hflip, rotate1, ToTensor(), norm]),\n       Compose([resize, hflip, rotate2, ToTensor(), norm]),\n       Compose([resize, hflip, rotate3, ToTensor(), norm])\n       ]\n\n## Transform relative img path to absolute\ntest_df['image_path'] = test_df['image_path'].apply(lambda relative_path: os.path.join(root_dir, relative_path))\n\n## Create target idx\ntarget_name_to_idx = {'Dry_Clover_g' : 0, 'Dry_Dead_g' : 1, 'Dry_Green_g' : 2, 'Dry_Total_g' : 3, 'GDM_g' : 4}\ntest_df['target_idx'] = test_df['target_name'].map(target_name_to_idx).values\n\n## Group df by img path\ngroup_df = test_df.groupby('image_path')\n\n## Create dataset & Dataloader\ndataset = TestDataset(group_df)\ndataloader = DataLoader(\n    dataset = dataset,\n    batch_size = 32,\n    shuffle = False,\n    collate_fn = custom_collate\n)\n\n## Loop through data\npreds = []\n\nwith torch.no_grad():\n    for i, (img_path_batch, df_batch) in enumerate(dataloader):\n        enc_inputs = []\n    \n        ## Convert img to embs\n        for img_path in img_path_batch:\n            with Image.open(img_path) as pil_img:\n                [ enc_inputs.append(aug(pil_img)) for aug in augs ]\n                \n        enc_inputs = torch.stack(enc_inputs) # B*8, C, H, W\n        enc_inputs = enc_inputs.to(device)\n        enc_outputs = img_encoder(enc_inputs)\n        inputs = enc_outputs.pooler_output # B*8, 384 (batch, features, dim)\n        B, C = inputs.shape\n        inputs = inputs.view(-1, 8, C) # B, 8, 384\n        \n        ## Inference\n        batch_preds = model(inputs) # B, 5\n        ## Fetch targets from preds\n        for i, df in enumerate(df_batch):\n            batch_pred = batch_preds[i] # 5\n            for _, row in df.iterrows():\n                sample_id = row['sample_id']\n                target_idx = row['target_idx']\n                target = F.relu(batch_pred[target_idx]).item() # 1\n                preds.append( {'sample_id' : sample_id, 'target' : target} )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:56:59.847530Z","iopub.execute_input":"2025-12-12T17:56:59.847842Z","iopub.status.idle":"2025-12-12T17:57:00.160522Z","shell.execute_reply.started":"2025-12-12T17:56:59.847821Z","shell.execute_reply":"2025-12-12T17:57:00.159920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Submit predictions\nsubmission = pd.DataFrame(preds)\nsubmission.to_csv('submission.csv', index = False)\nsubmission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-12T17:57:05.404271Z","iopub.execute_input":"2025-12-12T17:57:05.404880Z","iopub.status.idle":"2025-12-12T17:57:05.414889Z","shell.execute_reply.started":"2025-12-12T17:57:05.404854Z","shell.execute_reply":"2025-12-12T17:57:05.414164Z"}},"outputs":[],"execution_count":null}]}