{"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":94147,"databundleVersionId":11390004,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Global AI Hackathon 2025 Solution by Swandip Singha(Master #146)","metadata":{}},{"cell_type":"markdown","source":"# Data Loading and EDA","metadata":{}},{"cell_type":"code","source":"pip install imgaug","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:56:19.198612Z","iopub.execute_input":"2025-05-19T13:56:19.199840Z","iopub.status.idle":"2025-05-19T13:56:25.439320Z","shell.execute_reply.started":"2025-05-19T13:56:19.199804Z","shell.execute_reply":"2025-05-19T13:56:25.438213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import h5py\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\nfrom matplotlib.patches import Rectangle\nfrom matplotlib import cm\nplt.style.use('ggplot')\nsns.set_palette('viridis')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:51:53.660169Z","iopub.execute_input":"2025-05-19T13:51:53.661124Z","iopub.status.idle":"2025-05-19T13:51:53.667618Z","shell.execute_reply.started":"2025-05-19T13:51:53.661094Z","shell.execute_reply":"2025-05-19T13:51:53.666387Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndata_path = \"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\"\n\nwith h5py.File(data_path, \"r\") as f:\n    print(\"HDF5 Groups:\", list(f.keys()))\n    \n    train_slides = list(f[\"images/Train\"].keys())\n    test_slides = list(f[\"images/Test\"].keys())\n    print(\"\\nTraining Slides:\", train_slides)\n    print(\"Test Slide:\", test_slides)\n\n    train_images = {k: f[\"images/Train\"][k][()] for k in f[\"images/Train\"]}\n    train_spots = {k: f[\"spots/Train\"][k][()] for k in f[\"spots/Train\"]}\n    test_images = f[\"images/Test\"]\n    test_spots = f[\"spots/Test\"]\n\n\n    print(\"\\n Train Slides Overview\")\n    print(\"------------------------\")\n    for name in train_images.keys():\n        img_shape = train_images[name].shape\n        spot_count = len(train_spots[name])\n        spot_fields = train_spots[name].dtype.names\n        print(f\"Slide {name}:\")\n        print(f\"   Image Shape : {img_shape}\")\n        print(f\"   Spot Count  : {spot_count}\")\n        print(f\"   Spot Fields : {spot_fields}\")\n        print(\"\")\n\n    print(\" Test Slide Overview\")\n    print(\"------------------------\")\n    for name in test_images.keys():\n        img_shape = test_images[name].shape\n        spot_count = len(test_spots[name])\n        spot_fields = test_spots[name].dtype.names\n        print(f\"Slide {name}:\")\n        print(f\"   Image Shape : {img_shape}\")\n        print(f\"   Spot Count  : {spot_count}\")\n        print(f\"   Spot Fields : {spot_fields}\")\n        print(\"\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:51:53.669470Z","iopub.execute_input":"2025-05-19T13:51:53.669808Z","iopub.status.idle":"2025-05-19T13:51:55.624605Z","shell.execute_reply.started":"2025-05-19T13:51:53.669777Z","shell.execute_reply":"2025-05-19T13:51:55.623311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\npatch_w = patch_h = 75\nfig, axes = plt.subplots(len(train_images), 3, figsize=(12, 4 * len(train_images)))\n\nfor i, slide in enumerate(train_images):\n    img = train_images[slide]\n    spots = train_spots[slide]\n\n    idx = np.random.randint(len(spots))\n    x0, y0 = spots[\"x\"][idx], spots[\"y\"][idx]\n    vals = [spots[f\"C{j}\"][idx] for j in range(1, 36)]\n\n    x1 = max(0, x0 - patch_w // 2)\n    x2 = min(img.shape[1], x0 + patch_w // 2)\n    y1 = max(0, y0 - patch_h // 2)\n    y2 = min(img.shape[0], y0 + patch_h // 2)\n\n    ax = axes[i, 0]\n    ax.imshow(img)\n    rect = Rectangle((x1, y1), x2 - x1, y2 - y1, linewidth=2, edgecolor='red', facecolor='none')\n    ax.add_patch(rect)\n    ax.set_title(f\"{slide}: spot {idx}\")\n    ax.axis(\"off\")\n\n    ax = axes[i, 1]\n    patch = img[y1:y2, x1:x2]\n    ax.imshow(patch)\n    rel_x = x0 - x1\n    rel_y = y0 - y1\n    ax.scatter(rel_x, rel_y, marker='x', color='red', s=100, lw=2)\n    ax.set_title(\"Zoomed patch\")\n    ax.axis(\"off\")\n\n    ax = axes[i, 2]\n    ax.bar(range(1, 36), vals, color=cm.viridis(np.linspace(0, 1, 35)))\n    ax.set_xlabel(\"C1–C35\")\n    ax.set_ylabel(\"Abundance\")\n    ax.set_title(\"Spot composition\")\n\nfig.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:51:55.625724Z","iopub.execute_input":"2025-05-19T13:51:55.625964Z","iopub.status.idle":"2025-05-19T13:52:02.788686Z","shell.execute_reply.started":"2025-05-19T13:51:55.625946Z","shell.execute_reply":"2025-05-19T13:52:02.787581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\nslide = \"S_2\"  \nimg = train_images[slide]\nspots = train_spots[slide]\n\nct_cols = [f\"C{j}\" for j in range(1, 36)]\nn_cols = 7\nn_rows = int(np.ceil(35 / n_cols))\n\nfig, axes = plt.subplots(n_rows, n_cols, figsize=(3 * n_cols, 3 * n_rows), constrained_layout=True)\naxes = axes.flatten()\n\nfor idx, ct in enumerate(ct_cols):\n    ax = axes[idx]\n    ax.imshow(img)\n    sc = ax.scatter(\n        spots[\"x\"], spots[\"y\"],\n        c=spots[ct], cmap=\"viridis\", s=20, vmin=0, vmax=spots[ct].max()\n    )\n    ax.set_title(ct, fontsize=8)\n    ax.axis(\"off\")\n\nfor j in range(idx + 1, n_rows * n_cols):\n    axes[j].axis('off')\n\ncbar = fig.colorbar(sc, ax=axes.tolist(), orientation='vertical', fraction=0.02, pad=0.02)\ncbar.set_label(\"Abundance\", rotation=270, labelpad=15)\n\nfig.suptitle(f\"{slide} - Per Cell Type Overlays (No Shift)\", y=1.02, fontsize=14)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:02.791189Z","iopub.execute_input":"2025-05-19T13:52:02.791545Z","iopub.status.idle":"2025-05-19T13:52:35.724849Z","shell.execute_reply.started":"2025-05-19T13:52:02.791520Z","shell.execute_reply":"2025-05-19T13:52:35.723585Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def check_missing_values(spots_group, label):\n    print(f\"\\n Checking for missing values in {label}...\")\n    for slide_name in spots_group.keys():\n        slide_data = spots_group[slide_name][()]  \n        df = pd.DataFrame(slide_data)\n\n        missing = df.isnull().sum().sum()         \n        nans = np.isnan(df.values).sum()           \n\n        if missing > 0 or nans > 0:\n            print(f\" Slide {slide_name}: {missing} missing, {nans} NaN values found!\")\n        else:\n            print(f\" Slide {slide_name}: No missing or NaN values.\")\n\nwith h5py.File(data_path, \"r\") as f:\n    train_spots = f[\"spots/Train\"]\n    test_spots = f[\"spots/Test\"]\n\n    check_missing_values(train_spots, \"Train Spots\")\n    check_missing_values(test_spots, \"Test Spots\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:35.726154Z","iopub.execute_input":"2025-05-19T13:52:35.726479Z","iopub.status.idle":"2025-05-19T13:52:35.870361Z","shell.execute_reply.started":"2025-05-19T13:52:35.726456Z","shell.execute_reply":"2025-05-19T13:52:35.869426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_slide_with_spots(slide_name, h5_file, zoom_region=None):\n    with h5py.File(h5_file, \"r\") as f:\n        image = np.array(f[f\"images/Train/{slide_name}\"])\n        spots = pd.DataFrame(np.array(f[f\"spots/Train/{slide_name}\"]))\n    \n    fig, ax = plt.subplots(1, 1 + int(zoom_region is not None), \n                         figsize=(18, 6) if zoom_region else (10, 10))\n    \n    if zoom_region:\n        x1, x2, y1, y2 = zoom_region\n        zoom_img = image[y1:y2, x1:x2]\n        zoom_spots = spots[(spots['x'].between(x1, x2)) & \n                          (spots['y'].between(y1, y2))]\n        \n        ax[0].imshow(image)\n        ax[0].set_title(f\"Full Slide {slide_name}\")\n        ax[1].imshow(zoom_img)\n        ax[1].scatter(zoom_spots[\"x\"]-x1, zoom_spots[\"y\"]-y1, \n                     s=10, c=\"red\", alpha=0.7)\n        ax[1].set_title(f\"Zoomed Region ({x1}-{x2}, {y1}-{y2})\")\n    else:\n        ax.imshow(image)\n        ax.scatter(spots[\"x\"], spots[\"y\"], s=5, c=\"red\", alpha=0.5)\n        ax.set_title(f\"Slide {slide_name} | Spots: {len(spots)}\")\n    \n    for a in fig.axes:\n        a.axis('off')\n    plt.tight_layout()\n    plt.show()\nplot_slide_with_spots(train_slides[0], data_path, \n                     zoom_region=(1000, 1500, 1000, 1500))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:35.871615Z","iopub.execute_input":"2025-05-19T13:52:35.872551Z","iopub.status.idle":"2025-05-19T13:52:37.906276Z","shell.execute_reply.started":"2025-05-19T13:52:35.872526Z","shell.execute_reply":"2025-05-19T13:52:37.905177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cell_data = []\nfor slide in train_slides:\n    with h5py.File(data_path, \"r\") as f:\n        spots = pd.DataFrame(np.array(f[f\"spots/Train/{slide}\"]))\n        cell_data.append(spots.iloc[:, 2:37])  # Columns C1-C35\n\ncell_types = [f\"C{i}\" for i in range(1, 36)]\nall_cell_data = pd.concat(cell_data)\n\n# Plot distribution of mean abundances\nplt.figure(figsize=(15, 6))\nmean_abundance = all_cell_data.mean().sort_values(ascending=False)\nsns.barplot(x=mean_abundance.index, y=mean_abundance.values)\nplt.xticks(rotation=90)\nplt.title(\"Mean Cell-Type Abundance Across All Slides\")\nplt.ylabel(\"Mean Abundance\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:37.907207Z","iopub.execute_input":"2025-05-19T13:52:37.907492Z","iopub.status.idle":"2025-05-19T13:52:38.392535Z","shell.execute_reply.started":"2025-05-19T13:52:37.907472Z","shell.execute_reply":"2025-05-19T13:52:38.391403Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_cell_types = mean_abundance.index[:5]\n\nwith h5py.File(data_path, \"r\") as f:\n    image = np.array(f[\"images/Train/S_1\"])\n    spots = pd.DataFrame(np.array(f[\"spots/Train/S_1\"]))\n\nfig, axes = plt.subplots(2, 3, figsize=(18, 12))\naxes = axes.ravel()\n\nfor i, cell_type in enumerate(top_cell_types):\n    sc = axes[i].scatter(\n        spots[\"x\"], \n        spots[\"y\"], \n        c=spots[cell_type], \n        cmap=\"viridis\", \n        s=10,\n        alpha=0.7\n    )\n    axes[i].set_title(f\"{cell_type} Distribution\")\n    plt.colorbar(sc, ax=axes[i])\n    axes[i].axis('off')\n\n# Remove empty subplot\nfig.delaxes(axes[-1])\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:38.393567Z","iopub.execute_input":"2025-05-19T13:52:38.393826Z","iopub.status.idle":"2025-05-19T13:52:39.842960Z","shell.execute_reply.started":"2025-05-19T13:52:38.393807Z","shell.execute_reply":"2025-05-19T13:52:39.841681Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"corr_matrix = all_cell_data.corr()\n\nplt.figure(figsize=(15, 12))\nsns.heatmap(\n    corr_matrix, \n    cmap=\"coolwarm\", \n    vmin=-0.5, \n    vmax=0.5,\n    center=0,\n    square=True,\n    annot=False,\n    cbar_kws={\"shrink\": 0.8}\n)\nplt.title(\"Cell-Type Correlation Matrix Across All Slides\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:52:39.845102Z","iopub.execute_input":"2025-05-19T13:52:39.845426Z","iopub.status.idle":"2025-05-19T13:52:40.514405Z","shell.execute_reply.started":"2025-05-19T13:52:39.845403Z","shell.execute_reply":"2025-05-19T13:52:40.513246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport time\nimport random\nfrom pathlib import Path\nimport cv2\nimport h5py\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torchvision\nfrom torch.utils.data import DataLoader, Dataset\nimport torchvision.models as models\nimport torchvision.transforms as T\nfrom sklearn.model_selection import train_test_split\nfrom scipy.stats import spearmanr\nfrom tqdm import tqdm\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport imgaug\nimport wandb  \nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nCONFIG = {\n    \"seed\": 42,\n    \"data_path\": \"/kaggle/input/el-hackathon-2025\",\n    \"output_dir\": \"/kaggle/working/\",\n    \"batch_size\": 32,\n    \"num_workers\": 6,\n    \"learning_rate\": 0.003,\n    \"weight_decay\": 1e-1,\n    \"scheduler_step_size\": 5,\n    \"scheduler_gamma\": 0.1,\n    \"num_classes\": 35,\n    \"image_size\": (162, 162),\n    \"patch_size\": 54,\n    \"max_epochs\": 5,\n    \"patience\": 5,\n    \"min_delta\": 0.001,\n    \"save_best_only\": True,\n    \"checkpoint_epochs\": [],\n    \"use_wandb\": False,\n    \"model_type\": \"resnet18\",\n    \"mixed_precision\": False,\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:56:30.245506Z","iopub.execute_input":"2025-05-19T13:56:30.245843Z","iopub.status.idle":"2025-05-19T13:56:34.545470Z","shell.execute_reply.started":"2025-05-19T13:56:30.245817Z","shell.execute_reply":"2025-05-19T13:56:34.544459Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Utility Functions","metadata":{}},{"cell_type":"code","source":"def set_seed(seed=42):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    imgaug.seed(seed)\n\ndef get_device():\n    if torch.cuda.is_available():\n        return torch.device(\"cuda\")\n    elif torch.backends.mps.is_available():\n        return torch.device(\"mps\")\n    else:\n        return torch.device(\"cpu\")\n\ndef spearman_rank_correlation(x, y):\n    if np.all(x == x[0]) or np.all(y == y[0]):\n        return 0.0\n    return spearmanr(x, y)[0]\n\ndef spearman_corr(preds, targets):\n    correlations = []\n    for i in range(len(preds)):\n        corr = spearman_rank_correlation(preds[i], targets[i])\n        if not np.isnan(corr):\n            correlations.append(corr)\n    return np.mean(correlations)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:56:52.206099Z","iopub.execute_input":"2025-05-19T13:56:52.206703Z","iopub.status.idle":"2025-05-19T13:56:52.215004Z","shell.execute_reply.started":"2025-05-19T13:56:52.206677Z","shell.execute_reply":"2025-05-19T13:56:52.213760Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Loading and Preprocessing","metadata":{}},{"cell_type":"code","source":"class HackhathonDataset(Dataset):\n    def __init__(self, data_path, transform=None, mode=\"train\"):\n        self.data_path = data_path\n        self.materials = []\n        self.transform = transform\n        train_slides = [\"S_1\",\"S_2\",\"S_3\", \"S_4\", \"S_5\"]\n        val_slide = [\"S_6\"]\n        test_slide = [\"S_7\"]\n        self.mode = mode\n        slide_list = train_slides if mode == \"train\" else val_slide if mode == \"val\" else test_slide\n\n        with h5py.File(f\"{self.data_path}/elucidata_ai_challenge_data.h5\", \"r\") as h5file:\n            images_group = \"images/Train\" if mode != \"test\" else \"images/Test\"\n            spots_group = \"spots/Train\" if mode != \"test\" else \"spots/Test\"\n            train_images = h5file[images_group]\n            train_spots = h5file[spots_group]\n\n            for slide_name in tqdm(slide_list, desc=f\"Loading {mode} data\"):\n                if slide_name in train_images.keys():\n                    image = np.array(train_images[slide_name])\n                    spots = np.array(train_spots[slide_name])\n                    df = pd.DataFrame(spots)\n                    self._split_into_patches(image, df, CONFIG[\"patch_size\"])\n\n    def __len__(self):\n        return len(self.materials)\n\n    def __getitem__(self, idx):\n        image, stats = self.materials[idx]\n        if self.transform:\n            image = self.transform(image)\n        stats = torch.tensor(stats[2:], dtype=torch.float32)\n        return image, stats\n\n    def _split_into_patches(self, arr, df, patch_size):\n        h, w, c = arr.shape\n        for idx in range(len(df)):\n            row = df.iloc[idx]\n            x, y = int(row[\"x\"]), int(row[\"y\"])\n            half_size = patch_size // 2\n            y_min = max(y - half_size, 0)\n            y_max = min(y + half_size, h)\n            x_min = max(x - half_size, 0)\n            x_max = min(x + half_size, w)\n            patch = arr[y_min:y_max, x_min:x_max, :]\n            if patch.shape[0] == patch_size and patch.shape[1] == patch_size:\n                self.materials.append([patch, row])\n            else:\n                padded_patch = np.zeros((patch_size, patch_size, c), dtype=patch.dtype)\n                padded_patch[:patch.shape[0], :patch.shape[1], :] = patch\n                self.materials.append([padded_patch, row])\n\ndef get_transforms():\n    train_transform = T.Compose([\n        T.ToTensor(),\n        T.Resize(CONFIG[\"image_size\"]),\n        T.RandomApply([T.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.1)], p=0.8),\n        T.RandomHorizontalFlip(p=0.5),\n        T.RandomVerticalFlip(p=0.5),\n        T.RandomRotation(degrees=45),\n        T.RandomAffine(degrees=0, translate=(0.1, 0.1), scale=(0.9, 1.1)),\n    ])\n    val_transform = T.Compose([\n        T.ToTensor(),\n        T.Resize(CONFIG[\"image_size\"]),\n    ])\n    return train_transform, val_transform\n\ndef get_tta_transforms():\n    tta_transforms = [\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"])]),\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"]), T.RandomHorizontalFlip(p=1.0)]),\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"]), T.RandomVerticalFlip(p=1.0)]),\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"]), T.RandomRotation(degrees=(90, 90))]),\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"]), T.RandomRotation(degrees=(180, 180))]),\n        T.Compose([T.ToTensor(), T.Resize(CONFIG[\"image_size\"]), T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2)]),\n    ]\n    return tta_transforms","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:57:26.659979Z","iopub.execute_input":"2025-05-19T13:57:26.660316Z","iopub.status.idle":"2025-05-19T13:57:26.679400Z","shell.execute_reply.started":"2025-05-19T13:57:26.660294Z","shell.execute_reply":"2025-05-19T13:57:26.678234Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Definition","metadata":{}},{"cell_type":"code","source":"class DifferentiableSpearmanLoss(nn.Module):\n    def __init__(self, regularization_strength=1.0):\n        super().__init__()\n        self.regularization_strength = regularization_strength\n\n    def forward(self, y_pred, y_true):\n        y_pred = y_pred.float()\n        y_true = y_true.float()\n        pred_rank = self._soft_rank(y_pred)\n        true_rank = self._soft_rank(y_true)\n        pred_rank = F.normalize(pred_rank, dim=1)\n        true_rank = F.normalize(true_rank, dim=1)\n        spearman = torch.sum(pred_rank * true_rank, dim=1)\n        return 1 - spearman.mean()\n\n    def _soft_rank(self, x, regularization_strength=None):\n        if regularization_strength is None:\n            regularization_strength = self.regularization_strength\n        x = x.unsqueeze(-1)\n        diff = x - x.transpose(-1, -2)\n        P = torch.sigmoid(-regularization_strength * diff)\n        ranks = P.sum(dim=-1)\n        return ranks\n\nclass CombinedLoss(nn.Module):\n    def __init__(self, alpha=0.5, regularization_strength=1.0):\n        super().__init__()\n        self.l1 = nn.L1Loss()\n        self.spearman = DifferentiableSpearmanLoss(regularization_strength)\n        self.alpha = alpha\n\n    def forward(self, y_pred, y_true):\n        l1_loss = self.l1(y_pred, y_true)\n        spearman_loss = self.spearman(y_pred, y_true)\n        return l1_loss + self.alpha * spearman_loss\n\ndef create_model(model_type, num_classes):\n    if model_type == \"resnet18\":\n        MODEL_PATH = '/kaggle/input/ckpt-file/tenpercent_resnet18.ckpt'\n        RETURN_PREACTIVATION = False\n        NUM_CLASSES = 35\n\n        def load_model_weights(model, weights):\n            model_dict = model.state_dict()\n            weights = {k: v for k, v in weights.items() if k in model_dict}\n            if weights == {}:\n                print('No weight could be loaded..')\n            model_dict.update(weights)\n            model.load_state_dict(model_dict)\n            return model\n\n        model = models.resnet18(pretrained=False)\n        state = torch.load(MODEL_PATH, map_location='cuda:0', weights_only=False)\n        state_dict = state['state_dict']\n        for key in list(state_dict.keys()):\n            state_dict[key.replace('model.', '').replace('resnet.', '')] = state_dict.pop(key)\n        model = load_model_weights(model, state_dict)\n\n        if RETURN_PREACTIVATION:\n            model.fc = torch.nn.Sequential()\n        else:\n            model.fc = torch.nn.Linear(model.fc.in_features, NUM_CLASSES)\n    elif model_type == \"resnet50\":\n        model = models.resnet50(weights=None)\n        model.fc = nn.Linear(model.fc.in_features, num_classes)\n    elif model_type == \"swin\":\n        model = models.swin_v2_b(weights=models.Swin_V2_B_Weights.IMAGENET1K_V1)\n        model.head = nn.Linear(model.head.in_features, num_classes)\n    elif model_type == \"efficientnet_b3\":\n        model = models.efficientnet_b7(weights=models.EfficientNet_B7_Weights.IMAGENET1K_V1)\n        model.classifier = nn.Linear(model.classifier[1].in_features, num_classes)\n    elif model_type == \"convnext_large\":\n        model = models.convnext_large(weights=models.ConvNeXt_Large_Weights.IMAGENET1K_V1)\n        in_features = model.classifier[2].in_features\n        model.classifier[2] = nn.Linear(in_features, num_classes)\n    else:\n        raise ValueError(f\"Unsupported model type: {model_type}\")\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:57:56.830660Z","iopub.execute_input":"2025-05-19T13:57:56.830988Z","iopub.status.idle":"2025-05-19T13:57:56.845641Z","shell.execute_reply.started":"2025-05-19T13:57:56.830963Z","shell.execute_reply":"2025-05-19T13:57:56.844668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training and Evaluation","metadata":{}},{"cell_type":"code","source":"def train_one_epoch(model, dataloader, loss_fn, optimizer, device, scaler=None):\n    model.train()\n    epoch_loss = 0\n    all_preds, all_labels = [], []\n    progress_bar = tqdm(dataloader, desc=\"Training\")\n\n    for images, labels in progress_bar:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        if scaler:\n            with torch.cuda.amp.autocast():\n                outputs = model(images)\n                loss = loss_fn(outputs, labels)\n            optimizer.zero_grad()\n            scaler.scale(loss).backward()\n            scaler.step(optimizer)\n            scaler.update()\n        else:\n            outputs = model(images)\n            loss = loss_fn(outputs, labels)\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n        epoch_loss += loss.item()\n        all_preds.extend(outputs.detach().cpu().numpy())\n        all_labels.extend(labels.detach().cpu().numpy())\n        progress_bar.set_postfix({\"loss\": f\"{loss.item():.4f}\"})\n\n    avg_loss = epoch_loss / len(dataloader)\n    spearman_score = spearman_corr(all_preds, all_labels)\n    return avg_loss, spearman_score, all_preds, all_labels\n\ndef validate(model, dataloader, loss_fn, device):\n    model.eval()\n    val_loss = 0\n    all_preds, all_labels = [], []\n\n    with torch.no_grad():\n        for images, labels in tqdm(dataloader, desc=\"Validation\"):\n            images = images.to(device)\n            labels = labels.to(device)\n            outputs = model(images)\n            loss = loss_fn(outputs, labels)\n            val_loss += loss.item()\n            all_preds.extend(outputs.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    avg_loss = val_loss / len(dataloader)\n    spearman_score = spearman_corr(all_preds, all_labels)\n    return avg_loss, spearman_score, all_preds, all_labels\n\ndef predict_test_set_with_tta(model, data_path, device):\n    tta_transforms = get_tta_transforms()\n    with h5py.File(f\"{data_path}/elucidata_ai_challenge_data.h5\", \"r\") as f:\n        test_spots = f[\"spots/Test\"]\n        test_images = f[\"images/Test\"]\n        sample = 'S_7'\n        image = np.array(test_images[sample])\n        spots = np.array(test_spots[sample])\n        x, y = spots[\"x\"], spots[\"y\"]\n        outputs = []\n\n        with torch.inference_mode():\n            model.eval()\n            patch_size = CONFIG[\"patch_size\"]\n            for x_, y_ in tqdm(zip(x, y), desc=\"Generating predictions with TTA\", total=len(x)):\n                half_size = patch_size // 2\n                y_min = max(y_ - half_size, 0)\n                y_max = min(y_ + half_size, image.shape[0])\n                x_min = max(x_ - half_size, 0)\n                x_max = min(x_ + half_size, image.shape[1])\n                patch = image[y_min:y_max, x_min:x_max, :]\n\n                if patch.shape[0] != patch_size or patch.shape[1] != patch_size:\n                    padded_patch = np.zeros((patch_size, patch_size, 3), dtype=patch.dtype)\n                    padded_patch[:patch.shape[0], :patch.shape[1], :] = patch\n                    patch = padded_patch\n\n                patch_predictions = []\n                for transform in tta_transforms:\n                    patch_tensor = transform(patch)\n                    patch_tensor = patch_tensor.to(device)\n                    with torch.no_grad():\n                        output = model(patch_tensor.unsqueeze(0)).cpu().numpy()\n                        patch_predictions.append(output[0])\n\n                avg_prediction = np.mean(patch_predictions, axis=0)\n                outputs.append(avg_prediction)\n\n    return np.array(outputs), x, y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:58:32.142707Z","iopub.execute_input":"2025-05-19T13:58:32.143057Z","iopub.status.idle":"2025-05-19T13:58:32.160223Z","shell.execute_reply.started":"2025-05-19T13:58:32.143033Z","shell.execute_reply":"2025-05-19T13:58:32.159196Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission and Checkpoint Handling","metadata":{}},{"cell_type":"code","source":"def save_checkpoint(model, optimizer, scheduler, epoch, metrics, filename):\n    checkpoint = {\n        'epoch': epoch,\n        'model_state_dict': model.state_dict(),\n        'optimizer_state_dict': optimizer.state_dict(),\n        'scheduler_state_dict': scheduler.state_dict(),\n        'metrics': metrics\n    }\n    torch.save(checkpoint, filename)\n\ndef save_submission(predictions, data_path, epoch, model_name):\n    example_df = pd.read_csv(f\"/kaggle/input/sample-submission/submission (1).csv\")\n    ID = example_df[\"ID\"]\n    output_df = pd.DataFrame(predictions)\n    submission_df = pd.concat([ID, output_df], axis=1)\n    submission_df.columns = example_df.columns\n    output_file = \"submission.csv\"\n    submission_df.to_csv(output_file, index=False)\n    print(f\"Saved submission to {output_file}\")\n    return output_file","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:59:01.774056Z","iopub.execute_input":"2025-05-19T13:59:01.774427Z","iopub.status.idle":"2025-05-19T13:59:01.782016Z","shell.execute_reply.started":"2025-05-19T13:59:01.774404Z","shell.execute_reply":"2025-05-19T13:59:01.780924Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Main Training Loop","metadata":{}},{"cell_type":"code","source":"'''\ndef main():\n    set_seed(CONFIG[\"seed\"])\n    device = get_device()\n    print(f\"Using device: {device}\")\n\n    train_transform, val_transform = get_transforms()\n    train_dataset = HackhathonDataset(CONFIG[\"data_path\"], transform=train_transform, mode=\"train\")\n    val_dataset = HackhathonDataset(CONFIG[\"data_path\"], transform=val_transform, mode=\"val\")\n\n    train_loader = DataLoader(\n        train_dataset,\n        batch_size=CONFIG[\"batch_size\"],\n        shuffle=True,\n        num_workers=CONFIG[\"num_workers\"],\n        pin_memory=True\n    )\n\n    val_loader = DataLoader(\n        val_dataset,\n        batch_size=CONFIG[\"batch_size\"],\n        shuffle=False,\n        num_workers=CONFIG[\"num_workers\"],\n        pin_memory=True\n    )\n\n    model = create_model(CONFIG[\"model_type\"], CONFIG[\"num_classes\"])\n    model = model.to(device)\n    loss_fn = CombinedLoss()\n    optimizer = torch.optim.AdamW(\n        model.parameters(),\n        lr=CONFIG[\"learning_rate\"],\n        weight_decay=CONFIG[\"weight_decay\"]\n    )\n    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n        optimizer,\n        mode='min',\n        factor=CONFIG[\"scheduler_gamma\"],\n        patience=3,\n    )\n\n    scaler = torch.cuda.amp.GradScaler() if CONFIG[\"mixed_precision\"] and device.type == \"cuda\" else None\n\n    if CONFIG[\"use_wandb\"]:\n        wandb.init(\n            project=\"hackathon-gene-expression\",\n            config=CONFIG,\n            name=f\"{CONFIG['model_type']}_run\"\n        )\n        wandb.watch(model)\n\n    best_val_spearman = -1\n    no_improvement_count = 0\n    best_model_path = f\"{CONFIG['output_dir']}/best_{CONFIG['model_type']}_model.pt\"\n\n    for epoch in range(CONFIG[\"max_epochs\"]):\n        print(f\"\\nEpoch {epoch + 1}/{CONFIG['max_epochs']}\")\n\n        train_loss, train_spearman, _, _ = train_one_epoch(\n            model, train_loader, loss_fn, optimizer, device, scaler\n        )\n\n        val_loss, val_spearman, _, _ = validate(\n            model, val_loader, loss_fn, device\n        )\n\n        scheduler.step(val_loss)\n\n        metrics = {\n            \"train_loss\": train_loss,\n            \"train_spearman\": train_spearman,\n            \"val_loss\": val_loss,\n            \"val_spearman\": val_spearman,\n            \"learning_rate\": optimizer.param_groups[0]['lr']\n        }\n\n        print(f\"Train Loss: {train_loss:.4f}, Train Spearman: {train_spearman:.4f}\")\n        print(f\"Val Loss: {val_loss:.4f}, Val Spearman: {val_spearman:.4f}\")\n\n        if CONFIG[\"use_wandb\"]:\n            wandb.log(metrics)\n\n        improved = val_spearman > best_val_spearman + CONFIG[\"min_delta\"]\n\n        if improved:\n            best_val_spearman = val_spearman\n            no_improvement_count = 0\n            torch.save({\n                'epoch': epoch,\n                'model_state_dict': model.state_dict(),\n                'optimizer_state_dict': optimizer.state_dict(),\n                'scheduler_state_dict': scheduler.state_dict(),\n                'val_spearman': val_spearman,\n            }, best_model_path)\n\n            print(f\"Saved best model with validation Spearman: {val_spearman:.4f}\")\n            test_preds, x, y = predict_test_set_with_tta(model, CONFIG[\"data_path\"], device)\n            submission_file = save_submission(\n                test_preds, CONFIG[\"data_path\"], f\"best_epoch_{epoch}\", CONFIG[\"model_type\"]\n            )\n\n            if CONFIG[\"use_wandb\"]:\n                wandb.save(submission_file)\n        else:\n            no_improvement_count += 1\n\n        if epoch in CONFIG[\"checkpoint_epochs\"]:\n            test_preds, _, _ = predict_test_set_with_tta(model, CONFIG[\"data_path\"], device)\n            submission_file = save_submission(\n                test_preds, CONFIG[\"data_path\"], epoch, CONFIG[\"model_type\"]\n            )\n\n            if CONFIG[\"use_wandb\"]:\n                wandb.save(submission_file)\n\n        if no_improvement_count >= CONFIG[\"patience\"]:\n            print(f\"Early stopping triggered after {epoch + 1} epochs\")\n            break\n\n    checkpoint = torch.load(best_model_path,weights_only=False)\n    model.load_state_dict(checkpoint['model_state_dict'])\n    print(f\"Loaded best model from epoch {checkpoint['epoch']} with validation Spearman: {checkpoint['val_spearman']:.4f}\")\n\n    final_preds, _, _ = predict_test_set_with_tta(model, CONFIG[\"data_path\"], device)\n    final_submission = save_submission(\n        final_preds, CONFIG[\"data_path\"], \"final\", CONFIG[\"model_type\"]\n    )\n\n    if CONFIG[\"use_wandb\"]:\n        wandb.save(final_submission)\n        wandb.finish()\n\n    print(\"Training completed!\")\n\nmain()\n'''\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:01:08.138766Z","iopub.execute_input":"2025-05-19T14:01:08.139109Z","iopub.status.idle":"2025-05-19T14:01:08.148401Z","shell.execute_reply.started":"2025-05-19T14:01:08.139080Z","shell.execute_reply":"2025-05-19T14:01:08.147036Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Thanks for visiting!","metadata":{}}]}