R-CNN算法详细解读
R-CNN系列
- 比较好的博客参考:RCNN系列
from R-CNN to Mask-RCNN
- R-CNN
- Fast R-CNN
- Faster R-CNN
- FPN
- FPN + RPN + Fast R-CNN
- FPN + RPN + Fast R-CNN + Mask Prediction
sliding windows detector
-
滑动窗口是最开始用的一种目标检测的方法,是一种比较暴力的方法
-
过程:输入一张图片,设置多种尺度的BBox,对输入图片从左到右、从上到下进行滑动BBox,裁剪出BBox_img,然后输入到CNN中进行识别判断。
-
缺点:暴力搜索,非常耗时
Selective search
- 为了解决暴力搜索太耗时的问题,R-CNN使用选择性搜索的方法,生成候选区域。
- 生成候选区域的方法有很多种,作者选着了selective search方法,因为R-CNN检测图像时,是不知道这个图像中有什么的,也就没法针对某种目标来生成候选区域,selective search方法是以颜色,纹理等特征的相似度将像素点合并成区域的算法,相比于滑动窗口的方法复杂度更低。
- 选择性搜索方法,进行区域建议,在输入图片中对像素进行“聚类”处理(相似的区域进行融合,得到bbox),得到很多ROIs,然后将ROIs输入到CNN中进行识别。
R-CNN
- 整体框架
-
整体描述:①输入图片首先经过 Region proposal ,使用 selective search 方法得到2000 ROIs;生成的ROIs可能大小不一样,然后resize成相同大小ROI;②接着将相同尺寸的ROIs输入到CNN网络进行特征提取;③最后将特征送入每一类的SVM 分类器,判别是否属于该类;④并且进行位置精修,即使用回归器(边框回归)精细修正候选框位置。注意:为什么要进行边框回归,因为Selective search得到的ROIs应该是不准确的,所以需要回归!
-
具体说:
区域候选框的生成: Selective Search 的前期工作就是利用Graph-Based Image Segmentation的分割算法产生初始的分割区域,然后使用相似度计算方法合并一些小的区域,参考
(CNN)特征提取:采用的是AlexNet,由五个卷积层,两个全连接层组成,输出的特征是4096维的向量。由于这个网络要求的输入格式是227x227的RGB图像,所以将候选区域全部resize成227x227(最简单的变换方法),然后减均值输入到网络中。注意,resize前,在边框四周填充16个像素,再进行 各向异性 缩放, 这种形变使得mAp提高了3到5个百分点。
关于ROI缩放到固定尺寸的方法: 主要分为各向异性和各向同性两种方法,参考。结合下图讲,其中各向异性就是最简单的变换,就是不管图片的长宽比例,管它是否扭曲,进行缩放就是了(D)。因为图片扭曲后,估计会对后续CNN的训练精度有影响,作者也测试了“各向同性缩放”方案,有两种办法:①先扩充后裁剪(B);②先裁剪后扩充(C)。文献还有个padding处理,下面的示意图中,每个例子,上面的行采用padding=0,下面一行采用padding=16。经过最后的试验,作者发现采用各向异性缩放、padding=16的精度最高。 -
边框回归怎么做(重点,目标检测中都用)
- 蓝色框是经过Selective search得到的ROI,也就是Region Proposal得到的结果,表示为 Px、Py、Pw、Ph;
- 红色框是我们实际的汽车BBox(训练模型时,人工标注),Ground truth,表示为 Gx、Gy、Gw、Gh;
- 很明显,Proposal 和 Ground truth之间是有差距的,因此,我们的目的就是学习将Proposal变成Ground truth的一种变换,变换也就是dx(P)、dy(P)、dw(P)、dh(P);
- 具体的变换计算公式如Mapping 和 Target:
(1)对于x和y,使用偏移变换,那么为什么要用 Pw * dx(P)来表示,而不直接用 △x 来表示,即Gx = Px + △x? 因为,这个偏移值应当跟BBox的宽高有关系,比如:对于一个很小的物体,偏移几个像素,可能变化就很大了,对于一个较大的物体,偏移几个像素可能变化不大。
(2)对于宽和高的变换,使用缩放变换。
(3)最后我们预测的并不是Ground truth ,而是预测Proposal到Ground truth的变换,即 tx、ty、tw、th。 - 解码时的指数函数能够确保宽高的缩放比例大于0,所以 tw 和 th 的标签使用了log函数。
- 边框回归损失函数定义:
- 分类器和回归器的输入: 分类器的输入是特征提取器AlexNet的fc6的输出结果,回归器的输入是特征提取器AlexNet的pool5的输出结果。之所以这样取输入,是因为,分类器不依赖坐标信息,所以取fc6全连接层的结果是没有问题的。但是回归器依赖坐标信息(要输出坐标的修正量),必须取坐标信息还没有丢失前的层。而fc6全连接层已经丢失了坐标信息。
- 具体在训练的时候必须考虑正负样本的问题: 正负样本是必须要考虑的问题。论文的做法是每个batch所采样的正负样本比为1:3。当然这个比例是可以变化的,这个系列的后续改进就把正负样本比变为了1:1。如果之前没有接触过类似问题的话,是比较容易想当然地认为训练特征提取器、分类器、回归器时,就是把候选区域生成阶段的所有候选区域都放入训练,这样的思路是错的。对于目标检测问题,一张图片中,背景占了绝大多数地方,这样就导致训练用的正样本远远少于负样本,对训练不利。 正确的做法是对所有候选区域进行随机采样,要求采样的结果中正样本有x张,负样本y张,且保证x与y在数值上相近。(对于一些问题,不大容易做到x:y = 1:1,但至少x与y应该在同一数量级下)。关于正负样本的定义参考:正负样本问题
RCNN中正负样本的定义 :一张照片我们得到了2000个候选框。然而人工标注的数据一张图片中就只标注了正确的bounding box,我们搜索出来的2000个矩形框也不可能会出现一个与人工标注完全匹配的候选框。因此在CNN阶段我们需要用IOU为2000个bounding box打标签。如果用selective search挑选出来的候选框与物体的人工标注矩形框(PASCAL VOC的图片都有人工标注)的重叠区域IoU大于0.5,那么我们就把这个候选框标注成物体类别(正样本),否则我们就把它当做背景类别(负样本)。 - 训练阶段: RCNN的网络架构,注定了它不能像其他网络那样进行端到端(end-to-end)的训练。 前面提到RCNN分为4个阶段:Proposal阶段、特征提取阶段、分类阶段、回归阶段。这4个阶段都是相互独立训练的。
Fast R-CNN
-
R-CNN很明显还是比较慢,检测一张图片大概20s的时间,Fast R-CNN直接在feature map上生成ROIs,而不是在输入图片上Selective 2000个ROIs。
-
要解决的问题:
-
Fast RCNN直接改进:
-
整体框架
- 整体描述:①输入图片经过 CNN 得到 Feature map,同时 Selective search 在输入图片上生成2000ROIs;②然后将Selective 的ROIs,对应的在Feature map上切出 Feature map中的ROIs,相当于将原图的ROIs映射到Feature map上;③接着通过ROI Pooling将 Feature map ROIs resize成相同尺寸;④最后输入到FC中进行分类和边框回归。
- 具体说:
CNN特征提取: Fast RCNN中采用VGG作为backbone进行特征提取。从预训练网络进行初始化,使用三个经过预训练的ImageNet网络进行实验,每个网络具有五个最大池化层以及五个到十三个conv层。当预训练的网络初始化Fast R-CNN网络时,它将经历三个转换。首先,最后一个最大池化层被RoI池化层代替,该RoI池化层通过将H和W设置为与网络的第一个完全连接层兼容(例如,对于VGG16,H = W = 7)进行配置。其次,将网络的最后一个完全连接层和softmax替换为先前描述的两个同级层(K + 1类的完全连接层和softmax以及特定于类别的bounding-box regressors)。第三,修改网络以获取两个数据输入:图像和这些图像的RoI。
ROI Pooling: 将 Feature map 中的 ROIs 变换成固定尺寸使用的是ROI Pooling 的方法。
ROI pooling对齐误差分析: 如下图,假设输入图片800x800,原图中的 region proposal 为650x650,CNN对原图进行特征提取后尺寸缩小32倍,要求固定的fixed size ROI为7x7;可以看出主要有两次量化操作(蓝色标注),经过两次量化(第一次在映射过程中,第二次在池化过程中),即将浮点数取整,原本在特征图上映射的20x20大小的 region proposal,偏差成大小为14x14的,这样的像素偏差势必会对后层的回归定位产生影响,不过虽然有误差,但是边框回归对精度要求没有那么强烈!
损失函数的定义,multi-task loss: 损失主要包括两部分:分类损失 + 边界框回归损失
分类损失:p是分类器预测的softmax概率分布p={p0,p1, …,pk};u是目标真实类别标签
边界框回归损失:tu是边界框回归器预测的对应类别u的回归参数txu,tyu,twu,thu;v是真实目标的边界框回归参数vx,vy,vw,vh
- 与RCNN相比快在(Fast)哪里:
①将特征提取器、分类器、回归器合并,使得训练过程不需要再将每阶段结果保存磁盘单独训练,可以一次性完成训练,加快了训练速度。这是Fast之一;
②对整张图片进行特征提取,用ROI层处理候选区域的特征,使得原本每一个候选区域都要做一次特征提取,变为了现在一整张图片做一次特征提取。训练速度(8.8倍)和测试速度(146倍)都大大加快,这是Fast之二; - 缺点:虽然测试比RCNN快200倍左右,但是候选区域的选取还是通过selective search,并且只能在CPU中运行这个算法,所以这个阶段浪费了大量时间。Fast RCNN也还不是真正意义上的 end-to-end。
Faster R-CNN
-
最直接的改进: Faster-RCNN 引入了RPN网络(region proposal network)来代替 selective-search,这使得整个网络实现了端到端。
Faster R-CNN算是RCNN系列算法的最杰出产物,也是two-stage中最为经典的物体检测算法。 -
整体框架
-
整体描述: ①输入图片经过CNN进行特征提取得到Feature map;②Feature map 经过RPN网络得到Feature map中的ROIs;③将Feature map中的ROIs进行ROI pooling 得到固定尺寸;④最后将固定尺寸的ROIs输入到后续FC中进行分类和边框回归。
-
具体说:
CNN特征提取: Faster RCNN 只使用了VGG16的前30层作为特征提取器,所以每一张图片在经过特征提取后,尺寸会变为原来的1/16,根据前文的假设,一张800x600的图片变为50x37的 Feature map,而且通道数从原来的3变为512。
Anchor的概念: 将FeatureMap中的每一点按照视野域找到原图中对应的位置,称为Anchor;每个Anchor生成不同大小不同长宽比的多个候选区域。Selective-search方法的候选区域生成方式,它是按照颜色和纹理不断合并得到候选区域的,候选区域的产生没有规律,而RPN是每个Anchor都有对应的固定数量的候选区域,规律很明显。
结合Faster R-CNN深刻理解 Anchor: 预测阶段(预测阶段的anchor都是训练好的,用来直接预测物体BBox),例如下图左边,是特征图上的一个特征点,这个特征点对应有9个训练好的Anchor,不同尺度,可以看出这个特征点对应原图片的人这个类别,而且anchor也覆盖了原图片人的区域,见下图中间,如果将所有特征点的所有Anchor绘制出来,可以看出完全覆盖整幅图片,见下图右边。训练阶段:训练阶段每个特征点会有初始的生成的9个Anchor,然后通过训练对其进行微调,例如:如果这个特征点的类别是一个人,然后将9个Anchor与人的GT BBox算IOU,选择IOU最大的那个Anchor来预测人的BBox,从而对选中的Anchor计算回归损失,完成训练。
在原图中生成anchor主要分为三步:参考
RPN网络: RPN网络是Faster RCNN的精髓所在,将传统的候选区域生成方法使用卷积运算来代替,而且根据原文作者所说,使用卷积来生成候选区域使得RPN具有一定注意力机制,效果与前几代的RCNN相比,提升明显。首先,RPN网络接受任意尺寸大小的 Feature map 作为输入,然后会生成9K个anchor,并且RPN有两个输出,一个是anchor的类别信息,也就是该anchor是背景还是前景(只要有要识别的物体就属于前景),还有一个输出是该anchor的位置偏移信息(如果该anchor是背景,则该输出不重要),下图是RPN网络结构:
RPN网络从接入vgg 提取的特征,到RPN网络如何识别每一个anchor的类别,注意这边进行的是二分类,即判断anchor的内容是背景还是前景;到RPN网络是如何对anchor位置进行修正的,详细过程参考:参考1,参考2,RPN网络详细结构如下(红色部分):
对Anchor的筛选工作: 现在我们已经有一堆经过修正后的anchor,并且也知道了每一个anchor属于前景的概率,但我们细想一下,现在anchor的数量是不是太多了,我们只用了一张800x600的图像作为输入就生成了16650个anchor,如果全部作为RoI(Region of Intererst,也就是感兴趣区域或者说候选区域)输入到后续网络中,这计算量属实有点大,所以就需要进行一些筛选工作,这其实也就是 RPN 网络中Proposal层所做的工作。
首先 ,现在我们的anchor有许多因为是在边缘生成的,所以它们的坐标可能是负值,或者简单来说就是超出了图片的范围,那么就需要对这些anchor进行裁剪,把它们统一裁剪到图片范围内,也就是将anchor左上角坐标小于0的值用0代替,右下角坐标的X轴数值大于W就用W代替,Y轴数值大于H的用H代替。经过上一步的裁剪工作,就会有许多anchor会变得很小,这里我们设定一个阈值,凡是小于16*16的anchor,我们都把它丢弃掉。
接着 ,因为我们已经有了每一个anchor属于前景的概率,那么很明显如果一个anchor属于前景的概率太小,那么也没有留着的必要性,所以对这些anchor的前景概率从大到小进行argsort,得到每一个anchor的排序索引,只取前6000个,到这一步anchor还是很多,但此时不能再鲁莽的去除anchor,因为有可能会有误判(毕竟这个前景概率只是rpn的预测,并不是真实的),此时需要用NMS方法把IoU大于0.7的进行合并,对于合并完的anchor再取前300个,这样就把输入到RoI网络的anchor的数量大大减少了。
注:需要注意的是,此处的6000以及300是测试阶段的配置,训练与此处不一样。
RPN总结: RPN部分内容比较多,第一次了解可能会被绕晕,做一个简单的小结如下图。在作者代码中,主要把RPN主要分成了两部分,一个是RPN Head,另一个是Proposal,RPN Head主要负责anchor的生成、anchor位置偏移量预测以及anchor的类别判断;Proposal负责对生成的anchor进行进一步的筛选,将筛选后的anchor作为RoI输入到后续的网络中。需要注意的是,因为不管是anchor的类别预测还是位置偏移量预测,这些都是在channel里面的,所以对这两个输出都需要使用 pytorch 的 permute 函数来将维度进行换位。
ROI Head 部分: 与Fast RCNN 类似,固定ROI尺寸的方法也是使用ROI Pooling 的方法。
训练部分: Faster RCNN 有三个部分需要训练,分别是特征提取器VGG16,RPN以及RoIHead。其中特征提取器一般是采用预训练模型进行微调,重点是 RPN 的训练以及 RoI 的训练,虽然原论文中Faster RCNN是将这两部分分开训练的,但现在大多数实现都是进行联合训练的方式。具体训练过程参考:参考 -
思考一个问题? 为什么要生成一堆anchor,再对它们进行修正,而不是一开始直接预测候选区域的坐标?其实 YOLO v1 就是没有使用anchor,直接对候选区域的坐标进行预测,但作者发现,效果并不好,主要是因为网络很难收敛,训练难度较大,所以 YOLO 的作者后来就将 Faster RCC 的 RPN 进行了相关的修改,将 anchor 加入到了YOLO v2中,效果有了显著的提高。
-
比Fast rcnn 快在哪里: 容易想到,现在RPN网络可以与其他3个阶段共用同一个特征提取结果了,省掉了 selective-search 的时间。而事实上,selective-search是非常慢的,所以叫Faster。
FPN
- 单尺度训练弊端:注意到,Fast R-CNN是单尺度进行训练的,即:对于224x224的原图,生成7x7的Feature map,缩小的倍数非常大,最后一层Feature map 的感受野非常大,因此对于小物体检测效果非常差。
- 考虑: FPN网络考虑利用浅层的Feature map来进行预测,但是浅层的信息虽然感受野比较小,位置信息很明确,但是浅层的Feature map语义信息比较弱,也就是不利于最后的分类。所以这里是一个矛盾的问题?怎么解决呢?融合呗!!!
- FPN就能很好的解决这一个矛盾的问题!!!!
- 尺度常见方法:
- FPN网络结构:
思想: 将高层的特征通过上采样与低层的特征进行融合,这样每一层的语义信息和位置信息都比较好。
- 整体描述: 首先是从底到上的特征提取过程(vgg、resNet等都可以用);然后是从上到下的过程(进行上采样,方法可以是双线性插值、反卷积等等);以及从左到右的横向连接(融合特征),最后为了解决融合的一种混叠效应,一般都又进行3x3卷积特征提取,得到最终的特征层。注意:FPN是一种思想,而不是某一种具体的网络,结合其他具体的网络可以形成特定的FPN网络,比如ResNet-FPN等等。
- FPN的优势: 精准的定位+很强的语义信息。
- ResNet-FPN:
注意:FPN还可能输出一个P6,这个P6作为RPN的输入。
- FPN+RPN+Fast RCNN = Faster RCNN
- 抛出一个疑问 ?如果使用FPN的话,我们的ROI到底要去哪一个Feature map上切呢????之前的Fast RCNN 我们是只有一个Feature map的,但是多尺度训练,怎么选择多个Feature map的哪一个呢?
做法:使用公式计算,主要根据ROI的宽高来进行确定,例如宽高越大的ROI,应该从更高层的Feature map去切!因为高层感受野大嘛!
Mask-RCNN
- FPN+RPN+Fast RCNN+Mask predition = Mask RCNN
- 整体结构
- 整体描述: ①首先输入图片经过FPN网络得到多尺度特征图;②然后将多尺度特征图的P6特征层输入到RPN网络中生成对应原图的ROIs,并且在步骤1中生成的多尺度特征图上切出对应的Feature map的ROIs;③然后将ROIs进行ROI Align操作固定到同一尺寸;④最后将固定尺寸的ROIs输入到FC中进行分类和边框回归,同时输入到mask分支进行mask预测。
- 具体说:
特征提取: Mask RCNN采用ResNet-FPN作为backbone进行特征提取,输出多尺度特征图。
mask分支: Faster-RCNN网络的最后分别是分类网络和回归网络两条路并行,Mask-RCNN则是再加一条Mask网络与它们并行。结构如下图:(使用全卷积网络FCN)注意不管物体的大小是多少,返回的都是28x28x80的结果,返回原图得根据proposal进行变换。
ROI Align:
类似于ROI Pooling,使得所有的Feature map RoIs的尺寸固定(fixed size feature map),进而输入到后续网络进行分类、边框回归和Mask预测。对于尺寸变换操作,由于RoI Pooling进行 两次量化操作(前面有误差分析) ,使得特征图对应位置不准,带来较大的精度损失问题,因此使用RoIAlign算法,提高RoI与原图的对齐精度。对齐问题在分类和框选时影响不大,但在语义分割需要严格依赖每个像素点的坐标时,影响会很大。 ROIAlign能够解决对齐问题。图像处理中常用双线性插值对颜色进行采样,RoIAlign使用双线性插值的方法解决获得浮点数坐标点上的特征值。双线性插值原理如图3.8所示,红色点Q11,Q12,Q21,Q22为已知的4个像素点,首先进行X方向的线性插值,在Q12,Q22中插入蓝色点R2,Q11,Q21中插入蓝色点R1,然后进行Y方向的线性插值,通过第一步计算出的R1与R2在Y方向上插值计算出插值P点。
如下图所示,虚线网格表示 5x5 的特征图,实线的矩形框表示在特征图上的 H x W 的RoI,输出为 2x2 的实线网格。对于输出 2x2 的每一个cell又分成四个小网格,并取每一个小网格的中心点,每个中心点的值又通过邻近的特征图点进行双线性插值得到,最后每个cell的值为四个小网格的最大值或者平均值。
ROI Align保留了两次的浮点数,因此对齐精度大大提高。ROI Pooling / Align 详细分析参考
注意:还有第三种池化的方法:ROI Warp,参考 RoIWarp的想法和RoIAlign差不多,唯一的区别是 ROI Warp是将ROI量化到 Feature map上。
损失函数定义: 训练过程中,损失函数L的定义如下式所示,由分类损失函数L_cls、定位损失函数L_box和分割损失函数L_mask三部分共同构成。
Lcls和Lbox: 分类误差和检测误差与 farster R-CNN基本一致。
Lmask:
①训练的时候:结合下图,mask分支预测的7x7的特征图,对于每一个特征点都预测一个向量,向量的维度为类别总数。Lmask对于每一个像素使用二值的sigmoid交叉熵损失,这与一般的分类使用交叉熵损失有区别。对于每一个像素,都是用sigmod函数进行求相对熵,得到平均相对熵误差Lmask。对于每一个ROI,如果检测得到ROI属于哪一个分类,就只使用哪一个分支的相对熵误差作为误差值进行计算。(举例说明:分类有3类(猫,狗,人),检测得到当前ROI属于“人”这一类,那么所使用的Lmask为“人”这一分支的mask。)这样的定义使得我们的网络不需要去区分每一个像素属于哪一类,只需要去区别在这个类当中的不同分别小类。最后可以通过与阈值0.5作比较输出二值mask(预测时)。这样避免了类间的竞争,将分类的任务交给专业的classification分支。
②预测的时候:假设有三种类别(猫、狗和气球),首先是得到图片中物体的检测框BBox和类别名,例如类别是气球,然后对BBox框里面的每一个像素值取相应的狗的类别的 mask 结果,通过与阈值0.5作比较输出二值mask:0 或者 1,这样就绘制了气球的掩码!示意图如下:
- Mask RCNN应用:
实例分割
目标检测也可以(屏蔽mask分支)
人体姿态估计:简单修改,将mask分支的类别改为人体关键部位。
6-D姿态估计:给一个二维图片,画出物体在三维空间中的BBox。mask rcnn + pose分支!!!