1+ # -*- coding: utf-8 -*-
2+ '''ResNet50 model for Keras.
3+ # Reference:
4+ - [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385)
5+ Adapted from code contributed by BigMoyan.
6+ '''
7+ from __future__ import print_function
8+
9+ import numpy as np
10+ import warnings
11+
12+ from keras .layers import Input
13+ from keras import layers
14+ from keras .layers import Dense
15+ from keras .layers import Activation
16+ from keras .layers import Flatten
17+ from keras .layers import Conv2D
18+ from keras .layers import MaxPooling2D
19+ from keras .layers import GlobalMaxPooling2D
20+ from keras .layers import ZeroPadding2D
21+ from keras .layers import AveragePooling2D
22+ from keras .layers import GlobalAveragePooling2D
23+ from keras .layers import BatchNormalization
24+ from keras .models import Model
25+ from keras .preprocessing import image
26+ import keras .backend as K
27+ from keras .utils import layer_utils
28+ from keras .utils .data_utils import get_file
29+ from keras .applications .imagenet_utils import decode_predictions
30+ from keras .applications .imagenet_utils import preprocess_input
31+ from keras .applications .imagenet_utils import _obtain_input_shape
32+ from keras .engine .topology import get_source_inputs
33+
34+
35+ WEIGHTS_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels.h5'
36+ WEIGHTS_PATH_NO_TOP = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'
37+
38+
39+ def identity_block (input_tensor , kernel_size , filters , stage , block ):
40+ """The identity block is the block that has no conv layer at shortcut.
41+ # Arguments
42+ input_tensor: input tensor
43+ kernel_size: defualt 3, the kernel size of middle conv layer at main path
44+ filters: list of integers, the filterss of 3 conv layer at main path
45+ stage: integer, current stage label, used for generating layer names
46+ block: 'a','b'..., current block label, used for generating layer names
47+ # Returns
48+ Output tensor for the block.
49+ """
50+ filters1 , filters2 , filters3 = filters
51+ if K .image_data_format () == 'channels_last' :
52+ bn_axis = 3
53+ else :
54+ bn_axis = 1
55+ conv_name_base = 'res' + str (stage ) + block + '_branch'
56+ bn_name_base = 'bn' + str (stage ) + block + '_branch'
57+
58+ x = Conv2D (filters1 , (1 , 1 ), name = conv_name_base + '2a' )(input_tensor )
59+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2a' )(x )
60+ x = Activation ('relu' )(x )
61+
62+ x = Conv2D (filters2 , kernel_size ,
63+ padding = 'same' , name = conv_name_base + '2b' )(x )
64+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2b' )(x )
65+ x = Activation ('relu' )(x )
66+
67+ x = Conv2D (filters3 , (1 , 1 ), name = conv_name_base + '2c' )(x )
68+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2c' )(x )
69+
70+ x = layers .add ([x , input_tensor ])
71+ x = Activation ('relu' )(x )
72+ return x
73+
74+
75+ def conv_block (input_tensor , kernel_size , filters , stage , block , strides = (2 , 2 )):
76+ """conv_block is the block that has a conv layer at shortcut
77+ # Arguments
78+ input_tensor: input tensor
79+ kernel_size: defualt 3, the kernel size of middle conv layer at main path
80+ filters: list of integers, the filterss of 3 conv layer at main path
81+ stage: integer, current stage label, used for generating layer names
82+ block: 'a','b'..., current block label, used for generating layer names
83+ # Returns
84+ Output tensor for the block.
85+ Note that from stage 3, the first conv layer at main path is with strides=(2,2)
86+ And the shortcut should have strides=(2,2) as well
87+ """
88+ filters1 , filters2 , filters3 = filters
89+ if K .image_data_format () == 'channels_last' :
90+ bn_axis = 3
91+ else :
92+ bn_axis = 1
93+ conv_name_base = 'res' + str (stage ) + block + '_branch'
94+ bn_name_base = 'bn' + str (stage ) + block + '_branch'
95+
96+ x = Conv2D (filters1 , (1 , 1 ), strides = strides ,
97+ name = conv_name_base + '2a' )(input_tensor )
98+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2a' )(x )
99+ x = Activation ('relu' )(x )
100+
101+ x = Conv2D (filters2 , kernel_size , padding = 'same' ,
102+ name = conv_name_base + '2b' )(x )
103+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2b' )(x )
104+ x = Activation ('relu' )(x )
105+
106+ x = Conv2D (filters3 , (1 , 1 ), name = conv_name_base + '2c' )(x )
107+ x = BatchNormalization (axis = bn_axis , name = bn_name_base + '2c' )(x )
108+
109+ shortcut = Conv2D (filters3 , (1 , 1 ), strides = strides ,
110+ name = conv_name_base + '1' )(input_tensor )
111+ shortcut = BatchNormalization (axis = bn_axis , name = bn_name_base + '1' )(shortcut )
112+
113+ x = layers .add ([x , shortcut ])
114+ x = Activation ('relu' )(x )
115+ return x
116+
117+
118+ def ResNet50 (include_top = True , weights = 'imagenet' ,
119+ input_tensor = None , input_shape = None ,
120+ pooling = None ,
121+ classes = 1000 ):
122+ """Instantiates the ResNet50 architecture.
123+ Optionally loads weights pre-trained
124+ on ImageNet. Note that when using TensorFlow,
125+ for best performance you should set
126+ `image_data_format="channels_last"` in your Keras config
127+ at ~/.keras/keras.json.
128+ The model and the weights are compatible with both
129+ TensorFlow and Theano. The data format
130+ convention used by the model is the one
131+ specified in your Keras config file.
132+ # Arguments
133+ include_top: whether to include the fully-connected
134+ layer at the top of the network.
135+ weights: one of `None` (random initialization)
136+ or "imagenet" (pre-training on ImageNet).
137+ input_tensor: optional Keras tensor (i.e. output of `layers.Input()`)
138+ to use as image input for the model.
139+ input_shape: optional shape tuple, only to be specified
140+ if `include_top` is False (otherwise the input shape
141+ has to be `(224, 224, 3)` (with `channels_last` data format)
142+ or `(3, 224, 244)` (with `channels_first` data format).
143+ It should have exactly 3 inputs channels,
144+ and width and height should be no smaller than 197.
145+ E.g. `(200, 200, 3)` would be one valid value.
146+ pooling: Optional pooling mode for feature extraction
147+ when `include_top` is `False`.
148+ - `None` means that the output of the model will be
149+ the 4D tensor output of the
150+ last convolutional layer.
151+ - `avg` means that global average pooling
152+ will be applied to the output of the
153+ last convolutional layer, and thus
154+ the output of the model will be a 2D tensor.
155+ - `max` means that global max pooling will
156+ be applied.
157+ classes: optional number of classes to classify images
158+ into, only to be specified if `include_top` is True, and
159+ if no `weights` argument is specified.
160+ # Returns
161+ A Keras model instance.
162+ # Raises
163+ ValueError: in case of invalid argument for `weights`,
164+ or invalid input shape.
165+ """
166+ if weights not in {'imagenet' , None }:
167+ raise ValueError ('The `weights` argument should be either '
168+ '`None` (random initialization) or `imagenet` '
169+ '(pre-training on ImageNet).' )
170+
171+ if weights == 'imagenet' and include_top and classes != 1000 :
172+ raise ValueError ('If using `weights` as imagenet with `include_top`'
173+ ' as true, `classes` should be 1000' )
174+
175+ # Determine proper input shape
176+ input_shape = _obtain_input_shape (input_shape ,
177+ default_size = 224 ,
178+ min_size = 197 ,
179+ data_format = K .image_data_format (),
180+ include_top = include_top )
181+
182+ if input_tensor is None :
183+ img_input = Input (shape = input_shape )
184+ else :
185+ if not K .is_keras_tensor (input_tensor ):
186+ img_input = Input (tensor = input_tensor , shape = input_shape )
187+ else :
188+ img_input = input_tensor
189+ if K .image_data_format () == 'channels_last' :
190+ bn_axis = 3
191+ else :
192+ bn_axis = 1
193+
194+ x = ZeroPadding2D ((3 , 3 ))(img_input )
195+ x = Conv2D (64 , (7 , 7 ), strides = (2 , 2 ), name = 'conv1' )(x )
196+ x = BatchNormalization (axis = bn_axis , name = 'bn_conv1' )(x )
197+ x = Activation ('relu' )(x )
198+ x = MaxPooling2D ((3 , 3 ), strides = (2 , 2 ))(x )
199+
200+ x = conv_block (x , 3 , [64 , 64 , 256 ], stage = 2 , block = 'a' , strides = (1 , 1 ))
201+ x = identity_block (x , 3 , [64 , 64 , 256 ], stage = 2 , block = 'b' )
202+ x = identity_block (x , 3 , [64 , 64 , 256 ], stage = 2 , block = 'c' )
203+
204+ x = conv_block (x , 3 , [128 , 128 , 512 ], stage = 3 , block = 'a' )
205+ x = identity_block (x , 3 , [128 , 128 , 512 ], stage = 3 , block = 'b' )
206+ x = identity_block (x , 3 , [128 , 128 , 512 ], stage = 3 , block = 'c' )
207+ x = identity_block (x , 3 , [128 , 128 , 512 ], stage = 3 , block = 'd' )
208+
209+ x = conv_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'a' )
210+ x = identity_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'b' )
211+ x = identity_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'c' )
212+ x = identity_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'd' )
213+ x = identity_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'e' )
214+ x = identity_block (x , 3 , [256 , 256 , 1024 ], stage = 4 , block = 'f' )
215+
216+ x = conv_block (x , 3 , [512 , 512 , 2048 ], stage = 5 , block = 'a' )
217+ x = identity_block (x , 3 , [512 , 512 , 2048 ], stage = 5 , block = 'b' )
218+ x = identity_block (x , 3 , [512 , 512 , 2048 ], stage = 5 , block = 'c' )
219+
220+ x = AveragePooling2D ((7 , 7 ), name = 'avg_pool' )(x )
221+
222+ if include_top :
223+ x = Flatten ()(x )
224+ x = Dense (classes , activation = 'softmax' , name = 'fc1000' )(x )
225+ else :
226+ if pooling == 'avg' :
227+ x = GlobalAveragePooling2D ()(x )
228+ elif pooling == 'max' :
229+ x = GlobalMaxPooling2D ()(x )
230+
231+ # Ensure that the model takes into account
232+ # any potential predecessors of `input_tensor`.
233+ if input_tensor is not None :
234+ inputs = get_source_inputs (input_tensor )
235+ else :
236+ inputs = img_input
237+ # Create model.
238+ model = Model (inputs , x , name = 'resnet50' )
239+
240+ # load weights
241+ if weights == 'imagenet' :
242+ if include_top :
243+ weights_path = get_file ('resnet50_weights_tf_dim_ordering_tf_kernels.h5' ,
244+ WEIGHTS_PATH ,
245+ cache_subdir = 'models' ,
246+ md5_hash = 'a7b3fe01876f51b976af0dea6bc144eb' )
247+ else :
248+ weights_path = get_file ('resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5' ,
249+ WEIGHTS_PATH_NO_TOP ,
250+ cache_subdir = 'models' ,
251+ md5_hash = 'a268eb855778b3df3c7506639542a6af' )
252+ model .load_weights (weights_path )
253+ if K .backend () == 'theano' :
254+ layer_utils .convert_all_kernels_in_model (model )
255+
256+ if K .image_data_format () == 'channels_first' :
257+ if include_top :
258+ maxpool = model .get_layer (name = 'avg_pool' )
259+ shape = maxpool .output_shape [1 :]
260+ dense = model .get_layer (name = 'fc1000' )
261+ layer_utils .convert_dense_weights_data_format (dense , shape , 'channels_first' )
262+
263+ if K .backend () == 'tensorflow' :
264+ warnings .warn ('You are using the TensorFlow backend, yet you '
265+ 'are using the Theano '
266+ 'image data format convention '
267+ '(`image_data_format="channels_first"`). '
268+ 'For best performance, set '
269+ '`image_data_format="channels_last"` in '
270+ 'your Keras config '
271+ 'at ~/.keras/keras.json.' )
272+ return model
273+
274+
275+ if __name__ == '__main__' :
276+ model = ResNet50 (include_top = True , weights = 'imagenet' )
277+
278+ img_path = 'elephant.jpg'
279+ img = image .load_img (img_path , target_size = (224 , 224 ))
280+ x = image .img_to_array (img )
281+ x = np .expand_dims (x , axis = 0 )
282+ x = preprocess_input (x )
283+ print ('Input image shape:' , x .shape )
284+
285+ preds = model .predict (x )
286+ print ('Predicted:' , decode_predictions (preds ))
0 commit comments