shufflenet_v2.py 9.3 KB

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