当前一般采用
小模块一般采用:
loss的使用:
使用mmdetection进行训练,一定要注意目标的大小,匹配过小(未匹配等)会导致出现Nan出现(查找问题比较困难,之前还以为LR等问题)
数据增强不是越多越好,比如人脸检测不需要上下Flip、人脸检测不需要遮挡问题(情况极少),数据增强过多会导致小模型不收敛,建议先进行基础操作(color-transform + random-crop + resize),之后根据提升进行特定数据增强。
数据和网络尽量保持等比例,笔者数据 1280 × 720 1280\times720 1280×720 ,网络输入 256 × 256 256\times256 256×256 ,padding之后再进行输入明显效果好
小目标过多,为了不丢弃数据,在增强策略先放大然后进行crop,或者直接crop目标区域(尽量不让目标缩小再进网络),笔者测试效果较好
anchor在每一层上一定大于stride(原始的RetinaNet中anchor比stride大4倍),小模型一般超过 × 1.25 \times1.25 ×1.25 以上,大模型 × 2 \times2 ×2 以上
之前有朋友建议两层之间的anchor大小有交集(开源项目没见过这种操作),实际测试提高了召回率同时也增加了误检,看个人取舍
关于感受野和anchor的关系,这部分前两年说的较多,现在论文基本没见过了
这里以目前最流行的人脸检测器之一为例:
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 |
从上面两个官方给出的表格,速度快的同时精度也高
注释: 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 |
原版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优化(GhostNet、如果使用mobilenetV2可以增加CSPNet进行通道压缩)
- FPN优化(DUpsample进行上采样)
- head优化(cls和reg不共享卷积,如果使用共享卷积可以在最后增加1*1卷积进行优化)
- [x]