{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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.13"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":7712823,"sourceType":"datasetVersion","datasetId":4504017}],"dockerImageVersionId":30648,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# importing libraries\nimport warnings\nimport os\nimport re\nimport gc\nimport torch\nimport json\nimport random\nimport librosa\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport scipy.fft as fft\nimport scipy.signal as signal\nimport torchvision.transforms as transforms\n\nfrom typing import Optional, Callable\nfrom tqdm.notebook import tqdm\nfrom collections import defaultdict\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader, RandomSampler, default_collate\nfrom torch.optim import AdamW\nfrom torch.optim.lr_scheduler import ExponentialLR, OneCycleLR\nfrom torchvision import models as models\nfrom torchvision.transforms import Compose, RandomHorizontalFlip, RandomVerticalFlip\nfrom torchaudio.transforms import FrequencyMasking, TimeMasking\nfrom scipy.signal import butter, lfilter\nfrom sklearn.model_selection import KFold, StratifiedGroupKFold, GroupKFold, StratifiedKFold\nfrom sklearn.preprocessing import normalize as normalize_sk","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:46.051451Z","iopub.execute_input":"2024-02-27T12:42:46.052385Z","iopub.status.idle":"2024-02-27T12:42:55.937817Z","shell.execute_reply.started":"2024-02-27T12:42:46.052354Z","shell.execute_reply":"2024-02-27T12:42:55.936903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# defining macros\n# defining the modes for runnning the notebook\nclass MODES:\n    TRAIN = False\n    CV_SCORES =  False\n    VIZ_ARCH = False\n    VIZ_DATA = False\n    INFERENCE = True\n\n# defining useful global variables\nclass GLOBALS:\n    SAMPLING_FREQUENCY = 200\n    NQVIST_FREQUENCY = SAMPLING_FREQUENCY // 2\n    EEG_LENGTH = 50\n    SPECTROGRAM_LENGTH = 600\n    SEED = 1024\n    N_CLASSES = 6\n    N_SPLITS = 5\n    EPS = 1e-15\n    CLASS_MAP = {\n        'seizure': 0, \n        'lpd': 1,\n        'gpd': 2, \n        'lrda': 3, \n        'grda': 4, \n        'other': 5, \n    }\n    CHANNELS_DIM_MAP = {\n        'Fp1': 0,\n        'F3': 1,\n        'C3': 2,\n        'P3': 3,\n        'F7': 4,\n        'T3': 5,\n        'T5': 6,\n        'O1': 7,\n        'Fz': 8,\n        'Cz': 9,\n        'Pz': 10,\n        'Fp2': 11,\n        'F4': 12,\n        'C4': 13,\n        'P4': 14,\n        'F8': 15,\n        'T4': 16,\n        'T6': 17,\n        'O2': 18,\n        'EKG': 19\n    }\n    NUM_WORKERS = 2\n    USE_WAVELET = None \n    NAMES = ['LL','LP','RP','RR']\n    FEATS = [['Fp1','F7','T3','T5','O1'],\n             ['Fp1','F3','C3','P3','O1'],\n             ['Fp2','F8','T4','T6','O2'],\n             ['Fp2','F4','C4','P4','O2']]\n    TO_VIZUALIZE = 5\n    SPLITS_TO_SKIP = []\n\n# the configurations for the model\nclass CONF:\n    LCLIP = np.exp(-8)\n    RCLIP = np.exp(8)\n    WINDOW_SIZE = 200 * 50\n    TRAIN_BATCH_SIZE = 16\n    VAL_BATCH_SIZE = 8\n    MODEL = \"efficientnetb0-DBBHannKaggleSpectrograms1Channel-NoIntermediateConvk1-FreqMasking128-4Iter\"\n    LR = 1e-3\n    WEIGHT_DECAY = 1.0e-02\n    EPOCHS = 10\n    PATIENCE = -1\n    SCHED_STEP_AFTER_TRAIN = True\n    SCALING_FACTOR = 1\n    RESIZE = False\n    CONCATENATE = True\n    CONCATENATE_2C = False\n    CONTEXT_SIZE = 512\n    APPLY_AUGMENTATIONS = True\n    MASK_WINDOW = 128\n    MASK_ITER = 4\n    PARZEN = False\n    HANN = False\n    GAUSSIAN = True\n    STRATIFY = True\n    SIGMA = 20\n    MONTAGE = \"dbb\"\n    AUG_PROBABILITY = 0.5\n    \n# the paths that we will need to do our stuff\nclass PATHS:\n    DATA_ROOT = \"/kaggle/input/hms-harmful-brain-activity-classification\"\n    TRAIN_EEGS = os.path.join(DATA_ROOT,\"train_eegs\")\n    TRAIN_SPECTROGRAMS = os.path.join(DATA_ROOT,\"train_spectrograms\")\n    TRAIN_METADATA = os.path.join(DATA_ROOT,\"train.csv\")\n    TEST_EEGS = os.path.join(DATA_ROOT,\"test_eegs\")\n    TEST_SPECTROGRAMS = os.path.join(DATA_ROOT,\"test_spectrograms\")\n    TEST_METADATA = os.path.join(DATA_ROOT,\"test.csv\")\n    EEG_SPECTROGRAMS_HANN = \"./eeg_spects/eeg_spectrograms_hann\"\n    EEG_SPECTROGRAMS_PARZEN = \"./eeg_spects/eeg_spectrograms_parzen\"\n    EEG_SPECTROGRAMS_GAUSSIAN = \"./eeg_spects/eeg_spectrograms_gaussian\"\n    MODELS_ROOT = \"./models\"\n    DUMPS = \"./dumps_eeg\"\n    BEST_MODEL = os.path.join(MODELS_ROOT, CONF.MODEL)\n    INFERENCE_MODEL = \"/kaggle/input/efficientnetb0-dbbhannkaggle-1channel-freqmask128\"","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:55.939465Z","iopub.execute_input":"2024-02-27T12:42:55.939959Z","iopub.status.idle":"2024-02-27T12:42:55.955619Z","shell.execute_reply.started":"2024-02-27T12:42:55.939930Z","shell.execute_reply":"2024-02-27T12:42:55.954651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# defining functions\n# function used for reading the metadata file, while adding two \n# extra columns for the spectrogram and eeg paths\ndef read_metadata_file(train= True):\n    path = PATHS.TRAIN_METADATA if train else PATHS.TEST_METADATA\n    eeg_base_path = PATHS.TRAIN_EEGS if train else PATHS.TEST_EEGS\n    spectrogram_base_path = PATHS.TRAIN_SPECTROGRAMS if train else PATHS.TEST_SPECTROGRAMS\n    df = pd.read_csv(path)\n    df[\"eeg_path\"] = df[\"eeg_id\"].map(\n        lambda eeg_id: os.path.join(eeg_base_path,f\"{eeg_id}.parquet\")\n    )\n    df[\"spectrogram_path\"] = df[\"spectrogram_id\"].map(\n        lambda spectrogram_id: os.path.join(spectrogram_base_path,f\"{spectrogram_id}.parquet\")\n    )\n    return df\n\n# function used for setting the seed for deterministic, reproducible behaviours\ndef seed_everything():\n    torch.backends.cudnn.deterministic = True  \n    torch.backends.cudnn.benchmark = True  \n    torch.manual_seed(GLOBALS.SEED)  \n    np.random.seed(GLOBALS.SEED)  \n    random.seed(GLOBALS.SEED)\n    \n# function used for getting the eeg spectrogram data. it performs\n# aggragation over the \"eeg_id\" feature, thus the length of the resulting\n# data list object will be equal to the number of \n# unique \"eeg_id\"s in the training.csv file\ndef get_spectrogram_data(train=True): \n    data_list = []\n    train_metadata = read_metadata_file(train=train)\n    train_metadata_eeg_id = train_metadata.groupby(\"eeg_id\")\n    for eeg_id in train_metadata_eeg_id.groups:\n        eeg_id_df = train_metadata_eeg_id.get_group(eeg_id)\n        patient_id = eeg_id_df[\"patient_id\"].unique()\n        assert patient_id.size == 1\n        patient_id = patient_id[0]\n        if train:\n            eeg_path = os.path.join(PATHS.DUMPS, f\"{eeg_id}.npz\")\n            eeg_spectrogram_path_hann = os.path.join(PATHS.EEG_SPECTROGRAMS_HANN + f\"_{CONF.MONTAGE}\", f\"{eeg_id}.npz\")\n            eeg_spectrogram_path_parzen = os.path.join(PATHS.EEG_SPECTROGRAMS_PARZEN + f\"_{CONF.MONTAGE}\", f\"{eeg_id}.npz\")\n            eeg_spectrogram_path_gaussian = os.path.join(PATHS.EEG_SPECTROGRAMS_GAUSSIAN + f\"_{CONF.MONTAGE}\", f\"{eeg_id}.npz\")\n            data = np.load(eeg_path)\n            consensus = np.argmax(data[\"votes\"])\n            data_item = {\n                \"eeg_id\": eeg_id,\n                \"patient_id\": patient_id,\n                \"eeg_path\": eeg_path,\n                \"eeg_spectrogram_path_hann\": eeg_spectrogram_path_hann,\n                \"eeg_spectrogram_path_gaussian\": eeg_spectrogram_path_gaussian,\n                \"eeg_spectrogram_path_parzen\": eeg_spectrogram_path_parzen,\n                \"consensus\": consensus\n            }\n        else:\n            eeg_path = eeg_id_df[\"eeg_path\"].unique()\n            assert eeg_path.size == 1\n            eeg_path = eeg_path[0]\n            spectrogram_path = eeg_id_df[\"spectrogram_path\"].unique()\n            assert spectrogram_path.size == 1\n            spectrogram_path = spectrogram_path[0]\n            data_item = {\n                \"eeg_id\": eeg_id,\n                \"patient_id\": patient_id,\n                \"eeg_path\": eeg_path, \n                \"spectrogram_path\": spectrogram_path\n            }\n        data_list.append(data_item)\n    return data_list\n\n# function used for splitting the data using stratified k fold procedure\ndef split_data(data_list, data_key = \"eeg_path\", target_key = \"consensus\", group_key = \"patient_id\"):\n    splits_dictionary = dict()\n    G = [datapoint[group_key] for datapoint in data_list]\n    X = [datapoint[data_key] for datapoint in data_list]\n    Y = [datapoint[target_key] for datapoint in data_list]\n    splitter = StratifiedGroupKFold(n_splits = GLOBALS.N_SPLITS) if CONF.STRATIFY else GroupKFold(n_splits = GLOBALS.N_SPLITS)\n    splits = splitter.split(X, Y, G)\n    for split_id, (train_idx, val_idx) in enumerate(splits):\n        train_data = [data_list[idx] for idx in train_idx]\n        val_data = [data_list[idx] for idx in val_idx]\n        split_data = {\n            \"train\": train_data,\n            \"validation\": val_data\n        }\n        splits_dictionary[split_id] = split_data\n    return splits_dictionary\n\n# reshapes the spectrogram array to the target shape\ndef reshape_spectrogram_array(spectrogram_array, n_channels = 4, offset = 100):\n    spectrogram_array = spectrogram_array[:, 1:]\n    spects = []\n    for idx in range(n_channels):\n        spect_channel = spectrogram_array[:, idx*offset:(idx+1)*offset].T\n        spects.append(spect_channel)\n    spects = np.stack(spects, axis = 0)\n    return spects # n_channels, n_freq_ranges, n_time \n\n# function used for sampling the random window context from a 3 dimensional array\ndef sample_random_window(array: np.ndarray, window_size: int = 256):\n    offset = np.random.randint(0, array.shape[-1]-window_size+1)\n    array = array[:,:,offset:offset+window_size]\n    return array\n\n# pad the array along the 2st axis\ndef pad_array(array, target_height: int = 128):\n    _, height, _ = array.shape\n    pad = (target_height - height) // 2\n    pad = (0, 0), (pad, pad), (0, 0)\n    array = np.pad(array, pad, \"constant\", constant_values = 0)\n    return array\n\n# prepares the kaggle provided spectrogram for preprocessing\ndef prepare_kaggle_spectrogram(kaggle_spectrogram, train = False, window_size: int = 256):\n    kaggle_spectrogram = reshape_spectrogram_array(kaggle_spectrogram)\n    if train:\n        kaggle_spectrogram = sample_random_window(kaggle_spectrogram)\n    else:\n        offset = (kaggle_spectrogram.shape[-1] - window_size)//2\n        kaggle_spectrogram = kaggle_spectrogram[:, :, offset: offset+window_size]\n    return kaggle_spectrogram\n\n# applies transformations on the input\ndef clip_log_norm(kaggle_spectrogram, eps = 1e-15):\n    kaggle_spectrogram = np.clip(kaggle_spectrogram, CONF.LCLIP, CONF.RCLIP)\n    kaggle_spectrogram = np.log(kaggle_spectrogram)\n    mean = kaggle_spectrogram.mean()\n    std = kaggle_spectrogram.std()\n    kaggle_spectrogram = (kaggle_spectrogram - mean)/(std + eps)\n    return kaggle_spectrogram\n\n\n# applies transformations on the input\ndef clip_log_norm(kaggle_spectrogram, eps = 1e-15):\n    kaggle_spectrogram = np.clip(kaggle_spectrogram, CONF.LCLIP, CONF.RCLIP)\n    kaggle_spectrogram = np.log(kaggle_spectrogram)\n    mean = kaggle_spectrogram.mean()\n    std = kaggle_spectrogram.std()\n    kaggle_spectrogram = (kaggle_spectrogram - mean)/(std + eps)\n    return kaggle_spectrogram\n\n\n# applies transformations on the input\ndef norm(kaggle_spectrogram, eps = 1e-15, channelwise = False):\n    if channelwise:\n        mean = np.mean(kaggle_spectrogram, axis = (1, 2))[:, np.newaxis, np.newaxis]\n        std = np.std(kaggle_spectrogram, axis = (1, 2))[:, np.newaxis, np.newaxis]\n    else:\n        mean = kaggle_spectrogram.mean()\n        std = kaggle_spectrogram.std()\n    kaggle_spectrogram = (kaggle_spectrogram - mean)/(std + eps)\n    return kaggle_spectrogram\n\ndef maddest(d, axis=None):\n    return np.mean(np.absolute(d - np.mean(d, axis)), axis)\n\ndef denoise(x, wavelet='haar', level=1):    \n    coeff = pywt.wavedec(x, wavelet, mode=\"per\")\n    sigma = (1/0.6745) * maddest(coeff[-level])\n\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\n    ret=pywt.waverec(coeff, wavelet, mode='per')\n    \n    return ret\n\ndef spectrogram_from_eeg(parquet_path):\n    \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    signals = []\n    for k in range(4):\n        COLS = GLOBALS.FEATS[k]\n        \n        for kk in range(4):\n        \n            x = eeg[COLS[kk]].values - eeg[COLS[kk+1]].values\n\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            if GLOBALS.USE_WAVELET:\n                x = denoise(x, wavelet=USE_WAVELET)\n            signals.append(x)\n\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            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            mel_spec_db = (mel_spec_db+40)/40 \n            img[:,:,k] += mel_spec_db\n                \n        img[:,:,k] /= 4.0\n        \n    return img\n\ndef reshape_spect_to_one_channel(ks):\n    n_channels, _, _ = ks.shape\n    arr = []\n    for channel in range(0, n_channels - 1, 2):\n        arr.append([ks[channel], ks[channel + 1]])\n    arr = [np.concatenate(item, axis = 0) for item in arr]\n    arr = np.concatenate(arr, axis = 1)\n    return arr\n\n\ndef concatenate_spectrograms(kaggle_spectrogram, eeg_spectrograms):\n    cat_ks, cat_es = [], []\n    n_channels = kaggle_spectrogram.shape[0]\n    for channel in range(n_channels):\n        ks = kaggle_spectrogram[channel]\n        es = eeg_spectrograms[channel]\n        cat_ks.append(ks)\n        cat_es.append(es)\n    cat_ks = np.concatenate(cat_ks)\n    cat_es = np.concatenate(cat_es)\n    if CONF.CONCATENATE_2C:\n        return np.stack((cat_ks, cat_es), axis = 0)\n    return np.concatenate((cat_ks, cat_es), axis = 1)\n","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:55.956726Z","iopub.execute_input":"2024-02-27T12:42:55.957056Z","iopub.status.idle":"2024-02-27T12:42:56.007943Z","shell.execute_reply.started":"2024-02-27T12:42:55.957028Z","shell.execute_reply":"2024-02-27T12:42:56.006965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# defining classes\nclass SpectrogramDataset(Dataset):\n    def __init__(self, data_list, train = True):\n        self.data_list = data_list\n        self.train = train\n    \n    def __len__(self):\n        return len(self.data_list)\n    \n    def __getitem__(self, idx):\n        data_item = self.data_list[idx]\n        kaggle_data_path = data_item[\"eeg_path\"]\n        eeg_spectrogram_data_path = data_item[\"eeg_spectrogram_path\"]\n        kaggle_data = np.load(kaggle_data_path)\n        eeg_spectrogram_data = np.load(eeg_spectrogram_data_path)\n        eeg_spectrogram = eeg_spectrogram_data[\"eeg_spectrogram\"]\n        kaggle_spectrogram = kaggle_data[\"spectrogram\"]\n        kaggle_spectrogram = np.nan_to_num(kaggle_spectrogram, nan=0)\n        votes = kaggle_data[\"votes\"]\n        eeg_spectrogram = reshape_eeg_spectrogram_array(eeg_spectrogram)\n        kaggle_spectrogram = clip_log_norm(kaggle_spectrogram)\n        kaggle_spectrogram = prepare_kaggle_spectrogram(kaggle_spectrogram) / CONF.SCALING_FACTOR\n        merged_spectrogram = np.concatenate([eeg_spectrogram, kaggle_spectrogram])\n        #merged_spectrogram = clip_log_norm(merged_spectrogram)\n        eeg_spectrogram = torch.from_numpy(eeg_spectrogram)\n        kaggle_spectrogram = torch.from_numpy(kaggle_spectrogram)\n        merged_spectrogram = torch.from_numpy(merged_spectrogram)\n        if CONF.CONCATENATE:\n            merged_spectrogram = concatenate_spectrograms(kaggle_spectrogram, eeg_spectrogram) \n            merged_spectrogram = torch.from_numpy(merged_spectrogram)\n            if not CONF.CONCATENATE_2C:\n                merged_spectrogram = torch.unsqueeze(merged_spectrogram, dim = 0)\n        if CONF.RESIZE:\n            image_transform = transforms.Compose([transforms.Resize((CONF.CONTEXT_SIZE, CONF.CONTEXT_SIZE)),])\n            merged_spectrogram = image_transform(merged_spectrogram)\n        if CONF.APPLY_AUGMENTATIONS and self.train:\n            masking = FrequencyMasking(CONF.MASK_WINDOW)\n            merged_spectrogram = masking(merged_spectrogram)\n        data_dict = {\n            \"votes\": votes, \n            \"merged_spectrogram\":merged_spectrogram, \n            \"kaggle_spectrogram\":kaggle_spectrogram,\n            \"eeg_spectrogram\": eeg_spectrogram\n        }\n        return data_dict\n\nclass SpectrogramDatasetMultiEEGSpect(Dataset):\n    def __init__(self, data_list, train = True, apply_augmentations = False):\n        self.data_list = data_list\n        self.train = train\n        self.apply_augmentations = apply_augmentations\n        self.image_transform = transforms.Resize((CONF.CONTEXT_SIZE, CONF.CONTEXT_SIZE))\n    \n    def __len__(self):\n        return len(self.data_list)\n    \n    def __getitem__(self, idx):\n        data_item = self.data_list[idx]\n        # reading votes and eeg_id\n        eeg_id = data_item[\"eeg_id\"]\n        eeg_path = os.path.join(PATHS.TRAIN_EEGS if self.train else PATHS.TEST_EEGS, f\"{eeg_id}.parquet\")\n        # reading hann spectrogram and reshaping it to desired shape\n        if self.train:\n            eeg_spectrogram_data_path_hann = data_item[\"eeg_spectrogram_path_hann\"]\n            eeg_spectrogram_data_hann = np.load(eeg_spectrogram_data_path_hann)\n            eeg_spectrogram_data_hann = eeg_spectrogram_data_hann[\"eeg_spectrogram\"]\n        else:\n            eeg_spectrogram_data_hann = spectrogram_from_eeg(data_item[\"eeg_path\"])\n        eeg_spectrogram_data_hann = np.transpose(eeg_spectrogram_data_hann, (2, 0, 1))\n        eeg_spectrogram_data_hann = norm(eeg_spectrogram_data_hann)\n        eeg_spectrogram_data_hann = reshape_spect_to_one_channel(eeg_spectrogram_data_hann)\n        hann_spectrogram = eeg_spectrogram_data_hann\n        #hann_spectrogram = torch.from_numpy(hann_spectrogram)\n        #hann_spectrogram = torch.unsqueeze(hann_spectrogram, dim = 0)\n        #hann_spectrogram = self.image_transform(hann_spectrogram)\n        # reading gaussian spectrogram and reshaping it to desired shape\n        #if self.train:\n        #    eeg_spectrogram_data_path_gaussian = data_item[\"eeg_spectrogram_path_gaussian\"]\n        #    eeg_spectrogram_data_gaussian = np.load(eeg_spectrogram_data_path_gaussian)\n        #    eeg_spectrogram_data_gaussian = eeg_spectrogram_data_gaussian[\"eeg_spectrogram\"]\n        #else:\n        #    eeg_spectrogram_data_gaussian = spectrogram_from_eeg(eeg_path, window = (\"gaussian\", CONF.SIGMA))\n        #eeg_spectrogram_data_gaussian = np.transpose(eeg_spectrogram_data_gaussian, (2, 0, 1))\n        #eeg_spectrogram_data_gaussian = norm(eeg_spectrogram_data_gaussian)\n        #eeg_spectrogram_data_gaussian = reshape_spect_to_one_channel(eeg_spectrogram_data_gaussian)\n        #gaussian_spectrogram = eeg_spectrogram_data_gaussian\n        #gaussian_spectrogram = torch.from_numpy(gaussian_spectrogram)\n        #gaussian_spectrogram = torch.unsqueeze(gaussian_spectrogram, dim = 0)\n        #gaussian_spectrogram = self.image_transform(gaussian_spectrogram)\n        # reading parzen spectrogram and reshaping it to desired shape\n        #if self.train:            \n        #    eeg_spectrogram_data_path_parzen = data_item[\"eeg_spectrogram_path_parzen\"]\n        #    eeg_spectrogram_data_parzen = np.load(eeg_spectrogram_data_path_parzen)\n        #    eeg_spectrogram_data_parzen = eeg_spectrogram_data_parzen[\"eeg_spectrogram\"]\n        #else:\n        #    eeg_spectrogram_data_parzen = spectrogram_from_eeg(eeg_path, window = \"parzen\")\n        #eeg_spectrogram_data_parzen = np.transpose(eeg_spectrogram_data_parzen, (2, 0, 1))\n        #eeg_spectrogram_data_parzen = norm(eeg_spectrogram_data_parzen)\n        #eeg_spectrogram_data_parzen = reshape_spect_to_one_channel(eeg_spectrogram_data_parzen)\n        #parzen_spectrogram = eeg_spectrogram_data_parzen\n        #parzen_spectrogram = torch.from_numpy(parzen_spectrogram)\n        #parzen_spectrogram = torch.unsqueeze(parzen_spectrogram, dim = 0)\n        #parzen_spectrogram = self.image_transform(parzen_spectrogram)\n        # reading kaggle spectrogam and replacing nan\n        if self.train: \n            kaggle_data_path = data_item[\"eeg_path\"]\n            kaggle_data = np.load(kaggle_data_path)\n            votes = kaggle_data[\"votes\"]\n            kaggle_spectrogram = kaggle_data[\"spectrogram\"]\n        else:\n            kaggle_spectrogram = pd.read_parquet(data_item[\"spectrogram_path\"]).values\n        kaggle_spectrogram = np.nan_to_num(kaggle_spectrogram, nan=0) / 32\n        kaggle_spectrogram = prepare_kaggle_spectrogram(kaggle_spectrogram) \n        kaggle_spectrogram = np.clip(kaggle_spectrogram, CONF.LCLIP, CONF.RCLIP)\n        kaggle_spectrogram = np.log(kaggle_spectrogram)\n        kaggle_spectrogram = norm(kaggle_spectrogram)\n        kaggle_spectrogram = pad_array(kaggle_spectrogram, )\n        kaggle_spectrogram = reshape_spect_to_one_channel(kaggle_spectrogram)\n        #kaggle_spectrogram = torch.from_numpy(kaggle_spectrogram)\n        #kaggle_spectrogram = torch.unsqueeze(kaggle_spectrogram, dim = 0)\n        #kaggle_spectrogram = pad_array(kaggle_spectrogram)\n        #kaggle_spectrogram = torch.from_numpy(kaggle_spectrogram)\n        #kaggle_spectrogram = self.image_transform(kaggle_spectrogram)\n        # reading votes\n        # merging spectrograms\n        #merged_spectrogram = hann_spectrogram\n        merged_spectrogram = np.concatenate(\n            [\n                kaggle_spectrogram, \n                hann_spectrogram,\n            ], axis = 0\n        )\n        merged_spectrogram = torch.from_numpy(merged_spectrogram)\n        merged_spectrogram = torch.unsqueeze(merged_spectrogram, dim = 0)\n        #merged_spectrogram = torch.concatenate(\n        #    [\n        #        kaggle_spectrogram, \n        #        hann_spectrogram,\n        #        #gaussian_spectrogram,\n        #        #parzen_spectrogram \n        #    ], dim = 0\n        #)\n        #merged_spectrogram = kaggle_spectrogram\n        if self.apply_augmentations:\n            t_masking = TimeMasking(CONF.MASK_WINDOW)\n            f_masking = FrequencyMasking(CONF.MASK_WINDOW)\n            for _ in range(CONF.MASK_ITER):\n                #merged_spectrogram = t_masking(merged_spectrogram)\n                merged_spectrogram = f_masking(merged_spectrogram)\n            #transforms = Compose(\n            #    [\n            #        RandomHorizontalFlip(CONF.AUG_PROBABILITY)\n            #    ]\n            #)\n        data_dict = {\n            #\"kaggle_spectrogram\": kaggle_spectrogram,\n            #\"hann_spectrogram\": hann_spectrogram,\n            #\"gaussian_spectrogram\": gaussian_spectrogram,\n            #\"parzen_spectrogram\": parzen_spectrogram,\n            \"merged_spectrogram\": merged_spectrogram,\n            \"votes\": votes,\n        } if self.train else {\n            \"eeg_id\": eeg_id,\n            \"merged_spectrogram\": merged_spectrogram\n        }\n        return data_dict\n        \n# class for creating a wrapper around the predefined torchvision\n# models in order to be able to accept any custom number of\n# input channels and output classes. on the input side, \n# the adaptation is done through a convolutional layer \n# with kernel size of 1, while on the output side, it will be\n# done obtained with a simple linear layer with the desired output size\nclass MyModelWrapper(nn.Module):\n    def __init__(\n            self,\n            backbone,\n            in_channels = 4,\n            hidden_size = 3,\n            out_classes = GLOBALS.N_CLASSES,\n        ):\n        super(MyModelWrapper, self).__init__()\n        self.hidden_size = hidden_size\n        self.in_channels = in_channels\n        self.backbone = backbone\n        self.out_classes = out_classes\n        self.output_layer = nn.LazyLinear(\n            self.out_classes\n        )\n        self.conv0 = nn.Conv2d(self.in_channels, self.hidden_size, kernel_size=7, stride=2, padding=3, bias=False) #here 4 indicates 4-channel input\n        self.conv1 = nn.Conv2d(self.in_channels, 3, 1, bias=False)\n\n    def forward(self, x):\n        #x = self.conv0(x)\n        x = self.conv1(x)\n        x = self.backbone(x)\n        x = self.output_layer(x)\n        return x\n","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:56.010830Z","iopub.execute_input":"2024-02-27T12:42:56.011234Z","iopub.status.idle":"2024-02-27T12:42:56.042316Z","shell.execute_reply.started":"2024-02-27T12:42:56.011197Z","shell.execute_reply":"2024-02-27T12:42:56.041251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# vizualizing architecture\nif MODES.VIZ_ARCH:\n    example_index = 0\n    in_channels = 1\n    eeg_spectrograms_data = get_spectrogram_data()\n    train_dataset = SpectrogramDataset(eeg_spectrograms_data)\n    example = train_dataset[0][\"merged_spectrogram\"]\n    example = torch.unsqueeze(example, dim = 0).float()\n    model = models.efficientnet_b2(weights = models.EfficientNet_B2_Weights.DEFAULT)\n    model = MyModelWrapper(model, in_channels = in_channels)\n    example_output = model(example)\n    dot = make_dot(example_output, dict(model.named_parameters()))\n    dot.render(directory='doctest-output', view=True)","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:56.043496Z","iopub.execute_input":"2024-02-27T12:42:56.043868Z","iopub.status.idle":"2024-02-27T12:42:56.058833Z","shell.execute_reply.started":"2024-02-27T12:42:56.043833Z","shell.execute_reply":"2024-02-27T12:42:56.057644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# vizualizing transformed data\nif MODES.VIZ_DATA:\n    seed_everything()\n    warnings.filterwarnings('ignore', category=Warning)\n    eeg_spectrograms_data = get_spectrogram_data()\n    eeg_spectrogram_subsample = list(np.random.choice(eeg_spectrograms_data, size = GLOBALS.TO_VIZUALIZE))\n    subsample_dataset = SpectrogramDatasetMultiEEGSpect(eeg_spectrogram_subsample, apply_augmentations=True)\n    print(subsample_dataset[0][\"merged_spectrogram\"].shape)\n    #n_channels, _, _  = subsample_dataset[0][\"merged_spectrogram\"].shape\n    n_channels = 1\n    fig, axes = plt.subplots(GLOBALS.TO_VIZUALIZE, n_channels, figsize = (50, 50))\n    for obs_idx, observation in enumerate(subsample_dataset):\n        merged_spectrogram = observation[\"merged_spectrogram\"]\n        axes[obs_idx].imshow(merged_spectrogram[0])","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:56.060015Z","iopub.execute_input":"2024-02-27T12:42:56.060320Z","iopub.status.idle":"2024-02-27T12:42:56.073414Z","shell.execute_reply.started":"2024-02-27T12:42:56.060296Z","shell.execute_reply":"2024-02-27T12:42:56.072361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train models\nif MODES.TRAIN:\n    os.mkdir(PATHS.BEST_MODEL)\n    warnings.filterwarnings('ignore', category=Warning)\n    seed_everything()\n    training_history = dict()\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    eeg_spectrograms_data = get_spectrogram_data()\n    eeg_spectrograms_splits = split_data(eeg_spectrograms_data)\n    for split_id, splits in eeg_spectrograms_splits.items():\n        if split_id in GLOBALS.SPLITS_TO_SKIP:\n            continue\n        in_channels =  1\n        best_epoch = -1\n        no_improvement = 0\n        best_val_loss = float('inf')\n        train_losses = []\n        val_losses = []\n        train_losses_epochs = []\n        val_losses_epochs = []\n        lrs = []\n        train_data = splits[\"train\"]\n        val_data = splits[\"validation\"]\n        train_dataset = SpectrogramDatasetMultiEEGSpect(train_data, apply_augmentations = CONF.APPLY_AUGMENTATIONS)\n        val_dataset = SpectrogramDatasetMultiEEGSpect(val_data)\n        print(f\"Training on split {split_id}. Train dataset has {len(train_dataset)} observations, val dataset has {len(val_dataset)} observations\")\n        train_dataloader = DataLoader(train_dataset, batch_size = CONF.TRAIN_BATCH_SIZE, num_workers = GLOBALS.NUM_WORKERS, shuffle = True)\n        val_dataloader = DataLoader(val_dataset, batch_size = CONF.VAL_BATCH_SIZE, num_workers = GLOBALS.NUM_WORKERS, shuffle = True)\n        model = models.efficientnet_b0(weights = models.EfficientNet_B0_Weights.DEFAULT)\n        model = MyModelWrapper(model, in_channels = in_channels)\n        model.to(device)\n        optimizer = AdamW(model.parameters(),lr = CONF.LR,weight_decay = CONF.WEIGHT_DECAY)\n        loss_function = nn.KLDivLoss(reduction = \"batchmean\")\n        scheduler = OneCycleLR(optimizer, max_lr=CONF.LR,\n                               steps_per_epoch=len(train_dataloader), epochs=CONF.EPOCHS,\n                                pct_start=0.0, div_factor=25, final_div_factor=4.0e-01)\n        scaler = torch.cuda.amp.GradScaler(enabled=True)\n        for epoch in range(CONF.EPOCHS):\n            epoch_length = len(train_dataloader)\n            time=tqdm(range(epoch_length))\n            model.train()\n            train_loss = 0.0\n            step = 0\n            for idx, batch in enumerate(train_dataloader):\n                optimizer.zero_grad()\n                step += 1\n                inputs, labels = batch[\"merged_spectrogram\"], batch[\"votes\"]\n                inputs = inputs.to(device).float()\n                labels = labels.to(device).float()\n                with torch.cuda.amp.autocast():\n                    outputs = model(inputs)\n                    outputs = nn.functional.log_softmax(outputs, dim = 1)\n                    loss = loss_function(outputs, labels)\n                scaler.scale(loss).backward() \n                scaler.unscale_(optimizer)\n                scaler.step(optimizer)\n                scaler.update()\n                optimizer.zero_grad()\n                if CONF.SCHED_STEP_AFTER_TRAIN:\n                    scheduler.step()\n                train_loss += loss.item()\n                train_losses.append(train_loss)\n                lrs.append([item['lr'] for item in optimizer.param_groups])\n                time.set_description(f\"{step}/{epoch_length}, train_loss: {train_loss / step:.4f}, lr: {[item['lr'] for item in optimizer.param_groups]}\")\n                time.update()\n                del inputs, labels, outputs, loss\n                gc.collect()\n                torch.cuda.empty_cache()\n            train_losses_epochs.append(train_loss / step)\n            with torch.no_grad():\n                epoch_length = len(val_dataloader)\n                time=tqdm(range(epoch_length))\n                model.eval()\n                val_loss = 0.0\n                step = 0\n                for idx, batch in enumerate(val_dataloader):\n                    step += 1\n                    inputs, labels = batch[\"merged_spectrogram\"], batch[\"votes\"]\n                    inputs = inputs.to(device).float()\n                    labels = labels.to(device).float()\n                    outputs = model.forward(inputs)\n                    outputs = nn.functional.log_softmax(outputs,dim = 1)\n                    loss = loss_function(outputs, labels)\n                    val_loss += loss.item()\n                    val_losses.append(val_loss)\n                    time.set_description(f\"{step}/{epoch_length}, val_loss: {val_loss/step:.4f}\")\n                    time.update()\n                    del inputs, labels, outputs, loss\n                    gc.collect()\n                    torch.cuda.empty_cache()\n            val_losses_epochs.append(val_loss / step)\n            if not CONF.SCHED_STEP_AFTER_TRAIN:\n                scheduler.step()\n            if val_loss / step < best_val_loss:\n                no_improvement = 0\n                best_val_loss = val_loss / step\n                best_metric_epoch = epoch + 1\n                dump_file = os.path.join(PATHS.BEST_MODEL, f\"{split_id}.pth\")\n                torch.save(model.state_dict(), dump_file)\n                print(\"saved new best validation loss model\")\n            else:\n                no_improvement += 1\n                print(f\"validation loss not improving for {no_improvement} epochs\")\n                if no_improvement == CONF.PATIENCE:\n                    print(\"patience reached, quitting training\")\n                    break\n            print(f\"current epoch: {epoch + 1} current val loss: {val_loss / step:.4f} best val loss: {best_val_loss:.4f} at epoch {best_metric_epoch}\")\n            history = {\n                \"train_losses\": train_losses,\n                \"train_losses_epochs\": train_losses_epochs, \n                \"val_losses_epochs\": val_losses_epochs,\n                \"val_losses\": val_losses,\n                \"lrs\": lrs\n            }\n            training_history[split_id] = history\n            history_file = os.path.join(PATHS.BEST_MODEL,\"history.json\")\n            with open(history_file, \"w\") as hist_fh:\n                json.dump(training_history, hist_fh)","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:56.074632Z","iopub.execute_input":"2024-02-27T12:42:56.074974Z","iopub.status.idle":"2024-02-27T12:42:56.103317Z","shell.execute_reply.started":"2024-02-27T12:42:56.074948Z","shell.execute_reply":"2024-02-27T12:42:56.102431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# computing cv score\nif MODES.CV_SCORES:\n    seed_everything()\n    warnings.filterwarnings('ignore', category=Warning)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    eeg_spectrograms_data = get_spectrogram_data()\n    eeg_spectrograms_splits = split_data(eeg_spectrograms_data)\n    inverse_class_map = {v:k for k, v in GLOBALS.CLASS_MAP.items()}\n    idx = 0\n    predictions_list = []  \n    ground_truth_list = []\n    for split_id, splits in eeg_spectrograms_splits.items():\n        in_channels =  9\n        print(f\"running predictions on split {split_id + 1}\")\n        val_data = splits[\"validation\"]\n        val_dataset = SpectrogramDatasetMultiEEGSpect(val_data)\n        val_dataloader = DataLoader(val_dataset, batch_size = 1, shuffle = False)\n        best_model_file = os.path.join(PATHS.BEST_MODEL, f\"{split_id}.pth\")\n        best_weights = torch.load(best_model_file)\n        model = models.efficientnet_b0()\n        #model = models.resnet34(in_channels = 3)\n        model = MyModelWrapper(model, in_channels = in_channels).to(device)\n        model.load_state_dict(best_weights)\n        model.eval()\n        time = tqdm(range(len(val_dataloader)))\n        with torch.no_grad():\n            for batch in val_dataloader:\n                inputs, labels = batch[\"merged_spectrogram\"], batch[\"votes\"]\n                inputs = inputs.to(device).float()\n                labels = labels.to(device).float()\n                outputs = model.forward(inputs)\n                outputs = nn.functional.softmax(outputs,dim = 1)\n                outputs = outputs.detach().cpu().numpy().reshape(-1)\n                labels = labels.detach().cpu().numpy().reshape(-1)\n                pred = dict()\n                gt = dict()\n                pred[\"id\"] = idx\n                gt[\"id\"] = idx\n                for label_id in range(GLOBALS.N_CLASSES):\n                    label_string = inverse_class_map[label_id]\n                    column_name = label_string + \"_vote\"\n                    prediction = outputs[label_id]\n                    ground_truth = labels[label_id]\n                    pred[column_name] = prediction\n                    gt[column_name] = ground_truth\n                predictions_list.append(pred)\n                ground_truth_list.append(gt)\n                idx += 1\n                time.update()\n    predictions_df = pd.concat([pd.DataFrame(pred, index = [0]) for pred in predictions_list])\n    ground_truth_df = pd.concat([pd.DataFrame(gt, index = [0]) for gt in ground_truth_list])\n    score = score_fn(ground_truth_df, predictions_df, \"id\")\n    print(f\"CV Score: {score}\")\n","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-02-27T12:42:56.104814Z","iopub.execute_input":"2024-02-27T12:42:56.105219Z","iopub.status.idle":"2024-02-27T12:42:56.120861Z","shell.execute_reply.started":"2024-02-27T12:42:56.105193Z","shell.execute_reply":"2024-02-27T12:42:56.119919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# inference\nif MODES.INFERENCE:\n    seed_everything()\n    warnings.filterwarnings('ignore', category=Warning)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    inverse_class_map = {v:k for k, v in GLOBALS.CLASS_MAP.items()}\n    test_data = get_spectrogram_data(train=False)\n    test_dataset = SpectrogramDatasetMultiEEGSpect(test_data, train=False)\n    obs = test_dataset[0]    \n    spect = obs[\"merged_spectrogram\"]\n    test_dataloader = DataLoader(test_dataset, batch_size = 1, shuffle = False)\n    predictions_list = []\n    in_channels =  1\n    weights_path = os.listdir(PATHS.INFERENCE_MODEL)\n    weights_path = [os.path.join(PATHS.INFERENCE_MODEL, model_path) for model_path in weights_path if \".pth\" in model_path]\n    weights = [torch.load(weight) for weight in weights_path]\n    for batch in test_dataloader:\n        inputs = batch[\"merged_spectrogram\"]\n        inputs = inputs.to(device).float()\n        predictions = []\n        with torch.no_grad():\n            for weight_dict in weights:\n                model = models.efficientnet_b0()\n                model = MyModelWrapper(model, in_channels = in_channels).to(device)\n                model.load_state_dict(weight_dict)\n                model.to(device)\n                model.eval()\n                outputs = model(inputs)\n                predictions.append(outputs)\n        predictions = torch.stack(predictions, dim = 0)\n        predictions = torch.mean(predictions, dim = 0)\n        predictions = nn.functional.softmax(predictions, dim = 1)\n        predictions = predictions.detach().cpu().numpy()\n        predictions = predictions.reshape(-1)\n        pred = dict()\n        pred[\"eeg_id\"] = batch[\"eeg_id\"]\n        for label_id in range(GLOBALS.N_CLASSES):\n            label_string = inverse_class_map[label_id]\n            column_name = label_string + \"_vote\"\n            prediction = predictions[label_id]\n            pred[column_name] = prediction\n        predictions_list.append(pred)\n    predictions_df = pd.concat([pd.DataFrame(pred, index = [0]) for pred in predictions_list])\n    predictions_df.to_csv(\"submission.csv\", index = False)\n    print(predictions_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-02-27T12:42:56.121951Z","iopub.execute_input":"2024-02-27T12:42:56.122245Z","iopub.status.idle":"2024-02-27T12:43:11.716653Z","shell.execute_reply.started":"2024-02-27T12:42:56.122220Z","shell.execute_reply":"2024-02-27T12:43:11.715482Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]}]}