{"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":640139,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":482823,"modelId":498401},{"sourceId":640161,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":482839,"modelId":498418}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from torch.utils.data import DataLoader\nfrom transformers import AutoModel\nfrom torch.utils.data import Dataset\nfrom glob import glob\n\nfrom torch import nn\nfrom tqdm import tqdm\n\nimport os\nimport cv2\nimport torch\nimport argparse\nimport numpy as np\nimport pandas as pd\nimport albumentations as A\n\nos.environ['HF_HUB_OFFLINE'] = '1'\nos.environ['TRANSFORMERS_OFFLINE'] = '1'\n\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\nos.environ[\"XLA_FLAGS\"] = \"--xla_gpu_cuda_data_dir=/usr/lib/cuda\"\n\nimport google.protobuf.message_factory as mf\nif not hasattr(mf.MessageFactory, \"GetPrototype\"):\n    def _GetPrototype(self, descriptor):\n        return \n    mf.MessageFactory.GetPrototype = _GetPrototype\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:04.411802Z","iopub.execute_input":"2025-11-12T15:18:04.412032Z","iopub.status.idle":"2025-11-12T15:18:04.418704Z","shell.execute_reply.started":"2025-11-12T15:18:04.412015Z","shell.execute_reply":"2025-11-12T15:18:04.417922Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\n\nfrom torch import nn\n\nSEED = 42\n\n# DATASET AND DATALOADER PARAMS\n\n# Worker\nNUM_WORKERS = 1\nNUM_WORKERS_INFER = 4\n\n# Image size (height, width)\nIMAGE_SIZE = (768, 768)\nAUGMENT = True\n\n# Normalization\nNORMALIZE_MEAN = (0.485, 0.456, 0.406)\nNORMALIZE_STD = (0.229, 0.224, 0.225)\n\nID_ROW_NAME = \"id\"  # Name of the ID column in the dataset\nDRY_CLOVER_G_ROW_NAME = \"Dry_Clover_g\"\nDRY_DEAD_G_ROW_NAME = \"Dry_Dead_g\"\nDRY_GREEN_G_ROW_NAME = \"Dry_Green_g\"\nDRY_TOTAL_G_ROW_NAME = \"Dry_Total_g\"\nGDM_G_ROW_NAME = \"GDM_g\"\nTRAIN_TARGET_NAME = [DRY_TOTAL_G_ROW_NAME, GDM_G_ROW_NAME, DRY_GREEN_G_ROW_NAME]\nTARGET_NAME = [\n    DRY_CLOVER_G_ROW_NAME,\n    DRY_DEAD_G_ROW_NAME,\n    DRY_GREEN_G_ROW_NAME,\n    DRY_TOTAL_G_ROW_NAME,\n    GDM_G_ROW_NAME\n]\n\nUSE_SINH: bool = True\n\nVERBOSE_LOGGING = False\n\n# MODEL\nFEATURE_EXTRACTOR_SRCS = {\n    \"transformers\": [\n        \"facebook/dinov3-convnext-tiny-pretrain-lvd1689m\",\n        \"facebook/dinov2-large\"\n    ],\n    \"torchvision\": [\n        \"convnext_tiny\", \"convnext_small\"\n    ]\n}\nFEATURE_EXTRACTOR_SRC = \"transformers\"  # Options: \"transformers\", \"torchvision\"\nFEATURE_EXTRACTOR_NAME = \"facebook/dinov2-large\"  # Name of the feature extractor model\nassert FEATURE_EXTRACTOR_NAME in FEATURE_EXTRACTOR_SRCS[FEATURE_EXTRACTOR_SRC], \\\n    f\"FEATURE_EXTRACTOR_NAME '{FEATURE_EXTRACTOR_NAME}' not found in FEATURE_EXTRACTOR_SRCS for source '{FEATURE_EXTRACTOR_SRC}'\"\n\n# TRAINING PARAMS\nEXP_DIR = \"outputs\"\nBATCH_SIZE = 1\nBATCH_SIZE_INFER = 4\nEPOCHS = 200\nLEARNING_RATE = 1e-4\nMIN_LEARNING_RATE = 1e-6\nWEIGHT_DECAY = 1e-2\nLOSS_FUNCS = {\n    \"MSELoss\": nn.MSELoss(),\n    \"L1Loss\": nn.L1Loss(),\n    \"SmoothL1Loss\": nn.SmoothL1Loss()\n}\nLOSS_FUNC_NAME = \"SmoothL1Loss\"  # Options: \"MSELoss\", \"L1Loss\", \"SmoothL1Loss\"\nLOSS_FUNC = LOSS_FUNCS[LOSS_FUNC_NAME]\nDRY_TOTAL_G_WEIGHT = 0.6\nGDM_G_WEIGHT = 0.2\nDRY_GREEN_G_WEIGHT = 0.2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:07.082895Z","iopub.execute_input":"2025-11-12T15:18:07.083197Z","iopub.status.idle":"2025-11-12T15:18:07.092916Z","shell.execute_reply.started":"2025-11-12T15:18:07.083158Z","shell.execute_reply":"2025-11-12T15:18:07.092368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestDataset(Dataset):\n    def __init__(\n        self, \n        test_csv_path: str,\n        test_image_dir: str\n    ):\n        self.test_csv_path = test_csv_path\n        self.test_image_dir = test_image_dir\n        self.long_df = pd.read_csv(test_csv_path)\n        self.df = self.long_df.drop_duplicates(subset=['image_path']).reset_index(drop=True)\n        self.out_transform = A.Compose([\n            A.Resize(IMAGE_SIZE[0], IMAGE_SIZE[1]),\n            A.Normalize(\n                mean=NORMALIZE_MEAN,\n                std=NORMALIZE_STD\n            ),\n            A.pytorch.ToTensorV2()\n        ])\n\n        self.img_paths = self.df['image_path'].values\n\n    def __len__(self):\n        return len(self.img_paths)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.test_image_dir, self.img_paths[idx])\n        img_id = os.path.basename(img_path).split('.')[0]\n\n        image = cv2.imread(img_path)\n\n        if image is None:\n            image = np.ones((1000, 2000, 3), dtype=np.uint8)\n        \n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        _, width, _ = image.shape\n        mid_point = width // 2\n        img_left = image[:, :mid_point]\n        img_right = image[:, mid_point:]\n\n        img_left = self.out_transform(image = img_left)[\"image\"]\n        img_right = self.out_transform(image = img_right)[\"image\"]\n\n        return img_left, img_right, img_id","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:07.093650Z","iopub.execute_input":"2025-11-12T15:18:07.093940Z","iopub.status.idle":"2025-11-12T15:18:07.113550Z","shell.execute_reply.started":"2025-11-12T15:18:07.093894Z","shell.execute_reply":"2025-11-12T15:18:07.112731Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_path = '/kaggle/input/csiro-biomass'\ntest_csv_path = os.path.join(base_path, 'test.csv')\ntest_data_dir = os.path.join(base_path, 'test')\nprint(f\"Number of testing files: {len(os.listdir(test_data_dir))}\")\n\ntest_dataset = TestDataset(\n    test_csv_path=test_csv_path,\n    test_image_dir=base_path\n)\nprint(f\"Load dataset with {len(test_dataset)} samples\")\n\ntest_dataloader = DataLoader(\n    test_dataset,\n    batch_size=BATCH_SIZE_INFER,\n    shuffle=False,\n    num_workers=NUM_WORKERS_INFER,\n    pin_memory=True,\n    drop_last=False\n)\n\nprint(f\"Number of batch: {len(test_dataloader)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:07.114218Z","iopub.execute_input":"2025-11-12T15:18:07.114463Z","iopub.status.idle":"2025-11-12T15:18:07.167526Z","shell.execute_reply.started":"2025-11-12T15:18:07.114447Z","shell.execute_reply":"2025-11-12T15:18:07.166950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n\nfeature_extractor = AutoModel.from_pretrained(\n    '/kaggle/input/facebook_dinov2-large/pytorch/default/1',\n    device_map=DEVICE\n)\nlast_dim = feature_extractor.config.hidden_size\nfeature_extractor.eval()\nnum_params = sum(p.numel() for p in feature_extractor.parameters())\nprint(f\"Loaded model '{FEATURE_EXTRACTOR_NAME}' with {num_params} parameters, last_dim: {last_dim}.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:07.169618Z","iopub.execute_input":"2025-11-12T15:18:07.169838Z","iopub.status.idle":"2025-11-12T15:18:19.597002Z","shell.execute_reply.started":"2025-11-12T15:18:07.169822Z","shell.execute_reply":"2025-11-12T15:18:19.596173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ExpertModel(nn.Module):\n    def __init__(self, input_dim: int, output_dim: int):\n        super(ExpertModel, self).__init__()\n        self.expert_layers = nn.ModuleList([\n            nn.Sequential(\n                nn.Linear(input_dim, input_dim // 2),\n                nn.ReLU(),\n                nn.Dropout(0.3),\n                nn.Linear(input_dim // 2, 1)\n            ) for _ in range(output_dim)\n        ])\n\n    def forward(self, x, targets=None):\n        outputs = [expert(x) for expert in self.expert_layers]\n        return torch.cat(outputs, dim=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:19.597734Z","iopub.execute_input":"2025-11-12T15:18:19.597970Z","iopub.status.idle":"2025-11-12T15:18:19.603373Z","shell.execute_reply.started":"2025-11-12T15:18:19.597954Z","shell.execute_reply":"2025-11-12T15:18:19.602636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model: nn.Module = ExpertModel(\n    input_dim=last_dim * 2,\n    output_dim=len(TRAIN_TARGET_NAME)\n)\n\n# Load trained model weights\nmodel_path = '/kaggle/input/image2biomass_expert_model/pytorch/default/1/best_model.pth'\nmodel.load_state_dict(torch.load(model_path, map_location='cpu'))\nmodel.to(DEVICE)\nmodel.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:18:19.604273Z","iopub.execute_input":"2025-11-12T15:18:19.604573Z","iopub.status.idle":"2025-11-12T15:18:20.012054Z","shell.execute_reply.started":"2025-11-12T15:18:19.604549Z","shell.execute_reply":"2025-11-12T15:18:20.010443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"save_path = 'submission.csv'\ntest_results = {\n    'sample_id': [],\n    'target': []\n}\n    \nfor batch in tqdm(test_dataloader, desc=\"Inference\"):\n    img_left, img_right, img_ids = batch\n    img_left = img_left.to(DEVICE)\n    img_right = img_right.to(DEVICE)\n\n    with torch.inference_mode():\n        features_left = feature_extractor(img_left).pooler_output\n        features_right = feature_extractor(img_right).pooler_output\n        features = torch.cat((features_left, features_right), dim=1)\n\n        predictions = model(features)\n        if USE_SINH:\n            predictions = torch.sinh(predictions)\n        predictions = predictions.cpu().numpy()\n\n        total_g_preds = np.clip(predictions[:, 0], 0, None)\n        gdm_g_preds = np.clip(predictions[:, 1], 0, None)\n        green_g_preds = np.clip(predictions[:, 2], 0, None)\n        dead_g_preds = np.maximum(0, total_g_preds - gdm_g_preds)\n        clover_g_preds = np.maximum(0, gdm_g_preds - green_g_preds)\n\n        for img_id, \\\n            clover_g_pred, \\\n                dead_g_pred, \\\n                    green_g_pred, \\\n                        total_g_pred, \\\n                            gdm_g_pred in zip(\n                                img_ids,\n                                clover_g_preds.tolist(),\n                                dead_g_preds.tolist(),\n                                green_g_preds.tolist(),\n                                total_g_preds.tolist(),\n                                gdm_g_preds.tolist(),                                            \n                            ):\n            test_results['sample_id'].extend(\n                [f'{img_id}__{x}' for x in TARGET_NAME]\n            )\n            test_results['target'].extend(\n                [\n                    clover_g_pred,\n                    dead_g_pred,\n                    green_g_pred,\n                    total_g_pred,\n                    gdm_g_pred\n                ]\n            )\n# Save results to CSV\ndf = pd.DataFrame(test_results)\ndf.to_csv(save_path, index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-12T15:20:07.659842Z","iopub.execute_input":"2025-11-12T15:20:07.660640Z","iopub.status.idle":"2025-11-12T15:20:08.912706Z","shell.execute_reply.started":"2025-11-12T15:20:07.660604Z","shell.execute_reply":"2025-11-12T15:20:08.911866Z"}},"outputs":[],"execution_count":null}]}