{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":59093,"databundleVersionId":7469972},{"sourceType":"datasetVersion","sourceId":15572536,"datasetId":9962817,"databundleVersionId":16503673},{"sourceType":"modelInstanceVersion","sourceId":372176,"databundleVersionId":12140652,"modelInstanceId":308005,"modelId":328453}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":3978.826578,"end_time":"2025-05-20T12:42:12.708853","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-05-20T11:35:53.882275","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"01cc6714","cell_type":"code","source":"# !pip install efficientnet_pytorch\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom torchvision import datasets, models, transforms\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport warnings\nimport time\nimport cv2\nwarnings.filterwarnings('ignore')\n\n","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2026-04-07T08:04:57.459376Z","iopub.execute_input":"2026-04-07T08:04:57.459664Z","iopub.status.idle":"2026-04-07T08:05:08.751753Z","shell.execute_reply.started":"2026-04-07T08:04:57.459626Z","shell.execute_reply":"2026-04-07T08:05:08.751134Z"},"papermill":{"duration":18.214223,"end_time":"2025-05-20T11:36:18.075758","exception":false,"start_time":"2025-05-20T11:35:59.861535","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e9814ddf","cell_type":"code","source":"start_time = time.time()\nBASE_DIR = \"/kaggle/input/competitions/hms-harmful-brain-activity-classification\"\n\nbrain_activities = ['Seizure', 'GPD', 'LRDA', 'Other', 'GRDA', 'LPD']\nactivity_mapping = {activity: idx for idx, activity in enumerate(brain_activities)}","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:08:56.988853Z","iopub.execute_input":"2026-04-07T08:08:56.989211Z","iopub.status.idle":"2026-04-07T08:08:56.995295Z","shell.execute_reply.started":"2026-04-07T08:08:56.989172Z","shell.execute_reply":"2026-04-07T08:08:56.994503Z"},"papermill":{"duration":0.007923,"end_time":"2025-05-20T11:36:18.086434","exception":false,"start_time":"2025-05-20T11:36:18.078511","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"bb387974","cell_type":"code","source":"# import pandas as pd\n# import numpy as np\n\n# df = pd.read_csv(f\"{BASE_DIR}train.csv\")\n# copy_df = df.copy()\n\n# cols_to_check = ['seizure_vote', 'lpd_vote','gpd_vote', 'lrda_vote', 'grda_vote', 'other_vote']\n# copy_df['total_votes'] = copy_df[cols_to_check].sum(axis=1)\n\n# # Get the column with max vote\n# copy_df['max_column'] = copy_df[cols_to_check].apply(lambda row: row.idxmax(), axis=1)\n\n# # Use numpy advanced indexing to get max value per row\n# row_idx = np.arange(len(copy_df))\n# col_idx = copy_df['max_column']\n# max_vals = copy_df[cols_to_check].to_numpy()[row_idx, copy_df[cols_to_check].columns.get_indexer(col_idx)]\n\n# copy_df['confidence'] = max_vals / copy_df['total_votes']\n","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:05:08.758982Z","iopub.execute_input":"2026-04-07T08:05:08.759265Z","iopub.status.idle":"2026-04-07T08:05:08.772778Z","shell.execute_reply.started":"2026-04-07T08:05:08.759226Z","shell.execute_reply":"2026-04-07T08:05:08.771803Z"},"papermill":{"duration":1.101316,"end_time":"2025-05-20T11:36:19.189852","exception":false,"start_time":"2025-05-20T11:36:18.088536","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"88559f70","cell_type":"code","source":"# df = copy_df[copy_df['confidence'] == 1.0].drop(['total_votes', 'max_column', 'confidence'], axis=1)","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:05:08.773823Z","iopub.execute_input":"2026-04-07T08:05:08.774233Z","iopub.status.idle":"2026-04-07T08:05:08.784980Z","shell.execute_reply.started":"2026-04-07T08:05:08.774201Z","shell.execute_reply":"2026-04-07T08:05:08.784284Z"},"papermill":{"duration":0.019464,"end_time":"2025-05-20T11:36:19.211691","exception":false,"start_time":"2025-05-20T11:36:19.192227","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"67ae7a6a","cell_type":"code","source":"# toy_df = df.sample(frac=0.4, random_state=42)\n\n# # Split 80% Train, 20% Temp (Validation + Test)\n# train_df, temp_df = train_test_split(toy_df, test_size=0.2, random_state=42)\n\n# # Split 10% Validation, 10% Test from Temp\n# val_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42)\n\ntrain_df = pd.read_csv(\"/kaggle/input/datasets/ashok2000singh/cross-patient/train.csv\")\nval_df = pd.read_csv(\"/kaggle/input/datasets/ashok2000singh/cross-patient/val.csv\")\ntest_df = pd.read_csv(\"/kaggle/input/datasets/ashok2000singh/cross-patient/test.csv\")\n\npercent_of_train_data = 0.1\n\ntrain_df = train_df.sample(frac=percent_of_train_data, random_state=42)\n\nclass_counts = train_df['expert_consensus'].value_counts()\nprint(\"Original class distribution:\", class_counts.to_dict())\n\n# Calculate the average class count (rounded to integer)\navg_count = int(class_counts.mean())\nprint(f\"Average class count: {avg_count}\")\n\n# Create a balanced dataset by adjusting each class to the average count\nbalanced_train_df = pd.DataFrame()\n\nfor class_label, group in train_df.groupby('expert_consensus'):\n    if len(group) > avg_count:\n        # Undersample to average count\n        balanced_group = group.sample(avg_count, random_state=42)\n    elif len(group) < avg_count:\n        # Oversample to average count\n        oversampled = group.sample(avg_count - len(group), replace=True, random_state=42)\n        balanced_group = pd.concat([group, oversampled])\n    else:\n        balanced_group = group\n    \n    balanced_train_df = pd.concat([balanced_train_df, balanced_group])\n\n# Shuffle the balanced dataframe\nbalanced_train_df = balanced_train_df.sample(frac=1, random_state=42).reset_index(drop=True)\n\n# Verify the new class distribution\nnew_class_counts = balanced_train_df['expert_consensus'].value_counts()\nprint(\"Balanced class distribution:\", new_class_counts.to_dict())\n\n# Save the balanced dataframe\nbalanced_train_df.to_csv(\"train.csv\", index=False)\nval_df.to_csv(\"validation.csv\", index=False)\ntest_df.to_csv(\"test.csv\", index=False)\n\nprint(\"Splitting done with balanced training data!\")\nprint(\"Train:\", len(balanced_train_df), \"Val:\", len(val_df), \"Test:\", len(test_df))","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:05:08.785941Z","iopub.execute_input":"2026-04-07T08:05:08.786290Z","iopub.status.idle":"2026-04-07T08:05:09.228556Z","shell.execute_reply.started":"2026-04-07T08:05:08.786268Z","shell.execute_reply":"2026-04-07T08:05:09.227614Z"},"papermill":{"duration":0.151609,"end_time":"2025-05-20T11:36:19.365423","exception":false,"start_time":"2025-05-20T11:36:19.213814","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"85b671dd","cell_type":"code","source":"class ChunkedBrainActivityDataset(Dataset):\n    def __init__(self, csv_file, base_dir, activity_mapping,md):\n        self.df = csv_file\n        self.base_dir = base_dir\n        self.activity_mapping = activity_mapping\n        self.resize_transform = transforms.Resize((224, 224))\n        self.md = md\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        spect_id, label, offset = self.df.iloc[idx][[\"spectrogram_id\", \"expert_consensus\", \"spectrogram_label_offset_seconds\"]]\n\n        temp_df = pd.read_parquet(f'{self.base_dir}/train_spectrograms/{spect_id}.parquet')\n        temp_df.drop(['time'], axis=1, inplace=True)\n\n        start = int(offset) // 2\n        temp_df = temp_df[start:start+300]\n        temp_df = np.log1p(temp_df)\n        temp_df /= temp_df.max()\n        temp_arr = np.nan_to_num(temp_df.to_numpy(), nan=1e-4)\n\n        # Use OpenCV to apply a colormap and convert to RGB\n        temp_arr_uint8 = np.uint8(255 * temp_arr)\n        rgb_image = cv2.applyColorMap(temp_arr_uint8, cv2.COLORMAP_JET)\n\n        # Normalize to [0, 1] and convert to tensor\n        rgb_image = rgb_image.astype(np.float32) / 255.0\n        rgb_image_tensor = torch.tensor(rgb_image).permute(2, 0, 1)  # (C, H, W)\n        rgb_image_tensor = self.resize_transform(rgb_image_tensor)\n            \n        y = self.activity_mapping[label]\n        y_tensor = torch.nn.functional.one_hot(torch.tensor(y, dtype=torch.long), num_classes=6).float()\n        \n        return rgb_image_tensor, y_tensor","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:05:09.229709Z","iopub.execute_input":"2026-04-07T08:05:09.230062Z","iopub.status.idle":"2026-04-07T08:05:09.238379Z","shell.execute_reply.started":"2026-04-07T08:05:09.230025Z","shell.execute_reply":"2026-04-07T08:05:09.237366Z"},"papermill":{"duration":0.010174,"end_time":"2025-05-20T11:36:19.378117","exception":false,"start_time":"2025-05-20T11:36:19.367943","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"ce85e6ec","cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.models as models\n\ntrain_dataset = ChunkedBrainActivityDataset(csv_file=train_df, base_dir=BASE_DIR, activity_mapping=activity_mapping, md=\"lr\")\nval_dataset = ChunkedBrainActivityDataset(csv_file=val_df, base_dir=BASE_DIR, activity_mapping=activity_mapping, md=\"lr\")\ntest_dataset = ChunkedBrainActivityDataset(csv_file=test_df, base_dir=BASE_DIR, activity_mapping=activity_mapping, md=\"lr\")\n\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2, pin_memory=True, prefetch_factor=2)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=2, pin_memory=True, prefetch_factor=2)\ntest_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2, pin_memory=True, prefetch_factor=2)\n\nclass EfficientNetV2EncoderLogisticRegression(nn.Module):\n    def __init__(self, num_classes=6):\n        super(EfficientNetV2EncoderLogisticRegression, self).__init__()\n        self.encoder = models.efficientnet_v2_s(weights=models.EfficientNet_V2_S_Weights.DEFAULT)\n        n_features = self.encoder.classifier[1].in_features\n        self.encoder.classifier = nn.Identity()\n        self.logistic_regression = nn.Linear(n_features, num_classes)\n\n    def forward(self, x):\n        features = self.encoder(x)\n        logits = self.logistic_regression(features)\n        return logits\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nnum_classes = 6\nmodel = EfficientNetV2EncoderLogisticRegression(num_classes=num_classes).to(device)\nprint(\"Model initialized successfully!\")\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n\nnum_epochs = 10\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, targets in train_loader:\n        images = images.to(device)\n        targets = targets.to(device)\n        labels = torch.argmax(targets, dim=1)\n\n        optimizer.zero_grad()\n        logits = model(images)\n        loss = criterion(logits, labels)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item() * images.size(0)\n        _, preds = torch.max(logits, dim=1)\n        total += labels.size(0)\n        correct += (preds == labels).sum().item()\n\n    epoch_loss = running_loss / total\n    epoch_acc = 100 * correct / total\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.2f}%\")","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:09:02.086980Z","iopub.execute_input":"2026-04-07T08:09:02.087257Z"},"papermill":{"duration":3900.692086,"end_time":"2025-05-20T12:41:20.072313","exception":false,"start_time":"2025-05-20T11:36:19.380227","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8f5f08e0","cell_type":"code","source":"# Import necessary libraries\nfrom sklearn.metrics import confusion_matrix, accuracy_score, recall_score, f1_score\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\n\n# Add this code after your model evaluation section\ndef evaluate_classification_metrics(model, data_loader, device):\n    \"\"\"\n    Calculate accuracy, recall, F1 score and confusion matrix\n    \"\"\"\n    model.eval()\n    all_preds = []\n    all_labels = []\n    \n    with torch.no_grad():\n        for images, targets in data_loader:\n            images = images.to(device)\n            targets = targets.to(device)\n            labels = torch.argmax(targets, dim=1)\n            \n            # Forward pass\n            outputs = model(images)\n            _, preds = torch.max(outputs, dim=1)\n            \n            # Collect predictions and labels\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n    \n    # Convert to numpy arrays\n    all_preds = np.array(all_preds)\n    all_labels = np.array(all_labels)\n    \n    # Calculate metrics\n    accuracy = accuracy_score(all_labels, all_preds)\n    recall = recall_score(all_labels, all_preds, average='macro')\n    f1 = f1_score(all_labels, all_preds, average='macro')\n    \n    # Calculate confusion matrix\n    cm = confusion_matrix(all_labels, all_preds)\n    \n    return accuracy, recall, f1, cm\n\n# Function to plot confusion matrix\ndef plot_confusion_matrix(cm, class_names=None):\n    \"\"\"\n    Plot confusion matrix as a heatmap\n    \"\"\"\n    if class_names is None:\n        class_names = [f'Class {i}' for i in range(cm.shape[0])]\n    \n    plt.figure(figsize=(10, 8))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=class_names, yticklabels=class_names)\n    plt.xlabel('Predicted labels')\n    plt.ylabel('True labels')\n    plt.title('Confusion Matrix')\n    plt.tight_layout()\n    plt.show()\n\n# Add these lines to your main code after model training\nprint(\"Calculating metrics on test data...\")\n\n# Define class names if available, otherwise use generic names\nclass_names = ['Seizure', 'GPD', 'LRDA', 'Other', 'GRDA', 'LPD']  # Replace with your actual class names\n\n# Calculate metrics\naccuracy, recall, f1, confusion_mat = evaluate_classification_metrics(model, test_loader, device)\n\n# Print the metrics\nprint(f\"Accuracy: {accuracy:.4f}\")\nprint(f\"Recall: {recall:.4f}\")\nprint(f\"F1 Score: {f1:.4f}\")\n\n# Plot confusion matrix\nprint(\"\\nConfusion Matrix:\")\nplot_confusion_matrix(confusion_mat, class_names)","metadata":{"execution":{"iopub.status.busy":"2026-04-07T08:05:11.001864Z","iopub.status.idle":"2026-04-07T08:05:11.002549Z","shell.execute_reply.started":"2026-04-07T08:05:11.002360Z","shell.execute_reply":"2026-04-07T08:05:11.002385Z"},"papermill":{"duration":48.933319,"end_time":"2025-05-20T12:42:09.008627","exception":false,"start_time":"2025-05-20T12:41:20.075308","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}