本文主要参考了Faster R-CNN具体实现详解和Object Detection and Classification using R-CNNs。
在这篇文章中,将详细描述最近引入的基于深度学习的对象检测和分类方法,R-CNN(Regions with CNN features)是如何工作的。事实证明,R-CNN在检测和分类自然图像中的物体方面非常有效,其mAP远高于之前的方法。R-CNN方法在Ross Girshick等人的以下系列论文中描述。
- R-CNN(https://arxiv.org/abs/1311.2524)
- Fast R-CNN(https://arxiv.org/abs/1504.08083)
- Faster R-CNN(https://arxiv.org/abs/1506.01497)
这篇文章描述了最后一篇论文中R-CNN方法的最终版本。我首先考虑介绍该方法从第一次引入到最终版本的演变,然而事实表明这工作量太大。所以决定直接详细描述最终版本。
幸运的是,在TensorFlow,PyTorch和其他机器学习库中,网上有许多R-CNN算法的实现。本文参考了以下实现:https://github.com/ruotianluo/pytorch-faster-rcnn
本文中使用的大部分术语(例如,不同层的名称)遵循代码中使用的术语。理解本文中提供的信息应该可以更容易地遵循PyTorch实现并进行自己的修改。
文章组织
- 第1部分 - 图像预处理:在本节中,我们将描述应用于输入图像的预处理步骤。这些步骤包括减去平均像素值和缩放图像。训练和推理之间的预处理步骤必须相同。
- 第2节 - 网络组织:在本节中,我们将描述网络的三个主要组成部分——“head”network,region proposal network(RPN)和分类网络。
- 第3节 - 实现细节(训练):这是该文章最长的部分,详细描述了训练R-CNN网络所涉及的步骤。
- 第4节 - 实现细节(推理):在本节中,我们将描述在推理过程涉及的步骤,使用训练好的R-CNN网络来识别有希望的区域并对这些区域中的对象进行分类。
- 附录:这里我们将介绍R-CNN运行过程中一些常用算法的细节,如非极大值抑制和Resnet50架构的细节。
图像预处理
在将图片送入网络之前首先执行如下的网络预处理步骤。在训练和推理过程中,以下步骤必须一致。均值向量(大小$1\times3$,每一个图像通道一个均值)并非当前图像像素值的均值,而是针对所有训练和测试图片所设置的一个统一的初始值。图像预处理流程如下:
默认的$targetSize$和$maxSize$为600和1000。
网络组织
R-CNN使用神经网络主要解决如下两个问题:
- 识别输入图片中可能包含前景目标的区域(Region of Interest - RoI)
- 计算每一个RoI中的类别概率分布——例如,计算RoI中包含特定类别的目标的概率,在此基础上,可以选择具有最高概率的类别作为分类结果。
R-CNN主要包含三种类型的神经网络:
- Head
- Region Proposal Network (RPN)
- Classification Network(分类网络)
R-CNN使用预训练网络(例如ResNet)的前几层从输入图片中提取特征,这一做法由迁移学习理论作为支持(将同一个数据集上训练得到的网络用于不同的问题是可能的)。网络的前几层检测一些通用的特征(如,边、颜色块等在不同的问题中都具有较好的区分性的特征),而后几层学习到的更多是与特定问题相关的高层特征。在我们搭建的网路中,可以直接将后面几层移除或者在反向传播过程中对其参数进行微调。这些从预训练的网络的前几层迁移过来的层构成了”head”网络。
由”head”网络产生的卷积特征图将被送入RPN网络中,RPN网络使用一系列的卷积层和全连接层产生可能存在前景目标的RoI区域。接着将使用这些RoI区域从“head”网络产生的特征图中裁剪出相应的特征图区域,称为“crop pooling”。由“crop pooling”得到的特征图区域将被送入分类网络,进而经过学习得到该RoI区域所包含的目标种类。
另一方面,ResNet的权重也可以使用如下方式进行初始化:
1 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels |
网络结构
下图分别展示了上述几种不同的网络模块,图中给出了每一层网络输出和输入特征的大小,这将有助于理解网络中的特征是如何进行转换的,$w$、$h$表示经过预处理后的输入图片的大小。
实现细节:训练
在本节将详细介绍训练R-CNN所涉及到的步骤。一旦理解了训练的流程,理解推理过程将很容易,因为推理只用到了训练过程的一个子集。训练的目标是调整RPN、分类网络的权重以及微调Head(从预训练模型如Resnet中初始化)的权重。RPN网络的任务是产生RoIs区域,分类网络的任务是对每一个RoI给定一个类别分数。为了训练这些网络,我们需要得到相应的ground truths(即图片中所出现的目标的bounding boxes的坐标以及这些目标的类别)。这些信息已经由数据集的标定文件给出,这里有一些常用的通用数据集:
第一个常用数据集:PASCAL VOC。VOC2007数据集有20类,有9963张图片,共有24640个标注。
- Person: person
- Animal: bird, cat, cow, dog, horse, sheep
- Vehicle: aeroplane, bicycle, boat, bus, car, motorbike, train
- Indoor: bottle, chair, dining table, potted plant, sofa, tv/monitor
第二个常用数据集:COCO。该数据集包含超过200K个标记图片,共有90类。
本文章使用较小的Pascal VOC 2007数据集进行训练。R-CNN能够同时训练RPN网络和分类网络。
首先介绍两个常见概念:
概念一:Bounding Boxes Regression Coefficients(也称为regression coefficients或regression targets)。R-CNN的目标之一就是产生与目标边界尽可能匹配的bounding boxes。R-CNN通过使用一组“regression coefficients”对给定的bounding boxes(给定左上角坐标、宽、高)进行调整来得到匹配的bounding boxes。回归系数由如下方式得出:
分别将目标(Target,Ground Truth)bounding boxes和原始(Origin,RPN)bounding boxes的左上角坐标表示为:$T_x, T_y, O_x, O_y$,width/height表示为$T_w, T_h, O_w, O_h$。那么“regression targets”(将原始bounding boxes转换为目标bounding boxes的函数系数)计算公式为:
上述方程是可逆的,给定“regression coefficients ”以及原始bounding boxes的左上角坐标、宽和高,便可以计算得到目标bounding boxes的左上角坐标、宽和高。注意,“regression coefficients ”对于没有剪切的仿射变换是不变的。这一性质在计算分类损失时非常重要。因为目标的“regression coefficients ”在原始长宽比例的图片上计算得到的,而分类网络输出的“regression coefficients ”是在正方形特征图(1:1的长宽比)上经过ROI pooling后计算得到的。当我们在下面讨论分类损失时,这一点将变得更加清楚。
下面在学习Bounding Boxes Regression Coefficients时,对应的真实标记为上面的$t_x,t_y,t_w,t_h$。所以下面要介绍的回归网络分支的输出就是每个anchor的平移量和变换尺度$t_x,t_y,t_w,t_h$,显然即可用来修正Anchor位置了。简单来说,可以理解Bounding Boxes Regression Coefficients为每个anchor的平移量和变换尺度$t_x,t_y,t_w,t_h$。
我们记学习到Bounding Boxes Regression Coefficients记作$d_x(A),d_y(A),d_w(A),d_h(A)$。当输入的原始bounding boxes与目标bounding boxes相差较小时,可以认为这种变换是一种线性变换, 那么就可以用线性回归来建模对窗口进行微调(注意,只有当原始bounding boxes与目标bounding boxes比较接近时,才能使用线性回归模型,否则就是复杂的非线性问题了)。
线性回归就是给定输入的特征向量$X,$ 学习一组参数$W$, 使得经过线性回归后的值跟真实值$Y$非常接近,即$Y=WX$。对于该问题,输入$X$是cnn feature map,定义为$\phi$,同时输入$t_x,t_y,t_w,t_h$,输出是$d_x(A),d_y(A),d_w(A),d_h(A)$这四种变换。目标函数为:
其中$\phi(A)$是对应anchor的feature map组成的特征向量,$W_$是需要学习的参数,$d_(A)$是得到的预测值($$表示$x,y,w,h$,也就是每一个变换对应一个上述目标函数)。为了让预测$d_(A)$和真实值$t_*$差距最小,设计$L1$损失函数:
函数的优化目标为:
为了方便描述,这里以$L1$损失为例介绍,而真实情况中一般使用$smooth_L1$损失。
总结一下:对于训练bouding box regression网络回归分支,输入是cnn feature $\phi$,监督信号是Anchor与GT的差距$t_x,t_y,t_w,t_h$,即训练目标是:输入$\phi$的情况下使网络输出与监督信号尽可能接近。那么当bouding box regression工作时,当输出$\phi$时,回归网络分支的输出就是每个Anchor的平移量和变换尺度$t_x,t_y,t_w,t_h$,显然即可用来修正Anchor的位置了。
概念2:Intersection over Union (IoU) Overlap。我们需要一种度量给定的bounding boxes与另一个bounding boxes的接近程度(与所使用的度量bounding boxes大小的单位无关)。这一度量方法需要满足:当两个bounding boxes完全重合时,结果为1;当两个bounding boxes完全不重合时,结果为0;同时要易于计算。常用的度量方式为交并比,计算方式如下所示:
有了这些初步准备,现在让我们深入了解训练R-CNN的实施细节。在实现中,R-CNN被分解为几个层,如下所示。一个封装了一系列逻辑步骤(例如,数据的流通)的层以及其他步骤,诸如bounding boxes重合度的比较、non-maxima suppression(非最大值抑制)等其他步骤。
作者的原图在这里可以找到,但是个人觉得这个图不太正确,并且不够清晰,下面是我绘制的个人理解图,如果有错误,还请大家能够指出来。
- Anchor Generation Layer: 这一层生成固定数量的“anchors”(边界框),首先生成9个不同比例和宽高比的anchors,然后在输入图像的均匀间隔网格点上复制这些“anchors”。即这些选中的格子上都包含9个不同尺度和宽高比的anchors。
- Proposal Layer:根据bounding box regression coefficients对anchors进行变换得到变换后的anchors。然后以每一个anchor属于前景目标的分数为依据,对anchors进行非极大抑制,以对anchors的数目进行进一步的微调。
- Anchor Target Layer: Anchor Target Layer的目的是产生一些好的anchors以及相对应的前景/背景类标、target regression coefficients,以用于RPN网络的训练。该层的输出仅用于训练RPN网络,不用于分类层的训练。给定由anchors Generation Layer生成的anchors,anchor target layer将对好的前景/背景anchors进行识别。其中与真实box的重合度高于设定阈值的anchors将被识别为好的前景anchors;与真实box的重合度低于某一阈值的anchors将被识别为背景anchors。anchor target layer也产生一系列bounding box的回归数,即每个目标anchor到最近的bounding box的距离。这些回归值只对前景框有意义,因为背景框没有“closest bounding box”的概念。
- RPN Loss: 在训练RPN网络时,RPN Loss是被最小化的。由以下两部分组成:
- RPN所产生的bounding boxes中被正确划分为前景/背景的比例
- 预测的bounding boxe与目标bounding boxes之间的“regression coefficients”距离
- Proposal Target Layer: Proposal Target Layer的目标是删除一些由proposal layer产生的anchors,生成特定于类别的bounding box regression targets,用于训练分类层产生良好的类标签和回归目标。
- ROI Pooling Layer: 实现空间转换网络,给定proposal target layer产生的proposal区域的bounding box坐标,对输入特征图进行采样。这些坐标通常不会位于整数边界上,因此需要基于插值的采样。
- Classification Layer: 分类层以ROI Pooling layer的输出作为输入,将其通过一系列的卷积层后,送入两层全连接层,第一层对于每一个region proposal分别产生类别概率分布;第二层产生一组类别相关的bounding box回归值。
- Classification Loss: 与RPN Loss相似,在训练分类层时会最小化Classification Loss。在反向传播的过程中,误差的梯度同样会流入RPN网络,所以对分类层进行训练时同样也会修改RPN网络的权重。分类误差由以下部分构成:
- 由RPN产生的bounding boxes被正确分类的比例
- 预测与目标regression coefficients之间的距离
下面将详细介绍每一层。
Anchor Generation Layer
针对整幅输入图像,产生一系列的具有不同尺度和比例的bounding boxes,即anchor boxes。对于所有图片来说,产生的anchor boxes是一样的,例如,与图片的内容无关。Anchor boxes中的一些将包含有目标,而大多数不包含目标。RPN的目标则是识别好的anchor boxes(那些更有可能包含前景目标的anchor)以及产生bounding boxes regression coefficients(将anchors转换为更好的bounding boxes,更紧密的贴合前景目标)。
下图展示了如何生成这些anchor boxes。下图中,输入图像经过resize后,得到的尺寸为800x600,而均匀采样的间隔为16,等于网络总步长(Resnet下采样16倍)。因为Resnet下采样16倍,最终得到的特征图大小为$\frac{800}{16} \times \frac{600}{16}=1900$。所以也可以理解为在最终特征图的每个点上都设置9个anchors。
注:关于上面的anchors size,其实是根据检测图像设置的。在Python demo中,会把任意大小的输入图像reshape成800x600(记作M=800,N=600)。再回头来看anchors的大小,anchors中长宽1:2中最大为352x704,长宽2:1中最大736x384,基本是cover了800x600的各个尺度和形状。
那么为每个点生成的这9个anchors是做什么的呢?借用Faster RCNN论文中的原图,如下图,遍历Head网络获得的feature maps,为每一个点都配备这9种anchors作为初始的检测框。这样做获得检测框很不准确,不用担心,后面还有2次bounding box regression可以修正检测框位置。
解释一下上面这张图的数字。
- 在原文中使用的是ZF model中,其Head网络中最后的conv5层num_output=256,对应生成256张特征图,所以相当于feature map每个点都是256-dimensions
- 在conv5之后,做了rpn_conv/3x3卷积且num_output=256,相当于每个点又融合了周围3x3的空间信息(猜测这样做也许更鲁棒?),同时256-d不变
- 假设在conv5 feature map中每个点上有k个anchor(默认k=9),而每个anchor要分positive和negative,所以每个点由256d feature转化为cls=2k scores;而每个anchor都有(x, y, w, h)对应4个偏移量,所以reg=4k coordinates
- 补充一点,全部anchors拿去训练太多了,训练程序会在合适的anchors中随机选取128个postive anchors+128个negative anchors进行训练(什么是合适的anchors下文有解释)
注意,在本文讲解中使用的VGG conv5 num_output=512,所以是512d,其他类似。
Region Proposal Layer
目标检测方法需要产生一组稀疏或稠密特征的“region proposal system”作为输入。R-CNN的第一个版本中使用selective search method产生region proposal。在Faster R-CNN中,基于滑窗的技术被用于产生一系列稠密的候选框。接着RPN依据区域包含目标的概率对region proposal进行评分。Region Proposal Layer有两个目的:
- 从一系列的anchor中,识别出前景和背景目标。
- 通过使用一组“regression coefficients”,对anchor的坐标和宽高进行调整,以得到更为精确的anchors(使其更加匹配真正的目标)。
Region Proposal Layer包含Region Proposal Network以及三个Layer:Proposal Layer、Anchor Target Layer 和Proposal Target Layer。具体细节描述如下:
Region Proposal Network
RPN的输入为head网络产生的特征图,将该特征图经过一层卷积层(rpn_net)进行处理,后接ReLU激活函数。激活后的特征图再分别通过两个并行的大小为1x1的卷积层,分别产生前景/背景类别分数和相应的boundding boxes regression coefficients。Head networks的步长与产生anchors时的步长长度相匹配。所以anchor boxes的数量与region proposal network产生的信息是一一对应的(anchor boxes数量=类别得分数量=bounding box regression coefficients数量=$\frac{w}{16}\times\frac{h}{16}\times9$)。
Proposal Layer
Proposal Layer以anchor generation layer产生的anchors为输入,依据各anchors包含前景目标的概率对anchors进行non-maximum suppression,以达到减少anchors数目的目的。同时,依据由RPN产生的regression coefficients对anchor boxes进行变换得到对应的transformed bounding boxes。
Anchor Target Layer
anchor target layer的目的是选择能够用来训练RPN网络的好的anchors:
- 更好地区别前景和背景区域
- 对于前景boxes产生好的bounding box regression coefficients
在进一步讲解Anchor Target Layer之前,我们将首先了解RPN损失是如何计算的。
Calculating RPN Loss
我们已经知道RPN层的目标是产生好的bounding boxes。为了达到这一目标,RPN必须学会从给定的anchor boxes中区分出前景和背景,并计算regression coefficients以对前景anchor boxes的位置、宽和高进行修正,使其更好地匹配前景目标。RPN损失正是以这种方式使得网络学到更好的行为。
RPN损失可以看作分类损失和bounding box regression损失之和。分类损失使用交叉熵损失对未被正确分类的boxes进行惩罚,回归损失使用真实边框regression coefficients(使用与前景anchor boxes最为匹配的ground truth boxes计算得到)与预测的regression coefficients(由RPN网络结构中的rpn_bbox_pred_net给出)之间的距离函数计算得出。
分类损失:
Bounding Box Regression Loss:
将所有前景anchor的回归损失相加。计算背景anchor的回归损失没有意义,因为不存在ground truths与背景anchor匹配。回归损失计算如下:
这展示了给定前景anchor如何计算回归损失。我们首先计算由预测(由RPN网络得到)与目标(由离anchor box最近的真实box得到)regression coefficients的差别。这有四个元素:对应左上角的坐标和bounding box的宽度/高度。$L1$平滑函数定义如下:
上式中$\sigma$任意选定(代码中设定为3)。注意,在Python实现中,表示前景目标的mask array(bbox_inside_weights)以向量运算的形式来计算损失,以避免for-if循环。
因而,为了计算损失,我们需要计算如下量:
- 真实类标(前景或背景)以及anchor boxes的分数
- 前景anchor boxes的target regression coefficients
下面将展示anchor target layer是如何计算得出上述量的。首先选择出在图片内部的anchor boxes;接着,通过计算图像内的所有anchor boxes与所有ground truth boxes的IoU来选出好的前景boxes。基于重合度,以下两类boxes被标记为前景:
- typeA:对于每一个ground truth box,与其有着最大IoU重合度的前景boxes(可能有多个)
- typeB:与一些ground truth boxes的最大IoU重合度超过了设定阈值的anchor box
下图展示了这些boxes:
注意,只有与一些ground truth boxes的重合度超过给定的阈值的anchor boxes才会被选定为前景框。这一做法的目的是为了避免RPN进行一些无谓的学习任务(学习一些与最匹配的ground truth boxes相距较远的anchor boxes)。同样,重合度低于负样本阈值的anchor boxes将被归类为背景框。并不是所有未被划分为前景框的目标都会被划分为背景框。这些既未被划分为前景框,也未被划分未背景框的anchor boxes是不被关心的。在计算RPN损失时,不计算这些框。
有两个参数与我们最终得到的背景目标和前景目标的总数有关,分别是前景目标和背景目标的总数、前景目标占两者的比例。如果通过测试的前景目标的数目超过了阈值,我们便将这些超过部分的前景框随机标记为“don’t care”,同样,背景框中超出的部分将被标记为“don’t care”。
接着,我们计算前景框和与其相对应有最大重合度的ground truths boxes的bounding box regression coefficients。这一步是很容易的,依据给定公式进行计算即可。
这就结束了我们对anchor target layer的讨论。总结该层的输入、输出如下:
参数:
- TRAIN.RPN_POSITIVE_OVERLAP:确定anchor box是否是好的前景框 (Default: 0.7)
- TRAIN.RPN_NEGATIVE_OVERLAP:如果一个anchor与最匹配的ground truth box的重合度小于该阈值,则将其标定为背景。阈值大于RPN_NEGATIVE_OVERLAP但小于RPN_POSITIVE_OVERLAP的框被标记为“don’t care”。(Default: 0.3)
- TRAIN.RPN_BATCHSIZE:前景和背景anchors的总数。 (default: 256)
- TRAIN.RPN_FG_FRACTION:前景框所占比例 (default: 0.5)。如果前景框的数目大于
TRAIN.RPN_BATCHSIZE×TRAIN.RPN_FG_FRACTION,超出的部分将被标记为 (随机选择索引) “don’t care”。
输入:
- RPN Network Outputs (predicted foreground/background class labels, regression coefficients)
- Anchor boxes (由anchor generation layer生成)
- Ground truth boxes
输出:
- 好的foreground/background boxes以及相关类标
- Target regression coefficients
其它几层,proposal target layer、RoI pooling layer、classfication layer用于产生计算分类损失所需的信息。正如我们介绍anchor target layer那样,将首先介绍计算分类层损失所需的信息。
Calculating Classification Layer Loss
与RPN损失类似,分类层损失可以分为两部分——分类损失和bounding box regression loss。
RPN层与分类层的主要不同在于:RPN解决两分类问题——前景和背景,分类层需要处理所有的目标类别(外加背景类)。
分类损失等于真实类别与预测类别之间的交叉熵损失,计算方式如下:
这里的bounding boxes regression loss的计算方式与RPN中的计算方式类似,除了这里的regression coefficients是与类别相关的。网络针对每一个目标种类计算regression coefficients。很明显,target regression coefficients只对正确的类别有效,正确的类别即与给定的anchor box有着最大的重合度的ground truth bounding box的类别。在计算回归系数损失时,使用为每一个anchor box标定正确类别的mask array。不正确的类别的regression coefficients则被忽略。mask矩阵的使用避免了复杂的for循环,而采用矩阵乘法的形式,更为高效。
在计算classification layer loss需要以下数值:
- 预测出的类标及bounding box regression coefficients(由分类网络输出)。
- 每一个anchor box的类别
- Target bounding box regression coefficients
现在让我们看看这些数值如何在proposal target and classification layers中被计算。
Proposal Target Layer
Proposal Target Layer的作用是从proposal layer输出的RoIs中选择出有可能存在目标的RoIs。这些RoIs将被用于对head layer产生的特征图进行裁剪池化(crop pooling),裁剪得到的小的特征图将被传入网络的剩余部分(head_to_tail) ,进而计算得出预测的类别分数和box regression coefficients。
与anchor target layer类似,选择好的proposals(与gt boxes有着最大重合度的)传入classification layer是很重要的。否则,classification layer所学习的将是无望的学习任务。
传入proposal layer target层的是由proposal layer计算得出的RoIs。利用每个ROI与所有ground truth boxes的最大重叠度,将RoI划分为前景或背景ROIs。最大重合度超过给定阈值(TRAIN.FG_THRESH, default: 0.5)的将被设定为前景目标。最大重合度位于阈值区间 TRAIN.BG_THRESH_LO 和 TRAIN.BG_THRESH_HI (default 0.1, 0.5)中的将被设定为背景目标。这是一个称为“hard negative mining”的方法,该方法将识别起来较为困难的背景样本传入分类层。
该方法的目标是使得前景和背景region的数目保持为常数。为了避免出现背景目标过少的情况,该算法通过随机重复一些背景目标的索引来补足batch中的差额。
接着, bounding box target regression误差将在每一个RoI及其最匹配的gt之间计算得到(包括背景RoI,因为对于这些背景目标,同样存在与其重叠的gt)。这些regression targets将被扩充至所有的类别,如下图所示:
图中的bbox_inside_weights是一个掩模矩阵,只有前景ROI所对应的正确的类别处的值为1,其他值为0,背景ROIs所对应的值为0。因为在计算classification layer loss的bounding box regression部分时,只有前景区域的regression coefficients才被包括在内,背景目标的边框回归损失不计。但在计算classification损失部分时,要计算背景目标,因为背景目标所属的类别为0。
输入:
- 由proposal layer产生的RoIs
- ground truth信息
输出:
- 选择出的符合重合度标准的前景和背景ROIs
- RoIs的类别相关的target regression coefficients
参数:
- TRAIN.FG_THRESH:(default: 0.5)用于选择前景ROIs。与gt的最大重合度高于该阈值的RoI被设定为前景
- TRAIN.BG_THRESH_HI: (default 0.5)
- TRAIN.BG_THRESH_LO: (default 0.1)这俩个参数用于选择背景ROIs,最大重合度位于BG_THRESH_HI 和BG_THRESH_LO 区间内的RoI被设定为背景目标
- TRAIN.BATCH_SIZE: (default 128)前景和被选中的背景boxes的总数
- TRAIN.FG_FRACTION: (default 0.25)前景目标的数目不能超过BATCH_SIZE*FG_FRACTION
Crop Pooling
Proposal target layer产生可能的ROIs,提供相关目标类别标签和regression coefficients以便训练分类器时使用。下一步是使用这些ROIs从由head network产生的特征图中抽取对应的特征。这些抽取得到的特征图将被用于剩下的网络层,进一步产生每一个ROI的目标类别概率分布和regression coefficients。Crop Pooling的作用就是从卷积特征图中抽取ROIs对应的特征图。
Crop Pooling的核心内容描述于“Spatial Transformation Networks” (Anon. 2016)*。目标是在输入特征图上使用一个扭曲函数(warping function,2x3的仿射变换矩阵)来输出warped 特征图,如下图所示。
crop pooling主要有两个步骤:
- 对一个集合的目标坐标(target coordinates)应用仿射变换,得到一个方格的源坐标(source coordinates)。公式为:$\left[ \begin{array} { c } { x _ { i } ^ { s } } \\ { y _ { i } ^ { s } } \end{array} \right] = \left[ \begin{array} { l l l } { \theta _ { 11 } } & { \theta _ { 12 } } & { \theta _ { 13 } } \\ { \theta _ { 21 } } & { \theta _ { 22 } } & { \theta _ { 23 } } \end{array} \right] \left[ \begin{array} { l } { x _ { i } ^ { t } } \\ { y _ { i } ^ { t } } \\ { 1 } \end{array} \right]$,其中,$x _ { i } ^ { s } , y _ { i } ^ { s } , x _ { i } ^ { t } , y _ { i } ^ { t }$都是width/height归一化的坐标,因而$- 1 \leq x _ { i } ^ { s } , y _ { i } ^ { s } , x _ { i } ^ { t } , y _ { i } ^ { t } \leq 1$。
- 在第二步中,在源坐标处采样输入(源)映射,以生成输出(目标)映射。每一对$\left( x _ { i } ^ { s } , y _ { i } ^ { s } \right)$坐标对应输入特征图中的一个空间位置,接着使用采样核(如双线性采样核)对该位置进行采样,最终得到输出图像上相对应的特定位置的值。
Spatial transformation中所描述的采样方法是可微的,因而损失的梯度可以直接反向传播回输入特征图和采样的方格坐标。
幸运的是,pytroch提供了裁剪池化对应的API,API中的两个函数分别对应上述两个步骤。torch.nn.functional.affine_grid
以仿射变换矩阵为输入,输出一个集合的采样坐标,torch.nn.functional.grid_sample
对这些坐标处的格子进行采样。Pytorch自动进行误差梯度的反向传播。
为了使用裁剪池化,我们需要进行如下操作:
- 将RoI的坐标除以head network的stride长度。Proposal target layer产生的是原始输入图像上ROIs的坐标(800x600)。为了将这些坐标转换至”head”网络所输出的特征图上,我们必须将这些坐标除以stride(本次实现中为16)。
- 为了使用API,我们需要提供仿射变换矩阵,仿射变换矩阵的计算方法如下所示。
- 我们同样需要知道目标特征图中$x、y$两个维度上的点的个数。这一参数由
cfg.POOLING_SIZE
(default 7)提供。因而,在进行裁剪池化时,非正方形RoI将被用于从卷积特征图上裁剪出扭曲为大小恒定的正方形特征图。所以必须执行裁剪池化操作(实现上述扭曲),因为接下来的卷积和全连接层要求输入的特征图大小是固定的。
我们所需要的是未经过扭曲的变换,由于我们已经知道了源坐标(对预测得出的RoI的坐标进行归一化得到)和目标坐标的值(池化得到的特征图的对角坐标是固定的,如上图所示),使用简单的矩阵运算即可得出仿射变换矩阵。由于每一个RoI的坐标都是不同的,所以对于每一个RoI都需要单独计算一个仿射变换矩阵。
Classification Layer
crop pooling layer以proposal target layer输出的RoIs和head network输出的特征图为输入,输出固定大小的输出特征图。该输出特征图将被传入后接最大池化(用于改变特征图的空间大小)的四层ResNet。结果(代码中为”fc7”)是,对于每一个RoI将得到一个一维的特征向量。流程如下,下面$n$表示从Proposal target layer得到的ROIs的数量:
所产生的一维特征将被传入另个全连接层bbox_pred_net和cls_score_net。针对每一个bounding boxes,cls_score_net layer将产生类别分数(可以使用softmax将其转换为概率)。bbox_pred_net layer将产生类别相关的bounding box regression coefficients,该regression coefficients将和由proposal target layer产生的原始的bounding box系数一起产生最后的bounding boxes。流程如下:
有必要回顾一下两个bounding box regression coefficients的差别。第一个由RPN网络产生,第二个由分类网络产生。
第一个用于引导RPN网络产生好的前景bounding boxes(与目标边界贴合地更加紧)。target regression coefficients(将ROI框与其最接近匹配的真实bounding box对齐所需的coefficients)由anchor target layer生成。很难清楚地解释学习过程是如何发生地,但我可以假设,RPN卷积网络和全连接网络会将由神经网络产生的不同的图像特征图转换为尽可能好的目标边界框。我们将会在前向推理章节介绍regression coefficients的使用方法。
第二个bounding box coefficients由classification layer层产生。这些回归系数是类别相关的,即,对于每一个RoI,会针对每一个类别分别产生一个边框回归系数。这些regression coefficients的target regression coefficients是由proposal target layer产生的。要注意到,classification layer在由仿射变化作用于head网络输出所产生的正方形特征图上操作。然而,因为回归系数对于无裁剪的仿射变换具有不变性,由proposal target layer产生的target regression coefficients才可以和classification layer产生的regression coefficients进行比较,并作为一个有效的学习信号。
要注意的是,在训练classification layer时,误差的梯度同样反向传播至RPN层。这时由于在进行crop pooling时所用的RoI坐标正是网络的输出本身,因为他们是将RPN网络生成regression coefficient施加在anchor boxes上的结果。在反向传播过程中,误差的梯度将通过crop pooling传播至RPN。计算和实现这些梯度运算是存在一定的难度的,庆幸的是这些运算pytorch中已经给出了实现。
实现细节:推理
推理过程如下:
anchor target and proposal target layers没有使用到。RPN网络被假定已经学习了如何将anchor boxes分为前景和背景boxes,以及产生好的bounding box coefficients。 proposal layer将bounding box coefficients用于最好的前top个anchor boxes,同时使用NMS避免大量的重叠。为了更加清晰,这些步骤的输出如下所示。结果框被发送到分类层,在那里生成类分数和类特定的bounding box regression coefficients。
红色方框显示的是得分最高的6个anchors。绿框表示应用RPN网络计算的regression parameters后的anchor boxes。绿色方框似乎更贴合底层对象。注意,应用regression parameters后,矩形仍然是矩形,即,没有剪切。还要注意矩形之间的明显重叠。这种冗余通过应用非极大值抑制来解决。
红色方框显示NMS之前的前5个边框,绿色方框显示NMS之后的前5个边框。通过抑制重叠的方框,其他方框(得分列表中较低的方框)有机会显示出来。
从最终的分类分数数组(dim: n, 21;n表示有n个前景目标)中,我们选择一个前景目标对应的列,比如car。然后,我们选择该数组中与最大得分对应的行。这一行对应的建议最有可能是一辆车。现在让这一行的索引为car_score_max_idx。最后的边界框坐标数组(应用回归系数后)为bboxes (dim: n,21*4)。从这个数组中,我们选择与car_score_max_idx对应的行。我们期望在该行中,与car列对应的bounding boxes应该比其他bounding boxes(对应错误的目标类)更适合测试图像中的car。情况确实如此。红色框对应于original proposal box,蓝色框是car类计算出来的bounding box,白色框对应于其他(不正确的)前景类。可以看出,蓝色的盒子比其他盒子更适合实际的车。
为了显示最终的分类结果,我们应用了另一轮的NMS,并对类分数应用了目标检测阈值。然后,我们绘制所有满足检测阈值的ROIs对应的转换边界框。结果如下所示。
附录
ResNet50 Network结构
Non-Maximum Suppression (NMS)
非最大抑制是一种技术,用于通过消除重叠超过阈值的方框来减少候选方框的数量。首先根据一些标准 (通常是右下角的y坐标) 对这些框进行排序。然后我们遍历方框列表,并抑制那些与考虑中的方框IoU重叠度超过阈值的方框。根据y坐标对这些盒子进行排序,结果会在一组重叠的盒子中保留最低的那个盒子。这可能并不总是理想的结果。在R-CNN中使用的NMS是根据前景得分来排序的。这将导致在一组重叠的框中保留得分最高的框。
下图显示了这两种方法的区别。黑色的数字是每个框的前景得分。右边的图像显示了对左边的图像应用NMS的结果。第一个图使用标准的NMS(方框按右下角的y坐标排列)。这将导致框中保留较低的分数。第二个图使用了修改过的NMS(按前景得分对框进行排序)。这将导致保留前景得分最高的框,这是更可取的。在这两种情况下,盒之间的重叠被认为高于NMS重叠阈值的。
附件
本文的VISIO图对应的原件可以在这里找到。
参考
Object Detection and Classification using R-CNNs
Faster R-CNN具体实现详解
一文读懂Faster RCNN
物体检测丨Faster R-CNN详解