{"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":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":716648,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":544792,"modelId":557864}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"%%writefile csiro_infer.py\n\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import KFold, GroupKFold, StratifiedGroupKFold\nfrom tqdm.auto import tqdm\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.optim import AdamW\nfrom transformers import get_cosine_schedule_with_warmup\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms as T\n\nimport os\nfrom pathlib import Path\nimport timm\nimport warnings \n\nwarnings.filterwarnings('ignore')\ntqdm.pandas()\n\nclass RegressionDataset(Dataset):\n    def __init__(self, data, transform=None):\n        self.data = data\n        self.transform = transform\n    def __len__(self):\n        return self.data.shape[0]\n\n    def __getitem__(self, idx):\n        item = self.data.iloc[idx]\n        image = item.image\n        targets = [item['Dry_Green_g'], item['Dry_Clover_g'], item['Dry_Dead_g']]\n        width, height = image.size\n        mid_point = width // 2\n        left_image = image.crop((0, 0, mid_point, height))\n        right_image = image.crop((mid_point, 0, width, height))\n\n        if self.transform is not None:\n            left_image = self.transform(left_image)\n            right_image = self.transform(right_image)\n\n        return left_image, right_image, targets\n\ndef get_test_dataloaders(data, image_size, batch_size):\n    res = []\n    for trans in [None, T.RandomHorizontalFlip(p=1.0), T.RandomVerticalFlip(p=1.0)]:\n        transform = T.Compose([\n            T.Resize(image_size),\n            T.ToTensor(),\n            T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        if trans:\n            transform = T.Compose([\n                T.Resize(image_size),\n                trans,\n                T.ToTensor(),\n                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n            ])\n        dataset = RegressionDataset(data, transform=transform)\n        res.append(DataLoader(dataset, batch_size=batch_size, shuffle=False, num_workers=4))\n    return res\n\n\nclass 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        return torch.chunk(gamma_beta, 2, dim=1)\n\nclass CSIROModelRegressor(nn.Module):\n    def __init__(self, model_name, pretrained=True, num_classes=3, dropout=0.0, freeze_backbone=False):\n        super().__init__()\n        self.backbone = timm.create_model(model_name, pretrained=pretrained, num_classes=0, global_pool='avg')\n\n        self.film = FiLM(self.backbone.num_features)\n\n        self.dropout = nn.Dropout(dropout)\n\n        def make_head():\n            return nn.Sequential(\n                nn.Linear(self.backbone.num_features * 2, 8),\n                nn.ReLU(inplace=True),\n                nn.Dropout(dropout),\n                nn.Linear(8, 1),\n            )\n\n        self.head_green = make_head()\n        self.head_clover = make_head()\n        self.head_dead = make_head()\n\n        self.softplus = nn.Softplus(beta=1.0)\n\n        if freeze_backbone:\n            for p in self.backbone.parameters():\n                p.requires_grad = False\n\n\n    def forward(self, left_img, right_img):\n        left_feat = self.backbone(left_img)\n        right_feat = self.backbone(right_img)\n\n        context = (left_feat + right_feat) / 2\n        gamma, beta = self.film(context)\n\n        left_feat_modulated = left_feat * (1 + gamma) + beta\n        right_feat_modulated = right_feat * (1 + gamma) + beta\n\n        combined = torch.cat([left_feat_modulated, right_feat_modulated], dim=1)\n\n        green = self.softplus(self.head_green(combined))   \n        clover = self.softplus(self.head_clover(combined))\n        dead = self.softplus(self.head_dead(combined)) \n\n        logits = torch.cat([green, clover, dead], dim=1)\n\n        return logits\n\ndef predict(model, dataloader, device):\n    model.to(device)\n    model.eval()\n\n    all_outputs = []\n    with torch.no_grad():\n        for left_images, right_images, targets in dataloader:\n            left_images = left_images.to(device)\n            right_images = right_images.to(device)\n\n            outputs = model(left_images, right_images)\n            all_outputs.append(outputs.detach().cpu())\n\n    outputs = torch.cat(all_outputs).numpy()\n    return outputs\n\ndef predict_loaders(model, dataloaders, device):\n    all_outputs = []\n    for dataloader in dataloaders:\n        outputs = predict(model, dataloader, device)\n        all_outputs.append(outputs)\n    avg_outputs = np.mean(all_outputs, axis=0)\n    return avg_outputs\n\ndef predict_folds(dataloaders,models_dir):\n    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n\n    all_preds = []\n    for model_file in Path(models_dir).glob('*.pth'):\n        model = CSIROModelRegressor(CFG.MODEL_NAME, pretrained=False, num_classes=3)\n        model.load_state_dict(torch.load(model_file))\n        preds = predict_loaders(model, dataloaders, device)\n        all_preds.append(preds)\n\n    avg_preds = np.mean(all_preds, axis=0)\n    return avg_preds\n\n\nclass CFG:\n    DATA_PATH=\"/kaggle/input/csiro-biomass/\"\n    TEST_DATA_PATH=\"/kaggle/input/csiro-biomass/test.csv\"\n    MODEL_NAME=\"vit_large_patch16_dinov3_qkvb\"\n    MODELS_DIR ='/kaggle/input/csiro-vit-large-dinov3/pytorch/default/1/csiro-dinov3-models'\n    IMG_SIZE=(512,512)\n\n\ntest_df = pd.read_csv(CFG.TEST_DATA_PATH)\n\ntest_df['target'] = 0.0\ntest_df[['sample_id_prefix', 'sample_id_suffix']] = test_df.sample_id.str.split('__', expand=True)\n\ntest_data_df = test_df.groupby(['sample_id_prefix', 'image_path']).apply(lambda df: df.set_index('target_name').target)\ntest_data_df.reset_index(inplace=True)\ntest_data_df.columns.name = None\n\ntest_data_df['image'] = test_data_df.image_path.progress_apply(lambda path: Image.open(CFG.DATA_PATH + path).convert('RGB'))\n\ntest_loaders = get_test_dataloaders(test_data_df, CFG.IMG_SIZE, 32)\npreds = predict_folds(test_loaders,models_dir=CFG.MODELS_DIR)\n\npreds = np.where(preds < 1, 0, preds)\n\ntest_data_df[['Dry_Green_g', 'Dry_Clover_g', 'Dry_Dead_g']] = preds\ntest_data_df['GDM_g'] = test_data_df.Dry_Green_g + test_data_df.Dry_Clover_g\ntest_data_df['Dry_Total_g'] = test_data_df.GDM_g + test_data_df.Dry_Dead_g\n\ncols = [ 'Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g', 'GDM_g' , 'Dry_Total_g']\nsub_df = test_data_df.set_index('sample_id_prefix')[cols].stack()\nsub_df = sub_df.reset_index()\nsub_df.columns = ['sample_id_prefix', 'target_name', 'target']\n\nsub_df['sample_id'] = sub_df.sample_id_prefix + '__' + sub_df.target_name\n\ncols = ['sample_id', 'target']\nsub_df[cols].to_csv('submission.csv', index=False)\n\nprint(sub_df[cols])","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-11T19:34:55.898167Z","iopub.execute_input":"2026-01-11T19:34:55.898445Z","iopub.status.idle":"2026-01-11T19:34:55.906155Z","shell.execute_reply.started":"2026-01-11T19:34:55.898417Z","shell.execute_reply":"2026-01-11T19:34:55.905548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python csiro_infer.py","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T19:34:55.907145Z","iopub.execute_input":"2026-01-11T19:34:55.907676Z","iopub.status.idle":"2026-01-11T19:35:37.913913Z","shell.execute_reply.started":"2026-01-11T19:34:55.907651Z","shell.execute_reply":"2026-01-11T19:35:37.913226Z"}},"outputs":[],"execution_count":null}]}