ttf_fpn.py 8.8 KB
Newer Older
F
Feng Ni 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# 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.

import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from paddle import ParamAttr
W
wangguanzhong 已提交
19
from paddle.nn.initializer import Constant, Uniform, Normal, XavierUniform
F
Feng Ni 已提交
20 21 22
from paddle import ParamAttr
from ppdet.core.workspace import register, serializable
from paddle.regularizer import L2Decay
W
wangguanzhong 已提交
23
from ppdet.modeling.layers import DeformableConvV2, ConvNormLayer, LiteConv
F
Feng Ni 已提交
24 25
import math
from ppdet.modeling.ops import batch_norm
26 27 28
from ..shape_spec import ShapeSpec

__all__ = ['TTFFPN']
F
Feng Ni 已提交
29 30 31


class Upsample(nn.Layer):
32
    def __init__(self, ch_in, ch_out, norm_type='bn'):
F
Feng Ni 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45
        super(Upsample, self).__init__()
        fan_in = ch_in * 3 * 3
        stdv = 1. / math.sqrt(fan_in)
        self.dcn = DeformableConvV2(
            ch_in,
            ch_out,
            kernel_size=3,
            weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
            bias_attr=ParamAttr(
                initializer=Constant(0),
                regularizer=L2Decay(0.),
                learning_rate=2.),
            lr_scale=2.,
W
wangxinxin08 已提交
46
            regularizer=L2Decay(0.))
F
Feng Ni 已提交
47 48

        self.bn = batch_norm(
49
            ch_out, norm_type=norm_type, initializer=Constant(1.))
F
Feng Ni 已提交
50 51 52 53 54 55 56 57 58

    def forward(self, feat):
        dcn = self.dcn(feat)
        bn = self.bn(dcn)
        relu = F.relu(bn)
        out = F.interpolate(relu, scale_factor=2., mode='bilinear')
        return out


W
wangguanzhong 已提交
59
class DeConv(nn.Layer):
60
    def __init__(self, ch_in, ch_out, norm_type='bn'):
W
wangguanzhong 已提交
61 62 63 64 65 66 67 68
        super(DeConv, self).__init__()
        self.deconv = nn.Sequential()
        conv1 = ConvNormLayer(
            ch_in=ch_in,
            ch_out=ch_out,
            stride=1,
            filter_size=1,
            norm_type=norm_type,
69
            initializer=XavierUniform())
W
wangguanzhong 已提交
70 71 72 73 74 75 76 77 78
        conv2 = nn.Conv2DTranspose(
            in_channels=ch_out,
            out_channels=ch_out,
            kernel_size=4,
            padding=1,
            stride=2,
            groups=ch_out,
            weight_attr=ParamAttr(initializer=XavierUniform()),
            bias_attr=False)
79
        bn = batch_norm(ch_out, norm_type=norm_type, norm_decay=0.)
W
wangguanzhong 已提交
80 81 82 83 84 85
        conv3 = ConvNormLayer(
            ch_in=ch_out,
            ch_out=ch_out,
            stride=1,
            filter_size=1,
            norm_type=norm_type,
86
            initializer=XavierUniform())
W
wangguanzhong 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100

        self.deconv.add_sublayer('conv1', conv1)
        self.deconv.add_sublayer('relu6_1', nn.ReLU6())
        self.deconv.add_sublayer('conv2', conv2)
        self.deconv.add_sublayer('bn', bn)
        self.deconv.add_sublayer('relu6_2', nn.ReLU6())
        self.deconv.add_sublayer('conv3', conv3)
        self.deconv.add_sublayer('relu6_3', nn.ReLU6())

    def forward(self, inputs):
        return self.deconv(inputs)


class LiteUpsample(nn.Layer):
101
    def __init__(self, ch_in, ch_out, norm_type='bn'):
W
wangguanzhong 已提交
102
        super(LiteUpsample, self).__init__()
103 104
        self.deconv = DeConv(ch_in, ch_out, norm_type=norm_type)
        self.conv = LiteConv(ch_in, ch_out, norm_type=norm_type)
W
wangguanzhong 已提交
105 106 107 108 109 110 111 112

    def forward(self, inputs):
        deconv_up = self.deconv(inputs)
        conv = self.conv(inputs)
        interp_up = F.interpolate(conv, scale_factor=2., mode='bilinear')
        return deconv_up + interp_up


F
Feng Ni 已提交
113
class ShortCut(nn.Layer):
W
wangguanzhong 已提交
114 115 116 117 118 119 120
    def __init__(self,
                 layer_num,
                 ch_in,
                 ch_out,
                 norm_type='bn',
                 lite_neck=False,
                 name=None):
F
Feng Ni 已提交
121
        super(ShortCut, self).__init__()
F
Feng Ni 已提交
122
        shortcut_conv = nn.Sequential()
F
Feng Ni 已提交
123 124 125 126 127
        for i in range(layer_num):
            fan_out = 3 * 3 * ch_out
            std = math.sqrt(2. / fan_out)
            in_channels = ch_in if i == 0 else ch_out
            shortcut_name = name + '.conv.{}'.format(i)
W
wangguanzhong 已提交
128 129 130 131 132 133 134
            if lite_neck:
                shortcut_conv.add_sublayer(
                    shortcut_name,
                    LiteConv(
                        in_channels=in_channels,
                        out_channels=ch_out,
                        with_act=i < layer_num - 1,
135
                        norm_type=norm_type))
W
wangguanzhong 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149
            else:
                shortcut_conv.add_sublayer(
                    shortcut_name,
                    nn.Conv2D(
                        in_channels=in_channels,
                        out_channels=ch_out,
                        kernel_size=3,
                        padding=1,
                        weight_attr=ParamAttr(initializer=Normal(0, std)),
                        bias_attr=ParamAttr(
                            learning_rate=2., regularizer=L2Decay(0.))))
                if i < layer_num - 1:
                    shortcut_conv.add_sublayer(shortcut_name + '.act',
                                               nn.ReLU())
150
        self.shortcut = self.add_sublayer('shortcut', shortcut_conv)
F
Feng Ni 已提交
151 152 153 154 155 156 157 158 159

    def forward(self, feat):
        out = self.shortcut(feat)
        return out


@register
@serializable
class TTFFPN(nn.Layer):
160 161 162 163 164
    """
    Args:
        in_channels (list): number of input feature channels from backbone.
            [128,256,512,1024] by default, means the channels of DarkNet53
            backbone return_idx [1,2,3,4].
W
wangguanzhong 已提交
165 166
        planes (list): the number of output feature channels of FPN.
            [256, 128, 64] by default
167 168 169
        shortcut_num (list): the number of convolution layers in each shortcut.
            [3,2,1] by default, means DarkNet53 backbone return_idx_1 has 3 convs
            in its shortcut, return_idx_2 has 2 convs and return_idx_3 has 1 conv.
W
wangguanzhong 已提交
170 171 172 173 174 175
        norm_type (string): norm type, 'sync_bn', 'bn', 'gn' are optional. 
            bn by default
        lite_neck (bool): whether to use lite conv in TTFNet FPN, 
            False by default
        fusion_method (string): the method to fusion upsample and lateral layer.
            'add' and 'concat' are optional, add by default
176 177
    """

W
wangguanzhong 已提交
178 179
    __shared__ = ['norm_type']

F
Feng Ni 已提交
180
    def __init__(self,
W
wangguanzhong 已提交
181 182 183 184 185 186
                 in_channels,
                 planes=[256, 128, 64],
                 shortcut_num=[3, 2, 1],
                 norm_type='bn',
                 lite_neck=False,
                 fusion_method='add'):
F
Feng Ni 已提交
187
        super(TTFFPN, self).__init__()
W
wangguanzhong 已提交
188
        self.planes = planes
189
        self.shortcut_num = shortcut_num[::-1]
F
Feng Ni 已提交
190
        self.shortcut_len = len(shortcut_num)
191
        self.ch_in = in_channels[::-1]
W
wangguanzhong 已提交
192
        self.fusion_method = fusion_method
193

F
Feng Ni 已提交
194 195
        self.upsample_list = []
        self.shortcut_list = []
W
wangguanzhong 已提交
196
        self.upper_list = []
F
Feng Ni 已提交
197
        for i, out_c in enumerate(self.planes):
W
wangguanzhong 已提交
198 199
            in_c = self.ch_in[i] if i == 0 else self.upper_list[-1]
            upsample_module = LiteUpsample if lite_neck else Upsample
F
Feng Ni 已提交
200 201
            upsample = self.add_sublayer(
                'upsample.' + str(i),
W
wangguanzhong 已提交
202
                upsample_module(
203
                    in_c, out_c, norm_type=norm_type))
F
Feng Ni 已提交
204 205 206 207 208
            self.upsample_list.append(upsample)
            if i < self.shortcut_len:
                shortcut = self.add_sublayer(
                    'shortcut.' + str(i),
                    ShortCut(
W
wangguanzhong 已提交
209 210 211 212 213 214
                        self.shortcut_num[i],
                        self.ch_in[i + 1],
                        out_c,
                        norm_type=norm_type,
                        lite_neck=lite_neck,
                        name='shortcut.' + str(i)))
F
Feng Ni 已提交
215
                self.shortcut_list.append(shortcut)
W
wangguanzhong 已提交
216 217 218 219 220 221 222 223
                if self.fusion_method == 'add':
                    upper_c = out_c
                elif self.fusion_method == 'concat':
                    upper_c = out_c * 2
                else:
                    raise ValueError('Illegal fusion method. Expected add or\
                        concat, but received {}'.format(self.fusion_method))
                self.upper_list.append(upper_c)
F
Feng Ni 已提交
224 225 226 227 228 229 230

    def forward(self, inputs):
        feat = inputs[-1]
        for i, out_c in enumerate(self.planes):
            feat = self.upsample_list[i](feat)
            if i < self.shortcut_len:
                shortcut = self.shortcut_list[i](inputs[-i - 2])
W
wangguanzhong 已提交
231 232 233 234
                if self.fusion_method == 'add':
                    feat = feat + shortcut
                else:
                    feat = paddle.concat([feat, shortcut], axis=1)
F
Feng Ni 已提交
235
        return feat
236 237 238 239 240 241 242

    @classmethod
    def from_config(cls, cfg, input_shape):
        return {'in_channels': [i.channels for i in input_shape], }

    @property
    def out_shape(self):
W
wangguanzhong 已提交
243
        return [ShapeSpec(channels=self.upper_list[-1], )]