|
a |
|
b/EEGLearn/model.py |
|
|
1 |
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
|
2 |
## Created by: Yang Wang |
|
|
3 |
## School of Automation, Huazhong University of Science & Technology (HUST) |
|
|
4 |
## wangyang_sky@hust.edu.cn |
|
|
5 |
## Copyright (c) 2018 |
|
|
6 |
## |
|
|
7 |
## This source code is licensed under the MIT-style license found in the |
|
|
8 |
## LICENSE file in the root directory of this source tree |
|
|
9 |
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
|
10 |
|
|
|
11 |
#coding:utf-8 |
|
|
12 |
|
|
|
13 |
import tensorflow as tf |
|
|
14 |
|
|
|
15 |
def my_conv2d(inputs, filters, kernel_size, strides=(1, 1), padding='same', activation=None, name=None, reuse=None): |
|
|
16 |
return tf.layers.conv2d(inputs=inputs, filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, activation=activation, |
|
|
17 |
kernel_initializer=tf.truncated_normal_initializer(stddev=0.1), bias_initializer=tf.constant_initializer(0.1), name=name, reuse=reuse) |
|
|
18 |
|
|
|
19 |
def build_cnn(input_image=None, image_size=32, n_colors=3, activation_function=tf.nn.relu, reuse=None, name='VGG_NET_CNN'): |
|
|
20 |
# VGG_NET 32 # [samples, W, H, colors] |
|
|
21 |
with tf.variable_scope(name, reuse=reuse): |
|
|
22 |
input_image = tf.reshape(input_image, shape=[-1, image_size, image_size, n_colors], name='Reshape_inputs') |
|
|
23 |
# layer_1 # 4个3*3*32 |
|
|
24 |
|
|
|
25 |
h_conv1_1 = my_conv2d(input_image, filters=32, kernel_size=(3,3), activation=activation_function, name='conv1_1') |
|
|
26 |
h_conv1_2 = my_conv2d(h_conv1_1, filters=32, kernel_size=(3,3), activation=activation_function, name='conv1_2') |
|
|
27 |
h_conv1_3 = my_conv2d(h_conv1_2, filters=32, kernel_size=(3,3), activation=activation_function, name='conv1_3') |
|
|
28 |
h_conv1_4 = my_conv2d(h_conv1_3, filters=32, kernel_size=(3,3), activation=activation_function, name='conv1_4') |
|
|
29 |
h_pool1 = tf.layers.max_pooling2d(h_conv1_4, pool_size=(2,2), strides=(2,2), padding='same', name='max_pooling_1') # shape is (None, 16, 16, 32) |
|
|
30 |
|
|
|
31 |
# layer_2 |
|
|
32 |
h_conv2_1 = my_conv2d(h_pool1, filters=64, kernel_size=(3,3), activation=activation_function, name='conv2_1') |
|
|
33 |
h_conv2_2 = my_conv2d(h_conv2_1, filters=64, kernel_size=(3,3), activation=activation_function, name='conv2_2') |
|
|
34 |
h_pool2 = tf.layers.max_pooling2d(h_conv2_2, pool_size=(2,2), strides=(2,2), padding='same', name='max_pooling_2') # shape is (None, 8, 8, 64) |
|
|
35 |
|
|
|
36 |
# layer_3 |
|
|
37 |
h_conv3_1 = my_conv2d(h_pool2, filters=128, kernel_size=(3,3), activation=activation_function, name='conv3_1') |
|
|
38 |
h_pool3 = tf.layers.max_pooling2d(h_conv3_1, pool_size=(2,2), strides=(2,2), padding='same', name='max_pooling_3') # shape is (None, 4, 4, 128) |
|
|
39 |
|
|
|
40 |
return h_pool3 |
|
|
41 |
|
|
|
42 |
|
|
|
43 |
def build_convpool_max(input_image, nb_classes, image_size=32, n_colors=3, |
|
|
44 |
n_timewin=7, dropout_rate=0.5, name='CNN_Max', train=True, reuse=False): |
|
|
45 |
""" |
|
|
46 |
Builds the complete network with maxpooling layer in time. |
|
|
47 |
|
|
|
48 |
:param input_image: list of EEG images (one image per time window) |
|
|
49 |
:param nb_classes: number of classes |
|
|
50 |
:param image_size: size of the input image (assumes a square input) |
|
|
51 |
:param n_colors: number of color channels in the image |
|
|
52 |
:param n_timewin: number of time windows in the snippet |
|
|
53 |
:return: a pointer to the output of last layer |
|
|
54 |
""" |
|
|
55 |
with tf.name_scope(name): |
|
|
56 |
with tf.name_scope('Parallel_CNNs'): |
|
|
57 |
convnets = [] |
|
|
58 |
# Build 7 parallel CNNs with shared weights |
|
|
59 |
for i in range(n_timewin): |
|
|
60 |
if i==0: |
|
|
61 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=reuse) |
|
|
62 |
else: |
|
|
63 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=True) |
|
|
64 |
convnets.append(convnet) # list contains [None, 4, 4, 128] |
|
|
65 |
convnets = tf.stack(convnets) # [n_timewin, nSamples, 4, 4, 128] |
|
|
66 |
convnets = tf.transpose(convnets, [1,0,2,3,4]) # [nSamples, n_timewin, 4, 4, 128] |
|
|
67 |
|
|
|
68 |
with tf.variable_scope('Max_pooling_over_flames'): |
|
|
69 |
# convpooling using Max pooling over frames |
|
|
70 |
convnets = tf.reshape(convnets, shape=[ -1, n_timewin, 4*4*128, 1]) |
|
|
71 |
convpool = tf.nn.max_pool(convnets, # [nSamples, 1,4*4*128, 1] |
|
|
72 |
ksize=[1, n_timewin, 1, 1], strides=[1, 1, 1, 1], padding='VALID', name='convpool_max') |
|
|
73 |
|
|
|
74 |
|
|
|
75 |
convpool_flat = tf.reshape(convpool, [-1, 4*4*128]) |
|
|
76 |
h_fc1_drop1 = tf.layers.dropout(convpool_flat, rate=dropout_rate, training=train, name='dropout_1') |
|
|
77 |
# input shape [batch, 4*4*128] output shape [batch, 512] |
|
|
78 |
h_fc1 = tf.layers.dense(h_fc1_drop1, 512, activation=tf.nn.relu, name='fc_relu_512') |
|
|
79 |
# dropout |
|
|
80 |
h_fc1_drop2 = tf.layers.dropout(h_fc1, rate=dropout_rate, training=train, name='dropout_2') |
|
|
81 |
# inputshape [batch, 512] output shape [batch, nb_classes] # the loss function contains the softmax activation |
|
|
82 |
prediction = tf.layers.dense(h_fc1_drop2, nb_classes, name='fc_softmax') |
|
|
83 |
|
|
|
84 |
return prediction |
|
|
85 |
|
|
|
86 |
def build_convpool_conv1d(input_image, nb_classes, image_size=32, n_colors=3, |
|
|
87 |
n_timewin=7, dropout_rate=0.5, name='CNN_Conv1d', train=True, reuse=False): |
|
|
88 |
""" |
|
|
89 |
Builds the complete network with 1D-conv layer to integrate time from sequences of EEG images. |
|
|
90 |
|
|
|
91 |
:param input_image: list of EEG images (one image per time window) |
|
|
92 |
:param nb_classes: number of classes |
|
|
93 |
:param image_size: size of the input image (assumes a square input)S |
|
|
94 |
:param n_colors: number of color channels in the image |
|
|
95 |
:param n_timewin: number of time windows in the snippet |
|
|
96 |
:return: a pointer to the output of last layer |
|
|
97 |
""" |
|
|
98 |
with tf.name_scope(name): |
|
|
99 |
with tf.name_scope('Parallel_CNNs'): |
|
|
100 |
convnets = [] |
|
|
101 |
# Build 7 parallel CNNs with shared weights |
|
|
102 |
for i in range(n_timewin): |
|
|
103 |
if i==0: |
|
|
104 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=reuse) |
|
|
105 |
else: |
|
|
106 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=True) |
|
|
107 |
convnets.append(convnet) |
|
|
108 |
convnets = tf.stack(convnets) |
|
|
109 |
convnets = tf.transpose(convnets, [1,0,2,3,4]) |
|
|
110 |
|
|
|
111 |
with tf.variable_scope('Conv1d_over_flames'): |
|
|
112 |
convnets = tf.reshape(convnets, shape=[ -1, n_timewin, 4*4*128, 1]) |
|
|
113 |
convpool = my_conv2d(convnets, filters=64, kernel_size=(3, 4*4*128), strides=(1, 1), padding='valid', activation=tf.nn.relu, name='convpool_conv1d') |
|
|
114 |
|
|
|
115 |
|
|
|
116 |
with tf.variable_scope('Output_layers'): |
|
|
117 |
convpool_flat = tf.reshape(convpool, [-1, (n_timewin-2)*64]) |
|
|
118 |
h_fc1_drop1 = tf.layers.dropout(convpool_flat, rate=dropout_rate, training=train, name='dropout_1') |
|
|
119 |
h_fc1 = tf.layers.dense(h_fc1_drop1, 256, activation=tf.nn.relu, name='fc_relu_256') |
|
|
120 |
h_fc1_drop2 = tf.layers.dropout(h_fc1, rate=dropout_rate, training=train, name='dropout_2') |
|
|
121 |
prediction = tf.layers.dense(h_fc1_drop2, nb_classes, name='fc_softmax') |
|
|
122 |
|
|
|
123 |
return prediction |
|
|
124 |
|
|
|
125 |
|
|
|
126 |
def build_convpool_lstm(input_image, nb_classes, grad_clip=110, image_size=32, n_colors=3, |
|
|
127 |
n_timewin=7, dropout_rate=0.5, num_units=128, batch_size=32, name='CNN_LSTM', train=True, reuse=False): |
|
|
128 |
""" |
|
|
129 |
Builds the complete network with LSTM layer to integrate time from sequences of EEG images. |
|
|
130 |
|
|
|
131 |
:param input_image: list of EEG images (one image per time window) |
|
|
132 |
:param nb_classes: number of classes |
|
|
133 |
:param grad_clip: the gradient messages are clipped to the given value during |
|
|
134 |
the backward pass. |
|
|
135 |
:param image_size: size of the input image (assumes a square input) |
|
|
136 |
:param n_colors: number of color channels in the image |
|
|
137 |
:param n_timewin: number of time windows in the snippet |
|
|
138 |
:param num_units: number of units in the LSTMCell |
|
|
139 |
:return: a pointer to the output of last layer |
|
|
140 |
""" |
|
|
141 |
with tf.name_scope(name): |
|
|
142 |
with tf.name_scope('Parallel_CNNs'): |
|
|
143 |
convnets = [] |
|
|
144 |
# Build 7 parallel CNNs with shared weights |
|
|
145 |
for i in range(n_timewin): |
|
|
146 |
if i==0: |
|
|
147 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=reuse) |
|
|
148 |
else: |
|
|
149 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=True) |
|
|
150 |
convnets.append(convnet) |
|
|
151 |
convnets = tf.stack(convnets) |
|
|
152 |
convnets = tf.transpose(convnets, [1,0,2,3,4]) # 调换轴 shape: (nSamples, n_timewin, 4, 4, 128) |
|
|
153 |
|
|
|
154 |
with tf.variable_scope('LSTM_layer'): |
|
|
155 |
# (nSamples, n_timewin, 4, 4, 128) ==> (nSamples, n_timewin, 4*4*128) |
|
|
156 |
convnets = tf.reshape(convnets, shape=[-1, n_timewin, 4*4*128], name='Reshape_for_lstm') |
|
|
157 |
#lstm cell inputs:[batchs, time_steps, 4*4*128] |
|
|
158 |
with tf.variable_scope('LSTM_Cell'): |
|
|
159 |
lstm_cell = tf.contrib.rnn.BasicLSTMCell(num_units=num_units, forget_bias=1.0, state_is_tuple=True) |
|
|
160 |
outputs, final_state = tf.nn.dynamic_rnn(lstm_cell, convnets, dtype=tf.float32, time_major=False) |
|
|
161 |
# outputs.shape is (batch_size, time_steps, num_units) |
|
|
162 |
outputs = tf.transpose(outputs, [1,0,2]) # (time_steps, batch_size, num_units) |
|
|
163 |
outputs = outputs[-1] |
|
|
164 |
|
|
|
165 |
with tf.variable_scope('Output_layers'): |
|
|
166 |
h_fc1_drop1 = tf.layers.dropout(outputs, rate=dropout_rate, training=train, name='dropout_1') |
|
|
167 |
h_fc1 = tf.layers.dense(h_fc1_drop1, 256, activation=tf.nn.relu, name='fc_relu_256') |
|
|
168 |
h_fc1_drop2 = tf.layers.dropout(h_fc1, rate=dropout_rate, training=train, name='dropout_2') |
|
|
169 |
prediction = tf.layers.dense(h_fc1_drop2, nb_classes, name='fc_softmax') |
|
|
170 |
|
|
|
171 |
return prediction |
|
|
172 |
|
|
|
173 |
|
|
|
174 |
def build_convpool_mix(input_image, nb_classes, grad_clip=110, image_size=32, n_colors=3, |
|
|
175 |
n_timewin=7, dropout_rate=0.5, num_units=128, batch_size=32, name='CNN_Mix', train=True, reuse=False): |
|
|
176 |
""" |
|
|
177 |
Builds the complete network with LSTM and 1D-conv layers combined |
|
|
178 |
|
|
|
179 |
:param input_image: list of EEG images (one image per time window) |
|
|
180 |
:param nb_classes: number of classes |
|
|
181 |
:param grad_clip: the gradient messages are clipped to the given value during |
|
|
182 |
the backward pass. |
|
|
183 |
:param imsize: size of the input image (assumes a square input) |
|
|
184 |
:param n_colors: number of color channels in the image |
|
|
185 |
:param n_timewin: number of time windows in the snippet |
|
|
186 |
:return: a pointer to the output of last layer |
|
|
187 |
""" |
|
|
188 |
with tf.name_scope(name): |
|
|
189 |
with tf.name_scope('Parallel_CNNs'): |
|
|
190 |
convnets = [] |
|
|
191 |
# Build 7 parallel CNNs with shared weights |
|
|
192 |
for i in range(n_timewin): |
|
|
193 |
if i==0: |
|
|
194 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=reuse) |
|
|
195 |
else: |
|
|
196 |
convnet = build_cnn(input_image[i],image_size=image_size,n_colors=n_colors, reuse=True) |
|
|
197 |
convnets.append(convnet) |
|
|
198 |
convnets = tf.stack(convnets) |
|
|
199 |
convnets = tf.transpose(convnets, [1,0,2,3,4]) |
|
|
200 |
|
|
|
201 |
with tf.variable_scope('Conv1d_over_flames'): |
|
|
202 |
convpool = tf.reshape(convnets, shape=[ -1, n_timewin, 4*4*128, 1]) |
|
|
203 |
convpool = my_conv2d(convpool, filters=64, kernel_size=(3, 4*4*128), strides=(1, 1), padding='valid', activation=tf.nn.relu, name='convpool_conv1d') |
|
|
204 |
conv1d_out = tf.reshape(convpool, [-1, (n_timewin-2)*64]) |
|
|
205 |
|
|
|
206 |
with tf.variable_scope('LSTM_layer'): |
|
|
207 |
# (nSamples, n_timewin, 4, 4, 128) ==> (nSamples, n_timewin, 4*4*128) |
|
|
208 |
convnets = tf.reshape(convnets, shape=[-1, n_timewin, 4*4*128], name='Reshape_for_lstm') |
|
|
209 |
#lstm cell inputs:[batchs, time_steps, 4*4*128] |
|
|
210 |
with tf.variable_scope('LSTM_Cell'): |
|
|
211 |
lstm_cell = tf.contrib.rnn.BasicLSTMCell(num_units=num_units, forget_bias=1.0, state_is_tuple=True) |
|
|
212 |
outputs, final_state = tf.nn.dynamic_rnn(lstm_cell, convnets, dtype=tf.float32, time_major=False) |
|
|
213 |
# outputs.shape is (batch_size, time_steps, num_units) |
|
|
214 |
outputs = tf.transpose(outputs, [1,0,2]) |
|
|
215 |
lstm_out = outputs[-1] |
|
|
216 |
|
|
|
217 |
with tf.variable_scope('Output_layers'): |
|
|
218 |
dense_in = tf.concat((conv1d_out, lstm_out), axis=1, name='concat_conv1d_lstm') # shape [batch, (n_timewin-2)*64+num_units] |
|
|
219 |
h_fc1_drop1 = tf.layers.dropout(dense_in, rate=dropout_rate, training=train, name='dropout_1') |
|
|
220 |
h_fc1 = tf.layers.dense(h_fc1_drop1, 512, activation=tf.nn.relu, name='fc_relu_512') |
|
|
221 |
h_fc1_drop2 = tf.layers.dropout(h_fc1, rate=dropout_rate, training=train, name='dropout_2') |
|
|
222 |
prediction = tf.layers.dense(h_fc1_drop2, nb_classes, name='fc_softmax') |
|
|
223 |
|
|
|
224 |
return prediction |