PulseFocusPlatform/static/ppdet/modeling/backbones/cspdarknet.py

213 lines
6.6 KiB
Python
Raw Normal View History

2022-06-01 11:18:00 +08:00
# 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