{"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":660130,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":499265,"modelId":514509}],"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\nfrom PIL import Image\nimport timm\n\n# Model definition\nclass BiomassModel(nn.Module):\n    def __init__(self, num_targets=3):\n        super(BiomassModel, self).__init__()\n        self.backbone = timm.create_model('vit_base_patch16_224', \n                                         pretrained=False, \n                                         num_classes=0)\n        feature_dim = self.backbone.num_features\n        total_features = feature_dim + 2\n        \n        self.regressor = nn.Sequential(\n            nn.Linear(total_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(512, 256),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(256, num_targets)\n        )\n    \n    def forward(self, x, ndvi, height):\n        image_features = self.backbone(x)\n        additional_features = torch.stack([ndvi, height], dim=1)\n        combined_features = torch.cat([image_features, additional_features], dim=1)\n        outputs = self.regressor(combined_features)\n        return outputs\n\n# Test Dataset\nclass BiomassTestDataset(Dataset):\n    def __init__(self, csv_file, image_dir, transform=None):\n        self.data = pd.read_csv(csv_file)\n        self.image_dir = image_dir\n        self.transform = transform\n        \n        # Create a mapping from image to its entries\n        self.image_groups = {}\n        for image_path in self.data['image_path'].unique():\n            self.image_groups[image_path] = self.data[self.data['image_path'] == image_path]\n    \n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        image_path = os.path.join(self.image_dir, row['image_path'])\n        \n        # Check if file exists, if not try alternative path\n        if not os.path.exists(image_path):\n            # Try without the 'test/' prefix since images might be in root test directory\n            alt_path = os.path.join('/kaggle/input/csiro-biomass', row['image_path'])\n            if os.path.exists(alt_path):\n                image_path = alt_path\n            else:\n                # Create a blank image if file not found\n                print(f\"Warning: Image not found: {image_path}\")\n                image = Image.new('RGB', (224, 224), color='white')\n                if self.transform:\n                    image = self.transform(image)\n                return {\n                    'image': image,\n                    'sample_id': row['sample_id'],\n                    'target_name': row['target_name'],\n                    'image_path': row['image_path']\n                }\n        \n        image = Image.open(image_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        return {\n            'image': image,\n            'sample_id': row['sample_id'],\n            'target_name': row['target_name'],\n            'image_path': row['image_path']\n        }\n\ndef run_inference():\n    # Device configuration\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f\"Using device: {device}\")\n    \n    # Load test data\n    test_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\n    print(f\"Test data shape: {test_df.shape}\")\n    print(\"Test data sample:\")\n    print(test_df.head())\n    \n    # Transform for test data\n    test_transform = transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    # Create test dataset and dataloader\n    test_dataset = BiomassTestDataset(\n        csv_file='/kaggle/input/csiro-biomass/test.csv',\n        image_dir='/kaggle/input/csiro-biomass',\n        transform=test_transform\n    )\n    \n    test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=4)\n    \n    # Load model\n    model = BiomassModel(num_targets=3)\n    \n    # Load the trained weights from the specified path\n    model_path = '/kaggle/input/dino/pytorch/default/1/biomass_model.pth'\n    \n    if os.path.exists(model_path):\n        print(f\"Loading model from: {model_path}\")\n        # Handle DataParallel wrapped models\n        state_dict = torch.load(model_path, map_location=device)\n        \n        # Check if the state dict is from DataParallel\n        if all(key.startswith('module.') for key in state_dict.keys()):\n            # Remove 'module.' prefix from keys if model was saved with DataParallel\n            from collections import OrderedDict\n            new_state_dict = OrderedDict()\n            for k, v in state_dict.items():\n                name = k[7:]  # remove 'module.'\n                new_state_dict[name] = v\n            state_dict = new_state_dict\n            print(\"Removed 'module.' prefix from state dict keys\")\n        \n        # Load the state dict\n        model.load_state_dict(state_dict)\n        print(\"Model loaded successfully!\")\n    else:\n        print(f\"Model file not found at: {model_path}\")\n        print(\"Available files in directory:\")\n        model_dir = '/kaggle/input/dino/pytorch/default/1/'\n        if os.path.exists(model_dir):\n            for file in os.listdir(model_dir):\n                print(f\"  {file}\")\n        # Create a dummy submission file\n        submission_df = test_df[['sample_id']].copy()\n        submission_df['target'] = 0.0\n        submission_df.to_csv('submission.csv', index=False)\n        print(\"Created dummy submission file with zeros\")\n        return\n    \n    # Use DataParallel for inference if multiple GPUs are available\n    if torch.cuda.device_count() > 1:\n        model = nn.DataParallel(model)\n        print(f\"Using {torch.cuda.device_count()} GPUs for inference\")\n    \n    model.to(device)\n    model.eval()\n    \n    # For inference, we need to predict for each image and then distribute the predictions\n    # Since we don't have NDVI and Height for test data, we'll use average values from training\n    avg_ndvi = 0.6  # Approximate average from training data\n    avg_height = 5.0  # Approximate average from training data\n    \n    # Dictionary to store predictions per image\n    image_predictions = {}\n    \n    print(\"Running inference...\")\n    with torch.no_grad():\n        for batch_idx, batch in enumerate(test_loader):\n            images = batch['image'].to(device)\n            sample_ids = batch['sample_id']\n            target_names = batch['target_name']\n            image_paths = batch['image_path']\n            \n            # Create dummy NDVI and height tensors with average values\n            batch_size = images.size(0)\n            ndvi_tensor = torch.full((batch_size,), avg_ndvi, device=device)\n            height_tensor = torch.full((batch_size,), avg_height, device=device)\n            \n            # Get predictions (three values per image)\n            predictions = model(images, ndvi_tensor, height_tensor)\n            predictions = predictions.cpu().numpy()\n            \n            # Store predictions by image path\n            for i in range(batch_size):\n                img_path = image_paths[i]\n                if img_path not in image_predictions:\n                    image_predictions[img_path] = predictions[i]\n            \n            if (batch_idx + 1) % 10 == 0:\n                print(f\"Processed {batch_idx + 1}/{len(test_loader)} batches\")\n    \n    print(f\"Processed {len(image_predictions)} unique images\")\n    \n    # Create submission dataframe\n    submission_data = []\n    \n    for idx, row in test_df.iterrows():\n        sample_id = row['sample_id']\n        target_name = row['target_name']\n        image_path = row['image_path']\n        \n        if image_path in image_predictions:\n            preds = image_predictions[image_path]\n            clover_pred, dead_pred, green_pred = preds\n            \n            # Calculate values based on target_name\n            if target_name == 'Dry_Clover_g':\n                target_value = max(0.0, clover_pred)\n            elif target_name == 'Dry_Dead_g':\n                target_value = max(0.0, dead_pred)\n            elif target_name == 'Dry_Green_g':\n                target_value = max(0.0, green_pred)\n            elif target_name == 'Dry_Total_g':\n                target_value = max(0.0, clover_pred + dead_pred + green_pred)\n            elif target_name == 'GDM_g':\n                target_value = max(0.0, clover_pred + green_pred)  # 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    print(f\"Submission shape: {submission_df.shape}\")\n    print(\"\\nFirst few rows of submission:\")\n    print(submission_df.head(10))\n    \n    # Show some statistics\n    print(f\"\\nSubmission statistics:\")\n    print(f\"Target value range: [{submission_df['target'].min():.4f}, {submission_df['target'].max():.4f}]\")\n    print(f\"Mean target value: {submission_df['target'].mean():.4f}\")\n    \n    # Show distribution by target type\n    target_types = submission_df['sample_id'].str.split('__').str[-1].value_counts()\n    print(f\"\\nTarget type distribution:\")\n    for target_type, count in target_types.items():\n        mean_val = submission_df[submission_df['sample_id'].str.endswith(target_type)]['target'].mean()\n        print(f\"  {target_type}: {count} samples, mean: {mean_val:.4f}\")\n\n# Alternative function if you want to test with a specific model\ndef test_model_loading():\n    \"\"\"Test if the model can be loaded correctly\"\"\"\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    model = BiomassModel(num_targets=3)\n    \n    model_path = '/kaggle/input/dino/pytorch/default/1/biomass_model.pth'\n    \n    if os.path.exists(model_path):\n        print(f\"Testing model loading from: {model_path}\")\n        state_dict = torch.load(model_path, map_location=device)\n        \n        # Check state dict structure\n        print(f\"State dict keys sample: {list(state_dict.keys())[:5]}\")\n        \n        # Try to load\n        try:\n            model.load_state_dict(state_dict)\n            print(\"✓ Model loaded successfully!\")\n            return True\n        except Exception as e:\n            print(f\"✗ Error loading model: {e}\")\n            \n            # Try with DataParallel handling\n            try:\n                from collections import OrderedDict\n                new_state_dict = OrderedDict()\n                for k, v in state_dict.items():\n                    if k.startswith('module.'):\n                        name = k[7:]  # remove 'module.'\n                    else:\n                        name = k\n                    new_state_dict[name] = v\n                model.load_state_dict(new_state_dict)\n                print(\"✓ Model loaded successfully after removing 'module.' prefix!\")\n                return True\n            except Exception as e2:\n                print(f\"✗ Still failed: {e2}\")\n                return False\n    else:\n        print(f\"Model file not found at: {model_path}\")\n        return False\n\nif __name__ == \"__main__\":\n    # First test if model loads correctly\n    if test_model_loading():\n        print(\"\\n\" + \"=\"*50)\n        print(\"Starting full inference...\")\n        print(\"=\"*50)\n        run_inference()\n    else:\n        print(\"Model loading test failed. Cannot proceed with inference.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-25T08:34:01.748574Z","iopub.execute_input":"2025-11-25T08:34:01.749106Z","iopub.status.idle":"2025-11-25T08:34:22.274589Z","shell.execute_reply.started":"2025-11-25T08:34:01.749072Z","shell.execute_reply":"2025-11-25T08:34:22.273584Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}