mobilenet_v2.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
  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 = [(1, 16, 1, 1), (6, 24, 2, 2),
  31. (6, 32, 3, 2), (6, 64, 4, 2),
  32. (6, 96, 3, 1), (6, 160, 3, 2),
  33. (6, 320, 1, 1)]
  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(
  93. input=output,
  94. size=self.num_classes,
  95. param_attr=ParamAttr(name='fc10_weights'),
  96. bias_attr=ParamAttr(name='fc10_offset'))
  97. return output
  98. def modify_bottle_params(self, output_stride=None):
  99. if output_stride is not None and output_stride % 2 != 0:
  100. raise Exception("output stride must to be even number")
  101. if output_stride is None:
  102. return
  103. else:
  104. stride = 2
  105. for i, layer_setting in enumerate(self.bottleneck_params_list):
  106. t, c, n, s = layer_setting
  107. stride = stride * s
  108. if stride > output_stride:
  109. s = 1
  110. self.bottleneck_params_list[i] = (t, c, n, s)
  111. def conv_bn_layer(self,
  112. input,
  113. filter_size,
  114. num_filters,
  115. stride,
  116. padding,
  117. channels=None,
  118. num_groups=1,
  119. if_act=True,
  120. name=None,
  121. use_cudnn=True):
  122. conv = fluid.layers.conv2d(
  123. input=input,
  124. num_filters=num_filters,
  125. filter_size=filter_size,
  126. stride=stride,
  127. padding=padding,
  128. groups=num_groups,
  129. act=None,
  130. use_cudnn=use_cudnn,
  131. param_attr=ParamAttr(name=name + '_weights'),
  132. bias_attr=False)
  133. bn_name = name + '_bn'
  134. bn = fluid.layers.batch_norm(
  135. input=conv,
  136. param_attr=ParamAttr(name=bn_name + "_scale"),
  137. bias_attr=ParamAttr(name=bn_name + "_offset"),
  138. moving_mean_name=bn_name + '_mean',
  139. moving_variance_name=bn_name + '_variance')
  140. if if_act:
  141. return fluid.layers.relu6(bn)
  142. else:
  143. return bn
  144. def shortcut(self, input, data_residual):
  145. return fluid.layers.elementwise_add(input, data_residual)
  146. def inverted_residual_unit(self,
  147. input,
  148. num_in_filter,
  149. num_filters,
  150. ifshortcut,
  151. stride,
  152. filter_size,
  153. padding,
  154. expansion_factor,
  155. name=None):
  156. num_expfilter = int(round(num_in_filter * expansion_factor))
  157. channel_expand = self.conv_bn_layer(
  158. input=input,
  159. num_filters=num_expfilter,
  160. filter_size=1,
  161. stride=1,
  162. padding=0,
  163. num_groups=1,
  164. if_act=True,
  165. name=name + '_expand')
  166. bottleneck_conv = self.conv_bn_layer(
  167. input=channel_expand,
  168. num_filters=num_expfilter,
  169. filter_size=filter_size,
  170. stride=stride,
  171. padding=padding,
  172. num_groups=num_expfilter,
  173. if_act=True,
  174. name=name + '_dwise',
  175. use_cudnn=False)
  176. depthwise_output = bottleneck_conv
  177. linear_out = self.conv_bn_layer(
  178. input=bottleneck_conv,
  179. num_filters=num_filters,
  180. filter_size=1,
  181. stride=1,
  182. padding=0,
  183. num_groups=1,
  184. if_act=False,
  185. name=name + '_linear')
  186. if ifshortcut:
  187. out = self.shortcut(input=input, data_residual=linear_out)
  188. return out, depthwise_output
  189. else:
  190. return linear_out, depthwise_output
  191. def invresi_blocks(self, input, in_c, t, c, n, s, name=None):
  192. first_block, depthwise_output = self.inverted_residual_unit(
  193. input=input,
  194. num_in_filter=in_c,
  195. num_filters=c,
  196. ifshortcut=False,
  197. stride=s,
  198. filter_size=3,
  199. padding=1,
  200. expansion_factor=t,
  201. name=name + '_1')
  202. last_residual_block = first_block
  203. last_c = c
  204. for i in range(1, n):
  205. last_residual_block, depthwise_output = self.inverted_residual_unit(
  206. input=last_residual_block,
  207. num_in_filter=last_c,
  208. num_filters=c,
  209. ifshortcut=True,
  210. stride=1,
  211. filter_size=3,
  212. padding=1,
  213. expansion_factor=t,
  214. name=name + '_' + str(i + 1))
  215. return last_residual_block, depthwise_output