You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ghostnet.py 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. # Copyright 2020 Huawei Technologies Co., Ltd
  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. # ============================================================================
  15. """GhostNet model define"""
  16. from functools import partial
  17. import math
  18. import numpy as np
  19. import mindspore.nn as nn
  20. from mindspore.ops import operations as P
  21. from mindspore import Tensor
  22. from .quant import QuanConv
  23. __all__ = ['ghostnet']
  24. def _make_divisible(x, divisor=4):
  25. return int(np.ceil(x * 1. / divisor) * divisor)
  26. class MyHSigmoid(nn.Cell):
  27. """
  28. Hard Sigmoid definition.
  29. Args:
  30. Returns:
  31. Tensor, output tensor.
  32. Examples:
  33. >>> MyHSigmoid()
  34. """
  35. def __init__(self):
  36. super(MyHSigmoid, self).__init__()
  37. self.relu6 = nn.ReLU6()
  38. def construct(self, x):
  39. return self.relu6(x + 3.) * 0.16666667
  40. class Activation(nn.Cell):
  41. """
  42. Activation definition.
  43. Args:
  44. act_func(string): activation name.
  45. Returns:
  46. Tensor, output tensor.
  47. """
  48. def __init__(self, act_func):
  49. super(Activation, self).__init__()
  50. if act_func == 'relu':
  51. self.act = nn.ReLU()
  52. elif act_func == 'relu6':
  53. self.act = nn.ReLU6()
  54. elif act_func in ('hsigmoid', 'hard_sigmoid'):
  55. self.act = MyHSigmoid()
  56. elif act_func in ('hswish', 'hard_swish'):
  57. self.act = nn.HSwish()
  58. else:
  59. raise NotImplementedError
  60. def construct(self, x):
  61. return self.act(x)
  62. class GlobalAvgPooling(nn.Cell):
  63. """
  64. Global avg pooling definition.
  65. Args:
  66. Returns:
  67. Tensor, output tensor.
  68. Examples:
  69. >>> GlobalAvgPooling()
  70. """
  71. def __init__(self, keep_dims=False):
  72. super(GlobalAvgPooling, self).__init__()
  73. self.mean = P.ReduceMean(keep_dims=keep_dims)
  74. def construct(self, x):
  75. x = self.mean(x, (2, 3))
  76. return x
  77. class SE(nn.Cell):
  78. """
  79. SE warpper definition.
  80. Args:
  81. num_out (int): output channel.
  82. ratio (int): middle output ratio.
  83. Returns:
  84. Tensor, output tensor.
  85. Examples:
  86. >>> SE(4)
  87. """
  88. def __init__(self, num_out, ratio=4):
  89. super(SE, self).__init__()
  90. num_mid = _make_divisible(num_out // ratio)
  91. self.pool = GlobalAvgPooling(keep_dims=True)
  92. self.conv_reduce = QuanConv(in_channels=num_out, out_channels=num_mid,
  93. kernel_size=1, has_bias=True, pad_mode='pad')
  94. self.act1 = Activation('relu')
  95. self.conv_expand = QuanConv(in_channels=num_mid, out_channels=num_out,
  96. kernel_size=1, has_bias=True, pad_mode='pad')
  97. self.act2 = Activation('hsigmoid')
  98. self.mul = P.Mul()
  99. def construct(self, x):
  100. out = self.pool(x)
  101. out = self.conv_reduce(out)
  102. out = self.act1(out)
  103. out = self.conv_expand(out)
  104. out = self.act2(out)
  105. out = self.mul(x, out)
  106. return out
  107. class ConvUnit(nn.Cell):
  108. """
  109. ConvUnit warpper definition.
  110. Args:
  111. num_in (int): Input channel.
  112. num_out (int): Output channel.
  113. kernel_size (int): Input kernel size.
  114. stride (int): Stride size.
  115. padding (int): Padding number.
  116. num_groups (int): Output num group.
  117. use_act (bool): Used activation or not.
  118. act_type (string): Activation type.
  119. Returns:
  120. Tensor, output tensor.
  121. Examples:
  122. >>> ConvUnit(3, 3)
  123. """
  124. def __init__(self, num_in, num_out, kernel_size=1, stride=1, padding=0, num_groups=1,
  125. use_act=True, act_type='relu'):
  126. super(ConvUnit, self).__init__()
  127. self.conv = QuanConv(in_channels=num_in,
  128. out_channels=num_out,
  129. kernel_size=kernel_size,
  130. stride=stride,
  131. padding=padding,
  132. group=num_groups,
  133. has_bias=False,
  134. pad_mode='pad')
  135. self.bn = nn.BatchNorm2d(num_out)
  136. self.use_act = use_act
  137. self.act = Activation(act_type) if use_act else None
  138. def construct(self, x):
  139. out = self.conv(x)
  140. out = self.bn(out)
  141. if self.use_act:
  142. out = self.act(out)
  143. return out
  144. class GhostModule(nn.Cell):
  145. """
  146. GhostModule warpper definition.
  147. Args:
  148. num_in (int): Input channel.
  149. num_out (int): Output channel.
  150. kernel_size (int): Input kernel size.
  151. stride (int): Stride size.
  152. padding (int): Padding number.
  153. ratio (int): Reduction ratio.
  154. dw_size (int): kernel size of cheap operation.
  155. use_act (bool): Used activation or not.
  156. act_type (string): Activation type.
  157. Returns:
  158. Tensor, output tensor.
  159. Examples:
  160. >>> GhostModule(3, 3)
  161. """
  162. def __init__(self, num_in, num_out, kernel_size=1, stride=1, padding=0, ratio=2, dw_size=3,
  163. use_act=True, act_type='relu'):
  164. super(GhostModule, self).__init__()
  165. init_channels = math.ceil(num_out / ratio)
  166. new_channels = init_channels * (ratio - 1)
  167. self.primary_conv = ConvUnit(num_in, init_channels, kernel_size=kernel_size, stride=stride, padding=padding,
  168. num_groups=1, use_act=use_act, act_type='relu')
  169. self.cheap_operation = ConvUnit(init_channels, new_channels, kernel_size=dw_size, stride=1, padding=dw_size//2,
  170. num_groups=init_channels, use_act=use_act, act_type='relu')
  171. self.concat = P.Concat(axis=1)
  172. def construct(self, x):
  173. x1 = self.primary_conv(x)
  174. x2 = self.cheap_operation(x1)
  175. return self.concat((x1, x2))
  176. class GhostBottleneck(nn.Cell):
  177. """
  178. GhostBottleneck warpper definition.
  179. Args:
  180. num_in (int): Input channel.
  181. num_mid (int): Middle channel.
  182. num_out (int): Output channel.
  183. kernel_size (int): Input kernel size.
  184. stride (int): Stride size.
  185. act_type (str): Activation type.
  186. use_se (bool): Use SE warpper or not.
  187. Returns:
  188. Tensor, output tensor.
  189. Examples:
  190. >>> GhostBottleneck(16, 3, 1, 1)
  191. """
  192. def __init__(self, num_in, num_mid, num_out, kernel_size, stride=1, act_type='relu', use_se=False):
  193. super(GhostBottleneck, self).__init__()
  194. self.ghost1 = GhostModule(num_in, num_mid, kernel_size=1,
  195. stride=1, padding=0, act_type=act_type)
  196. self.use_dw = stride > 1
  197. self.dw = None
  198. if self.use_dw:
  199. self.dw = ConvUnit(num_mid, num_mid, kernel_size=kernel_size, stride=stride,
  200. padding=self._get_pad(kernel_size), act_type=act_type, num_groups=num_mid, use_act=False)
  201. self.use_se = use_se
  202. if use_se:
  203. self.se = SE(num_mid)
  204. self.ghost2 = GhostModule(num_mid, num_out, kernel_size=1, stride=1,
  205. padding=0, act_type=act_type, use_act=False)
  206. self.down_sample = False
  207. if num_in != num_out or stride != 1:
  208. self.down_sample = True
  209. self.shortcut = None
  210. if self.down_sample:
  211. self.shortcut = nn.SequentialCell([
  212. ConvUnit(num_in, num_in, kernel_size=kernel_size, stride=stride,
  213. padding=self._get_pad(kernel_size), num_groups=num_in, use_act=False),
  214. ConvUnit(num_in, num_out, kernel_size=1, stride=1,
  215. padding=0, num_groups=1, use_act=False),
  216. ])
  217. self.add = P.TensorAdd()
  218. def construct(self, x):
  219. r"""construct of GhostNet BottleNeck"""
  220. shortcut = x
  221. out = self.ghost1(x)
  222. if self.use_dw:
  223. out = self.dw(out)
  224. if self.use_se:
  225. out = self.se(out)
  226. out = self.ghost2(out)
  227. if self.down_sample:
  228. shortcut = self.shortcut(shortcut)
  229. out = self.add(shortcut, out)
  230. return out
  231. def _get_pad(self, kernel_size):
  232. """set the padding number"""
  233. pad = 0
  234. if kernel_size == 1:
  235. pad = 0
  236. elif kernel_size == 3:
  237. pad = 1
  238. elif kernel_size == 5:
  239. pad = 2
  240. elif kernel_size == 7:
  241. pad = 3
  242. else:
  243. raise NotImplementedError
  244. return pad
  245. class GhostNet(nn.Cell):
  246. """
  247. GhostNet architecture.
  248. Args:
  249. model_cfgs (Cell): number of classes.
  250. num_classes (int): Output number classes.
  251. multiplier (int): Channels multiplier for round to 8/16 and others. Default is 1.
  252. final_drop (float): Dropout number.
  253. round_nearest (list): Channel round to . Default is 8.
  254. Returns:
  255. Tensor, output tensor.
  256. Examples:
  257. >>> GhostNet(num_classes=1000)
  258. """
  259. def __init__(self, model_cfgs, num_classes=1000, multiplier=1., final_drop=0., round_nearest=8):
  260. super(GhostNet, self).__init__()
  261. self.cfgs = model_cfgs['cfg']
  262. self.inplanes = 16
  263. first_conv_in_channel = 3
  264. first_conv_out_channel = _make_divisible(multiplier * self.inplanes)
  265. self.conv_stem = QuanConv(in_channels=first_conv_in_channel,
  266. out_channels=first_conv_out_channel,
  267. kernel_size=3, padding=1, stride=2,
  268. has_bias=False, pad_mode='pad')
  269. self.bn1 = nn.BatchNorm2d(first_conv_out_channel)
  270. self.act1 = Activation('relu')
  271. self.blocks = []
  272. for layer_cfg in self.cfgs:
  273. self.blocks.append(self._make_layer(kernel_size=layer_cfg[0],
  274. exp_ch=_make_divisible(
  275. multiplier * layer_cfg[1]),
  276. out_channel=_make_divisible(
  277. multiplier * layer_cfg[2]),
  278. use_se=layer_cfg[3],
  279. act_func=layer_cfg[4],
  280. stride=layer_cfg[5]))
  281. output_channel = _make_divisible(
  282. multiplier * model_cfgs["cls_ch_squeeze"])
  283. self.blocks.append(ConvUnit(_make_divisible(multiplier * self.cfgs[-1][2]), output_channel,
  284. kernel_size=1, stride=1, padding=0, num_groups=1, use_act=True))
  285. self.blocks = nn.SequentialCell(self.blocks)
  286. self.global_pool = GlobalAvgPooling(keep_dims=True)
  287. self.conv_head = QuanConv(in_channels=output_channel,
  288. out_channels=model_cfgs['cls_ch_expand'],
  289. kernel_size=1, padding=0, stride=1,
  290. has_bias=True, pad_mode='pad')
  291. self.act2 = Activation('relu')
  292. self.squeeze = P.Flatten()
  293. self.final_drop = final_drop
  294. if self.final_drop > 0:
  295. self.dropout = nn.Dropout(self.final_drop)
  296. self.classifier = nn.Dense(
  297. model_cfgs['cls_ch_expand'], num_classes, has_bias=True)
  298. self._initialize_weights()
  299. def construct(self, x):
  300. r"""construct of GhostNet"""
  301. x = self.conv_stem(x)
  302. x = self.bn1(x)
  303. x = self.act1(x)
  304. x = self.blocks(x)
  305. x = self.global_pool(x)
  306. x = self.conv_head(x)
  307. x = self.act2(x)
  308. x = self.squeeze(x)
  309. if self.final_drop > 0:
  310. x = self.dropout(x)
  311. x = self.classifier(x)
  312. return x
  313. def _make_layer(self, kernel_size, exp_ch, out_channel, use_se, act_func, stride=1):
  314. mid_planes = exp_ch
  315. out_planes = out_channel
  316. layer = GhostBottleneck(self.inplanes, mid_planes, out_planes,
  317. kernel_size, stride=stride, act_type=act_func, use_se=use_se)
  318. self.inplanes = out_planes
  319. return layer
  320. def _initialize_weights(self):
  321. """
  322. Initialize weights.
  323. Args:
  324. Returns:
  325. None.
  326. Examples:
  327. >>> _initialize_weights()
  328. """
  329. self.init_parameters_data()
  330. for _, m in self.cells_and_names():
  331. if isinstance(m, (nn.Conv2d)):
  332. n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
  333. m.weight.set_parameter_data(Tensor(np.random.normal(0, np.sqrt(2. / n),
  334. m.weight.data.shape).astype("float32")))
  335. if m.bias is not None:
  336. m.bias.set_parameter_data(
  337. Tensor(np.zeros(m.bias.data.shape, dtype="float32")))
  338. elif isinstance(m, nn.BatchNorm2d):
  339. m.gamma.set_parameter_data(
  340. Tensor(np.ones(m.gamma.data.shape, dtype="float32")))
  341. m.beta.set_parameter_data(
  342. Tensor(np.zeros(m.beta.data.shape, dtype="float32")))
  343. elif isinstance(m, nn.Dense):
  344. m.weight.set_parameter_data(Tensor(np.random.normal(
  345. 0, 0.01, m.weight.data.shape).astype("float32")))
  346. if m.bias is not None:
  347. m.bias.set_parameter_data(
  348. Tensor(np.zeros(m.bias.data.shape, dtype="float32")))
  349. def ghostnet(model_name, **kwargs):
  350. """
  351. Constructs a GhostNet model
  352. """
  353. model_cfgs = {
  354. "1x": {
  355. "cfg": [
  356. # k, exp, c, se, nl, s,
  357. # stage1
  358. [3, 16, 16, False, 'relu', 1],
  359. # stage2
  360. [3, 48, 24, False, 'relu', 2],
  361. [3, 72, 24, False, 'relu', 1],
  362. # stage3
  363. [5, 72, 40, True, 'relu', 2],
  364. [5, 120, 40, True, 'relu', 1],
  365. # stage4
  366. [3, 240, 80, False, 'relu', 2],
  367. [3, 200, 80, False, 'relu', 1],
  368. [3, 184, 80, False, 'relu', 1],
  369. [3, 184, 80, False, 'relu', 1],
  370. [3, 480, 112, True, 'relu', 1],
  371. [3, 672, 112, True, 'relu', 1],
  372. # stage5
  373. [5, 672, 160, True, 'relu', 2],
  374. [5, 960, 160, False, 'relu', 1],
  375. [5, 960, 160, True, 'relu', 1],
  376. [5, 960, 160, False, 'relu', 1],
  377. [5, 960, 160, True, 'relu', 1]],
  378. "cls_ch_squeeze": 960,
  379. "cls_ch_expand": 1280,
  380. },
  381. "nose_1x": {
  382. "cfg": [
  383. # k, exp, c, se, nl, s,
  384. # stage1
  385. [3, 16, 16, False, 'relu', 1],
  386. # stage2
  387. [3, 48, 24, False, 'relu', 2],
  388. [3, 72, 24, False, 'relu', 1],
  389. # stage3
  390. [5, 72, 40, False, 'relu', 2],
  391. [5, 120, 40, False, 'relu', 1],
  392. # stage4
  393. [3, 240, 80, False, 'relu', 2],
  394. [3, 200, 80, False, 'relu', 1],
  395. [3, 184, 80, False, 'relu', 1],
  396. [3, 184, 80, False, 'relu', 1],
  397. [3, 480, 112, False, 'relu', 1],
  398. [3, 672, 112, False, 'relu', 1],
  399. # stage5
  400. [5, 672, 160, False, 'relu', 2],
  401. [5, 960, 160, False, 'relu', 1],
  402. [5, 960, 160, False, 'relu', 1],
  403. [5, 960, 160, False, 'relu', 1],
  404. [5, 960, 160, False, 'relu', 1]],
  405. "cls_ch_squeeze": 960,
  406. "cls_ch_expand": 1280,
  407. }
  408. }
  409. return GhostNet(model_cfgs[model_name], **kwargs)
  410. ghostnet_1x = partial(ghostnet, model_name="1x", final_drop=0.8)
  411. ghostnet_nose_1x = partial(ghostnet, model_name="nose_1x", final_drop=0.8)