{"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":"gpu","dataSources":[{"sourceId":94147,"databundleVersionId":11390004,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1. Data Loading and Visualization\n\nBelow, we load the HE images (both Train and Test) from the HDF5 file and overlay the spot coordinates on the images.","metadata":{}},{"cell_type":"code","source":"import h5py\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, random_split\nfrom tqdm import tqdm\nimport torch\nimport pandas as pd\nimport torchvision.transforms as T\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom sklearn.model_selection import train_test_split","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:01:30.340292Z","iopub.execute_input":"2025-05-17T12:01:30.340585Z","iopub.status.idle":"2025-05-17T12:01:48.317142Z","shell.execute_reply.started":"2025-05-17T12:01:30.340562Z","shell.execute_reply":"2025-05-17T12:01:48.316418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from kaggle_secrets import UserSecretsClient\nimport wandb\n\nuser_secrets = UserSecretsClient()\n\nwandb.login(key=user_secrets.get_secret(\"wandb\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:01:48.318051Z","iopub.execute_input":"2025-05-17T12:01:48.318493Z","iopub.status.idle":"2025-05-17T12:01:57.670299Z","shell.execute_reply.started":"2025-05-17T12:01:48.318471Z","shell.execute_reply":"2025-05-17T12:01:57.669512Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_transforms():\n    \"\"\"Get data transformations for training and validation\"\"\"\n    train_transform = T.Compose([\n        T.ToTensor(),\n        T.Resize((224,224)),\n        T.RandomApply([T.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.1)], p=0.8),\n        T.RandomAffine(degrees=0, translate=(0.1, 0.1), scale=(0.9, 1.1)),\n    ])\n\n    val_transform = T.Compose([\n        T.ToTensor(),\n        T.Resize((224,224)),\n    ])\n\n    return train_transform, val_transform","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:01:57.671745Z","iopub.execute_input":"2025-05-17T12:01:57.672275Z","iopub.status.idle":"2025-05-17T12:01:57.677221Z","shell.execute_reply.started":"2025-05-17T12:01:57.672250Z","shell.execute_reply":"2025-05-17T12:01:57.676273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Custom dataset class to extract and process patches from HDF5 slides\ndef CustomDataset(data_path, slide_names, is_train=True,transform=None):\n        data = []\n        targets = []\n        patch_size = 224\n        slide_names = slide_names\n        is_train = is_train\n        transform = transform\n\n        with h5py.File(data_path, \"r\") as f:\n            images = f[\"images/Train\"] if is_train else f[\"images/Test\"]\n            coords = f[\"spots/Train\"] if is_train else f[\"spots/Test\"]\n            for slide_name in slide_names:\n                slide = np.array(images[slide_name])\n                spots = np.array(coords[slide_name])\n                df = pd.DataFrame(spots)\n\n                # Apply specific x/y shifts for alignment if needed\n                x_shift, y_shift = 0, 0\n                if slide_name == 'S_1':\n                    x_shift, y_shift = 50, 60\n                elif slide_name == 'S_2':\n                    x_shift, y_shift = 95, 55\n                df['x'] -= x_shift\n                df['y'] -= y_shift\n\n                # Extract patches and corresponding targets\n                for _, row in df.iterrows():\n                    x_center, y_center = int(row['x']), int(row['y'])\n                    x0 = x_center - patch_size // 2\n                    y0 = y_center - patch_size // 2\n                    patch = slide[y0:y0 + patch_size, x0:x0 + patch_size, :]\n                    if transform:\n                        patch = transform(patch)\n                    data.append(patch)\n                    targets.append(row[2:].values)\n        return data, targets","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:01:57.678526Z","iopub.execute_input":"2025-05-17T12:01:57.678819Z","iopub.status.idle":"2025-05-17T12:01:57.695825Z","shell.execute_reply.started":"2025-05-17T12:01:57.678787Z","shell.execute_reply":"2025-05-17T12:01:57.694925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dataset paths and slide setup\ndata_path = \"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\"\ntrain_slides = ['S_1', 'S_2', 'S_3', 'S_4', 'S_5','S_6']\n# Load full dataset and split into train/val subsets\ndataset = CustomDataset(data_path, slide_names=train_slides, is_train=True,transform=get_transforms()[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:01:57.696639Z","iopub.execute_input":"2025-05-17T12:01:57.697001Z","iopub.status.idle":"2025-05-17T12:02:56.532044Z","shell.execute_reply.started":"2025-05-17T12:01:57.696968Z","shell.execute_reply":"2025-05-17T12:02:56.531122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_path = \"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\"\ntest_slide = ['S_7']\ntest_data = CustomDataset(data_path, slide_names=test_slide, is_train=False,transform=get_transforms()[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:02:56.533073Z","iopub.execute_input":"2025-05-17T12:02:56.533320Z","iopub.status.idle":"2025-05-17T12:02:57.952550Z","shell.execute_reply.started":"2025-05-17T12:02:56.533301Z","shell.execute_reply":"2025-05-17T12:02:57.951560Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_images, val_images, train_targets, val_targets = train_test_split(dataset[0], dataset[1], test_size=0.2, random_state=42)\n\ntrain_data = {'image': train_images, 'targets': train_targets}\nval_data = {'image': val_images, 'targets': val_targets}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:02:57.953474Z","iopub.execute_input":"2025-05-17T12:02:57.953720Z","iopub.status.idle":"2025-05-17T12:02:57.964934Z","shell.execute_reply.started":"2025-05-17T12:02:57.953699Z","shell.execute_reply":"2025-05-17T12:02:57.963974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from datasets import Dataset, concatenate_datasets\n\ndef load_dataset_in_batches(data_dict, batch_size=500):\n    datasets = []\n    for i in range(0, len(data_dict['image']), batch_size):\n        batch = {\n            key: val[i:i+batch_size]\n            for key, val in data_dict.items()\n        }\n        datasets.append(Dataset.from_dict(batch))\n    return concatenate_datasets(datasets)\n\n\ntrain_data = load_dataset_in_batches(train_data)\nval_data = load_dataset_in_batches(val_data)\ndataset = {'train': train_data, 'val': val_data}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T12:02:57.967583Z","iopub.execute_input":"2025-05-17T12:02:57.967809Z","iopub.status.idle":"2025-05-17T12:04:15.619338Z","shell.execute_reply.started":"2025-05-17T12:02:57.967788Z","shell.execute_reply":"2025-05-17T12:04:15.618645Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from datasets import load_dataset, DatasetDict\nfrom PIL import Image\nimport torch\nfrom torchvision import transforms\nfrom transformers import ViTModel, TrainingArguments, Trainer\nfrom torch import nn\nfrom torch.utils.data import DataLoader\nfrom safetensors.torch import load_file as safetensors_load_file\nimport logging\nimport os\nimport json\nimport shutil\nfrom datasets import Dataset\n\ndef get_device():\n    \"\"\"Get the appropriate device\"\"\"\n    if torch.cuda.is_available():\n        return torch.device(\"cuda\")  \n    else:\n        return torch.device(\"cpu\")\n\ndevice = get_device()\nprint(f\"Using device is: {device}\")\n\n\nclass ViTRegressionModel(nn.Module):\n    def __init__(self,num_targets):\n        super(ViTRegressionModel, self).__init__()\n        self.vit = ViTModel.from_pretrained('google/vit-base-patch16-224',device_map=0, ignore_mismatched_sizes=True)\n        self.classifier = nn.Linear(self.vit.config.hidden_size, num_targets)\n\n    def forward(self, pixel_values, labels=None):\n        outputs = self.vit(pixel_values=pixel_values)\n        cls_output = outputs.last_hidden_state[:, 0, :]  # Take the [CLS] token\n        values = self.classifier(cls_output)\n        loss = None\n        if labels is not None:\n            loss_fct = nn.MSELoss()\n            # print(f\"label: {labels}\")\n            # print(f\"pixel_values: {values}\")\n            loss = loss_fct(values, labels)\n        return (loss, values) if loss is not None else values\n\n\n\ndef train_model(dataset, value_column_name, test_split, output_dir, num_train_epochs, learning_rate):\n    # Load the dataset\n    dataset = dataset\n\n    # Split the dataset into train and test\n    dataset = DatasetDict({\n        'train': dataset['train'],\n        'val': dataset['val']\n    })\n    \n    def collate_fn(batch):\n        pixel_values = torch.stack([\n            item['image'] if isinstance(item['image'], torch.Tensor) else torch.tensor(item['image']) \n            for item in batch])\n        labels = torch.tensor([item[value_column_name] for item in batch], dtype=torch.float)\n        return {'pixel_values': pixel_values, 'labels': labels}\n\n\n    model = ViTRegressionModel(num_targets=35)\n\n    training_args = TrainingArguments(\n        output_dir=output_dir,\n        per_device_train_batch_size=16,\n        per_device_eval_batch_size=16,\n        num_train_epochs=num_train_epochs,\n        learning_rate=learning_rate,\n        save_steps=10,\n        save_total_limit=5,\n        logging_steps=10,\n        remove_unused_columns=False,\n        resume_from_checkpoint=True,\n    )\n    \n    trainer = Trainer(\n        model=model,\n        args=training_args,\n        train_dataset=dataset['train'],\n        eval_dataset=dataset['val'],\n        data_collator=collate_fn,\n    )\n\n    # Add logging to inspect the model outputs and labels\n    def compute_metrics(p):\n        preds = p.predictions[0] if isinstance(p.predictions, tuple) else p.predictions\n        labels = p.label_ids\n        mse = ((preds - labels) ** 2).mean().item()\n        return {\"mse\": mse}\n\n    trainer.compute_metrics = compute_metrics\n\n    trainer.train()\n    eval_results = trainer.evaluate()\n    print(f\"Evaluation results: {eval_results}\")\n\n\ndef predict_batch(test_images, checkpoint_path=\"/kaggle/working/results/checkpoint-2090/model.safetensors\"):\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n    model = ViTRegressionModel(num_targets=35)\n    state_dict = safetensors_load_file(checkpoint_path)\n    model.load_state_dict(state_dict)\n    model.to(device)\n    model.eval()\n\n    transform = transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.ToTensor(),\n    ])\n\n    if isinstance(test_images, Image.Image):\n        test_images = [test_images]\n\n    batch = torch.stack([\n        img if isinstance(img, torch.Tensor) else transform(img)\n        for img in test_images\n    ]).to(device)\n\n    with torch.no_grad():\n        output = model(pixel_values=batch)\n        preds = output[1] if isinstance(output, tuple) else output\n\n    return preds.cpu().numpy().tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:32:34.429989Z","iopub.execute_input":"2025-05-17T14:32:34.430285Z","iopub.status.idle":"2025-05-17T14:32:34.445452Z","shell.execute_reply.started":"2025-05-17T14:32:34.430264Z","shell.execute_reply":"2025-05-17T14:32:34.444751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_model(dataset=dataset,\n            value_column_name='targets',\n            test_split=0.2,\n            output_dir='/kaggle/working/results',\n            num_train_epochs=5,\n            learning_rate=1e-4)","metadata":{"trusted":true,"_kg_hide-input":false,"execution":{"iopub.status.busy":"2025-05-17T14:32:37.120876Z","iopub.execute_input":"2025-05-17T14:32:37.121202Z","iopub.status.idle":"2025-05-17T14:33:23.025269Z","shell.execute_reply.started":"2025-05-17T14:32:37.121180Z","shell.execute_reply":"2025-05-17T14:33:23.023957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_all(images, batch_size=30):\n    \"\"\"\n    All images will be divided into batches and input into the model.\n    \"\"\"\n    all_preds = []\n\n    # all image len\n    total = len(images)\n\n    for start in range(0, total, batch_size):\n        end = min(start + batch_size, total)\n        batch_images = images[start:end]\n\n        preds = predict_batch(batch_images)\n        all_preds.extend(preds)\n\n    return all_preds\npreds = predict_all(test_data[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:26:56.950009Z","iopub.execute_input":"2025-05-17T14:26:56.950320Z","iopub.status.idle":"2025-05-17T14:28:39.616446Z","shell.execute_reply.started":"2025-05-17T14:26:56.950297Z","shell.execute_reply":"2025-05-17T14:28:39.615759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(preds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:18:14.748466Z","iopub.status.idle":"2025-05-17T14:18:14.748709Z","shell.execute_reply":"2025-05-17T14:18:14.748609Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Creating Submission","metadata":{}},{"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    \n    # Dictionary to store DataFrames for each slide\n    train_spot_tables = {}\n    \n    for slide_name in train_spots.keys():\n        # Load dataset as NumPy structured array\n        spot_array = np.array(train_spots[slide_name])\n        \n        # Convert to DataFrame\n        df = pd.DataFrame(spot_array)\n        \n        # Store in dictionary\n        train_spot_tables[slide_name] = df\n\n# Example: Display the spots table for slide 'S_1'\ntrain_spot_tables['S_1']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:28:39.617396Z","iopub.execute_input":"2025-05-17T14:28:39.617609Z","iopub.status.idle":"2025-05-17T14:28:39.692823Z","shell.execute_reply.started":"2025-05-17T14:28:39.617590Z","shell.execute_reply":"2025-05-17T14:28:39.692001Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Display spot table for Test slide (only the spot coordinates on 2D array)\nwith h5py.File(\"/kaggle/input/el-hackathon-2025/elucidata_ai_challenge_data.h5\", \"r\") as f:\n    test_spots = f[\"spots/Test\"]\n    spot_array = np.array(test_spots['S_7'])\n    test_spot_table = pd.DataFrame(spot_array)\n    \n# Show the test spots coordinates for slide 'S_7'\ntest_spot_table","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:28:39.694127Z","iopub.execute_input":"2025-05-17T14:28:39.694417Z","iopub.status.idle":"2025-05-17T14:28:39.710010Z","shell.execute_reply.started":"2025-05-17T14:28:39.694396Z","shell.execute_reply":"2025-05-17T14:28:39.709333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Use the cell type columns from the train spots table; assuming first two columns are (x, y)\ncell_type_columns = train_spot_tables['S_1'].columns[2:].values  # Expecting 35 cell types here\nindices = test_spot_table.index.values  # All spots on the Test slide\n\n# Create a 2D array of random floats between 0 and 2 for each spot and cell type\npredicted_labels = pd.DataFrame(preds, columns=cell_type_columns, index=indices)\n\npredicted_labels.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:28:39.710840Z","iopub.execute_input":"2025-05-17T14:28:39.711166Z","iopub.status.idle":"2025-05-17T14:28:39.739097Z","shell.execute_reply.started":"2025-05-17T14:28:39.711136Z","shell.execute_reply":"2025-05-17T14:28:39.738480Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. Submission File Generation\n\nFinally, we generate the submission file in the required format.  \nEach row corresponds to a test spot with its identifier (constructed here as the index) followed by 35 predictions.","metadata":{}},{"cell_type":"code","source":"# Prepare submission DataFrame: spot_id column and then predictions for each cell type\nsubmission_df = predicted_labels.copy()\nsubmission_df.insert(0, 'ID', submission_df.index)\n\n# Save the submission file as submission.csv\nsubmission_df.to_csv(\"./submission.csv\", index=False)\nprint(\"Submission file 'submission.csv' created!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-17T14:28:39.739742Z","iopub.execute_input":"2025-05-17T14:28:39.740019Z","iopub.status.idle":"2025-05-17T14:28:39.850603Z","shell.execute_reply.started":"2025-05-17T14:28:39.739998Z","shell.execute_reply":"2025-05-17T14:28:39.849784Z"}},"outputs":[],"execution_count":null}]}