ssd_head.py 7.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# 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.

Q
qingqing01 已提交
15 16 17 18
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from ppdet.core.workspace import register
19 20 21
from paddle.regularizer import L2Decay
from paddle import ParamAttr

22
from ..layers import AnchorGeneratorSSD
23
from ..cls_utils import _get_class_default_kwargs
24

25 26 27 28 29 30 31

class SepConvLayer(nn.Layer):
    def __init__(self,
                 in_channels,
                 out_channels,
                 kernel_size=3,
                 padding=1,
32
                 conv_decay=0.):
33 34 35 36 37 38 39 40
        super(SepConvLayer, self).__init__()
        self.dw_conv = nn.Conv2D(
            in_channels=in_channels,
            out_channels=in_channels,
            kernel_size=kernel_size,
            stride=1,
            padding=padding,
            groups=in_channels,
41
            weight_attr=ParamAttr(regularizer=L2Decay(conv_decay)),
42 43 44 45
            bias_attr=False)

        self.bn = nn.BatchNorm2D(
            in_channels,
46 47
            weight_attr=ParamAttr(regularizer=L2Decay(0.)),
            bias_attr=ParamAttr(regularizer=L2Decay(0.)))
48 49 50 51 52 53 54

        self.pw_conv = nn.Conv2D(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=1,
            stride=1,
            padding=0,
55
            weight_attr=ParamAttr(regularizer=L2Decay(conv_decay)),
56 57 58 59 60 61 62
            bias_attr=False)

    def forward(self, x):
        x = self.dw_conv(x)
        x = F.relu6(self.bn(x))
        x = self.pw_conv(x)
        return x
Q
qingqing01 已提交
63 64


65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
class SSDExtraHead(nn.Layer):
    def __init__(self,
                 in_channels=256,
                 out_channels=([256, 512], [256, 512], [128, 256], [128, 256],
                               [128, 256]),
                 strides=(2, 2, 2, 1, 1),
                 paddings=(1, 1, 1, 0, 0)):
        super(SSDExtraHead, self).__init__()
        self.convs = nn.LayerList()
        for out_channel, stride, padding in zip(out_channels, strides,
                                                paddings):
            self.convs.append(
                self._make_layers(in_channels, out_channel[0], out_channel[1],
                                  stride, padding))
            in_channels = out_channel[-1]

    def _make_layers(self, c_in, c_hidden, c_out, stride_3x3, padding_3x3):
        return nn.Sequential(
            nn.Conv2D(c_in, c_hidden, 1),
            nn.ReLU(),
            nn.Conv2D(c_hidden, c_out, 3, stride_3x3, padding_3x3), nn.ReLU())

    def forward(self, x):
        out = [x]
        for conv_layer in self.convs:
            out.append(conv_layer(out[-1]))
        return out


Q
qingqing01 已提交
94 95
@register
class SSDHead(nn.Layer):
96 97 98 99 100 101 102 103 104 105 106 107
    """
    SSDHead

    Args:
        num_classes (int): Number of classes
        in_channels (list): Number of channels per input feature
        anchor_generator (dict): Configuration of 'AnchorGeneratorSSD' instance
        kernel_size (int): Conv kernel size
        padding (int): Conv padding
        use_sepconv (bool): Use SepConvLayer if true
        conv_decay (float): Conv regularization coeff
        loss (object): 'SSDLoss' instance
108
        use_extra_head (bool): If use ResNet34 as baskbone, you should set `use_extra_head`=True
109 110
    """

Q
qingqing01 已提交
111 112 113 114
    __shared__ = ['num_classes']
    __inject__ = ['anchor_generator', 'loss']

    def __init__(self,
115
                 num_classes=80,
Q
qingqing01 已提交
116
                 in_channels=(512, 1024, 512, 256, 256, 256),
117
                 anchor_generator=_get_class_default_kwargs(AnchorGeneratorSSD),
118 119 120 121
                 kernel_size=3,
                 padding=1,
                 use_sepconv=False,
                 conv_decay=0.,
122 123
                 loss='SSDLoss',
                 use_extra_head=False):
Q
qingqing01 已提交
124
        super(SSDHead, self).__init__()
125 126
        # add background class
        self.num_classes = num_classes + 1
Q
qingqing01 已提交
127 128 129
        self.in_channels = in_channels
        self.anchor_generator = anchor_generator
        self.loss = loss
130 131 132 133 134
        self.use_extra_head = use_extra_head

        if self.use_extra_head:
            self.ssd_extra_head = SSDExtraHead()
            self.in_channels = [256, 512, 512, 256, 256, 256]
Q
qingqing01 已提交
135

136 137 138 139
        if isinstance(anchor_generator, dict):
            self.anchor_generator = AnchorGeneratorSSD(**anchor_generator)

        self.num_priors = self.anchor_generator.num_priors
Q
qingqing01 已提交
140 141 142
        self.box_convs = []
        self.score_convs = []
        for i, num_prior in enumerate(self.num_priors):
143 144 145 146
            box_conv_name = "boxes{}".format(i)
            if not use_sepconv:
                box_conv = self.add_sublayer(
                    box_conv_name,
Q
qingqing01 已提交
147
                    nn.Conv2D(
148
                        in_channels=self.in_channels[i],
Q
qingqing01 已提交
149
                        out_channels=num_prior * 4,
150 151 152 153 154 155
                        kernel_size=kernel_size,
                        padding=padding))
            else:
                box_conv = self.add_sublayer(
                    box_conv_name,
                    SepConvLayer(
156
                        in_channels=self.in_channels[i],
157 158 159
                        out_channels=num_prior * 4,
                        kernel_size=kernel_size,
                        padding=padding,
160
                        conv_decay=conv_decay))
161 162 163 164 165 166
            self.box_convs.append(box_conv)

            score_conv_name = "scores{}".format(i)
            if not use_sepconv:
                score_conv = self.add_sublayer(
                    score_conv_name,
Q
qingqing01 已提交
167
                    nn.Conv2D(
168
                        in_channels=self.in_channels[i],
169
                        out_channels=num_prior * self.num_classes,
170 171 172 173 174 175
                        kernel_size=kernel_size,
                        padding=padding))
            else:
                score_conv = self.add_sublayer(
                    score_conv_name,
                    SepConvLayer(
176
                        in_channels=self.in_channels[i],
177
                        out_channels=num_prior * self.num_classes,
178 179
                        kernel_size=kernel_size,
                        padding=padding,
180
                        conv_decay=conv_decay))
181
            self.score_convs.append(score_conv)
Q
qingqing01 已提交
182

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

    def forward(self, feats, image, gt_bbox=None, gt_class=None):
188 189 190 191 192
        if self.use_extra_head:
            assert len(feats) == 1, \
                ("If you set use_extra_head=True, backbone feature "
                 "list length should be 1.")
            feats = self.ssd_extra_head(feats[0])
Q
qingqing01 已提交
193 194
        box_preds = []
        cls_scores = []
K
Kaipeng Deng 已提交
195 196
        for feat, box_conv, score_conv in zip(feats, self.box_convs,
                                              self.score_convs):
Q
qingqing01 已提交
197 198 199 200 201 202 203 204 205 206 207 208
            box_pred = box_conv(feat)
            box_pred = paddle.transpose(box_pred, [0, 2, 3, 1])
            box_pred = paddle.reshape(box_pred, [0, -1, 4])
            box_preds.append(box_pred)

            cls_score = score_conv(feat)
            cls_score = paddle.transpose(cls_score, [0, 2, 3, 1])
            cls_score = paddle.reshape(cls_score, [0, -1, self.num_classes])
            cls_scores.append(cls_score)

        prior_boxes = self.anchor_generator(feats, image)

209 210 211 212
        if self.training:
            return self.get_loss(box_preds, cls_scores, gt_bbox, gt_class,
                                 prior_boxes)
        else:
K
Kaipeng Deng 已提交
213
            return (box_preds, cls_scores), prior_boxes
Q
qingqing01 已提交
214

215 216
    def get_loss(self, boxes, scores, gt_bbox, gt_class, prior_boxes):
        return self.loss(boxes, scores, gt_bbox, gt_class, prior_boxes)