{"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":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":243917676,"sourceType":"kernelVersion"},{"sourceId":272527373,"sourceType":"kernelVersion"},{"sourceId":272527978,"sourceType":"kernelVersion"},{"sourceId":272529485,"sourceType":"kernelVersion"},{"sourceId":272853433,"sourceType":"kernelVersion"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# **Biomass Test Inference**","metadata":{}},{"cell_type":"markdown","source":"### Strategy for this competition\n\nThe test tabular data is missing some items compared to the training tabular data.\n\nFirst, in the training phase, we verify that we can predict targets using only the tabular data (#1).\n\nWe also verify that image data can predict items that are present in the train tabular data but not in the test tabular data (#2).\n\n**In the testing phase, we first predict the missing items in the test tabular data from the images. Finally, we use the model already trained on the training data to predict targets using the complete test tabular data (#3,this notebook).**\n\n1. https://www.kaggle.com/code/stpeteishii/biomass-train-data-visualize-importance<br>\n2. https://www.kaggle.com/code/stpeteishii/pre-gshh-ndvi-pytorch-lightning-cnn-regressor<br>\nhttps://www.kaggle.com/code/stpeteishii/height-ave-cm-pytorch-lightning-cnn-regressor<br>\nhttps://www.kaggle.com/code/stpeteishii/species-pytorch-lightning-cnn-classifier<br>\n3. https://www.kaggle.com/code/stpeteishii/biomass-test-inference<br>","metadata":{}},{"cell_type":"code","source":"#!pip install lightning","metadata":{"trusted":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:55.564504Z","iopub.execute_input":"2025-11-01T10:55:55.564870Z","iopub.status.idle":"2025-11-01T10:55:55.572492Z","shell.execute_reply.started":"2025-11-01T10:55:55.564844Z","shell.execute_reply":"2025-11-01T10:55:55.571122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!grep -v \"nvidia-\" /kaggle/input/download-lightning2/requirements.txt > requirements_no_cuda.txt\n!pip install --no-index --find-links /kaggle/input/download-lightning2/offline_packages/ -r requirements_no_cuda.txt --no-deps\n\n\nimport pytorch_lightning as L\nfrom pytorch_lightning import LightningDataModule\nfrom pytorch_lightning import LightningModule\nfrom pytorch_lightning import Trainer\nfrom pytorch_lightning import seed_everything\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping\nfrom pytorch_lightning.loggers import TensorBoardLogger, WandbLogger","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:55.574179Z","iopub.execute_input":"2025-11-01T10:55:55.574429Z","iopub.status.idle":"2025-11-01T10:55:57.791278Z","shell.execute_reply.started":"2025-11-01T10:55:55.574410Z","shell.execute_reply":"2025-11-01T10:55:57.789573Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport random\nimport time\nfrom contextlib import contextmanager\n\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset, Subset\nfrom torch.utils.data import random_split, SubsetRandomSampler\n\n#import lightning.pytorch as L\n#from lightning.pytorch import LightningDataModule\n#from lightning.pytorch import LightningModule\n#from lightning.pytorch import Trainer\n\nfrom torchvision import datasets, transforms, models\nfrom torchvision.datasets import ImageFolder\nfrom torchvision.transforms import ToTensor\nfrom torchvision.utils import make_grid\n\nimport joblib\nimport lightgbm as lgbm\nfrom sklearn.model_selection import KFold, train_test_split\nfrom sklearn.metrics import (\n    accuracy_score,\n    classification_report,\n    log_loss,\n    mean_squared_error,\n)\nfrom tensorflow.keras.utils import to_categorical # Keras utility\n\nimport category_encoders as ce","metadata":{"papermill":{"duration":8.958811,"end_time":"2021-06-21T06:51:56.66964","exception":false,"start_time":"2021-06-21T06:51:47.710829","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.793892Z","iopub.execute_input":"2025-11-01T10:55:57.794339Z","iopub.status.idle":"2025-11-01T10:55:57.804167Z","shell.execute_reply.started":"2025-11-01T10:55:57.794297Z","shell.execute_reply":"2025-11-01T10:55:57.803093Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data preparation","metadata":{"papermill":{"duration":0.024602,"end_time":"2021-06-21T06:51:56.719864","exception":false,"start_time":"2021-06-21T06:51:56.695262","status":"completed"},"tags":[]}},{"cell_type":"code","source":"data0 = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\ndisplay(data0[0:3].T)\nprint(data0.columns.tolist())\ntest0=pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\ndisplay(test0[0:3].T)\nprint(test0.columns.tolist())\ndelete_cols=['image_path','Sampling_Date','State']\ndata0=data0.drop(columns=delete_cols,axis=1)\ndisplay(data0[0:3].T)\nprint(set(data0.columns.tolist())-set(test0.columns.tolist()))\n\n# In test data,'Species', 'Pre_GSHH_NDVI', and 'Height_Ave_cm' will be predicted \n# from test image data.\n# ['Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm', 'target_name', 'target']","metadata":{"papermill":{"duration":0.075867,"end_time":"2021-06-21T06:51:56.820913","exception":false,"start_time":"2021-06-21T06:51:56.745046","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.806962Z","iopub.execute_input":"2025-11-01T10:55:57.807285Z","iopub.status.idle":"2025-11-01T10:55:57.862654Z","shell.execute_reply.started":"2025-11-01T10:55:57.807260Z","shell.execute_reply":"2025-11-01T10:55:57.861394Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"names=sorted(data0['Species'].unique().tolist())\nname_mapping=dict(zip(names,list(range(len(names)))))\nprint(name_mapping)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.863718Z","iopub.execute_input":"2025-11-01T10:55:57.864079Z","iopub.status.idle":"2025-11-01T10:55:57.872097Z","shell.execute_reply.started":"2025-11-01T10:55:57.864053Z","shell.execute_reply":"2025-11-01T10:55:57.870688Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"class CustomDataset(torch.utils.data.Dataset):\n    def __init__(self, path_label, transform=None):\n        self.path_label = path_label\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.path_label)\n\n    def __getitem__(self, idx):\n        path, label = self.path_label[idx]\n        img = Image.open(path).convert('RGB')\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.873218Z","iopub.execute_input":"2025-11-01T10:55:57.873600Z","iopub.status.idle":"2025-11-01T10:55:57.894941Z","shell.execute_reply.started":"2025-11-01T10:55:57.873575Z","shell.execute_reply":"2025-11-01T10:55:57.893899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"        \nclass DataModule(LightningDataModule):\n    def __init__(self, path_label=None, root_dir=None, batch_size=32):\n        super().__init__()\n        self.path_label = path_label\n        self.root_dir = root_dir\n        self.batch_size = batch_size\n        \n        # Define your transforms\n        self.transform = transforms.Compose([\n            transforms.Resize(224),             # resize shortest side to 224 pixels\n            transforms.CenterCrop(224),         # crop longest side to 224 pixels at center            \n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406],\n                               [0.229, 0.224, 0.225])\n        ])\n        \n        # Initialize datasets\n        self.train_dataset = None\n        self.test_dataset = None\n\n    def setup(self, stage=None):\n        # Create dataset based on what's provided\n        if self.path_label is not None:\n            dataset = CustomDataset(self.path_label, self.transform)\n        elif self.root_dir is not None:\n            dataset = datasets.ImageFolder(root=self.root_dir, transform=self.transform)\n        else:\n            raise ValueError(\"Either path_label or root_dir must be provided\")\n        \n        dataset_size = len(dataset)\n        train_size = int(0.8 * dataset_size) \n        test_size = dataset_size - train_size\n\n        # Split dataset\n        self.train_dataset = Subset(dataset, range(train_size))\n        self.test_dataset = Subset(dataset, range(train_size, dataset_size))\n\n    def train_dataloader(self):\n        return DataLoader(self.train_dataset, batch_size=self.batch_size, shuffle=True)\n\n    def test_dataloader(self):\n        return DataLoader(self.test_dataset, batch_size=self.batch_size)\n\n    def predict_dataloader(self):\n        return DataLoader(self.test_dataset, batch_size=self.batch_size)\n\n    def __len__(self):\n        if self.train_dataset is not None:\n            return len(self.train_dataset)\n        elif self.test_dataset is not None:\n            return len(self.test_dataset)\n        else:\n            return 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.896155Z","iopub.execute_input":"2025-11-01T10:55:57.896513Z","iopub.status.idle":"2025-11-01T10:55:57.918851Z","shell.execute_reply.started":"2025-11-01T10:55:57.896458Z","shell.execute_reply":"2025-11-01T10:55:57.917736Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ConvolutionalRegressor(LightningModule):\n    \n    def __init__(self):\n        super(ConvolutionalRegressor, self).__init__()\n        self.conv1 = nn.Conv2d(3, 6, 3, 1)\n        self.conv2 = nn.Conv2d(6, 16, 3, 1)\n        self.fc1 = nn.Linear(16 * 54 * 54, 120)\n        self.fc2 = nn.Linear(120, 84)\n        self.fc3 = nn.Linear(84, 20)\n        self.fc4 = nn.Linear(20, 1)  # Regression: output 1 value\n\n    def forward(self, X):\n        X = F.relu(self.conv1(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv2(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = X.view(-1, 16 * 54 * 54)\n        X = F.relu(self.fc1(X))\n        X = F.relu(self.fc2(X))\n        X = F.relu(self.fc3(X))\n        X = self.fc4(X)  # Output a continuous value\n        return X.squeeze(1)  # Output shape: [batch_size]\n\n    def configure_optimizers(self):\n        return torch.optim.Adam(self.parameters(), lr=0.001)\n\n    def training_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.mse_loss(y_hat, y.float())\n        self.log(\"train_loss\", loss)\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.mse_loss(y_hat, y.float())\n        self.log(\"val_loss\", loss)\n\n    def test_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.mse_loss(y_hat, y.float())\n        self.log(\"test_loss\", loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.919935Z","iopub.execute_input":"2025-11-01T10:55:57.920219Z","iopub.status.idle":"2025-11-01T10:55:57.950006Z","shell.execute_reply.started":"2025-11-01T10:55:57.920197Z","shell.execute_reply":"2025-11-01T10:55:57.948317Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ConvolutionalClassifier(LightningModule):\n    \n    def __init__(self, num_classes=len(names)):\n        super(ConvolutionalClassifier, self).__init__()\n        self.save_hyperparameters()\n        \n        # Keep convolutional layers unchanged\n        self.conv1 = nn.Conv2d(3, 6, 3, 1)\n        self.conv2 = nn.Conv2d(6, 16, 3, 1)\n        self.fc1 = nn.Linear(16 * 54 * 54, 120)\n        self.fc2 = nn.Linear(120, 84)\n        self.fc3 = nn.Linear(84, 20)\n        # Modified for classification: output for num_classes\n        self.fc4 = nn.Linear(20, num_classes)\n\n    def forward(self, X):\n        X = F.relu(self.conv1(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv2(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = X.view(-1, 16 * 54 * 54)\n        X = F.relu(self.fc1(X))\n        X = F.relu(self.fc2(X))\n        X = F.relu(self.fc3(X))\n        X = self.fc4(X)  # Output for number of classes\n        return X  # Output shape: [batch_size, num_classes]\n\n    def configure_optimizers(self):\n        return torch.optim.Adam(self.parameters(), lr=0.001)\n\n    def training_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        # Regression → Classification: MSE Loss → Cross Entropy Loss\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"train_loss\", loss)\n        \n        # Also record accuracy\n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"train_acc\", acc)\n        \n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"val_loss\", loss)\n        \n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"val_acc\", acc)\n\n    def test_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"test_loss\", loss)\n        \n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"test_acc\", acc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.951309Z","iopub.execute_input":"2025-11-01T10:55:57.951615Z","iopub.status.idle":"2025-11-01T10:55:57.974388Z","shell.execute_reply.started":"2025-11-01T10:55:57.951593Z","shell.execute_reply":"2025-11-01T10:55:57.972563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fix the transform to match training (224x224, not 128x128)\ntransform = transforms.Compose([\n    transforms.Resize(224),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\ndef predict_single_image(image_path, model):\n    \"\"\"Predict for a single image\"\"\"\n    image = Image.open(image_path).convert('RGB')\n    image_tensor = transform(image).unsqueeze(0) \n\n    with torch.no_grad():\n        prediction = model(image_tensor)\n    \n    return prediction.item() if prediction.dim() == 0 else prediction.cpu().numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:57.976893Z","iopub.execute_input":"2025-11-01T10:55:57.977874Z","iopub.status.idle":"2025-11-01T10:55:58.004444Z","shell.execute_reply.started":"2025-11-01T10:55:57.977827Z","shell.execute_reply":"2025-11-01T10:55:58.002957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fixed version with better error handling\n\nimport os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import transforms\nimport joblib\n\n# ===== Fix 1: Ensure all images are processed =====\ndef predict_batch(image_folder, model):\n    \"\"\"Predict for all images in a folder with error handling\"\"\"\n    if not os.path.exists(image_folder):\n        print(f\"Warning: Folder {image_folder} does not exist\")\n        return {}\n    \n    image_paths = [os.path.join(image_folder, f) for f in os.listdir(image_folder) \n                  if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\n    \n    print(f\"Found {len(image_paths)} images to process\")\n    \n    transform = transforms.Compose([\n        transforms.Resize(224),\n        transforms.CenterCrop(224),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    predictions = {}\n\n    for image_path in image_paths:\n        try:\n            pred = predict_single_image(image_path, model)\n            # Extract image ID from filename (e.g., ID1001187975 from ID1001187975.jpg)\n            image_id = os.path.basename(image_path).split('.')[0]\n            #-----------------\n            if isinstance(pred, (list, np.ndarray)):\n                predictions[image_id] = float(pred[0]) if len(pred) > 0 else float(pred)\n            elif isinstance(pred, torch.Tensor):\n                predictions[image_id] = pred.item() if pred.dim() == 0 else float(pred.flatten()[0])\n            else:\n                predictions[image_id] = float(pred)\n            #--------------\n            #predictions[image_id] = pred[0]\n            print(f\"{os.path.basename(image_path)}: {pred[0]}\")\n        except Exception as e:\n            print(f\"Error processing {image_path}: {e}\")\n            import traceback\n            traceback.print_exc()\n    \n    return predictions\n\ndef predict_batch_classification(image_folder, model):\n    \"\"\"Predict species class for all images\"\"\"\n    if not os.path.exists(image_folder):\n        print(f\"Warning: Folder {image_folder} does not exist\")\n        return {}\n    \n    image_paths = [os.path.join(image_folder, f) for f in os.listdir(image_folder) \n                  if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\n    \n    print(f\"Found {len(image_paths)} images to process\")\n    \n    transform = transforms.Compose([\n        transforms.Resize(224),\n        transforms.CenterCrop(224),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    predictions = {}\n    for image_path in image_paths:\n        try:\n            image = Image.open(image_path).convert('RGB')\n            image_tensor = transform(image).unsqueeze(0)\n            \n            with torch.no_grad():\n                logits = model(image_tensor)\n                pred_class = torch.argmax(logits, dim=1).item()\n            \n            image_id = os.path.basename(image_path).split('.')[0]\n            predictions[image_id] = pred_class\n            \n        except Exception as e:\n            print(f\"Error processing {os.path.basename(image_path)}: {e}\")\n    \n    return predictions\n\n# ===== Fix 2: Handle missing target_names properly =====\ndef prepare_test_data(test_df, species_preds, ndvi_preds, height_preds, train_df):\n    \"\"\"Prepare test data with proper handling of mappings\"\"\"\n    \n    # Extract image ID\n    test_df['image_id'] = test_df['image_path'].apply(lambda x: os.path.basename(x).split('.')[0])\n    \n    # Create target_name mapping from training data ####\n    names = sorted(train_df['target_name'].unique().tolist())\n    name_mapping = dict(zip(names, range(len(names))))\n    \n    # Map target_name - keep original if not in mapping\n    test_df['target_name_encoded'] = test_df['target_name'].map(name_mapping)\n    \n    # Check for unmapped target names\n    unmapped = test_df[test_df['target_name_encoded'].isna()]['target_name'].unique()\n    if len(unmapped) > 0:\n        print(f\"Warning: Found unmapped target names: {unmapped}\")\n        # Assign new indices for unmapped names\n        max_idx = max(name_mapping.values())\n        for i, name in enumerate(unmapped):\n            name_mapping[name] = max_idx + i + 1\n        test_df['target_name_encoded'] = test_df['target_name'].map(name_mapping)\n    \n    # Map predictions\n    test_df['Species'] = test_df['image_id'].map(species_preds)\n    test_df['Pre_GSHH_NDVI'] = test_df['image_id'].map(ndvi_preds)\n    test_df['Height_Ave_cm'] = test_df['image_id'].map(height_preds)\n    \n    # Calculate fallback values from training data\n    species_fallback = train_df['Species'].mode()[0] if 'Species' in train_df.columns else 0\n    ndvi_fallback = train_df['Pre_GSHH_NDVI'].median() if 'Pre_GSHH_NDVI' in train_df.columns else 0.5\n    height_fallback = train_df['Height_Ave_cm'].median() if 'Height_Ave_cm' in train_df.columns else 5.0\n    \n    # Fill missing values\n    test_df['Species'] = test_df['Species'].fillna(species_fallback).astype(int)\n    test_df['Pre_GSHH_NDVI'] = test_df['Pre_GSHH_NDVI'].fillna(ndvi_fallback).astype(float)\n    test_df['Height_Ave_cm'] = test_df['Height_Ave_cm'].fillna(height_fallback).astype(float)\n    \n    # Prepare features\n    X_test = test_df[['target_name_encoded', 'Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm']].copy()\n    X_test.columns = ['target_name', 'Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm']  # Rename for compatibility\n    \n    return X_test, test_df\n\n# ===== Fix 3: Add model loading error handling =====\ndef safe_load_model(model_path, model_class, **kwargs):\n    \"\"\"Safely load a model with error handling\"\"\"\n    try:\n        if not os.path.exists(model_path):\n            print(f\"Error: Model file not found: {model_path}\")\n            return None\n        \n        model = model_class.load_from_checkpoint(model_path, **kwargs)\n        model.eval()\n        return model\n    except Exception as e:\n        print(f\"Error loading model from {model_path}: {e}\")\n        import traceback\n        traceback.print_exc()\n        return None\n\n# ===== Main execution with better error handling =====\nprint(\"=\" * 50)\nprint(\"Starting prediction pipeline...\")\nprint(\"=\" * 50)\n\n# Load data\ntry:\n    train_df = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\n    test_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\n    print(f\"Loaded train data: {len(train_df)} rows\")\n    print(f\"Loaded test data: {len(test_df)} rows\")\nexcept Exception as e:\n    print(f\"Error loading data: {e}\")\n    raise\n\ntest_folder = \"/kaggle/input/csiro-biomass/test\"\n\n# Predict with error handling for each model\nndvi_preds = {}\nheight_preds = {}\nspecies_preds = {}\n\n# NDVI Model\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Predicting Pre_GSHH_NDVI...\")\nprint(\"=\" * 50)\nndvi_model = safe_load_model(\n    \"/kaggle/input/pre-gshh-ndvi-pytorch-lightning-cnn-regressor/lightning_logs/version_0/checkpoints/epoch=999-step=9000.ckpt\",\n    ConvolutionalRegressor\n)\nif ndvi_model:\n    ndvi_preds = predict_batch(test_folder, ndvi_model)\n    print(f\"NDVI predictions: {len(ndvi_preds)} images\")\n\n# Height Model\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Predicting Height_Ave_cm...\")\nprint(\"=\" * 50)\nheight_model = safe_load_model(\n    \"/kaggle/input/height-ave-cm-pytorch-lightning-cnn-regressor/lightning_logs/version_0/checkpoints/epoch=999-step=9000.ckpt\",\n    ConvolutionalRegressor\n)\nif height_model:\n    height_preds = predict_batch(test_folder, height_model)\n    print(f\"Height predictions: {len(height_preds)} images\")\n\n# Species Model\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Predicting Species...\")\nprint(\"=\" * 50)\nspecies_model = safe_load_model(\n    \"/kaggle/input/species-pytorch-lightning-cnn-classifieri/lightning_logs/version_0/checkpoints/epoch=999-step=9000.ckpt\",\n    ConvolutionalClassifier,\n    num_classes=15\n)\nif species_model:\n    species_preds = predict_batch_classification(test_folder, species_model)\n    print(f\"Species predictions: {len(species_preds)} images\")\n\n# Prepare test data\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Preparing test data...\")\nprint(\"=\" * 50)\nX_test, test_df_processed = prepare_test_data(test_df, species_preds, ndvi_preds, height_preds, train_df)\nprint(\"Test data prepared successfully\")\nprint(X_test.head())\n\n# LightGBM Predictions\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Predicting with LightGBM ensemble...\")\nprint(\"=\" * 50)\n\ntry:\n    loaded_models = joblib.load('/kaggle/input/biomass-train-data-visualize-importance/models/all_models_target0.joblib')\n    print(f\"Loaded {len(loaded_models)} models\")\n    \n    all_predictions = []\n    for fold_idx, model in enumerate(loaded_models):\n        pred_fold = model.predict(X_test)\n        all_predictions.append(pred_fold)\n    \n    final_predictions = np.mean(all_predictions, axis=0)\n    print(f\"Generated {len(final_predictions)} predictions\")\n    print(f\"Prediction range: [{final_predictions.min():.2f}, {final_predictions.max():.2f}]\")\n    \nexcept Exception as e:\n    print(f\"Error in LightGBM prediction: {e}\")\n    import traceback\n    traceback.print_exc()\n    # Create dummy predictions as fallback\n    final_predictions = np.zeros(len(test_df))\n    print(\"Using fallback predictions (zeros)\")\n\n# Create submission\nsubmit = test0[['sample_id']]\nsubmit['target'] = final_predictions\nsubmit.to_csv('submission.csv', index=False)\nprint(\"\\n\" + \"=\" * 50)\nprint(\"Submission file created successfully!\")\nprint(\"=\" * 50)\nprint(submit.head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T10:55:58.005694Z","iopub.execute_input":"2025-11-01T10:55:58.006176Z","iopub.status.idle":"2025-11-01T10:56:02.164299Z","shell.execute_reply.started":"2025-11-01T10:55:58.006139Z","shell.execute_reply":"2025-11-01T10:56:02.163098Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **This approach was successfully completed, but resulted in a low score, which we believe is due to inaccurate species prediction from images, and some change in strategy is essential.**","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}