-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcnn.py
More file actions
352 lines (276 loc) · 14.3 KB
/
Copy pathcnn.py
File metadata and controls
352 lines (276 loc) · 14.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#!/usr/bin/env/ python
# ECBM E4040 Fall 2017 Assignment 2
# TensorFlow CNN
import tensorflow as tf
import numpy as np
import time
class conv_layer(object):
def __init__(self, input_x, in_channel, out_channel, kernel_shape, rand_seed, index=0):
"""
:param input_x: The input of the conv layer. Should be a 4D array like (batch_num, img_len, img_len, channel_num)
:param in_channel: The 4-th demension (channel number) of input matrix. For example, in_channel=3 means the input contains 3 channels.
:param out_channel: The 4-th demension (channel number) of output matrix. For example, out_channel=5 means the output contains 5 channels (feature maps).
:param kernel_shape: the shape of the kernel. For example, kernal_shape = 3 means you have a 3*3 kernel.
:param rand_seed: An integer that presents the random seed used to generate the initial parameter value.
:param index: The index of the layer. It is used for naming only.
"""
assert len(input_x.shape) == 4 and input_x.shape[1] == input_x.shape[2] and input_x.shape[3] == in_channel
with tf.variable_scope('conv_layer_%d' % index):
with tf.name_scope('conv_kernel'):
w_shape = [kernel_shape, kernel_shape, in_channel, out_channel]
weight = tf.get_variable(name='conv_kernel_%d' % index, shape=w_shape,
initializer=tf.glorot_uniform_initializer(seed=rand_seed))
self.weight = weight
with tf.variable_scope('conv_bias'):
b_shape = [out_channel]
bias = tf.get_variable(name='conv_bias_%d' % index, shape=b_shape,
initializer=tf.glorot_uniform_initializer(seed=rand_seed))
self.bias = bias
# strides [1, x_movement, y_movement, 1]
conv_out = tf.nn.conv2d(input_x, weight, strides=[1, 1, 1, 1], padding="SAME")
cell_out = tf.nn.relu(conv_out + bias)
self.cell_out = cell_out
tf.summary.histogram('conv_layer/{}/kernel'.format(index), weight)
tf.summary.histogram('conv_layer/{}/bias'.format(index), bias)
def output(self):
return self.cell_out
class max_pooling_layer(object):
def __init__(self, input_x, k_size, padding="SAME"):
"""
:param input_x: The input of the pooling layer.
:param k_size: The kernel size you want to behave pooling action.
:param padding: The padding setting. Read documents of tf.nn.max_pool for more information.
"""
with tf.variable_scope('max_pooling'):
# strides [1, k_size, k_size, 1]
pooling_shape = [1, k_size, k_size, 1]
cell_out = tf.nn.max_pool(input_x, strides=pooling_shape,
ksize=pooling_shape, padding=padding)
self.cell_out = cell_out
def output(self):
return self.cell_out
class norm_layer(object):
def __init__(self, input_x):
"""
:param input_x: The input that needed for normalization.
"""
with tf.variable_scope('batch_norm'):
mean, variance = tf.nn.moments(input_x, axes=[0], keep_dims=True)
cell_out = tf.nn.batch_normalization(input_x,
mean,
variance,
offset=np.zeros_like(input_x),
scale=np.ones_like(input_x),
variance_epsilon=1e-6,
name=None)
self.cell_out = cell_out
def output(self):
return self.cell_out
class fc_layer(object):
def __init__(self, input_x, in_size, out_size, rand_seed, activation_function=None, index=0):
"""
:param input_x: The input of the FC layer. It should be a flatten vector.
:param in_size: The length of input vector.
:param out_size: The length of output vector.
:param rand_seed: An integer that presents the random seed used to generate the initial parameter value.
:param keep_prob: The probability of dropout. Default set by 1.0 (no drop-out applied)
:param activation_function: The activation function for the output. Default set to None.
:param index: The index of the layer. It is used for naming only.
"""
with tf.variable_scope('fc_layer_%d' % index):
with tf.name_scope('fc_kernel'):
w_shape = [in_size, out_size]
weight = tf.get_variable(name='fc_kernel_%d' % index, shape=w_shape,
initializer=tf.glorot_uniform_initializer(seed=rand_seed))
self.weight = weight
with tf.variable_scope('fc_kernel'):
b_shape = [out_size]
bias = tf.get_variable(name='fc_bias_%d' % index, shape=b_shape,
initializer=tf.glorot_uniform_initializer(seed=rand_seed))
self.bias = bias
cell_out = tf.add(tf.matmul(input_x, weight), bias)
if activation_function is not None:
cell_out = activation_function(cell_out)
self.cell_out = cell_out
tf.summary.histogram('fc_layer/{}/kernel'.format(index), weight)
tf.summary.histogram('fc_layer/{}/bias'.format(index), bias)
def output(self):
return self.cell_out
def my_LeNet(input_x, input_y,
img_len=32, channel_num=3, output_size=10,
conv_featmap=[6, 16], fc_units=[84],
conv_kernel_size=[5, 5], dropout_rate=[0,0], pooling_size=[2, 2],
l2_norm=0.01, seed=235):
"""
LeNet is an early and famous CNN architecture for image classfication task.
It is proposed by Yann LeCun. Here we use its architecture as the startpoint
for your CNN practice. Its architecture is as follow.
input >> Conv2DLayer >> Conv2DLayer >> flatten >>
DenseLayer >> AffineLayer >> softmax loss >> output
Or
input >> [conv2d-maxpooling] >> [conv2d-maxpooling] >> flatten >>
DenseLayer >> AffineLayer >> softmax loss >> output
http://deeplearning.net/tutorial/lenet.html
"""
assert len(conv_featmap) == len(conv_kernel_size) and len(conv_featmap) == len(pooling_size)
# conv layer
conv_layer_0 = conv_layer(input_x=input_x,
in_channel=channel_num,
out_channel=conv_featmap[0],
kernel_shape=conv_kernel_size[0],
rand_seed=seed,
index=0)
pooling_layer_0 = max_pooling_layer(input_x=conv_layer_0.output(),
k_size=pooling_size[0],
padding="VALID")
drop_out_1=tf.nn.dropout(pooling_layer_0.output(),keep_prob=dropout_rate[1],seed=seed)
conv_layer_1 = conv_layer(input_x=drop_out_1,
in_channel=conv_featmap[0],
out_channel=conv_featmap[1],
kernel_shape=conv_kernel_size[1],
rand_seed=seed,
index=1)
pooling_layer_1 = max_pooling_layer(input_x=conv_layer_1.output(),
k_size=pooling_size[1],
padding="VALID")
drop_out_2=tf.nn.dropout(pooling_layer_1.output(),keep_prob=dropout_rate[2],seed=seed)
conv_layer_2 = conv_layer(input_x=drop_out_2,
in_channel=conv_featmap[1],
out_channel=conv_featmap[2],
kernel_shape=conv_kernel_size[2],
rand_seed=seed,
index=2)
pooling_layer_2 = max_pooling_layer(input_x=conv_layer_2.output(),
k_size=pooling_size[2],
padding="VALID")
# flatten
pool_shape = pooling_layer_2.output().get_shape()
img_vector_length = pool_shape[1].value * pool_shape[2].value * pool_shape[3].value
flatten = tf.reshape(pooling_layer_2.output(), shape=[-1, img_vector_length])
drop_out_0=tf.layers.dropout(flatten,rate=dropout_rate[0],training=True,seed=seed)
# fc layer
nm_layer_0 = norm_layer(input_x=drop_out_0)
fc_layer_0 = fc_layer(input_x=nm_layer_0.output(),
in_size=img_vector_length,
out_size=fc_units[0],
rand_seed=seed,
activation_function=tf.nn.relu,
index=0)
nm_layer_1 = norm_layer(input_x=fc_layer_0.output())
fc_layer_1 = fc_layer(input_x=nm_layer_1.output(),
in_size=fc_units[0],
out_size=output_size,
rand_seed=seed,
activation_function=None,
index=1)
# saving the parameters for l2_norm loss
conv_w = [conv_layer_0.weight, conv_layer_1.weight, conv_layer_2.weight]
fc_w = [fc_layer_0.weight, fc_layer_1.weight]
# loss
with tf.name_scope("loss"):
l2_loss = tf.reduce_sum([tf.norm(w) for w in fc_w])
l2_loss += tf.reduce_sum([tf.norm(w, axis=[-2, -1]) for w in conv_w])
label = tf.one_hot(input_y, 10)
cross_entropy_loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=label, logits=fc_layer_1.output()),
name='cross_entropy')
loss = tf.add(cross_entropy_loss, l2_norm * l2_loss, name='loss')
tf.summary.scalar('LeNet_loss', loss)
return fc_layer_1.output(), loss
def cross_entropy(output, input_y):
with tf.name_scope('cross_entropy'):
label = tf.one_hot(input_y, 10)
ce = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=label, logits=output))
return ce
def train_step(loss, learning_rate=1e-3):
with tf.name_scope('train_step'):
step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
return step
def evaluate(output, input_y):
with tf.name_scope('evaluate'):
pred = tf.argmax(output, axis=1)
error_num = tf.count_nonzero(pred - input_y, name='error_num')
tf.summary.scalar('LeNet_error_num', error_num)
return error_num
# training function for the LeNet model
def my_training(X_train, y_train, X_val, y_val,
conv_featmap=[6],
fc_units=[84],
conv_kernel_size=[5],
dropout_rate=[0.1],
pooling_size=[2],
l2_norm=0.01,
seed=235,
learning_rate=1e-2,
epoch=20,
batch_size=245,
verbose=False,
pre_trained_model=None):
print("Building my LeNet. Parameters: ")
print("conv_featmap={}".format(conv_featmap))
print("fc_units={}".format(fc_units))
print("conv_kernel_size={}".format(conv_kernel_size))
print("pooling_size={}".format(pooling_size))
print("l2_norm={}".format(l2_norm))
print("seed={}".format(seed))
print("learning_rate={}".format(learning_rate))
# define the variables and parameter needed during training
with tf.name_scope('inputs'):
xs = tf.placeholder(shape=[None, 32, 32, 3], dtype=tf.float32)
ys = tf.placeholder(shape=[None, ], dtype=tf.int64)
output, loss = my_LeNet(xs, ys,
img_len=32,
channel_num=3,
output_size=10,
conv_featmap=conv_featmap,
fc_units=fc_units,
conv_kernel_size=conv_kernel_size,
dropout_rate=dropout_rate,
pooling_size=pooling_size,
l2_norm=l2_norm,
seed=seed)
iters = int(X_train.shape[0] / batch_size)
print('number of batches for training: {}'.format(iters))
step = train_step(loss)
eve = evaluate(output, ys)
iter_total = 0
best_acc = 0
cur_model_name = 'lenet_{}'.format(int(time.time()))
with tf.Session() as sess:
merge = tf.summary.merge_all()
writer = tf.summary.FileWriter("log/{}".format(cur_model_name), sess.graph)
saver = tf.train.Saver()
sess.run(tf.global_variables_initializer())
# try to restore the pre_trained
if pre_trained_model is not None:
try:
print("Load the model from: {}".format(pre_trained_model))
saver.restore(sess, 'model/{}'.format(pre_trained_model))
except Exception:
print("Load model Failed!")
pass
for epc in range(epoch):
print("epoch {} ".format(epc + 1))
for itr in range(iters):
iter_total += 1
training_batch_x = X_train[itr * batch_size: (1 + itr) * batch_size]
training_batch_y = y_train[itr * batch_size: (1 + itr) * batch_size]
_, cur_loss = sess.run([step, loss], feed_dict={xs: training_batch_x, ys: training_batch_y})
if iter_total % 100 == 0:
# do validation
valid_eve, merge_result = sess.run([eve, merge], feed_dict={xs: X_val, ys: y_val})
valid_acc = 100 - valid_eve * 100 / y_val.shape[0]
if verbose:
print('{}/{} loss: {} validation accuracy : {}%'.format(
batch_size * (itr + 1),
X_train.shape[0],
cur_loss,
valid_acc))
# save the merge result summary
writer.add_summary(merge_result, iter_total)
# when achieve the best validation accuracy, we store the model paramters
if valid_acc > best_acc:
print('Best validation accuracy! iteration:{} accuracy: {}%'.format(iter_total, valid_acc))
best_acc = valid_acc
saver.save(sess, 'model/{}'.format(cur_model_name))
print("Traning ends. The best valid accuracy is {}. Model named {}.".format(best_acc, cur_model_name))