{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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":"tpuV5e8","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":9959,"sourceType":"datasetVersion","datasetId":6885,"isSourceIdPinned":false}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset,random_split\nimport pandas as pd\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nimport numpy as np\nimport torchvision.transforms as transforms\nfrom PIL import Image\nimport os\nimport cv2\nimport matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.463138Z","iopub.execute_input":"2026-01-29T08:10:49.463392Z","iopub.status.idle":"2026-01-29T08:10:49.467854Z","shell.execute_reply.started":"2026-01-29T08:10:49.463372Z","shell.execute_reply":"2026-01-29T08:10:49.467073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\ndf['image_path'] = '/kaggle/input/csiro-biomass/'+df['image_path']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.468360Z","iopub.execute_input":"2026-01-29T08:10:49.468506Z","iopub.status.idle":"2026-01-29T08:10:49.491520Z","shell.execute_reply.started":"2026-01-29T08:10:49.468492Z","shell.execute_reply":"2026-01-29T08:10:49.490352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_target = df.groupby('image_path')['target'].apply(list).reset_index()\ndf_target_name = df.groupby('image_path')['target_name'].apply(list).reset_index()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.491914Z","iopub.execute_input":"2026-01-29T08:10:49.492072Z","iopub.status.idle":"2026-01-29T08:10:49.517625Z","shell.execute_reply.started":"2026-01-29T08:10:49.492056Z","shell.execute_reply":"2026-01-29T08:10:49.516946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.518149Z","iopub.execute_input":"2026-01-29T08:10:49.518297Z","iopub.status.idle":"2026-01-29T08:10:49.527946Z","shell.execute_reply.started":"2026-01-29T08:10:49.518283Z","shell.execute_reply":"2026-01-29T08:10:49.527238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = df.drop(columns = ['target','target_name','sample_id'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.528316Z","iopub.execute_input":"2026-01-29T08:10:49.528491Z","iopub.status.idle":"2026-01-29T08:10:49.536026Z","shell.execute_reply.started":"2026-01-29T08:10:49.528475Z","shell.execute_reply":"2026-01-29T08:10:49.535407Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = df.drop_duplicates()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.536527Z","iopub.execute_input":"2026-01-29T08:10:49.536693Z","iopub.status.idle":"2026-01-29T08:10:49.546610Z","shell.execute_reply.started":"2026-01-29T08:10:49.536678Z","shell.execute_reply":"2026-01-29T08:10:49.545945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.merge(df, df_target_name, on = 'image_path')\ndf = pd.merge(df, df_target, on = 'image_path')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.547086Z","iopub.execute_input":"2026-01-29T08:10:49.547238Z","iopub.status.idle":"2026-01-29T08:10:49.555748Z","shell.execute_reply.started":"2026-01-29T08:10:49.547223Z","shell.execute_reply":"2026-01-29T08:10:49.555109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_final = df.drop(columns = ['Sampling_Date','State','Species','Pre_GSHH_NDVI','Height_Ave_cm','target_name'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.556179Z","iopub.execute_input":"2026-01-29T08:10:49.556347Z","iopub.status.idle":"2026-01-29T08:10:49.562740Z","shell.execute_reply.started":"2026-01-29T08:10:49.556315Z","shell.execute_reply":"2026-01-29T08:10:49.562152Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.imshow(cv2.imread('/kaggle/input/csiro-biomass/train/ID1011485656.jpg'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.563200Z","iopub.execute_input":"2026-01-29T08:10:49.563364Z","iopub.status.idle":"2026-01-29T08:10:49.841492Z","shell.execute_reply.started":"2026-01-29T08:10:49.563348Z","shell.execute_reply":"2026-01-29T08:10:49.840706Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Read the image\nimg = cv2.imread('/kaggle/input/csiro-biomass/train/ID1011485656.jpg')\n\n# Resize the image\n# OpenCV also uses (width, height)\nresized_img = cv2.resize(img, (256,128), interpolation=cv2.INTER_AREA)\nplt.imshow(resized_img)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:49.841916Z","iopub.execute_input":"2026-01-29T08:10:49.842090Z","iopub.status.idle":"2026-01-29T08:10:50.011083Z","shell.execute_reply.started":"2026-01-29T08:10:49.842073Z","shell.execute_reply":"2026-01-29T08:10:50.010316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Hyperparameters to tune\nIMAGE_HEIGHT_SIZE = 224\nIMAGE_WIDTH_SIZE = 224\nNUM_EPOCHS = 50\nBATCH_SIZE = 64\nLEARNING_RATE = 0.0005\nIMAGE_DIR_TRAIN = \"/kaggle/input/csiro-biomass/train\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.011871Z","iopub.execute_input":"2026-01-29T08:10:50.012036Z","iopub.status.idle":"2026-01-29T08:10:50.014807Z","shell.execute_reply.started":"2026-01-29T08:10:50.012020Z","shell.execute_reply":"2026-01-29T08:10:50.014084Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Split the paths and labels (80% Train, 20% Val)\ntrain_df, val_df = train_test_split(df_final, test_size=0.2, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.015251Z","iopub.execute_input":"2026-01-29T08:10:50.015427Z","iopub.status.idle":"2026-01-29T08:10:50.026885Z","shell.execute_reply.started":"2026-01-29T08:10:50.015411Z","shell.execute_reply":"2026-01-29T08:10:50.026239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision import transforms\n\n# For TRAINING: Dynamic and random\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMAGE_HEIGHT_SIZE, IMAGE_WIDTH_SIZE)),      # Resize slightly larger first\n    transforms.RandomResizedCrop(224),  # Zoom in on different parts of the image\n    transforms.RandomHorizontalFlip(),  # Flip image horizontally\n    transforms.ColorJitter(brightness=0.1, contrast=0.1), # Random lighting\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# For VALIDATION: Fixed and stable\nval_transform = transforms.Compose([\n    transforms.Resize((IMAGE_HEIGHT_SIZE, IMAGE_WIDTH_SIZE)),      # Exact ResNet size\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.027489Z","iopub.execute_input":"2026-01-29T08:10:50.027662Z","iopub.status.idle":"2026-01-29T08:10:50.035203Z","shell.execute_reply.started":"2026-01-29T08:10:50.027647Z","shell.execute_reply":"2026-01-29T08:10:50.034562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# df_final is the preprocessed DataFrame from the previous successful step.\nclass MultimodalDataset(Dataset):\n    def __init__(self, df_processed, image_dir, transform=None):\n        self.data_frame = df_processed\n        self.image_dir = image_dir # Root directory where 'train/' folder is located\n        self.transform = transform\n    def __len__(self):\n        return len(self.data_frame)\n\n    def __getitem__(self, idx):\n        row = self.data_frame.iloc[idx]\n        \n        # 1. --- IMAGE DATA LOADING ---\n        img_name_relative = row['image_path'] # e.g., 'train/ID1011485656.jpg'\n        full_img_path = img_name_relative  # os.path.join(self.image_dir, img_name_relative)\n        \n        try:\n            # This line requires the actual image files.\n            image = Image.open(full_img_path).convert('RGB')\n            if self.transform:\n                image_tensor = self.transform(image)\n        except FileNotFoundError:\n             print(f\"Warning: Image file not found at {full_img_path}\")\n             image_tensor = torch.zeros(3, IMAGE_SIZE, IMAGE_SIZE, dtype=torch.float32)\n\n        # 3. --- TARGET LABEL ---\n        label = row['target']\n        label_tensor = torch.tensor(label, dtype=torch.float32).unsqueeze(0)\n        return image_tensor, label_tensor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.035707Z","iopub.execute_input":"2026-01-29T08:10:50.035867Z","iopub.status.idle":"2026-01-29T08:10:50.047822Z","shell.execute_reply.started":"2026-01-29T08:10:50.035852Z","shell.execute_reply":"2026-01-29T08:10:50.047162Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create the training version\ntrain_dataset = MultimodalDataset(\n    df_processed=train_df, \n    image_dir=IMAGE_DIR_TRAIN, \n    transform=train_transform\n)\n\n# Create the validation version\nval_dataset = MultimodalDataset(\n    df_processed=val_df, \n    image_dir=IMAGE_DIR_TRAIN, \n    transform=val_transform\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.048261Z","iopub.execute_input":"2026-01-29T08:10:50.048423Z","iopub.status.idle":"2026-01-29T08:10:50.059238Z","shell.execute_reply.started":"2026-01-29T08:10:50.048409Z","shell.execute_reply":"2026-01-29T08:10:50.058613Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.059836Z","iopub.execute_input":"2026-01-29T08:10:50.059988Z","iopub.status.idle":"2026-01-29T08:10:50.068560Z","shell.execute_reply.started":"2026-01-29T08:10:50.059973Z","shell.execute_reply":"2026-01-29T08:10:50.067894Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check if a GPU is available and use it, otherwise fall back to CPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.068903Z","iopub.execute_input":"2026-01-29T08:10:50.069050Z","iopub.status.idle":"2026-01-29T08:10:50.076870Z","shell.execute_reply.started":"2026-01-29T08:10:50.069035Z","shell.execute_reply":"2026-01-29T08:10:50.076242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# image_transform = transforms.Compose([\n#     # 1. Resize: ResNet expects 224x224\n#     transforms.Resize((IMAGE_HEIGHT_SIZE, IMAGE_WIDTH_SIZE)),\n\n#     # 2. ToTensor: Scales [0, 255] to [0.0, 1.0]\n#     transforms.ToTensor(),\n    \n#     # 3. Normalize: CRITICAL for Transfer Learning\n#     # These values (Mean & Std) are standard for all pre-trained PyTorch models\n#     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n# ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.077209Z","iopub.execute_input":"2026-01-29T08:10:50.077368Z","iopub.status.idle":"2026-01-29T08:10:50.086435Z","shell.execute_reply.started":"2026-01-29T08:10:50.077353Z","shell.execute_reply":"2026-01-29T08:10:50.085812Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # see what is in train_loader.\n# train_features = next(iter(train_loader))\n# print(f\"image_tensor batch shape: {train_features[0].size()}\")\n# print(f\"features batch shape: {train_features[1][2]}\")\n# print(f\"Labels batch shape: {train_features[2][0]}\")\n\n# # img = train_features[0].squeeze()\n# # label = train_features[1]\n# # print(f\"Label: {label}\")\n# # print(f\"image: {img}\")","metadata":{"jupyter":{"source_hidden":true},"scrolled":true,"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.086878Z","iopub.execute_input":"2026-01-29T08:10:50.087022Z","iopub.status.idle":"2026-01-29T08:10:50.098473Z","shell.execute_reply.started":"2026-01-29T08:10:50.087008Z","shell.execute_reply":"2026-01-29T08:10:50.097810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\n\nclass EnsembleRegressor(nn.Module):\n    def __init__(self, num_outputs=5):\n        super(EnsembleRegressor, self).__init__()\n        \n        # 1. ResNet18 Backbone (Feature Extractor)\n        self.resnet = models.resnet18()\n        # Assume resnet18.pth is in your Kaggle path\n        state_dict = torch.load('/kaggle/input/resnet18/resnet18.pth', map_location='cpu', weights_only=False)\n        self.resnet.load_state_dict(state_dict)\n        self.resnet.fc = nn.Identity() # Remove final layer to get features (512 units)\n\n        # 2. Your Custom CNN Backbone\n        # (Modified to exclude the regressor part)\n        self.custom_cnn = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(64, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(64, 256, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(256, 512, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.BatchNorm2d(512),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Flatten()\n        )\n\n        # 3. Calculate Combined Features\n        # ResNet18 gives 512 features\n        # Custom CNN (for 256x512 input) gives 512 * 32 * 16 = 262,144 features\n        self.combined_features_dim = 401920\n\n        # 4. Final Unified Regressor\n        self.final_regressor = nn.Sequential(\n            nn.Linear(self.combined_features_dim, 1024),\n            nn.BatchNorm1d(1024),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(1024, 512),\n            nn.ReLU(),\n            nn.Linear(512, 128),\n            nn.Linear(128, 5)\n        )\n\n    def forward(self, x):\n        res_features = self.resnet(x)          # Features from ResNet\n        custom_features = self.custom_cnn(x)   # Features from Custom CNN\n        \n        # Concatenate them\n        combined = torch.cat((res_features, custom_features), dim=1)\n        \n        return self.final_regressor(combined)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.098931Z","iopub.execute_input":"2026-01-29T08:10:50.099076Z","iopub.status.idle":"2026-01-29T08:10:50.106881Z","shell.execute_reply.started":"2026-01-29T08:10:50.099061Z","shell.execute_reply":"2026-01-29T08:10:50.106258Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # to see how each layer of cnn performs and outputs.\n# model = MultimodalModel(30,1)\n# dummy = torch.randn(1,3,64,64)\n# print(f\"Original data shape:{dummy.shape}\")\n# tensor = dummy \n# for i, layer in enumerate(model.vision_encoder):\n#     tensor = layer(tensor)\n#     print(f\"Layer {i} - {layer.__class__.__name__}:\")\n#     print(f\"Output Shape: {tensor.shape}\")","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2026-01-29T08:10:50.107362Z","iopub.execute_input":"2026-01-29T08:10:50.107509Z","iopub.status.idle":"2026-01-29T08:10:50.118691Z","shell.execute_reply.started":"2026-01-29T08:10:50.107495Z","shell.execute_reply":"2026-01-29T08:10:50.118044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# 2. Instantiate Model, Loss, and Optimizer\nmodel = EnsembleRegressor(\n).to(device)\ncriterion = nn.MSELoss() # Correct loss for Regression\noptimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE, weight_decay=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:50.119074Z","iopub.execute_input":"2026-01-29T08:10:50.119231Z","iopub.status.idle":"2026-01-29T08:10:52.325483Z","shell.execute_reply.started":"2026-01-29T08:10:50.119215Z","shell.execute_reply":"2026-01-29T08:10:52.324662Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Now you can instantiate the Dataset and DataLoader\n# IMAGE_ROOT_DIR = '/kaggle/input/csiro-biomass/train/' # Set this to the parent of the 'train/' folder\n# train_dataset = MultimodalDataset(df_processed=df_final, \n#                                    image_dir=IMAGE_ROOT_DIR,\n#                                  transform = image_transform)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:52.326074Z","iopub.execute_input":"2026-01-29T08:10:52.326249Z","iopub.status.idle":"2026-01-29T08:10:52.328955Z","shell.execute_reply.started":"2026-01-29T08:10:52.326232Z","shell.execute_reply":"2026-01-29T08:10:52.328275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # 1. Define split sizes (e.g., 80% training, 20% validation)\n# train_size = int(0.8 * len(train_dataset))\n# val_size = len(train_dataset) - train_size\n\n# # 2. Randomly split the dataset\n# train_subset, val_subset = random_split(train_dataset, [train_size, val_size])\n\n# # 3. Create separate DataLoaders\n# train_loader = DataLoader(train_subset, batch_size=16, shuffle=True)\n# val_loader = DataLoader(val_subset, batch_size=16, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:52.329485Z","iopub.execute_input":"2026-01-29T08:10:52.329671Z","iopub.status.idle":"2026-01-29T08:10:52.344771Z","shell.execute_reply.started":"2026-01-29T08:10:52.329653Z","shell.execute_reply":"2026-01-29T08:10:52.344121Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(NUM_EPOCHS):\n    # --- TRAINING PHASE ---\n    model.train()  # Set model to training mode\n    train_total_loss = 0\n    print(f'Epoch {epoch+1}/{NUM_EPOCHS} Training', end='')\n    \n    for image_batch, label_batch in train_loader:\n        print(\"-\", end='')\n        image_batch, label_batch = image_batch.to(device), label_batch.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(image_batch)\n        \n        # Ensure target shape is [batch_size, 5]\n        label_batch = label_batch.squeeze(1) if label_batch.dim() == 3 else label_batch\n        \n        loss = criterion(outputs, label_batch)\n        loss.backward()\n        optimizer.step()\n        \n        train_total_loss += loss.item()\n\n    avg_train_loss = train_total_loss / len(train_loader)\n\n    # --- VALIDATION PHASE ---\n    model.eval()  # Set model to evaluation mode (turns off Dropout/BatchNorm)\n    val_total_loss = 0\n    \n    with torch.no_grad():  # Disable gradient calculation for speed and memory\n        for val_images, val_labels in val_loader:\n            val_images, val_labels = val_images.to(device), val_labels.to(device)\n            \n            val_outputs = model(val_images)\n            val_labels = val_labels.squeeze(1) if val_labels.dim() == 3 else val_labels\n            \n            v_loss = criterion(val_outputs, val_labels)\n            val_total_loss += v_loss.item()\n            \n    avg_val_loss = val_total_loss / len(val_loader)\n    \n    # --- RESULTS ---\n    print(f\"\\n[Epoch {epoch+1}] Train Loss: {avg_train_loss:.2f} | Val Loss: {avg_val_loss:.2f}\")\n  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:10:52.345192Z","iopub.execute_input":"2026-01-29T08:10:52.345366Z","iopub.status.idle":"2026-01-29T08:54:36.494680Z","shell.execute_reply.started":"2026-01-29T08:10:52.345349Z","shell.execute_reply":"2026-01-29T08:54:36.493651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_original = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\ntest_original['image_path'] = '/kaggle/input/csiro-biomass/'+test_original['image_path']\ntest_original_no_id = test_original[['image_path','target_name']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:54:36.495858Z","iopub.execute_input":"2026-01-29T08:54:36.496750Z","iopub.status.idle":"2026-01-29T08:54:36.509288Z","shell.execute_reply.started":"2026-01-29T08:54:36.496726Z","shell.execute_reply":"2026-01-29T08:54:36.508563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.DataFrame(test_original_no_id.groupby('image_path')['target_name'].apply(list)).reset_index()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:54:36.509753Z","iopub.execute_input":"2026-01-29T08:54:36.509919Z","iopub.status.idle":"2026-01-29T08:54:36.514406Z","shell.execute_reply.started":"2026-01-29T08:54:36.509902Z","shell.execute_reply":"2026-01-29T08:54:36.513709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MultimodalDataset(Dataset):\n    def __init__(self, df_processed, image_dir, transform=None, is_test=False):\n        self.data_frame = df_processed\n        self.image_dir = image_dir \n        self.transform = transform\n        self.is_test = is_test # Flag to identify if this is a test set\n\n    def __len__(self):\n        return len(self.data_frame)\n\n    def __getitem__(self, idx):\n        row = self.data_frame.iloc[idx]\n        \n        # 1. --- IMAGE DATA LOADING ---\n        img_name_relative = row['image_path'] \n        full_img_path = img_name_relative \n        \n        try:\n            image = Image.open(full_img_path).convert('RGB')\n            if self.transform:\n                image_tensor = self.transform(image)\n            else:\n                # Default fallback if no transform is provided\n                import torchvision.transforms as T\n                image_tensor = T.ToTensor()(image)\n        except FileNotFoundError:\n             print(f\"Warning: Image file not found at {full_img_path}\")\n             # Ensure IMAGE_SIZE is defined globally (e.g., 224)\n             image_tensor = torch.zeros(3, IMAGE_SIZE, IMAGE_SIZE, dtype=torch.float32)\n\n        # 2. --- CONDITIONAL RETURN ---\n        if self.is_test:\n            # For the test set, return only the features needed for prediction\n            return image_tensor\n        else:\n            # For training/validation, return features and the target label\n            label = row['target']\n            label_tensor = torch.tensor(label, dtype=torch.float32).unsqueeze(0)\n            return image_tensor, label_tensor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:54:36.514831Z","iopub.execute_input":"2026-01-29T08:54:36.514991Z","iopub.status.idle":"2026-01-29T08:54:36.519958Z","shell.execute_reply.started":"2026-01-29T08:54:36.514974Z","shell.execute_reply":"2026-01-29T08:54:36.519353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import math\nIMAGE_ROOT_DIR1 = '/kaggle/input/csiro-biomass/test/' #Set this to the parent of the 'train/' folder\ntest_dataset = MultimodalDataset(\n    df_processed=test_df, \n    image_dir=IMAGE_ROOT_DIR1, \n    transform=val_transform, \n    is_test=True\n)\ntest_loader = DataLoader(test_dataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:11.528670Z","iopub.execute_input":"2026-01-29T08:57:11.528939Z","iopub.status.idle":"2026-01-29T08:57:11.532249Z","shell.execute_reply.started":"2026-01-29T08:57:11.528918Z","shell.execute_reply":"2026-01-29T08:57:11.531575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_features = next(iter(test_loader))\nprint(f\"image_tensor batch shape:{test_features[0].size()}\")\nprint(f\"features batch shape: {test_features[0].size()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:13.483998Z","iopub.execute_input":"2026-01-29T08:57:13.484201Z","iopub.status.idle":"2026-01-29T08:57:13.610826Z","shell.execute_reply.started":"2026-01-29T08:57:13.484186Z","shell.execute_reply":"2026-01-29T08:57:13.610351Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()  # Step 1: Set model to evaluation mode\ntest_loss = 0\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():  # Step 2: Disable gradient calculation\n    for image_batch in test_loader:\n        if isinstance(image_batch, (list, tuple)):\n            # Move to device\n            image_batch = image_batch[0].to(device)\n            # tabular_batch = tabular_batch.to(device)\n        else:\n            image_batch = image_batch.to(device)\n\n        # Forward pass\n        outputs = model(image_batch)\n\n        # Collect results for metrics\n        all_preds.append(outputs.cpu().squeeze())\n        # all_preds = outputs.cpu().squeeze().tolist()","metadata":{"scrolled":true,"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:13.764552Z","iopub.execute_input":"2026-01-29T08:57:13.764770Z","iopub.status.idle":"2026-01-29T08:57:13.999109Z","shell.execute_reply.started":"2026-01-29T08:57:13.764742Z","shell.execute_reply":"2026-01-29T08:57:13.998203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_preds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:14.034902Z","iopub.execute_input":"2026-01-29T08:57:14.035077Z","iopub.status.idle":"2026-01-29T08:57:14.046478Z","shell.execute_reply.started":"2026-01-29T08:57:14.035060Z","shell.execute_reply":"2026-01-29T08:57:14.045749Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_original","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:16.472387Z","iopub.execute_input":"2026-01-29T08:57:16.472637Z","iopub.status.idle":"2026-01-29T08:57:16.479037Z","shell.execute_reply.started":"2026-01-29T08:57:16.472617Z","shell.execute_reply":"2026-01-29T08:57:16.478352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport numpy as np\n\n# 1. Concatenate all batches in the list into one single Tensor\n# Use torch.cat to join them along the 0th dimension\npredictions_tensor = torch.cat(all_preds, dim=0)\n\n# 2. Convert to a flat NumPy array\n# We use .cpu() to ensure it's not on GPU, .detach() to remove from graph, \n# and .numpy() to convert it. .flatten() ensures it's a 1D array.\npredictions_array = predictions_tensor.cpu().detach().numpy().flatten()\n\n# 3. Add to your DataFrame\n# Replace 'test_final' with the name of your test DataFrame\n# Replace 'target_name' with your specific column name (e.g., 'target' or 'prediction')\ntest_original['target_name'] = predictions_array\n\n# 4. Verification\ntest_original = test_original[['sample_id', 'target_name']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:16.827875Z","iopub.execute_input":"2026-01-29T08:57:16.828047Z","iopub.status.idle":"2026-01-29T08:57:16.832368Z","shell.execute_reply.started":"2026-01-29T08:57:16.828032Z","shell.execute_reply":"2026-01-29T08:57:16.831697Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_original","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:19.533657Z","iopub.execute_input":"2026-01-29T08:57:19.533872Z","iopub.status.idle":"2026-01-29T08:57:19.539525Z","shell.execute_reply.started":"2026-01-29T08:57:19.533852Z","shell.execute_reply":"2026-01-29T08:57:19.538757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_original.to_csv('submission.csv', index = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:19.849477Z","iopub.execute_input":"2026-01-29T08:57:19.849678Z","iopub.status.idle":"2026-01-29T08:57:19.860539Z","shell.execute_reply.started":"2026-01-29T08:57:19.849659Z","shell.execute_reply":"2026-01-29T08:57:19.859811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(IMAGE_HEIGHT_SIZE,IMAGE_WIDTH_SIZE, NUM_EPOCHS, BATCH_SIZE, LEARNING_RATE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T08:57:20.419701Z","iopub.execute_input":"2026-01-29T08:57:20.419926Z","iopub.status.idle":"2026-01-29T08:57:20.423331Z","shell.execute_reply.started":"2026-01-29T08:57:20.419905Z","shell.execute_reply":"2026-01-29T08:57:20.422536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}