yolo_head.py 6.0 KB
Newer Older
1
import paddle.fluid as fluid
W
wangguanzhong 已提交
2
import paddle
W
wangxinxin08 已提交
3 4 5
import paddle.nn as nn
import paddle.nn.functional as F
from paddle import ParamAttr
6 7 8 9 10
from paddle.fluid.regularizer import L2Decay
from ppdet.core.workspace import register
from ..backbone.darknet import ConvBNLayer


W
wangxinxin08 已提交
11
class YoloDetBlock(nn.Layer):
W
wangguanzhong 已提交
12
    def __init__(self, ch_in, channel, name):
13
        super(YoloDetBlock, self).__init__()
W
wangguanzhong 已提交
14 15
        self.ch_in = ch_in
        self.channel = channel
16 17
        assert channel % 2 == 0, \
            "channel {} cannot be divided by 2".format(channel)
W
wangguanzhong 已提交
18 19 20 21 22 23 24 25 26
        conv_def = [
            ['conv0', ch_in, channel, 1, '.0.0'],
            ['conv1', channel, channel * 2, 3, '.0.1'],
            ['conv2', channel * 2, channel, 1, '.1.0'],
            ['conv3', channel, channel * 2, 3, '.1.1'],
            ['route', channel * 2, channel, 1, '.2'],
            #['tip', channel, channel * 2, 3],
        ]

W
wangxinxin08 已提交
27
        self.conv_module = nn.Sequential()
W
wangguanzhong 已提交
28 29 30 31 32 33 34 35 36 37
        for idx, (conv_name, ch_in, ch_out, filter_size,
                  post_name) in enumerate(conv_def):
            self.conv_module.add_sublayer(
                conv_name,
                ConvBNLayer(
                    ch_in=ch_in,
                    ch_out=ch_out,
                    filter_size=filter_size,
                    padding=(filter_size - 1) // 2,
                    name=name + post_name))
38 39 40 41 42

        self.tip = ConvBNLayer(
            ch_in=channel,
            ch_out=channel * 2,
            filter_size=3,
W
wangguanzhong 已提交
43 44
            padding=1,
            name=name + '.tip')
45 46

    def forward(self, inputs):
W
wangguanzhong 已提交
47
        route = self.conv_module(inputs)
48 49 50 51 52
        tip = self.tip(route)
        return route, tip


@register
W
wangxinxin08 已提交
53
class YOLOFeat(nn.Layer):
W
wangguanzhong 已提交
54 55 56
    __shared__ = ['num_levels']

    def __init__(self, feat_in_list=[1024, 768, 384], num_levels=3):
57 58 59 60
        super(YOLOFeat, self).__init__()
        self.feat_in_list = feat_in_list
        self.yolo_blocks = []
        self.route_blocks = []
W
wangguanzhong 已提交
61 62 63
        self.num_levels = num_levels
        for i in range(self.num_levels):
            name = 'yolo_block.{}'.format(i)
64
            yolo_block = self.add_sublayer(
W
wangguanzhong 已提交
65
                name,
66
                YoloDetBlock(
W
wangguanzhong 已提交
67
                    feat_in_list[i], channel=512 // (2**i), name=name))
68 69
            self.yolo_blocks.append(yolo_block)

W
wangguanzhong 已提交
70 71
            if i < self.num_levels - 1:
                name = 'yolo_transition.{}'.format(i)
72
                route = self.add_sublayer(
W
wangguanzhong 已提交
73
                    name,
74 75 76 77 78
                    ConvBNLayer(
                        ch_in=512 // (2**i),
                        ch_out=256 // (2**i),
                        filter_size=1,
                        stride=1,
W
wangguanzhong 已提交
79 80
                        padding=0,
                        name=name))
81 82
                self.route_blocks.append(route)

W
wangguanzhong 已提交
83 84 85
    def forward(self, body_feats):
        assert len(body_feats) == self.num_levels
        body_feats = body_feats[::-1]
86
        yolo_feats = []
W
wangguanzhong 已提交
87
        for i, block in enumerate(body_feats):
88
            if i > 0:
W
wangxinxin08 已提交
89
                block = paddle.concat([route, block], axis=1)
90 91 92
            route, tip = self.yolo_blocks[i](block)
            yolo_feats.append(tip)

W
wangguanzhong 已提交
93
            if i < self.num_levels - 1:
94
                route = self.route_blocks[i](route)
W
wangxinxin08 已提交
95
                route = F.resize_nearest(route, scale=2.)
96

W
wangguanzhong 已提交
97
        return yolo_feats
98 99 100


@register
W
wangxinxin08 已提交
101
class YOLOv3Head(nn.Layer):
W
wangguanzhong 已提交
102
    __shared__ = ['num_classes', 'num_levels', 'use_fine_grained_loss']
103 104
    __inject__ = ['yolo_feat']

W
wangguanzhong 已提交
105 106 107 108 109 110 111 112 113
    def __init__(self,
                 yolo_feat,
                 num_classes=80,
                 anchor_per_position=3,
                 num_levels=3,
                 use_fine_grained_loss=False,
                 ignore_thresh=0.7,
                 downsample=32,
                 label_smooth=True):
114 115 116 117
        super(YOLOv3Head, self).__init__()
        self.num_classes = num_classes
        self.anchor_per_position = anchor_per_position
        self.yolo_feat = yolo_feat
W
wangguanzhong 已提交
118 119 120 121 122 123 124
        self.num_levels = num_levels
        self.use_fine_grained_loss = use_fine_grained_loss
        self.ignore_thresh = ignore_thresh
        self.downsample = downsample
        self.label_smooth = label_smooth
        self.yolo_out_list = []
        for i in range(num_levels):
125 126 127
            # TODO: optim here
            #num_filters = len(cfg.anchor_masks[i]) * (self.num_classes + 5)
            num_filters = self.anchor_per_position * (self.num_classes + 5)
W
wangguanzhong 已提交
128
            name = 'yolo_output.{}'.format(i)
129
            yolo_out = self.add_sublayer(
W
wangguanzhong 已提交
130
                name,
W
wangxinxin08 已提交
131 132 133 134
                nn.Conv2d(
                    in_channels=1024 // (2**i),
                    out_channels=num_filters,
                    kernel_size=1,
135 136
                    stride=1,
                    padding=0,
W
wangxinxin08 已提交
137
                    weight_attr=ParamAttr(name=name + '.conv.weights'),
138
                    bias_attr=ParamAttr(
W
wangguanzhong 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
                        name=name + '.conv.bias', regularizer=L2Decay(0.))))
            self.yolo_out_list.append(yolo_out)

    def forward(self, body_feats):
        assert len(body_feats) == self.num_levels
        yolo_feats = self.yolo_feat(body_feats)
        yolo_head_out = []
        for i, feat in enumerate(yolo_feats):
            yolo_out = self.yolo_out_list[i](feat)
            yolo_head_out.append(yolo_out)
        return yolo_head_out

    def loss(self, inputs, head_out, anchors, anchor_masks, mask_anchors):
        if self.use_fine_grained_loss:
            raise NotImplementedError
154 155

        yolo_losses = []
W
wangguanzhong 已提交
156
        for i, out in enumerate(head_out):
157 158 159 160 161
            loss = fluid.layers.yolov3_loss(
                x=out,
                gt_box=inputs['gt_bbox'],
                gt_label=inputs['gt_class'],
                gt_score=inputs['gt_score'],
W
wangguanzhong 已提交
162 163
                anchors=anchors,
                anchor_mask=anchor_masks[i],
164
                class_num=self.num_classes,
W
wangguanzhong 已提交
165 166 167
                ignore_thresh=self.ignore_thresh,
                downsample_ratio=self.downsample // 2**i,
                use_label_smooth=self.label_smooth,
168 169 170
                name='yolo_loss_' + str(i))
            loss = fluid.layers.reduce_mean(loss)
            yolo_losses.append(loss)
W
wangguanzhong 已提交
171
        return {'loss': sum(yolo_losses)}