yolo_head.py 6.2 KB
Newer Older
1
import paddle.fluid as fluid
W
wangguanzhong 已提交
2
import paddle
3 4 5 6 7
from paddle.fluid.dygraph import Layer
from paddle.fluid.param_attr import ParamAttr
from paddle.fluid.initializer import Normal
from paddle.fluid.regularizer import L2Decay
from paddle.fluid.dygraph.nn import Conv2D, BatchNorm
W
wangguanzhong 已提交
8
from paddle.fluid.dygraph import Sequential
9 10 11 12 13
from ppdet.core.workspace import register
from ..backbone.darknet import ConvBNLayer


class YoloDetBlock(Layer):
W
wangguanzhong 已提交
14
    def __init__(self, ch_in, channel, name):
15
        super(YoloDetBlock, self).__init__()
W
wangguanzhong 已提交
16 17
        self.ch_in = ch_in
        self.channel = channel
18 19
        assert channel % 2 == 0, \
            "channel {} cannot be divided by 2".format(channel)
W
wangguanzhong 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
        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],
        ]

        self.conv_module = Sequential()
        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))
40 41 42 43 44

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

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


@register
class YOLOFeat(Layer):
W
wangguanzhong 已提交
56 57 58
    __shared__ = ['num_levels']

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

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

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

W
wangguanzhong 已提交
95
            if i < self.num_levels - 1:
96
                route = self.route_blocks[i](route)
W
wangguanzhong 已提交
97
                route = fluid.layers.resize_nearest(route, scale=2.)
98

W
wangguanzhong 已提交
99
        return yolo_feats
100 101 102 103


@register
class YOLOv3Head(Layer):
W
wangguanzhong 已提交
104
    __shared__ = ['num_classes', 'num_levels', 'use_fine_grained_loss']
105 106
    __inject__ = ['yolo_feat']

W
wangguanzhong 已提交
107 108 109 110 111 112 113 114 115
    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):
116 117 118 119
        super(YOLOv3Head, self).__init__()
        self.num_classes = num_classes
        self.anchor_per_position = anchor_per_position
        self.yolo_feat = yolo_feat
W
wangguanzhong 已提交
120 121 122 123 124 125 126
        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):
127 128 129
            # 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 已提交
130
            name = 'yolo_output.{}'.format(i)
131
            yolo_out = self.add_sublayer(
W
wangguanzhong 已提交
132
                name,
133 134 135 136 137 138 139
                Conv2D(
                    num_channels=1024 // (2**i),
                    num_filters=num_filters,
                    filter_size=1,
                    stride=1,
                    padding=0,
                    act=None,
W
wangguanzhong 已提交
140
                    param_attr=ParamAttr(name=name + '.conv.weights'),
141
                    bias_attr=ParamAttr(
W
wangguanzhong 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
                        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
157 158

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