mobilenet_v2.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. # copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. from __future__ import absolute_import
  14. from __future__ import division
  15. from __future__ import print_function
  16. import paddle.fluid as fluid
  17. from paddle.fluid.param_attr import ParamAttr
  18. class MobileNetV2:
  19. def __init__(self,
  20. num_classes=None,
  21. scale=1.0,
  22. output_stride=None,
  23. end_points=None,
  24. decode_points=None):
  25. self.scale = scale
  26. self.num_classes = num_classes
  27. self.output_stride = output_stride
  28. self.end_points = end_points
  29. self.decode_points = decode_points
  30. self.bottleneck_params_list = [
  31. (1, 16, 1, 1), (6, 24, 2, 2), (6, 32, 3, 2), (6, 64, 4, 2),
  32. (6, 96, 3, 1), (6, 160, 3, 2), (6, 320, 1, 1)
  33. ]
  34. self.modify_bottle_params(output_stride)
  35. def __call__(self, input):
  36. scale = self.scale
  37. decode_ends = dict()
  38. def check_points(count, points):
  39. if points is None:
  40. return False
  41. else:
  42. if isinstance(points, list):
  43. return (True if count in points else False)
  44. else:
  45. return (True if count == points else False)
  46. # conv1
  47. input = self.conv_bn_layer(
  48. input,
  49. num_filters=int(32 * scale),
  50. filter_size=3,
  51. stride=2,
  52. padding=1,
  53. if_act=True,
  54. name='conv1_1')
  55. layer_count = 1
  56. if check_points(layer_count, self.decode_points):
  57. decode_ends[layer_count] = input
  58. if check_points(layer_count, self.end_points):
  59. return input, decode_ends
  60. # bottleneck sequences
  61. i = 1
  62. in_c = int(32 * scale)
  63. for layer_setting in self.bottleneck_params_list:
  64. t, c, n, s = layer_setting
  65. i += 1
  66. input, depthwise_output = self.invresi_blocks(
  67. input=input,
  68. in_c=in_c,
  69. t=t,
  70. c=int(c * scale),
  71. n=n,
  72. s=s,
  73. name='conv' + str(i))
  74. in_c = int(c * scale)
  75. layer_count += n
  76. if check_points(layer_count, self.decode_points):
  77. decode_ends[layer_count] = depthwise_output
  78. if check_points(layer_count, self.end_points):
  79. return input, decode_ends
  80. # last_conv
  81. output = self.conv_bn_layer(
  82. input=input,
  83. num_filters=int(1280 * scale) if scale > 1.0 else 1280,
  84. filter_size=1,
  85. stride=1,
  86. padding=0,
  87. if_act=True,
  88. name='conv9')
  89. if self.num_classes is not None:
  90. output = fluid.layers.pool2d(
  91. input=output, pool_type='avg', global_pooling=True)
  92. output = fluid.layers.fc(input=output,
  93. size=self.num_classes,
  94. param_attr=ParamAttr(name='fc10_weights'),
  95. bias_attr=ParamAttr(name='fc10_offset'))
  96. return output
  97. def modify_bottle_params(self, output_stride=None):
  98. if output_stride is not None and output_stride % 2 != 0:
  99. raise Exception("output stride must to be even number")
  100. if output_stride is None:
  101. return
  102. else:
  103. stride = 2
  104. for i, layer_setting in enumerate(self.bottleneck_params_list):
  105. t, c, n, s = layer_setting
  106. stride = stride * s
  107. if stride > output_stride:
  108. s = 1
  109. self.bottleneck_params_list[i] = (t, c, n, s)
  110. def conv_bn_layer(self,
  111. input,
  112. filter_size,
  113. num_filters,
  114. stride,
  115. padding,
  116. channels=None,
  117. num_groups=1,
  118. if_act=True,
  119. name=None,
  120. use_cudnn=True):
  121. conv = fluid.layers.conv2d(
  122. input=input,
  123. num_filters=num_filters,
  124. filter_size=filter_size,
  125. stride=stride,
  126. padding=padding,
  127. groups=num_groups,
  128. act=None,
  129. use_cudnn=use_cudnn,
  130. param_attr=ParamAttr(name=name + '_weights'),
  131. bias_attr=False)
  132. bn_name = name + '_bn'
  133. bn = fluid.layers.batch_norm(
  134. input=conv,
  135. param_attr=ParamAttr(name=bn_name + "_scale"),
  136. bias_attr=ParamAttr(name=bn_name + "_offset"),
  137. moving_mean_name=bn_name + '_mean',
  138. moving_variance_name=bn_name + '_variance')
  139. if if_act:
  140. return fluid.layers.relu6(bn)
  141. else:
  142. return bn
  143. def shortcut(self, input, data_residual):
  144. return fluid.layers.elementwise_add(input, data_residual)
  145. def inverted_residual_unit(self,
  146. input,
  147. num_in_filter,
  148. num_filters,
  149. ifshortcut,
  150. stride,
  151. filter_size,
  152. padding,
  153. expansion_factor,
  154. name=None):
  155. num_expfilter = int(round(num_in_filter * expansion_factor))
  156. channel_expand = self.conv_bn_layer(
  157. input=input,
  158. num_filters=num_expfilter,
  159. filter_size=1,
  160. stride=1,
  161. padding=0,
  162. num_groups=1,
  163. if_act=True,
  164. name=name + '_expand')
  165. bottleneck_conv = self.conv_bn_layer(
  166. input=channel_expand,
  167. num_filters=num_expfilter,
  168. filter_size=filter_size,
  169. stride=stride,
  170. padding=padding,
  171. num_groups=num_expfilter,
  172. if_act=True,
  173. name=name + '_dwise',
  174. use_cudnn=False)
  175. depthwise_output = bottleneck_conv
  176. linear_out = self.conv_bn_layer(
  177. input=bottleneck_conv,
  178. num_filters=num_filters,
  179. filter_size=1,
  180. stride=1,
  181. padding=0,
  182. num_groups=1,
  183. if_act=False,
  184. name=name + '_linear')
  185. if ifshortcut:
  186. out = self.shortcut(input=input, data_residual=linear_out)
  187. return out, depthwise_output
  188. else:
  189. return linear_out, depthwise_output
  190. def invresi_blocks(self, input, in_c, t, c, n, s, name=None):
  191. first_block, depthwise_output = self.inverted_residual_unit(
  192. input=input,
  193. num_in_filter=in_c,
  194. num_filters=c,
  195. ifshortcut=False,
  196. stride=s,
  197. filter_size=3,
  198. padding=1,
  199. expansion_factor=t,
  200. name=name + '_1')
  201. last_residual_block = first_block
  202. last_c = c
  203. for i in range(1, n):
  204. last_residual_block, depthwise_output = self.inverted_residual_unit(
  205. input=last_residual_block,
  206. num_in_filter=last_c,
  207. num_filters=c,
  208. ifshortcut=True,
  209. stride=1,
  210. filter_size=3,
  211. padding=1,
  212. expansion_factor=t,
  213. name=name + '_' + str(i + 1))
  214. return last_residual_block, depthwise_output