{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:51:27.671522Z","iopub.execute_input":"2025-07-15T06:51:27.671798Z","iopub.status.idle":"2025-07-15T06:51:57.708631Z","shell.execute_reply.started":"2025-07-15T06:51:27.671776Z","shell.execute_reply":"2025-07-15T06:51:57.707523Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 1.1: Load train.csv and Check Distribution","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Load labels\ndf = pd.read_csv(\"/kaggle/input/hms-harmful-brain-activity-classification/train.csv\")\n\n# Display basic info\nprint(df.shape)\nprint(df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:51:57.710279Z","iopub.execute_input":"2025-07-15T06:51:57.710594Z","iopub.status.idle":"2025-07-15T06:51:57.990168Z","shell.execute_reply.started":"2025-07-15T06:51:57.710569Z","shell.execute_reply":"2025-07-15T06:51:57.989371Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot class distribution\nlabels = [\"seizure_vote\", \"lpd_vote\", \"gpd_vote\", \"lrda_vote\", \"grda_vote\", \"other_vote\"]\ndf[labels].mean().plot(kind='bar', title='Average Class Distribution')\nplt.ylabel('Average Vote Probability')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:51:57.990906Z","iopub.execute_input":"2025-07-15T06:51:57.991116Z","iopub.status.idle":"2025-07-15T06:51:58.437157Z","shell.execute_reply.started":"2025-07-15T06:51:57.991097Z","shell.execute_reply":"2025-07-15T06:51:58.436219Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Step 1.2: Visualize a Raw EEG Signal","metadata":{}},{"cell_type":"code","source":"# Load EEG file again\nimport pyarrow.parquet as pq\n\neeg_path = \"/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet\"\neeg_df = pq.read_table(eeg_path).to_pandas()\n\n# See all available columns\nprint(eeg_df.columns.tolist())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:53:09.798908Z","iopub.execute_input":"2025-07-15T06:53:09.799276Z","iopub.status.idle":"2025-07-15T06:53:09.814984Z","shell.execute_reply.started":"2025-07-15T06:53:09.799245Z","shell.execute_reply":"2025-07-15T06:53:09.814189Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Select a few EEG channels to visualize\nchannels = eeg_df.columns[:4]  # Take first 4 channels (e.g., 'Fp1', 'Fp2', 'F3', 'F4')\n\n# Plot the EEG signals\neeg_df[channels].plot(figsize=(15, 5), title=\"EEG Channel Signals (Sample ID: 1000913311)\")\nplt.xlabel(\"Time step (5 ms each @200Hz)\")\nplt.ylabel(\"Signal amplitude (μV)\")\nplt.grid(True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:53:29.072174Z","iopub.execute_input":"2025-07-15T06:53:29.072481Z","iopub.status.idle":"2025-07-15T06:53:30.181199Z","shell.execute_reply.started":"2025-07-15T06:53:29.072457Z","shell.execute_reply":"2025-07-15T06:53:30.180313Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 1.3: Visualize Spectrogram (like an image)","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\n# Load train.csv\ndf = pd.read_csv(\"/kaggle/input/hms-harmful-brain-activity-classification/train.csv\")\n\n# Pick a sample eeg_id\neeg_id = 1000913311\n\n# Get corresponding spectrogram_id(s)\nspec_id = df[df[\"eeg_id\"] == eeg_id][\"spectrogram_id\"].iloc[0]\nprint(f\"SPECTROGRAM ID: {spec_id}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:54:13.626353Z","iopub.execute_input":"2025-07-15T06:54:13.627005Z","iopub.status.idle":"2025-07-15T06:54:13.809625Z","shell.execute_reply.started":"2025-07-15T06:54:13.626975Z","shell.execute_reply":"2025-07-15T06:54:13.808640Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pyarrow.parquet as pq\n\n# Construct the correct file path using spectrogram_id\nspec_path = f\"/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/{spec_id}.parquet\"\n\n# Read the file\nspectrogram = pq.read_table(spec_path).to_pandas()\n\n# Inspect shape\nprint(spectrogram.shape)\nspectrogram.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:54:26.413150Z","iopub.execute_input":"2025-07-15T06:54:26.413455Z","iopub.status.idle":"2025-07-15T06:54:26.493569Z","shell.execute_reply.started":"2025-07-15T06:54:26.413434Z","shell.execute_reply":"2025-07-15T06:54:26.492673Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\nspec_data = spectrogram.drop(columns=[\"time\"]).values  # shape: (300, 400)\n\nimg = spec_data.reshape(300, 20, 20)  # shape: (time, freq, channels)\nimg = img.transpose(1, 0, 2)  # now shape = (freq, time, channels)\n\nimport matplotlib.pyplot as plt\n\nchannel_idx = 10\nplt.figure(figsize=(10, 6))\nplt.imshow(img[:, :, channel_idx], cmap=\"magma\", aspect=\"auto\")\nplt.title(f\"Spectrogram of Channel {channel_idx}\")\nplt.xlabel(\"Time Steps\")\nplt.ylabel(\"Frequency Bins\")\nplt.colorbar(label=\"Power\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:56:08.010751Z","iopub.execute_input":"2025-07-15T06:56:08.011054Z","iopub.status.idle":"2025-07-15T06:56:08.452884Z","shell.execute_reply.started":"2025-07-15T06:56:08.011032Z","shell.execute_reply":"2025-07-15T06:56:08.452033Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1.4 Match EEG ID with Label","metadata":{}},{"cell_type":"code","source":"eeg_id = 1000913311\nlabel_row = df[df[\"eeg_id\"] == eeg_id]\nprint(label_row[labels])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:57:10.155170Z","iopub.execute_input":"2025-07-15T06:57:10.155472Z","iopub.status.idle":"2025-07-15T06:57:10.164381Z","shell.execute_reply.started":"2025-07-15T06:57:10.155450Z","shell.execute_reply":"2025-07-15T06:57:10.163233Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1.5: Feature Extraction from EEG","metadata":{}},{"cell_type":"markdown","source":"1.5.1: Load EEG and Extract Channel Stats","metadata":{}},{"cell_type":"code","source":"import pyarrow.parquet as pq\nimport pandas as pd\nimport numpy as np\nfrom scipy.stats import skew, kurtosis\n\n# Load EEG file\neeg_path = \"/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet\"\neeg_df = pq.read_table(eeg_path).to_pandas()\n\n# Basic stats for each EEG channel\nfeatures = {}\nfor col in eeg_df.columns:\n    signal = eeg_df[col].values\n    features[f\"{col}_mean\"] = np.mean(signal)\n    features[f\"{col}_std\"] = np.std(signal)\n    features[f\"{col}_skew\"] = skew(signal)\n    features[f\"{col}_kurt\"] = kurtosis(signal)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:57:40.931211Z","iopub.execute_input":"2025-07-15T06:57:40.931540Z","iopub.status.idle":"2025-07-15T06:57:41.455973Z","shell.execute_reply.started":"2025-07-15T06:57:40.931516Z","shell.execute_reply":"2025-07-15T06:57:41.455008Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"1.5.2: Add Band Power (Delta, Theta, Alpha, Beta)","metadata":{}},{"cell_type":"code","source":"from scipy.signal import welch\n\ndef bandpower(signal, sf, band, window_sec=2):\n    band = np.asarray(band)\n    freqs, psd = welch(signal, sf, nperseg=window_sec * sf)\n    idx_band = np.logical_and(freqs >= band[0], freqs <= band[1])\n    return np.trapz(psd[idx_band], freqs[idx_band])\n\nsf = 200  # Sampling frequency (Hz)\nbands = {'delta': (0.5, 4), 'theta': (4, 8), 'alpha': (8, 13), 'beta': (13, 30)}\n\nfor col in eeg_df.columns:\n    for band_name, band_range in bands.items():\n        bp = bandpower(eeg_df[col].values, sf, band_range)\n        features[f\"{col}_{band_name}_power\"] = bp","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:58:00.980683Z","iopub.execute_input":"2025-07-15T06:58:00.981097Z","iopub.status.idle":"2025-07-15T06:58:01.234729Z","shell.execute_reply.started":"2025-07-15T06:58:00.981075Z","shell.execute_reply":"2025-07-15T06:58:01.233780Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"1.5.3: Convert Features to DataFrame","metadata":{}},{"cell_type":"code","source":"features_df = pd.DataFrame([features])\nfeatures_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T06:58:17.332706Z","iopub.execute_input":"2025-07-15T06:58:17.333164Z","iopub.status.idle":"2025-07-15T06:58:17.359333Z","shell.execute_reply.started":"2025-07-15T06:58:17.333141Z","shell.execute_reply":"2025-07-15T06:58:17.358356Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  2: Modeling from Spectrogram or Features.","metadata":{}},{"cell_type":"markdown","source":"1. EEG Feature Extraction (Tabular)","metadata":{}},{"cell_type":"code","source":"from scipy.stats import skew, kurtosis\nfrom scipy.signal import welch\nimport numpy as np\nimport pandas as pd\nimport pyarrow.parquet as pq\n\ndef extract_features_from_eeg(file_path, sf=200):\n    df = pq.read_table(file_path).to_pandas()\n    bands = {'delta': (0.5, 4), 'theta': (4, 8), 'alpha': (8, 13), 'beta': (13, 30)}\n    \n    def bandpower(signal, sf, band):\n        freqs, psd = welch(signal, sf, nperseg=sf*2)\n        idx = (freqs >= band[0]) & (freqs <= band[1])\n        return np.trapz(psd[idx], freqs[idx])\n\n    features = {}\n    for col in df.columns:\n        sig = df[col].values\n        features[f\"{col}_mean\"] = np.mean(sig)\n        features[f\"{col}_std\"] = np.std(sig)\n        features[f\"{col}_skew\"] = skew(sig)\n        features[f\"{col}_kurt\"] = kurtosis(sig)\n        for name, rng in bands.items():\n            features[f\"{col}_{name}_power\"] = bandpower(sig, sf, rng)\n    \n    return pd.Series(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:00:35.887816Z","iopub.execute_input":"2025-07-15T07:00:35.888120Z","iopub.status.idle":"2025-07-15T07:00:35.896155Z","shell.execute_reply.started":"2025-07-15T07:00:35.888097Z","shell.execute_reply":"2025-07-15T07:00:35.895127Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"2. LightGBM Model","metadata":{}},{"cell_type":"code","source":"import lightgbm as lgb\n\ndef build_train_lgbm(X, y):\n    models = {}\n    for i, col in enumerate(y.columns):\n        m = lgb.LGBMRegressor(n_estimators=200)\n        m.fit(X, y[col])\n        models[col] = m\n    return models\n\ndef predict_with_lgbm(models, X):\n    preds = np.stack([models[col].predict(X) for col in models], axis=1)\n    return preds / preds.sum(axis=1, keepdims=True)  # Normalize","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:00:53.593904Z","iopub.execute_input":"2025-07-15T07:00:53.594222Z","iopub.status.idle":"2025-07-15T07:01:01.262673Z","shell.execute_reply.started":"2025-07-15T07:00:53.594200Z","shell.execute_reply":"2025-07-15T07:01:01.261957Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"3. Spectrogram Tensor Preparation","metadata":{}},{"cell_type":"code","source":"import torch\n\ndef prepare_spectrogram_tensor(spectrogram_df):\n    spec = spectrogram_df.drop(columns=[\"time\"]).values\n    spec = spec.reshape(300, 20, 20).transpose(2, 0, 1)  # (channels=20, time=300, freq=20)\n    return torch.tensor(spec).float().unsqueeze(0)  # shape: [1, 20, 300, 20]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:01:11.696031Z","iopub.execute_input":"2025-07-15T07:01:11.696653Z","iopub.status.idle":"2025-07-15T07:01:18.479637Z","shell.execute_reply.started":"2025-07-15T07:01:11.696620Z","shell.execute_reply":"2025-07-15T07:01:18.478858Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"4. CNN Model (PyTorch)","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\n\nclass SpectrogramCNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(20, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2)\n        )\n        self.fc = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(64 * 75 * 5, 128),\n            nn.ReLU(),\n            nn.Linear(128, 6),\n            nn.Softmax(dim=1)\n        )\n\n    def forward(self, x):\n        return self.fc(self.conv(x))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:01:33.587075Z","iopub.execute_input":"2025-07-15T07:01:33.588171Z","iopub.status.idle":"2025-07-15T07:01:33.596865Z","shell.execute_reply.started":"2025-07-15T07:01:33.588135Z","shell.execute_reply":"2025-07-15T07:01:33.595878Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"5. Blend Predictions","metadata":{}},{"cell_type":"code","source":"def blend_predictions(cnn_preds, lgbm_preds, w1=0.6, w2=0.4):\n    blend = w1 * cnn_preds + w2 * lgbm_preds\n    return blend / blend.sum(axis=1, keepdims=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:02:13.197825Z","iopub.execute_input":"2025-07-15T07:02:13.198163Z","iopub.status.idle":"2025-07-15T07:02:13.202883Z","shell.execute_reply.started":"2025-07-15T07:02:13.198139Z","shell.execute_reply":"2025-07-15T07:02:13.201794Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"6. Example main() Pipeline","metadata":{}},{"cell_type":"code","source":"def main():\n    # Load sample EEG & Spectrogram file paths\n    eeg_path = \"/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet\"\n    spec_path = \"/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/1000913311.parquet\"\n\n    # === EEG Tabular Features ===\n    X = pd.DataFrame([extract_features_from_eeg(eeg_path)])\n    y = pd.DataFrame([[1, 0, 0, 0, 0, 0]], columns=[\"seizure_vote\", \"lpd_vote\", \"gpd_vote\", \"lrda_vote\", \"grda_vote\", \"other_vote\"])  # dummy target\n    lgbm_models = build_train_lgbm(X, y)\n    lgbm_preds = predict_with_lgbm(lgbm_models, X)\n\n    # === CNN Prediction ===\n    spec_df = pq.read_table(spec_path).to_pandas()\n    cnn_input = prepare_spectrogram_tensor(spec_df)\n    cnn_model = SpectrogramCNN()\n    with torch.no_grad():\n        cnn_preds = cnn_model(cnn_input).numpy()\n\n    # === Blend ===\n    ensemble = blend_predictions(cnn_preds, lgbm_preds)\n    print(\"Blended Prediction:\", ensemble)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:02:35.372273Z","iopub.execute_input":"2025-07-15T07:02:35.372620Z","iopub.status.idle":"2025-07-15T07:02:35.379103Z","shell.execute_reply.started":"2025-07-15T07:02:35.372594Z","shell.execute_reply":"2025-07-15T07:02:35.378177Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🧠 FULL PIPELINE FOR HMS COMPETITION","metadata":{}},{"cell_type":"markdown","source":"1. Step 1: EEG Feature Extraction (Batched)","metadata":{}},{"cell_type":"code","source":"import os\nfrom tqdm import tqdm\n\ndef batch_extract_eeg_features(eeg_dir, limit=None):\n    feature_list = []\n    ids = []\n    \n    files = os.listdir(eeg_dir)\n    if limit: files = files[:limit]\n    \n    for f in tqdm(files):\n        eeg_id = int(f.replace(\".parquet\", \"\"))\n        try:\n            path = os.path.join(eeg_dir, f)\n            features = extract_features_from_eeg(path)\n            feature_list.append(features)\n            ids.append(eeg_id)\n        except Exception as e:\n            print(f\"Failed on {f}: {e}\")\n    \n    X = pd.DataFrame(feature_list)\n    X[\"eeg_id\"] = ids\n    return X.set_index(\"eeg_id\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:04:31.523250Z","iopub.execute_input":"2025-07-15T07:04:31.523579Z","iopub.status.idle":"2025-07-15T07:04:31.529997Z","shell.execute_reply.started":"2025-07-15T07:04:31.523537Z","shell.execute_reply":"2025-07-15T07:04:31.528994Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 2: Load Labels & Merge with EEG Features","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(\"/kaggle/input/hms-harmful-brain-activity-classification/train.csv\")\n\n# Votes as labels\nlabel_cols = [\"seizure_vote\", \"lpd_vote\", \"gpd_vote\", \"lrda_vote\", \"grda_vote\", \"other_vote\"]\nlabels = train_df.groupby(\"eeg_id\")[label_cols].mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:04:47.048985Z","iopub.execute_input":"2025-07-15T07:04:47.049277Z","iopub.status.idle":"2025-07-15T07:04:47.248049Z","shell.execute_reply.started":"2025-07-15T07:04:47.049257Z","shell.execute_reply":"2025-07-15T07:04:47.247272Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" Step 3: Train LightGBM on Extracted Features","metadata":{}},{"cell_type":"code","source":"features_df = batch_extract_eeg_features(\"/kaggle/input/hms-harmful-brain-activity-classification/train_eegs\", limit=5000)\n\n# Join features with labels\ndata = features_df.join(labels, how=\"inner\")\n\n# Split\nfrom sklearn.model_selection import train_test_split\nX_train, X_val, y_train, y_val = train_test_split(data.drop(columns=label_cols), data[label_cols], test_size=0.2)\n\n# Train LGBM\nlgbm_models = build_train_lgbm(X_train, y_train)\nlgbm_preds_val = predict_with_lgbm(lgbm_models, X_val)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:05:05.548015Z","iopub.execute_input":"2025-07-15T07:05:05.548322Z","iopub.status.idle":"2025-07-15T07:16:35.648797Z","shell.execute_reply.started":"2025-07-15T07:05:05.548301Z","shell.execute_reply":"2025-07-15T07:16:35.647594Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 4: CNN on Spectrograms","metadata":{}},{"cell_type":"code","source":"def batch_predict_cnn(cnn_model, spectro_dir, ids):\n    cnn_model.eval()\n    preds = []\n    with torch.no_grad():\n        for eid in tqdm(ids):\n            try:\n                df = pq.read_table(f\"{spectro_dir}/{eid}.parquet\").to_pandas()\n                tensor = prepare_spectrogram_tensor(df)\n                pred = cnn_model(tensor).numpy()[0]\n                preds.append(pred)\n            except:\n                preds.append([1/6] * 6)  # fallback: uniform\n    return np.array(preds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:16:35.650837Z","iopub.execute_input":"2025-07-15T07:16:35.651169Z","iopub.status.idle":"2025-07-15T07:16:35.658100Z","shell.execute_reply.started":"2025-07-15T07:16:35.651139Z","shell.execute_reply":"2025-07-15T07:16:35.657242Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 5: Blend Predictions","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass SpectrogramCNN(nn.Module):\n    def __init__(self):\n        super(SpectrogramCNN, self).__init__()\n        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)\n        self.pool1 = nn.MaxPool2d(2)\n        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n        self.pool2 = nn.MaxPool2d(2)\n        self.dropout = nn.Dropout(0.3)\n        self.fc1 = nn.Linear(64 * 32 * 32, 128)\n        self.fc2 = nn.Linear(128, 6)  # 6 classes for 6 EEG pattern votes\n\n    def forward(self, x):\n        x = self.pool1(F.relu(self.conv1(x)))   # [B, 32, 64, 64]\n        x = self.pool2(F.relu(self.conv2(x)))   # [B, 64, 32, 32]\n        x = x.view(x.size(0), -1)               # Flatten\n        x = F.relu(self.fc1(x))\n        x = self.dropout(x)\n        x = self.fc2(x)\n        return F.softmax(x, dim=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:21:29.806560Z","iopub.execute_input":"2025-07-15T07:21:29.807007Z","iopub.status.idle":"2025-07-15T07:21:29.817188Z","shell.execute_reply.started":"2025-07-15T07:21:29.806982Z","shell.execute_reply":"2025-07-15T07:21:29.816297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Instantiate model\ncnn_model = SpectrogramCNN()\n\n# Load trained weights (optional)\n# cnn_model.load_state_dict(torch.load(\"model_weights.pth\", map_location=\"cpu\"))\n\ncnn_model.eval()  # Important for inference","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:21:38.216821Z","iopub.execute_input":"2025-07-15T07:21:38.217880Z","iopub.status.idle":"2025-07-15T07:21:38.411917Z","shell.execute_reply.started":"2025-07-15T07:21:38.217849Z","shell.execute_reply":"2025-07-15T07:21:38.411028Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 6: Evaluate on Validation","metadata":{}},{"cell_type":"code","source":"# This assumes cnn_model is defined and in eval mode\ndef batch_predict_cnn(model, spectrogram_dir, eeg_ids):\n    preds = []\n    model.eval()\n\n    for eeg_id in eeg_ids:\n        try:\n            path = f\"{spectrogram_dir}/{eeg_id}.parquet\"\n            df = pd.read_parquet(path)\n\n            # Prepare tensor\n            img = df.drop(columns=\"time\").values.reshape(3, 128, 128)\n            tensor = torch.tensor(img).float().unsqueeze(0)  # (1, 3, 128, 128)\n\n            with torch.no_grad():\n                output = model(tensor)\n                preds.append(output.cpu().numpy()[0])\n\n        except Exception as e:\n            print(f\"Missing or broken file: {eeg_id}, using uniform prediction\")\n            preds.append([1/6]*6)  # fallback if spectrogram missing\n\n    return np.array(preds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:23:06.732157Z","iopub.execute_input":"2025-07-15T07:23:06.733160Z","iopub.status.idle":"2025-07-15T07:23:06.740347Z","shell.execute_reply.started":"2025-07-15T07:23:06.733121Z","shell.execute_reply":"2025-07-15T07:23:06.739544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_preds_val = batch_predict_cnn(\n    cnn_model, \n    \"/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms\", \n    list(X_val.index)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:23:14.414072Z","iopub.execute_input":"2025-07-15T07:23:14.414347Z","iopub.status.idle":"2025-07-15T07:23:15.234221Z","shell.execute_reply.started":"2025-07-15T07:23:14.414328Z","shell.execute_reply":"2025-07-15T07:23:15.233176Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ensemble_val = blend_predictions(cnn_preds_val, lgbm_preds_val)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:23:34.975284Z","iopub.execute_input":"2025-07-15T07:23:34.975645Z","iopub.status.idle":"2025-07-15T07:23:34.981392Z","shell.execute_reply.started":"2025-07-15T07:23:34.975617Z","shell.execute_reply":"2025-07-15T07:23:34.980451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from scipy.special import rel_entr\n\ndef kl_divergence(y_true, y_pred):\n    return np.mean(np.sum(rel_entr(y_true, y_pred), axis=1))\n\nkl = kl_divergence(y_val.values, ensemble_val)\nprint(f\"Validation KL Divergence: {kl:.5f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:23:43.020749Z","iopub.execute_input":"2025-07-15T07:23:43.021364Z","iopub.status.idle":"2025-07-15T07:23:43.027344Z","shell.execute_reply.started":"2025-07-15T07:23:43.021340Z","shell.execute_reply":"2025-07-15T07:23:43.026392Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" Step 7: Generate Test Submission","metadata":{}},{"cell_type":"code","source":"# Load test EEGs\ntest_df = pd.read_csv(\"/kaggle/input/hms-harmful-brain-activity-classification/test.csv\")\ntest_ids = test_df[\"eeg_id\"].unique()\n\n# Extract features\nX_test = batch_extract_eeg_features(\"/kaggle/input/hms-harmful-brain-activity-classification/test_eegs\", limit=None)\nX_test = X_test.loc[X_test.index.isin(test_ids)]\n\n# Predict\nlgbm_test_preds = predict_with_lgbm(lgbm_models, X_test)\ncnn_test_preds = batch_predict_cnn(cnn_model, \"/kaggle/input/hms-harmful-brain-activity-classification/test_spectrograms\", list(X_test.index))\n\n# Blend\ntest_preds = blend_predictions(cnn_test_preds, lgbm_test_preds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:23:53.038016Z","iopub.execute_input":"2025-07-15T07:23:53.038326Z","iopub.status.idle":"2025-07-15T07:23:53.209339Z","shell.execute_reply.started":"2025-07-15T07:23:53.038303Z","shell.execute_reply":"2025-07-15T07:23:53.208372Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Step 8: Format submission.csv","metadata":{}},{"cell_type":"code","source":"submission = pd.DataFrame(test_preds, columns=label_cols)\nsubmission.insert(0, \"eeg_id\", X_test.index)\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T07:24:00.971767Z","iopub.execute_input":"2025-07-15T07:24:00.972085Z","iopub.status.idle":"2025-07-15T07:24:00.987558Z","shell.execute_reply.started":"2025-07-15T07:24:00.972063Z","shell.execute_reply":"2025-07-15T07:24:00.986558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}