{"metadata":{"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":7515232,"sourceType":"datasetVersion","datasetId":4377463}],"dockerImageVersionId":30636,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"},"papermill":{"default_parameters":{},"duration":270.012179,"end_time":"2024-01-14T22:56:02.916427","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-01-14T22:51:32.904248","version":"2.4.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> HMS: <span style='color:#F1A424'>Harmful Brain Activity Classification</span><span style='color:#ABABAB'> [Inference]</span></b> \n\n***\n\n- [Train Notebook](https://www.kaggle.com/alejopaullier/hms-efficientnetb0-pytorch-train)\n\n\nYour goal in this competition is to detect and classify seizures and other types of harmful brain activity. You will develop a model trained on electroencephalography (EEG) signals recorded from critically ill hospital patients.\n\nIn this notebook you will learn how to infer with an `efficientnet` model for image classification using PyTorch. Hope you enjoy it and find it useful.\n\n### <b><span style='color:#F1A424'>Table of Contents</span></b> <a class='anchor' id='top'></a>\n<div style=\" background-color:#3b3745; padding: 13px 13px; border-radius: 8px; color: white\">\n<li><a href=\"#import_libraries\">Import Libraries</a></li>\n<li><a href=\"#configuration\">Configuration</a></li>\n<li><a href=\"#utils\">Utils</a></li>\n<li><a href=\"#load_data\">Load Data</a></li>\n<li><a href=\"#dataset\">Dataset</a></li>\n<li><a href=\"#dataloader\">DataLoader</a></li>\n<li><a href=\"#model\">Model</a></li>\n<li><a href=\"#inference_function\">Inference Function</a></li>\n<li><a href=\"#infer\">Infer</a></li>\n<li><a href=\"#submission\">Save Submission</a></li>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Import Libraries</b><a class='anchor' id='import_libraries'></a> [↑](#top) \n\n***\n\nImport all the required libraries for this notebook.","metadata":{}},{"cell_type":"code","source":"import albumentations as A\nimport gc\nimport librosa\nimport matplotlib.pyplot as plt\nimport math\nimport multiprocessing\nimport numpy as np\nimport os\nimport pandas as pd\nimport pywt\nimport random\nimport time\nimport timm\nimport torch\nimport torch.nn as nn\n\n\nfrom albumentations.pytorch import ToTensorV2\nfrom glob import glob\nfrom torch.utils.data import DataLoader, Dataset\nfrom tqdm import tqdm\nfrom typing import Dict, List\n\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0,1\"\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint('Using', torch.cuda.device_count(), 'GPU(s)')","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:07.647224Z","iopub.execute_input":"2024-01-30T19:22:07.648147Z","iopub.status.idle":"2024-01-30T19:22:14.185313Z","shell.execute_reply.started":"2024-01-30T19:22:07.648109Z","shell.execute_reply":"2024-01-30T19:22:14.184161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Configuration</b><a class='anchor' id='configuration'></a> [↑](#top) \n\n***","metadata":{}},{"cell_type":"code","source":"class config:\n    BATCH_SIZE = 64\n    MODEL = \"tf_efficientnet_b0\"\n    NUM_WORKERS = 0 # multiprocessing.cpu_count()\n    PRINT_FREQ = 20\n    SEED = 20\n    VISUALIZE = False\n    \n    \nclass paths:\n    MODEL_WEIGHTS = \"/kaggle/input/hms-multi-class-image-classification-train/tf_efficientnet_b0_epoch_3.pth\"\n    OUTPUT_DIR = \"/kaggle/working/\"\n    TEST_CSV = \"/kaggle/input/hms-harmful-brain-activity-classification/test.csv\"\n    TEST_EEGS= \"/kaggle/input/hms-harmful-brain-activity-classification/test_eegs/\"\n    TEST_SPECTROGRAMS = \"/kaggle/input/hms-harmful-brain-activity-classification/test_spectrograms/\"\n    \nmodel_weights = [x for x in glob(\"/kaggle/input/hms-efficientnetb0-5-folds/*.pth\")]\nmodel_weights","metadata":{"papermill":{"duration":0.016556,"end_time":"2024-01-14T22:51:51.671783","exception":false,"start_time":"2024-01-14T22:51:51.655227","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-30T19:22:14.187671Z","iopub.execute_input":"2024-01-30T19:22:14.188174Z","iopub.status.idle":"2024-01-30T19:22:14.209922Z","shell.execute_reply.started":"2024-01-30T19:22:14.188128Z","shell.execute_reply":"2024-01-30T19:22:14.208842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Utils</b><a class='anchor' id='utils'></a> [↑](#top) \n\n***\n\nUtility functions:\n- [pywt.wavedec][1]\n\n[1]: https://pywavelets.readthedocs.io/en/latest/ref/dwt-discrete-wavelet-transform.html#multilevel-decomposition-using-wavedec","metadata":{}},{"cell_type":"code","source":"USE_WAVELET = None \n\nNAMES = ['LL','LP','RP','RR']\n\nFEATS = [['Fp1','F7','T3','T5','O1'],\n         ['Fp1','F3','C3','P3','O1'],\n         ['Fp2','F8','T4','T6','O2'],\n         ['Fp2','F4','C4','P4','O2']]\n\ndef maddest(d, axis: int = None):\n    \"\"\"\n    Denoise function.\n    \"\"\"\n    return np.mean(np.absolute(d - np.mean(d, axis)), axis)\n\ndef denoise(x: np.ndarray, wavelet: str = 'haar', level: int = 1): \n    coeff = pywt.wavedec(x, wavelet, mode=\"per\") # multilevel 1D Discrete Wavelet Transform of data.\n    sigma = (1/0.6745) * maddest(coeff[-level])\n    uthresh = sigma * np.sqrt(2*np.log(len(x)))\n    coeff[1:] = (pywt.threshold(i, value=uthresh, mode='hard') for i in coeff[1:])\n    output = pywt.waverec(coeff, wavelet, mode='per')\n    return output\n\ndef spectrogram_from_eeg(parquet_path, display=False):\n    # LOAD MIDDLE 50 SECONDS OF EEG SERIES\n    eeg = pd.read_parquet(parquet_path)\n    middle = (len(eeg)-10_000)//2\n    eeg = eeg.iloc[middle:middle+10_000]\n    \n    # VARIABLE TO HOLD SPECTROGRAM\n    img = np.zeros((128,256,4),dtype='float32')\n    \n    if display:\n        plt.figure(figsize=(10,7))\n    signals = []\n    for k in range(4):\n        COLS = FEATS[k]\n        \n        for kk in range(4):\n        \n            # COMPUTE PAIR DIFFERENCES\n            x = eeg[COLS[kk]].values - eeg[COLS[kk+1]].values\n\n            # FILL NANS\n            m = np.nanmean(x)\n            if np.isnan(x).mean()<1: x = np.nan_to_num(x,nan=m)\n            else: x[:] = 0\n\n            # DENOISE\n            if USE_WAVELET:\n                x = denoise(x, wavelet=USE_WAVELET)\n            signals.append(x)\n\n            # RAW SPECTROGRAM\n            mel_spec = librosa.feature.melspectrogram(y=x, sr=200, hop_length=len(x)//256, \n                  n_fft=1024, n_mels=128, fmin=0, fmax=20, win_length=128)\n\n            # LOG TRANSFORM\n            width = (mel_spec.shape[1]//32)*32\n            mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max).astype(np.float32)[:,:width]\n\n            # STANDARDIZE TO -1 TO 1\n            mel_spec_db = (mel_spec_db+40)/40 \n            img[:,:,k] += mel_spec_db\n                \n        # AVERAGE THE 4 MONTAGE DIFFERENCES\n        img[:,:,k] /= 4.0\n        \n        if display:\n            plt.subplot(2,2,k+1)\n            plt.imshow(img[:,:,k],aspect='auto',origin='lower')\n            plt.title(f'EEG {eeg_id} - Spectrogram {NAMES[k]}')\n            \n    if display: \n        plt.show()\n        plt.figure(figsize=(10,5))\n        offset = 0\n        for k in range(4):\n            if k>0: offset -= signals[3-k].min()\n            plt.plot(range(10_000),signals[k]+offset,label=NAMES[3-k])\n            offset += signals[3-k].max()\n        plt.legend()\n        plt.title(f'EEG {eeg_id} Signals')\n        plt.show()\n        print(); print('#'*25); print()\n        \n    return img\n\ndef plot_spectrogram(spectrogram_path: str):\n    \"\"\"\n    Source: https://www.kaggle.com/code/mvvppp/hms-eda-and-domain-journey\n    Visualize spectrogram recordings from a parquet file.\n    :param spectrogram_path: path to the spectrogram parquet.\n    \"\"\"\n    sample_spect = pd.read_parquet(spectrogram_path)\n    \n    split_spect = {\n        \"LL\": sample_spect.filter(regex='^LL', axis=1),\n        \"RL\": sample_spect.filter(regex='^RL', axis=1),\n        \"RP\": sample_spect.filter(regex='^RP', axis=1),\n        \"LP\": sample_spect.filter(regex='^LP', axis=1),\n    }\n    \n    fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(15, 12))\n    axes = axes.flatten()\n    label_interval = 5\n    for i, split_name in enumerate(split_spect.keys()):\n        ax = axes[i]\n        img = ax.imshow(np.log(split_spect[split_name]).T, cmap='viridis', aspect='auto', origin='lower')\n        cbar = fig.colorbar(img, ax=ax)\n        cbar.set_label('Log(Value)')\n        ax.set_title(split_name)\n        ax.set_ylabel(\"Frequency (Hz)\")\n        ax.set_xlabel(\"Time\")\n\n        ax.set_yticks(np.arange(len(split_spect[split_name].columns)))\n        ax.set_yticklabels([column_name[3:] for column_name in split_spect[split_name].columns])\n        frequencies = [column_name[3:] for column_name in split_spect[split_name].columns]\n        ax.set_yticks(np.arange(0, len(split_spect[split_name].columns), label_interval))\n        ax.set_yticklabels(frequencies[::label_interval])\n    plt.tight_layout()\n    plt.show()\n\n    \ndef seed_everything(seed: int):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed) \n\n    \ndef sep():\n    print(\"-\"*100)\n    \nlabel_to_num = {'Seizure': 0, 'LPD': 1, 'GPD': 2, 'LRDA': 3, 'GRDA': 4, 'Other':5}\nnum_to_label = {v: k for k, v in label_to_num.items()}\nseed_everything(config.SEED)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-01-30T19:22:14.211639Z","iopub.execute_input":"2024-01-30T19:22:14.212037Z","iopub.status.idle":"2024-01-30T19:22:14.248030Z","shell.execute_reply.started":"2024-01-30T19:22:14.212000Z","shell.execute_reply":"2024-01-30T19:22:14.246947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Load Data</b><a class='anchor' id='load_data'></a> [↑](#top) \n\n***\n\nLoad the competition's data.","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv(paths.TEST_CSV)\nprint(f\"Test dataframe shape is: {test_df.shape}\")\ntest_df.head()","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":0.288611,"end_time":"2024-01-14T22:51:51.984993","exception":false,"start_time":"2024-01-14T22:51:51.696382","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-30T19:22:14.250762Z","iopub.execute_input":"2024-01-30T19:22:14.251112Z","iopub.status.idle":"2024-01-30T19:22:14.281073Z","shell.execute_reply.started":"2024-01-30T19:22:14.251071Z","shell.execute_reply":"2024-01-30T19:22:14.279948Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <b><span style='color:#F1A424'>Read Spectrograms</span></b>","metadata":{"papermill":{"duration":0.00881,"end_time":"2024-01-14T22:51:52.142747","exception":false,"start_time":"2024-01-14T22:51:52.133937","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%time\n\npaths_spectrograms = glob(paths.TEST_SPECTROGRAMS + \"*.parquet\")\nprint(f'There are {len(paths_spectrograms)} spectrogram parquets')\nall_spectrograms = {}\n\nfor file_path in tqdm(paths_spectrograms):\n    aux = pd.read_parquet(file_path)\n    name = int(file_path.split(\"/\")[-1].split('.')[0])\n    all_spectrograms[name] = aux.iloc[:,1:].values\n    del aux\n    \nif config.VISUALIZE:\n    idx = np.random.randint(0, len(paths_spectrograms))\n    spectrogram_path = paths_spectrograms[idx]\n    plot_spectrogram(spectrogram_path)","metadata":{"papermill":{"duration":55.16894,"end_time":"2024-01-14T22:52:47.320438","exception":false,"start_time":"2024-01-14T22:51:52.151498","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-30T19:22:14.282347Z","iopub.execute_input":"2024-01-30T19:22:14.282650Z","iopub.status.idle":"2024-01-30T19:22:14.520549Z","shell.execute_reply.started":"2024-01-30T19:22:14.282624Z","shell.execute_reply":"2024-01-30T19:22:14.519386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <b><span style='color:#F1A424'>Read EEG Spectrograms</span></b>\n\nThe resulting `all_eegs` dictionary contains `eeg_id` as keys (`int` keys) and the values are the eeg sequences (as 3-dimensional `np.array`) of shape `(128, 256, 4)`.\n\n","metadata":{}},{"cell_type":"code","source":"%%time\n\npaths_eegs = glob(paths.TEST_EEGS + \"*.parquet\")\nprint(f'There are {len(paths_eegs)} EEG spectrograms')\nall_eegs = {}\ncounter = 0\n\nfor file_path in tqdm(paths_eegs):\n    eeg_id = file_path.split(\"/\")[-1].split(\".\")[0]\n    eeg_spectrogram = spectrogram_from_eeg(file_path, counter < 1)\n    all_eegs[int(eeg_id)] = eeg_spectrogram\n    counter += 1","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:14.521834Z","iopub.execute_input":"2024-01-30T19:22:14.522187Z","iopub.status.idle":"2024-01-30T19:22:26.728965Z","shell.execute_reply.started":"2024-01-30T19:22:14.522157Z","shell.execute_reply":"2024-01-30T19:22:26.727920Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Dataset</b><a class='anchor' id='dataset'></a> [↑](#top) \n\n***\n\nCreate a custom `Dataset` to load data.\n\nThis dataloader outputs 4 spectrogram images as a 4 channel image of size 128x256x4 per train sample. This notebook version is not using data augmention but the code is available below to experiment with `albumentations` data augmention. Just add `augment = True` when creating the train data loader. And consider adding new transformations to the augment function below.\n\nA more detailed [explanation][1] of the `r` parameter inside the `__data_generation()` method.\n\nOur dataloader outputs both Kaggle spectrograms and EEG spectrogams as 8 channel image of size `(128, 256, 8)`\n\n[1]: https://www.kaggle.com/code/cdeotte/efficientnetb0-starter-lb-0-43/comments#2617811","metadata":{}},{"cell_type":"code","source":"class CustomDataset(Dataset):\n    def __init__(\n        self, df: pd.DataFrame, config,\n        augment: bool = False, mode: str = 'train',\n        specs: Dict[int, np.ndarray] = all_spectrograms,\n        eeg_specs: Dict[int, np.ndarray] = all_eegs\n    ): \n        self.df = df\n        self.config = config\n        self.batch_size = self.config.BATCH_SIZE\n        self.augment = augment\n        self.mode = mode\n        self.spectrograms = all_spectrograms\n        self.eeg_spectrograms = all_eegs\n        \n    def __len__(self):\n        \"\"\"\n        Denotes the number of batches per epoch.\n        \"\"\"\n        return len(self.df)\n        \n    def __getitem__(self, index):\n        \"\"\"\n        Generate one batch of data.\n        \"\"\"\n        X, y = self.__data_generation(index)\n        if self.augment:\n            X = self.__transform(X)\n        return torch.tensor(X, dtype=torch.float32), torch.tensor(y, dtype=torch.float32)\n                        \n    def __data_generation(self, index):\n        \"\"\"\n        Generates data containing batch_size samples.\n        \"\"\"\n        X = np.zeros((128, 256, 8), dtype='float32')\n        y = np.zeros(6, dtype='float32')\n        img = np.ones((128,256), dtype='float32')\n        row = self.df.iloc[index]\n        if self.mode=='test': \n            r = 0\n        else: \n            r = int((row['min'] + row['max']) // 4)\n            \n        for region in range(4):\n            img = self.spectrograms[row.spectrogram_id][r:r+300, region*100:(region+1)*100].T\n            \n            # Log transform spectrogram\n            img = np.clip(img, np.exp(-4), np.exp(8))\n            img = np.log(img)\n\n            # Standarize per image\n            ep = 1e-6\n            mu = np.nanmean(img.flatten())\n            std = np.nanstd(img.flatten())\n            img = (img-mu)/(std+ep)\n            img = np.nan_to_num(img, nan=0.0)\n            X[14:-14, :, region] = img[:, 22:-22] / 2.0\n            img = self.eeg_spectrograms[row.eeg_id]\n            X[:, :, 4:] = img\n                \n            if self.mode != 'test':\n                y = row[label_cols].values.astype(np.float32)\n            \n        return X, y\n    \n    def __transform(self, img):\n        transforms = A.Compose([\n            A.HorizontalFlip(p=0.5),\n        ])\n        return transforms(image=img)['image']","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:26.730555Z","iopub.execute_input":"2024-01-30T19:22:26.731258Z","iopub.status.idle":"2024-01-30T19:22:26.749865Z","shell.execute_reply.started":"2024-01-30T19:22:26.731217Z","shell.execute_reply":"2024-01-30T19:22:26.748630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> DataLoader</b><a class='anchor' id='dataloader'></a> [↑](#top) \n\n***\n\nBelow we display example dataloader spectrogram images.","metadata":{}},{"cell_type":"code","source":"test_dataset = CustomDataset(test_df, config, mode=\"test\")\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=config.BATCH_SIZE,\n    shuffle=False,\n    num_workers=config.NUM_WORKERS, pin_memory=True, drop_last=False\n)\nX, y = test_dataset[0]\nprint(f\"X shape: {X.shape}\")\nprint(f\"y shape: {y.shape}\")","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:26.751540Z","iopub.execute_input":"2024-01-30T19:22:26.751978Z","iopub.status.idle":"2024-01-30T19:22:26.796081Z","shell.execute_reply.started":"2024-01-30T19:22:26.751941Z","shell.execute_reply":"2024-01-30T19:22:26.794986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Model</b><a class='anchor' id='model'></a> [↑](#top) \n\n***\n\nWe will be using the [timm](https://github.com/huggingface/pytorch-image-models) library for our models.\n\nOur models receives both Kaggle spectrograms and EEG spectrograms from our data loader. We then reshape these 8 spectrograms into 1 large flat image and feed it into EfficientNet.","metadata":{}},{"cell_type":"code","source":"class CustomModel(nn.Module):\n    def __init__(self, config, num_classes: int = 6):\n        super(CustomModel, self).__init__()\n        self.USE_KAGGLE_SPECTROGRAMS = True\n        self.USE_EEG_SPECTROGRAMS = True\n        self.model = timm.create_model(\n            config.MODEL,\n            pretrained=False\n        )\n        self.features = nn.Sequential(*list(self.model.children())[:-2])\n        self.custom_layers = nn.Sequential(\n            nn.AdaptiveAvgPool2d(1),\n            nn.Flatten(),\n            nn.Linear(self.model.num_features, num_classes)\n        )\n\n    def __reshape_input(self, x):\n        \"\"\"\n        Reshapes input (128, 256, 8) -> (512, 512, 3) monotone image.\n        \"\"\" \n        # === Get spectrograms ===\n        spectrograms = [x[:, :, :, i:i+1] for i in range(4)]\n        spectrograms = torch.cat(spectrograms, dim=1)\n        \n        # === Get EEG spectrograms ===\n        eegs = [x[:, :, :, i:i+1] for i in range(4,8)]\n        eegs = torch.cat(eegs, dim=1)\n        \n        # === Reshape (512,512,3) ===\n        if self.USE_KAGGLE_SPECTROGRAMS & self.USE_EEG_SPECTROGRAMS:\n            x = torch.cat([spectrograms, eegs], dim=2)\n        elif self.USE_EEG_SPECTROGRAMS:\n            x = eegs\n        else:\n            x = spectrograms\n            \n        x = torch.cat([x,x,x], dim=3)\n        x = x.permute(0, 3, 1, 2)\n        return x\n    \n    def forward(self, x):\n        x = self.__reshape_input(x)\n        x = self.features(x)\n        x = self.custom_layers(x)\n        return x","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-01-30T19:22:26.797329Z","iopub.execute_input":"2024-01-30T19:22:26.797665Z","iopub.status.idle":"2024-01-30T19:22:26.810415Z","shell.execute_reply.started":"2024-01-30T19:22:26.797637Z","shell.execute_reply":"2024-01-30T19:22:26.809224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Inference Function</b><a class='anchor' id='inference_function'></a> [↑](#top) \n\n***","metadata":{"papermill":{"duration":0.033717,"end_time":"2024-01-14T22:53:06.742557","exception":false,"start_time":"2024-01-14T22:53:06.70884","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def inference_function(test_loader, model, device):\n    model.eval()\n    softmax = nn.Softmax(dim=1)\n    prediction_dict = {}\n    preds = []\n    with tqdm(test_loader, unit=\"test_batch\", desc='Inference') as tqdm_test_loader:\n        for step, (X, y) in enumerate(tqdm_test_loader):\n            X = X.to(device)\n            y = y.to(device)\n            batch_size = y.size(0)\n            with torch.no_grad():\n                y_preds = model(X)\n            y_preds = softmax(y_preds)\n            preds.append(y_preds.to('cpu').numpy()) \n                \n    prediction_dict[\"predictions\"] = np.concatenate(preds) \n    return prediction_dict","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:26.813469Z","iopub.execute_input":"2024-01-30T19:22:26.813865Z","iopub.status.idle":"2024-01-30T19:22:26.822845Z","shell.execute_reply.started":"2024-01-30T19:22:26.813824Z","shell.execute_reply":"2024-01-30T19:22:26.821778Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Infer</b><a class='anchor' id='infer'></a> [↑](#top) \n\n***","metadata":{}},{"cell_type":"code","source":"predictions = []\n\nfor model_weight in model_weights:\n    test_dataset = CustomDataset(test_df, config, mode=\"test\", augment=False)\n    train_loader = DataLoader(\n        test_dataset,\n        batch_size=config.BATCH_SIZE,\n        shuffle=False,\n        num_workers=config.NUM_WORKERS,\n        pin_memory=True, drop_last=False\n    )\n    model = CustomModel(config)\n    checkpoint = torch.load(model_weight)\n    model.load_state_dict(checkpoint[\"model\"])\n    model.to(device)\n    prediction_dict = inference_function(test_loader, model, device)\n    predictions.append(prediction_dict[\"predictions\"])\n    torch.cuda.empty_cache()\n    gc.collect()\n    \npredictions = np.array(predictions)\npredictions = np.mean(predictions, axis=0)","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:26.824014Z","iopub.execute_input":"2024-01-30T19:22:26.824407Z","iopub.status.idle":"2024-01-30T19:22:31.198392Z","shell.execute_reply.started":"2024-01-30T19:22:26.824378Z","shell.execute_reply":"2024-01-30T19:22:31.197265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <b><span style='color:#F1A424'>|</span> Save Submission</b><a class='anchor' id='submission'></a> [↑](#top) \n\n***","metadata":{}},{"cell_type":"code","source":"TARGETS = ['seizure_vote', 'lpd_vote', 'gpd_vote', 'lrda_vote', 'grda_vote', 'other_vote']\nsub = pd.DataFrame({'eeg_id': test_df.eeg_id.values})\nsub[TARGETS] = predictions\nsub.to_csv('submission.csv',index=False)\nprint(f'Submissionn shape: {sub.shape}')\nsub.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-30T19:22:31.199695Z","iopub.execute_input":"2024-01-30T19:22:31.200033Z","iopub.status.idle":"2024-01-30T19:22:31.220821Z","shell.execute_reply.started":"2024-01-30T19:22:31.200006Z","shell.execute_reply":"2024-01-30T19:22:31.219876Z"},"trusted":true},"execution_count":null,"outputs":[]}]}