1.数据增强

可参考:

初识CV:MMDetection中文文档—4.技术细节

数据增强是增加深度模型鲁棒性和泛化性能的常用手段,随机翻转、随机裁剪、添加噪声等也被引入到检测任务的训练中来,个人认为数据(监督信息)的适时传入可能是更有潜力的方向。

个人观点:

问题:为什么图像和Bbox需要进行数据增强呢?

答:因为数据多了就可以尽可能多的学习到图像中的不变性,学习到的不变性越多那么模型的泛化能力越强。

但是输入到CNN中的图像为什么不具有平移不变性?如何去解决?下面链接有专门的解析:

初识CV:输入到CNN中的图像为什么不具有平移不变性?

MMDetection中,数据增强包括两部分:(源码解析)

  1. 图像增强。
  • 源码在mmdet/datasets/extra_aug.py里面,包括RandomCrop、brightness、contrast、saturation、ExtraAugmentation等等图像增强方法。
  • 添加位置是train_pipeline或test_pipeline这个地方(一般train进行增强而test不需要),例如数据增强RandomFlip,flip_ratio代表随机翻转的概率:

    train_pipeline = [dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
    ]
    test_pipeline = [dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1333, 800),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img']),])
    ]

    2. Bbox增强。

  • 源码在mmdet/datasets/custom.py里面,增强源码为:

    def pre_pipeline(self, results):results['img_prefix'] = self.img_prefixresults['seg_prefix'] = self.seg_prefixresults['proposal_file'] = self.proposal_fileresults['bbox_fields'] = []results['mask_fields'] = []

2.Multi-scale Training/Testing 多尺度训练/测试

可参考:

初识CV:MMDetection中文文档—4.技术细节

输入图片的尺寸对检测模型的性能影响相当明显,事实上,多尺度是提升精度最明显的技巧之一。在基础网络部分常常会生成比原图小数十倍的特征图,导致小物体的特征描述不容易被检测网络捕捉。通过输入更大、更多尺寸的图片进行训练,能够在一定程度上提高检测模型对物体大小的鲁棒性,仅在测试阶段引入多尺度,也可享受大尺寸和多尺寸带来的增益。

multi-scale training/testing最早见于[1],训练时,预先定义几个固定的尺度,每个epoch随机选择一个尺度进行训练。测试时,生成几个不同尺度的feature map,对每个Region Proposal,在不同的feature map上也有不同的尺度,我们选择最接近某一固定尺寸(即检测头部的输入尺寸)的Region Proposal作为后续的输入。在[2]中,选择单一尺度的方式被Maxout(element-wise max,逐元素取最大)取代:随机选两个相邻尺度,经过Pooling后使用Maxout进行合并,如下图所示。

使用Maxout合并feature vector

近期的工作如FPN等已经尝试在不同尺度的特征图上进行检测,但多尺度训练/测试仍作为一种提升性能的有效技巧被应用在MS COCO等比赛中。

MMDetection中,多尺度训练/测试:(源码解析)

只需要修改train_pipeline 和test_pipeline中的img_scale部分即可(换成[(), ()])。带来的影响是:train达到拟合的时间增加、test的时间增加,一旦test的时间增加一定会影响比赛的分数,因为比赛都会将测试的时间作为评分标准之一:

train_pipeline = [dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), #这里可以更换多尺度[(),()]dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1333, 800),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img']),])
]

3.Global Context 全局语境

这一技巧在ResNet的工作[3]中提出,做法是把整张图片作为一个RoI,对其进行RoI Pooling并将得到的feature vector拼接于每个RoI的feature vector上,作为一种辅助信息传入之后的R-CNN子网络。目前,也有把相邻尺度上的RoI互相作为context共同传入的做法。

这一部分暂时没有代码解析。

4.Box Refinement/Voting 预测框微调/投票法/模型融合

微调法投票法由工作[4]提出,前者也被称为Iterative Localization。

微调法最初是在SS算法得到的Region Proposal基础上用检测头部进行多次迭代得到一系列box,在ResNet的工作中,作者将输入R-CNN子网络的Region Proposal和R-CNN子网络得到的预测框共同进行NMS(见下面小节)后处理,最后,把跟NMS筛选所得预测框的IoU超过一定阈值的预测框进行按其分数加权的平均,得到最后的预测结果。

投票法可以理解为以顶尖筛选出一流,再用一流的结果进行加权投票决策。

不同的训练策略,不同的 epoch 预测的结果,使用 NMS 来融合,或者soft_nms

需要调整的参数:

  • box voting 的阈值。
  • 不同的输入中这个框至少出现了几次来允许它输出。
  • 得分的阈值,一个目标框的得分低于这个阈值的时候,就删掉这个目标框。

模型融合主要分为两种情况:

  1. 单个模型的不同epoch进行融合

这里主要是在nms之前,对于不同模型预测出来的结果,根据score来排序再做nms操作。

2. 多个模型的融合

这里是指不同的方法,比如说faster rcnn与retinanet的融合,可以有两种情况:

a) 取并集,防止漏检。

b) 取交集,防止误检,提高精度。

5.随机权值平均(Stochastic Weight Averaging,SWA)

随机权值平均只需快速集合集成的一小部分算力,就可以接近其表现。SWA 可以用在任意架构和数据集上,都会有不错的表现。根据论文中的实验,SWA 可以得到我之前提到过的更宽的极小值。在经典认知下,SWA 不算集成,因为在训练的最终阶段你只得到一个模型,但它的表现超过了快照集成,接近 FGE(多个模型取平均)。

左图:W1、W2、W3分别代表3个独立训练的网络,Wswa为其平均值。中图:WSWA 在测试集上的表现超越了SGD。右图:WSWA 在训练时的损失比SGD要高。

结合 WSWA 在测试集上优于 SGD 的表现,这意味着尽管 WSWA 训练时的损失较高,它的泛化性更好。

SWA 的直觉来自以下由经验得到的观察:每个学习率周期得到的局部极小值倾向于堆积在损失平面的低损失值区域的边缘(上图左侧的图形中,褐色区域误差较低,点W1、W2、3分别表示3个独立训练的网络,位于褐色区域的边缘)。对这些点取平均值,可能得到一个宽阔的泛化解,其损失更低(上图左侧图形中的 WSWA)。

下面是 SWA 的工作原理。它只保存两个模型,而不是许多模型的集成:

  1. 第一个模型保存模型权值的平均值(WSWA)。在训练结束后,它将是用于预测的最终模型。
  2. 第二个模型(W)将穿过权值空间,基于周期性学习率规划探索权重空间。

SWA权重更新公式

在每个学习率周期的末尾,第二个模型的当前权重将用来更新第一个模型的权重(公式如上)。因此,在训练阶段,只需训练一个模型,并在内存中储存两个模型。预测时只需要平均模型,基于其进行预测将比之前描述的集成快很多,因为在那种集成中,你需要使用多个模型进行预测,最后再进行平均。

方法实现:

论文的作者自己提供了一份 PyTorch 的实现 :

timgaripov/swa​github.com

此外,基于 fast.ai 库的 SWA 可见 :

Add Stochastic Weight Averaging by wdhorton · Pull Request #276 · fastai/fastai

6.OHEM 在线难例挖掘

OHEM(Online Hard negative Example Mining,在线难例挖掘)见于[5]。两阶段检测模型中,提出的RoI Proposal在输入R-CNN子网络前,我们有机会对正负样本(背景类和前景类)的比例进行调整。通常,背景类的RoI Proposal个数要远远多于前景类,Fast R-CNN的处理方式是随机对两种样本进行上采样和下采样,以使每一batch的正负样本比例保持在1:3,这一做法缓解了类别比例不均衡的问题,是两阶段方法相比单阶段方法具有优势的地方,也被后来的大多数工作沿用。

论文中把OHEM应用在Fast R-CNN,是因为Fast R-CNN相当于目标检测各大框架的母体,很多框架都是它的变形,所以作者在Fast R-CNN上应用很有说明性。

Fast R-CNN框架

上图是Fast R-CNN框架,简单的说,Fast R-CNN框架是将224×224的图片当作输入,经过conv,pooling等操作输出feature map,通过selective search 创建2000个region proposal,将其一起输入ROI pooling层,接上全连接层与两个损失层。

OHEM图解,应用于Fast R-CNN

作者将OHEM应用在Fast R-CNN的网络结构,如上图,这里包含两个RoI network,上面一个RoI network是只读的,为所有的RoI 在前向传递的时候分配空间,下面一个RoI network则同时为前向和后向分配空间。在OHEM的工作中,作者提出用R-CNN子网络对RoI Proposal预测的分数来决定每个batch选用的样本。这样,输入R-CNN子网络的RoI Proposal总为其表现不好的样本,提高了监督学习的效率。

首先,RoI 经过RoI plooling层生成feature map,然后进入只读的RoI network得到所有RoI 的loss;然后是hard RoI sampler结构根据损失排序选出hard example,并把这些hard example作为下面那个RoI network的输入。

实际训练的时候,每个mini-batch包含N个图像,共|R|个RoI ,也就是每张图像包含|R|/N个RoI 。经过hard RoI sampler筛选后得到B个hard example。作者在文中采用N=2,|R|=4000,B=128。 另外关于正负样本的选择:当一个RoI 和一个ground truth的IoU大于0.5,则为正样本;当一个RoI 和所有ground truth的IoU的最大值小于0.5时为负样本。

总结来说,对于给定图像,经过selective search RoIs,同样计算出卷积特征图。但是在绿色部分的(a)中,一个只读的RoI网络对特征图和所有RoI进行前向传播,然后Hard RoI module利用这些RoI的loss选择B个样本。在红色部分(b)中,这些选择出的样本(hard examples)进入RoI网络,进一步进行前向和后向传播。

MMDetection中,OHEM(online hard example mining):(源码解析)

rcnn=[dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.4, # 更换neg_iou_thr=0.4,min_pos_iou=0.4,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler', # 解决难易样本,也解决了正负样本比例问题。num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.6,neg_iou_thr=0.6,min_pos_iou=0.6,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False)],stage_loss_weights=[1, 0.5, 0.25])

7.Soft NMS 软化非极大抑制

NMS后处理图示

NMS(Non-Maximum Suppression,非极大抑制)是检测模型的标准后处理操作,用于去除重合度(IoU)较高的预测框,只保留预测分数最高的预测框作为检测输出。Soft NMS由[6]提出。在传统的NMS中,跟最高预测分数预测框重合度超出一定阈值的预测框会被直接舍弃,作者认为这样不利于相邻物体的检测。提出的改进方法是根据IoU将预测框的预测分数进行惩罚,最后再按分数过滤。配合Deformable Convnets(将在之后的文章介绍),Soft NMS在MS COCO上取得了当时最佳的表现。算法改进如下:

Soft-NMS算法改进

上图中的即为软化函数,通常取线性或高斯函数,后者效果稍好一些。当然,在享受这一增益的同时,Soft-NMS也引入了一些超参,对不同的数据集需要试探以确定最佳配置。

解析Soft NMS论文的一个小知识点:Improving Object Detection With One Line of Code

可以根据论文Table3中对应AP@(置信度阈值)的值设置iou_thr(nms的值:

左边一半(红色框)是NMS,右边一半(绿色框)是Soft NMS。在NMS部分,相同(置信度阈值)条件下(较小的情况下),基本上值越大,其AP值越小。这主要是因为值越大,有越多的重复框没有过滤掉。

左边一半(红色框)是NMS,右边一半(绿色框)是Soft NMS。

MMDetection中,Soft NMS 软化非极大抑制:(源码解析)

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='nms', iou_thr=0.5), max_per_img=20)) # 这里可以换为soft_nms

8.RoIAlign RoI对齐

RoIAlign是Mask R-CNN([7])的工作中提出的,针对的问题是RoI在进行Pooling时有不同程度的取整,这影响了实例分割中mask损失的计算。文章采用双线性插值的方法将RoI的表示精细化,并带来了较为明显的性能提升。这一技巧也被后来的一些工作(如light-head R-CNN)沿用。

这一部分暂时没有代码解析。

9.其他方法

除去上面所列的技巧外,还有一些做法也值得注意:

  • 更好的先验(YOLOv2):使用聚类方法统计数据中box标注的大小和长宽比,以更好的设置anchor box的生成配置
  • 更好的pre-train模型:检测模型的基础网络通常使用ImageNet(通常是ImageNet-1k)上训练好的模型进行初始化,使用更大的数据集(ImageNet-5k)预训练基础网络对精度的提升亦有帮助
  • 超参数的调整:部分工作也发现如NMS中IoU阈值的调整(从0.3到0.5)也有利于精度的提升,但这一方面尚无最佳配置参照

最后,集成(Ensemble)作为通用的手段也被应用在比赛中。

代码部分

1.各部分代码解析

1.1 faster_rcnn_r50_fpn_1x.py:

首先介绍一下这个配置文件所描述的框架,它是基于resnet50的backbone,有着5个fpn特征层的faster-RCNN目标检测网络,训练迭代次数为标准的12次epoch。

# model settings
model = dict(type='FasterRCNN',                         # model类型pretrained='modelzoo://resnet50',          # 预训练模型:imagenet-resnet50backbone=dict(type='ResNet',                         # backbone类型depth=50,                              # 网络层数num_stages=4,                          # resnet的stage数量out_indices=(0, 1, 2, 3),              # 输出的stage的序号frozen_stages=1,                       # 冻结的stage数量,即该stage不更新参数,-1表示所有的stage都更新参数style='pytorch'),                      # 网络风格:如果设置pytorch,则stride为2的层是conv3x3的卷积层;如果设置caffe,则stride为2的层是第一个conv1x1的卷积层neck=dict(type='FPN',                            # neck类型in_channels=[256, 512, 1024, 2048],    # 输入的各个stage的通道数out_channels=256,                      # 输出的特征层的通道数num_outs=5),                           # 输出的特征层的数量rpn_head=dict(type='RPNHead',                        # RPN网络类型in_channels=256,                       # RPN网络的输入通道数feat_channels=256,                     # 特征层的通道数anchor_scales=[8],                     # 生成的anchor的baselen,baselen = sqrt(w*h),w和h为anchor的宽和高anchor_ratios=[0.5, 1.0, 2.0],         # anchor的宽高比anchor_strides=[4, 8, 16, 32, 64],     # 在每个特征层上的anchor的步长(对应于原图)target_means=[.0, .0, .0, .0],         # 均值target_stds=[1.0, 1.0, 1.0, 1.0],      # 方差use_sigmoid_cls=True),                 # 是否使用sigmoid来进行分类,如果False则使用softmax来分类bbox_roi_extractor=dict(type='SingleRoIExtractor',                                   # RoIExtractor类型roi_layer=dict(type='RoIAlign', out_size=7, sample_num=2),   # ROI具体参数:ROI类型为ROIalign,输出尺寸为7,sample数为2out_channels=256,                                            # 输出通道数featmap_strides=[4, 8, 16, 32]),                             # 特征图的步长bbox_head=dict(type='SharedFCBBoxHead',                     # 全连接层类型num_fcs=2,                                   # 全连接层数量in_channels=256,                             # 输入通道数fc_out_channels=1024,                        # 输出通道数roi_feat_size=7,                             # ROI特征层尺寸num_classes=81,                              # 分类器的类别数量+1,+1是因为多了一个背景的类别target_means=[0., 0., 0., 0.],               # 均值target_stds=[0.1, 0.1, 0.2, 0.2],            # 方差reg_class_agnostic=False))                   # 是否采用class_agnostic的方式来预测,class_agnostic表示输出bbox时只考虑其是否为前景,后续分类的时候再根据该bbox在网络中的类别得分来分类,也就是说一个框可以对应多个类别
# model training and testing settings
train_cfg = dict(rpn=dict(assigner=dict(type='MaxIoUAssigner',            # RPN网络的正负样本划分pos_iou_thr=0.7,                  # 正样本的iou阈值neg_iou_thr=0.3,                  # 负样本的iou阈值min_pos_iou=0.3,                  # 正样本的iou最小值。如果assign给ground truth的anchors中最大的IOU低于0.3,则忽略所有的anchors,否则保留最大IOU的anchorignore_iof_thr=-1),               # 忽略bbox的阈值,当ground truth中包含需要忽略的bbox时使用,-1表示不忽略sampler=dict(type='RandomSampler',             # 正负样本提取器类型num=256,                          # 需提取的正负样本数量pos_fraction=0.5,                 # 正样本比例neg_pos_ub=-1,                    # 最大负样本比例,大于该比例的负样本忽略,-1表示不忽略add_gt_as_proposals=False),       # 把ground truth加入proposal作为正样本allowed_border=0,                     # 允许在bbox周围外扩一定的像素pos_weight=-1,                        # 正样本权重,-1表示不改变原始的权重smoothl1_beta=1 / 9.0,                # 平滑L1系数debug=False),                         # debug模式rcnn=dict(assigner=dict(type='MaxIoUAssigner',            # RCNN网络正负样本划分pos_iou_thr=0.5,                  # 正样本的iou阈值neg_iou_thr=0.5,                  # 负样本的iou阈值min_pos_iou=0.5,                  # 正样本的iou最小值。如果assign给ground truth的anchors中最大的IOU低于0.3,则忽略所有的anchors,否则保留最大IOU的anchorignore_iof_thr=-1),               # 忽略bbox的阈值,当ground truth中包含需要忽略的bbox时使用,-1表示不忽略sampler=dict(type='RandomSampler',             # 正负样本提取器类型num=512,                          # 需提取的正负样本数量pos_fraction=0.25,                # 正样本比例neg_pos_ub=-1,                    # 最大负样本比例,大于该比例的负样本忽略,-1表示不忽略add_gt_as_proposals=True),        # 把ground truth加入proposal作为正样本pos_weight=-1,                        # 正样本权重,-1表示不改变原始的权重debug=False))                         # debug模式
test_cfg = dict(rpn=dict(                                 # 推断时的RPN参数nms_across_levels=False,              # 在所有的fpn层内做nmsnms_pre=2000,                         # 在nms之前保留的的得分最高的proposal数量nms_post=2000,                        # 在nms之后保留的的得分最高的proposal数量max_num=2000,                         # 在后处理完成之后保留的proposal数量nms_thr=0.7,                          # nms阈值min_bbox_size=0),                     # 最小bbox尺寸rcnn=dict(score_thr=0.05, nms=dict(type='nms', iou_thr=0.5), max_per_img=100)   # max_per_img表示最终输出的det bbox数量# soft-nms is also supported for rcnn testing# e.g., nms=dict(type='soft_nms', iou_thr=0.5, min_score=0.05)            # soft_nms参数
)
# dataset settings
dataset_type = 'CocoDataset'                # 数据集类型
data_root = 'data/coco/'                    # 数据集根目录
img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)   # 输入图像初始化,减去均值mean并处以方差std,to_rgb表示将bgr转为rgb
data = dict(imgs_per_gpu=2,                # 每个gpu计算的图像数量workers_per_gpu=2,             # 每个gpu分配的线程数train=dict(type=dataset_type,                                                 # 数据集类型ann_file=data_root + 'annotations/instances_train2017.json',       # 数据集annotation路径img_prefix=data_root + 'train2017/',                               # 数据集的图片路径img_scale=(1333, 800),                                             # 输入图像尺寸,最大边1333,最小边800img_norm_cfg=img_norm_cfg,                                         # 图像初始化参数size_divisor=32,                                                   # 对图像进行resize时的最小单位,32表示所有的图像都会被resize成32的倍数flip_ratio=0.5,                                                    # 图像的随机左右翻转的概率with_mask=False,                                                   # 训练时附带maskwith_crowd=True,                                                   # 训练时附带difficult的样本with_label=True),                                                  # 训练时附带labelval=dict(type=dataset_type,                                                 # 同上ann_file=data_root + 'annotations/instances_val2017.json',         # 同上img_prefix=data_root + 'val2017/',                                 # 同上img_scale=(1333, 800),                                             # 同上img_norm_cfg=img_norm_cfg,                                         # 同上size_divisor=32,                                                   # 同上flip_ratio=0,                                                      # 同上with_mask=False,                                                   # 同上with_crowd=True,                                                   # 同上with_label=True),                                                  # 同上test=dict(type=dataset_type,                                                 # 同上ann_file=data_root + 'annotations/instances_val2017.json',         # 同上img_prefix=data_root + 'val2017/',                                 # 同上img_scale=(1333, 800),                                             # 同上img_norm_cfg=img_norm_cfg,                                         # 同上size_divisor=32,                                                   # 同上flip_ratio=0,                                                      # 同上with_mask=False,                                                   # 同上with_label=False,                                                  # 同上test_mode=True))                                                   # 同上
# optimizer
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)   # 优化参数,lr为学习率,momentum为动量因子,weight_decay为权重衰减因子
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))          # 梯度均衡参数
# learning policy
lr_config = dict(policy='step',                        # 优化策略warmup='linear',                      # 初始的学习率增加的策略,linear为线性增加warmup_iters=500,                     # 在初始的500次迭代中学习率逐渐增加warmup_ratio=1.0 / 3,                 # 起始的学习率step=[8, 11])                         # 在第8和11个epoch时降低学习率
checkpoint_config = dict(interval=1)      # 每1个epoch存储一次模型
# yapf:disable
log_config = dict(interval=50,                          # 每50个batch输出一次信息hooks=[dict(type='TextLoggerHook'),      # 控制台输出信息的风格# dict(type='TensorboardLoggerHook')])
# yapf:enable
# runtime settings
total_epochs = 12                               # 最大epoch数
dist_params = dict(backend='nccl')              # 分布式参数
log_level = 'INFO'                              # 输出信息的完整度级别
work_dir = './work_dirs/faster_rcnn_r50_fpn_1x' # log文件和模型文件存储路径
load_from = None                                # 加载模型的路径,None表示从预训练模型加载
resume_from = None                              # 恢复训练模型的路径
workflow = [('train', 1)]                       # 当前工作区名称

1.2 cascade_rcnn_r50_fpn_1x.py

cascade-RCNN是cvpr2018的文章,相比于faster-RCNN的改进主要在于其RCNN有三个stage,这三个stage逐级refine检测的结果,使得结果达到更高的精度。下面逐条解释其config的含义,与faster-RCNN相同的部分就不再赘述

# model settings
model = dict(type='CascadeRCNN',num_stages=3,                     # RCNN网络的stage数量,在faster-RCNN中为1pretrained='modelzoo://resnet50',backbone=dict(type='ResNet',depth=50,num_stages=4,out_indices=(0, 1, 2, 3),frozen_stages=1,style='pytorch'),neck=dict(type='FPN',in_channels=[256, 512, 1024, 2048],out_channels=256,num_outs=5),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],use_sigmoid_cls=True),bbox_roi_extractor=dict(type='SingleRoIExtractor',roi_layer=dict(type='RoIAlign', out_size=7, sample_num=2),out_channels=256,featmap_strides=[4, 8, 16, 32]),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=True),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.05, 0.05, 0.1, 0.1],reg_class_agnostic=True),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.033, 0.033, 0.067, 0.067],reg_class_agnostic=True)])
# model training and testing settings
train_cfg = dict(rpn=dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.7,neg_iou_thr=0.3,min_pos_iou=0.3,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=256,pos_fraction=0.5,neg_pos_ub=-1,add_gt_as_proposals=False),allowed_border=0,pos_weight=-1,smoothl1_beta=1 / 9.0,debug=False),rcnn=[                    # 注意,这里有3个RCNN的模块,对应开头的那个RCNN的stage数量dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.6,neg_iou_thr=0.6,min_pos_iou=0.6,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.7,neg_iou_thr=0.7,min_pos_iou=0.7,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False)],stage_loss_weights=[1, 0.5, 0.25])     # 3个RCNN的stage的loss权重
test_cfg = dict(rpn=dict(nms_across_levels=False,nms_pre=2000,nms_post=2000,max_num=2000,nms_thr=0.7,min_bbox_size=0),rcnn=dict(score_thr=0.05, nms=dict(type='nms', iou_thr=0.5), max_per_img=100),keep_all_stages=False)         # 是否保留所有stage的结果
# dataset settings
dataset_type = 'CocoDataset'
data_root = 'data/coco/'
img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
data = dict(imgs_per_gpu=2,workers_per_gpu=2,train=dict(type=dataset_type,ann_file=data_root + 'annotations/instances_train2017.json',img_prefix=data_root + 'train2017/',img_scale=(1333, 800),img_norm_cfg=img_norm_cfg,size_divisor=32,flip_ratio=0.5,with_mask=False,with_crowd=True,with_label=True),val=dict(type=dataset_type,ann_file=data_root + 'annotations/instances_val2017.json',img_prefix=data_root + 'val2017/',img_scale=(1333, 800),img_norm_cfg=img_norm_cfg,size_divisor=32,flip_ratio=0,with_mask=False,with_crowd=True,with_label=True),test=dict(type=dataset_type,ann_file=data_root + 'annotations/instances_val2017.json',img_prefix=data_root + 'val2017/',img_scale=(1333, 800),img_norm_cfg=img_norm_cfg,size_divisor=32,flip_ratio=0,with_mask=False,with_label=False,test_mode=True))
# optimizer
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
# learning policy
lr_config = dict(policy='step',warmup='linear',warmup_iters=500,warmup_ratio=1.0 / 3,step=[8, 11])
checkpoint_config = dict(interval=1)
# yapf:disable
log_config = dict(interval=50,hooks=[dict(type='TextLoggerHook'),# dict(type='TensorboardLoggerHook')])
# yapf:enable
# runtime settings
total_epochs = 12
dist_params = dict(backend='nccl')
log_level = 'INFO'
work_dir = './work_dirs/cascade_rcnn_r50_fpn_1x'
load_from = None
resume_from = None
workflow = [('train', 1)]

2.trick部分代码,cascade_rcnn_r50_fpn_1x.py:

# fp16 settings
fp16 = dict(loss_scale=512.)
# model settings
model = dict(type='CascadeRCNN',num_stages=3,pretrained='torchvision://resnet50',backbone=dict(type='ResNet',depth=50,num_stages=4,out_indices=(0, 1, 2, 3),frozen_stages=1,style='pytorch',#dcn=dict( #在最后三个block加入可变形卷积 #   modulated=False, deformable_groups=1, fallback_on_stride=False),#  stage_with_dcn=(False, True, True, True)),neck=dict(type='FPN',in_channels=[256, 512, 1024, 2048],out_channels=256,num_outs=5),rpn_head=dict(type='RPNHead',in_channels=256,feat_channels=256,anchor_scales=[8],anchor_ratios=[0.2, 0.5, 1.0, 2.0, 5.0], # 添加了0.2,5,过两天发图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='FocalLoss', use_sigmoid=True, loss_weight=1.0), # 修改了loss,为了调控难易样本与正负样本比例loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),bbox_roi_extractor=dict(type='SingleRoIExtractor',roi_layer=dict(type='RoIAlign', out_size=7, sample_num=2),out_channels=256,featmap_strides=[4, 8, 16, 32]),bbox_head=[dict(type='SharedFCBBoxHead',num_fcs=2,in_channels=256,fc_out_channels=1024,roi_feat_size=7,num_classes=11,target_means=[0., 0., 0., 0.],target_stds=[0.1, 0.1, 0.2, 0.2],reg_class_agnostic=True,loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),dict(type='SharedFCBBoxHead',num_fcs=2,in_channels=256,fc_out_channels=1024,roi_feat_size=7,num_classes=11,target_means=[0., 0., 0., 0.],target_stds=[0.05, 0.05, 0.1, 0.1],reg_class_agnostic=True,loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),dict(type='SharedFCBBoxHead',num_fcs=2,in_channels=256,fc_out_channels=1024,roi_feat_size=7,num_classes=11,target_means=[0., 0., 0., 0.],target_stds=[0.033, 0.033, 0.067, 0.067],reg_class_agnostic=True,loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))])
# model training and testing settings
train_cfg = dict(rpn=dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.7,neg_iou_thr=0.3,min_pos_iou=0.3,ignore_iof_thr=-1),sampler=dict(type='RandomSampler', num=256,pos_fraction=0.5,neg_pos_ub=-1,add_gt_as_proposals=False),allowed_border=0,pos_weight=-1,debug=False),rpn_proposal=dict(nms_across_levels=False,nms_pre=2000,nms_post=2000,max_num=2000,nms_thr=0.7,min_bbox_size=0),rcnn=[dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.4, # 更换neg_iou_thr=0.4,min_pos_iou=0.4,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler', # 解决难易样本,也解决了正负样本比例问题。num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False),dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.6,neg_iou_thr=0.6,min_pos_iou=0.6,ignore_iof_thr=-1),sampler=dict(type='OHEMSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False)],stage_loss_weights=[1, 0.5, 0.25])
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='nms', iou_thr=0.5), max_per_img=20)) # 这里可以换为soft_nms
# dataset settings
dataset_type = 'CocoDataset'
data_root = '../../data/chongqing1_round1_train1_20191223/'
img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(492,658), keep_ratio=True), #这里可以更换多尺度[(),()]dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(492,658),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img']),])
]
data = dict(imgs_per_gpu=8, # 有的同学不知道batchsize在哪修改,其实就是修改这里,每个gpu同时处理的images数目。workers_per_gpu=2,train=dict(type=dataset_type,ann_file=data_root + 'fixed_annotations.json', # 更换自己的json文件img_prefix=data_root + 'images/', # images目录pipeline=train_pipeline),val=dict(type=dataset_type,ann_file=data_root + 'fixed_annotations.json',img_prefix=data_root + 'images/',pipeline=test_pipeline),test=dict(type=dataset_type,ann_file=data_root + 'fixed_annotations.json',img_prefix=data_root + 'images/',pipeline=test_pipeline))
# optimizer
optimizer = dict(type='SGD', lr=0.001, momentum=0.9, weight_decay=0.0001) # lr = 0.00125*batch_size,不能过大,否则梯度爆炸。
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
# learning policy
lr_config = dict(policy='step',warmup='linear',warmup_iters=500,warmup_ratio=1.0 / 3,step=[6, 12, 19])
checkpoint_config = dict(interval=1)
# yapf:disable
log_config = dict(interval=64,hooks=[dict(type='TextLoggerHook'), # 控制台输出信息的风格# dict(type='TensorboardLoggerHook') # 需要安装tensorflow and tensorboard才可以使用])
# yapf:enable
# runtime settings
total_epochs = 20
dist_params = dict(backend='nccl')
log_level = 'INFO'
work_dir = '../work_dirs/cascade_rcnn_r50_fpn_1x' # 日志目录
load_from = '../work_dirs/cascade_rcnn_r50_fpn_1x/latest.pth' # 模型加载目录文件
#load_from = '../work_dirs/cascade_rcnn_r50_fpn_1x/cascade_rcnn_r50_coco_pretrained_weights_classes_11.pth'
resume_from = None
workflow = [('train', 1)]

Reference

  • [1]:

Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

  • [2]:

Object Detection Networks on Convolutional Feature Maps

  • [3]:

Deep Residual Learning for Image Classification

  • [4]:

Object Detection via a Multi-region & Semantic Segmentatio-aware CNN Model

  • [5]:

Training Region-based Object Detectors with Online Hard Example Mining

  • [6]:

Improving Object Detection With One Line of Code

  • [7]:

Mask R-CNN

作者:初识CV@知乎

推荐阅读:
PyTorch trick 集锦
目标检测 | 让 YOLOv2 告诉你 Trick 的力量
有关语义分割的奇技淫巧有哪些:分享一些实用的纯工程 tricks

目标检测比赛中的 trick相关推荐

  1. 百度飞桨开源Open Images Dataset V5目标检测比赛最好单模型MSF-DET

    目标检测是计算机视觉领域中的核心任务.Open Images Dataset V5(OIDV5)是目前规模最大的目标检测公开数据集[1].基于飞桨(PaddlePaddle)的PaddleDetect ...

  2. 算法的trick_目标检测算法中的常见trick

    目标检测算法中的常见trick 最近忙着打比赛,感觉看论文也很难静下心来了.基本上看得相当匆忙,主要还是以应用为主.上周压力比较大,没有来得及更新,感觉再不更就说不过去了. 因为比赛比较追求perfo ...

  3. 目标检测任务中常用的数据集格式(voc、coco、yolo)

    在目标检测任务中,常见的数据集格式有三种,分别为voc(xml).coco(json).yolo(txt). 一.Pascal VOC Pascal VOC数据集是目标检测的常用的大规模数据集之一,从 ...

  4. 目标检测模型中NMS、soft-NMS、softer-NMS的原理、LNMS文本检测系列(python代码实现)

    非极大值抑制NMS的作用: 是目标检测框架中的后处理模块,主要用于删除高度冗余的bbox. ?一.NMS[参考] 非极大值抑制NMS的过程: 根据置信度得分进行排序: 选择置信度最高的边界框添加到最终 ...

  5. 目标检测算法中ROI提取方法比较+源码分析

    本文主要介绍ROI提取结构在目标检测框架中的作用,并结合源码,理解它的实现方式.包含的算法有:ROI-pooling,ROI-align,Deformable-psroi-pooling. 目前,主流 ...

  6. 目标检测扩(六)一篇文章彻底搞懂目标检测算法中的评估指标计算方法(IoU(交并比)、Precision(精确度)、Recall(召回率)、AP(平均正确率)、mAP(平均类别AP) )

    ​ 基本在目标检测算法中会碰到一些评估指标.常见的指标参数有:IoU(交并比).Precision(精确度).Recall(召回率).AP(平均正确率).mAP(平均类别AP)等.这些评估指标是在评估 ...

  7. 『论文阅读笔记』目标检测模型中的性能评价方式-IOU、precision/recall、mAP、PR、Fps!

    目标检测模型中的性能评估标准-IOU.precision/recall.mAP.PR.Fps! 文章目录 一.交并比IOU 二.精确率(precision)和召回率(recall) 三.P-R(pre ...

  8. 目标检测项目中面对高分辨率图像的滑动窗口技术(二)(代码开源,超简便API封装,直接调用进行切图及保存)

    在上篇博客简单介绍了滑动窗口技术以及切图程序的使用文档,这篇展示对子图进行反向拼接到原图上(用于对测试集子图预测后结果汇总到原图上)的程序代码以及使用说明. 所需模块:numpy.pandas.mat ...

  9. 目标检测网络中的Backbone,Neck和Head - 以YOLOv4为例

    目标检测网络中的Backbone,Neck和Head - 以YOLOv4为例 目标检测网络中常见到的三个概念: Backbone:在不同图像细粒度上聚合并形成图像特征的卷积神经网络: Neck:一系列 ...

最新文章

  1. python打包_cx_freeze
  2. 蓝桥杯审核要多久_商标审核要多久?
  3. UVA11991第k次出现的v的下标
  4. sql语句中嵌套2层循环
  5. python提示错误TypeError: write() argument must be str, not bytes
  6. 随机样本一致性:一种用于图像分析和自动制图的模型拟合模型(3)--(P3P的迭代解)
  7. mysql php 时间比较大小_mysql中日期大小比较实例
  8. 读书笔记Black-Scholes-Merton之二
  9. Error: Cannot find module ‘node-sass‘---Node-sass下载
  10. jsonarray转化list对象_第8篇:Cython的面向对象--Python类 vs Cython扩展类
  11. 无用小知识-递归的使用
  12. linux 音频驱动实验
  13. Spring学习(五):动态代理的两种实现方式(全网最容易懂)
  14. 使用uib-tab时,获取页面输入框内容
  15. 【速成MSP430f149】电赛期间学习MSP430f149笔记
  16. 共阴极数码管,学号显示实验
  17. 判断位数为3的高级写法
  18. 史上最通俗计算机网络分层详解,附架构师必备技术详解
  19. android手机桌面怎么调,安卓手机如何修改手机桌面上的CMCC
  20. 青铜变王者,桌面云是如何逆袭的?

热门文章

  1. wxWidgets:文档/视图框架
  2. wxWidgets的简介
  3. boost::detail::invoke相关的测试程序
  4. boost::phoenix::lambda相关的测试程序
  5. boost::endian::endian_load的测试程序
  6. Boost:双图bimap与lambda表达式的测试程序
  7. ITK:使用曲率流平滑图像
  8. ITK:扩张的二进制图像
  9. ITK:转换三角网格为二进制图像
  10. VTK:简单操作之RandomSequence