{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":94147,"databundleVersionId":11390004,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import h5py\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import transforms\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import DataLoader, Dataset","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:08.629692Z","iopub.execute_input":"2025-04-24T10:14:08.630031Z","iopub.status.idle":"2025-04-24T10:14:18.106642Z","shell.execute_reply.started":"2025-04-24T10:14:08.629994Z","shell.execute_reply":"2025-04-24T10:14:18.105507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"PATCH_SIZE = 64\nBATCH_SIZE = 64\nEPOCHS = 200\nLR = 1e-3\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:18.107834Z","iopub.execute_input":"2025-04-24T10:14:18.108320Z","iopub.status.idle":"2025-04-24T10:14:18.116309Z","shell.execute_reply.started":"2025-04-24T10:14:18.108289Z","shell.execute_reply":"2025-04-24T10:14:18.115132Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PatchDataset(Dataset):\n    def __init__(self, image_patches, labels=None, transform=None):\n        self.image_patches = image_patches\n        self.labels = labels\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_patches)\n\n    def __getitem__(self, idx):\n        img = self.image_patches[idx]\n        if self.transform:\n            img = self.transform(img)\n        if self.labels is not None:\n            return img, self.labels[idx]\n        return img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:18.118455Z","iopub.execute_input":"2025-04-24T10:14:18.118799Z","iopub.status.idle":"2025-04-24T10:14:18.138460Z","shell.execute_reply.started":"2025-04-24T10:14:18.118768Z","shell.execute_reply":"2025-04-24T10:14:18.137257Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ColorJitter(brightness=0.1, contrast=0.1),\n])\n\nclass CellTypeDataset(Dataset):\n    def __init__(self, coordinates, labels, patch_size=64, transform=None):\n        self.coordinates = coordinates\n        self.labels = labels\n        self.patch_size = patch_size\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.coordinates)\n\n    def __getitem__(self, idx):\n        x, y = self.coordinates[idx]\n        patch = np.zeros((self.patch_size, self.patch_size), dtype=np.float32)\n        patch[x % self.patch_size, y % self.patch_size] = 1.0\n\n        patch = (patch - patch.mean()) / patch.std()\n        patch = torch.tensor(patch, dtype=torch.float32).unsqueeze(0) \n\n        if self.transform:\n            patch = self.transform(patch)\n\n        label = torch.tensor(np.argmax(self.labels[idx]), dtype=torch.long)\n        return patch, label\n\n\n\nclass CNNModel(nn.Module):\n    def __init__(self):\n        super(CNNModel, self).__init__()\n        self.conv_block1 = nn.Sequential(\n            nn.Conv2d(1, 16, kernel_size=3, padding=1),\n            nn.BatchNorm2d(16),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2)\n        )\n        self.conv_block2 = nn.Sequential(\n            nn.Conv2d(16, 32, kernel_size=3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2)\n        )\n        self.conv_block3 = nn.Sequential(\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.3)\n        )\n        self.conv_block4 = nn.Sequential(\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.4)\n        )\n        \n        self.fc1 = nn.Linear(128 * 4 * 4, 512)  \n        self.dropout_fc = nn.Dropout(0.5)\n        self.fc2 = nn.Linear(512, 128)\n        self.fc3 = nn.Linear(128, 35)\n\n    def forward(self, x):\n        x = self.conv_block1(x)\n        x = self.conv_block2(x)\n        x = self.conv_block3(x)\n        x = self.conv_block4(x)\n        x = x.view(x.size(0), -1)\n        x = torch.relu(self.fc1(x))\n        x = self.dropout_fc(x)\n        x = torch.relu(self.fc2(x))\n        return self.fc3(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:18.140211Z","iopub.execute_input":"2025-04-24T10:14:18.140536Z","iopub.status.idle":"2025-04-24T10:14:18.154975Z","shell.execute_reply.started":"2025-04-24T10:14:18.140509Z","shell.execute_reply":"2025-04-24T10:14:18.153878Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with h5py.File(\"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\", \"r\") as f:\n    train_spots = f[\"spots/Train\"]\n    train_spot_tables = {slide: pd.DataFrame(np.array(train_spots[slide])) for slide in train_spots.keys()}\n\ntrain_df = pd.concat(train_spot_tables.values(), ignore_index=True)\n\nX = train_df[['x', 'y']].values\ny = train_df.iloc[:, 2:].values\n\nX_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.1, random_state=42)\n\ntrain_dataset = CellTypeDataset(X_train, y_train, patch_size=PATCH_SIZE, transform=transform)\nvalid_dataset = CellTypeDataset(X_valid, y_valid, patch_size=PATCH_SIZE) \n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\nvalid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE)\n\nmodel = CNNModel().to(DEVICE)\noptimizer = optim.Adam(model.parameters(), lr=LR, weight_decay=1e-5)\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\n\nbest_val_loss = float('inf')\npatience = 5\nwait = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:18.156077Z","iopub.execute_input":"2025-04-24T10:14:18.156449Z","iopub.status.idle":"2025-04-24T10:14:18.335834Z","shell.execute_reply.started":"2025-04-24T10:14:18.156415Z","shell.execute_reply":"2025-04-24T10:14:18.334641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(EPOCHS):\n    model.train()\n    train_loss = 0\n    for inputs, targets in train_loader:\n        inputs, targets = inputs.to(DEVICE), targets.to(DEVICE)\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, targets)\n        loss.backward()\n        optimizer.step()\n        train_loss += loss.item()\n\n    model.eval()\n    valid_loss = 0\n    with torch.no_grad():\n        for inputs, targets in valid_loader:\n            inputs, targets = inputs.to(DEVICE), targets.to(DEVICE)\n            outputs = model(inputs)\n            loss = criterion(outputs, targets)\n            valid_loss += loss.item()\n\n    train_loss /= len(train_loader)\n    valid_loss /= len(valid_loader)\n    print(f\"Epoch {epoch + 1}/{EPOCHS}, Train Loss: {train_loss:.4f}, Valid Loss: {valid_loss:.4f}\")\n\n    if valid_loss < best_val_loss:\n        best_val_loss = valid_loss\n        wait = 0\n        torch.save(model.state_dict(), \"best_model.pt\")\n    else:\n        wait += 1\n        if wait >= patience:\n            print(\"Early stopping triggered.\")\n            break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-24T10:14:18.336959Z","iopub.execute_input":"2025-04-24T10:14:18.337384Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.load_state_dict(torch.load(\"best_model.pt\"))\nmodel.eval()\n\nwith h5py.File(\"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\", \"r\") as f:\n    test_spots = f[\"spots/Test\"]\n    test_spot_table = pd.DataFrame(np.array(test_spots['S_7']))\n\nX_test = test_spot_table[['x', 'y']].values\nX_test_resized = np.zeros((X_test.shape[0], 1, PATCH_SIZE, PATCH_SIZE))\n\nfor i, (x, y) in enumerate(X_test):\n    patch = np.zeros((PATCH_SIZE, PATCH_SIZE))\n    patch[x % PATCH_SIZE, y % PATCH_SIZE] = 1\n    patch = (patch - 0.5) / 0.5  # Normalize\n    X_test_resized[i] = patch.reshape(1, PATCH_SIZE, PATCH_SIZE)\n\nX_test_tensor = torch.tensor(X_test_resized, dtype=torch.float32).to(DEVICE)\n\nwith torch.no_grad():\n    test_preds = model(X_test_tensor)\n    test_preds_softmax = torch.softmax(test_preds, dim=1).cpu().numpy()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.DataFrame(test_preds, columns=[f\"C{i+1}\" for i in range(35)])\nsubmission_df.insert(0, 'ID', test_spot_table.index)\nsubmission_df.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"Submission file 'submission.csv' created!\")\nsubmission_df.head()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}