{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.11"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":98450,"databundleVersionId":11749951,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":1777.187198,"end_time":"2025-04-26T12:01:53.386416","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-26T11:32:16.199218","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install kornia","metadata":{"execution":{"iopub.execute_input":"2025-04-26T11:32:21.557252Z","iopub.status.busy":"2025-04-26T11:32:21.556843Z","iopub.status.idle":"2025-04-26T11:33:45.717555Z","shell.execute_reply":"2025-04-26T11:33:45.716089Z"},"papermill":{"duration":84.167092,"end_time":"2025-04-26T11:33:45.719574","exception":false,"start_time":"2025-04-26T11:32:21.552482","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torchvision.transforms as T\nfrom torch.utils.data import Dataset, DataLoader\nimport kornia.augmentation as K\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\n","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2025-04-26T11:33:45.772984Z","iopub.status.busy":"2025-04-26T11:33:45.772579Z","iopub.status.idle":"2025-04-26T11:34:07.851581Z","shell.execute_reply":"2025-04-26T11:34:07.850555Z"},"papermill":{"duration":22.108024,"end_time":"2025-04-26T11:34:07.85371","exception":false,"start_time":"2025-04-26T11:33:45.745686","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BANDS = 100\nBATCH_SIZE = 32\nEPOCHS = 25\nLEARNING_RATE = 0.001\nNUM_BANDS = 100\nDEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n\nclass HyperspectralDataset(Dataset):\n    def __init__(self, df, base_path, patch_size=64, augment=False, normalize_target=True):\n        self.df = df\n        self.base_path = base_path\n        self.patch_size = patch_size\n        self.augment = augment\n        self.normalize_target = normalize_target  \n        self.transform = nn.Sequential(\n            K.RandomHorizontalFlip(p=0.3),     \n            K.RandomVerticalFlip(p=0.3),\n            K.RandomAffine(degrees=5, translate=(0.05, 0.05), scale=(0.95, 1.05), p=0.5),\n            K.RandomCrop((patch_size, patch_size), padding=4, p=0.5)\n        )\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = f\"{self.base_path}/{row['id']}\"\n\n        try:\n            img = np.load(img_path)\n\n            if len(img.shape) == 2:\n                img = np.repeat(img[:, :, np.newaxis], NUM_BANDS, axis=2)\n            elif len(img.shape) == 3:\n                if img.shape[2] > NUM_BANDS:\n                    img = img[:, :, :NUM_BANDS]\n                elif img.shape[2] < NUM_BANDS:\n                    pad_width = ((0, 0), (0, 0), (0, NUM_BANDS - img.shape[2]))\n                    img = np.pad(img, pad_width, mode='constant')\n\n            img = img.astype(np.float32) / 65535.0  \n\n            img = torch.tensor(img, dtype=torch.float32).permute(2, 0, 1)\n\n            if self.augment:\n                img = self.transform(img.unsqueeze(0)).squeeze(0)\n\n            if img.shape[1] != self.patch_size or img.shape[2] != self.patch_size:\n                img = F.interpolate(img.unsqueeze(0), size=(self.patch_size, self.patch_size), mode='bilinear').squeeze(0)\n\n            label = torch.tensor(row['label'], dtype=torch.float32)\n            if self.normalize_target:\n                label = label / 100.0\n\n            return img, label\n\n        except Exception as e:\n            print(f\"Error loading {img_path}: {str(e)}\")\n            dummy_img = torch.zeros(NUM_BANDS, self.patch_size, self.patch_size)\n            dummy_label = torch.tensor(0.0, dtype=torch.float32)\n            return dummy_img, dummy_label\n\n\n\nclass ChannelAttention(nn.Module):\n    def __init__(self, in_channels, reduction_ratio=16):\n        super(ChannelAttention, self).__init__()\n        self.avg_pool = nn.AdaptiveAvgPool2d(1)\n        self.max_pool = nn.AdaptiveMaxPool2d(1)\n        \n        self.fc = nn.Sequential(\n            nn.Linear(in_channels, in_channels // reduction_ratio),\n            nn.ReLU(inplace=True),\n            nn.Linear(in_channels // reduction_ratio, in_channels),\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        b, c, _, _ = x.size()\n        avg_out = self.fc(self.avg_pool(x).view(b, c))\n        max_out = self.fc(self.max_pool(x).view(b, c))\n        out = avg_out + max_out\n        return out.view(b, c, 1, 1)\n\n\nclass SpatialAttention(nn.Module):\n    def __init__(self, kernel_size=7):\n        super(SpatialAttention, self).__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(2, 8, kernel_size, padding=kernel_size//2),\n            nn.ReLU(),\n            nn.Conv2d(8, 1, kernel_size=1)\n        )\n        self.sigmoid = nn.Sigmoid()\n    \n    def forward(self, x):\n        avg_out = torch.mean(x, dim=1, keepdim=True)\n        max_out, _ = torch.max(x, dim=1, keepdim=True)\n        concat = torch.cat([avg_out, max_out], dim=1)\n        attention = self.sigmoid(self.conv(concat))\n        return x * attention\n\nclass HyperspectralCNN(nn.Module):\n    def __init__(self, in_channels=NUM_BANDS):\n        super().__init__()\n        \n        self.conv1 = nn.Sequential(\n            nn.Conv2d(in_channels, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2)\n        )\n        \n        self.ca1 = ChannelAttention(64)\n        self.sa1 = SpatialAttention()\n        \n        self.conv2 = nn.Sequential(\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(2)\n        )\n        \n        self.ca2 = ChannelAttention(128)\n        self.sa2 = SpatialAttention()\n        \n        self.conv3 = nn.Sequential(\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.AdaptiveAvgPool2d(1)\n        )\n        \n        self.regressor = nn.Sequential(\n            nn.Linear(256, 128),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(128, 64),\n            nn.ReLU(),\n            nn.Linear(64, 1)\n        )\n        \n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.ca1(x) * x\n        x = self.sa1(x) * x\n        \n        x = self.conv2(x)\n        x = self.ca2(x) * x\n        x = self.sa2(x) * x\n        \n        x = self.conv3(x)\n        x = x.view(x.size(0), -1)\n        return self.regressor(x)","metadata":{"execution":{"iopub.execute_input":"2025-04-26T11:34:07.908283Z","iopub.status.busy":"2025-04-26T11:34:07.907722Z","iopub.status.idle":"2025-04-26T11:34:07.934375Z","shell.execute_reply":"2025-04-26T11:34:07.933192Z"},"papermill":{"duration":0.056151,"end_time":"2025-04-26T11:34:07.936338","exception":false,"start_time":"2025-04-26T11:34:07.880187","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample = np.load('/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/ot/ot/sample1002.npy')  # (128, 128, 125)\nplt.imshow(sample[:, :, 0])\nplt.title('First channel')\nplt.colorbar()\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2025-04-26T11:34:07.991738Z","iopub.status.busy":"2025-04-26T11:34:07.991375Z","iopub.status.idle":"2025-04-26T11:34:08.501172Z","shell.execute_reply":"2025-04-26T11:34:08.500068Z"},"papermill":{"duration":0.541771,"end_time":"2025-04-26T11:34:08.504009","exception":false,"start_time":"2025-04-26T11:34:07.962238","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_model(model, loader, criterion):\n    model.eval()\n    total_loss = 0.0\n    all_preds = []\n    all_labels = []\n    \n    with torch.no_grad():\n        for inputs, labels in loader:\n            inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)\n            outputs = model(inputs)\n            loss = criterion(outputs.squeeze(), labels)\n            total_loss += loss.item() * inputs.size(0)\n            \n            all_preds.extend(outputs.squeeze().cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n    \n    return total_loss / len(loader.dataset), np.array(all_preds), np.array(all_labels)\n\ndef train_model(model, train_loader, val_loader, epochs, criterion, optimizer):\n    best_loss = float('inf')\n    train_losses = []\n    val_losses = []\n    \n    for epoch in range(epochs):\n        model.train()\n        train_loss = 0.0\n        valid_samples = 0\n        \n        for inputs, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{epochs}\"):\n            inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)\n            \n            if torch.isnan(inputs).any() or torch.isnan(labels).any():\n                continue\n                \n            optimizer.zero_grad()\n            outputs = model(inputs)\n            \n            if torch.isnan(outputs).any():\n                continue\n                \n            loss = criterion(outputs.squeeze(), labels) \n            \n            if not torch.isnan(loss):\n                loss.backward()\n                optimizer.step()\n                train_loss += loss.item() * inputs.size(0)\n                valid_samples += inputs.size(0)\n        \n        if valid_samples > 0:\n            train_loss /= valid_samples\n            val_loss, val_preds, val_labels = evaluate_model(model, val_loader, criterion)\n            train_losses.append(train_loss)\n            val_losses.append(val_loss)\n            \n            print(f\"Epoch {epoch+1}: Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}\")\n            print(f\"Sample predictions: {val_preds[:5]}, True labels: {val_labels[:5]}\")\n            \n            if val_loss < best_loss:\n                best_loss = val_loss\n                torch.save(model.state_dict(), 'Spectrum_CNN.pth')\n        else:\n            print(f\"Epoch {epoch+1}: No valid training samples\")\n    \n    plt.figure(figsize=(8, 5))\n    plt.plot(train_losses, label='Train Loss', marker='o')\n    plt.plot(val_losses, label='Validation Loss', marker='o')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Loss')\n    plt.legend()\n    plt.grid(True)\n    plt.tight_layout()\n    plt.show()\n    \n    return model\n","metadata":{"execution":{"iopub.execute_input":"2025-04-26T11:34:08.565004Z","iopub.status.busy":"2025-04-26T11:34:08.564605Z","iopub.status.idle":"2025-04-26T11:34:08.582913Z","shell.execute_reply":"2025-04-26T11:34:08.581682Z"},"papermill":{"duration":0.050303,"end_time":"2025-04-26T11:34:08.584844","exception":false,"start_time":"2025-04-26T11:34:08.534541","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def main():\n    train_df = pd.read_csv('/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/train.csv')\n    base_path = '/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/ot/ot'\n    \n    train_df, val_df = train_test_split(train_df, test_size=0.2, random_state=42)\n    \n    train_dataset = HyperspectralDataset(train_df, base_path, augment=True)\n    val_dataset = HyperspectralDataset(val_df, base_path, augment=False)\n    \n    train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)\n    val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)\n    \n    model = HyperspectralCNN().to(DEVICE)\n    criterion = nn.MSELoss()\n    optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE, weight_decay=1e-5)\n    \n    model = train_model(model, train_loader, val_loader, EPOCHS, criterion, optimizer)\n    \n    model.load_state_dict(torch.load('Spectrum_CNN.pth'))\n    \n    return model\n\nif __name__ == '__main__':\n    model = main()","metadata":{"execution":{"iopub.execute_input":"2025-04-26T11:34:08.716203Z","iopub.status.busy":"2025-04-26T11:34:08.714666Z","iopub.status.idle":"2025-04-26T12:01:23.763908Z","shell.execute_reply":"2025-04-26T12:01:23.761998Z"},"papermill":{"duration":1635.15159,"end_time":"2025-04-26T12:01:23.765951","exception":false,"start_time":"2025-04-26T11:34:08.614361","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = HyperspectralCNN(in_channels=100).to(DEVICE)\n\nmodel.load_state_dict(torch.load('Spectrum_CNN.pth'))\nmodel.eval()\nprint(\"Model weights:\", list(model.parameters())[0][0, 0, :5])\ntest_input = torch.randn(1, 100, 64, 64).to(DEVICE)\nprint(\"Test output:\", model(test_input).item())\nclass TestHyperspectralDataset(Dataset):\n    def __init__(self, test_csv, base_path, patch_size=64, num_bands=100):\n        self.df = pd.read_csv(test_csv)\n        self.base_path = base_path\n        self.patch_size = patch_size\n        self.num_bands = num_bands\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = os.path.join(self.base_path, row['id'])\n        \n        try:\n            img = np.load(img_path)\n            \n            if len(img.shape) == 2:\n                img = np.repeat(img[:, :, np.newaxis], self.num_bands, axis=2)\n            elif len(img.shape) == 3:\n                if img.shape[2] > self.num_bands:\n                    img = img[:, :, :self.num_bands] \n                elif img.shape[2] < self.num_bands:\n                    pad_width = ((0, 0), (0, 0), (0, self.num_bands - img.shape[2]))\n                    img = np.pad(img, pad_width, mode='constant')\n            \n            normalized_img = np.zeros_like(img)\n            for band in range(img.shape[2]):\n                band_data = img[:, :, band]\n                if np.max(band_data) > 0:  \n                    normalized_img[:, :, band] = (band_data - np.min(band_data)) / (np.max(band_data) - np.min(band_data))\n            \n            img_tensor = torch.tensor(normalized_img, dtype=torch.float32).permute(2, 0, 1)\n            \n            if img_tensor.shape[1] != self.patch_size or img_tensor.shape[2] != self.patch_size:\n                img_tensor = F.interpolate(img_tensor.unsqueeze(0), \n                                         size=(self.patch_size, self.patch_size),\n                                         mode='bilinear').squeeze(0)\n            \n            return img_tensor, row['id']\n        \n        except Exception as e:\n            print(f\"Error loading {img_path}: {str(e)}\")\n            dummy_img = torch.zeros(self.num_bands, self.patch_size, self.patch_size)\n            return dummy_img, row['id']\n\n\ntest_csv_path = '/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/test.csv'\nbase_path = '/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/ot/ot'\n\ntest_dataset = TestHyperspectralDataset(test_csv_path, base_path, num_bands=100)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)\n\npredictions = []\nids = []\n\nwith torch.no_grad():\n    for inputs, img_ids in test_loader:\n        inputs = inputs.to(DEVICE)\n        \n        if torch.isnan(inputs).any():\n            print(f\"Skipping batch with NaN values\")\n            predictions.extend([50] * len(img_ids))  \n            ids.extend(img_ids)\n            continue\n            \n        outputs = model(inputs)\n        preds = outputs.squeeze().cpu().numpy()\n        preds = preds * 100  \n\n        preds = np.clip(preds, 1, 100).round().astype(int)\n        \n        if preds.ndim == 0:  \n            preds = [preds.item()]\n        else:\n            preds = preds.tolist()\n        \n        predictions.extend(preds)\n        ids.extend(img_ids)\n\nsubmission_df = pd.DataFrame({'ID': ids, 'TARGET': predictions})\nsubmission_df.to_csv('submission.csv', index=False)\nprint(\"Submission created successfully\")\nprint(\"\\nSubmission preview:\")\nprint(submission_df.head())","metadata":{"execution":{"iopub.execute_input":"2025-04-26T12:01:23.943842Z","iopub.status.busy":"2025-04-26T12:01:23.943371Z","iopub.status.idle":"2025-04-26T12:01:50.468613Z","shell.execute_reply":"2025-04-26T12:01:50.467198Z"},"papermill":{"duration":26.614561,"end_time":"2025-04-26T12:01:50.470549","exception":false,"start_time":"2025-04-26T12:01:23.855988","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}