{"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,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":1803569,"sourceType":"datasetVersion","datasetId":1071580},{"sourceId":13673594,"sourceType":"datasetVersion","datasetId":8694330}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nfrom PIL import Image\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Configuration\nclass Config:\n    batch_size = 16\n    image_size = 500\n\n# Model Architecture (MUST match your training architecture)\nclass BiomassModel(nn.Module):\n    def __init__(self):\n        super(BiomassModel, self).__init__()\n        # Use ResNet50 as backbone\n        self.backbone = models.resnet50(pretrained=False)\n        \n        # Replace the final fully connected layer\n        num_features = self.backbone.fc.in_features\n        self.backbone.fc = nn.Identity()  # Remove the original fc layer\n        \n        # Create separate heads for each biomass component\n        self.head_clover = nn.Sequential(\n            nn.Dropout(0.3),\n            nn.Linear(num_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 1)\n        )\n        \n        self.head_dead = nn.Sequential(\n            nn.Dropout(0.3),\n            nn.Linear(num_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 1)\n        )\n        \n        self.head_green = nn.Sequential(\n            nn.Dropout(0.3),\n            nn.Linear(num_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 1)\n        )\n    \n    def forward(self, x):\n        features = self.backbone(x)\n        \n        clover = self.head_clover(features)\n        dead = self.head_dead(features)\n        green = self.head_green(features)\n        \n        # Concatenate all predictions\n        output = torch.cat([clover, dead, green], dim=1)\n        return output\n\n# Test Dataset\nclass TestDataset(Dataset):\n    def __init__(self, csv_file, base_dir, transform=None):\n        self.data = pd.read_csv(csv_file)\n        self.base_dir = base_dir\n        self.transform = transform\n        \n        # Get unique images\n        self.unique_images = self.data['image_path'].unique()\n        \n    def __len__(self):\n        return len(self.unique_images)\n    \n    def __getitem__(self, idx):\n        img_path = self.unique_images[idx]\n        # Construct full path - CSV paths are like 'test/ID1001187975.jpg'\n        full_path = os.path.join(self.base_dir, img_path)\n        \n        try:\n            image = Image.open(full_path).convert('RGB')\n        except Exception as e:\n            print(f\"Error loading image {full_path}: {e}\")\n            # Return black image as fallback\n            image = Image.new('RGB', (Config.image_size, Config.image_size), color='black')\n        \n        if self.transform:\n            image = self.transform(image)\n            \n        return image, img_path\n\ndef main():\n    print(\"🚀 Starting CSIRO Biomass Prediction Inference...\")\n    \n    # Set device\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f\"📱 Using device: {device}\")\n    \n    # Model path - using your model file\n    model_path = '/kaggle/input/model-outputs/best_biomass_model.pth'\n    \n    # Check if model file exists\n    if not os.path.exists(model_path):\n        print(f\"❌ Error: Model file '{model_path}' not found!\")\n        return\n    \n    print(f\"✅ Model file found: {model_path}\")\n    \n    # Test transforms (must match training preprocessing)\n    test_transform = transforms.Compose([\n        transforms.Resize((Config.image_size, Config.image_size)),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    # Load test data\n    test_csv = '/kaggle/input/csiro-biomass/test.csv'\n    base_dir = '/kaggle/input/csiro-biomass'\n    \n    print(\"📊 Loading test data...\")\n    test_df = pd.read_csv(test_csv)\n    print(f\"✅ Test data loaded: {len(test_df)} rows\")\n    print(f\"📸 Sample test data:\")\n    print(test_df.head())\n    \n    # Create dataset and dataloader\n    test_dataset = TestDataset(test_csv, base_dir, test_transform)\n    test_loader = DataLoader(\n        test_dataset,\n        batch_size=Config.batch_size,\n        shuffle=False,\n        num_workers=0,  # Set to 0 to avoid multiprocessing issues in Kaggle\n        pin_memory=True\n    )\n    \n    print(f\"🎯 Found {len(test_dataset)} unique test images\")\n    \n    # Initialize model\n    print(\"🤖 Loading model...\")\n    model = BiomassModel()\n    \n    try:\n        # Load the trained weights\n        state_dict = torch.load(model_path, map_location=device)\n        \n        # Handle DataParallel wrapped models (if trained on multiple GPUs)\n        if all(key.startswith('module.') for key in state_dict.keys()):\n            print(\"🔧 Removing 'module.' prefix from state dict...\")\n            from collections import OrderedDict\n            new_state_dict = OrderedDict()\n            for k, v in state_dict.items():\n                name = k[7:]  # remove 'module.' prefix\n                new_state_dict[name] = v\n            state_dict = new_state_dict\n        \n        model.load_state_dict(state_dict)\n        model = model.to(device)\n        model.eval()  # Set to evaluation mode\n        print(\"✅ Model loaded successfully!\")\n        \n    except Exception as e:\n        print(f\"❌ Error loading model: {e}\")\n        return\n    \n    # Predict for each image\n    print(\"🎯 Starting prediction...\")\n    image_predictions = {}\n    \n    with torch.no_grad():\n        for batch_idx, (images, img_paths) in enumerate(test_loader):\n            print(f\"📦 Processing batch {batch_idx + 1}/{len(test_loader)}\")\n            \n            images = images.to(device)\n            outputs = model(images)\n            \n            # Convert to numpy and ensure positive values\n            predictions = outputs.cpu().numpy()\n            \n            for i, img_path in enumerate(img_paths):\n                clover, dead, green = predictions[i]\n                image_predictions[img_path] = {\n                    'Dry_Clover_g': max(0.0, float(clover)),\n                    'Dry_Dead_g': max(0.0, float(dead)),\n                    'Dry_Green_g': max(0.0, float(green))\n                }\n    \n    print(f\"✅ Prediction completed! Processed {len(image_predictions)} unique images\")\n    \n    # Create submission dataframe\n    print(\"📝 Creating submission file...\")\n    submission_data = []\n    \n    for idx, row in test_df.iterrows():\n        img_path = row['image_path']\n        target_name = row['target_name']\n        sample_id = row['sample_id']\n        \n        if img_path in image_predictions:\n            preds = image_predictions[img_path]\n            \n            if target_name == 'Dry_Clover_g':\n                target_value = preds['Dry_Clover_g']\n            elif target_name == 'Dry_Dead_g':\n                target_value = preds['Dry_Dead_g']\n            elif target_name == 'Dry_Green_g':\n                target_value = preds['Dry_Green_g']\n            elif target_name == 'Dry_Total_g':\n                # Calculate total from components\n                target_value = preds['Dry_Clover_g'] + preds['Dry_Dead_g'] + preds['Dry_Green_g']\n            elif target_name == 'GDM_g':\n                # Calculate GDM from components\n                target_value = preds['Dry_Clover_g'] + preds['Dry_Green_g']  # Total - Dead\n            else:\n                target_value = 0.0\n        else:\n            target_value = 0.0\n        \n        submission_data.append({\n            'sample_id': sample_id,\n            'target': target_value\n        })\n    \n    # Create submission file\n    submission_df = pd.DataFrame(submission_data)\n    submission_df.to_csv('submission.csv', index=False)\n    print(\"✅ Submission file saved as 'submission.csv'\")\n    \n    # Print detailed statistics\n    print(\"\\n📊 Submission Statistics:\")\n    print(f\"   Total predictions: {len(submission_df)}\")\n    print(f\"   Target range: {submission_df['target'].min():.4f} to {submission_df['target'].max():.4f}\")\n    print(f\"   Mean target: {submission_df['target'].mean():.4f}\")\n    print(f\"   Std target: {submission_df['target'].std():.4f}\")\n    \n    # Show breakdown by target type\n    print(\"\\n🔍 Breakdown by target type:\")\n    for target_type in ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']:\n        subset = submission_df[submission_df['sample_id'].str.contains(target_type)]\n        if len(subset) > 0:\n            print(f\"   {target_type}: {len(subset)} samples, mean: {subset['target'].mean():.4f}\")\n    \n    # Show first few rows of submission\n    print(\"\\n👀 First 10 rows of submission:\")\n    print(submission_df.head(10))\n    \n    print(\"\\n🎉 Inference completed successfully!\")\n\n# Run the inference\nif __name__ == '__main__':\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-10T03:40:44.995698Z","iopub.execute_input":"2025-11-10T03:40:44.995922Z","iopub.status.idle":"2025-11-10T03:40:56.697268Z","shell.execute_reply.started":"2025-11-10T03:40:44.995904Z","shell.execute_reply":"2025-11-10T03:40:56.696576Z"}},"outputs":[],"execution_count":null},{"cell_type":"raw","source":"","metadata":{"execution":{"iopub.status.busy":"2025-11-10T02:53:42.609699Z","iopub.execute_input":"2025-11-10T02:53:42.609928Z"}}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}