{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":98450,"databundleVersionId":11749951,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install kornia","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T21:58:58.857849Z","iopub.execute_input":"2025-04-26T21:58:58.858258Z","iopub.status.idle":"2025-04-26T22:00:31.075484Z","shell.execute_reply.started":"2025-04-26T21:58:58.858225Z","shell.execute_reply":"2025-04-26T22:00:31.074213Z"}},"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":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-26T21:58:45.077973Z","iopub.execute_input":"2025-04-26T21:58:45.078212Z","iopub.status.idle":"2025-04-26T21:58:58.828166Z","shell.execute_reply.started":"2025-04-26T21:58:45.078191Z","shell.execute_reply":"2025-04-26T21:58:58.827329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BANDS = 100\nBATCH_SIZE = 32\nEPOCHS = 50\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, num_bands=100):\n        self.df = df\n        self.base_path = base_path\n        self.patch_size = patch_size\n        self.augment = augment\n        self.num_bands = num_bands\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        \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], 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            img = img.astype(np.float32) / 65535.0  # Normalize image\n\n            img = torch.tensor(img, dtype=torch.float32).permute(2, 0, 1)  # Convert to [C, H, W]\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.long)  \n\n            if label > 0:\n                label = label - 1\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(self.num_bands, self.patch_size, self.patch_size)\n            dummy_label = torch.tensor(0, dtype=torch.long)  \n            return dummy_img, dummy_label\n\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\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 HyperspectralCNN(nn.Module):\n    def __init__(self, in_channels=NUM_BANDS, num_classes=100):\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.LeakyReLU(0.1, inplace=True),\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.LeakyReLU(0.1, inplace=True),\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.LeakyReLU(0.1, inplace=True),\n            nn.AdaptiveAvgPool2d(1)\n        )\n        \n        self.classifier = nn.Sequential(\n            nn.Linear(256, 128),\n            nn.SiLU(),\n            nn.Dropout(0.5),\n            nn.Linear(128, 64),\n            nn.SiLU(),\n            nn.Linear(64, num_classes) \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.classifier(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T23:11:29.159858Z","iopub.execute_input":"2025-04-26T23:11:29.160218Z","iopub.status.idle":"2025-04-26T23:11:29.183974Z","shell.execute_reply.started":"2025-04-26T23:11:29.160195Z","shell.execute_reply":"2025-04-26T23:11:29.182837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample = np.load('/kaggle/input/beyond-visible-spectrum-ai-for-agriculture-2025/ot/ot/sample1024.npy')  # (128, 128, 125)\nplt.imshow(sample[:, :, 0])\nplt.title('First channel')\nplt.colorbar()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T22:01:07.930802Z","iopub.execute_input":"2025-04-26T22:01:07.931183Z","iopub.status.idle":"2025-04-26T22:01:08.442321Z","shell.execute_reply.started":"2025-04-26T22:01:07.931121Z","shell.execute_reply":"2025-04-26T22:01:08.441234Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_model(model, loader, criterion, device = DEVICE):\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            \n            probabilities = torch.softmax(outputs, dim=1)\n            preds = torch.argmax(probabilities, dim=1)\n            \n            loss = criterion(outputs.squeeze(), labels)\n            total_loss += loss.item() * inputs.size(0)\n            \n            all_preds.extend(preds.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\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            if len(val_preds.shape) == 2: \n                val_preds = np.argmax(val_preds, axis=1)\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            \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":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T23:18:39.461395Z","iopub.execute_input":"2025-04-26T23:18:39.462410Z","iopub.status.idle":"2025-04-26T23:18:39.476027Z","shell.execute_reply.started":"2025-04-26T23:18:39.462373Z","shell.execute_reply":"2025-04-26T23:18:39.474929Z"}},"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.CrossEntropyLoss()\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":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T23:18:41.752971Z","iopub.execute_input":"2025-04-26T23:18:41.754024Z","iopub.status.idle":"2025-04-26T23:25:23.465433Z","shell.execute_reply.started":"2025-04-26T23:18:41.753993Z","shell.execute_reply":"2025-04-26T23:25:23.464205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = HyperspectralCNN(in_channels=100).to(DEVICE)\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)\n\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        print(preds)\n        preds = np.clip(preds, 1, 100).round().astype(int)\n        print(preds)\n        if isinstance(preds, np.ndarray) and preds.ndim > 1:\n            preds = np.max(preds, axis=1)  \n\n        if len(preds) != len(img_ids):\n            preds = preds[:len(img_ids)]  \n\n        predictions.extend(preds.tolist())  \n        ids.extend(img_ids)\n\n\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":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T23:25:35.236517Z","iopub.execute_input":"2025-04-26T23:25:35.237288Z","iopub.status.idle":"2025-04-26T23:25:59.969087Z","shell.execute_reply.started":"2025-04-26T23:25:35.237255Z","shell.execute_reply":"2025-04-26T23:25:59.967911Z"}},"outputs":[],"execution_count":null}]}