mobilenet_v2.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. from collections import OrderedDict
  17. import paddle.fluid as fluid
  18. from paddle.fluid.param_attr import ParamAttr
  19. class MobileNetV2:
  20. def __init__(self,
  21. num_classes=None,
  22. scale=1.0,
  23. output_stride=None,
  24. end_points=None,
  25. decode_points=None):
  26. self.scale = scale
  27. self.num_classes = num_classes
  28. self.output_stride = output_stride
  29. self.end_points = end_points
  30. self.decode_points = decode_points
  31. self.bottleneck_params_list = [(1, 16, 1, 1), (6, 24, 2, 2),
  32. (6, 32, 3, 2), (6, 64, 4, 2),
  33. (6, 96, 3, 1), (6, 160, 3, 2),
  34. (6, 320, 1, 1)]
  35. self.modify_bottle_params(output_stride)
  36. def __call__(self, input):
  37. scale = self.scale
  38. decode_ends = dict()
  39. def check_points(count, points):
  40. if points is None:
  41. return False
  42. else:
  43. if isinstance(points, list):
  44. return (True if count in points else False)
  45. else:
  46. return (True if count == points else False)
  47. # conv1
  48. input = self.conv_bn_layer(
  49. input,
  50. num_filters=int(32 * scale),
  51. filter_size=3,
  52. stride=2,
  53. padding=1,
  54. if_act=True,
  55. name='conv1_1')
  56. layer_count = 1
  57. if check_points(layer_count, self.decode_points):
  58. decode_ends[layer_count] = input
  59. if check_points(layer_count, self.end_points):
  60. return input, decode_ends
  61. # bottleneck sequences
  62. i = 1
  63. in_c = int(32 * scale)
  64. for layer_setting in self.bottleneck_params_list:
  65. t, c, n, s = layer_setting
  66. i += 1
  67. input, depthwise_output = self.invresi_blocks(
  68. input=input,
  69. in_c=in_c,
  70. t=t,
  71. c=int(c * scale),
  72. n=n,
  73. s=s,
  74. name='conv' + str(i))
  75. in_c = int(c * scale)
  76. layer_count += n
  77. if check_points(layer_count, self.decode_points):
  78. decode_ends[layer_count] = depthwise_output
  79. if check_points(layer_count, self.end_points):
  80. return input, decode_ends
  81. # last_conv
  82. output = self.conv_bn_layer(
  83. input=input,
  84. num_filters=int(1280 * scale) if scale > 1.0 else 1280,
  85. filter_size=1,
  86. stride=1,
  87. padding=0,
  88. if_act=True,
  89. name='conv9')
  90. if self.num_classes is not None:
  91. output = fluid.layers.pool2d(
  92. input=output, pool_type='avg', global_pooling=True)
  93. output = fluid.layers.fc(
  94. input=output,
  95. size=self.num_classes,
  96. param_attr=ParamAttr(name='fc10_weights'),
  97. bias_attr=ParamAttr(name='fc10_offset'))
  98. return OrderedDict([('logits', output)])
  99. return output
  100. def modify_bottle_params(self, output_stride=None):
  101. if output_stride is not None and output_stride % 2 != 0:
  102. raise Exception("output stride must to be even number")
  103. if output_stride is None:
  104. return
  105. else:
  106. stride = 2
  107. for i, layer_setting in enumerate(self.bottleneck_params_list):
  108. t, c, n, s = layer_setting
  109. stride = stride * s
  110. if stride > output_stride:
  111. s = 1
  112. self.bottleneck_params_list[i] = (t, c, n, s)
  113. def conv_bn_layer(self,
  114. input,
  115. filter_size,
  116. num_filters,
  117. stride,
  118. padding,
  119. channels=None,
  120. num_groups=1,
  121. if_act=True,
  122. name=None,
  123. use_cudnn=True):
  124. conv = fluid.layers.conv2d(
  125. input=input,
  126. num_filters=num_filters,
  127. filter_size=filter_size,
  128. stride=stride,
  129. padding=padding,
  130. groups=num_groups,
  131. act=None,
  132. use_cudnn=use_cudnn,
  133. param_attr=ParamAttr(name=name + '_weights'),
  134. bias_attr=False)
  135. bn_name = name + '_bn'
  136. bn = fluid.layers.batch_norm(
  137. input=conv,
  138. param_attr=ParamAttr(name=bn_name + "_scale"),
  139. bias_attr=ParamAttr(name=bn_name + "_offset"),
  140. moving_mean_name=bn_name + '_mean',
  141. moving_variance_name=bn_name + '_variance')
  142. if if_act:
  143. return fluid.layers.relu6(bn)
  144. else:
  145. return bn
  146. def shortcut(self, input, data_residual):
  147. return fluid.layers.elementwise_add(input, data_residual)
  148. def inverted_residual_unit(self,
  149. input,
  150. num_in_filter,
  151. num_filters,
  152. ifshortcut,
  153. stride,
  154. filter_size,
  155. padding,
  156. expansion_factor,
  157. name=None):
  158. num_expfilter = int(round(num_in_filter * expansion_factor))
  159. channel_expand = self.conv_bn_layer(
  160. input=input,
  161. num_filters=num_expfilter,
  162. filter_size=1,
  163. stride=1,
  164. padding=0,
  165. num_groups=1,
  166. if_act=True,
  167. name=name + '_expand')
  168. bottleneck_conv = self.conv_bn_layer(
  169. input=channel_expand,
  170. num_filters=num_expfilter,
  171. filter_size=filter_size,
  172. stride=stride,
  173. padding=padding,
  174. num_groups=num_expfilter,
  175. if_act=True,
  176. name=name + '_dwise',
  177. use_cudnn=False)
  178. depthwise_output = bottleneck_conv
  179. linear_out = self.conv_bn_layer(
  180. input=bottleneck_conv,
  181. num_filters=num_filters,
  182. filter_size=1,
  183. stride=1,
  184. padding=0,
  185. num_groups=1,
  186. if_act=False,
  187. name=name + '_linear')
  188. if ifshortcut:
  189. out = self.shortcut(input=input, data_residual=linear_out)
  190. return out, depthwise_output
  191. else:
  192. return linear_out, depthwise_output
  193. def invresi_blocks(self, input, in_c, t, c, n, s, name=None):
  194. first_block, depthwise_output = self.inverted_residual_unit(
  195. input=input,
  196. num_in_filter=in_c,
  197. num_filters=c,
  198. ifshortcut=False,
  199. stride=s,
  200. filter_size=3,
  201. padding=1,
  202. expansion_factor=t,
  203. name=name + '_1')
  204. last_residual_block = first_block
  205. last_c = c
  206. for i in range(1, n):
  207. last_residual_block, depthwise_output = self.inverted_residual_unit(
  208. input=last_residual_block,
  209. num_in_filter=last_c,
  210. num_filters=c,
  211. ifshortcut=True,
  212. stride=1,
  213. filter_size=3,
  214. padding=1,
  215. expansion_factor=t,
  216. name=name + '_' + str(i + 1))
  217. return last_residual_block, depthwise_output