{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":272453584,"sourceType":"kernelVersion"}],"dockerImageVersionId":31154,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Introduction\nThis notebook loads the trained model weights and performs inference using Test Time Augmentation (TTA).\n\nThe model training is done in [this notebook](https://www.kaggle.com/code/takaito/csiro-img2bio-training-notebook/notebook). (You can train it directly in the Kaggle environment!)\n\nSince I’m more experienced in tabular and NLP competitions, I’ve been looking forward to a chance to learn through a simple image competition like this one.\n\nI hope to pick up various image competition techniques through this challenge!","metadata":{}},{"cell_type":"code","source":"import timm\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport torchvision.transforms as T\nimport pytorch_lightning as pl\nfrom pytorch_lightning.callbacks import ModelCheckpoint\nimport random\nimport os\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import KFold","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:30.156795Z","iopub.execute_input":"2025-10-29T09:00:30.156977Z","iopub.status.idle":"2025-10-29T09:00:47.689381Z","shell.execute_reply.started":"2025-10-29T09:00:30.156960Z","shell.execute_reply":"2025-10-29T09:00:47.688748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ======== Seed ========\ndef seed_everything(seed: int = 42):\n    pl.seed_everything(seed, workers=True)\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    # \n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\nseed_everything(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.691331Z","iopub.execute_input":"2025-10-29T09:00:47.691846Z","iopub.status.idle":"2025-10-29T09:00:47.704197Z","shell.execute_reply.started":"2025-10-29T09:00:47.691826Z","shell.execute_reply":"2025-10-29T09:00:47.703524Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ======== Dataset ========\nclass InferenceDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        image = Image.open('/kaggle/input/csiro-biomass/' + row[\"image_path\"]).convert(\"RGB\")\n        if self.transform:\n            image = self.transform(image)\n        return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.704928Z","iopub.execute_input":"2025-10-29T09:00:47.705165Z","iopub.status.idle":"2025-10-29T09:00:47.716535Z","shell.execute_reply.started":"2025-10-29T09:00:47.705138Z","shell.execute_reply":"2025-10-29T09:00:47.715811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ======== Model ========\nclass MultiRegressionModel(pl.LightningModule):\n    def __init__(self, model_name=\"efficientnet_b0\", pretrained=False, lr=1e-4, output_dim=5):\n        super().__init__()\n        self.save_hyperparameters()\n        self.model = timm.create_model(model_name, pretrained=pretrained, num_classes=output_dim)\n        self.criterion = nn.MSELoss()\n        self.val_outputs = []\n\n    def forward(self, x):\n        return self.model(x)  # shape: (B, 5)\n\n    def training_step(self, batch, batch_idx):\n        x, y = batch\n        y_hat = self(x)\n        loss = self.criterion(y_hat, y)\n        self.log(\"train_loss\", loss, on_step=False, on_epoch=True)\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        x, y = batch\n        y_hat = self(x)\n        loss = self.criterion(y_hat, y)\n        self.val_outputs.append((y_hat.detach().cpu(), y.detach().cpu()))\n        self.log(\"val_loss\", loss, on_step=False, on_epoch=True)\n        return loss\n\n    def on_validation_epoch_end(self):\n        if len(self.val_outputs) == 0:\n            self.log(\"val_weighted_r2\", 0.0, prog_bar=True, on_epoch=True)\n            for name in [\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\", \"GDM_g\", \"Dry_Total_g\"]:\n                self.log(f\"val_r2_{name}\", 0.0, on_epoch=True)\n            self.val_outputs.clear()\n            return\n\n        preds, trues = zip(*self.val_outputs)\n        preds = torch.cat(preds).numpy()\n        trues = torch.cat(trues).numpy()\n        weighted_r2, r2s = weighted_r2_score(trues, preds)\n        self.log(\"val_weighted_r2\", weighted_r2, prog_bar=True, on_epoch=True)\n        for i, name in enumerate([\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\", \"GDM_g\", \"Dry_Total_g\"]):\n            self.log(f\"val_r2_{name}\", r2s[i], on_epoch=True)\n        self.val_outputs.clear()\n        return\n\n    def configure_optimizers(self):\n        optimizer = torch.optim.AdamW(self.parameters(), lr=self.hparams.lr)\n        scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\n        return {\"optimizer\": optimizer, \"lr_scheduler\": scheduler}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.717415Z","iopub.execute_input":"2025-10-29T09:00:47.717701Z","iopub.status.idle":"2025-10-29T09:00:47.730828Z","shell.execute_reply.started":"2025-10-29T09:00:47.717668Z","shell.execute_reply":"2025-10-29T09:00:47.730007Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def tta_inference(model, images):\n    preds = model(images)\n    preds_lr = model(torch.flip(images, dims=[3]))\n    preds_ud = model(torch.flip(images, dims=[2]))\n    preds_lrud = model(torch.flip(images, dims=[2, 3]))\n    preds_mean = (preds + preds_lr + preds_ud + preds_lrud) / 4.0\n    return preds_mean\n\ndef get_id(x):\n    return x.split('_')[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.731679Z","iopub.execute_input":"2025-10-29T09:00:47.731950Z","iopub.status.idle":"2025-10-29T09:00:47.746940Z","shell.execute_reply.started":"2025-10-29T09:00:47.731927Z","shell.execute_reply":"2025-10-29T09:00:47.746123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Transform\nimg_size = 1000\ninfer_transform = T.Compose([\n    T.Resize((img_size, img_size)),\n    T.ToTensor(),\n    T.Normalize([0.485, 0.456, 0.406],\n                [0.229, 0.224, 0.225])\n])\n\n# DataLoader\ntest_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\ntest_df = test_df[~test_df['image_path'].duplicated()][['sample_id', 'image_path']].reset_index(drop=True)\ntest_df['sample_id'] = test_df['sample_id'].apply(get_id)\ndataset = InferenceDataset(test_df, transform=infer_transform)\ndataloader = DataLoader(dataset, batch_size=8, shuffle=False, num_workers=4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.747884Z","iopub.execute_input":"2025-10-29T09:00:47.748221Z","iopub.status.idle":"2025-10-29T09:00:47.782106Z","shell.execute_reply.started":"2025-10-29T09:00:47.748202Z","shell.execute_reply":"2025-10-29T09:00:47.781435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nresults_dict = {}\nfor fold in range(5):\n    model = MultiRegressionModel(model_name=\"efficientnet_b2\", pretrained=False)\n    model.load_state_dict(torch.load(f\"/kaggle/input/csiro-img2bio-training-notebook/model_fold{fold}.pth\"))\n    model.eval()\n    model.to(device)\n    results = []\n    with torch.no_grad():\n        for batch in dataloader:\n            images = batch\n            images = images.to(device)\n            preds = tta_inference(model, images)\n            preds = preds.cpu().numpy()\n            results.append(preds)\n    results_dict[fold] = np.concatenate(results)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:00:47.784420Z","iopub.execute_input":"2025-10-29T09:00:47.784705Z","iopub.status.idle":"2025-10-29T09:00:52.432940Z","shell.execute_reply.started":"2025-10-29T09:00:47.784687Z","shell.execute_reply":"2025-10-29T09:00:52.431914Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result_df = pd.DataFrame(np.mean([results_dict[fold] for fold in range(5)], axis=0), columns=[\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\", \"GDM_g\", \"Dry_Total_g\"])\nresult_df['sample_id'] = test_df['sample_id']\nresult_df = pd.melt(result_df, id_vars='sample_id', value_vars=[\"Dry_Green_g\", \"Dry_Dead_g\", \"Dry_Clover_g\", \"GDM_g\", \"Dry_Total_g\"], value_name='target')\nresult_df['sample_id'] = result_df['sample_id'] + '__' + result_df['variable']\nresult_df['target'] = result_df['target'].clip(0, 200)\nresult_df[['sample_id', 'target']].to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T09:01:26.933720Z","iopub.execute_input":"2025-10-29T09:01:26.934484Z","iopub.status.idle":"2025-10-29T09:01:26.957766Z","shell.execute_reply.started":"2025-10-29T09:01:26.934452Z","shell.execute_reply":"2025-10-29T09:01:26.956900Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}