Kaggle实战目标检测奇淫技巧合集
加入极市专业CV交流群,与6000+来自腾讯,华为,百度,北大,清华,中科院等名企名校视觉开发者互动交流!更有机会与李开复老师等大牛群内互动!
同时提供每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流。点击文末“阅读原文”立刻申请入群~
本文授权转载自 我爱计算机视觉
本文来自52CV群友Spytensor参加Kaggle目标检测比赛的总结,作者是位数据竞赛爱好者,文章非常具有实战意义。欢迎收藏~
原文链接:
http://spytensor.com/index.php/archives/53/
背景
国内的数据竞赛真的缺乏交流,还是喜欢 kaggle 的 kernel 和讨论区,真硬核!这里分享一下我总结的一些目标检测中会用到的 “奇淫技巧”,牵扯到代码的我就直接拿 mmdetection[1] 来举例了,修改起来比较简单。
1. 模型选择
近一年多以来目标检测领域没有太大的动静,即使最近一段时间的 Anchor Free 和神经网络搜索框架比较热,但都没有太大的革新,当前检测竞赛圈的通用配置还是 Cascade-R-CNN + ResNeXt/ResNet 系列 + FPN+DCN 2,毕竟二阶段为王!然后就是一些根据实验结果的调整了,由于商汤没有开源 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 的策略,但是效果一般,后来改用 “填鸭式” 处理,效果要好很多,应该说前排基本都用了这个方法吧(猜测)。这里有一个要注意的就是“随机”,要是能够让目标在正常图像中的随机位置填充并且随机旋转和缩放,那就完美了,无奈这个没有实现好,如果有大佬实现了,求指导。。。。
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 的损失进行评估,选择损失较大的来优化网络。
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)
参考文献
[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
*延伸阅读
点击左下角“阅读原文”,即可申请加入极市目标跟踪、目标检测、工业检测、人脸方向、视觉竞赛等技术交流群,更有每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流,一起来让思想之光照的更远吧~
△长按关注极市平台
觉得有用麻烦给个在看啦~