mobilenet_v3.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. # limitations under the License.
  14. import paddle.fluid as fluid
  15. from paddle.fluid.param_attr import ParamAttr
  16. from paddle.fluid.regularizer import L2Decay
  17. import math
  18. class MobileNetV3():
  19. """
  20. MobileNet v3, see https://arxiv.org/abs/1905.02244
  21. Args:
  22. scale (float): scaling factor for convolution groups proportion of mobilenet_v3.
  23. model_name (str): There are two modes, small and large.
  24. norm_type (str): normalization type, 'bn' and 'sync_bn' are supported.
  25. norm_decay (float): weight decay for normalization layer weights.
  26. conv_decay (float): weight decay for convolution layer weights.
  27. with_extra_blocks (bool): if extra blocks should be added.
  28. extra_block_filters (list): number of filter for each extra block.
  29. """
  30. def __init__(self,
  31. scale=1.0,
  32. model_name='small',
  33. with_extra_blocks=False,
  34. conv_decay=0.0,
  35. norm_type='bn',
  36. norm_decay=0.0,
  37. extra_block_filters=[[256, 512], [128, 256], [128, 256],
  38. [64, 128]],
  39. num_classes=None):
  40. self.scale = scale
  41. self.with_extra_blocks = with_extra_blocks
  42. self.extra_block_filters = extra_block_filters
  43. self.conv_decay = conv_decay
  44. self.norm_decay = norm_decay
  45. self.inplanes = 16
  46. self.end_points = []
  47. self.block_stride = 1
  48. self.num_classes = num_classes
  49. if model_name == "large":
  50. self.cfg = [
  51. # kernel_size, expand, channel, se_block, act_mode, stride
  52. [3, 16, 16, False, 'relu', 1],
  53. [3, 64, 24, False, 'relu', 2],
  54. [3, 72, 24, False, 'relu', 1],
  55. [5, 72, 40, True, 'relu', 2],
  56. [5, 120, 40, True, 'relu', 1],
  57. [5, 120, 40, True, 'relu', 1],
  58. [3, 240, 80, False, 'hard_swish', 2],
  59. [3, 200, 80, False, 'hard_swish', 1],
  60. [3, 184, 80, False, 'hard_swish', 1],
  61. [3, 184, 80, False, 'hard_swish', 1],
  62. [3, 480, 112, True, 'hard_swish', 1],
  63. [3, 672, 112, True, 'hard_swish', 1],
  64. [5, 672, 160, True, 'hard_swish', 2],
  65. [5, 960, 160, True, 'hard_swish', 1],
  66. [5, 960, 160, True, 'hard_swish', 1],
  67. ]
  68. self.cls_ch_squeeze = 960
  69. self.cls_ch_expand = 1280
  70. elif model_name == "small":
  71. self.cfg = [
  72. # kernel_size, expand, channel, se_block, act_mode, stride
  73. [3, 16, 16, True, 'relu', 2],
  74. [3, 72, 24, False, 'relu', 2],
  75. [3, 88, 24, False, 'relu', 1],
  76. [5, 96, 40, True, 'hard_swish', 2],
  77. [5, 240, 40, True, 'hard_swish', 1],
  78. [5, 240, 40, True, 'hard_swish', 1],
  79. [5, 120, 48, True, 'hard_swish', 1],
  80. [5, 144, 48, True, 'hard_swish', 1],
  81. [5, 288, 96, True, 'hard_swish', 2],
  82. [5, 576, 96, True, 'hard_swish', 1],
  83. [5, 576, 96, True, 'hard_swish', 1],
  84. ]
  85. self.cls_ch_squeeze = 576
  86. self.cls_ch_expand = 1280
  87. else:
  88. raise NotImplementedError
  89. def _conv_bn_layer(self,
  90. input,
  91. filter_size,
  92. num_filters,
  93. stride,
  94. padding,
  95. num_groups=1,
  96. if_act=True,
  97. act=None,
  98. name=None,
  99. use_cudnn=True):
  100. conv_param_attr = ParamAttr(
  101. name=name + '_weights', regularizer=L2Decay(self.conv_decay))
  102. conv = fluid.layers.conv2d(
  103. input=input,
  104. num_filters=num_filters,
  105. filter_size=filter_size,
  106. stride=stride,
  107. padding=padding,
  108. groups=num_groups,
  109. act=None,
  110. use_cudnn=use_cudnn,
  111. param_attr=conv_param_attr,
  112. bias_attr=False)
  113. bn_name = name + '_bn'
  114. bn_param_attr = ParamAttr(
  115. name=bn_name + "_scale", regularizer=L2Decay(self.norm_decay))
  116. bn_bias_attr = ParamAttr(
  117. name=bn_name + "_offset", regularizer=L2Decay(self.norm_decay))
  118. bn = fluid.layers.batch_norm(
  119. input=conv,
  120. param_attr=bn_param_attr,
  121. bias_attr=bn_bias_attr,
  122. moving_mean_name=bn_name + '_mean',
  123. moving_variance_name=bn_name + '_variance')
  124. if if_act:
  125. if act == 'relu':
  126. bn = fluid.layers.relu(bn)
  127. elif act == 'hard_swish':
  128. bn = self._hard_swish(bn)
  129. elif act == 'relu6':
  130. bn = fluid.layers.relu6(bn)
  131. return bn
  132. def _hard_swish(self, x):
  133. return x * fluid.layers.relu6(x + 3) / 6.
  134. def _se_block(self, input, num_out_filter, ratio=4, name=None):
  135. num_mid_filter = int(num_out_filter // ratio)
  136. pool = fluid.layers.pool2d(
  137. input=input, pool_type='avg', global_pooling=True, use_cudnn=False)
  138. conv1 = fluid.layers.conv2d(
  139. input=pool,
  140. filter_size=1,
  141. num_filters=num_mid_filter,
  142. act='relu',
  143. param_attr=ParamAttr(name=name + '_1_weights'),
  144. bias_attr=ParamAttr(name=name + '_1_offset'))
  145. conv2 = fluid.layers.conv2d(
  146. input=conv1,
  147. filter_size=1,
  148. num_filters=num_out_filter,
  149. act='hard_sigmoid',
  150. param_attr=ParamAttr(name=name + '_2_weights'),
  151. bias_attr=ParamAttr(name=name + '_2_offset'))
  152. scale = fluid.layers.elementwise_mul(x=input, y=conv2, axis=0)
  153. return scale
  154. def _residual_unit(self,
  155. input,
  156. num_in_filter,
  157. num_mid_filter,
  158. num_out_filter,
  159. stride,
  160. filter_size,
  161. act=None,
  162. use_se=False,
  163. name=None):
  164. input_data = input
  165. conv0 = self._conv_bn_layer(
  166. input=input,
  167. filter_size=1,
  168. num_filters=num_mid_filter,
  169. stride=1,
  170. padding=0,
  171. if_act=True,
  172. act=act,
  173. name=name + '_expand')
  174. if self.block_stride == 16 and stride == 2:
  175. self.end_points.append(conv0)
  176. conv1 = self._conv_bn_layer(
  177. input=conv0,
  178. filter_size=filter_size,
  179. num_filters=num_mid_filter,
  180. stride=stride,
  181. padding=int((filter_size - 1) // 2),
  182. if_act=True,
  183. act=act,
  184. num_groups=num_mid_filter,
  185. use_cudnn=False,
  186. name=name + '_depthwise')
  187. if use_se:
  188. conv1 = self._se_block(
  189. input=conv1, num_out_filter=num_mid_filter, name=name + '_se')
  190. conv2 = self._conv_bn_layer(
  191. input=conv1,
  192. filter_size=1,
  193. num_filters=num_out_filter,
  194. stride=1,
  195. padding=0,
  196. if_act=False,
  197. name=name + '_linear')
  198. if num_in_filter != num_out_filter or stride != 1:
  199. return conv2
  200. else:
  201. return fluid.layers.elementwise_add(
  202. x=input_data, y=conv2, act=None)
  203. def _extra_block_dw(self,
  204. input,
  205. num_filters1,
  206. num_filters2,
  207. stride,
  208. name=None):
  209. pointwise_conv = self._conv_bn_layer(
  210. input=input,
  211. filter_size=1,
  212. num_filters=int(num_filters1),
  213. stride=1,
  214. padding="SAME",
  215. act='relu6',
  216. name=name + "_extra1")
  217. depthwise_conv = self._conv_bn_layer(
  218. input=pointwise_conv,
  219. filter_size=3,
  220. num_filters=int(num_filters2),
  221. stride=stride,
  222. padding="SAME",
  223. num_groups=int(num_filters1),
  224. act='relu6',
  225. use_cudnn=False,
  226. name=name + "_extra2_dw")
  227. normal_conv = self._conv_bn_layer(
  228. input=depthwise_conv,
  229. filter_size=1,
  230. num_filters=int(num_filters2),
  231. stride=1,
  232. padding="SAME",
  233. act='relu6',
  234. name=name + "_extra2_sep")
  235. return normal_conv
  236. def __call__(self, input):
  237. scale = self.scale
  238. inplanes = self.inplanes
  239. cfg = self.cfg
  240. blocks = []
  241. #conv1
  242. conv = self._conv_bn_layer(
  243. input,
  244. filter_size=3,
  245. num_filters=inplanes if scale <= 1.0 else int(inplanes * scale),
  246. stride=2,
  247. padding=1,
  248. num_groups=1,
  249. if_act=True,
  250. act='hard_swish',
  251. name='conv1')
  252. i = 0
  253. for layer_cfg in cfg:
  254. self.block_stride *= layer_cfg[5]
  255. if layer_cfg[5] == 2:
  256. blocks.append(conv)
  257. conv = self._residual_unit(
  258. input=conv,
  259. num_in_filter=inplanes,
  260. num_mid_filter=int(scale * layer_cfg[1]),
  261. num_out_filter=int(scale * layer_cfg[2]),
  262. act=layer_cfg[4],
  263. stride=layer_cfg[5],
  264. filter_size=layer_cfg[0],
  265. use_se=layer_cfg[3],
  266. name='conv' + str(i + 2))
  267. inplanes = int(scale * layer_cfg[2])
  268. i += 1
  269. blocks.append(conv)
  270. if self.num_classes:
  271. conv = self._conv_bn_layer(
  272. input=conv,
  273. filter_size=1,
  274. num_filters=int(scale * self.cls_ch_squeeze),
  275. stride=1,
  276. padding=0,
  277. num_groups=1,
  278. if_act=True,
  279. act='hard_swish',
  280. name='conv_last')
  281. conv = fluid.layers.pool2d(
  282. input=conv,
  283. pool_type='avg',
  284. global_pooling=True,
  285. use_cudnn=False)
  286. conv = fluid.layers.conv2d(
  287. input=conv,
  288. num_filters=self.cls_ch_expand,
  289. filter_size=1,
  290. stride=1,
  291. padding=0,
  292. act=None,
  293. param_attr=ParamAttr(name='last_1x1_conv_weights'),
  294. bias_attr=False)
  295. conv = self._hard_swish(conv)
  296. drop = fluid.layers.dropout(x=conv, dropout_prob=0.2)
  297. out = fluid.layers.fc(
  298. input=drop,
  299. size=self.num_classes,
  300. param_attr=ParamAttr(name='fc_weights'),
  301. bias_attr=ParamAttr(name='fc_offset'))
  302. return out
  303. if not self.with_extra_blocks:
  304. return blocks
  305. # extra block
  306. conv_extra = self._conv_bn_layer(
  307. conv,
  308. filter_size=1,
  309. num_filters=int(scale * cfg[-1][1]),
  310. stride=1,
  311. padding="SAME",
  312. num_groups=1,
  313. if_act=True,
  314. act='hard_swish',
  315. name='conv' + str(i + 2))
  316. self.end_points.append(conv_extra)
  317. i += 1
  318. for block_filter in self.extra_block_filters:
  319. conv_extra = self._extra_block_dw(conv_extra, block_filter[0],
  320. block_filter[1], 2,
  321. 'conv' + str(i + 2))
  322. self.end_points.append(conv_extra)
  323. i += 1
  324. return self.end_points