(十六)论文阅读 | 目标检测之DetNet

简介

在这里插入图片描述

图1:论文原文

论文是发表在 E C C V   2018 {\rm ECCV\ 2018} 上的关于在目标检测中的提取特征部分的模型设计。以往的目标检测的 b a c k {\rm back} b o n e {\rm -bone} 通常基于预训练的图像分类模型,然后通过在训练过程中微调以得到最终的检测模型。但图像分类和目标检测是两种不同的视觉任务,将二者混合会带来一定的不协调性,论文基于这一点重新设计了适用于目标检测的 b a c k b o n e {\rm backbone} D e t N e t {\rm DetNet}
论文原文

在进行下面的部分之前,首先来看另一个致力于解决目标检测中使用预训练分类模型产生的问题, S c r a t c h D e t {\rm ScratchDet} 。同样地,论文首先列出目标检测使用预训练分类模型所带来的问题:分类和检测任务对目标的平移等变换的敏感度不同;预训练的分类模型受限于分类任务,在检测任务中不易有效地调整。 S c r a t c h {\rm Scratch} D e t {\rm Det} 基于 R e s N e t {\rm ResNet} 设计出用于检测的 R o o t {\rm Root} - R e s N e t {\rm ResNet} 网络,同时使用批量归一化( B N {\rm BN} )帮助网络更好地收敛。以下是几种经典网络的结构对比:
在这里插入图片描述

图2:几种经典网络的对比

R o o t {\rm Root} - R e s N e t {\rm ResNet} 特点有:(1)作者实验得出,在网络输入部分中的第一次卷积之后紧接使用下采样会降低模型检测小目标的能力。 R o o t {\rm Root} - R e s N e t {\rm ResNet} 首先在输入部分连续使用 3 3 个大小为 3 × 3 {\rm 3×3} 、步长为 1 {\rm 1} 的卷积以有效保留小目标信息;(2)文中以大量实验证明了 B N {\rm BN} b a c k b o n e {\rm backbone} 、回归网络、分类网络等部分均起到了积极的作用,合理地使用 B N {\rm BN} 可以显著提高模型性能。以下是目标检测模型的实验结果对比:
在这里插入图片描述

图3:ScratchDet的实验结果对比


0. Abstract

作者指出,众多目标检测模型都是通过微调预训练的分类模型得到,而少有人关注为检测任务设计特定的 b a c k b o n e {\rm backbone} 。并指出分类任务和检测任务的几点不同:许多检测模型涉及多尺度预测,这与分类任务有所不同(分类任务通常只有一个输出,直接输出分类结果;但也有 G o o L e N e t {\rm GooLeNet} 等使用辅助分类分支 );检测任务除了包含分类外, 还需要定位目标。在下采样倍数较大时,得到的感受野也较大,这有助于图像分类,但不利于目标检测(例如对于图像中的小目标,在下采样倍数较大时,模型倾向于提取图像的高级特征,特征图包含的小目标信息较弱,难以有效定位目标)。基于如上问题,作者提出了适用于目标检测任务的特征提取器, D e t N e t {\rm DetNet} 。实验结果为在目标检测和实例分割任务上,模型使用 D e t N e t {\rm DetNet} 替换经典的 C N N {\rm CNN} 特征提取模块后可以达到 S O T A {\rm SOTA}

扫描二维码关注公众号,回复: 11515688 查看本文章

论文贡献:(一)论文提出在检测任务中使用预训练模型的弊端;(二)提出一种适用于检测任务的 b a c k b o n e {\rm backbone} D e t N e t {\rm DetNet} ;(三)使用 D e t N e t {\rm DetNet} 的模型在目标检测和实例分割任务上达到 S O T A {\rm SOTA}


1. Introduction

首先,如摘要部分提到的,将预训练分类模型用于目标检测任务中会产生诸多问题。其次,作者提出的 D e t N e t {\rm DetNet} 的思路是:为了适应 F P N {\rm FPN} 等模型的多尺度检测, D e t N e t {\rm DetNet} 添加了额外的阶段;不同于预训练模型, D e t N e t {\rm DetNet} 在深层网络中依然保持特征图的分辨率,同时使用空洞结构解决计算量和参数量庞大的问题。 D e t N e t {\rm DetNet} 全程保持较大的分辨率,后文可以看到,特征图的最小分辨率为原图的 16 {\rm 16} 倍下采样。


2. Related Work

相关工作部分作者提到在检测任务中用于特征提取的模块通常来自于经典 C N N {\rm CNN} ,包括 V G G {\rm VGG} R e s N e t {\rm ResNet} X c e p t i o n {\rm Xception} D e n s e N e t {\rm DenseNet} D R N {\rm DRN} 等。而少有人关注为目标检测设计一个合适的特征提取网络, D e t N e t {\rm DetNet} 的提出思路源自于此。


3. DetNet: A Backbone network for Object Detection

3.1 Motivation

设计分类模型的思路不适用于检测任务中的定位目标,随着网络层次的加深,特征图而变得越来越小。如下图:在这里插入图片描述

图3:几种特征提取模块的对比

上图 A {\rm A} F P N {\rm FPN} ,图 B {\rm B} 是经典的用于图像分类的 b a c k b o n e {\rm backbone} ,图 C {\rm C} 是论文提出的 D e t N e t {\rm DetNet}

F P N {\rm FPN} 使用上采样保持特征图的分辨率,但这同样会存在以下问题:(1)在 F P N {\rm FPN} 中使用的下采样倍数通常大于分类模型所使用的,如上图中的 A {\rm A} B {\rm B} (如分类中使用 5 5 次下采样而 F P N {\rm FPN} 中使用了 7 7 次下采样,所以 F P N {\rm FPN} 中的 P 6 P_6 P 7 P_7 等阶段没有利用预训练模型信息);(2)在下采样倍数较大时, F P N {\rm FPN} 中可能仅获得了目标的模糊边界,难以精确定位;(3) F P N {\rm FPN} 使用上采样缓解了小目标检测的问题,但这同时会丢失上下文信息。针对以上问题,如图 C {\rm C} D e t N e t {\rm DetNet} 具有如下特点:(1)模型的阶段数适用于目标检测(随着目标检测任务的要求随时变动而不影响模型的性能);(2)随着下采样倍数的增加, D e t N e t {\rm DetNet} 仍保持特征图的分辨率,同时保证了较大的感受野。

3.2 DetNet Design

D e t N e t 59 {\rm DetNet59} 的前 4 4 个阶段同 R e s N e t 50 {\rm ResNet50} ,其设计思路如下:

  • 添加额外的阶段用于目标检测,同时固定特征图的分辨率为 16 16 倍下采样;
  • 4 4 个阶段后的部分通过 1 × 1 1×1 的空洞模块添加;
  • 使用空洞模块作为基本模块,用以增大感受野和减少参数量。如下图:
    在这里插入图片描述

图3:几种模块的对比

A {\rm A} B {\rm B} D e t N e t {\rm DetNet} 中基本模块。最后,给出 D e t N e t {\rm DetNet} 的整个结构:
在这里插入图片描述

图4:DetNet

由图可知, D e t N e t {\rm DetNet} 在第 4 4 个阶段后,每个阶段由图 3 3 中的 B + 2 A {\rm B+2A} 保持特征图的分辨率,每个阶段使用 1 × 1 1×1 卷积作为多尺度输出。可以看到, D e t N e t {\rm DetNet} 可以重复添加上述模块以满足目标检测中的多尺度预测,同时后面阶段的特征图保持了较大的分辨率,使用空洞模块可以获得较大的感受野和减少计算量和参数量。


4. Experiments

首先是同 R e s N e t {\rm ResNet} 的实验结果对比:在这里插入图片描述

图5:DetNet同ResNet的实验结果对比

其次是在不同尺度检测下, D e t N e t 59 {\rm DetNet59} R e s N e t 50 {\rm ResNet50} 的实验结果对比:在这里插入图片描述

图6:DetNet-59同ResNet-50的实验结果对比

然后是在目标检测任务上的实验结果对比:
在这里插入图片描述

图7:相关目标检测模型的实验结果对比

最后是在实例分割任务上的实验结果对比:
在这里插入图片描述

图8:相关实例分割模型的实验结果对比


5. Conclusion

论文提出使用 D e t N e t {\rm DetNet} 作为目标检测任务中的特征提取模块,实验结果为模型在不使用预训练分类模型的前提下,在目标检测和实例分割任务上达到 S O T A {\rm SOTA}

本文的思路同 S c r a t c h D e t {\rm ScratchDet} 类似,其目的都是为了解决在目标检测任务中使用预训练分类模型所产生的问题。论文通过有效使用空洞模块获得较大感受野,同时保持特征图的分辨率以获得精确的目标定位 。实验结果证明使用 D e t N e t {\rm DetNet} 作为特征提取模块可以提升模型的性能。论文提出的方法较为简单,作者通过大量实验证明 D e t N e t {\rm DetNet} 的有效性。


附录:PyTorch实现DetNet59

以下是本人用 P y T o r c h {\rm PyTorch} 实现的 D e t N e t 59 {\rm DetNet59} 网络结构(参考图 4 4 ),其中 R e s N e t 50 {\rm ResNet50} 参考如下:在这里插入图片描述

图9:ResNet

import torch
import torch.nn as nn
import torch.nn.functional as F


# Conv + BN + LeakyReLu
class CBR(nn.Module):
    def __init__(self,
                 in_channels,
                 out_channels,
                 kernel_size,
                 stride,
                 padding,
                 dilation=1,
                 bias=True):
        super(CBR, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, bias=bias)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.LeakyReLU(inplace=True)

    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        y = self.relu(x)
        return y


# 残差模块
class ResidualBlock(nn.Module):
    def __init__(self,
                 channels,
                 in_channels,
                 out_channels,
                 stride=2,
                 shortcut=None):
        super(ResidualBlock, self).__init__()
        self.left = []
        # 带下采样
        if shortcut is not None:
            self.left = nn.Sequential(
                CBR(channels, in_channels, 1, 1, 0),
                CBR(in_channels, in_channels, 3, stride, 1),
                CBR(in_channels, out_channels, 1, 1, 0)
            )
        # 不带下采样
        else:
            self.left = nn.Sequential(
                CBR(out_channels, in_channels, 1, 1, 0),
                CBR(in_channels, in_channels, 3, 1, 1),
                CBR(in_channels, out_channels, 1, 1, 0)
            )

        self.right = shortcut

    def forward(self, x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)
        out += residual
        return F.relu(out)


# BottleNeck模块
class BottleNeck(nn.Module):
    def __init__(self,
                 in_channels,
                 out_channels,
                 shortcut=True):
        super(BottleNeck, self).__init__()
        self.left = nn.Sequential(
            CBR(in_channels, out_channels, 1, 1, 0),
            CBR(out_channels, out_channels, 3, 1, 2, 2),
            CBR(out_channels, out_channels, 1, 1, 0)
        )
        # B模块
        if shortcut:
            self.right = CBR(in_channels, out_channels, 1, 1, 0)
        # A模块
        else:
            self.right = None

    def forward(self, x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)
        out += residual
        return F.relu(out)


# DetNet模块:B+2A
class Block(nn.Module):
    def __init__(self,
                 in_channels,
                 out_channels):
        super(Block, self).__init__()
        self.b1 = BottleNeck(in_channels, out_channels)
        self.b2 = BottleNeck(out_channels, out_channels, shortcut=False)
        self.b3 = BottleNeck(out_channels, out_channels, shortcut=False)

    def forward(self, x):
        x = self.b1(x)
        x = self.b2(x)
        y = self.b3(x)
        return y


# 构建DetNet主体
class MyDetNet(nn.Module):
    def __init__(self, num_classes=1000):
        super(MyDetNet, self).__init__()
        self.pre = nn.Sequential(
            CBR(3, 64, 7, 2, 3),
            nn.MaxPool2d(3, 2, 1, ceil_mode=False)
        )
        # 第一个模块没有使用下采样,ResNet50的前4个阶段
        self.layer1 = self.make_layer(64, 64, 256, 3, stride=1)
        self.layer2 = self.make_layer(256, 128, 512, 4)
        self.layer3 = self.make_layer(512, 256, 1024, 6)
        # self.layer4 = self.make_layer(1024, 512, 2048, 3)

        # DetNet的模块
        self.block1 = Block(1024, 256)
        self.block2 = Block(256, 256)

        # 1×1模块用于得到输出
        self.conv1 = CBR(1024, 256, 1, 1, 0)
        self.conv2 = CBR(256, 256, 1, 1, 0)
        self.conv3 = CBR(256, 256, 1, 1, 0)

    # 组合模块内的层
    @staticmethod
    def make_layer(channels, in_channels, out_channels, block_num, stride=2):
        # 近路连接,根据stride确定是否需要下采样
        shortcut = nn.Sequential(
            nn.Conv2d(channels, out_channels, 1, stride, bias=True),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        layers = [ResidualBlock(channels, in_channels, out_channels, stride, shortcut)]
        for i in range(1, block_num):
            layers.append(ResidualBlock(channels, in_channels, out_channels, stride=1))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.pre(x)  # 64×56×56
        x = self.layer1(x)  # 256×56×56
        x = self.layer2(x)  # 512×28×28
        x = self.layer3(x)  # 1024×14×14
        # x = self.layer4(x)  # 2048×7×7

        y1 = self.conv1(x)   # 第一个通道输出
        x = self.block1(x)
        y2 = self.conv2(x)   # 第二个通道输出
        x = self.block2(x)
        y3 = self.conv3(x)   # 第三个通道输出
        return [torch.cat([y1, y2], dim=1).shape,  # 512×14×14
                torch.cat([y2, y3], dim=1).shape,  # 512×14×14
                y3.shape]                          # 256×14×14

参考

  1. Li Z, Peng C, Yu G, et al. Detnet: Design backbone for object detection[C]//Proceedings of the European Conference on Computer Vision (ECCV). 2018: 334-350.
  2. Zhu R, Zhang S, Wang X, et al. ScratchDet: Training single-shot object detectors from scratch[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2019: 2268-2277.


猜你喜欢

转载自blog.csdn.net/Skies_/article/details/105772107