cspdarknet.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. from __future__ import absolute_import
  15. from __future__ import division
  16. from __future__ import print_function
  17. import six
  18. from paddle import fluid
  19. from paddle.fluid.param_attr import ParamAttr
  20. from paddle.fluid.regularizer import L2Decay
  21. from ppdet.core.workspace import register
  22. __all__ = ['CSPDarkNet']
  23. @register
  24. class CSPDarkNet(object):
  25. """
  26. CSPDarkNet, see https://arxiv.org/abs/1911.11929
  27. Args:
  28. depth (int): network depth, currently only cspdarknet 53 is supported
  29. norm_type (str): normalization type, 'bn' and 'sync_bn' are supported
  30. norm_decay (float): weight decay for normalization layer weights
  31. """
  32. __shared__ = ['norm_type', 'weight_prefix_name']
  33. def __init__(self,
  34. depth=53,
  35. norm_type='bn',
  36. norm_decay=0.,
  37. weight_prefix_name=''):
  38. assert depth in [53], "unsupported depth value"
  39. self.depth = depth
  40. self.norm_type = norm_type
  41. self.norm_decay = norm_decay
  42. self.depth_cfg = {53: ([1, 2, 8, 8, 4], self.basicblock)}
  43. self.prefix_name = weight_prefix_name
  44. def _softplus(self, input):
  45. expf = fluid.layers.exp(fluid.layers.clip(input, -200, 50))
  46. return fluid.layers.log(1 + expf)
  47. def _mish(self, input):
  48. return input * fluid.layers.tanh(self._softplus(input))
  49. def _conv_norm(self,
  50. input,
  51. ch_out,
  52. filter_size,
  53. stride,
  54. padding,
  55. act='mish',
  56. name=None):
  57. conv = fluid.layers.conv2d(
  58. input=input,
  59. num_filters=ch_out,
  60. filter_size=filter_size,
  61. stride=stride,
  62. padding=padding,
  63. act=None,
  64. param_attr=ParamAttr(name=name + ".conv.weights"),
  65. bias_attr=False)
  66. bn_name = name + ".bn"
  67. bn_param_attr = ParamAttr(
  68. regularizer=L2Decay(float(self.norm_decay)),
  69. name=bn_name + '.scale')
  70. bn_bias_attr = ParamAttr(
  71. regularizer=L2Decay(float(self.norm_decay)),
  72. name=bn_name + '.offset')
  73. out = fluid.layers.batch_norm(
  74. input=conv,
  75. act=None,
  76. param_attr=bn_param_attr,
  77. bias_attr=bn_bias_attr,
  78. moving_mean_name=bn_name + '.mean',
  79. moving_variance_name=bn_name + '.var')
  80. if act == 'mish':
  81. out = self._mish(out)
  82. return out
  83. def _downsample(self,
  84. input,
  85. ch_out,
  86. filter_size=3,
  87. stride=2,
  88. padding=1,
  89. name=None):
  90. return self._conv_norm(
  91. input,
  92. ch_out=ch_out,
  93. filter_size=filter_size,
  94. stride=stride,
  95. padding=padding,
  96. name=name)
  97. def conv_layer(self,
  98. input,
  99. ch_out,
  100. filter_size=1,
  101. stride=1,
  102. padding=0,
  103. name=None):
  104. return self._conv_norm(
  105. input,
  106. ch_out=ch_out,
  107. filter_size=filter_size,
  108. stride=stride,
  109. padding=padding,
  110. name=name)
  111. def basicblock(self, input, ch_out, scale_first=False, name=None):
  112. conv1 = self._conv_norm(
  113. input,
  114. ch_out=ch_out // 2 if scale_first else ch_out,
  115. filter_size=1,
  116. stride=1,
  117. padding=0,
  118. name=name + ".0")
  119. conv2 = self._conv_norm(
  120. conv1,
  121. ch_out=ch_out,
  122. filter_size=3,
  123. stride=1,
  124. padding=1,
  125. name=name + ".1")
  126. out = fluid.layers.elementwise_add(x=input, y=conv2, act=None)
  127. return out
  128. def layer_warp(self,
  129. block_func,
  130. input,
  131. ch_out,
  132. count,
  133. keep_ch=False,
  134. scale_first=False,
  135. name=None):
  136. if scale_first:
  137. ch_out = ch_out * 2
  138. right = self.conv_layer(
  139. input, ch_out, name='{}.route_in.right'.format(name))
  140. neck = self.conv_layer(input, ch_out, name='{}.neck'.format(name))
  141. out = block_func(
  142. neck,
  143. ch_out=ch_out,
  144. scale_first=scale_first,
  145. name='{}.0'.format(name))
  146. for j in six.moves.xrange(1, count):
  147. out = block_func(out, ch_out=ch_out, name='{}.{}'.format(name, j))
  148. left = self.conv_layer(
  149. out, ch_out, name='{}.route_in.left'.format(name))
  150. route = fluid.layers.concat([left, right], axis=1)
  151. out = self.conv_layer(
  152. route,
  153. ch_out=ch_out if keep_ch else ch_out * 2,
  154. name='{}.conv_layer'.format(name))
  155. return out
  156. def __call__(self, input):
  157. """
  158. Get the backbone of CSPDarkNet, that is output for the 5 stages.
  159. Args:
  160. input (Variable): input variable.
  161. Returns:
  162. The last variables of each stage.
  163. """
  164. stages, block_func = self.depth_cfg[self.depth]
  165. stages = stages[0:5]
  166. conv = self._conv_norm(
  167. input=input,
  168. ch_out=32,
  169. filter_size=3,
  170. stride=1,
  171. padding=1,
  172. act='mish',
  173. name=self.prefix_name + "conv")
  174. blocks = []
  175. for i, stage in enumerate(stages):
  176. input = conv if i == 0 else block
  177. downsample_ = self._downsample(
  178. input=input,
  179. ch_out=input.shape[1] * 2,
  180. name=self.prefix_name + "stage.{}.downsample".format(i))
  181. block = self.layer_warp(
  182. block_func=block_func,
  183. input=downsample_,
  184. ch_out=32 * 2**i,
  185. count=stage,
  186. keep_ch=(i == 0),
  187. scale_first=i == 0,
  188. name=self.prefix_name + "stage.{}".format(i))
  189. blocks.append(block)
  190. return blocks