背景
国内的数据竞赛真的缺乏交流,还是喜欢kaggle的kernel和讨论区,真硬核!这里分享一下我总结的一些目标检测中会用到的“奇淫技巧”,牵扯到代码的我就直接拿 mmdetection[1]来举例了,修改起来比较简单。这些都是跟随郑烨、梁爽、元梵、杨胜、亚光、徐大哥等大佬一起学习的一部分思路,感谢各位大佬不嫌弃小弟,带我不断起飞,哈哈哈。
1.模型选择
近一年多以来目标检测领域没有太大的动静,即使最近一段时间的 Anchor Free 和神经网络搜索框架比较热,但都没有太大的革新,当前检测竞赛圈的通用配置还是 Cascade-R-CNN + ResNeXt/ResNet系列+FPN+DCN 2,毕竟二阶段为王,详情参考链接 cascade_rcnn_dconv_c3-c5_r50_fpn,根据自己的需要进行修改,如果没有修改源码的能力,那就只能用官方给出的配置了。然后就是一些根据实验结果的调整了,由于商汤没有开源 SeNet系列的训练模型(避免误会,这里指的是商汤未在mmd中开源,并没有senet的所属关系),所以如果有卡的话可以自己搞。
2.数据预处理
如果你 baseline 选的准,那么基本上已经领先一大部分人了,但是如果在数据预处理过程中没有搞好,那基本上就跟TOP系列无缘了,毕竟在数据处理上能够领先的大佬,后面炼丹的技术也绝对不差。这里分为以下几个部分聊一聊数据这方面的策略:
2.1 数据扩充
如果说待检测目标具有旋转不变性,那这里就可以对目标做上下翻转、左右反转、90°*3旋转等操作;如果目标中存在模糊的情况,在扩充的时候也可以适当做一些高斯模糊什么的;对于颜色抖动、锐度变化、随机缩放等这些操作,我实验的过程中也很难界定他们的效果,而且跟队友做相同实验时,所起的作用也不一样,总结来说,有的时候真的是随机上分。
更详细的附带代码的数据扩充请移步:目标检测系列二:数据增广
2.2 mixup
mixup的意思就是将两张图按照一定的比例混合在一起,详情移步论文:Bag of Freebies for Training Object Detection Neural Networks[4]。这里要说的是如何mixup,如何选择mixup的对象。
在工业类缺陷检测或者违禁物品检测中,常常会给出一些不含有待检测目标的正常图像,可以将含有目标的图像和随机选取
的正常图像进行mixup(随机意味着更多的组合~),这样数据量又上来了。还有一种是徐大哥用的比较骚的操作,就是跟coco的数据集进行mixup,真是服气。。。
2.3 填鸭式
这个肯定别人也这么叫过,但是我们队一开始想到的时候,就这么称呼了。所谓填鸭式,就是将一些目标(也可以是误捡的)扣出来,放到没有目标的图上去,增加图像的鲁棒性。比如我们在钢筋识别的时候,有一些小石子和吊机容易被误判成钢筋,索性就选了一些图,把这些伪目标填充合理的位置上,效果就是没有再误判过了。
在津南2违禁物品的检测中,我们一开始也测试了mixup的策略,但是效果一般,后来改用“填鸭式”处理,效果要好很多,应该说前排基本都用了这个方法吧(猜测)。这里有一个要注意的就是“随机”,要是能够让目标在正常图像中的随机位置填充并且随机旋转和缩放,那就完美了,无奈这个没有实现好,如果有大佬实现了,求指导。。。。
2.4 裁剪
根据具体任务,可以将待检测目标进行裁剪。在 DF新的交通标志物的识别竞赛中,图像尺寸较大,3200x1800,但是目标特别小,最大也在200x200左右,这就导致训练尺寸必须要不低于原始图像尺寸,很依赖算力。后来在跟徐大哥、郑烨讨论后,决定在目标周围裁剪出一个尺寸,然后进行训练,效果不错,且训练周期明显缩小。这个策略前排基本都在用,也没什么可隐藏的了。
想到其他的了,再更新。
3.预训练模型
数据处理完以后,基本上就是要冲击前排了,这里就是要考虑如何选用预训练模型了,一般的检测都是使用ImageNet预训练的backbone,这是基本配置,高级一点的就是针对数据集做一次预训练,比如津南2的违禁物品检测,可以将所有目标裁剪出来,然后训练一个不错的分类模型,这样的初始化相比ImageNet就要好太多了。。。
再一点就是使用coco预训练的完整检测模型权重,这样的效果就是模型收敛速度贼快,而且效果一般都比较好,也是大家最常用的方法,这里给出 mmdetection 修改coco预训练权重类别数的脚本:
# for cascade rcnn
import torch
num_classes = 21
model_coco = torch.load("cascade_rcnn_x101_32x4d_fpn_2x_20181218-28f73c4c.pth")
# weight
model_coco["state_dict"]["bbox_head.0.fc_cls.weight"].resize_(num_classes,1024)
model_coco["state_dict"]["bbox_head.1.fc_cls.weight"].resize_(num_classes,1024)
model_coco["state_dict"]["bbox_head.2.fc_cls.weight"].resize_(num_classes,1024)
# bias
model_coco["state_dict"]["bbox_head.0.fc_cls.bias"].resize_(num_classes)
model_coco["state_dict"]["bbox_head.1.fc_cls.bias"].resize_(num_classes)
model_coco["state_dict"]["bbox_head.2.fc_cls.bias"].resize_(num_classes)
#save new model
torch.save(model_coco,"coco_pretrained_weights_classes_%d.pth"%num_classes)
这里再说一下backbone的选择,因为要考虑到要使用coco预训练权重的原因,暂时采用的都是官方给出的模型以及对应的backbone。我自己也在mmdetection中实现了SeNet系列的backbone,无奈没卡,没法训一个coco出来。。。。
4. 训练技巧
深度炼丹名不虚传。。。。。
4.1 warmup lr
翻译一下就是对学习率进行预热,最开始是在 ResNet 的论文中提到的一种方法,原始是先在前几个epoch或iter或目标达到一个水准之前以小于预设值得lr进行训练,然后再恢复lr到初始值。后来Facebook提出了改良版本,详情请移步论文: Gradual warmup[5],这也是当前检测和分割中必不可少的环节,mmdetection中默认是启用了的:
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=1.0 / 3,
step=[8, 11])
4.2 lr 如何计算
学习率的初始化设置一直是一个比较头疼的问题,有的时候需要经常实验才能得到一个比较好的值,我们在检测任务中常用的计算方法是:lr = 0.02 / 8 x num_gpus x img_per_gpu / 2,一般情况都是这么计算后设置。
4.3 多尺度训练
检测的两大任务:分类和定位,定位需要模型能够适应变化频繁的尺度特征,但是这恰恰是卷积神经网络所不具备的,目前在网络模型上通过 FPN 结构可以缓解一部分,另外就是多尺度训练,在不同的iter下选择不同的尺度进行训练,注意尺寸最好时能够被32整除,不过mmdetection会检查这一点,帮你pad一下。
4.4 损失函数
4.4.1 Focal Loss
这是CV中根据实验结果调整损失函数最先考虑的一个,论文: Focal Loss for Dense Object Detection,主要是针对模型拟合困难的样例或者样本不均衡的样例,在图像分类中常用作最终的损失函数,直接进行优化,而在目标检测中却有两个选择,一个是在 RPN 层使用 FocalLoss,这样可以缓解由于目标占比较少导致生成的 anchor 正负样本比例失衡;另一种就是类似图像分类一样,在bbox_head中使用,mmdetection中的相应配置(如果要正确使用,需要做点改动,自行修改源码吧,不难):
#1. rpn 处更改
rpn_head=dict(
type='RPNHead',
in_channels=256,
feat_channels=256,
anchor_scales=[8],
anchor_ratios=[0.5, 1.0, 2.0],
anchor_strides=[4, 8, 16, 32, 64],
target_means=[.0, .0, .0, .0],
target_stds=[1.0, 1.0, 1.0, 1.0],
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0))
#2. bbox_head 处更改
bbox_head=dict(
type='SharedFCBBoxHead',
num_fcs=2,
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=81,
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2],
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', #在此处替换
use_sigmoid=False,
loss_weight=1.0),
loss_bbox=dict(
type='SmoothL1Loss', beta=1.0, loss_weight=1.0)))
4.4.2 GIou Loss
Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression,之前旷世提出了 Iou Loss 收敛性较差,GIOU的详细介绍,我之前一篇博客介绍了,这里不再赘述,详情移步:Generalized Intersection over Union,这里只提一下,这种损失函数对增加框的回归效果比较有效,如果你的任务要求 IOU > 0.8或者更高,这个可以优先考虑。
4.4.3 其他损失函数
针对分类的损失函数可以试试如 GHM-C Loss,针对回归的损失函数可以试试如 GHM-R Loss。
4.5 OHEM
OHEM(online hard example mining),翻译过来就是在线难例挖掘,就是对所有的 ROI 的损失进行评估,选择损失较大的来优化网络,详情移步:OHEM论文解读
4.6 待更。。。
想到其他的了,再更新。。。
5. infer技巧
5.1 TTA
检测任务中使用比较多的就是多尺度预测,这个时间开销有点高,但是效果也是不错的。另外一个就是CV中最常用的TTA了(Test Time Augmentation),如果在训练的过程中加入了旋转和翻转,那么前向过程中也需要加上,即使训练没用的话,加上翻转也会有提升。
5.2 Soft-NMS
Soft-NMS 改进了之前比较暴力的 NMS,当IOU超过某个阈值后,不再直接删除该框,而是降低它的置信度(得分),如果得分低到一个阈值,就会被排除;但是如果降低后仍然较高,就会被保留。实现细节移步:NMS与soft NMS
在 mmdetection 中的设置如下:
test_cfg = dict(
rpn=dict(
nms_across_levels=False,
nms_pre=1000,
nms_post=1000,
max_num=1000,
nms_thr=0.7,
min_bbox_size=0),
rcnn=dict(
score_thr=0.05, nms=dict(type='soft_nms', iou_thr=0.5), max_per_img=100),
keep_all_stages=False)
TODO
- [ ] 其他未知
参考文献
[1] : mmdetection
[2] : Cascade R-CNN: Delving into High Quality Object Detection
[3] : Deformable ConvNets v2: More Deformable, Better Results
[4] : Bag of Freebies for Training Object Detection Neural Networks
[5] : Gradual warmup
感谢您的分享,请问一下有没有mmdetection实现的mixup代码参考一下呢
在 mmdet/datasets/extra_aug.py中自己添加吧
博主,您好!请问,您那份包含GIOU损失函数的mmdetection 代码,什么时候公布出来? 求一份
后排的小尾巴来汲取经验,受益匪浅。
请问博主没卡是怎么使用mmdetection训练模型做比赛的?
emmm...一般没卡就是说卡太少,跑步起来
跑不起来
请问该公式lr = 0.02 / 8 x num_gpus x img_per_gpu / 2的计算顺序是什么?
正常的乘除运算,从左往右
想问下您,在mmdetection中,怎么正确修改cascade rcnn dcn 101中的focal loss,我们修改了rcnn三层的box head,但是训练结果并不对,不知道原因,您能贴下您的修改么,可以的话贴下修改后的源码,十分感谢。
请问lr = 0.02 / 8 x num_gpus x img_per_gpu / 2 学习率这样计算的根据是什么呢,有什么资料吗?
这是根据detectron的设置来的,你看看detectron的配置文件。
请问一下你们这个题目的代码开源了么?主要想看一下你们是怎么基于mmdetection改代码的
队伍内部使用,暂时没有开源的计划
这里有两个问题
1.对于预训练模型修改类别的时候,你贴出来的代码和我在配置文件里面修改num_classes有什么区别
2.如果使用focol loss的话,需要将rpn-head和bbox-head的cls_loss改为focal loss这么改对吧?
博主,您好!请问,您那份包含GIOU损失函数的mmdetection 代码,什么时候公布出来? 求一份
参考这里吧:https://github.com/yhcao6/mmdetection/blob/69ce11fac827391bfcc7f8209a8b9ad309a8c478/mmdet/core/loss/losses.py#L166
谢谢博主!
请问一下博主,有没有基于mmdetection的目标检测任务做数据增强的可以参考的代码呢?
你说的是在线增强吧,可以参考mmdetection中SSD中的增强方式。
为什么不用one stage的方法呢?比如SSD?
单阶段的优势是速度,精度和两阶段相比还是差很多,论文中给出的超过两阶段的mAP,看看就行了。
博主,在mmdetection中添加giou loss 如何操作啊
mmd已经更新了,参考相关代码就可以
谢谢博主的工作,请问图片过大,”目标周围裁剪出一个尺寸“,这个是怎样实现的呢?
在训练过程中有包含目标的标签,根据标签进行裁剪。
谢谢博主的回答,但还是有几个小疑问:1、如果是对原图裁剪的,那样是要重新修改原bbox值,但对于多标签的图片怎样处理?2、是在训练过程中裁剪,博主这个意思不太理解?
你好,请问一下,为什么在预训练模型这一部分,不使用pretrained加载coco训练好的模型+改类别个数,而是改类别并且保存模型呢?
请问下mmdetection 怎么添加多尺度训练
配置文件中将(1333,800)改成[(1333,800),(1333,900)]即可
你好,请问在裁剪中要裁剪的比目标大多少呢
这个自己根据任务实验调整一下吧,没有固定的值
请问可以分享一下mmdet的coco预训练模型嘛,官网已经没了
官网有,在docs/MODEL_ZOO.md里
博主您好,我注意到mmdtection里面多尺度训练的范围只能限制在两个以内,但是多尺度测试的范围可以有很多个,请问多尺度测试是什么意思您知道吗
改一下代码就行,debug到出错到位置,或者看htc的多尺度方式,多尺度测试就是测试的时候给多个尺度,和训练的时候给的一样
不好意思,就是2.3 填鸭式 有没有啥相关的论文或者是代码
您好,请问您有代码实现了吗?
博主好 ,填鸭式有实现代码吗
看到了第一句话感慨万千:自己之前也是这样想的,不过没办法,只能自己努力,虽然弯路很多,等我成为小佬一定要改变这种现状.
想请问一下博主,填鸭式这个方法粘贴上去的话如何考虑图像上下文信息和结构化噪声呢?这个如果直接随机放上去的话不就类似于复制粘贴了吗
请问博主cascade_rcnn要怎么使用MixUp增强呢?就现在的2.18版本有没有代码可以参考一下?
https://github.com/open-mmlab/mmdetection/issues/6673
这是我在官方下面的提问,但是还没有得到解决。。。