8

目标检测小网络

 3 years ago
source link: https://flashgene.com/archives/165796.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

目录

目标检测小网络

一. Anchor-based

1.1 网络结构

当前一般采用

backbone:shufflenetV2、mobilenetV2/3(V3比较难调试)、Ghostnet、SandGlass

FPN:PAN、BiFPN,这部分对检测提升很大(实际使用得进行裁剪压缩,原始版本不适合小模型),追求极致性能的网络可以去除(比如人脸检测超小模型,这类目标特征明显且背景不是很复杂的情况)

Head:RFB、SSH,如果不加FPN结构,Head必须做一番手脚,如果存在FPN,简单使用几个卷积即可

输出:(这部分一般包含在Head内部,最近遇到这个问题单独提出来),可以使用cls+reg共享一个卷积,也可以单独卷积(每层之间也可以共享或单独),小模型一般每层不共享(共享效果差,有朋友已实测),cls和reg之间随意选择(共享最好做一些trick,最好增加FPN,不共享可以增加RFB等操作)

小模块一般采用:

模块卷积一般使用Depthwise结构,小卷积一般使用 \(1\times1\)

卷积(很少使用 \(3\times3\)

卷积)

backbone的输出通道尽可能的大(笔者尝试 \(chennels=[24, 48, 96]\)

的输出效果很差),最基础的保证backbone的输出大于FPN的输出

小模型一般在 \(stride=[8,16,32,64]\)

进行采样, \(stride=4\)

没见过有人在小模型上使用

关于BatchNormalize操作,小网络一般使用BN即可,大网络使用GN效果较好(也可以直接使用torch自带的sync-BN)

loss的使用:

正负样本采用 \(3:1\)

,loss直接采用SSD,刚开始可以使用此方法,交叉熵的loss一般在1.0以下(具体多少适合你的网络看结果),如果在0.9附近无法下降,实测效果较差

Focal-Loss、GHM、TopK,笔者测试在单目标检测中使用较好,误检很少但难以训练

目前使用GIOU、DIOU作为回归较多

FocalLoss+GIOU实测效果较好

1.2 数据和anchor

使用mmdetection进行训练,一定要注意目标的大小,匹配过小(未匹配等)会导致出现Nan出现(查找问题比较困难,之前还以为LR等问题)

数据增强不是越多越好,比如人脸检测不需要上下Flip、人脸检测不需要遮挡问题(情况极少),数据增强过多会导致小模型不收敛,建议先进行基础操作(color-transform + random-crop + resize),之后根据提升进行特定数据增强。

数据和网络尽量保持等比例,笔者数据 \(1280\times720\)

,网络输入 \(256\times256\)

,padding之后再进行输入明显效果好

小目标过多,为了不丢弃数据,在增强策略先放大然后进行crop,或者直接crop目标区域(尽量不让目标缩小再进网络),笔者测试效果较好

anchor在每一层上一定大于stride(原始的RetinaNet中anchor比stride大4倍),小模型一般超过 \(\times1.25\)

以上,大模型 \(\times2\)

以上

之前有朋友建议两层之间的anchor大小有交集(开源项目没见过这种操作),实际测试提高了召回率同时也增加了误检,看个人取舍

关于感受野和anchor的关系,这部分前两年说的较多,现在论文基本没见过了

1.3 一系列问题

loss出现Nan

首先排查数据问题,一般是未匹配(目标太小,超出边界等)

尽量使用预训练模型(如果没有,先训练小批量数据得到预训练模型,之后再大数据上再训练)

减少学习率和batchsize等操作

小目标检测较差

查看anchor最小检测的尺寸(当正样本IOU=0.5,意味着最小检测尺寸是最小anchor的一般半,但实际上我们粗略认为最小目标等于最小anchor,因为考虑到anchor和目标匹配的问题(比如两个anchor之间存在一个目标))

增大stride,上面我们说到小模型一般stride不小于8,过小的stride对网络来说负担很大(只能增大网络获得trade-off)

多尺度训练,在实际的输入上下浮动即可(笔者将当前概率设为0.6,上下两个尺度设为0.2),实现方式两种:mmdet里面直接padding,yolov4直接resize操作,笔者仅使用了后者

mosic数据增强

网络结构,比如SSH针对小人脸检测较好

anchor匹配的时候给予小目标更大权重(笔者猜测,未尝试)

误检较多

语义无关的情况,可以扣mask打背景(比如杯子、苹果等检测),当然人脸检测肯定不行

增加样本,针对性的训练

多尺度训练,参见上文,实测有效

改变loss,针对困难样本的训练

1.4 具体案例

MobilenetV3+SSD+FPN

libface、utralface(实测对柔性检测较差,对人脸检测很好)

Yolov3/4/5剪枝

efficientnet缩小版

这里以 目前最流行的人脸检测器之一

为例:

Model model file size(MB) libfacedetection v1(caffe) 2.58 libfacedetection v2(caffe) 3.34 Official Retinaface-Mobilenet-0.25 (Mxnet) 1.68 version-slim 1.04 version-RFB 1.11 Model Easy Set Medium Set Hard Set libfacedetection v1(caffe) 0.65 0.5 0.233 libfacedetection v2(caffe) 0.714 0.585 0.306 Retinaface-Mobilenet-0.25 (Mxnet) 0.745 0.553 0.232 version-slim 0.77 0.671 0.395 version-RFB 0.787 0.698 0.438

从上面两个官方给出的表格,速度快的同时精度也高

backbone使用shufflenet改良版本,其中增加RFB用于扩大感受野

中间层使用SSD的multi-feature

head使用层不共享且cls与reg之间也不共享的卷DW卷积

feature在 \(stride=[8,16,32,64]\)

上进行检测,anchor设置 \([[10, 16, 24], [32, 48], [64, 96], [128, 192, 256]]\)

,由于人脸基本呈现方形,所以这里anchor都是 \(1:1\)

loss直接使用SSD策略,交叉熵+smoothL1+正负样本按比例挖掘

数据增强使用正常操作(color+random-crop等),未使用最新的mosic等

train策略包含很多种,一般采用SGD+Warmup+Cosin

二. Anchor-free

2.1 具体案例

注释: anchor-free之前流行的都是大网络,比如centerNet、FCOS、ATSS等,因为小网络很难训练centerness(网络有人测试),基本没见到小网络效果好的版本。自从 GFL-V1

论文出来以后,使得loss训练较为容易,然后NanoDet问世了。

这里直接以 目前最流程的anchor-free小网络

为例

Model Resolution COCO mAP Latency(ARM 4xCore) FLOPS Params Model Size(ncnn bin) NanoDet-m 320*320 20.6 10.23ms 0.72B 0.95M 1.8mb NanoDet-m 416*416 21.7 16.44ms 1.2B 0.95M 1.8mb YoloV3-Tiny 416*416 16.6 37.6ms 5.62B 8.86M 33.7mb YoloV4-Tiny 416*416 21.7 32.81ms 6.96B 6.06M 23.0mb

backbone使用shuffenetV2(原封不动)

FPN使用PAN简化版,直接双线性插值之后add即可

head层之间不共享,cls与reg之间进行共享卷积

loss使用GFL-V1版本

数据增强常规操作(未使用random-crop,未使用mosaic)

train直接使用SGD+Warmup+LinerStep

2.2 改进策略

数据增强

原版NanoDet仅使用简单操作,针对自己数据集进行特定数据增强,实测效果很大

训练多尺度

有效改善误检和漏检(建议使用预训练模型,直接使用容易崩)

最近 GFL-V2

出来了,尝试将trick加入Nanodet之中

笔者稍微修改了一下trick,目的两点:1)尽量不修改Nanodet结构。2)更容易训练。3)效果尽可能好。

直接使用GFL-V2很难训练,而且误检率较高,NanoDet作者给出的原因是共享的cls+reg对分布不敏感,不适用共享的卷积效果可以提升。笔者猜测原因:1)共享卷积已经学到部分分布信息。2)小网络最后阶段直接使用分布去指导质量分数很难收敛。

未加入GFL-V2,实测效果未提升反而下降

# 层定义
self.reg_conf = nn.ModuleList([nn.Sequential(nn.Conv2d(4 * 8, 32, 1),
                                                     nn.LeakyReLU(negative_slope=0.1, inplace=True),
                                                     nn.Conv2d(32, 1, 1), nn.Sigmoid()) for _ in self.anchor_strides])
self.reg_cls_com = nn.ModuleList([nn.Conv2d(2, 1, 1) for _ in self.anchor_strides]) 
# 前向传播
def forward_single(self, x, cls_convs, reg_convs, gfl_cls, gfl_reg, reg_conf, reg_cls_com):
        cls_feat = x
        reg_feat = x
        for cls_conv in cls_convs:
            cls_feat = cls_conv(cls_feat)
        for reg_conv in reg_convs:
            reg_feat = reg_conv(reg_feat)
        if self.share_cls_reg:
            feat = gfl_cls(cls_feat)
            cls_score, bbox_pred = torch.split(feat, [self.cls_out_channels, 4 * (self.reg_max + 1)], dim=1)
        else:
            cls_score = gfl_cls(cls_feat)
            bbox_pred = gfl_reg(reg_feat)
        B,C,H,W = bbox_pred.shape
        prob = F.softmax(bbox_pred.reshape(B, 4, self.reg_max+1, H*W), dim=2)
        quality_score = reg_conf(prob.reshape(B, C, H, W))
        cls_score = cls_score * quality_score
        
        #cls_score = torch.cat([cls_score, quality_score],dim=1)
        #cls_score = reg_cls_com(cls_score)
        if torch.onnx.is_in_onnx_export():
            cls_score = torch.sigmoid(cls_score).reshape(1, self.num_classes, -1).permute(0, 2, 1)
            bbox_pred = bbox_pred.reshape(1, (self.reg_max+1)*4, -1).permute(0, 2, 1)
        return cls_score, bbox_pred

改变网络结构

[ ] backbone优化

[ ] FPN优化(正在进行中)

[x] head优化(实测有效)

当然都是建立在尽量减少计算量的基础上进行优化,做到trade-off


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK