{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","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":7392775,"sourceType":"datasetVersion","datasetId":4297782}],"dockerImageVersionId":30635,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nimport pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom matplotlib.ticker import MaxNLocator\n\nimport pywt\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization, Conv2D, MaxPooling2D, GlobalAveragePooling2D\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.optimizers import Adam\n\nfrom sklearn.preprocessing import LabelEncoder, RobustScaler\nfrom sklearn.model_selection import train_test_split, StratifiedKFold\nfrom sklearn.metrics import log_loss, confusion_matrix, f1_score","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:23:19.423693Z","iopub.execute_input":"2024-01-16T02:23:19.424080Z","iopub.status.idle":"2024-01-16T02:23:34.438100Z","shell.execute_reply.started":"2024-01-16T02:23:19.424037Z","shell.execute_reply":"2024-01-16T02:23:34.436959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.environ[\"CUDA_VISIBLE_DEVICES\"]=\"0,1\"\n\n# USE MULTIPLE GPUS\ngpus = tf.config.list_physical_devices('GPU')\nif len(gpus)<=1: \n    strategy = tf.distribute.OneDeviceStrategy(device=\"/gpu:0\")\n    print(f'Using {len(gpus)} GPU')\nelse: \n    strategy = tf.distribute.MirroredStrategy()\n    print(f'Using {len(gpus)} GPUs')","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:23:36.847233Z","iopub.execute_input":"2024-01-16T02:23:36.848177Z","iopub.status.idle":"2024-01-16T02:23:38.183898Z","shell.execute_reply.started":"2024-01-16T02:23:36.848145Z","shell.execute_reply":"2024-01-16T02:23:38.182966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/train.csv')\ntest_df = pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/test.csv')\nsubmission = pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/sample_submission.csv')\n\nspectrograms = np.load('/kaggle/input/brain-spectrograms/specs.npy',allow_pickle=True)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:23:41.775276Z","iopub.execute_input":"2024-01-16T02:23:41.775921Z","iopub.status.idle":"2024-01-16T02:24:38.203386Z","shell.execute_reply.started":"2024-01-16T02:23:41.775886Z","shell.execute_reply":"2024-01-16T02:24:38.202563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train_df.head())\ndisplay(test_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:25:35.378456Z","iopub.execute_input":"2024-01-16T02:25:35.378823Z","iopub.status.idle":"2024-01-16T02:25:35.409351Z","shell.execute_reply.started":"2024-01-16T02:25:35.378793Z","shell.execute_reply":"2024-01-16T02:25:35.408446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_votes = train_df.columns[-6:]\n\ntrain = train_df.groupby('eeg_id')[['spectrogram_id','spectrogram_label_offset_seconds', 'patient_id' ,'expert_consensus']].agg(\n    {'spectrogram_id': 'first',\n     'spectrogram_label_offset_seconds': ['min', 'max'],\n     'patient_id': 'first',\n     'expert_consensus': 'first',\n    })\n\ntrain.columns = ['spec_id','spec_offset_min', 'spec_offset_max', 'patient_id', 'target']\n\ntmp = train_df.groupby('eeg_id')[train_votes].agg('sum') # get sum per vote for each eeg\ntmp[train_votes] = tmp[train_votes].div(tmp[train_votes].sum(axis=1), axis=0) # convert into probabilities\n\ntrain[train_votes] = tmp\ntrain = train[[col for col in train if col != 'target'] + ['target']] # put target variable at the end for more readability\ntrain = train.reset_index()\n\ndisplay(train.head())","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:25:38.536876Z","iopub.execute_input":"2024-01-16T02:25:38.537248Z","iopub.status.idle":"2024-01-16T02:25:38.630303Z","shell.execute_reply.started":"2024-01-16T02:25:38.537210Z","shell.execute_reply":"2024-01-16T02:25:38.629393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Denoising function\n\nCan be useful later to generate spectrogram from EEG","metadata":{}},{"cell_type":"code","source":"def 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    ret = {key:[] for key in x.columns}\n    \n    for pos in x.columns:\n        coeff = pywt.wavedec(x[pos], 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[pos]=pywt.waverec(coeff, wavelet, mode='per')\n    \n    return pd.DataFrame(ret)\n\n# eeg_denoised = denoise(eeg, wavelet=\"db8\")","metadata":{"execution":{"iopub.status.busy":"2024-01-15T18:07:41.616059Z","iopub.execute_input":"2024-01-15T18:07:41.616462Z","iopub.status.idle":"2024-01-15T18:07:41.624042Z","shell.execute_reply.started":"2024-01-15T18:07:41.616429Z","shell.execute_reply":"2024-01-15T18:07:41.623081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Ploting function","metadata":{}},{"cell_type":"code","source":"# 2366870, 2259539799\ndef eeg_info(eeg_id=36718960):\n    eeg_df = train_df[train_df[\"eeg_id\"] == eeg_id].reset_index(drop=True)\n    spec = pd.read_parquet(f'/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/{eeg_df[\"spectrogram_id\"].values[0]}.parquet')    \n\n    eeg = pd.read_parquet(f'/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/{eeg_id}.parquet')\n    \n    display(eeg_df)\n    \n    # PLOT THE SUB EEG TIME REPRESENTATION\n    plt.figure(figsize=(20, min(10, len(eeg_df))))\n    bars = plt.barh(y=eeg_df['eeg_sub_id'], width=50, left=eeg_df['eeg_label_offset_seconds'], color='plum')\n    \n    y_min, y_max = plt.ylim()\n    \n    for i,row in eeg_df.iterrows():\n        start_mid_range = row['eeg_label_offset_seconds'] + 50/2 - 5\n        end_mid_range = row['eeg_label_offset_seconds'] + 50/2 + 5\n        \n        ymin, ymax = (y_max - 0.4 - i)/(abs(y_min) + y_max), (y_max + 0.4 - i)/(abs(y_min) + y_max)\n        plt.axvspan(xmin=start_mid_range, xmax=end_mid_range, ymin=ymin, ymax=ymax, alpha=0.2, color='blue', label='10sec vote zone' if i == 0 else None)\n        plt.text(25 + eeg_df['eeg_label_offset_seconds'].iloc[i], i+0.05, f'{eeg_df[\"expert_consensus\"].iloc[i]}', color='white', fontweight='bold', horizontalalignment='center')\n    \n    \n    plt.gca().invert_yaxis()\n    plt.ylabel('EEG sub id')\n    plt.xlabel('Time (seconds)')\n    \n    max_offset = int(eeg_df[\"eeg_label_offset_seconds\"].max())\n    eeg_time = max_offset + 50\n                     \n    plt.title(f'EEG {eeg_id} during {eeg_time} seconds')\n    plt.legend()\n    plt.show()\n    \n    \n    # PLOT THE EEG VOTES \n    #display(eeg.head(3))\n    \n    plt.figure(figsize=(20,5))\n    plt.plot(eeg['Fp1'])\n    \n    for i,row in eeg_df.iterrows():\n        plt.axvline(x=(row['eeg_label_offset_seconds']*200),color='green', label='Start of subsample' if i == 0 else None)    \n        plt.axvline(x=((row['eeg_label_offset_seconds']+50)*200),color='red', label='End of subsample' if i == 0 else None)  \n    \n    plt.title(f'EEG - Fp1 having {eeg_time}s * 200 samples/s = {eeg_time*200} samples')\n    plt.xlim(xmin=0)\n    plt.grid()\n    plt.legend()\n    plt.show()\n    \n    \n    # PLOT THE SPECTROGRAM\n    #display(spec.head(3))\n               \n    max_spec_offset = int(eeg_df['spectrogram_label_offset_seconds'].max())\n    \n    plt.figure(figsize=(20,5))\n    plt.plot(spec['time'], spec['LL_0.59'])\n    \n    for i,row in eeg_df.iterrows():\n        plt.axvline(x=row['spectrogram_label_offset_seconds'], color='green', label='Start of subsample' if i == 0 else None)   \n        plt.axvline(x=((row['spectrogram_label_offset_seconds'])+600),color='red', label='End of subsample' if i == 0 else None)\n    \n    spec[\"time\"]\n    plt.title(f'Spectrogram - LL_0.59 during {spec[\"time\"].values[-1]} seconds')\n    plt.xlim(xmin=0)\n    plt.ylabel('Frequency (Hz)')\n    plt.xlabel('Time (seconds)')\n    plt.legend()\n    plt.grid()\n    plt.show()\n\neeg_info(2366870)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:26:00.238861Z","iopub.execute_input":"2024-01-16T02:26:00.239193Z","iopub.status.idle":"2024-01-16T02:26:01.431607Z","shell.execute_reply.started":"2024-01-16T02:26:00.239166Z","shell.execute_reply":"2024-01-16T02:26:01.430460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Some facts:**<br>\n    - Experts vote on the 10 middle seconds of each sub EEG<br>\n    - We have overlapping sub EEG<br>\n    - Votes seems to be majoritarly unique between each sub EEG<br>\n    - Expert consensus can be different in overlaping sub EEG during some seconds<br>","metadata":{}},{"cell_type":"markdown","source":"## Spectrogram working progress","metadata":{}},{"cell_type":"markdown","source":"#### The following code iterate through each row in train and preprocess 10 minutes of the corresponding spectrogram","metadata":{}},{"cell_type":"code","source":"channel_map = {0: \"LL\", 1: \"LP\", 2: \"RR\", 3: \"RP\"}\ntarget_map = {'Seizure': 0, 'LPD': 1, 'GPD': 2, 'LRDA': 3, 'GRDA': 4, 'Other': 5}\ni_target_map = {y:x for x,y in target_map.items()} # inverted target map\n\nIMG_WIDTH = 256\nIMG_HEIGHT = 128\n\nX_spec = np.zeros((len(train), IMG_HEIGHT, IMG_WIDTH, 4), dtype='float32')\ny_spec = np.zeros((len(train)), dtype='int8')\n\n\ndef plot_spectrogram(index):\n    for channel in range(4):\n        img = X_spec[index,:,:,channel]\n        plt.figure(figsize=(10, 2))\n        plt.imshow(img, aspect='auto', cmap='viridis', origin='lower')\n        plt.xlabel('Time [sec]')\n        plt.ylabel('Frequency [Hz]')\n        plt.title(f'Spectrogram {train[index].spec_id} - {channel_map[channel]} : {i_target_map[y_spec[index]]}')\n        plt.show()\n        \ndef generate_spectrograms():\n    for row in train.itertuples(): # for each row in train\n        i = row[0] # get index of the row in train\n        \n        spec_id = row.spec_id\n        spec = spectrograms.item()[spec_id]\n        \n        r = np.random.randint(row.spec_offset_min, row.spec_offset_max+1)//2\n\n        for k in range(4): # 4 different channel (LL, LP, RR, RP)\n            img = spec[r:r+300, k*100:(k+1)*100].T # get 10 random minutes of the spectrogram for each channel\n\n            # log scaling\n            img = np.clip(img, np.exp(-4), np.exp(8)) # avoid 0 for log\n            img = np.log(img)\n\n            # z-score scaling\n            ep = 1e-6\n            mean = np.nanmean(img.flatten())\n            std = np.nanstd(img.flatten())\n            img = (img - mean) / (std + ep)\n            \n            # convert nan to value 0\n            img = np.nan_to_num(img, nan=0.0)\n\n            # crop to size 256\n            img = img[:,22:-22] # 300-22-22=256\n\n            X_spec[i, 14:-14,:, k] = img # 14:-14 to put the IMG_HEIGHT (100) into 128\n            y_spec[i] = target_map[row.target]\n    \ngenerate_spectrograms()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:29:35.635115Z","iopub.execute_input":"2024-01-16T02:29:35.635973Z","iopub.status.idle":"2024-01-16T02:30:38.992157Z","shell.execute_reply.started":"2024-01-16T02:29:35.635939Z","shell.execute_reply":"2024-01-16T02:30:38.991379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Finally, we have 17 089 spectrograms, each one corresponding to an eeg_id**","metadata":{}},{"cell_type":"code","source":"plot_spectrogram(0)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:31:16.320965Z","iopub.execute_input":"2024-01-16T02:31:16.321337Z","iopub.status.idle":"2024-01-16T02:31:17.514061Z","shell.execute_reply.started":"2024-01-16T02:31:16.321308Z","shell.execute_reply":"2024-01-16T02:31:17.512983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Now we will try to create the CNN model","metadata":{}},{"cell_type":"code","source":"def build_cnn_model(lr=1e-3): # impossible because to much RAM used\n    model = Sequential()\n    \n    model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(128,256,4)))\n    model.add(BatchNormalization())\n    model.add(MaxPooling2D((2, 2), strides=1))\n    model.add(Conv2D(64, (3, 3), activation='relu'))\n    model.add(BatchNormalization())\n    model.add(MaxPooling2D((2, 2), strides=1))\n    \n    model.add(GlobalAveragePooling2D()) # avoid overfitting and because we have a lot of parameters per image\n    \n    model.add(Dense(len(set(y_spec)), activation='softmax', dtype='float32')) # to predict probability for each target class\n    \n    model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(lr=lr), metrics=['KLDivergence'])\n    \n    return model\n\nmodel = build_cnn_model()\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:31:34.490178Z","iopub.execute_input":"2024-01-16T02:31:34.490879Z","iopub.status.idle":"2024-01-16T02:31:34.609190Z","shell.execute_reply.started":"2024-01-16T02:31:34.490845Z","shell.execute_reply":"2024-01-16T02:31:34.608336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(X_spec, y_spec, test_size=0.2, random_state=42, shuffle=False)\n\nhistory = model.fit(X_train, y_train, batch_size=256, epochs=10, validation_data=0.2)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T02:32:13.811913Z","iopub.execute_input":"2024-01-16T02:32:13.812588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## EEG working progress\n\nCreate dataset for eeg","metadata":{}},{"cell_type":"code","source":"eeg_feature_df = []\nfor row in train.itertuples():\n    eeg_id = row.eeg_id\n    eeg_df = train_df[train_df[\"eeg_id\"] == eeg_id].reset_index(drop=True)\n    eeg = pd.read_parquet(f'/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/{eeg_id}.parquet')\n\n    ranges = [(int(200 * offset + 4000), int(200 * offset + 6000)) for offset in eeg_df['eeg_label_offset_seconds']]\n    filtered_eeg = pd.concat([eeg.iloc[s:e].mean().to_frame().T for s, e in ranges]).reset_index(drop=True)\n    filtered_eeg['target'] = eeg_df['expert_consensus']\n    filtered_eeg.insert(0, 'eeg_id', eeg_df['eeg_id'])\n    filtered_eeg.insert(1, 'spec_id', eeg_df['spectrogram_id'])\n\n    eeg_feature_df.append(filtered_eeg)\n        \neeg_feature_df = pd.concat(eeg_feature_df).reset_index(drop=True)\ndisplay(eeg_feature_df)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T21:43:43.693749Z","iopub.execute_input":"2024-01-15T21:43:43.694150Z","iopub.status.idle":"2024-01-15T21:52:42.162303Z","shell.execute_reply.started":"2024-01-15T21:43:43.694113Z","shell.execute_reply":"2024-01-15T21:52:42.161131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Processing","metadata":{}},{"cell_type":"code","source":"eeg_feature_df = eeg_feature_df.drop(['eeg_id', 'spec_id'], axis=1)\n\nle = LabelEncoder()\neeg_feature_df['expert_consensus'] = le.fit_transform(eeg_feature_df['expert_consensus'])\n\neeg_feature_df = eeg_feature_df.drop_duplicates()\n\ndisplay(eeg_feature_df)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T22:10:18.980279Z","iopub.execute_input":"2024-01-15T22:10:18.980728Z","iopub.status.idle":"2024-01-15T22:10:19.217359Z","shell.execute_reply.started":"2024-01-15T22:10:18.980691Z","shell.execute_reply":"2024-01-15T22:10:19.216275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"rs = RobustScaler()\n\n\nX = eeg_feature_df.drop(\"expert_consensus\", axis=1)\ny = eeg_feature_df[\"expert_consensus\"]\n\nX_scaled = pd.DataFrame(rs.fit_transform(X), columns=X.columns)\n\nX_train, X_val, y_train, y_val = train_test_split(X_scaled, y, test_size=0.1, random_state=42)\n\nnum_classes = len(set(y))","metadata":{"execution":{"iopub.status.busy":"2024-01-15T22:21:50.118698Z","iopub.execute_input":"2024-01-15T22:21:50.119143Z","iopub.status.idle":"2024-01-15T22:21:50.264660Z","shell.execute_reply.started":"2024-01-15T22:21:50.119107Z","shell.execute_reply":"2024-01-15T22:21:50.263274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_nn_model(num_neurons=64, learning_rate=1.0e-02, act='swish', dropout=0.1):\n    model = Sequential()\n\n    \n    model.add(Dense(num_neurons, activation=act, input_dim=X_train.shape[1]))\n    \n    model.add(Dense(num_neurons, activation=act))\n    model.add(Dropout(dropout))\n    model.add(BatchNormalization())\n\n    model.add(Dense(num_neurons, activation=act))\n    model.add(Dropout(dropout))\n    model.add(BatchNormalization())\n\n    model.add(Dense(int(num_neurons//2), activation=act))\n    model.add(BatchNormalization())\n\n    model.add(Dense(int(num_neurons//4), activation=act))\n    model.add(BatchNormalization())\n\n    model.add(Dense(num_classes, activation='softmax'))\n\n    model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(lr=learning_rate), metrics=['accuracy'])\n    \n    return model\n\nmodel = create_nn_model()\n\nhistory = model.fit(X_train, y_train, epochs=100, batch_size=16, validation_split=0.2)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T22:33:51.049440Z","iopub.execute_input":"2024-01-15T22:33:51.049876Z","iopub.status.idle":"2024-01-15T23:03:16.168563Z","shell.execute_reply.started":"2024-01-15T22:33:51.049845Z","shell.execute_reply":"2024-01-15T23:03:16.166719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_egg_id = 3911565283\ntest_spec_id = 853520\n\neeg_test = pd.read_parquet(f'/kaggle/input/hms-harmful-brain-activity-classification/test_eegs/{test_egg_id}.parquet')    \nplt.figure(figsize=(20,5))\nplt.plot(eeg_test['Fp1'])\n\nspec_test = pd.read_parquet(f'/kaggle/input/hms-harmful-brain-activity-classification/test_spectrograms/{test_spec_id}.parquet')    \nplt.figure(figsize=(20,5))\nplt.plot(spec_test['time'], spec_test['LL_0.59'])","metadata":{"execution":{"iopub.status.busy":"2024-01-15T23:06:31.284169Z","iopub.execute_input":"2024-01-15T23:06:31.284647Z","iopub.status.idle":"2024-01-15T23:06:31.911395Z","shell.execute_reply.started":"2024-01-15T23:06:31.284607Z","shell.execute_reply":"2024-01-15T23:06:31.910500Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"eeg_test_denoised = denoise(eeg_test, wavelet=\"db8\")\neeg_test_denoised = eeg_test_denoised.iloc[4000:6000].mean().to_frame().T\n\neeg_test_scaled = rs.transform(eeg_test_denoised)\n\nresult = model.predict(eeg_test_scaled)\nprint(result)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T23:23:15.540149Z","iopub.execute_input":"2024-01-15T23:23:15.540672Z","iopub.status.idle":"2024-01-15T23:23:15.660020Z","shell.execute_reply.started":"2024-01-15T23:23:15.540634Z","shell.execute_reply":"2024-01-15T23:23:15.658858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission[train_votes] = result\nsubmission.to_csv('submission.csv')","metadata":{"execution":{"iopub.status.busy":"2024-01-15T23:11:01.732136Z","iopub.execute_input":"2024-01-15T23:11:01.732623Z","iopub.status.idle":"2024-01-15T23:11:01.741967Z","shell.execute_reply.started":"2024-01-15T23:11:01.732583Z","shell.execute_reply":"2024-01-15T23:11:01.740692Z"},"trusted":true},"execution_count":null,"outputs":[]}]}