{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":8186978,"sourceType":"datasetVersion","datasetId":4607191}],"dockerImageVersionId":30664,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# HMS: Final Inference Notebook\n\nThis is a slightly cleaned up version of my inference notebook. If you want to see the raw score used in the Kaggle submission got to Version 27 in the version history. Version 28 is the exact same, except I removed some unnecessary comments. ","metadata":{}},{"cell_type":"code","source":"from tqdm.auto import tqdm\nimport pandas as pd\nimport polars as pl\nimport numpy as np\nimport argparse\nimport os\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader\n\nfrom typing import Optional\nfrom typing import Tuple\nfrom typing import List\nfrom typing import Dict\nfrom typing import Any","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-04-21T17:35:54.282717Z","iopub.execute_input":"2024-04-21T17:35:54.283110Z","iopub.status.idle":"2024-04-21T17:35:59.061424Z","shell.execute_reply.started":"2024-04-21T17:35:54.283080Z","shell.execute_reply":"2024-04-21T17:35:59.060042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/hms-harmful-brain-activity-classification/\"\nSPEC_DIR = os.path.join(DATA_DIR, \"test_spectrograms/\")\nEEG_DIR = os.path.join(DATA_DIR, \"test_eegs/\")\n\ndf_train = pl.read_csv(os.path.join(DATA_DIR, \"train.csv\"))\ndf_test = pl.read_csv(os.path.join(DATA_DIR, \"test.csv\"))\nsample_submission = pl.read_csv(os.path.join(DATA_DIR, \"sample_submission.csv\"))\n\ndf_test.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:35:59.064634Z","iopub.execute_input":"2024-04-21T17:35:59.065748Z","iopub.status.idle":"2024-04-21T17:35:59.291430Z","shell.execute_reply.started":"2024-04-21T17:35:59.065700Z","shell.execute_reply":"2024-04-21T17:35:59.290339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('Using device:', device)\nprint()","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:35:59.295759Z","iopub.execute_input":"2024-04-21T17:35:59.296109Z","iopub.status.idle":"2024-04-21T17:35:59.328774Z","shell.execute_reply.started":"2024-04-21T17:35:59.296075Z","shell.execute_reply":"2024-04-21T17:35:59.327555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_model(path: str, model: nn.Module) -> nn.Module:\n    model.load_state_dict(torch.load(path, map_location = torch.device(\"cpu\"))[\"model_state_dict\"])\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:35:59.332028Z","iopub.execute_input":"2024-04-21T17:35:59.332587Z","iopub.status.idle":"2024-04-21T17:35:59.340082Z","shell.execute_reply.started":"2024-04-21T17:35:59.332555Z","shell.execute_reply":"2024-04-21T17:35:59.339218Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nsys.path.append(\"/kaggle/input/hms-models/\")\n\nfrom comb_model import MultimodalModel","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:35:59.341087Z","iopub.execute_input":"2024-04-21T17:35:59.342007Z","iopub.status.idle":"2024-04-21T17:35:59.379885Z","shell.execute_reply.started":"2024-04-21T17:35:59.341973Z","shell.execute_reply":"2024-04-21T17:35:59.379042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\nimport sys\n\n# Ensemble model\nmodels_multimodal = []\nfor model_path in sorted(glob.glob(\"/kaggle/input/hms-models/final_multimodal_sanity/final_multimodal_sanity/*\")):\n    try:\n        model = MultimodalModel(None, None, None)\n        print(model_path)\n        model = load_model(model_path, model)\n        model = model.to(device)\n        model.eval()\n        models_multimodal.append(model)\n    except:\n        print(\"Corrupted:\", model_path)\n        \nfor model_path in sorted(glob.glob(\"/kaggle/input/hms-models/stage_6/stage_6/*\")):\n    try:\n        model = MultimodalModel(None, None, None)\n        print(model_path)\n        model = load_model(model_path, model)\n        model = model.to(device)\n        model.eval()\n        models_multimodal.append(model)\n    except:\n        print(\"Corrupted:\", model_path)\n\nsize = len(models_multimodal)\nsize","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:35:59.381091Z","iopub.execute_input":"2024-04-21T17:35:59.381360Z","iopub.status.idle":"2024-04-21T17:36:18.439430Z","shell.execute_reply.started":"2024-04-21T17:35:59.381338Z","shell.execute_reply":"2024-04-21T17:36:18.438439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from scipy.signal import butter\nfrom scipy.signal import filtfilt\n\ndef MAD(signal, axis = -1):\n    \"\"\"Compute the robust standard deviation (MAD) of a signal.\"\"\"\n    median = np.median(signal, axis = axis, keepdims = True)\n    absolute_deviations = np.abs(signal - median)\n    median_absolute_deviation = np.median(absolute_deviations, axis = axis, keepdims = True)\n    scale_factor = 1.4826  # This is a constant for normal distribution\n    robust_std = median_absolute_deviation * scale_factor\n    return robust_std\n\ndef butter_filter(eeg_data, fs = 200, cutoff_freq = 22, order = 4, btype = \"lowpass\"):\n    b, a = butter(\n        N = order, \n        Wn = cutoff_freq / (0.5 * fs), \n        btype = btype, \n        analog = False\n    )\n\n    return filtfilt(b, a, eeg_data)","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:18.440448Z","iopub.execute_input":"2024-04-21T17:36:18.440695Z","iopub.status.idle":"2024-04-21T17:36:19.066906Z","shell.execute_reply.started":"2024-04-21T17:36:18.440674Z","shell.execute_reply":"2024-04-21T17:36:19.065908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import scipy\nimport torchaudio\nfrom torchaudio.transforms import Spectrogram as _Spectrogram\n\nn_fft = 800\nwin_length = 256\nhop_length = 44\n\nspectrogram = _Spectrogram(\n    n_fft = n_fft, win_length = win_length, hop_length = hop_length, power = None\n)\n\n@torch.no_grad()\ndef compute_spec(chain):\n    chain = torch.Tensor(chain)\n    #chain = chain[:, :, :10000]\n    chain = spectrogram(chain)\n    chain = chain[:, :, 2:98]\n    chain = torch.abs(chain) / 15\n    chain = torch.log(chain.clip(math.exp(-4), math.exp(7)))\n    chain = chain.mean(axis = 1)\n    return chain.numpy()\n\ndef compute_spec_eeg(a, b) -> np.ndarray:\n    return butter_filter(a - b, cutoff_freq = np.array([0.25, 40.0]), order = 5, btype = \"bandpass\")\n\ndef compute_spec_chain(df_eeg: pl.DataFrame) -> np.ndarray:\n    Fp1 = df_eeg[\"Fp1\"].to_numpy() \n    Fp2 = df_eeg[\"Fp2\"].to_numpy()\n    Fz  = df_eeg[\"Fz\"].to_numpy()\n    Cz  = df_eeg[\"Cz\"].to_numpy()\n    Pz  = df_eeg[\"Pz\"].to_numpy()\n    F3  = df_eeg[\"F3\"].to_numpy()\n    F4  = df_eeg[\"F4\"].to_numpy()\n    F7  = df_eeg[\"F7\"].to_numpy()\n    F8  = df_eeg[\"F8\"].to_numpy()\n    C3  = df_eeg[\"C3\"].to_numpy()\n    C4  = df_eeg[\"C4\"].to_numpy()\n    P3  = df_eeg[\"P3\"].to_numpy()\n    P4  = df_eeg[\"P4\"].to_numpy()\n    T3  = df_eeg[\"T3\"].to_numpy()\n    T4  = df_eeg[\"T4\"].to_numpy()\n    T5  = df_eeg[\"T5\"].to_numpy()\n    T6  = df_eeg[\"T6\"].to_numpy()\n    O1  = df_eeg[\"O1\"].to_numpy()\n    O2  = df_eeg[\"O2\"].to_numpy()\n    \n    ll = np.stack([(compute_spec_eeg(Fp1 , F7), compute_spec_eeg(F7 , T3), compute_spec_eeg(T3 , T5), compute_spec_eeg(T5 , O1))])\n    lp = np.stack([(compute_spec_eeg(Fp1 , F3), compute_spec_eeg(F3 , C3), compute_spec_eeg(C3 , P3), compute_spec_eeg(P3 , O1))])\n    rp = np.stack([(compute_spec_eeg(Fp2 , F4), compute_spec_eeg(F4 , C4), compute_spec_eeg(C4 , P4), compute_spec_eeg(P4 , O2))])\n    rl = np.stack([(compute_spec_eeg(Fp2 , F8), compute_spec_eeg(F8 , T4), compute_spec_eeg(T4 , T6), compute_spec_eeg(T6 , O2))])\n    chain = np.stack([ll, lp, rp, rl])[:, 0]\n    \n    mads = MAD(chain, axis = -1)\n    mads = np.median(mads.reshape(-1))\n    chain = chain / (mads + 1e-5)\n    \n    chain = compute_spec(chain)\n    \n    return chain\n\n\ndef compute_spec_from_file(filepath: str) -> np.ndarray:\n    df_eeg = pl.read_parquet(filepath).fill_null(0)\n    chain = compute_spec_chain(df_eeg)\n    return chain","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.068448Z","iopub.execute_input":"2024-04-21T17:36:19.068867Z","iopub.status.idle":"2024-04-21T17:36:19.635488Z","shell.execute_reply.started":"2024-04-21T17:36:19.068839Z","shell.execute_reply":"2024-04-21T17:36:19.634721Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process_spec(spec: np.ndarray) -> np.ndarray:\n    spec = spec[:, 1:]\n    spec = np.stack([\n        spec[:,   0:100].T,\n        spec[:, 100:200].T,\n        spec[:, 200:300].T,\n        spec[:, 300:400].T,\n    ])\n    \n    return spec\n\ndef compute_kaggle_spec_from_file(filepath: str) -> np.ndarray:\n    spec = pl.read_parquet(filepath).to_numpy().astype(np.float32)\n    spec = process_spec(spec)\n    return spec","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.636507Z","iopub.execute_input":"2024-04-21T17:36:19.636784Z","iopub.status.idle":"2024-04-21T17:36:19.643039Z","shell.execute_reply.started":"2024-04-21T17:36:19.636760Z","shell.execute_reply":"2024-04-21T17:36:19.642107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport math\n\nfrom typing import Union\nfrom typing import Tuple\nfrom typing import List\n\n\ndef bin_array(\n    array, \n    bin_size, \n    axis = -1, \n    pad_dir = \"symmetric\", \n    mode = \"edge\", \n    return_padding = False,\n    **padding_kwargs\n) -> Union[np.ndarray, Tuple[np.ndarray, List]]:\n    \"\"\"Given an array and bin size, bins the array along an arbitrary axis into\n    bins of size `bin_size`. It will perform padding if the array does not split\n    up into equal bin sizes. \n\n    Args:\n        array (np.ndarray): The input array.\n        bin_size (int): The size of each bin.\n        axis (int): The axis to bin the array along. Default is -1.\n        pad_dir (str): The padding direction. One of `left`, `right`, or\n            `symmetric` (default).\n        return_padding (bool): Option to return the padding width used. \n        mode (str): The padding mode. See the NumPy documentation for options. \n\n    Returns:\n        np.ndarray: The binned array where the number of bins is first. That is,\n            the shape will be `(..., n_bins, bin_size, ...)`.\n\n    \"\"\"\n    if axis == -1:\n        axis = array.ndim - 1\n\n    curr_len = array.shape[axis]\n    n_bins = math.ceil(curr_len / bin_size)\n    new_len = n_bins * bin_size\n\n    new_shape = list(array.shape)\n    new_shape[axis] = n_bins\n    new_shape.insert(axis + 1, bin_size)\n\n    # Perform padding if curr_len does not equal new_len.\n    padding = [(0, 0)] * array.ndim\n    if curr_len != new_len:\n        if pad_dir == \"left\":\n            pad_l = new_len - curr_len\n            pad_r = 0\n        elif pad_dir == \"right\":\n            pad_l = 0\n            pad_r = new_len - curr_len\n        else:\n            pad_l = (new_len - curr_len) // 2\n            pad_r = (new_len - curr_len) - pad_l\n\n        padding[axis] = (pad_l, pad_r)\n        array = np.pad(array, padding, mode = mode, **padding_kwargs)\n\n    array = array.reshape(new_shape)\n\n    if return_padding:\n        return array, padding\n\n    return array","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.644175Z","iopub.execute_input":"2024-04-21T17:36:19.644463Z","iopub.status.idle":"2024-04-21T17:36:19.655420Z","shell.execute_reply.started":"2024-04-21T17:36:19.644441Z","shell.execute_reply":"2024-04-21T17:36:19.654577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_eeg(eeg: np.ndarray) -> np.ndarray:\n    eeg = butter_filter(eeg, cutoff_freq = np.array([0.25, 50]), btype = \"bandpass\")\n    eeg = bin_array(eeg, bin_size = 4, mode = \"reflect\").mean(axis = -1)\n    return eeg\n    \ndef compute_eeg_chain(df_eeg: pl.DataFrame) -> np.ndarray:\n    Fp1 = df_eeg[\"Fp1\"].to_numpy()\n    Fp2 = df_eeg[\"Fp2\"].to_numpy()\n    Fz  = df_eeg[\"Fz\"].to_numpy()\n    Cz  = df_eeg[\"Cz\"].to_numpy()\n    Pz  = df_eeg[\"Pz\"].to_numpy()\n    F3  = df_eeg[\"F3\"].to_numpy()\n    F4  = df_eeg[\"F4\"].to_numpy()\n    F7  = df_eeg[\"F7\"].to_numpy()\n    F8  = df_eeg[\"F8\"].to_numpy()\n    C3  = df_eeg[\"C3\"].to_numpy()\n    C4  = df_eeg[\"C4\"].to_numpy()\n    P3  = df_eeg[\"P3\"].to_numpy()\n    P4  = df_eeg[\"P4\"].to_numpy()\n    T3  = df_eeg[\"T3\"].to_numpy()\n    T4  = df_eeg[\"T4\"].to_numpy()\n    T5  = df_eeg[\"T5\"].to_numpy()\n    T6  = df_eeg[\"T6\"].to_numpy()\n    O1  = df_eeg[\"O1\"].to_numpy()\n    O2  = df_eeg[\"O2\"].to_numpy()\n    \n    ekg = df_eeg[\"EKG\"].to_numpy()\n    ekg = butter_filter(ekg, cutoff_freq = np.array([0.50, 20.0]), btype = \"bandpass\")\n    ekg = bin_array(ekg, bin_size = 4, mode = \"reflect\").mean(axis = -1)\n    ekg = ekg.reshape(1, -1)\n    \n    ll = np.stack([(compute_eeg(Fp1 - F7), compute_eeg(F7 - T3), compute_eeg(T3 - T5), compute_eeg(T5 - O1))])\n    lp = np.stack([(compute_eeg(Fp1 - F3), compute_eeg(F3 - C3), compute_eeg(C3 - P3), compute_eeg(P3 - O1))])\n    rp = np.stack([(compute_eeg(Fp2 - F4), compute_eeg(F4 - C4), compute_eeg(C4 - P4), compute_eeg(P4 - O2))])\n    rl = np.stack([(compute_eeg(Fp2 - F8), compute_eeg(F8 - T4), compute_eeg(T4 - T6), compute_eeg(T6 - O2))])\n    mid = np.stack([compute_eeg(Fz - Cz), compute_eeg(Cz - Pz)])\n    \n    chain = np.stack([ll, lp, rp, rl])[:, 0]\n    \n    return chain, mid, ekg\n\ndef compute_eeg_from_file(filepath: str) -> np.ndarray:\n    df_eeg = pl.read_parquet(filepath).fill_null(0)\n    return compute_eeg_chain(df_eeg)","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.656635Z","iopub.execute_input":"2024-04-21T17:36:19.656920Z","iopub.status.idle":"2024-04-21T17:36:19.672004Z","shell.execute_reply.started":"2024-04-21T17:36:19.656897Z","shell.execute_reply":"2024-04-21T17:36:19.670994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LABELS = [\n    \"seizure_vote\", \n    \"lpd_vote\", \n    \"gpd_vote\", \n    \"lrda_vote\", \n    \"grda_vote\", \n    \"other_vote\"\n]","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.673124Z","iopub.execute_input":"2024-04-21T17:36:19.673405Z","iopub.status.idle":"2024-04-21T17:36:19.684751Z","shell.execute_reply.started":"2024-04-21T17:36:19.673382Z","shell.execute_reply":"2024-04-21T17:36:19.683991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Option for better printing.\nnp.set_printoptions(formatter = {\"all\": lambda x: f\"{x:0.3f}\"})\n\nimport albumentations as A\nimport cv2\n\nspec_transforms = A.Compose([\n    A.Resize(\n        height = 96,\n        width = 224,\n        interpolation = cv2.INTER_CUBIC, \n        always_apply = True, \n        p = 1.0\n    ),\n])\n\ndef proc_kspec(x):\n    x = x.copy()\n        \n    x = x[:, 2:98]\n    x[np.isnan(x) | np.isinf(x)] = 0\n    x = x.clip(np.exp(-4), np.exp(7))\n    x = np.log(x)\n    \n    x = x - x.mean(axis = (1, 2), keepdims = True)\n    x = x / (x.std(axis = (1, 2), keepdims = True) + 1e-5)\n    \n    x = x.transpose(1, 2, 0)\n    x = spec_transforms(image = x)[\"image\"]\n    x = x.transpose(2, 0, 1)\n    \n    x = x.reshape(4, 96, 224)\n    \n    return x\n\n\ndef proc_eeg_spec(x):\n    x = x.copy()\n        \n    x = x[:, :, 2:-2]\n    x[np.isnan(x) | np.isinf(x)] = 0\n    x = x + 1\n    x = x.reshape(4, 96, 224)\n    \n    return x\n\ndef proc_eeg(eeg, mid, ekg):\n    eeg = eeg.copy()\n    mid = mid.copy()\n    ekg = ekg.copy()\n\n    eeg[np.isnan(eeg) | np.isinf(eeg)] = 0\n    ekg[np.isnan(ekg) | np.isinf(ekg)] = 0\n    mid[np.isnan(mid) | np.isinf(mid)] = 0\n\n    eeg = eeg - eeg.mean(axis = -1, keepdims = True)\n    mid = mid - mid.mean(axis = -1, keepdims = True)\n\n    # Robust estimate of standard deviation.\n    mad_std = MAD(eeg, axis = -1).reshape(-1)\n    mad_std = np.median(mad_std) + 1e-5\n\n    eeg = eeg / mad_std\n    eeg = eeg.clip(-10, 10)\n\n    mid = mid / mad_std\n    mid = mid.clip(-10, 10)\n\n    ekg = ekg / (MAD(ekg, axis = -1).reshape(-1) + 1e-5)\n\n    eeg = eeg.reshape(16, -1)\n    eeg = np.concatenate([eeg, mid, ekg], axis = 0)\n\n    eeg = eeg.reshape(19, 2_500)\n\n    return eeg\n\ndef flip_h(eeg):\n    eeg = eeg.copy()\n    eeg = eeg[::-1]\n    return eeg.copy()\n\ndef flip_v(eeg):\n    eeg = eeg.copy()\n    eeg = eeg[:, ::-1]\n    return eeg.copy()\n\n@torch.no_grad()\ndef gen_ensemble_pred(df_row: pl.DataFrame) -> np.ndarray:\n    eeg_id = df_row[\"eeg_id\"].item()\n    spc_id = df_row[\"spectrogram_id\"].item()\n    eeg_filepath = os.path.join(EEG_DIR, f\"{eeg_id}.parquet\")\n    spc_filepath = os.path.join(SPEC_DIR, f\"{spc_id}.parquet\")\n\n    preds = []\n    \n    kspec = compute_kaggle_spec_from_file(spc_filepath)\n    kspec = proc_kspec(kspec)\n    kspec = torch.Tensor(kspec).to(device).unsqueeze(0)\n    \n    eeg_spec = compute_spec_from_file(eeg_filepath)\n    eeg_spec = proc_eeg_spec(eeg_spec)\n    eeg_spec = torch.Tensor(eeg_spec).to(device).unsqueeze(0)\n    \n    eeg, mid, ekg = compute_eeg_from_file(eeg_filepath)\n    eeg[np.isnan(eeg) | np.isinf(eeg)] = 0\n    eeg = proc_eeg(eeg, mid, ekg)\n    eeg = torch.Tensor(eeg).to(device).unsqueeze(0)\n\n    for model in models_multimodal:\n        model.eval()\n        preds.append(model(eeg, eeg_spec, kspec).exp().cpu().detach().numpy().reshape(-1))\n        \n    for pred in preds:\n        print(pred)\n        \n    preds = np.mean(preds, axis = 0)\n    preds = preds / preds.sum()\n    return preds","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:19.687703Z","iopub.execute_input":"2024-04-21T17:36:19.687982Z","iopub.status.idle":"2024-04-21T17:36:20.194502Z","shell.execute_reply.started":"2024-04-21T17:36:19.687959Z","shell.execute_reply":"2024-04-21T17:36:20.193703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm.auto import tqdm\n    \npreds_final = []\nfor i in range(len(df_test)):\n    pred = gen_ensemble_pred(df_test[i])\n    preds_final.append(pred)","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:20.195688Z","iopub.execute_input":"2024-04-21T17:36:20.196211Z","iopub.status.idle":"2024-04-21T17:36:21.964262Z","shell.execute_reply.started":"2024-04-21T17:36:20.196172Z","shell.execute_reply":"2024-04-21T17:36:21.963335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = pd.DataFrame({\"eeg_id\": df_test[\"eeg_id\"].to_list()})\ndf_sub[LABELS] = preds_final\ndf_sub.to_csv(\"submission.csv\", index = False)\ndf_sub","metadata":{"execution":{"iopub.status.busy":"2024-04-21T17:36:21.965520Z","iopub.execute_input":"2024-04-21T17:36:21.965896Z","iopub.status.idle":"2024-04-21T17:36:21.992681Z","shell.execute_reply.started":"2024-04-21T17:36:21.965862Z","shell.execute_reply":"2024-04-21T17:36:21.991839Z"},"trusted":true},"execution_count":null,"outputs":[]}]}