├── 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 | [](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 |
--------------------------------------------------------------------------------