{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":7989184,"sourceType":"datasetVersion","datasetId":4646800}],"dockerImageVersionId":30674,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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)')\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-03-23T17:28:08.304058Z","iopub.execute_input":"2024-03-23T17:28:08.304555Z","iopub.status.idle":"2024-03-23T17:28:19.903533Z","shell.execute_reply.started":"2024-03-23T17:28:08.304524Z","shell.execute_reply":"2024-03-23T17:28:19.902392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class config:\n    BATCH_SIZE = 32\n    MODEL = \"tf_efficientnet_b7\"\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/hba-efficientnet-weights/tf_efficientnet_b7_epoch_0.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/hba-efficientnet-weights/*.pth\")]\n# model_weights  = [paths.MODEL_WEIGHTS,*model_weights]\n# model_weights = [paths.MODEL_WEIGHTS]\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:35:53.393580Z","iopub.execute_input":"2024-03-23T17:35:53.394295Z","iopub.status.idle":"2024-03-23T17:35:53.399947Z","shell.execute_reply.started":"2024-03-23T17:35:53.394263Z","shell.execute_reply":"2024-03-23T17:35:53.399024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Specs","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)\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:16:47.636140Z","iopub.execute_input":"2024-03-23T17:16:47.637058Z","iopub.status.idle":"2024-03-23T17:16:47.668304Z","shell.execute_reply.started":"2024-03-23T17:16:47.637021Z","shell.execute_reply":"2024-03-23T17:16:47.667362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{"execution":{"iopub.status.busy":"2024-03-23T17:16:47.750413Z","iopub.execute_input":"2024-03-23T17:16:47.751261Z","iopub.status.idle":"2024-03-23T17:16:47.772972Z","shell.execute_reply.started":"2024-03-23T17:16:47.751234Z","shell.execute_reply":"2024-03-23T17:16:47.772132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"paths_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)\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:16:49.798643Z","iopub.execute_input":"2024-03-23T17:16:49.799466Z","iopub.status.idle":"2024-03-23T17:16:50.015603Z","shell.execute_reply.started":"2024-03-23T17:16:49.799435Z","shell.execute_reply":"2024-03-23T17:16:50.014607Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"paths_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-03-23T17:16:50.888758Z","iopub.execute_input":"2024-03-23T17:16:50.889140Z","iopub.status.idle":"2024-03-23T17:17:02.406174Z","shell.execute_reply.started":"2024-03-23T17:16:50.889110Z","shell.execute_reply":"2024-03-23T17:17:02.405298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:29:12.782817Z","iopub.execute_input":"2024-03-23T17:29:12.783189Z","iopub.status.idle":"2024-03-23T17:29:12.794065Z","shell.execute_reply.started":"2024-03-23T17:29:12.783161Z","shell.execute_reply":"2024-03-23T17:29:12.793130Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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']\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:17:02.408286Z","iopub.execute_input":"2024-03-23T17:17:02.409242Z","iopub.status.idle":"2024-03-23T17:17:02.426713Z","shell.execute_reply.started":"2024-03-23T17:17:02.409207Z","shell.execute_reply":"2024-03-23T17:17:02.425806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-03-23T17:17:02.442048Z","iopub.execute_input":"2024-03-23T17:17:02.442881Z","iopub.status.idle":"2024-03-23T17:17:02.482334Z","shell.execute_reply.started":"2024-03-23T17:17:02.442855Z","shell.execute_reply":"2024-03-23T17:17:02.481368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## INFERENCE","metadata":{}},{"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-03-23T17:17:12.831583Z","iopub.execute_input":"2024-03-23T17:17:12.831949Z","iopub.status.idle":"2024-03-23T17:17:12.839551Z","shell.execute_reply.started":"2024-03-23T17:17:12.831920Z","shell.execute_reply":"2024-03-23T17:17:12.838313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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#     \n    model = CustomModel(config)\n    checkpoint = torch.load(model_weight)\n    model.load_state_dict(checkpoint[\"model\"])\n#     \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)\n","metadata":{"execution":{"iopub.status.busy":"2024-03-22T02:43:41.406131Z","iopub.execute_input":"2024-03-22T02:43:41.406480Z","iopub.status.idle":"2024-03-22T02:43:46.454046Z","shell.execute_reply.started":"2024-03-22T02:43:41.406454Z","shell.execute_reply":"2024-03-22T02:43:46.453142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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-03-22T02:40:26.927214Z","iopub.execute_input":"2024-03-22T02:40:26.927999Z","iopub.status.idle":"2024-03-22T02:40:26.945051Z","shell.execute_reply.started":"2024-03-22T02:40:26.927968Z","shell.execute_reply":"2024-03-22T02:40:26.944123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}