shufflenet_v2.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. # limitations under the License.
  14. from __future__ import absolute_import
  15. from __future__ import division
  16. from __future__ import print_function
  17. import paddle
  18. from paddle import ParamAttr, reshape, transpose, concat, split
  19. from paddle.nn import Layer, Conv2D, MaxPool2D, AdaptiveAvgPool2D, BatchNorm, Linear
  20. from paddle.nn.initializer import KaimingNormal
  21. from paddle.nn.functional import swish
  22. __all__ = [
  23. "ShuffleNetV2_x0_25", "ShuffleNetV2_x0_33", "ShuffleNetV2_x0_5",
  24. "ShuffleNetV2_x1_0", "ShuffleNetV2_x1_5", "ShuffleNetV2_x2_0",
  25. "ShuffleNetV2_swish"
  26. ]
  27. def channel_shuffle(x, groups):
  28. batch_size, num_channels, height, width = x.shape[0:4]
  29. channels_per_group = num_channels // groups
  30. # reshape
  31. x = reshape(
  32. x=x, shape=[batch_size, groups, channels_per_group, height, width])
  33. # transpose
  34. x = transpose(x=x, perm=[0, 2, 1, 3, 4])
  35. # flatten
  36. x = reshape(x=x, shape=[batch_size, num_channels, height, width])
  37. return x
  38. class ConvBNLayer(Layer):
  39. def __init__(
  40. self,
  41. in_channels,
  42. out_channels,
  43. kernel_size,
  44. stride,
  45. padding,
  46. groups=1,
  47. act=None,
  48. name=None, ):
  49. super(ConvBNLayer, self).__init__()
  50. self._conv = Conv2D(
  51. in_channels=in_channels,
  52. out_channels=out_channels,
  53. kernel_size=kernel_size,
  54. stride=stride,
  55. padding=padding,
  56. groups=groups,
  57. weight_attr=ParamAttr(
  58. initializer=KaimingNormal(), name=name + "_weights"),
  59. bias_attr=False)
  60. self._batch_norm = BatchNorm(
  61. out_channels,
  62. param_attr=ParamAttr(name=name + "_bn_scale"),
  63. bias_attr=ParamAttr(name=name + "_bn_offset"),
  64. act=act,
  65. moving_mean_name=name + "_bn_mean",
  66. moving_variance_name=name + "_bn_variance")
  67. def forward(self, inputs):
  68. y = self._conv(inputs)
  69. y = self._batch_norm(y)
  70. return y
  71. class InvertedResidual(Layer):
  72. def __init__(self,
  73. in_channels,
  74. out_channels,
  75. stride,
  76. act="relu",
  77. name=None):
  78. super(InvertedResidual, self).__init__()
  79. self._conv_pw = ConvBNLayer(
  80. in_channels=in_channels // 2,
  81. out_channels=out_channels // 2,
  82. kernel_size=1,
  83. stride=1,
  84. padding=0,
  85. groups=1,
  86. act=act,
  87. name='stage_' + name + '_conv1')
  88. self._conv_dw = ConvBNLayer(
  89. in_channels=out_channels // 2,
  90. out_channels=out_channels // 2,
  91. kernel_size=3,
  92. stride=stride,
  93. padding=1,
  94. groups=out_channels // 2,
  95. act=None,
  96. name='stage_' + name + '_conv2')
  97. self._conv_linear = ConvBNLayer(
  98. in_channels=out_channels // 2,
  99. out_channels=out_channels // 2,
  100. kernel_size=1,
  101. stride=1,
  102. padding=0,
  103. groups=1,
  104. act=act,
  105. name='stage_' + name + '_conv3')
  106. def forward(self, inputs):
  107. x1, x2 = split(
  108. inputs,
  109. num_or_sections=[inputs.shape[1] // 2, inputs.shape[1] // 2],
  110. axis=1)
  111. x2 = self._conv_pw(x2)
  112. x2 = self._conv_dw(x2)
  113. x2 = self._conv_linear(x2)
  114. out = concat([x1, x2], axis=1)
  115. return channel_shuffle(out, 2)
  116. class InvertedResidualDS(Layer):
  117. def __init__(self,
  118. in_channels,
  119. out_channels,
  120. stride,
  121. act="relu",
  122. name=None):
  123. super(InvertedResidualDS, self).__init__()
  124. # branch1
  125. self._conv_dw_1 = ConvBNLayer(
  126. in_channels=in_channels,
  127. out_channels=in_channels,
  128. kernel_size=3,
  129. stride=stride,
  130. padding=1,
  131. groups=in_channels,
  132. act=None,
  133. name='stage_' + name + '_conv4')
  134. self._conv_linear_1 = ConvBNLayer(
  135. in_channels=in_channels,
  136. out_channels=out_channels // 2,
  137. kernel_size=1,
  138. stride=1,
  139. padding=0,
  140. groups=1,
  141. act=act,
  142. name='stage_' + name + '_conv5')
  143. # branch2
  144. self._conv_pw_2 = ConvBNLayer(
  145. in_channels=in_channels,
  146. out_channels=out_channels // 2,
  147. kernel_size=1,
  148. stride=1,
  149. padding=0,
  150. groups=1,
  151. act=act,
  152. name='stage_' + name + '_conv1')
  153. self._conv_dw_2 = ConvBNLayer(
  154. in_channels=out_channels // 2,
  155. out_channels=out_channels // 2,
  156. kernel_size=3,
  157. stride=stride,
  158. padding=1,
  159. groups=out_channels // 2,
  160. act=None,
  161. name='stage_' + name + '_conv2')
  162. self._conv_linear_2 = ConvBNLayer(
  163. in_channels=out_channels // 2,
  164. out_channels=out_channels // 2,
  165. kernel_size=1,
  166. stride=1,
  167. padding=0,
  168. groups=1,
  169. act=act,
  170. name='stage_' + name + '_conv3')
  171. def forward(self, inputs):
  172. x1 = self._conv_dw_1(inputs)
  173. x1 = self._conv_linear_1(x1)
  174. x2 = self._conv_pw_2(inputs)
  175. x2 = self._conv_dw_2(x2)
  176. x2 = self._conv_linear_2(x2)
  177. out = concat([x1, x2], axis=1)
  178. return channel_shuffle(out, 2)
  179. class ShuffleNet(Layer):
  180. def __init__(self, class_dim=1000, scale=1.0, act="relu"):
  181. super(ShuffleNet, self).__init__()
  182. self.scale = scale
  183. self.class_dim = class_dim
  184. stage_repeats = [4, 8, 4]
  185. if scale == 0.25:
  186. stage_out_channels = [-1, 24, 24, 48, 96, 512]
  187. elif scale == 0.33:
  188. stage_out_channels = [-1, 24, 32, 64, 128, 512]
  189. elif scale == 0.5:
  190. stage_out_channels = [-1, 24, 48, 96, 192, 1024]
  191. elif scale == 1.0:
  192. stage_out_channels = [-1, 24, 116, 232, 464, 1024]
  193. elif scale == 1.5:
  194. stage_out_channels = [-1, 24, 176, 352, 704, 1024]
  195. elif scale == 2.0:
  196. stage_out_channels = [-1, 24, 224, 488, 976, 2048]
  197. else:
  198. raise NotImplementedError("This scale size:[" + str(scale) +
  199. "] is not implemented!")
  200. # 1. conv1
  201. self._conv1 = ConvBNLayer(
  202. in_channels=3,
  203. out_channels=stage_out_channels[1],
  204. kernel_size=3,
  205. stride=2,
  206. padding=1,
  207. act=act,
  208. name='stage1_conv')
  209. self._max_pool = MaxPool2D(kernel_size=3, stride=2, padding=1)
  210. # 2. bottleneck sequences
  211. self._block_list = []
  212. for stage_id, num_repeat in enumerate(stage_repeats):
  213. for i in range(num_repeat):
  214. if i == 0:
  215. block = self.add_sublayer(
  216. name=str(stage_id + 2) + '_' + str(i + 1),
  217. sublayer=InvertedResidualDS(
  218. in_channels=stage_out_channels[stage_id + 1],
  219. out_channels=stage_out_channels[stage_id + 2],
  220. stride=2,
  221. act=act,
  222. name=str(stage_id + 2) + '_' + str(i + 1)))
  223. else:
  224. block = self.add_sublayer(
  225. name=str(stage_id + 2) + '_' + str(i + 1),
  226. sublayer=InvertedResidual(
  227. in_channels=stage_out_channels[stage_id + 2],
  228. out_channels=stage_out_channels[stage_id + 2],
  229. stride=1,
  230. act=act,
  231. name=str(stage_id + 2) + '_' + str(i + 1)))
  232. self._block_list.append(block)
  233. # 3. last_conv
  234. self._last_conv = ConvBNLayer(
  235. in_channels=stage_out_channels[-2],
  236. out_channels=stage_out_channels[-1],
  237. kernel_size=1,
  238. stride=1,
  239. padding=0,
  240. act=act,
  241. name='conv5')
  242. # 4. pool
  243. self._pool2d_avg = AdaptiveAvgPool2D(1)
  244. self._out_c = stage_out_channels[-1]
  245. # 5. fc
  246. self._fc = Linear(
  247. stage_out_channels[-1],
  248. class_dim,
  249. weight_attr=ParamAttr(name='fc6_weights'),
  250. bias_attr=ParamAttr(name='fc6_offset'))
  251. def forward(self, inputs):
  252. y = self._conv1(inputs)
  253. y = self._max_pool(y)
  254. for inv in self._block_list:
  255. y = inv(y)
  256. y = self._last_conv(y)
  257. y = self._pool2d_avg(y)
  258. y = paddle.flatten(y, start_axis=1, stop_axis=-1)
  259. y = self._fc(y)
  260. return y
  261. def ShuffleNetV2_x0_25(**args):
  262. model = ShuffleNet(scale=0.25, **args)
  263. return model
  264. def ShuffleNetV2_x0_33(**args):
  265. model = ShuffleNet(scale=0.33, **args)
  266. return model
  267. def ShuffleNetV2_x0_5(**args):
  268. model = ShuffleNet(scale=0.5, **args)
  269. return model
  270. def ShuffleNetV2_x1_0(**args):
  271. model = ShuffleNet(scale=1.0, **args)
  272. return model
  273. def ShuffleNetV2_x1_5(**args):
  274. model = ShuffleNet(scale=1.5, **args)
  275. return model
  276. def ShuffleNetV2_x2_0(**args):
  277. model = ShuffleNet(scale=2.0, **args)
  278. return model
  279. def ShuffleNetV2_swish(**args):
  280. model = ShuffleNet(scale=1.0, act="swish", **args)
  281. return model