├── 10-20_system.mat ├── EEG_Channel_Interpolation_arxiv.pdf ├── README.md ├── baselines ├── __pycache__ │ └── ecr_baseline.cpython-37.pyc ├── ecr_baseline.py ├── ecr_ssp.py ├── maps │ └── 10-20_system.mat └── ssp_run.sh ├── example.ipynb ├── figure ├── Figure_overview.png ├── architecture.png ├── baseline_res.pdf ├── data_split_bw.pdf └── ecr_figure3_3_gimp.pdf ├── model ├── topology │ └── model.json └── weights │ ├── nn_weights-400.hdf5 │ └── nn_weights-800.hdf5 ├── train ├── ecr_cnn.py ├── ecr_hyper_parameters.npy ├── ecr_loadModel.py ├── run.sh └── run_loadModel.sh └── transfer ├── ecr_transfer.py └── run_transfer.sh /10-20_system.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/10-20_system.mat -------------------------------------------------------------------------------- /EEG_Channel_Interpolation_arxiv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/EEG_Channel_Interpolation_arxiv.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EEG Channel Interpolation Using Deep Encoder-decoder Networks 2 | [Sari Saba-Sadiya](https://cse.msu.edu/~sadiyasa/)1, 3 | [Taosheng Liu](https://npal.psy.msu.edu/)1, 4 | [Tuka Alhanai](https://talhanai.xyz/)2, 5 | [Mohammad Ghassemi](https://ghassemi.xyz/)1
6 | 1 Michigan State University 2 New York University Abu Dhabi 7 | 8 | Code for the paper ["EEG Channel Interpolation Using Deep Encoder-decoder Networks"](https://arxiv.org/abs/2009.12244), presented in BIBM-DLB2H'2020. 9 | 10 | ## Overview: 11 | The code implemented here can be used to interpolate `poped' EEG channels. Though not restricted to any specific EEG acquisition setup, the encoder-decoder in this particular implementation was trained on data collected using a 500Hz international 10-20 system. The EEG data is first segmented into 16ms and transformed into an 8x8x8 tensor before being piped through the encoder-decoder trained to interpolate missing channels. 12 | 13 | 14 | 15 | ## Youtube Demo 16 | The recorded presentation contains a theoretical part and a demo that starts at 9:40s. 17 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/jLRZeJg5viM/0.jpg)](https://youtu.be/jLRZeJg5viM?t=580) 18 | 19 | ## Transfer learning 20 | The performance of the trained model (available in `model`) can be further improved using transfer learning on the specific dataset you are using. See `transfer/ecr_transfer.py` for an example. 21 | 22 | ## Contents: 23 | * `train`: 24 | * `ecr_cnn.py`: The code you need to compile train and run the neural networks 25 | * `ecr_hyper_parameters.npy` 26 | * `ecr_loadModel`: load the trained model and run it to interpolate on non-training data. 27 | * `run.sh` code to run the training 28 | * `baselines`: 29 | * `ecr_baseline.py`: The code to calculate the EDP and EGL baselines. 30 | * `ecr_ssp.py`: The code to calculate the spherical splines baseline. 31 | * `transfer`: 32 | * `ecr_transfer.py`: The code for transfer learning 33 | * `run_transfer.sh` 34 | * `README.txt`: This file. 35 | 36 | ## Cite 37 | ``` 38 | @INPROCEEDINGS{9312979, 39 | author={S. {Saba-Sadiya} and T. {Alhanai} and T. {Liu} and M. M. {Ghassemi}}, 40 | booktitle={2022 IEEE International Conference on Bioinformatics and Biomedicine (BIBM)}, 41 | title={EEG Channel Interpolation Using Deep Encoder-decoder Networks}, 42 | year={2020}, 43 | volume={}, 44 | number={}, 45 | pages={2432-2439}, 46 | doi={10.1109/BIBM49941.2020.9312979} 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /baselines/__pycache__/ecr_baseline.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/baselines/__pycache__/ecr_baseline.cpython-37.pyc -------------------------------------------------------------------------------- /baselines/ecr_baseline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import scipy.io 6 | import glob 7 | import numpy as np 8 | import keras 9 | import scipy.stats 10 | import vincenty 11 | import sys 12 | 13 | def load_val_data(fold): 14 | l = range(10) 15 | trnIdx = [fold] 16 | trn = [] 17 | for ii in trnIdx: 18 | foldData = np.load('ecr_data2_'+str(ii)+'.npy', allow_pickle=True)[()] 19 | for subjData in foldData: 20 | for ttData in foldData[subjData]: 21 | [org,data] = foldData[subjData][ttData] 22 | trn.append(org) 23 | return trn 24 | 25 | class baselines: 26 | def __init__(self,orgEEG,system='10-20_system'): 27 | 28 | ignoreCh = set(['EEGA2A1','EDFAnnotations','ECGECG']); 29 | 30 | self.sysMap = scipy.io.loadmat('./baselines/maps/'+system+'.mat') 31 | mapChan = list(set([ channel[0] for channel in self.sysMap['map'].flatten().tolist()])) 32 | self.eegChan = [orgEEG['chanlocs'][0][0,ii][0][0].replace(' ','').replace('-','') for ii in range(np.size(orgEEG['chanlocs'][0]))] 33 | distLabels = [x[0] for x in list(orgEEG['chanlocs'][0].dtype.fields.items())] 34 | xIdx = distLabels.index('X') 35 | yIdx = distLabels.index('Y') 36 | zIdx = distLabels.index('Z') 37 | thtIdx = distLabels.index('sph_theta') 38 | phiIdx = distLabels.index('sph_phi') 39 | self.distances = {} 40 | for ii,chan in enumerate(self.eegChan): 41 | if chan in ignoreCh: 42 | continue 43 | self.distances[chan] = {} 44 | self.distances[chan]['x'] = float(orgEEG['chanlocs'][0][0,ii][xIdx]) 45 | self.distances[chan]['y'] = float(orgEEG['chanlocs'][0][0,ii][yIdx]) 46 | self.distances[chan]['z'] = float(orgEEG['chanlocs'][0][0,ii][zIdx]) 47 | self.distances[chan]['tht'] = np.radians(float(orgEEG['chanlocs'][0][0,ii][thtIdx])) 48 | self.distances[chan]['phi'] = np.radians(float(orgEEG['chanlocs'][0][0,ii][phiIdx])) 49 | return 50 | 51 | def broadcast_to_8x8(self,A): 52 | B = np.hstack((A[:,:2,:],np.tile(A[:,2,:].reshape(5,1,10),(1,2,1)),A[:,3:,:])) 53 | C = np.vstack((B[:2,:,:],np.tile(B[2,:,:].reshape(1,6,10),(2,1,1)),B[3:,:,:])) 54 | D = A[0,0,:].reshape((1,1,10)) #A1-A2 ear channel 55 | E = A[4,4,:].reshape((1,1,10)) #A1-A2 ear channel 56 | F = np.array(np.vstack((np.hstack((D,E)),np.hstack((E,D))))) 57 | G = np.tile(F,(4,4,1)) 58 | G[1:7,1:7,:] = C 59 | return G[:,:,:8] 60 | 61 | def normalize_data(self,orgEEG,startIdx,endIdx): 62 | EEG5x5 = [] 63 | EEG8x8 = [] 64 | full_data = orgEEG['data'][0] 65 | for epoch in range(startIdx,endIdx): 66 | EEG5x5.append(np.zeros((5,5,10))) 67 | for ii in range(orgEEG['data'][0].shape[0]): 68 | chan_name = orgEEG['chanlocs'][0][0][ii][0][0].replace(" ","").replace("-","") 69 | xy_tulip = np.where(self.sysMap['map']==chan_name) 70 | for jj in range(np.size(xy_tulip[0])): 71 | x = xy_tulip[0][jj] 72 | y = xy_tulip[1][jj] 73 | EEG5x5[-1][x,y,:] = full_data[ii,8*epoch:8*(epoch+1)+2] 74 | EEG5x5[-1] = scipy.stats.zscore(EEG5x5[-1],2) 75 | EEG8x8.append(self.broadcast_to_8x8(EEG5x5[-1])) 76 | return EEG8x8 77 | 78 | def EUD(self,EEG,p=1): 79 | mse = [] 80 | for intChan in self.distances.keys(): 81 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 82 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 83 | newChan = np.zeros(np.shape(EEG)[2]) 84 | normChan = 0 85 | for chan in self.distances.keys(): 86 | dij = 0 87 | if chan == intChan: 88 | continue 89 | dij += (self.distances[chan]['x']-self.distances[intChan]['x'])**2 90 | dij += (self.distances[chan]['y']-self.distances[intChan]['y'])**2 91 | dij += (self.distances[chan]['z']-self.distances[intChan]['z'])**2 92 | dij = np.sqrt(dij) 93 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 94 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 95 | normChan += 1/(dij**p) 96 | newChan = newChan / normChan 97 | orgChan = scipy.stats.zscore(orgChan) 98 | newChan = scipy.stats.zscore(newChan) 99 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 100 | return mse 101 | 102 | def GCD(self,EEG): 103 | mse = [] 104 | for intChan in self.distances.keys(): 105 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 106 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 107 | newChan = np.zeros(np.shape(EEG)[2]) 108 | for chan in self.distances.keys(): 109 | dij = 0 110 | if chan == intChan: 111 | continue 112 | dij += np.sin((self.distances[intChan]['phi']-self.distances[chan]['phi'])/2)**2 113 | dij += np.cos(self.distances[intChan]['phi'])*self.distances[chan]['phi']*np.sin((self.distances[intChan]['tht']-self.distances[chan]['tht'])/2)**2 114 | #print(dij) 115 | dij = np.sqrt(dij) 116 | dij = 2*np.arcsin(dij) 117 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 118 | newChan += dij*EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 119 | orgChan = scipy.stats.zscore(orgChan) 120 | newChan = scipy.stats.zscore(newChan) 121 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 122 | return mse 123 | 124 | def EGL(self,EEG,p=1): 125 | mse = [] 126 | for intChan in self.distances.keys(): 127 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 128 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 129 | newChan = np.zeros(np.shape(EEG)[2]) 130 | normChan = 0 131 | for chan in self.distances.keys(): 132 | dij = 0 133 | if chan == intChan: 134 | continue 135 | dij = vincenty.vincenty((self.distances[intChan]['phi'],self.distances[intChan]['tht']),(self.distances[chan]['phi'],self.distances[chan]['tht'])) 136 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 137 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 138 | normChan += 1/(dij**p) 139 | newChan = newChan / normChan 140 | orgChan = scipy.stats.zscore(orgChan) 141 | newChan = scipy.stats.zscore(newChan) 142 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 143 | return mse 144 | 145 | def new_EUD(self,EEG,ii,jj,p=1): 146 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 147 | newChan = np.zeros(np.shape(EEG)[2]) 148 | intChan = self.sysMap['map'][ii,jj][0] 149 | normChan = 0 150 | for chan in self.distances.keys(): 151 | dij = 0 152 | if chan == intChan: 153 | continue 154 | dij += (self.distances[chan]['x']-self.distances[intChan]['x'])**2 155 | dij += (self.distances[chan]['y']-self.distances[intChan]['y'])**2 156 | dij += (self.distances[chan]['z']-self.distances[intChan]['z'])**2 157 | dij = np.sqrt(dij) 158 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 159 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 160 | normChan += 1/(dij**p) 161 | newChan = newChan / normChan 162 | orgChan = scipy.stats.zscore(orgChan) 163 | newChan = scipy.stats.zscore(newChan) 164 | return newChan 165 | 166 | def new_EGL(self,EEG,ii,jj,p=1): 167 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 168 | newChan = np.zeros(np.shape(EEG)[2]) 169 | intChan = self.sysMap['map'][ii,jj][0] 170 | normChan = 0 171 | for chan in self.distances.keys(): 172 | dij = 0 173 | if chan == intChan: 174 | continue 175 | dij = vincenty.vincenty((self.distances[intChan]['phi'],self.distances[intChan]['tht']),\ 176 | (self.distances[chan]['phi'],self.distances[chan]['tht'])) 177 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 178 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 179 | normChan += 1/(dij**p) 180 | newChan = newChan / normChan 181 | orgChan = scipy.stats.zscore(orgChan) 182 | newChan = scipy.stats.zscore(newChan) 183 | return newChan 184 | 185 | def occlude(self,EEGList,ii,jj): 186 | popEEG = [] 187 | for EEG in EEGList: 188 | A = EEG.copy() 189 | A[ii,jj,:] = np.zeros((1,1,8)) 190 | popEEG.append(A) 191 | return popEEG 192 | 193 | def calc_mse(orgEEG,intEEG): 194 | mse = list() 195 | for iChan in range(19): 196 | orgChanData = orgEEG[0]['data'][iChan] 197 | intChanData = intEEG[iChan]['data'][0,0][iChan] 198 | mse.append(np.array(keras.losses.mean_squared_error(orgChanData,intChanData))) 199 | return np.mean(mse) 200 | 201 | if __name__ == "__main__": 202 | # The fold here is the data file, devide the data for easire parallelization 203 | fold = int(sys.argv[1]) 204 | p = 5 #power param between -4 and 4 205 | 206 | # This file is from the original data and simply carries meta 207 | # information such as channel names and locations 208 | files = glob.glob('./subject*_1_intrp.mat') 209 | 210 | orgEEG = scipy.io.loadmat(files[0])['EEG'][0] 211 | 212 | baseline = baselines(orgEEG) 213 | trn = load_val_data(fold) 214 | EUD_trn_res = [] 215 | EGL_trn_res = [] 216 | for dat in trn: 217 | EUD_trn_res.append(np.mean(baseline.EUD(dat,p))) 218 | EGL_trn_res.append(np.mean(baseline.EGL(dat,p))) 219 | np.save('./EUD_data'+str(fold)+'_p'+str(p),EUD_trn_res) 220 | np.save('./EGL_data'+str(fold)+'_p'+str(p),EGL_trn_res) 221 | -------------------------------------------------------------------------------- /baselines/ecr_ssp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import scipy.io 6 | import glob 7 | import numpy as np 8 | import scipy.stats 9 | import json 10 | import time 11 | import os 12 | import sys 13 | from scipy.special import legendre 14 | import keras 15 | 16 | 17 | fold = int(sys.argv[1]) 18 | 19 | 20 | def load_val_data(fold): 21 | l = range(10) 22 | trnIdx = [fold] 23 | trn = [] 24 | for ii in trnIdx: 25 | foldData = np.load('ecr_data_'+str(ii)+'.npy', allow_pickle=True)[()] 26 | for subjData in foldData: 27 | for ttData in foldData[subjData]: 28 | [org,data] = foldData[subjData][ttData] 29 | trn.append(org) 30 | return trn 31 | 32 | 33 | class baselines: 34 | # for ssp function following: 35 | # https://github.com/openroc/eeglab/blob/master/branches/ 36 | # eeglab10/external/bioelectromagnetism_ligth/eeg_lap_sph_spline.m 37 | def __init__(self,orgEEG,system='10-20_system'): 38 | 39 | ignoreCh = set(['EEGA2A1','EDFAnnotations','ECGECG']); 40 | 41 | self.sysMap = scipy.io.loadmat('../../ECR/maps/'+system+'.mat') 42 | mapChan = list(set([ channel[0] for channel in self.sysMap['map'].flatten().tolist()])) 43 | self.eegChan = [orgEEG['chanlocs'][0][0,ii][0][0].replace(' ','').replace('-','') for ii in range(np.size(orgEEG['chanlocs'][0]))] 44 | distLabels = [x[0] for x in list(orgEEG['chanlocs'][0].dtype.fields.items())] 45 | xIdx = distLabels.index('X') 46 | yIdx = distLabels.index('Y') 47 | zIdx = distLabels.index('Z') 48 | thtIdx = distLabels.index('sph_theta') 49 | phiIdx = distLabels.index('sph_phi') 50 | self.distances = {} 51 | for ii,chan in enumerate(self.eegChan): 52 | if chan in ignoreCh: 53 | continue 54 | self.distances[chan] = {} 55 | self.distances[chan]['x'] = float(orgEEG['chanlocs'][0][0,ii][xIdx]) 56 | self.distances[chan]['y'] = float(orgEEG['chanlocs'][0][0,ii][yIdx]) 57 | self.distances[chan]['z'] = float(orgEEG['chanlocs'][0][0,ii][zIdx]) 58 | self.distances[chan]['tht'] = np.radians(float(orgEEG['chanlocs'][0][0,ii][thtIdx])) 59 | self.distances[chan]['phi'] = np.radians(float(orgEEG['chanlocs'][0][0,ii][phiIdx])) 60 | self.sort_chan = list(self.distances.keys()) 61 | self.sort_chan.sort() 62 | self.m = 4 63 | self.calc_cosines() 64 | self.calc_g() 65 | return 66 | 67 | def EUD(self,EEG,p=1): 68 | mse = [] 69 | for intChan in self.distances.keys(): 70 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 71 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 72 | newChan = np.zeros(np.shape(EEG)[2]) 73 | normChan = 0 74 | for chan in self.distances.keys(): 75 | dij = 0 76 | if chan == intChan: 77 | continue 78 | dij += (self.distances[chan]['x']-self.distances[intChan]['x'])**2 79 | dij += (self.distances[chan]['y']-self.distances[intChan]['y'])**2 80 | dij += (self.distances[chan]['z']-self.distances[intChan]['z'])**2 81 | dij = np.sqrt(dij) 82 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 83 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 84 | normChan += 1/(dij**p) 85 | newChan = newChan / normChan 86 | orgChan = scipy.stats.zscore(orgChan) 87 | newChan = scipy.stats.zscore(newChan) 88 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 89 | return mse 90 | 91 | def new_EUD(self,EEG,ii,jj,p=1): 92 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 93 | newChan = np.zeros(np.shape(EEG)[2]) 94 | intChan = self.sysMap['map'][ii,jj][0] 95 | normChan = 0 96 | for chan in self.distances.keys(): 97 | dij = 0 98 | if chan == intChan: 99 | continue 100 | dij += (self.distances[chan]['x']-self.distances[intChan]['x'])**2 101 | dij += (self.distances[chan]['y']-self.distances[intChan]['y'])**2 102 | dij += (self.distances[chan]['z']-self.distances[intChan]['z'])**2 103 | dij = np.sqrt(dij) 104 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 105 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 106 | normChan += 1/(dij**p) 107 | newChan = newChan / normChan 108 | orgChan = scipy.stats.zscore(orgChan) 109 | newChan = scipy.stats.zscore(newChan) 110 | return newChan 111 | 112 | def GCD(self,EEG): 113 | mse = [] 114 | for intChan in self.distances.keys(): 115 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 116 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 117 | newChan = np.zeros(np.shape(EEG)[2]) 118 | for chan in self.distances.keys(): 119 | dij = 0 120 | if chan == intChan: 121 | continue 122 | dij += np.sin((self.distances[intChan]['phi']-self.distances[chan]['phi'])/2)**2 123 | dij += np.cos(self.distances[intChan]['phi'])*self.distances[chan]['phi']* np.sin((self.distances[intChan]['tht']-self.distances[chan]['tht'])/2)**2 124 | #print(dij) 125 | dij = np.sqrt(dij) 126 | dij = 2*np.arcsin(dij) 127 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 128 | newChan += dij*EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 129 | orgChan = scipy.stats.zscore(orgChan,2) 130 | newChan = scipy.stats.zscore(newChan,2) 131 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 132 | return mse 133 | 134 | def EGL(self,EEG): 135 | mse = [] 136 | for intChan in self.distances.keys(): 137 | [ii],[jj]=np.where(self.sysMap['map'] == [intChan]) 138 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 139 | newChan = np.zeros(np.shape(EEG)[2]) 140 | for chan in self.distances.keys(): 141 | dij = 0 142 | if chan == intChan: 143 | continue 144 | dij = vincenty.vincenty((self.distances[intChan]['phi'],self.distances[intChan]['tht']), (self.distances[chan]['phi'],self.distances[chan]['tht'])) 145 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 146 | newChan += dij*EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 147 | orgChan = scipy.stats.zscore(orgChan) 148 | newChan = scipy.stats.zscore(newChan) 149 | mse.append(float(keras.losses.mean_squared_error(orgChan,newChan))) 150 | return mse 151 | 152 | def new_EGL(self,EEG,ii,jj,p=1): 153 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 154 | newChan = np.zeros(np.shape(EEG)[2]) 155 | intChan = self.sysMap['map'][ii,jj][0] 156 | normChan = 0 157 | for chan in self.distances.keys(): 158 | dij = 0 159 | if chan == intChan: 160 | continue 161 | dij = vincenty.vincenty((self.distances[intChan]['phi'],self.distances[intChan]['tht']), (self.distances[chan]['phi'],self.distances[chan]['tht'])) 162 | [ii],[jj]=np.where(self.sysMap['map'] == [chan]) 163 | newChan += EEG[ii,jj,:].reshape(np.shape(EEG)[2]) / (dij**p) 164 | normChan += 1/(dij**p) 165 | newChan = newChan / normChan 166 | orgChan = scipy.stats.zscore(orgChan) 167 | newChan = scipy.stats.zscore(newChan) 168 | return newChan 169 | 170 | def calc_cosines(self): 171 | self.cosines = np.zeros((len(self.sort_chan),len(self.sort_chan))) 172 | for ii,chanA in enumerate(self.sort_chan): 173 | for jj,chanB in enumerate(self.sort_chan): 174 | x1 = self.distances[chanA]['x'] 175 | y1 = self.distances[chanA]['y'] 176 | z1 = self.distances[chanA]['z'] 177 | x2 = self.distances[chanB]['x'] 178 | y2 = self.distances[chanB]['y'] 179 | z2 = self.distances[chanB]['z'] 180 | self.cosines[ii,jj] = (x1*x2+y1*y2+z1*z2)/ (np.sqrt(x1**2 + y1**2 + z1**2)*np.sqrt(x2**2 + y2**2 + z2**2)) 181 | return 182 | 183 | def calc_g(self): 184 | self.g = np.zeros((len(self.sort_chan),len(self.sort_chan))) 185 | for p in range(1,8): 186 | Pn = legendre(p) 187 | for ii,chanA in enumerate(self.sort_chan): 188 | for jj,chanB in enumerate(self.sort_chan): 189 | self.g[ii,jj] += (1/(4*np.pi))* (2*p+1)/((p**self.m)*((p+1)**self.m)) * Pn(self.cosines[ii,jj]) 190 | return 191 | 192 | def calc_coeff(self,V_list): 193 | coeff = [] 194 | for V in V_list: 195 | c = [] 196 | for ii,intChan in enumerate(self.sort_chan): 197 | g = np.delete(self.g, ii, axis=0) 198 | g = np.delete(g, ii, axis=1) 199 | v = np.delete(V, ii, axis=0) 200 | Gx = np.ones((len(self.sort_chan),len(self.sort_chan))) 201 | Gx[1:,1:] = g 202 | Gx[0,0] = 0 203 | CoV = np.insert(v,0,0) 204 | c.append(np.linalg.solve(Gx,CoV)) 205 | coeff.append(c) 206 | return coeff 207 | 208 | 209 | 210 | 211 | def new_SSM(self,EEG,ii,jj,m=4): 212 | orgChan = EEG[ii,jj,:].reshape(np.shape(EEG)[2]) 213 | newChan = np.zeros(np.shape(EEG)[2]) 214 | intChan = self.sysMap['map'][ii,jj][0] 215 | 216 | # This file is from the original data and simply carries meta 217 | # information such as channel names and locations 218 | files = glob.glob('./subject*_1_intrp.mat') 219 | orgEEG = scipy.io.loadmat(files[0])['EEG'][0] 220 | baseline = baselines(orgEEG) 221 | 222 | 223 | trn = load_val_data(fold) 224 | 225 | mse = [] 226 | 227 | 228 | for data in trn: 229 | full_data = [] 230 | for chan in baseline.sort_chan: 231 | [ii],[jj]=np.where(baseline.sysMap['map'] == [chan]) 232 | full_data.append(data[ii,jj,:].reshape(np.shape(data)[2])) 233 | 234 | chan_unsorted = baseline.sort_chan 235 | data_unsorted = full_data 236 | data = [] 237 | for chan in baseline.sort_chan: 238 | ii = chan_unsorted.index(chan) 239 | data.append(data_unsorted[ii]) 240 | V_tmp = np.stack(data, axis=0) 241 | print('here',np.shape(V_tmp)) 242 | V = [V_tmp[:,ii] for ii in range(np.shape(V_tmp)[1])] 243 | coeff = baseline.calc_coeff(V) 244 | ssp = [] 245 | gt = [] 246 | for jj in range(len(V)): 247 | sspT = [] 248 | gtT = [] 249 | for ii in range(19): 250 | g = np.delete(baseline.g[ii,:], ii, axis=0) 251 | g = np.insert(g,0,1) 252 | sspT.append(np.dot(coeff[jj][ii],g)) 253 | gtT.append(V[jj][ii]) 254 | ssp.append(np.array(sspT)) 255 | gt.append(np.array(gtT)) 256 | ssp = np.vstack(ssp) 257 | gt = np.vstack(gt) 258 | for ii in range(19): 259 | mse.append(float(keras.losses.mean_squared_error(scipy.stats.zscore(ssp[:,ii]), scipy.stats.zscore(gt[:,ii])))) 260 | 261 | 262 | 263 | np.save('./SSP_data'+str(fold),mse) 264 | -------------------------------------------------------------------------------- /baselines/maps/10-20_system.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/baselines/maps/10-20_system.mat -------------------------------------------------------------------------------- /baselines/ssp_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SBATCH -N 1 4 | #SBATCH -n 1 5 | #SBATCH --gres=gpu:1 6 | #SBATCH --exclusive # Reserving for exclusive use 7 | #SBATCH --mem=350G 8 | #SBATCH --partition=sched_mit_rgmark 9 | #SBATCH --time=72:00:00 10 | #SBATCH --output=%a.ssp1_out 11 | #SBATCH --error=%a.ssp1_err 12 | #SBATCH --array=0-7 13 | #SBATCH --mail-type=FAIL 14 | #SBATCH --mail-user=sadiyasa@msu.edu 15 | 16 | # THIS IS IMPORTANT OR THE MODULES WILL NOT IMPORT 17 | . /etc/profile.d/modules.sh 18 | module load python/3.6.3 19 | module load cuda/8.0 20 | module load cudnn/6.0 21 | 22 | pip3 install --user virtualenv 23 | #conda install --user virtualenv 24 | virtualenv -p python3 venv 25 | source venv/bin/activate 26 | pip3 install -r baseline_req.txt 27 | KERAS_BACKEND=tensorflow 28 | python3 ecr_ssp.py $SLURM_ARRAY_TASK_ID 29 | 30 | -------------------------------------------------------------------------------- /example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 71, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import keras\n", 11 | "import keras.optimizers\n", 12 | "from keras.models import Sequential\n", 13 | "from keras.models import model_from_json\n", 14 | "from keras.layers import Dense, Activation, MaxPooling2D,Dropout,Conv2D,BatchNormalization,Reshape,UpSampling2D,ZeroPadding2D,Conv2DTranspose\n", 15 | "from keras.backend import resize_images\n", 16 | "from sklearn.utils import class_weight\n", 17 | "import json\n", 18 | "import time\n", 19 | "import os\n", 20 | "from keras.utils.vis_utils import plot_model\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "import scipy.io\n", 23 | "import glob\n", 24 | "import numpy as np\n", 25 | "import scipy.stats" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 3, 31 | "metadata": {}, 32 | "outputs": [ 33 | { 34 | "name": "stdout", 35 | "output_type": "stream", 36 | "text": [ 37 | "/home/sarisadiya/Projects/ECR/eeg-channel-interpolation-using-deep-encoder-decoder-networks\n" 38 | ] 39 | } 40 | ], 41 | "source": [ 42 | "cd ../eeg-channel-interpolation-using-deep-encoder-decoder-networks/" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 72, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "\u001b[0m\u001b[01;34mtopology\u001b[0m/ \u001b[01;34mweights\u001b[0m/\r\n" 55 | ] 56 | } 57 | ], 58 | "source": [ 59 | "ls model" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 73, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "exp_top = './model/topology/model.json' # topology\n", 69 | "exp_weight = './model/weights/nn_weights-800.hdf5' # weight" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 74, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "with open(exp_top, 'r') as json_file:\n", 79 | " architecture = json.load(json_file)\n", 80 | " nn = model_from_json(architecture)\n", 81 | " nn.load_weights(exp_weight, by_name=True)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 75, 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "name": "stdout", 91 | "output_type": "stream", 92 | "text": [ 93 | "Model: \"sequential_1\"\n", 94 | "_________________________________________________________________\n", 95 | "Layer (type) Output Shape Param # \n", 96 | "=================================================================\n", 97 | "zero_padding2d_1 (ZeroPaddin (None, 16, 16, 8) 0 \n", 98 | "_________________________________________________________________\n", 99 | "conv2d_1 (Conv2D) (None, 8, 8, 16) 1168 \n", 100 | "_________________________________________________________________\n", 101 | "batch_normalization_1 (Batch (None, 8, 8, 16) 64 \n", 102 | "_________________________________________________________________\n", 103 | "activation_1 (Activation) (None, 8, 8, 16) 0 \n", 104 | "_________________________________________________________________\n", 105 | "dropout_1 (Dropout) (None, 8, 8, 16) 0 \n", 106 | "_________________________________________________________________\n", 107 | "conv2d_2 (Conv2D) (None, 8, 8, 32) 4640 \n", 108 | "_________________________________________________________________\n", 109 | "batch_normalization_2 (Batch (None, 8, 8, 32) 128 \n", 110 | "_________________________________________________________________\n", 111 | "activation_2 (Activation) (None, 8, 8, 32) 0 \n", 112 | "_________________________________________________________________\n", 113 | "max_pooling2d_1 (MaxPooling2 (None, 4, 4, 32) 0 \n", 114 | "_________________________________________________________________\n", 115 | "conv2d_transpose_1 (Conv2DTr (None, 8, 8, 16) 2064 \n", 116 | "_________________________________________________________________\n", 117 | "batch_normalization_3 (Batch (None, 8, 8, 16) 64 \n", 118 | "_________________________________________________________________\n", 119 | "activation_3 (Activation) (None, 8, 8, 16) 0 \n", 120 | "_________________________________________________________________\n", 121 | "zero_padding2d_2 (ZeroPaddin (None, 8, 8, 16) 0 \n", 122 | "_________________________________________________________________\n", 123 | "conv2d_transpose_2 (Conv2DTr (None, 8, 8, 8) 520 \n", 124 | "_________________________________________________________________\n", 125 | "batch_normalization_4 (Batch (None, 8, 8, 8) 32 \n", 126 | "_________________________________________________________________\n", 127 | "activation_4 (Activation) (None, 8, 8, 8) 0 \n", 128 | "_________________________________________________________________\n", 129 | "dense_1 (Dense) (None, 8, 8, 8) 72 \n", 130 | "=================================================================\n", 131 | "Total params: 8,752\n", 132 | "Trainable params: 8,608\n", 133 | "Non-trainable params: 144\n", 134 | "_________________________________________________________________\n" 135 | ] 136 | } 137 | ], 138 | "source": [ 139 | "nn.summary()" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 76, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "from baselines.ecr_baseline import baselines" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 77, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "orgFile = glob.glob('../baseline/subject00_1_intrp.mat')[0]\n", 158 | "orgEEG = scipy.io.loadmat(orgFile)['EEG'][0]" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 79, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "#orgEEG['chanlocs'][0]" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 80, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "data": { 177 | "text/plain": [ 178 | "(21, 91000)" 179 | ] 180 | }, 181 | "execution_count": 80, 182 | "metadata": {}, 183 | "output_type": "execute_result" 184 | } 185 | ], 186 | "source": [ 187 | "orgEEG['data'][0].shape" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 81, 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "baseline = baselines(orgEEG)" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 95, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "eegData = baseline.normalize_data(orgEEG,20,30)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 96, 211 | "metadata": {}, 212 | "outputs": [ 213 | { 214 | "data": { 215 | "text/plain": [ 216 | "(8, 8, 8)" 217 | ] 218 | }, 219 | "execution_count": 96, 220 | "metadata": {}, 221 | "output_type": "execute_result" 222 | } 223 | ], 224 | "source": [ 225 | "eegData[2].shape" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 97, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "popedData = baseline.occlude(eegData,3,3)" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 98, 240 | "metadata": {}, 241 | "outputs": [ 242 | { 243 | "data": { 244 | "text/plain": [ 245 | "array([[ 0.06017202, 0.06017202, 0.06017202, 0.06017202, 0.06017202,\n", 246 | " 0.06017202, 0.06017202, 0.06017202],\n", 247 | " [ 0.06017202, 0.06017202, -1.05979623, 0.06017202, 0.06017202,\n", 248 | " -0.5808349 , 0.06017202, 0.06017202],\n", 249 | " [ 0.06017202, -0.7170937 , -1.60442477, -1.24187448, -1.24187448,\n", 250 | " -1.16752275, 2.13094926, 0.06017202],\n", 251 | " [ 0.06017202, -0.49758637, -1.49931111, 0. , -0.6895707 ,\n", 252 | " -0.44820369, -1.05716565, 0.06017202],\n", 253 | " [ 0.06017202, -0.49758637, -1.49931111, -0.6895707 , -0.6895707 ,\n", 254 | " -0.44820369, -1.05716565, 0.06017202],\n", 255 | " [ 0.06017202, -1.44963847, -1.70541392, -0.18030348, -0.18030348,\n", 256 | " -0.04880974, -1.03846295, 0.06017202],\n", 257 | " [ 0.06017202, 0.06017202, -1.77429762, 0.06017202, 0.06017202,\n", 258 | " 0.16666314, 0.06017202, 0.06017202],\n", 259 | " [ 0.06017202, 0.06017202, 0.06017202, 0.06017202, 0.06017202,\n", 260 | " 0.06017202, 0.06017202, 0.06017202]])" 261 | ] 262 | }, 263 | "execution_count": 98, 264 | "metadata": {}, 265 | "output_type": "execute_result" 266 | } 267 | ], 268 | "source": [ 269 | "popedData[1][:,:,0]" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 99, 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [ 278 | "ORG = []\n", 279 | "EGL = []\n", 280 | "EUD = []\n", 281 | "for ii in range(5):\n", 282 | " ORG.append(eegData[ii][3,3,:])\n", 283 | " EGL.append(baseline.new_EGL(popedData[ii],3,3,2))\n", 284 | " EUD.append(baseline.new_EUD(popedData[ii],3,3,2))\n", 285 | "ORG = np.hstack(ORG)\n", 286 | "EGL = np.hstack(EGL)\n", 287 | "EUD = np.hstack(EUD)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 100, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [ 296 | "eegInt = nn.predict(np.array(popedData))" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": 101, 309 | "metadata": {}, 310 | "outputs": [ 311 | { 312 | "data": { 313 | "text/plain": [ 314 | "[]" 315 | ] 316 | }, 317 | "execution_count": 101, 318 | "metadata": {}, 319 | "output_type": "execute_result" 320 | }, 321 | { 322 | "data": { 323 | "image/png": "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\n", 324 | "text/plain": [ 325 | "
" 326 | ] 327 | }, 328 | "metadata": { 329 | "needs_background": "light" 330 | }, 331 | "output_type": "display_data" 332 | } 333 | ], 334 | "source": [ 335 | "plt.plot(range(8*5),ORG)\n", 336 | "plt.plot(range(8*5),EGL)\n", 337 | "plt.plot(range(8*5),EUD)\n", 338 | "plt.plot(range(8*5),eegInt[0:5,3,3,:].flatten())" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": null, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [] 354 | } 355 | ], 356 | "metadata": { 357 | "kernelspec": { 358 | "display_name": "Python 3", 359 | "language": "python", 360 | "name": "python3" 361 | }, 362 | "language_info": { 363 | "codemirror_mode": { 364 | "name": "ipython", 365 | "version": 3 366 | }, 367 | "file_extension": ".py", 368 | "mimetype": "text/x-python", 369 | "name": "python", 370 | "nbconvert_exporter": "python", 371 | "pygments_lexer": "ipython3", 372 | "version": "3.7.4" 373 | } 374 | }, 375 | "nbformat": 4, 376 | "nbformat_minor": 2 377 | } 378 | -------------------------------------------------------------------------------- /figure/Figure_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/figure/Figure_overview.png -------------------------------------------------------------------------------- /figure/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/figure/architecture.png -------------------------------------------------------------------------------- /figure/baseline_res.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/figure/baseline_res.pdf -------------------------------------------------------------------------------- /figure/data_split_bw.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/figure/data_split_bw.pdf -------------------------------------------------------------------------------- /figure/ecr_figure3_3_gimp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/figure/ecr_figure3_3_gimp.pdf -------------------------------------------------------------------------------- /model/topology/model.json: -------------------------------------------------------------------------------- 1 | "{\"class_name\": \"Sequential\", \"config\": {\"name\": \"sequential_1\", \"layers\": [{\"class_name\": \"ZeroPadding2D\", \"config\": {\"name\": \"zero_padding2d_1\", \"trainable\": true, \"batch_input_shape\": [null, 8, 8, 8], \"dtype\": \"float32\", \"padding\": [[4, 4], [4, 4]], \"data_format\": \"channels_last\"}}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_1\", \"trainable\": true, \"batch_input_shape\": [null, 8, 8, 8], \"dtype\": \"float32\", \"filters\": 16, \"kernel_size\": [3, 3], \"strides\": [2, 2], \"padding\": \"same\", \"data_format\": \"channels_last\", \"dilation_rate\": [1, 1], \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"VarianceScaling\", \"config\": {\"scale\": 1.0, \"mode\": \"fan_in\", \"distribution\": \"normal\", \"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}, {\"class_name\": \"BatchNormalization\", \"config\": {\"name\": \"batch_normalization_1\", \"trainable\": true, \"dtype\": \"float32\", \"axis\": -1, \"momentum\": 0.99, \"epsilon\": 0.001, \"center\": true, \"scale\": true, \"beta_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"gamma_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"moving_mean_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"moving_variance_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"beta_regularizer\": null, \"gamma_regularizer\": null, \"beta_constraint\": null, \"gamma_constraint\": null}}, {\"class_name\": \"Activation\", \"config\": {\"name\": \"activation_1\", \"trainable\": true, \"dtype\": \"float32\", \"activation\": \"tanh\"}}, {\"class_name\": \"Dropout\", \"config\": {\"name\": \"dropout_1\", \"trainable\": true, \"dtype\": \"float32\", \"rate\": 0.1, \"noise_shape\": null, \"seed\": null}}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_2\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 32, \"kernel_size\": [3, 3], \"strides\": [1, 1], \"padding\": \"same\", \"data_format\": \"channels_last\", \"dilation_rate\": [1, 1], \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"VarianceScaling\", \"config\": {\"scale\": 1.0, \"mode\": \"fan_in\", \"distribution\": \"normal\", \"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}, {\"class_name\": \"BatchNormalization\", \"config\": {\"name\": \"batch_normalization_2\", \"trainable\": true, \"dtype\": \"float32\", \"axis\": -1, \"momentum\": 0.99, \"epsilon\": 0.001, \"center\": true, \"scale\": true, \"beta_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"gamma_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"moving_mean_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"moving_variance_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"beta_regularizer\": null, \"gamma_regularizer\": null, \"beta_constraint\": null, \"gamma_constraint\": null}}, {\"class_name\": \"Activation\", \"config\": {\"name\": \"activation_2\", \"trainable\": true, \"dtype\": \"float32\", \"activation\": \"tanh\"}}, {\"class_name\": \"MaxPooling2D\", \"config\": {\"name\": \"max_pooling2d_1\", \"trainable\": true, \"dtype\": \"float32\", \"pool_size\": [2, 2], \"padding\": \"same\", \"strides\": [2, 2], \"data_format\": \"channels_last\"}}, {\"class_name\": \"Conv2DTranspose\", \"config\": {\"name\": \"conv2d_transpose_1\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 16, \"kernel_size\": [2, 2], \"strides\": [2, 2], \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": [1, 1], \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"VarianceScaling\", \"config\": {\"scale\": 1.0, \"mode\": \"fan_in\", \"distribution\": \"normal\", \"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null, \"output_padding\": null}}, {\"class_name\": \"BatchNormalization\", \"config\": {\"name\": \"batch_normalization_3\", \"trainable\": true, \"dtype\": \"float32\", \"axis\": -1, \"momentum\": 0.99, \"epsilon\": 0.001, \"center\": true, \"scale\": true, \"beta_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"gamma_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"moving_mean_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"moving_variance_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"beta_regularizer\": null, \"gamma_regularizer\": null, \"beta_constraint\": null, \"gamma_constraint\": null}}, {\"class_name\": \"Activation\", \"config\": {\"name\": \"activation_3\", \"trainable\": true, \"dtype\": \"float32\", \"activation\": \"tanh\"}}, {\"class_name\": \"ZeroPadding2D\", \"config\": {\"name\": \"zero_padding2d_2\", \"trainable\": true, \"dtype\": \"float32\", \"padding\": [[0, 0], [0, 0]], \"data_format\": \"channels_last\"}}, {\"class_name\": \"Conv2DTranspose\", \"config\": {\"name\": \"conv2d_transpose_2\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 8, \"kernel_size\": [2, 2], \"strides\": [1, 1], \"padding\": \"same\", \"data_format\": \"channels_last\", \"dilation_rate\": [1, 1], \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"VarianceScaling\", \"config\": {\"scale\": 1.0, \"mode\": \"fan_in\", \"distribution\": \"normal\", \"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null, \"output_padding\": null}}, {\"class_name\": \"BatchNormalization\", \"config\": {\"name\": \"batch_normalization_4\", \"trainable\": true, \"dtype\": \"float32\", \"axis\": -1, \"momentum\": 0.99, \"epsilon\": 0.001, \"center\": true, \"scale\": true, \"beta_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"gamma_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"moving_mean_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"moving_variance_initializer\": {\"class_name\": \"Ones\", \"config\": {}}, \"beta_regularizer\": null, \"gamma_regularizer\": null, \"beta_constraint\": null, \"gamma_constraint\": null}}, {\"class_name\": \"Activation\", \"config\": {\"name\": \"activation_4\", \"trainable\": true, \"dtype\": \"float32\", \"activation\": \"tanh\"}}, {\"class_name\": \"Dense\", \"config\": {\"name\": \"dense_1\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 8, \"activation\": \"tanh\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"VarianceScaling\", \"config\": {\"scale\": 1.0, \"mode\": \"fan_avg\", \"distribution\": \"uniform\", \"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}]}, \"keras_version\": \"2.3.1\", \"backend\": \"tensorflow\"}" -------------------------------------------------------------------------------- /model/weights/nn_weights-400.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/model/weights/nn_weights-400.hdf5 -------------------------------------------------------------------------------- /model/weights/nn_weights-800.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/model/weights/nn_weights-800.hdf5 -------------------------------------------------------------------------------- /train/ecr_cnn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Thu Jan 30 23:53:57 2020 5 | 6 | @author: sarisadiya 7 | """ 8 | 9 | 10 | import numpy as np 11 | import keras 12 | import keras.optimizers 13 | from keras.models import Sequential 14 | from keras.layers import Dense, Activation, MaxPooling2D,Dropout,Conv2D,BatchNormalization,Reshape,UpSampling2D,ZeroPadding2D,Conv2DTranspose 15 | from sklearn.utils import class_weight 16 | import json 17 | import time 18 | import os 19 | import sys 20 | import keras.backend as K 21 | #Let the user know if they are not using a GPU 22 | if K.tensorflow_backend._get_available_gpus() == []: 23 | print('YOU ARE NOT USING A GPU ON THIS DEVICE!') 24 | else: 25 | print('USING GPU!!!!!!') 26 | 27 | h = int(sys.argv[1]) 28 | hp = np.load('ecr_hyper_parameters.npy', allow_pickle=True)[()] 29 | 30 | hp['batch_perc'] = 0.001 31 | hp['num_epochs'] = 200 32 | 33 | 34 | def broadcast_to_8x8(A): 35 | B = np.hstack((A[:,:2,:],np.tile(A[:,2,:].reshape(5,1,10),(1,2,1)),A[:,3:,:])) 36 | C = np.vstack((B[:2,:,:],np.tile(B[2,:,:].reshape(1,6,10),(2,1,1)),B[3:,:,:])) 37 | D = A[0,0,:].reshape((1,1,10)) #A1-A2 ear channel 38 | E = A[4,4,:].reshape((1,1,10)) #A1-A2 ear channel 39 | F = np.array(np.vstack((np.hstack((D,E)),np.hstack((E,D))))) 40 | G = np.tile(F,(4,4,1)) 41 | G[1:7,1:7,:] = C 42 | return G[:,:,:8] 43 | 44 | def ecr_load_data(fold): 45 | l = range(10) 46 | trnIdx = [ii % len(l) for ii in range(fold,fold+6)] 47 | valIdx = [ii % len(l) for ii in range(fold+6,fold+8)] 48 | trnY = [] 49 | trnX = [] 50 | valY = [] 51 | valX = [] 52 | for ii in trnIdx: 53 | foldData = np.load('ecr_data_'+str(ii)+'.npy', allow_pickle=True)[()] 54 | for subjData in foldData: 55 | for ttData in foldData[subjData]: 56 | [org,data] = foldData[subjData][ttData] 57 | for tt in list(data.keys())[:10]: 58 | trnY.append(broadcast_to_8x8(org)) 59 | trnX.append(broadcast_to_8x8(np.nan_to_num(data[tt], copy=False))) 60 | for ii in valIdx: 61 | foldData = np.load('ecr_data_'+str(ii)+'.npy', allow_pickle=True)[()] 62 | for subjData in foldData: 63 | for ttData in foldData[subjData]: 64 | [org,data] = foldData[subjData][ttData] 65 | for tt in list(data.keys())[:10]: 66 | valY.append(broadcast_to_8x8(org)) 67 | valX.append(broadcast_to_8x8(np.nan_to_num(data[tt], copy=False))) 68 | return np.array(trnX),np.array(trnY),np.array(valX),np.array(valY) 69 | 70 | 71 | def callbacks(hp,h,fold): 72 | 73 | ########################################################################### 74 | # Checkpoints 75 | ########################################################################### 76 | filepath="Checkpoints/" + hp['experiment'][h] + "/fold" + str(fold) + "/weights/nn_weights-{epoch:02d}.hdf5" # Where are checkpoints saved 77 | checkpoint = keras.callbacks.ModelCheckpoint( 78 | filepath, 79 | monitor='val_loss', # Validation set Loss 80 | verbose = 0, # Display text 81 | save_weights_only = True, # if True, only the model weights are saved 82 | save_best_only = False, # if True, the latest-best model is overwritten 83 | mode = 'auto', # used if 'save_best_only' is True 84 | period = 5) # Epochs between checkpoints 85 | return checkpoint 86 | 87 | def CNN_2D(hp,h): 88 | nn = Sequential() 89 | for i in range(0,hp['cnn2d_num_cnn_layers'][h]): 90 | #-------------------------------------------------------------------------- 91 | # 3D Convolution 92 | if i == 0: 93 | if hp['cnn2d_strides_per_layer'][h][i][0] == 1: 94 | nn.add(Conv2D(hp['cnn2d_filters_per_layer'][h][i], # 8 95 | kernel_size = hp['cnn2d_kernal_sizes_per_layer'][h][i], # (3, 3, 3), 96 | strides = hp['cnn2d_strides_per_layer'][h][i], # (1, 1, 1), 97 | kernel_initializer = hp['weights_initialization'][h], 98 | bias_initializer = 'zeros', 99 | input_shape = hp['input_shape'], 100 | data_format ='channels_last', 101 | padding ='same')) 102 | else: 103 | s = hp['cnn2d_strides_per_layer'][h][i][0] 104 | k = hp['cnn2d_kernal_sizes_per_layer'][h][i][0] 105 | d = hp['input_shape'][0] 106 | p = int(np.floor(((s-1)*d+k-s)/2)) 107 | nn.add(ZeroPadding2D(padding=(p,p), input_shape = hp['input_shape'])) 108 | nn.add(Conv2D(hp['cnn2d_filters_per_layer'][h][i], # 8 109 | kernel_size = hp['cnn2d_kernal_sizes_per_layer'][h][i], # (3, 3, 3), 110 | strides = hp['cnn2d_strides_per_layer'][h][i], # (1, 1, 1), 111 | kernel_initializer = hp['weights_initialization'][h], 112 | bias_initializer = 'zeros', 113 | input_shape = hp['input_shape'], 114 | data_format ='channels_last', 115 | padding ='same')) 116 | else: 117 | s = hp['cnn2d_strides_per_layer'][h][i][0] 118 | k = hp['cnn2d_kernal_sizes_per_layer'][h][i][0] 119 | d = nn.layers[-1].output_shape[1] 120 | p = int(np.floor(((s-1)*d+k-s)/2)) 121 | if hp['cnn2d_strides_per_layer'][h][i][0] != 1: 122 | nn.add(ZeroPadding2D(padding=(p,p))) 123 | nn.add(Conv2D(hp['cnn2d_filters_per_layer'][h][i], # 8 124 | kernel_size = hp['cnn2d_kernal_sizes_per_layer'][h][i], # (3, 3, 3), 125 | strides = hp['cnn2d_strides_per_layer'][h][i], # (1, 1, 1), 126 | kernel_initializer = hp['weights_initialization'][h], 127 | bias_initializer = 'zeros', 128 | padding = 'same')) 129 | #Batch Noramlization 130 | if hp['cnn2d_batchnormalize_per_layer'][h][i]: 131 | nn.add(BatchNormalization( axis = -1, 132 | momentum = 0.99, 133 | epsilon = 0.001, 134 | center = True, 135 | scale = True, 136 | beta_initializer = 'zeros', 137 | gamma_initializer = 'ones', 138 | moving_mean_initializer = 'zeros', 139 | moving_variance_initializer = 'ones', 140 | beta_regularizer = None, 141 | gamma_regularizer = None, 142 | beta_constraint = None, 143 | gamma_constraint = None)) 144 | if hp['cnn2d_activations_per_layer'][h][i]: 145 | nn.add(Activation('relu')) 146 | if hp['cnn2d_maxpool_per_layer'][h][i]: 147 | nn.add(MaxPooling2D(pool_size = (2,2), 148 | padding = 'same')) 149 | if hp['cnn2d_dropouts_per_layer'][h][i] != []: 150 | nn.add(Dropout(hp['cnn2d_dropouts_per_layer'][h][i])) 151 | 152 | while nn.layers[-1].output_shape[3] > 8: 153 | if 2*nn.layers[-1].output_shape[1] <= 8: 154 | nn.add(Conv2DTranspose(int(np.floor(nn.layers[-1].output_shape[3]/2)), 155 | kernel_size = (2,2), 156 | strides = (2,2), 157 | kernel_initializer = hp['weights_initialization'][h], 158 | bias_initializer = 'zeros', 159 | data_format ='channels_last', 160 | padding ='valid')) 161 | else: 162 | k = 2 163 | s = 1 164 | d = nn.layers[-1].output_shape[1] 165 | p = int(np.floor(((s-1)*d+k-s)/2)) 166 | nn.add(ZeroPadding2D(padding=(p,p))) 167 | nn.add(Conv2DTranspose(int(np.floor(nn.layers[-1].output_shape[3]/2)), 168 | kernel_size = (2,2), 169 | strides = (1,1), 170 | kernel_initializer = hp['weights_initialization'][h], 171 | bias_initializer = 'zeros', 172 | data_format ='channels_last', 173 | padding ='same')) 174 | #Batch Noramlization 175 | if hp['cnn2d_batchnormalize_per_layer'][h][i]: 176 | nn.add(BatchNormalization( axis = -1, 177 | momentum = 0.99, 178 | epsilon = 0.001, 179 | center = True, 180 | scale = True, 181 | beta_initializer = 'zeros', 182 | gamma_initializer = 'ones', 183 | moving_mean_initializer = 'zeros', 184 | moving_variance_initializer = 'ones', 185 | beta_regularizer = None, 186 | gamma_regularizer = None, 187 | beta_constraint = None, 188 | gamma_constraint = None)) 189 | 190 | #Activation 191 | if hp['cnn2d_activations_per_layer'][h][i]: 192 | nn.add(Activation('relu')) 193 | 194 | 195 | while 2*nn.layers[-1].output_shape[1] <= 8: 196 | nn.add(Conv2DTranspose(int(np.floor(nn.layers[-1].output_shape[3])), 197 | kernel_size = (2,2), 198 | strides = (2,2), 199 | kernel_initializer = hp['weights_initialization'][h], 200 | bias_initializer = 'zeros', 201 | data_format ='channels_last', 202 | padding ='same')) 203 | 204 | 205 | #Batch Noramlization 206 | if hp['cnn2d_batchnormalize_per_layer'][h][i]: 207 | nn.add(BatchNormalization( axis = -1, 208 | momentum = 0.99, 209 | epsilon = 0.001, 210 | center = True, 211 | scale = True, 212 | beta_initializer = 'zeros', 213 | gamma_initializer = 'ones', 214 | moving_mean_initializer = 'zeros', 215 | moving_variance_initializer = 'ones', 216 | beta_regularizer = None, 217 | gamma_regularizer = None, 218 | beta_constraint = None, 219 | gamma_constraint = None)) 220 | 221 | 222 | #Activation 223 | if hp['cnn2d_activations_per_layer'][h][i]: 224 | nn.add(Activation('relu')) 225 | nn.add(Dense(nn.layers[-1].output_shape[1], activation='relu')) 226 | return nn 227 | 228 | def evaluate_model(Y_test,Y_pred): 229 | test_loss = keras.losses.mean_squared_error(Y_test, Y_pred) 230 | return [['NMSE loss'],[np.mean(np.array(test_loss))]] 231 | 232 | 233 | os.mkdir('Checkpoints/' + hp['experiment'][h]) # make the directory '' 234 | os.mkdir('Checkpoints/' + hp['experiment'][h] + '/topology') # make the directory 'topology' 235 | os.mkdir('Checkpoints/' + hp['experiment'][h] +'/all_performance') # make the directory 'overall' 236 | 237 | 238 | for fold in range(1): 239 | os.mkdir('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold)) # make the director for this fold 240 | os.mkdir('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold) + '/weights') # make the directory for weights 241 | os.mkdir('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold) + '/performance') # make the directory for performance 242 | 243 | tic = time.time() 244 | print("(ecr_CNN): at ecr_load_data") 245 | xTrn,yTrn,xVal,yVal = ecr_load_data(fold) 246 | toc = time.time() 247 | print("(ecr_CNN): finished loading data after "+str(int(toc-tic))+" seconds") 248 | 249 | hp['input_shape'] = (8,8,8) 250 | nn = CNN_2D(hp,h) 251 | nn.summary() 252 | 253 | if nn.layers[-1].output_shape[1:] != (8,8,8): 254 | print("(ecr_CNN): size Error!!!!!") 255 | raise Exception('size Error!') 256 | 257 | print("(ecr_CNN): Save topology") 258 | json_nn = nn.to_json() 259 | with open('Checkpoints/' + hp['experiment'][h] + '/topology/model.json', 'w') as outfile: 260 | json.dump(json_nn, outfile) 261 | 262 | print("(ecr_CNN): creating an optimizer") 263 | grad_desc_algorithm = keras.optimizers.SGD(lr=hp['lr'][h], decay=0, momentum=hp['momentum'][h], nesterov=hp['nesterov'][h]) 264 | 265 | print("(ecr_CNN): compiling") 266 | nn.compile(loss = 'mean_squared_error', optimizer = grad_desc_algorithm, metrics = [keras.losses.mean_squared_error]) 267 | 268 | batch_size = int(round(hp['batch_perc']*np.size(xTrn,0))) 269 | print("(ecr_CNN): batch_size is:"+str(batch_size)+" input size is "+str(np.size(xTrn,0))+" batch perc "+str(hp['batch_perc'])) 270 | 271 | checkpoint = callbacks(hp,h,fold) 272 | 273 | print("(ecr_CNN): fitting") 274 | fit_nn = nn.fit(xTrn, # Training Data X 275 | yTrn, # Training Data Y 276 | validation_data = (xVal, 277 | yVal), # Validation data tuple 278 | shuffle = 1, # shuffle the training data epoch before you use it 279 | initial_epoch = 0, # Starting Epoch (should awlways be 0) 280 | epochs = hp['num_epochs'], # Number of runs through the data 281 | batch_size = batch_size, # Number of samples per gradient update. 282 | verbose = 1, # display options to console 283 | callbacks=[checkpoint]) # We want to save checkpoints 284 | 285 | 286 | train_over_time = [] 287 | val_over_time = [] 288 | 289 | print("(ecr_CNN): evaluating") 290 | #EVALUATE TRAINING SET 291 | loss = nn.evaluate(xTrn, yTrn, verbose=0) 292 | yPred = nn.predict(xTrn,verbose=0) 293 | [test_names, results] = evaluate_model(yTrn, yPred) 294 | results = np.append(loss[0],np.array(results)) 295 | train_over_time.append(results) 296 | 297 | #EVALUATE VALIDATION SET 298 | loss = nn.evaluate(xVal, yVal, verbose=0) 299 | yPred = nn.predict(xVal,verbose=0) 300 | [test_names, results] = evaluate_model(yVal, yPred) 301 | results = np.append(loss[0],np.array(results)) 302 | val_over_time.append(results) 303 | 304 | train_over_time = np.vstack(train_over_time) 305 | val_over_time = np.vstack(val_over_time) 306 | 307 | # SAVE THE RESULTS 308 | np.save('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold) +'/performance/train_performance',train_over_time) 309 | np.save('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold) +'/performance/val_performance',val_over_time) 310 | np.save('Checkpoints/' + hp['experiment'][h] + '/fold' + str(fold) +'/performance/metric_names',test_names) 311 | -------------------------------------------------------------------------------- /train/ecr_hyper_parameters.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sari-saba-sadiya/EEG-Channel-Interpolation-Using-Deep-Encoder-Decoder-Networks/fd1307ca2d3f71618e31d101edb438175e0aa205/train/ecr_hyper_parameters.npy -------------------------------------------------------------------------------- /train/ecr_loadModel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | import numpy as np 8 | import keras 9 | import keras.optimizers 10 | from keras.models import Sequential 11 | from keras.models import model_from_json 12 | from keras.layers import Dense, Activation, MaxPooling2D,Dropout,Conv2D,BatchNormalization,Reshape,UpSampling2D,ZeroPadding2D,Conv2DTranspose 13 | from keras.backend import resize_images 14 | from sklearn.utils import class_weight 15 | import json 16 | import time 17 | import os 18 | import sys 19 | 20 | # In[2]: 21 | 22 | 23 | index = int(sys.argv[1]) 24 | 25 | h = 0 26 | hp = np.load('ecr_hyper_parameters.npy', allow_pickle=True)[()] 27 | 28 | 29 | # In[ ]: 30 | 31 | 32 | hp['batch_perc'] = 0.001 33 | hp['num_epochs'] = 1000 34 | 35 | 36 | # In[3]: 37 | 38 | 39 | def broadcast_to_8x8(A): 40 | B = np.hstack((A[:,:2,:],np.tile(A[:,2,:].reshape(5,1,10),(1,2,1)),A[:,3:,:])) 41 | C = np.vstack((B[:2,:,:],np.tile(B[2,:,:].reshape(1,6,10),(2,1,1)),B[3:,:,:])) 42 | D = A[0,0,:].reshape((1,1,10)) #A1-A2 ear channel 43 | E = A[4,4,:].reshape((1,1,10)) #A1-A2 ear channel 44 | F = np.array(np.vstack((np.hstack((D,E)),np.hstack((E,D))))) 45 | G = np.tile(F,(4,4,1)) 46 | G[1:7,1:7,:] = C 47 | return G[:,:,:8] 48 | 49 | 50 | # In[4]: 51 | 52 | 53 | def ecr_load_data(index): 54 | #l = range(10) 55 | trnIdx = [index] #[ii % len(l) for ii in range(fold,fold+6)] 56 | #valIdx = [ii % len(l) for ii in range(fold+6,fold+8)] 57 | trnY = [] 58 | trnX = [] 59 | #valY = [] 60 | #valX = [] 61 | for ii in trnIdx: 62 | foldData = np.load('../data/ecr_data2_'+str(ii)+'.npy', allow_pickle=True)[()] 63 | for subjData in foldData: 64 | for ttData in foldData[subjData]: 65 | [org,data] = foldData[subjData][ttData] 66 | for tt in list(data.keys())[:10]: 67 | trnY.append(broadcast_to_8x8(org)) 68 | trnX.append(broadcast_to_8x8(np.nan_to_num(data[tt], copy=False))) 69 | return np.array(trnX),np.array(trnY) 70 | 71 | 72 | def evaluate_model(X,Y_test,Y_pred): 73 | test_loss = [] 74 | for ii in range(len(X)): 75 | x,y = np.argwhere(X[ii][:,:,0]==0)[0] 76 | test_loss.append(keras.losses.mean_squared_error(Y_test[ii][x,y], Y_pred[ii][x,y])) 77 | return [['NMSE loss'],[np.mean(np.array(test_loss))]] 78 | 79 | 80 | # In[5]: 81 | 82 | 83 | exp_top = '../Checkpoints/' + hp['experiment'][h] + '/topology/model.json' # topology 84 | exp_perf = '../Checkpoints/' + hp['experiment'][h] +'/all_performance' # performance 85 | exp_weight = '../Checkpoints/' + hp['experiment'][h] +'/fold0/weights/nn_weights-500.hdf5' 86 | 87 | 88 | # In[6]: 89 | 90 | 91 | with open(exp_top, 'r') as json_file: 92 | architecture = json.load(json_file) 93 | nn = model_from_json(architecture) 94 | 95 | 96 | # In[7]: 97 | 98 | 99 | nn.load_weights(exp_weight, by_name=True) 100 | 101 | 102 | # In[ ]: 103 | 104 | 105 | tic = time.time() 106 | print("(ecr_transfer): at ecr_load_data") 107 | xTrn,yTrn = ecr_load_data(index) 108 | toc = time.time() 109 | print("(ecr_transfer): finished loading data after "+str(int(toc-tic))+" seconds") 110 | 111 | 112 | # In[ ]: 113 | 114 | 115 | fullPerf_over_time = [] 116 | 117 | 118 | # In[ ]: 119 | grad_desc_algorithm = keras.optimizers.SGD(lr=hp['lr'][h], decay=0, momentum=hp['momentum'][h], nesterov=hp['nesterov'][h]) 120 | nn.compile(loss = 'mean_squared_error', optimizer = grad_desc_algorithm) 121 | 122 | print("(ecr_transfer): evaluating") 123 | #EVALUATE TRAINING SET 124 | loss = [0] 125 | loss[0] = nn.evaluate(xTrn, yTrn, verbose=0) 126 | fullPerf_over_time.append(loss) 127 | yPred = nn.predict(xTrn,verbose=0) 128 | [test_names, results] = evaluate_model(xTrn,yTrn, yPred) 129 | #results = loss.extend(loss,np.array(results)) 130 | fullPerf_over_time.append(results) 131 | 132 | 133 | # In[ ]: 134 | 135 | 136 | ##fullPerf_over_time = np.vstack(fullPerf_over_time) 137 | fullPerf_over_time = np.array(fullPerf_over_time) 138 | 139 | # In[ ]: 140 | 141 | 142 | # SAVE THE RESULTS ############################################################ 143 | np.save('./transfer_performance_'+str(index),fullPerf_over_time) 144 | 145 | -------------------------------------------------------------------------------- /train/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SBATCH -N 1 4 | #SBATCH -n 1 5 | #SBATCH --gres=gpu:1 6 | #SBATCH --exclusive # Reserving for exclusive use 7 | #SBATCH --mem=350G 8 | #SBATCH --partition=sched_mit_rgmark 9 | #SBATCH --time=96:00:00 10 | #SBATCH --output=out/model_%a.out 11 | #SBATCH --error=er/model_%a.err 12 | #SBATCH --array=1-10 13 | #SBATCH --mail-type=FAIL 14 | #SBATCH --mail-user=sadiyasa@msu.edu 15 | 16 | # THIS IS IMPORTANT OR THE MODULES WILL NOT IMPORT 17 | . /etc/profile.d/modules.sh 18 | module load python/3.6.3 19 | module load cuda/8.0 20 | module load cudnn/6.0 21 | 22 | pip3 install --user virtualenv 23 | #conda install --user virtualenv 24 | virtualenv -p python3 venv 25 | source venv/bin/activate 26 | pip3 install -r requirements.txt 27 | KERAS_BACKEND=tensorflow 28 | python3 ecr_cnn.py $SLURM_ARRAY_TASK_ID 29 | -------------------------------------------------------------------------------- /train/run_loadModel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SBATCH -N 1 4 | #SBATCH -n 1 5 | #SBATCH --gres=gpu:1 6 | #SBATCH --exclusive # Reserving for exclusive use 7 | #SBATCH --mem=350G 8 | #SBATCH --partition=sched_mit_rgmark 9 | #SBATCH --time=96:00:00 10 | #SBATCH --output=out_load/modelPred_%a.out 11 | #SBATCH --error=err_load/modelPred_%a.err 12 | #SBATCH --array=1 13 | #SBATCH --mail-type=FAIL 14 | #SBATCH --mail-user=sadiyasa@msu.edu 15 | 16 | # THIS IS IMPORTANT OR THE MODULES WILL NOT IMPORT 17 | . /etc/profile.d/modules.sh 18 | module load python/3.6.3 19 | module load cuda/8.0 20 | module load cudnn/6.0 21 | 22 | pip3 install --user virtualenv 23 | #conda install --user virtualenv 24 | virtualenv -p python3 venv 25 | source venv/bin/activate 26 | pip3 install -r requirements.txt 27 | KERAS_BACKEND=tensorflow 28 | python3 ecr_loadmodel.py $SLURM_ARRAY_TASK_ID 29 | -------------------------------------------------------------------------------- /transfer/ecr_transfer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | import numpy as np 8 | import keras 9 | import keras.optimizers 10 | from keras.models import Sequential 11 | from keras.models import model_from_json 12 | from keras.layers import Dense, Activation, MaxPooling2D,Dropout,Conv2D,BatchNormalization,Reshape,UpSampling2D,ZeroPadding2D,Conv2DTranspose 13 | from keras.backend import resize_images 14 | from sklearn.utils import class_weight 15 | import json 16 | import time 17 | import os 18 | 19 | 20 | # In[ ]: 21 | 22 | 23 | datasetName = 'ecr_data_' 24 | foldBegin = 6 25 | foldEnd = 9 26 | 27 | 28 | # In[38]: 29 | 30 | 31 | index = 0 32 | h = 14 33 | hp = np.load('ecr_hyper_parameters.npy', allow_pickle=True)[()] 34 | 35 | 36 | # In[3]: 37 | 38 | 39 | hp['batch_perc'] = 0.01 40 | hp['num_epochs'] = 400 41 | 42 | 43 | # In[4]: 44 | 45 | 46 | def broadcast_to_8x8(A): 47 | B = np.hstack((A[:,:2,:],np.tile(A[:,2,:].reshape(5,1,10),(1,2,1)),A[:,3:,:])) 48 | C = np.vstack((B[:2,:,:],np.tile(B[2,:,:].reshape(1,6,10),(2,1,1)),B[3:,:,:])) 49 | D = A[0,0,:].reshape((1,1,10)) #A1-A2 ear channel 50 | E = A[4,4,:].reshape((1,1,10)) #A1-A2 ear channel 51 | F = np.array(np.vstack((np.hstack((D,E)),np.hstack((E,D))))) 52 | G = np.tile(F,(4,4,1)) 53 | G[1:7,1:7,:] = C 54 | return G[:,:,:8] 55 | 56 | 57 | # In[5]: 58 | 59 | 60 | def ecr_load_data(index,foldBegin,foldEnd,datasetName): 61 | l = range(10) 62 | trnIdx = [ii % len(l) for ii in range(foldBegin,foldEnd+1)] 63 | trnY = [] 64 | trnX = [] 65 | valY = [] 66 | valX = [] 67 | for ii in trnIdx: 68 | foldData = np.load(datasetName+str(ii)+'.npy', allow_pickle=True)[()] 69 | for subjData in foldData: 70 | for ttData in foldData[subjData]: 71 | [org,data] = foldData[subjData][ttData] 72 | for jj,tt in enumerate(list(data.keys())[:10]): 73 | if jj % 10 == 0: 74 | trnY.append(broadcast_to_8x8(org)) 75 | trnX.append(broadcast_to_8x8(np.nan_to_num(data[tt], copy=False))) 76 | else: 77 | valY.append(broadcast_to_8x8(org)) 78 | valX.append(broadcast_to_8x8(np.nan_to_num(data[tt], copy=False))) 79 | return np.array(trnX),np.array(trnY),np.array(valX),np.array(valY) 80 | 81 | 82 | # 83 | def evaluate_model(X,Y_test,Y_pred): 84 | test_loss = [] 85 | for ii in range(len(X)): 86 | x,y = np.argwhere(X[ii][:,:,0]==0)[0] 87 | test_loss.append(keras.losses.mean_squared_error(Y_test[ii][x,y], Y_pred[ii][x,y])) 88 | return [['NMSE loss'],[np.mean(np.array(test_loss))]] 89 | 90 | # In[6]: 91 | 92 | 93 | exp_top = './Checkpoints/' + hp['experiment'][h] + '/topology/model.json' # topology 94 | exp_perf = './Checkpoints/' + hp['experiment'][h] +'/all_performance' # performance 95 | exp_weight = './Checkpoints/' + hp['experiment'][h] +'/fold0/weights/nn_weights-200.hdf5' 96 | 97 | 98 | # In[7]: 99 | 100 | 101 | with open(exp_top, 'r') as json_file: 102 | architecture = json.load(json_file) 103 | nn = model_from_json(architecture) 104 | 105 | 106 | # In[8]: 107 | 108 | 109 | nn.load_weights(exp_weight, by_name=True) 110 | 111 | 112 | # In[37]: 113 | 114 | 115 | tic = time.time() 116 | print("(ecr_transfer): at ecr_load_data") 117 | xTrn,yTrn,xVal,yVal = ecr_load_data(index,foldBegin,foldEnd,datasetName) 118 | toc = time.time() 119 | print("(ecr_transfer): finished loading data after "+str(int(toc-tic))+" seconds") 120 | 121 | 122 | # In[25] 123 | batch_size = int(round(hp['batch_perc']*np.size(xTrn,0))) 124 | print("(ecr_CNN): batch_size is:"+str(batch_size)+" input size is "+str(np.size(xTrn,0))+" batch perc "+str(hp['batch_perc'])) 125 | 126 | 127 | grad_desc_algorithm = keras.optimizers.SGD(lr=hp['lr'][h], decay=0, momentum=hp['momentum'][h], nesterov=hp['nesterov'][h]) 128 | 129 | 130 | # In[33]: 131 | 132 | 133 | nn.compile(loss = 'mean_squared_error', optimizer = grad_desc_algorithm) 134 | 135 | 136 | # In[ ]: 137 | 138 | 139 | tic = time.time() 140 | print("(ecr_transfer): fitting") 141 | fit_nn = nn.fit(xTrn, # Training Data X 142 | yTrn, # Training Data Y 143 | validation_data = (xVal, 144 | yVal), # Validation data tuple 145 | shuffle = 1, # shuffle the training data epoch before you use it 146 | initial_epoch = 0, # Starting Epoch (should awlways be 0) 147 | epochs = hp['num_epochs'], # Number of runs through the data 148 | batch_size = batch_size) # Number of samples per gradient update. 149 | toc = time.time() 150 | print("(ecr_transfer): finished training after "+str(int(toc-tic))+" seconds") 151 | 152 | 153 | # In[19]: 154 | 155 | 156 | valPerf_over_time = [] 157 | 158 | 159 | # In[30]: 160 | 161 | 162 | print("(ecr_transfer): evaluating") 163 | 164 | #EVALUATE TRAINING SET 165 | loss = nn.evaluate(xTrn, yTrn, verbose=0) 166 | yPred = nn.predict(xTrn,verbose=0) 167 | [test_names, results] = evaluate_model(xTrn,yTrn, yPred) 168 | results = np.append(loss,np.array(results)) 169 | 170 | valPerf_over_time.append(results) 171 | 172 | 173 | # In[31]: 174 | 175 | 176 | valPerf_over_time = np.vstack(valPerf_over_time) 177 | 178 | 179 | # In[ ]: 180 | 181 | 182 | # SAVE THE RESULTS ############################################################ 183 | np.save('transfer_performance_'+datasetName+str(foldBegin)+'_'+str(foldEnd),valPerf_over_time) 184 | 185 | 186 | # In[ ]: 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /transfer/run_transfer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SBATCH -N 1 4 | #SBATCH -n 1 5 | #SBATCH --gres=gpu:1 6 | #SBATCH --exclusive # Reserving for exclusive use 7 | #SBATCH --mem=350G 8 | #SBATCH --partition=sched_mit_rgmark 9 | #SBATCH --time=96:00:00 10 | #SBATCH --output=out_trans/trans_%a.out 11 | #SBATCH --error=err_trans/trans_%a.err 12 | #SBATCH --array=1 13 | #SBATCH --mail-type=FAIL 14 | #SBATCH --mail-user=sadiyasa@msu.edu 15 | 16 | # THIS IS IMPORTANT OR THE MODULES WILL NOT IMPORT 17 | . /etc/profile.d/modules.sh 18 | module load python/3.6.3 19 | module load cuda/8.0 20 | module load cudnn/6.0 21 | 22 | pip3 install --user virtualenv 23 | #conda install --user virtualenv 24 | virtualenv -p python3 venv 25 | source venv/bin/activate 26 | pip3 install -r requirements.txt 27 | KERAS_BACKEND=tensorflow 28 | python3 ecr_transfer.py 29 | --------------------------------------------------------------------------------