{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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":"none","dataSources":[{"sourceType":"competition","sourceId":59093,"databundleVersionId":7469972},{"sourceType":"datasetVersion","sourceId":15997229,"datasetId":10259661,"databundleVersionId":16960115},{"sourceType":"datasetVersion","sourceId":16027838,"datasetId":10280178,"databundleVersionId":16993203}],"dockerImageVersionId":31328,"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\n# import os\n# for 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":"2026-04-29T02:27:28.019027Z","iopub.execute_input":"2026-04-29T02:27:28.019462Z","iopub.status.idle":"2026-04-29T02:27:28.025717Z","shell.execute_reply.started":"2026-04-29T02:27:28.019427Z","shell.execute_reply":"2026-04-29T02:27:28.024232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.model_selection import train_test_split\nfrom scipy.signal import spectrogram\nimport os\nfrom tqdm import tqdm\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport matplotlib.pyplot as plt\n\nfrom torch.utils.data import Subset\nimport random\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:19.249425Z","iopub.execute_input":"2026-04-30T19:53:19.250259Z","iopub.status.idle":"2026-04-30T19:53:24.334177Z","shell.execute_reply.started":"2026-04-30T19:53:19.250215Z","shell.execute_reply":"2026-04-30T19:53:24.333245Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"columnLabels = [\"seizure_vote\", \"lpd_vote\", \"gpd_vote\", \"lrda_vote\", \"grda_vote\", \"other_vote\"] # 6 potential labels. \neegSampleRate = 200 # in hertz\neegDuration = 50  # in seconds\neegLength = eegSampleRate * eegDuration # total number of timesteps for a record\nnumFolds = 5 # for cross validation\nbatchSize = 64 # hyperparams\nnumWorkers = 4 # can try increasing this for more speed in training\n\neegDirectory  = \"/kaggle/input/competitions/hms-harmful-brain-activity-classification/test_eegs\"\nspecDirectory = \"/kaggle/input/competitions/hms-harmful-brain-activity-classification/test_spectrograms\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:24.336049Z","iopub.execute_input":"2026-04-30T19:53:24.336667Z","iopub.status.idle":"2026-04-30T19:53:24.342414Z","shell.execute_reply.started":"2026-04-30T19:53:24.336632Z","shell.execute_reply":"2026-04-30T19:53:24.341231Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/competitions/hms-harmful-brain-activity-classification/test.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:24.343674Z","iopub.execute_input":"2026-04-30T19:53:24.344099Z","iopub.status.idle":"2026-04-30T19:53:24.368465Z","shell.execute_reply.started":"2026-04-30T19:53:24.344067Z","shell.execute_reply":"2026-04-30T19:53:24.367242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def standardize_spec(spec):\n    spec = np.nan_to_num(spec, nan=0.0)\n    spec = np.log1p(spec)\n    spec = (spec - spec.mean()) / (spec.std() + 1e-6)\n\n    return spec","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:24.370631Z","iopub.execute_input":"2026-04-30T19:53:24.371052Z","iopub.status.idle":"2026-04-30T19:53:24.376515Z","shell.execute_reply.started":"2026-04-30T19:53:24.371020Z","shell.execute_reply":"2026-04-30T19:53:24.375391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"features = [\n    (0,4),\n    (4,5),\n    (5,6),\n    (6,7),\n    (0,1),\n    (1,2),\n    (2,3),\n    (3,7),\n    (11,15),\n    (15,16),\n    (16,17),\n    (17,18),\n    (11,12),\n    (12,13),\n    (13,14),\n    (14,18),\n    (8,9),\n    (9,10)\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-29T02:28:24.835157Z","iopub.execute_input":"2026-04-29T02:28:24.835525Z","iopub.status.idle":"2026-04-29T02:28:24.841629Z","shell.execute_reply.started":"2026-04-29T02:28:24.835493Z","shell.execute_reply":"2026-04-29T02:28:24.840641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Dual(Dataset):\n    def __init__(self, df):\n        self.df = df.reset_index(drop=True)\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n\n        eeg_id = row[\"eeg_id\"]\n        spec_id = row[\"spectrogram_id\"]\n        eeg_offset = row.get(\"eeg_label_offset_seconds\", 0)\n        offset     = row.get(\"spectrogram_label_offset_seconds\", 0)\n\n\n        # transforming eeg\n        raw = pd.read_parquet(f\"{eegDirectory}/{eeg_id}.parquet\").values.astype(np.float32)\n        start = int(eeg_offset * eegSampleRate)\n        window = raw[start : start + eegLength]\n    \n        if len(window) < eegLength:\n            pad = np.zeros((eegLength - len(window), raw.shape[1]), dtype=np.float32)\n            window = np.concatenate([window, pad], axis=0)\n    \n        specs = []\n        for e1, e2 in features:\n            data = window[:,e1] - window[:,e2]\n            \n            f,t,Sxx = spectrogram(\n                data, \n                fs=eegSampleRate, \n                nperseg=512, \n                noverlap=256\n            )\n            \n            cleaned_spec = standardize_spec(Sxx)\n            specs.append(cleaned_spec)\n    \n        trans_spec = np.stack(specs, axis=0).astype(np.float32)\n    \n\n        \n        # Spectrogram cleaning\n        # remove time col\n        spec = pd.read_parquet(f\"{specDirectory}/{spec_id}.parquet\").values        \n        spec = spec[:, 1:]\n\n        height, width = spec.shape\n        # 2sec increments\n        rows = 25\n        \n        row_start = int(offset // 2)\n        row_end = row_start + rows\n\n        \n        if row_end > height:\n            row_end = height\n            row_start = height-rows\n\n\n        ll = spec[row_start:row_end, 0:100]\n        rl = spec[row_start:row_end, 100:200]\n        lp = spec[row_start:row_end, 200:300]\n        rp = spec[row_start:row_end, 300:400]\n\n        # pad\n        if len(ll) < rows:\n            pad = rows - len(ll)\n            ll = np.pad(ll, ((0, pad), (0, 0)), mode=\"constant\")\n\n        if len(rl) < rows:\n            pad = rows - len(rl)\n            rl = np.pad(rl, ((0, pad), (0, 0)), mode=\"constant\")\n\n        if len(lp) < rows:\n            pad = rows - len(lp)\n            lp = np.pad(lp, ((0, pad), (0, 0)), mode=\"constant\")\n\n        if len(rp) < rows:\n            pad = rows - len(rp)\n            rp = np.pad(rp, ((0, pad), (0, 0)), mode=\"constant\")\n\n\n\n        spec_crop = np.stack([ll, rl, lp, rp], axis=0)\n        spec_crop = standardize_spec(spec_crop)\n\n        target = np.zeros(len(columnLabels), dtype=np.float32)\n\n        trans_spec = torch.tensor(trans_spec, dtype=torch.float32)\n        spec_crop = torch.tensor(spec_crop, dtype=torch.float32)\n        target = torch.tensor(target, dtype=torch.float32)\n\n        return trans_spec, spec_crop, target","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:24.378035Z","iopub.execute_input":"2026-04-30T19:53:24.378453Z","iopub.status.idle":"2026-04-30T19:53:24.400777Z","shell.execute_reply.started":"2026-04-30T19:53:24.378421Z","shell.execute_reply":"2026-04-30T19:53:24.399620Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class EEGBranch(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.stack = nn.Sequential(\n            nn.Conv2d(18, 32, kernel_size=(3, 3), padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            \n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.AdaptiveAvgPool2d((1, 1)),\n\n            nn.Flatten()\n        )\n    def forward(self, x):\n        return self.stack(x)\n\nclass SpecBranch(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.stack = nn.Sequential(\n            nn.Conv2d(4, 16, kernel_size=(5, 3), padding=(2, 1)),\n            nn.BatchNorm2d(16),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            \n            nn.Conv2d(16, 32, kernel_size=3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            \n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.AdaptiveAvgPool2d((1, 1)),\n\n            nn.Flatten(),\n        )\n    def forward(self, x):\n        return self.stack(x)\n\nclass DualHeadModel(nn.Module):\n    def __init__(self, num_classes=6):\n        super().__init__()\n\n        self.eeg = EEGBranch()\n        self.spec = SpecBranch()\n\n        self.stack = nn.Sequential(\n            nn.Linear(192, 128),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(128, num_classes)\n        )\n\n    def forward(self, eeg, spec):\n        eeg_out = self.eeg(eeg)\n        spec_out = self.spec(spec)\n\n        out = torch.cat((eeg_out, spec_out), 1)\n        out = self.stack(out)\n\n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:26.446194Z","iopub.execute_input":"2026-04-30T19:53:26.447050Z","iopub.status.idle":"2026-04-30T19:53:26.459550Z","shell.execute_reply.started":"2026-04-30T19:53:26.447007Z","shell.execute_reply":"2026-04-30T19:53:26.458240Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def double_mixup(eeg, spec, label, alpha=0.2):\n    dist = torch.distributions.Beta(torch.tensor([alpha]), torch.tensor([alpha]))\n    lam = float(dist.sample(()))\n    \n    eeg = eeg.roll(1, 0).mul(1 - lam).add(eeg.mul(lam))\n    spec = spec.roll(1, 0).mul(1 - lam).add(spec.mul(lam))\n    label = label.roll(1, 0).mul_(1.0 - lam).add_(label.mul(lam))\n\n    return eeg, spec, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:53:29.515011Z","iopub.execute_input":"2026-04-30T19:53:29.515409Z","iopub.status.idle":"2026-04-30T19:53:29.522465Z","shell.execute_reply.started":"2026-04-30T19:53:29.515377Z","shell.execute_reply":"2026-04-30T19:53:29.521373Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import subprocess\nresult = subprocess.run(['find', '/kaggle/input', '-name', '*.pth'], capture_output=True, text=True)\nprint(result.stdout)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:54:38.305434Z","iopub.execute_input":"2026-04-30T19:54:38.305802Z","iopub.status.idle":"2026-04-30T19:55:36.514547Z","shell.execute_reply.started":"2026-04-30T19:54:38.305775Z","shell.execute_reply":"2026-04-30T19:55:36.513199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nmodel = DualHeadModel(6)\n# model.load_state_dict(torch.load(\"/kaggle/input/datasets/laiclement/harmful-brain-model/best_manual_model_full.pth\", map_location=device))\nmodel.load_state_dict(torch.load(\"/kaggle/input/datasets/laiclement/patient-split/best_manual_model_full_patient.pth\", map_location=device))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:55:51.390816Z","iopub.execute_input":"2026-04-30T19:55:51.391961Z","iopub.status.idle":"2026-04-30T19:55:51.441394Z","shell.execute_reply.started":"2026-04-30T19:55:51.391917Z","shell.execute_reply":"2026-04-30T19:55:51.440368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nmodel = DualHeadModel(6)\n# model.load_state_dict(torch.load(\"/kaggle/input/datasets/laiclement/harmful-brain-model/best_manual_model_full.pth\", map_location=device))\nmodel.load_state_dict(torch.load(\"/kaggle/input/datasets/laiclement/patient-split/best_manual_model_full_patient.pth\", map_location=device))\nmodel.to(device).eval()\n\ntest_dataset = Dual(df)\ntest_loader  = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2)\n\nall_preds = []\nwith torch.no_grad():\n    for eeg, spec, _ in test_loader:\n        eeg, spec = eeg.to(device), spec.to(device)\n        probs = F.softmax(model(eeg, spec), dim=1).cpu().numpy()\n        all_preds.append(probs)\n\npreds = np.concatenate(all_preds, axis=0)\n\nsub = pd.DataFrame(preds, columns=columnLabels)\nsub.insert(0, \"eeg_id\", df[\"eeg_id\"].values)\nsub.to_csv(\"submission.csv\", index=False)\nprint(sub.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T19:56:13.070833Z","iopub.execute_input":"2026-04-30T19:56:13.071168Z","iopub.status.idle":"2026-04-30T19:56:13.445184Z","shell.execute_reply.started":"2026-04-30T19:56:13.071139Z","shell.execute_reply":"2026-04-30T19:56:13.443418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}