深度学习Pytorch检测实战 - Notes - 第4章 两阶经典检测器:Faster RCNN

2020-07-30 14:53:39 浏览数 (1)

第4章 两阶经典检测器:Faster RCNN

RCNN全称为Regions with CNN Features,是将深度学习应用到物体检测领域的经典之作,并凭借卷积网络出色的特征提取能力,大幅度提升了物体检测的效果。而随后基于RCNN的Fast RCNN及Faster RCNN将物体检测问题进一步优化,在实现方式、速度、精度上均有了大幅度提升。

物体检测领域出现的新成果很大一部分也是基于RCNN系列的思想,尤其是Faster RCNN,并且在解决小物体、拥挤等较难任务时, RCNN系列仍然具有较强的优势。因此,想要学习物体检测,RCNN系列是第一个需要全面掌握的算法。

4.1.1 开山之作:RCNN

RCNN算法将卷积神经网络应用于特征提取,仍然延续传统物体检测的思想,将物体检测当做分类问题处理,即先提取一系列的候选区域,然后对候选区域进行分类

具体过程主要包含4步:

(1)候选区域生成。采用Region Proposal提取候选区域,例如Selective Search算法,先将图像分割成小区域,然后合并包含同一物体可能性高的区域,并输出,在这一步需要提取约2000个候选区域。在提取完后,还需要将每一个区域进行归一化处理,得到固定大小的图像。

(2)CNN特征提取。将上述固定大小的图像,利用CNN网络得到固定维度的特征输出

(3)SVM分类器。使用线性二分类器对输出的特征进行分类,得到是否属于此类的结果,并采用难样本挖掘来平衡正负样本的不平衡。

(4)位置精修。通过一个回归器,对特征进行边界回归以得到更为精确的目标区域。

图4.1 RCNN算法流程图图4.1 RCNN算法流程图

RCNN虽然显著提升了物体检测的效果,但仍存在3个较大的问题。首先RCNN需要多步训练,步骤烦琐且训练速度较慢;其次,由于涉及分类中的全连接网络,因此输入尺寸是固定的,造成了精度的降低;最后,候选区域需要提前提取并保存,占用空间较大。

4.1.2 端到端:Fast RCNN

更快、更强的Fast RCNN算法,不仅训练的步骤可以实现端到端,而且算法基于VGG16网络,在训练速度上比RCNN快了近9倍,在测试速度上快了213倍。

Fast RCNN算法框架图如图4.2所示,相比起RCNN,主要有3点改进:

  • 共享卷积:将整幅图送到卷积网络中进行区域生成,而不是像 RCNN那样一个个的候选区域,虽然仍采用Selective Search方法,但共享卷积的优点使得计算量大大减少。
  • RoI Pooling:利用特征池化(RoI Pooling)的方法进行特征尺度变换,这种方法可以有任意大小图片的输入,使得训练过程更加灵活、准确。
  • 多任务损失:将分类与回归网络放到一起训练,并且为了避免 SVM分类器带来的单独训练与速度慢的缺点,使用了Softmax函数进行分类

Fast RCNN算法虽然取得了显著的成果,但在该算法中,Selective Search需要消耗2~3秒,而特征提取仅需要0.2秒,因此这种区域生成方法限制了Fast RCNN算法的发挥空间,这也为后来的Faster RCNN算法提供了改进方向。

4.1.3 走向实时:Faster RCNN

Faster RCNN算法最大的创新点在于提出 了RPN(Region Proposal Network)网络,利用Anchor机制区域生成 与卷积网络联系到一起,将检测速度一举提升到了17 FPS(Frames Per Second)。

Anchor可以看做是图像上很多固定大小与宽高的方框,由于需要检测的物体本身也都是一个个大小宽高不同的方框,因此Faster RCNN将Anchor当做强先验的知识,接下来只需要将Anchor与真实物体进行匹配,进行分类与位置的微调即可。相比起没有Anchor的物体检测算法, 这样的先验无疑降低了网络收敛的难度,再加上一系列的工程优化,使得Faster RCNN达到了物体检测中的一个高峰。

4.3 Faster RCNN总览

如图4.3所示为Faster RCNN算法的基本流程,从功能模块来讲,主要包括4部分:特征提取网络、RPN模块、RoI Pooling(Region of Interest)模块与RCNN模块,虚线表示仅仅在训练时有的步骤。Faster RCNN延续了RCNN系列的思想,即先进行感兴趣区域RoI的生成,然后再把生成的区域分类,最后完成物体的检测,这里的RoI使用的即是RPN模块,区域分类则是RCNN网络。

  • 特征提取网络Backbone:输入图像首先经过Backbone得到特征图, 在此以VGGNet为例,假设输入图像的维度为3×600×800,由于VGGNet 包含4个Pooling层(物体检测使用VGGNet时,通常不使用第5个Pooling 层),下采样率为16,因此输出的feature map的维度为512×37×50。
  • RPN模块:区域生成模块,如图4.3的中间部分,其作用是生成较好的建议框,即Proposal,这里用到了强先验的Anchor。RPN包含5个子模块:
    • Anchor生成:RPN对feature map上的每一个点都对应了9个 Anchors,这9个Anchors大小宽高不同,对应到原图基本可以覆盖所有可能出现的物体。因此,有了数量庞大的Anchors,RPN接下来的工作就是从中筛选,并调整出更好的位置,得到Proposal。
    • RPN卷积网络:与上面的Anchor对应,由于feature map上每个点对应了9个Anchors,因此可以利用1×1的卷积在feature map上得到每一个 Anchor的预测得分与预测偏移值。
    • 计算RPN loss:这一步只在训练中,将所有的Anchors与标签进行匹配,匹配程度较好的Anchors赋予正样本,较差的赋予负样本,得到分类与偏移的真值,与第二步中的预测得分与预测偏移值进行loss的计算。
    • 生成Proposal:利用第二步中每一个Anchor预测的得分与偏移量, 可以进一步得到一组较好的Proposal,送到后续网络中。
    • 筛选Proposal得到RoI:在训练时,由于Proposal数量还是太多(默认是2000),需要进一步筛选Proposal得到RoI(默认数量是256)。在测试阶段,则不需要此模块,Proposal可以直接作为RoI,默认数量为 300
  • RoI Pooling模块:这部分承上启下,接受卷积网络提取的feature map和RPN的RoI,输出送到RCNN网络中。由于RCNN模块使用了全连接网络,要求特征的维度固定,而每一个RoI对应的特征大小各不相同,无法送入到全连接网络,因此RoI Pooling将RoI的特征池化到固定的维度,方便送到全连接网络中
  • RCNN模块:将RoI Pooling得到的特征送入全连接网络,预测每一 个RoI的分类,并预测偏移量以精修边框位置,并计算损失,完成整个 Faster RCNN过程。主要包含3部分:
  • RCNN全连接网络:将得到的固定维度的RoI特征接到全连接网络 中,输出为RCNN部分的预测得分与预测回归偏移量。
  • 计算RCNN的真值:对于筛选出的RoI,需要确定是正样本还是负样本,同时计算与对应真实物体的偏移量。在实际实现时,为实现方便,这一步往往与RPN最后筛选RoI那一步放到一起。
  • RCNN loss:通过RCNN的预测值与RoI部分的真值,计算分类与回归loss。
图4.3 Faster RCNN算法过程示意图图4.3 Faster RCNN算法过程示意图

从整个过程可以看出,Faster RCNN是一个两阶的算法,即RPN与 RCNN,这两步都需要计算损失,只不过前者还要为后者提供较好的感兴趣区域。

4.4 详解RPN

RPN部分的输入、输出如下:

  • 输入:feature map、物体标签,即训练集中所有物体的类别与边框位置。
  • 输出:Proposal、分类Loss、回归Loss,其中,Proposal作为生成的区域,供后续模块分类与回归。两部分损失用作优化网络。

RPN模块的总体代码逻辑如下,源代码文件见 lib/model/faster_rcnn/faster_rcnn.py。

代码语言:python代码运行次数:0复制
def forward(self, im_data, im_info, gt_boxes, num_boxes):    
    # 输入数据的第一维是batch数     
    batch_size = im_data.size()    
    im_info = im_info.data    
    gt_boxes = gt_boxes.data    
    num_boxes =  num_boxes.data    
    # 从VGG的Backbone中获取feature map    
    base_feat = self.RCNN_base(im_data)    
    # 将feature map送入RPN,得到Proposal与分类与回归loss    
    rois, rpn_loss_cls, rpn_loss_bbox = self.RCNN_rpn(base_feat, im_info,     
    gt_boxes, num_boxes)    
    ······

4.4.1 理解Anchor

理解Anchor是理解RPN乃至Faster RCNN的关键。Faster RCNN先提供一些先验的边框,然后再去筛选与修正,这样在Anchor的基础上做物体检测要比从无到有的直接拟合物体的边框容易一些。

Anchor的本质是在原图大小上的一系列的矩形框,但Faster RCNN 将这一系列的矩形框和feature map进行了关联。具体做法是,首先对 feature map进行3×3的卷积操作,得到的每一个点的维度是512维,这 512维的数据对应着原始图片上的很多不同的大小与宽高区域的特征这些区域的中心点都相同。如果下采样率为默认的16,则每一个点的坐标乘以16即可得到对应的原图坐标。

为适应不同物体的大小与宽高,在作者的论文中,默认在每一个点上抽取了9种Anchors,具体Scale为{8,16,32}Ratio为{0.5,1,2},将这9 种Anchors的大小反算到原图上,即得到不同的原始Proposal,如图4.4所示。由于feature map大小为37×50,因此一共有37×50×9=16650个 Anchors。而后通过分类网络与回归网络得到每一个Anchor的前景背景概率和偏移量,前景背景概率用来判断Anchor是前景的概率,回归网络则是将预测偏移量作用到Anchor上使得Anchor更接近于真实物体坐标

图4.4 Anchor原理示意图图4.4 Anchor原理示意图
不同的ratio不同的ratio
不同的scale不同的scale

在具体的代码实现时,lib/model/rpn下的anchor_target_layer.py与 proposal_layer.py在类的初始化中均生成了所需的Anchor,下面从代码角度简单讲解一下生成过程,源代码文件见 lib/model/rpn/generate_anchors.py。

代码语言:python代码运行次数:0复制
def generate_anchors(base_size=16, ratios=[0.5, 1, 2], scales=2**np.arange(3, 6)):    
    # 首先创建一个基本Anchor为[0, 0, 15, 15]    
    base_anchor = np.array([1, 1, base_size, base_size]) - 1    
    # 将基本Anchor进行宽高变化,生成三种宽高比的s:Anchor    
    ratio_anchors = _ratio_enum(base_anchor, ratio)    
    # 将上述Anchor再进行尺度变化,得到最终的9种Anchors    
    anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales)                            
                            for i in xrange(ratio_anchors.shape[0])])    
    # 返回对应于feature map大小的Anchors    
    return anchors

4.4.2 RPN的真值与预测量

理解RPN的预测量与真值分别是什么,也是理解RPN原理的关键。 对于物体检测任务来讲,模型需要预测每一个物体的类别及其出现的位置,即类别、中心点坐标x与y、宽w与高h这5个量。由于有了Anchor这个先验框,RPN可以预测Anchor的类别作为预测边框的类别并且可以预测真实的边框相对于Anchor的偏移量,而不是直接预测边框的中心点坐标x与y、宽高w与h。

举个例子,如图4.5所示,输入图像中有3个Anchors与两个标签,从位置来看,Anchor A、C分别和标签M、N有一定的重叠,而Anchor B位置更像是背景。

图4.5 图像中Anchor与标签的关系图4.5 图像中Anchor与标签的关系

首先介绍模型的真值。对于类别的真值,由于RPN只负责区域生成,保证recall,而没必要细分每一个区域属于哪一个类别,因此只需要前景与背景两个类别,前景即有物体,背景则没有物体。RPN通过计算Anchor与标签的IoU来判断一个Anchor是属于前景还是背景。当IoU大于一定值时,该Anchor的真值为前景,低于一定值时,该Anchor的真值为背景。

然后是偏移量的真值。仍以图4.5中的Anchor A与标签M为例,假设 Anchor A的中心坐标为xa与ya,宽高分别为wa与ha,标签M的中心坐标为x与y,宽高分别为w与h,则对应的偏移真值计算公式如式(4-2)所 示。

偏移真值计算公式偏移真值计算公式

从式(4-2)中可以看到,位置偏移tx与ty利用宽与高进行了归一 化,而宽高偏移tw与th进行了对数处理,这样的好处是进一步限制了偏移量的范围便于预测

有了上述的真值,为了求取损失,RPN通过卷积网络分别得到了类别与偏移量的预测值。具体来讲,RPN需要预测每一个Anchor属于前景与背景的概率,同时也需要预测真实物体相对于Anchor的偏移量,记为 t*x、t*y、t*w和t*h。

另外,在得到预测偏移量后,可以使用式(4-3)的公式将预测偏移量作用到对应的Anchor上,得到预测框的实际位置x*、y*、w*和h*。

预测框的实际位置预测框的实际位置

如果没有Anchor,做物体检测需要直接预测每个框的坐标,由于框的坐标变化幅度大,使网络很难收敛与准确预测,而Anchor相当于提供了一个先验的阶梯,使得模型去预测Anchor的偏移量,即可更好地接近真实物体

实际上,Anchor是我们想要预测属性的先验参考值,并不局限于矩形框。如果需要,我们也可以增加其他类型的先验,如多边形框、角度和速度等。

4.4.3 RPN卷积网络

为了实现上述的预测,RPN搭建了如图4.6所示的网络结构。具体实现时,在feature map上首先用3×3的卷积进行更深的特征提取,然后利 用1×1的卷积分别实现分类网络和回归网络。

RPN网络RPN网络

在物体检测中,通常我们将有物体的位置称为前景,没有物体的位置称为背景。在分类网络分支中,首先使用1×1卷积输出18×37×50的特征,由于每个点默认有9个Anchors,并且每个Anchor只预测其属于前景还是背景,因此通道数为18。随后利用torch.view()函数将特征映射到 2×333×75,这样第一维仅仅是一个Anchor的前景背景得分,并送到 Softmax函数中进行概率计算,得到的特征再变换到18×37×50的维度, 最终输出的是每个Anchor属于前景与背景的概率。

在回归分支中,利用1×1卷积输出36×37×50的特征,第一维的36包含9个Anchors的预测,每一个Anchor有4个数据,分别代表了每一个Anchor的中心点横纵坐标及宽高这4个量相对于真值的偏移量。RPN的网络部分代码如下,源代码文件见lib/model/rpn/rpn.py。

代码语言:python代码运行次数:0复制
def forward(self, base_feat, im_info, gt_boxes, num_boxes):    
    # 输入数据的第一维是batch值    
    batch_size = base_feat.size(0)    
    # 首先利用3×3卷积进一步融合特征    
    rpn_conv1 = F.relu(self.RPN_Conv(base_feat), inplace=True)    
    # 利用1×1卷积得到分类网络,每个点代表Anchor的前景背景得分    
    rpn_cls_score = self.RPN_cls_score(rpn_conv1)    
    # 利用reshape与softmax得到Anchor的前景背景概率    
    rpn_cls_score_reshape = self.reshape(rpn_cls_score, 2)    
    rpn_cls_prob_reshape = F.softmax(rpn_cls_score_reshape, 1)    
    rpn_cls_prob = self.reshape(rpn_cls_prob_reshape, 18)    
    # 利用1×1卷积得到回归网络,每一个点代表Anchor的偏移    
    rpn_bbox_pred = self.RPN_bbox_pred(rpn_conv1)

4.4.4 RPN真值的求取

上一节的RPN分类与回归网络得到的是模型的预测值,而为了计算预测的损失,还需要得到分类与偏移预测的真值,具体指的是每一个 Anchor是否对应着真实物体,以及每一个Anchor对应物体的真实偏移值。求真值的具体实现过程如图4.7所示,主要包含4步,下面具体介绍。

图4.7 RPN真值求取过程图4.7 RPN真值求取过程

1.Anchor生成

这部分与前面Anchor的生成过程一样,可以得到37×50×9=16650个 Anchors。由于按照这种方式生成的Anchor会有一些边界在图像边框外,因此还需要把这部分超过图像边框的Anchors过滤掉,具体生成过程如下,源代码文件见lib/model/rpn/anchor_target_layer.py。

代码语言:python代码运行次数:0复制
def forward(self, input):    
    ······    
    # 利用NumPy首先得到原图上的中心点坐标,并利用contiguous保证内存连续    
    shifts = torch.from_numpy(np.vstack((shift_x.ravel(), shift_y.ravel(),                            
                                shift_x.ravel(), shift_y.ravel())).transpose())    
    shifts = shifts.contiguous().type_as(rpn_cls_score).float()    
    ·······   
    # 调用基础Anchor生成所有Anchors   
    self._anchors = self._anchors.type_as(gt_boxes)
    all_anchors = self._anchors.view(1, A, 4)   shifts.view(K, 1, 4)    
    ·······    
    # 保留边框内的Anchors    
    inds_inside = torch.nonzero(keep).view(-1)   
    anchors = all_anchors[inds_inside, :]

2.Anchor与标签的匹配

为了计算Anchor的损失,在生成Anchor之后,我们还需要得到每个 Anchor的类别,由于RPN的作用是建议框生成,而非详细的分类,因此只需要区分正样本与负样本,即每个Anchor是属于正样本还是负样本

前面已经介绍了通过计算Anchor与标签的IoU来判断是正样本还是 负样本。在具体实现时,需要计算每一个Anchor与每一个标签的IoU, 因此会得到一个IoU矩阵,具体的判断标准如下:

  • 对于任何一个Anchor,与所有标签的最大IoU小于0.3,则视为负样本。
  • 对于任何一个标签,与其有最大IoU的Anchor视为正样本。(为了保证每个标签至少有一个Anchor,这样才可以不漏检)
  • 对于任何一个Anchor,与所有标签的最大IoU大于0.7,则视为正样本。

匹配与筛选的代码示例如下,源代码文件见 lib/model/rpn/anchor_target_layer.py。

代码语言:javascript复制
def forward(self, input):    
    # 生成标签向量,对应每一个Anchor的状态,1为正,0为负,初始化为-1    
    labels = gt_boxes.new(batch_size, inds_inside.size(0)).fill_(-1)    
    # 生成IoU矩阵,每一行代表一个Anchor,每一列代表一个标签    
    overlaps = bbox_overlaps_batch(anchors, gt_boxes)    
    
    # 对每一行求最大值,返回的第一个为最大值,第二个为最大值的位置    
    max_overlaps, argmax_overlaps = torch.max(overlaps, 2)    
    # 对每一列取最大值,返回的是每一个标签对应的IoU最大值    
    gt_max_overlaps, _ = torch.max(overlaps, 1)
    
    # 如果一个Anchor最大的IoU小于0.3,视为负样本    
    labels[max_overlaps < 0.3] = 0    
    # 与所有Anchors的最大IoU为0的标签要过滤掉    
    gt_max_overlaps[gt_max_overlaps==0] = 1e-5    
    # 将与标签有最大IoU的Anchor赋予正样本    
    keep = torch.sum(overlaps.eq(gt_max_overlaps.view(batch_size,                                       
                    1, -1).expand_as(overlaps)), 2)   
    if torch.sum(keep)>0:            
                    labels[keep>0] = 1    
    # 如果一个Anchor最大的IoU大于0.7,视为正样本    
    labels[max_overlaps >= 0.7] = 1

需要注意的是,上述三者的顺序不能随意变动,要保证一个Anchor既符合正样本,也符合负样本时,赋予正样本。并且为了保证这一阶段的召回率,允许多个Anchors对应一个标签,而不允许一个标签对应多个Anchors

3.Anchor的筛选

由于Anchor的总数量接近于2万,并且大部分Anchor的标签都是背景,如果都计算损失的话则正、负样本失去了均衡,不利于网络的收敛。在此,RPN默认选择256个Anchors进行损失的计算,其中最多不超过128个的正样本。如果数量超过了限定值,则进行随机选取。当然, 这里的256与128都可以根据实际情况进行调整,而不是固定死的。

代码语言:python代码运行次数:0复制
def forward(self, input):    
            ······    
            for i in range(batch_size):        
            # 如果正样本数量太多,则进行下采样随机选取        
            if sum_fg[i] > 128:            
                        fg_inds = torch.nonzero(labels[i] == 1).view(-1)            
                        rand_num = torch.from_numpy(np.random.permutation                               
                                    (fg_inds.size(0))).type_as(gt_boxes).long()            
                        disable_inds = fg_inds[rand_num[:fg_inds.size(0)-num_fg]]
                        labels[i][disable_inds] = -1        
            # 负样本同上    
            ······

4.求解回归偏移真值

上一步将每个Anchor赋予正样本或者负样本代表了预测类别的真值,而回归部分的偏移量真值还需要利用Anchor与对应的标签求解得到,具体公式见式4-2。

得到偏移量的真值后,将其保存在bbox_targets中。与此同时,还需要求解两个权值矩阵bbox_inside_weightsbbox_outside_weights,前者是用来设置正样本回归的权重正样本设置为1,负样本设置为0,因为负样本对应的是背景,不需要进行回归;后者的作用则是平衡RPN分类损失与回归损失的权重,在此设置为1/256。

代码语言:python代码运行次数:0复制
def forward(self, input):    
    ······    
    # 选择每一个Anchor对应最大IoU的标签进行偏移计算    
    bbox_targets = _compute_targets_batch(anchors,                 
                gt_boxes.view(-1, 5)[argmax_overlaps.view(-1), :].view(batch_ size, -1, 5))    
    # 设置两个权重向量    
    bbox_inside_weights[labels==1] = 1    
    num_examples = torch.sum(labels[i] >=0)    
    bbox_outside_weights[labels == 1] = 1.0 / examples.item()    
    bbox_outside_weights[labels == 0] = 1.0 / examples.item()

真值的求取部分最后的输出包含了分类的标签label、回归偏移的真值bbox_targets,以及两个权重向量bbox_inside_weights与 bbox_outside_weights。

4.4.5 损失函数设计

有了网络预测值与真值,接下来就可以计算损失了。RPN的损失函数包含分类与回归两部分,具体公式如式(4-4)所示。

RPN的损失函数RPN的损失函数

第一个累加代表了256个筛选出的Anchors的分类损失,pi为每一个 Anchor的类别真值,p*i为每一个Anchor的预测类别。由于RPN的作用是选择出Proposal,并不要求细分出是哪一类前景,因此在这一阶段是二分类,使用的是交叉熵损失。值得注意的是,在F.cross_entropy()函数中集成了Softmax的操作,因此应该传入得分,而非经过Softmax之后的预测值。

第二个累加代表了回归损失,其中bbox_inside_weights实际上起到了 p*i 进行筛选的作用bbox_outside_weights起到了来平衡两部分损失的作用。回归损失使用了smooth L1函数,具体公式如式(4-5)与式(4-6)所示。

回归损失回归损失

从式(4-6)中可以看到,smoothL1函数结合了1阶2阶损失函数, 原因在于,当预测偏移量与真值差距较大时,使用2阶函数时导数太大,模型容易发散而不容易收敛,因此在大于1时采用了导数较小的1阶损失函数。

损失函数的代码接口如下,源代码文件见ib/model/rpn/rpn.py。

代码语言:javascript复制
# 先对scores进行筛选得到256个样本的得分,随后进行交叉熵求解 
self.rpn_loss_cls = F.cross_entropy(rpn_cls_score, rpn_label) 
# 利用smoothL1损失函数进行loss计算 
self.rpn_loss_box = _smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets,
                  rpn_bbox_inside_weights, rpn_bbox_outside_weights, sigma=3,                 
                  dim=[1, 2, 3])

4.4.6 NMS与生成Proposal

完成了损失的计算,RPN的另一个功能就是区域生成,即生成较好的Proposal,以供下一个阶段进行细分类与回归。

NMS生成Proposal的主要过程如图4.8所示,首先生成大小固定的全部Anchors,然后将网络中得到的回归偏移作用到Anchor上使Anchor更加贴近于真值,并修剪超出图像尺寸的Proposal,得到最初的建议区域。在这之后,按照分类网络输出的得分对Anchor排序,保留前12000 个得分高的Anchors。由于一个物体可能会有多个Anchors重叠对应,因此再应用非极大值抑制(NMS)将重叠的框去掉,最后在剩余的Proposal中再次根据RPN的预测得分选择前2000个,作为最终的 Proposal,输出到下一个阶段。

NMS与Proposal的筛选过程如下,源代码文件见 lib/model/rpn/proposal_layer.py。

图4.8 RPN生成Proposal的过程图4.8 RPN生成Proposal的过程
代码语言:python代码运行次数:0复制
def forward(self, input):    
    # 生成Anchor后,首先利用回归网络对Anchor进行偏移修整    
    proposals = bbox_transform_inv(anchors, bbox_deltas, batch_size)    
    # 将超出图像范围的边框修整到图像边界    
    proposals = clip_boxes(proposals, im_info, batch_size)    
    # 利用分类网络的得分对proposal进行排序    
    _, order = torch.sort(scores_keep, 1, True)    
    # 选取前12000个    
    order_single = order_single[:12000]    
    # 进行NMS,在此利用GPU进行计算,提高效率    
    keep_idx_i = nms(torch.cat((proposals_single, scores_single), 1), 0.7,     
    force_cpu=False)    
    # 最终选择前2000个作为最终的Proposal输出    
    keep_idx_i = keep_idx_i[:2000]

4.4.7 筛选Proposal得到RoI

在训练时,上一步生成的Proposal数量为2000个,其中仍然有很多背景框,真正包含物体的仍占少数,因此完全可以针对Proposal进行再一步筛选,过程与RPN中筛选Anchor的过程类似,利用标签与Proposal构建IoU矩阵,通过与标签的重合程度选出256个正负样本。这一步有3个作用:

  • 筛选出了更贴近真实物体的RoI,使送入到后续网络的物体正、负样本更均衡,避免了负样本过多,正样本过少的情况。
  • 减少了送入后续全连接网络的数量,有效减少了计算量
  • 筛选Proposal得到RoI的过程中,由于使用了标签来筛选,因此也为每一个RoI赋予了正、负样本的标签,同时可以在此求得RoI变换到对应标签的偏移量,这样就求得了RCNN部分的真值

具体实现时,首先计算Proposal与所有的物体标签的IoU矩阵,然后根据IoU矩阵的值来筛选出符合条件的正负样本。筛选标准如下:

  • 对于任何一个Proposal,其与所有标签的最大IoU如果大于等于 0.5,则视为正样本。
  • 对于任何一个Proposal,其与所有标签的最大IoU如果大于等于0且小于0.5,则视为负样本。

经过上述标准的筛选,选出的正、负样本数量不一,在此设定正、 负样本的总数为256个,其中正样本的数量为p个。为了控制正、负样本的比例基本满足1:3,在此正样本数量p不超过64,如果超过了64则从正样本中随机选取64个。剩余的数量256-p为负样本的数量,如果超过了256-p则从负样本中随机选取256-p个。

经过上述操作后,选出了最终的256个RoI,并且每一个RoI都赋予 了正样本或者负样本的标签。在此也可以进一步求得每一个RoI的真值,即属于哪一个类别及对应真值物体的偏移量。

筛选Proposal过程的代码示例如下,源代码文件见 lib/model/rpn/proposal_target_layer_cascade.py。

代码语言:python代码运行次数:0复制
def _sample_rois_pytorch(self, all_rois, gt_boxes,                               
                               fg_rois_per_image, rois_per_image, num_class):    
     # 利用Proposal与标签生成IoU矩阵    
     overlaps = bbox_overlaps_batch(all_rois, gt_boxes)    
     # 选择满足条件的正负样本    
     fg_inds = torch.nonzero(max_overlaps[i] >= 0.5).view(-1)    
     bg_inds = torch.nonzero((max_overlaps[i] < 0.5 & max_overlaps[i] >= 0)).    
     view(-1)    
     # 如果正样本超过64个,负样本超过(256-正样本)的数量,则进行下采样随机选取    
     rand_num torch.from_numpy(np.random.permutation                                 
     (fg_num_rois)).type_as(gt_boxes).long()    
     fg_inds = fg_inds[rand_num[:fg_rois_per_this_image]]    
     # 计算每一个Proposal相对于其标签的偏移量,并记录权重    
     bbox_target_data = self._compute_targets_pytorch(rois_batch[:, :, 1:5],    
     gt_rois_batch[:,:,:4])    
     bbox_targets, bbox_inside_weights =         
     self._get_bbox_regression_labels_pytorch(bbox_target_data, labels_ batch, num_classes)

最终返回分类的真值label,回归的偏移真值bbox_targets,以及每一 个Proposal对应的权重bbox_inside_weightsbbox_outside_weights

4.5 RoI Pooling层

上述步骤得到了256个RoI,以及每一个RoI对应的类别与偏移量真值,为了计算损失,还需要计算每一个RoI的预测量。

前面的VGGNet网络已经提供了整张图像的feature map,因此自然联想到可以利用此feature map,将每一个RoI区域对应的特征提取出来,然后接入一个全连接网络,分别预测其RoI的分类与偏移量。

然而,由于RoI是由各种大小宽高不同的Anchors经过偏移修正、筛选等过程生成的,因此其大小不一且带有浮点数,然而后续相连的全连接网络要求输入特征大小维度固定,这就需要有一个模块,能够把各种维度不同的RoI变换到维度相同的特征,以满足后续全连接网络的要求,于是RoI Pooling就产生了。

对RoI进行池化的思想在SPPNet中就已经出现了,只不过在Fast RCNN中提出的RoI Pooling算法利用最近邻差值算法将池化过程进行简化,而在随后的Mask RCNN中进一步提出了RoI Align的算法,利用双线性插值,进一步提升了算法精度。

在此我们举一个例子来讲解这几种算法的思想,假设当前RoI大小为332×332,使用VGGNet的全连接层,其所需的特征向量维度为 512×7×7,由于目前的特征图通道数为512,Pooling的过程就是如何获得7×7大小区域的特征。

1.RoI Pooling简介

RoI Pooling的实现过程如图4.9所示,假设当前的RoI为图4.9中左侧 图像的边框,大小为332×332,为了得到这个RoI的特征图,首先需要将该区域映射到全图的特征图上,由于下采样率为16,因此该区域在特征图上的坐标直接除以16并取整,而对应的大小为332/16=20.75。在此,RoI Pooling的做法是直接将浮点数量化为整数,取整为20×20,也就得到了该RoI的特征。

图4.9 RoI Pooling的实现过程示例图4.9 RoI Pooling的实现过程示例

下一步还要将该20×20区域处理为7×7的特征,然而20/7≈2.857,再 次出现浮点数,RoI Pooling的做法是再次量化取整,将2.857取整为2, 然后以2为步长从左上角开始选取出7×7的区域,这样每个小方格在特征图上都对应2×2的大小。最后,取每个小方格内的最大特征值,作为这个小方格的输出,最终实现了7×7的输出,也完成了池化的过程。

从实现过程中可以看到,RoI本来对应于20.75×20.75的特征图区 域,最后只取了14×14的区域,因此RoI Pooling算法虽然简单,但量化取整带来的偏差势必会影响网络,尤其是回归物体位置的准确率

2.RoI Align简介

RoI Align的思想是使用双线性插值获得坐标为浮点数的点的值,主要过程如图4.10所示,依然将RoI对应到特征图上,但坐标与大小都保留着浮点数,大小为20.75×20.75,不做量化。

图4.10 RoI Align的实现过程示例图4.10 RoI Align的实现过程示例

接下来,将特征图上的20.75×20.75大小均匀分成7×7方格的大小, 中间的点依然保留浮点数。在此选择其中2×2方格为例,在每一个小方格内的特定位置选取4个采样点进行特征采样,如图4.11中每个小方格选择了4个小黑点,然后对这4个黑点的值选择最大值,作为这个方格最终的特征。这4个小黑点的位置与值该如何计算呢?

图4.11 RoI Align双线性插值与池化示例图4.11 RoI Align双线性插值与池化示例

对于黑点的位置,可以将小方格平均分成2×2的4份,然后这4份更小单元的中心点可以作为小黑点的位置

至于如何计算这4个小黑点的值,RoI Align使用了双线性插值的方法。小黑点周围会有特征图上的4个特征点,利用这4个特征点双线性插值出该黑点的值。

由于Align算法最大可能地保留了原始区域的特征,因此Align算法对检测性能有显著的提升,尤其是对于受RoI Pooling影响大的情形,如本身特征区域较小的小物体,改善更为明显。

4.6 全连接RCNN模块

在经过RoI Pooling层之后,特征被池化到了固定的维度,因此接下来可以利用全连接网络进行分类与回归预测量的计算。在训练阶段,最后需要计算预测量与真值的损失并反传优化,而在前向测试阶段,可以直接将预测量加到RoI上,并输出预测量。

4.6.1 RCNN全连接网络

RCNN全连接网络如图4.12所示,4.5节中256个RoI经过池化之后得到固定维度为512×7×7的特征,在此首先将这三个维度延展为一维,因为全连接网络需要将一个RoI的特征全部连接起来。

图4.12 RCNN部分网络计算流程图4.12 RCNN部分网络计算流程

接下来利用VGGNet的两个全连接层,得到长度为4096的256个RoI 特征。为了输出类别与回归的预测,将上述特征分别接入分类与回归的全连接网络。在此默认为21类物体,因此分类网络输出维度为21,回归网络则输出每一个类别下的4个位置偏移量,因此输出维度为84。

值得注意的是,虽然是256个RoI放到了一起计算,但相互之间是独立的,并没有使用到共享特征,因此造成了重复计算,这也是Faster RCNN的一个缺点。RCNN全连接部分的代码接口如下,源代码文件见 lib/model/faster_rcnn/faster_rcnn.py。

代码语言:python代码运行次数:0复制
def forward (self, im_data, im_info, gt_boxes, num_boxes):    
    # 利用VGG的两层分类全连接网络进一步计算     
    pooled_feat = self._head_to_tail(pooled_feat)    
    # 分别计算分类与回归预测值    
    cls_score = self.RCNN_cls_score(pooled_feat)    
    bbox_pred = self.RCNN_bbox_pred(pooled_feat)

4.6.2 损失函数设计

RCNN部分的损失函数计算方法与RPN部分相同,不再重复。只不过在此为21个类别的分类,而RPN部分则是二分类,需要注意回归时至多有64个正样本参与回归计算,负样本不参与回归计算

计算RCNN损失的代码接口如下,源代码文件见 lib/model/faster_rcnn/faster_rcnn.py。

代码语言:javascript复制
# 代码在lib/model/faster_rcnn/faster_rcnn.py文件中    
    # 对256个RoI进行分类损失求解,在此使用交叉熵损失    
    RCNN_loss_cls = F.cross_entropy(cls_score, rois_label)    
    # 利用smoothL1损失函数进行回归loss计算    
    RCNN_loss_box=_smooth_l1_loss(bbox_pred, rois_targets, rois_inside_ ws,rois_outside_ws)

4.7 Faster RCNN的改进算法

图4.13 Faster RCNN的多种改进算法图4.13 Faster RCNN的多种改进算法

Faster RCNN之所以生命力如此强大,应用如此广泛,离不开以下几个特点:

  • 性能优越:Faster RCNN通过两阶网络与RPN,实现了精度较高的物体检测性能。
  • 两阶网络:相比起其他一阶网络,两阶更为精准,尤其是针对高精度、多尺度以及小物体问题上,两阶网络优势更为明显。
  • 通用性与鲁棒性:Faster RCNN在多个数据集及物体任务上效果都很好,对于个人的数据集,往往Fine-tune后就能达到较好的效果。
  • 可优化点很多:Faster RCNN的整个算法框架中可以进行优化的点很多,提供了广阔的算法优化空间。
  • 代码全面:各大深度学习框架都有较好的Faster RCNN源码实现, 使用方便。

当然,原始的Faster RCNN也存在一些缺点,而这些缺点也恰好成 为了后续学者优化改进的方向,总体来看,可以从以下6个方面考虑:

  • 卷积提取网络:无论是VGGNet还是ResNet,其特征图仅仅是单层的,分辨率通常也较小,这些都不利于小物体及多尺度的物体检测,因此多层融合的特征图、增大特征图的分辨率等都是可以优化的方向。
  • NMS:在RPN产生Proposal时为了避免重叠的框,使用了NMS,并以分类得分为筛选标准。但NMS本身的过滤对于遮挡物体不是特别友好,本身属于两个物体的Proposal有可能因为NMS而过滤为1个,造成漏检,因此改进优化NMS是可以带来检测性能提升的。
  • RoI Pooling:Faster RCNN的原始RoI Pooling两次取整带来了精度的损失,因此后续Mask RCNN针对此Pooling进行了改进,提升了定位的精度。
  • 全连接:原始Faster RCNN最后使用全连接网络,这部分全连接网络占据了网络的大部分参数,并且RoI Pooling后每一个RoI都要经过一 遍全连接网络,没有共享计算,而如今全卷积网络是一个发展趋势,如何取代这部分全连接网络,实现更轻量的网络是需要研究的方向。
  • 正负样本:在RPN及RCNN部分,都是通过超参数限制正、负样本的数量,以保证正、负样本的均衡。而对于不同任务与数据,这种 正、负样本均衡方法是否都是最有效的,也是一个研究的方向。
  • 两阶网络:Faster RCNN的RPN与RCNN两个阶段分工明确,带来了精度的提升,但速度相对较慢,实际实现上还没有达到实时。因此, 网络阶数也是一个值得探讨的问题,如单阶是否可以使网络的速度更快,更多阶的网络是否可以进一步提升网络的精度等。

4.7.2 特征融合:HyperNet

卷积神经网络的特点是,深层的特征体现了强语义特征,有利于进行分类与识别,而浅层的特征分辨率高,有利于进行目标的定位。原始的Faster RCNN方法仅仅利用了单层的feature map(例如VGGNet的 conv5-3),对于小尺度目标的检测较差,同时高IoU阈值时,边框定位的精度也不高。

在2016 CVPR上发表的HyperNet方法认为单独一个feature map层的特征不足以覆盖RoI的全部特性,因此提出了一个精心设计的网络结构,融合了浅、中、深3个层次的特征,取长补短,在处理好区域生成的同时,实现了较好的物体检测效果。

图4.14 HyperNet卷积提取网络结构图4.14 HyperNet卷积提取网络结构

HyperNet提出的特征提取网络结构如图4.14所示,以VGGNet作为基础网络,分别从第1、3、5个卷积组后提取出特征,这3个特征分别对应着浅层、中层与深层的信息。然后,对浅层的特征进行最大值池化, 对深层的特征进行反卷积,使得二者的分辨率都为原图大小的1/4,与 中层的分辨率相同,方便进行融合。得到3个特征图后,再接一个5×5的卷积以减少特征通道数,得到通道数为42的特征。

在三层的特征融合前,需要先经过一个LRN(Local Response Normalization)处理,LRN层借鉴了神经生物学中的侧抑制概念,即激活的神经元抑制周围的神经元,在此的作用是增加泛化能力,做平滑处理

LPN介绍:https://www.jianshu.com/p/3ddc4bf1d400

最后将特征沿着通道数维度拼接到一起。3个通道数为42的特征拼接一起后形成通道数为126的特征,作为最终输出。

HyperNet融合了多层特征的网络有如下3点好处:

  1. 深层、中层、浅层的特征融合到一起,优势互补,利于提升检测精度。
  2. 特征图分辨率为1/4,特征细节更丰富,利于检测小物体
  3. 在区域生成与后续预测前计算好了特征,没有任何的冗余计算。

HyperNet实现了一个轻量化网络来实现候选区域生成。具体方法是,首先在特征图上生成3万个不同大小与宽高的候选框,经过RoI Pooling获得候选框的特征,再接卷积及相应的分类回归网络,进而可以得到预测值,结合标签就可以筛选出合适的Proposal。可以看出,这里的实现方法与Faster RCNN的RPN方法很相似,只不过先进行了RoI Pooling,再选择候选区域

HyperNet后续的网络与Faster RCNN也基本相同,接入全连接网络完成最后的分类与回归。不同的地方是,HyperNet先使用了一个卷积降低通道数,并且Dropout的比例从0.5调整到了0.25。

由于提前使用了RoI Pooling,导致众多候选框特征都要经过一遍此Pooling层,计算量较大,为了加速,可以在Pooling前使用一个3×3卷积降低通道数为4,这种方法在大幅度降低计算量的前提下,基本没有精度的损失。

总体来看,HyperNet最大的特点还是提出了多层融合的特征,因此,其检测小物体的能力更加出色,并且由于特征图分辨率较大,物体的定位也更精准。此外,由于其出色的特征提取,HyperNet的Proposal的质量很高,前100个Proposal就可以实现97%的召回率。

值得注意,HyperNet使用到了反卷积来实现上采样,以扩大尺寸。 通常来讲,上采样可以有3种实现方法:双线性插值、反池化 (Unpooling)与反卷积。反卷积也叫转置卷积,但并非正常卷积的完全可逆过程。具体实现过程是,先按照一定的比例在特征图上补充0, 然后旋转卷积核,再进行正向的卷积。反卷积方法经常被用在图像分割中,以扩大特征图尺寸。

反卷积

https://blog.csdn.net/u014722627/article/details/60574260?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

4.7.3 实例分割:Mask RCNN

基于Faster RCNN,何凯明进一步提出了新的实例分割网络Mask RCNN,该方法在高效地完成物体检测的同时也实现了高质量的实例分割,获得了ICCV 2017的最佳论文。

Mask RCNN的网络结构如图4.15所示,可以看到其结构与Faster RCNN非常类似,但有3点主要区别:

  • 在基础网络中采用了较为优秀的ResNet-FPN结构,多层特征图有利于多尺度物体及小物体的检测。
  • 提出了RoI Align方法来替代RoI Pooling,原因是RoI Pooling的取整做法损失了一些精度,而这对于分割任务来说较为致命。
  • 得到感兴趣区域的特征后,在原来分类与回归的基础上,增加了 一个Mask分支预测每一个像素的类别
图4.15 Mask RCNN网络结构图图4.15 Mask RCNN网络结构图
  1. 特征提取网络

基于ResNet的FPN基础网络也是一个多层特征结合的结构,包含了 自下而上、自上而下及横向连接3个部分,这种结构可以将浅层、中 层、深层的特征融合起来,使得特征同时具备强语义性强空间性

原始的FPN会输出P2、P3、P4与P5 4个阶段的特征图,但在Mask RCNN中又增加了一个P6。将P5进行最大值池化即可得到P6,目的是获得更大感受野的特征,该阶段仅仅用在RPN网络中。

Mask RCNN的做法与FPN论文中的方法一致,从合适尺度的特征图中切出RoI,大的RoI对应到高语义的特征图中,如P5,小的RoI对应到高分辨率的特征图中,如P3。式(4-7)中给出了精确的计算公式:

公式中的224代表着ImageNet预训练图片的大小,k0默认值为4,表示大小为224×224的RoI应该对应的层级为4,计算后做取整处理。这样的分配方法保证了大的RoI从高语义的特征图上产生,有利于检测大尺度物体,小的RoI从高分辨率的特征图上产生,有利于小物体的检测。

2.RoI Align部分

Maks RCNN提出的RoI Align取消了取整操作,而是保留所有的浮点,然后通过双线性插值的方法获得多个采样点的值,再将多个采样点进行最大值的池化,即可得到该点最终的值。

3.损失任务设计

在得到感兴趣区域的特征后,Mask RCNN增加了Mask分支来进行图像分割,确定每一个像素具体属于哪一个类别。具体实现时,采用了 FCN(Fully Convolutional Network)的网络结构,利用卷积与反卷积构建端到端的网络,最后对每一个像素分类,实现了较好的分割效果。

假设物体检测任务的类别是21类,则Mask分支对于每一个RoI,输出的维度为21×m×m,其中m×m表示mask的大小,每一个元素都是二值的,相当于得到了21个二值的mask。在训练时,如果当前RoI的标签类别是5,则只有第5个类别的mask参与计算,其他的类别并不参与计算。 这种方法可以有效地避免类间竞争,将分类的任务交给更为专业的分类分支去处理。

增加了Mask分支后,损失函数变为了3部分,如式(4-8)所示。

损失函数损失函数

公式中前两部分Lcls、Lbox与Faster RCNN中相同,最后一部分Lmask 代表了分割的损失。对mask上的每一个像素应用Sigmoid函数,送到交叉熵损失中,最后取所有像素损失的平均值作为Lnask。

Mask RCNN算法简洁明了,在物体检测与实例分割领域都能得到较高的精度,在实际应用中,尤其是涉及多任务时,可以采用Mask RCNN算法。

4.7.4 全卷积网络:R-FCN

Faster RCNN在RoI Pooling后采用了全连接网络来得到分类与回归的预测,这部分全连接网络占据了整个网络结构的大部分参数,而目前越来越多的全卷积网络证明了不使用全连接网络效果会更好,以适应各种输入尺度的图片

一个很自然的想法就是去掉RoI Pooling后的全连接,直接连接到分类与回归的网络中,但通过实验发现这种方法检测的效果很差,其中一 个原因就是基础的卷积网络是针对分类设计的,具有平移不变性,对位置不敏感,而物体检测则对位置敏感。

针对上述“痛点”,微软亚洲研究院的代季峰团队提出了RFCN(Region-based Fully Convolutional Networks)算法,利用一个精心设计的位置敏感得分图(position-sensitive score maps)实现了对位置的敏感,并且采用了全卷积网络,大大减少了网络的参数量。首先R-FCN采用了ResNet-101 网络作为Backbone,并在原始的100个卷积层后增加了一个1×1卷积,将通道数降低为1024

图4.16 R-FCN网络结构图图4.16 R-FCN网络结构图

此外,为了增大后续特征图的尺寸,R-FCN将ResNet-101的下采样率从32降到了16。具体做法是,在第5个卷积组里将卷积的步长从2变为1,同时在这个阶段的卷积使用空洞数为2的空洞卷积以扩大感受野。降低步长增加空洞卷积是一种常用的方法,可以在保持特征图尺寸的同时,增大感受野

在特征图上进行1×1卷积,可以得到位置敏感得分图,其通道数为 k*k*(c 1)。这里的c代表物体类别,一般需要再加上背景这一类别。k的含义是将RoI划分为k*k个区域,如图4.17分别展示了k为1、3、5的情况。例如当k=3时,可以将RoI分为左上、中上、右上等9个区域,每个区域对特征区域的信息敏感。因此,位置敏感得分图的通道包含了所有9个区域内所有类别的信息

图4.17 RoI划分为k2个区域示意图图4.17 RoI划分为k2个区域示意图

对于一个位置敏感得分图上的点,假设其坐标为m×n,通道在右上区域,类别为人,则该点表示当前位置属于人并且在人这个“物体”的右上区域的特征,因此这样就包含了位置信息

在RPN提供了一个感兴趣区域后,对应到位置敏感得分图上,首先将RoI划分为k×k个网格,如图4.18所示,左侧为将9个不同区域展开后的RoI特征,9个区域分别对应着不同的位置,在Pooling时首先选取其所在区域的对应位置的特征,例如左上区域只选取其左上角的特征,右下区域只选取右下角的特征,选取后对区域内求均值,最终可形成右侧的 一个c 1维的k×k特征图。

更好的解释:https://zhuanlan.zhihu.com/p/30867916

图4.18 位置敏感得分图的Pooling过程图4.18 位置敏感得分图的Pooling过程

接下来再对这个c 1维的k×k特征进行逐通道求和,即可得到c 1维的向量,最后进行Softmax即可完成这个RoI的分类预测。

图4.19 位置敏感得分图的Pooling过程图4.19 位置敏感得分图的Pooling过程

至于RoI的位置回归,则与分类很相似,只不过位置敏感得分图的通道数为k*k*(c 1),而回归的敏感回归图的通道数为k2×4,按照相同的方法进行Pooling,可形成通道数为4的k×k特征求和可得到1×4的向量, 即为回归的预测。

由于R-FCN去掉了全连接层,并且整个网络都是共享计算的,因此速度很快。此外,由于位置敏感得分图的存在,引入了位置信息,因此 R-FCN的检测效果也更好。

4.7.5 级联网络:Cascade RCNN

在前面的讲解中可以得知,在得到一个RoI后,Faster RCNN通过 RoI与标签的IoU值来判断该RoI是正样本还是负样本,默认的IoU阈值为 0.5这个阈值是一个超参数,对于检测的精度有较大影响

如何选择合适的阈值是一个矛盾的问题。一方面,阈值越高,选出的RoI会更接近真实物体,检测器的定位会更加准确,但此时符合条件的RoI会变少,正、负样本会更加不均衡,容易导致训练过拟合;另一 方面,阈值越低,正样本会更多,有利于模型训练,但这时误检也会增多,从而增大了分类的误差

对于阈值的问题,通过实验可以发现两个现象:

  • 一个检测器如果采用某个阈值界定正负样本时,那么当输入Proposal的IoU在这个阈值附近时,检测效果要比基于其他阈值时好,也就是,很难让一个在指定阈值界定正、负样本的检测模型对所有IoU的输入Proposal检测效果都最佳
  • 经过回归之后的候选框与标签之间的IoU会有所提升

基于以上结果,2018年CVPR上的Cascade RCNN算法通过级联多个检测器来不断优化结果,每个检测器都基于不同的IoU阈值来界定正负样本,前一个检测器的输出作为后一个检测器的输入,并且检测器越靠后,IoU的阈值越高。

级联检测器可以有多种形式,如图4.19所示为迭代式的边框回归模型示意图,图中的Pooling代表了RoI Pooling过程,H1表示RCNN部分网络,C与B分别表示分类与回归部分网络。从图中可以看出,这种方法将前一个回归网络输出的边框作为下一个检测器的输入继续进行回归, 连续迭代3次才得到结果。

图4.19 迭代式边框回归模型示意图图4.19 迭代式边框回归模型示意图

从前面的实验可以得知,经过一个固定IoU阈值的检测器后,边框的IoU会提升,分布也发生了变化,即越来越靠近真实物体。如果下一 个检测器仍然还是这个IoU阈值的话,显然不是一个最优的选择,这也是上述这种级联器的问题所在。

图4.20是另一种多个检测器的组合方式,称为Integral Loss。图中 H1、H2与H3分别代表不同的IoU阈值界定正负样本的检测器,当阈值较高时,预测的边框会更为精准,但会损失一些正样本。这种方法中多个检测器相互独立,没有反馈优化的思想,仅仅是利用了多个IoU阈值的检测器。

图4.21的结构则是Cascade RCNN采用的方法,可以看到每一个检测器的边框输出作为下一个检测器的输入,并且检测器的IoU阈值是逐渐提升的,因此这种方法可以逐步过滤掉一些误检框,并且提升边框的定位精度

总体来看,Cascade RCNN算法深入探讨了IoU阈值对检测器性能的影响,并且在不增加任何tricks的前提下,在多个数据集上都有了明显的精度提升,是一个性能优越的高精度物体检测器。

图4.20 Integral loss模型示意图图4.20 Integral loss模型示意图
图4.21 Cascade RCNN级联检测器示意图图4.21 Cascade RCNN级联检测器示意图

0 人点赞