-
Notifications
You must be signed in to change notification settings - Fork 6
/
ResnetV2.py
234 lines (170 loc) · 10.1 KB
/
ResnetV2.py
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
import keras
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D
from keras.layers import Dense, BatchNormalization, Activation
from keras.layers import Flatten, Input
from keras.layers.merge import add
from keras.regularizers import l2
from keras import backend as K
from keras.models import Model
import numpy as np
class resnetv2_builder:
def __init__(self, input_shape, output_units, block_fn = "bottleneck", num_blocks = [3,4,6,3], num_filters = [64,128,256,512],
init_kernel = (7,7), init_strides = (2,2), init_filters = 64, init_maxpooling = True,
regularizer = l2(1e-4), initializer = "he_normal"):
'''
constructor of resnetv2_builder
:param input_shape: input shape of dataset
:param output_units: classification labels
:param block_fn: type of block function you want to use, if the Convolution layer < 50 -> basic, bottlenect for layer > 50
:param num_blocks: number of block in each stages
:param num_filters: number of filters in each stages
:param init_kernel: The kernel size for first convolution layer
:param init_strides: The stride size for first convolution layer
:param init_filters: The number of filter for first convolution layer
:param init_maxpooling: Use the maxpooling after first convolution or not
:param regularizer: regularizer for convolution layer
:param initializer: initializer for convolution and fully-connect layer
'''
assert len(input_shape) == 3, "input shape should has dim 3 ( row, col, channel or channel, row, col )"
assert block_fn == "bottleneck" or block_fn == "basic", "wrong block function, should be bottleneck or basic"
assert len(num_blocks) == len(num_filters), "num_blocks should has same length as num_filters"
assert len(num_blocks) == len(num_filters), "The length of num_blocks should equal to the length of num_filters"
self.input_shape = input_shape
self.output_units = output_units
self.block_fn = block_fn
self.num_blocks = np.asarray(num_blocks, dtype = int)
self.num_filters = np.asarray(num_filters, dtype = int)
self.init_kernel = init_kernel
self.init_strides = init_strides
self.init_filters = init_filters
self.init_maxpooling = init_maxpooling
self.regularizer = regularizer
self.initializer = initializer
if K.image_dim_ordering() == "tf":
self.row_axis = 1
self.col_axis = 2
self.channel_axis = 3
else:
self.row_axis = 2
self.col_axis = 3
self.channel_axis = 1
def _cn_bn_relu(self, filters = 64, kernel_size = (3,3), strides = (1,1), padding = "same",
kernel_regularizer = l2(1e-4), kernel_initializer = "he_normal"):
'''
convenient function to build convolution -> batch_nromalization -> relu activation layers
'''
def f(input_x):
x = Conv2D(filters = filters, kernel_size = kernel_size, strides = strides, padding = padding,
kernel_regularizer = kernel_regularizer, kernel_initializer = kernel_initializer)(input_x)
x = BatchNormalization(axis = self.channel_axis)(x)
x = Activation("relu")(x)
return x
return f
def _bn_relu_cn(self, filters = 64, kernel_size = (3,3), strides = (1,1), padding = "same",
kernel_regularizer = l2(1e-4), kernel_initializer = "he_normal"):
'''
convenient function to build batch_nromalization -> relu activation -> convolution layers
'''
def f(input_x):
x = BatchNormalization(axis = self.channel_axis)(input_x)
x = Activation("relu")(x)
x = Conv2D(filters = filters, kernel_size = kernel_size, strides = strides, padding = padding,
kernel_regularizer = kernel_regularizer, kernel_initializer = kernel_initializer)(x)
return x
return f
def _shortcut(self, input_x, residual):
'''
Member function for merging input and residual layers
'''
input_dim = K.int_shape(input_x)
res_dim = K.int_shape(residual)
stride_width = int(round(input_dim[self.col_axis] / res_dim[self.col_axis]))
stride_height = int(round(input_dim[self.row_axis] / res_dim[self.row_axis]))
x = input_x
if stride_height != 1 or stride_width != 1 or input_dim[self.channel_axis] != res_dim[self.channel_axis]:
x = Conv2D(filters = res_dim[self.channel_axis], kernel_size = (1,1), strides = (stride_width, stride_height),
padding = "same", kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
return add([x,residual])
def basic_block(self, filters, strides = (1,1), first_block_in_first_stage = False):
'''
In paper, the basic block only be used when the layers you want is smaller than 50
function to build bottleneck block, which compose by 2 batch normalization -> relu -> convolution blocks
:param first_block_in_first_stage: the flag of whether current block is at first block and first stage
:return: return a basic block
'''
def f(input_x):
x = input_x
if first_block_in_first_stage:
#when the current layer is in first block of first stage
#we only do convolution since we just did the MaxPooling, Batch Normalization and activation
x = Conv2D(filters = filters, kernel_size = (3,3), strides = (1,1), padding = "same",
kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
else:
x = self._bn_relu_cn(filters = filters, kernel_size = (3,3), strides = strides,
kernel_initializer = self.initializer, kernel_regularizer = self.regularizer)(x)
x = self._bn_relu_cn(filters = filters, kernel_size = (3,3), strides = (1,1),
kernel_initializer = self.initializer, kernel_regularizer = self.regularizer)(x)
return self._shortcut(input_x = input_x, residual = x)
return f
def bottleneck_block(self, filters, strides = (1,1), first_block_in_first_stage = False):
'''
In paper, the bottleneck block only be used when the layers you want is bigger than 49
function to build bottleneck block, which compose by 3 batch normalization -> relu -> convolution blocks
:param first_block_in_first_stage: the flag of whether current block is at first block and first stage
:return: return a bottleneck block
'''
def f(input_x):
x = input_x
if first_block_in_first_stage:
# when the current layer is in first block of first stage
# we only do convolution since we just did the MaxPooling, Batch Normalization and activation
x = Conv2D(filters = filters, kernel_size = (1,1), strides = (1,1), padding = "same",
kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
else:
x = self._bn_relu_cn(filters = filters, kernel_size = (1,1), strides = strides, padding = "same",
kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
x = self._bn_relu_cn(filters = filters, kernel_size = (3,3), strides = (1,1),
kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
x = self._bn_relu_cn(filters = filters * 4, kernel_size = (1,1), strides = (1,1),
kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(x)
return self._shortcut(input_x = input_x, residual = x)
return f
def _resnet_block(self, filters, blocks, is_first_stage = False):
'''
:param filters: filter number of current stage
:param blocks: block number of current stage
:param is_first_stage: flag of whether current stage is first stage or not
:return: A resnet block which build by several basic or bottleneck blocks
'''
def f(input_x):
x = input_x
for block in range(blocks):
strides = (1,1)
if is_first_stage != True and block == 0:
strides = (2,2)
if self.block_fn == "basic":
x = self.basic_block(filters = filters, strides = strides, first_block_in_first_stage = is_first_stage and (block == 0))(x)
else:
x = self.bottleneck_block(filters = filters, strides = strides, first_block_in_first_stage = is_first_stage and (block == 0))(x)
return x
return f
def build_resnet(self):
'''
The main function to build a model
:return: Return a resnet v2 model
'''
input_x = Input(self.input_shape)
x = self._cn_bn_relu(filters = self.init_filters, kernel_size = self.init_kernel, strides = self.init_strides,
padding = "same", kernel_regularizer = self.regularizer, kernel_initializer = self.initializer)(input_x)
if self.init_maxpooling:
x = MaxPooling2D(pool_size = (3,3), strides = (2,2), padding = "same")(x)
for i,(blocks, filters) in enumerate(zip(self.num_blocks, self.num_filters)):
x = self._resnet_block(filters = filters, blocks = blocks, is_first_stage = (i==0))(x)
x = BatchNormalization(axis = self.channel_axis)(x)
x = Activation("relu")(x)
final_shape = K.int_shape(x)
x = AveragePooling2D(pool_size = (final_shape[self.row_axis], final_shape[self.col_axis]), strides = (1,1))(x)
x = Flatten()(x)
output_x = Dense(units = self.output_units, kernel_initializer = self.initializer, activation = "softmax")(x)
res_model = Model(inputs = [input_x], outputs = [output_x])
return res_model