123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- import six
- from paddle import fluid
- from paddle.fluid.param_attr import ParamAttr
- from paddle.fluid.regularizer import L2Decay
- from ppdet.core.workspace import register
- __all__ = ['CSPDarkNet']
- @register
- class CSPDarkNet(object):
- """
- CSPDarkNet, see https://arxiv.org/abs/1911.11929
- Args:
- depth (int): network depth, currently only cspdarknet 53 is supported
- norm_type (str): normalization type, 'bn' and 'sync_bn' are supported
- norm_decay (float): weight decay for normalization layer weights
- """
- __shared__ = ['norm_type', 'weight_prefix_name']
- def __init__(self,
- depth=53,
- norm_type='bn',
- norm_decay=0.,
- weight_prefix_name=''):
- assert depth in [53], "unsupported depth value"
- self.depth = depth
- self.norm_type = norm_type
- self.norm_decay = norm_decay
- self.depth_cfg = {53: ([1, 2, 8, 8, 4], self.basicblock)}
- self.prefix_name = weight_prefix_name
- def _softplus(self, input):
- expf = fluid.layers.exp(fluid.layers.clip(input, -200, 50))
- return fluid.layers.log(1 + expf)
- def _mish(self, input):
- return input * fluid.layers.tanh(self._softplus(input))
- def _conv_norm(self,
- input,
- ch_out,
- filter_size,
- stride,
- padding,
- act='mish',
- name=None):
- conv = fluid.layers.conv2d(
- input=input,
- num_filters=ch_out,
- filter_size=filter_size,
- stride=stride,
- padding=padding,
- act=None,
- param_attr=ParamAttr(name=name + ".conv.weights"),
- bias_attr=False)
- bn_name = name + ".bn"
- bn_param_attr = ParamAttr(
- regularizer=L2Decay(float(self.norm_decay)),
- name=bn_name + '.scale')
- bn_bias_attr = ParamAttr(
- regularizer=L2Decay(float(self.norm_decay)),
- name=bn_name + '.offset')
- out = fluid.layers.batch_norm(
- input=conv,
- act=None,
- param_attr=bn_param_attr,
- bias_attr=bn_bias_attr,
- moving_mean_name=bn_name + '.mean',
- moving_variance_name=bn_name + '.var')
- if act == 'mish':
- out = self._mish(out)
- return out
- def _downsample(self,
- input,
- ch_out,
- filter_size=3,
- stride=2,
- padding=1,
- name=None):
- return self._conv_norm(
- input,
- ch_out=ch_out,
- filter_size=filter_size,
- stride=stride,
- padding=padding,
- name=name)
- def conv_layer(self,
- input,
- ch_out,
- filter_size=1,
- stride=1,
- padding=0,
- name=None):
- return self._conv_norm(
- input,
- ch_out=ch_out,
- filter_size=filter_size,
- stride=stride,
- padding=padding,
- name=name)
- def basicblock(self, input, ch_out, scale_first=False, name=None):
- conv1 = self._conv_norm(
- input,
- ch_out=ch_out // 2 if scale_first else ch_out,
- filter_size=1,
- stride=1,
- padding=0,
- name=name + ".0")
- conv2 = self._conv_norm(
- conv1,
- ch_out=ch_out,
- filter_size=3,
- stride=1,
- padding=1,
- name=name + ".1")
- out = fluid.layers.elementwise_add(x=input, y=conv2, act=None)
- return out
- def layer_warp(self,
- block_func,
- input,
- ch_out,
- count,
- keep_ch=False,
- scale_first=False,
- name=None):
- if scale_first:
- ch_out = ch_out * 2
- right = self.conv_layer(
- input, ch_out, name='{}.route_in.right'.format(name))
- neck = self.conv_layer(input, ch_out, name='{}.neck'.format(name))
- out = block_func(
- neck,
- ch_out=ch_out,
- scale_first=scale_first,
- name='{}.0'.format(name))
- for j in six.moves.xrange(1, count):
- out = block_func(out, ch_out=ch_out, name='{}.{}'.format(name, j))
- left = self.conv_layer(
- out, ch_out, name='{}.route_in.left'.format(name))
- route = fluid.layers.concat([left, right], axis=1)
- out = self.conv_layer(
- route,
- ch_out=ch_out if keep_ch else ch_out * 2,
- name='{}.conv_layer'.format(name))
- return out
- def __call__(self, input):
- """
- Get the backbone of CSPDarkNet, that is output for the 5 stages.
- Args:
- input (Variable): input variable.
- Returns:
- The last variables of each stage.
- """
- stages, block_func = self.depth_cfg[self.depth]
- stages = stages[0:5]
- conv = self._conv_norm(
- input=input,
- ch_out=32,
- filter_size=3,
- stride=1,
- padding=1,
- act='mish',
- name=self.prefix_name + "conv")
- blocks = []
- for i, stage in enumerate(stages):
- input = conv if i == 0 else block
- downsample_ = self._downsample(
- input=input,
- ch_out=input.shape[1] * 2,
- name=self.prefix_name + "stage.{}.downsample".format(i))
- block = self.layer_warp(
- block_func=block_func,
- input=downsample_,
- ch_out=32 * 2**i,
- count=stage,
- keep_ch=(i == 0),
- scale_first=i == 0,
- name=self.prefix_name + "stage.{}".format(i))
- blocks.append(block)
- return blocks
|