RCNN到Faster R-CNN笔记
学习总结
排版不是非常整齐,适合当做查阅字典。
前言与知识预热
-
目标检测由来:1966年,Marvin Minsky让他的学生Gerald Jay Sussman花费一个暑假把相机连接到电脑上以使电脑能描述看到的东西。
-
实例分割(instance Segmentation):标记实例和语义, 不仅要分割出
人
这个类, 而且要分割出这个人是谁
, 也就是具体的实例。即不仅要分类,而且同类间需要区别每个个体。semantic segmentation - 只标记语义, 也就是说只分割出人
这个类来 -
定位框的表示:只需(x_0,y_0,width,height)四个参数,即可表示一个框
-
目标检测竞赛:
- Pascal VOC:20 classes停办
- COCO:200 classes,2018年COCO竞赛中国团队包揽全部六项任务的冠军,其中旷视取得4项冠军
- ImageNet ILSVRC停办
-
目标检测数据集:Caltech2005,Pascal VOC2005,,LabelMe2007,ImageNet2009,Caltech-USCD-birds-200 2010,FGVC-Aircraft2013
-
pre-train和fine-tune:pre-train指的是在ImageNet上对CNN模型进行预训练。预训练的数据和真实训练的数据集不必一样。fine-tune指的是使用真正的数据集进行训练,调整。
-
评价准则:召回率
- 如图所示,召回率指的是真实标签中有多少被检测出来了
- IOU(Intersection-over-Union)指的是真实检测框与计算出来的检测框之间的交并比
- 从一张图中提取候选框Region Proposal方法:EdgeBoxes和Selective Search(SS)。
- 选择性搜索SS于层次聚类算法,主要通过框与框之间的颜色,纹理,大小,吻合等相似度排序,合并相邻的相似区域来减少候选框。即不断合并相似的框,直到相似度达到一定程度或者框的数目减小到阈值(~2K个)。合并策略:S = a*S(color) + b* S(texture) + c*S(size) + d*S(fit);
- 非极大值抑制
- 抑制不是最大值的元素,把与指定框(分数高的)overlap大于一定阈值的其他框移除
- 边界框回归:调整边界框
两阶段
object detection技术的演进:
- RCNN(Selective Search + CNN + SVM)
- ->SPP-net:Spatial Pyramid Pooling(空间金字塔池化)(ROI Pooling)
- ->Fast R-CNN(Selective Search + CNN + ROI)
- ->Faster R-CNN(RPN + VGG + ROI)
- ->Mask R-CNN(resnet +FPN)
1. RCNN
2014年,作者RBG
R-CNN解决的是,“为什么不用CNN做classification呢?”
R-CNN步骤:
一:用SS提取候选区域
二:用CNN提取区域特征
三:对区域进行SVM分类+边框校准
R-CNN详解:
一:用SS提取候选区域
哪一层的特征好用?
二:用CNN提取区域特征
-
将不同大小的候选区域(原图)缩放到相同大小
-
将所有区域送入AlexNet提取特征:5个卷积层,2个全连接层
-
以最后一个全连接网络的输出作为区域的特征表示:
-
有监督预训练pretraining
- 图像分类任务:使用ImageNet,1000类,仅有图像标签,没有物体边框标准
- 数据量120张图像,多。此时得到的是分类的网络,而不是检测网络
-
针对目标任务进行微调 Fine-tuning
- 目标检测任务:Pascal VOC,20类,有物体边框标注
- 数据量:仅有数千或上万张图像,少。
- 已经经过了大量CNN的预训练,只需要少量图片就可以在检测上做的比较好
三:对区域进行分类+边框校准
-
线性SVM分类器(不使用之前的softmax了)
- 针对每个类别分类训练,每个类别对应一个SVM分类器
- 两类分类:one-vs-all
- 对于摩托车SVM,摩托车是正类,其余是父类。对于自行车SVM,自行车是正类,其余是父类。
-
softmax分类
- 和整个CNN一起端到端训练
- 所有类别一起训练
- 多类分类
-
边框校准(包含更少的背景)
线性回归模型(x,y,w,h)
非极大值抑制算法:得分从大到小排序,最大的分别于后面的IoU比较,与拥有最大值的图片重合度大于0.5的,则丢掉小的。这样丢掉了一部分图片。然后拿出来最大值的图片,第二大的再和其余更小的去比较。
非极大值抑制后可能图片数大大减少了,然后再进行边框回归。
缺点:
- 需要变形特征到固定大小,crop/warp会丢失了长宽比信息,影响检测精度
- 低效的流水线,SS耗时,每个候选区域输入到卷积也耗时
- 训练时间很长(48小时)=FineTune(18)+特征提取(63)+SVM/Bbox训练(3)
- 测试阶段很慢:VGG一张图片47s
- 所谓端到端是指在一个神经网络里整体去实现
pool5底层的特征,万金油,但是准确率不高
候选区域目标(RP) | 特征提取 | 分类 | |
---|---|---|---|
RCNN | selective search | CNN | SVM |
传统的算法 | objectness,constrainedparametric min-cuts,sliding window,edge boxes,… | HOG , SIFT,LBP, BoW,DPM,… | SVM |
2. SPP - net:Spatial Pyramid Pooling
作者何凯明
R-CNN的低效原因是因为全连接层的输入长度需要固定(而卷积层的输入大小任意),SPP的贡献是1:将不同大小的特征图归一化到相同大小。此外:2“SPP还对整张图计算卷积特征,去除了各个区域的重复计算。在整张图片上只进行一躺卷积,然后SPP-net从feature map上抽取特征。
SPP-net与R-CNN的对比
区别:RCNN输入原图像的proposal VS SPP输入特征的proposal
SPP:3个level和21个Bin:1*1,2*2,4*4。在bin内使用maxpooling。如图所示,把特征图分别16,4,1等分,从每个等分块中取一个像素,从而构成相同长度的向量。也可以只进行16等分,从而得到长度16的向量。(为了可以把卷积层与全连接层连接)
窗口大小 ,步长
解决的是,因为全连接层的输入需要固定尺寸,既然卷积层可以适应任何尺寸,那么只需要在卷积层的最后加入某种结构,使得后面全连接层得到的输入为固定程度就可以了。所以将金字塔思想加入CNN,提取固定长度的特征向量,实现了数据的多尺度输入。Spatial Pyramid Pooling添加在卷积后,全连接前。SPP还有一个优化点是:如果对ss提供的2000多个候选区域都逐一进行卷积处理,很耗时。解决思路是先将整张图卷积得到特征图,然后再将ss算法提供的2000多个候选区域的位置记录下来,通过比例映射到整张图的feature map上提取出候选区域的特征图B(映射关系与补偿有关),然后将B送入到金字塔池化层中,进行权重计算。
**SPP-net的缺点:**步骤还是很繁琐
- R-CNN和SPP-net的训练都包含多个单独的步骤
- 1)多网络进行微调
- R-CNN对整个CNN进行微调
- SPP-net只对SPP之后的(全连接)层进行微调
- 2)训练SVM,
- 3)训练边框回归模型
- 1)多网络进行微调
- 检测速度慢,尤其是R-CNN
- RCNN+VGG16:检测一张图需要47s
- 和RCNN一样,训练过程仍然是隔离的,多阶段。提取候选框 | 计算CNN特征| SVM分类 | Bounding Box回归独立训练,大量的中间结果需要转存,无法整体训练参数;
2 )SPP-Net在无法同时Tuning在SPP-Layer两边的卷积层和全连接层,很大程度上限制了深度CNN的效果;
3)在整个过程中,Proposal Region仍然很耗时。
- 新问题:SPP之前的所有卷积层参数不能finetune
3. Fast R-CNN
之前方法缺点:
解决的是,“为什么不一起输出bounding box和label呢?”。把SVM和边框回归去掉,由CNN直接得到类别和边框。
框架
- 输入一张图和一堆bounding boxes(由SS产生)
- 产生卷积特征图
- 对于每一个box,通过ROI Pooling层得到一个定长特征向量
- 输入:(1)K+1类别概率 ,(2)回归框位置
贡献:
-
提出了ROI Pooling,可以看做是单层SPP-net(single-level SPP)
简单地说就是把一张图差不多等分成很多份,从每一份中取一个像素,拼凑起来就得到相同长度的向量。窗口大小 ,步长
RoI pooling的梯度回传。RoI有重叠,会有一些像素重叠。非重叠的区域是maxpooling类似的梯度回传,如果是重叠的:多个区域的偏导之和。假设有2个区域r0和r1。r0经过roi pooling后假设是2*2的。pooling之后是2×2的输出。重合了1个像素。对于r0来说是贡献到了右下角,r1贡献到了左上角。梯度回传的时候还有更深的层回传梯度。知道了2×2的梯度往回传的时候,算大图的梯度的时候,是两个小图梯度的相加。
-
引入多任务学习,将多个步骤整合到一个模型中。
- SVM+Regressor -> Multitask Loss(SoftMax + Regressor)
- ,背景的时候,u是0
- 其中分类损失
-
边框回归:Lloc
即对于x,y,w,h,边框回归的损失都是smoothL1损失,t是预测的,v是真实的。u指示的是,背景的边框回归忽略掉不计算。
-
全连接层加速:Truncated SVD
W≈U
将一个大的全连接层分解层两个小的全连接层
时间复杂度:O(uv)→O(t(u+v))
-
多任务学习的优势
-
抛弃SVM vs 使用Softmax
- 为什么不使用SVM了:训练使用难样本挖掘以使网络获得高判别力,从而精准定位目标
-
结果比R-CNN好一些,不需要磁盘存储
缺点:
- Fast R-CNN让然需要专门的候选窗口生成模块
- 候选框提取方法仍是SS:CPU,2s/图,速度慢
- 其他方法:EdgeBox,GPU,0.2S/图
Fast R-CNN的RegionProposal是在feature map之后做的,这样可以不用对所有的区域进行单独的CNN Forward步骤。
- 分类损失:交叉熵损失
- 回归损失:预测值和真实值差异小于1的话,让损失小一些。如果差异比较大,让损失大一些
4. Faster R-CNN = Fast R-CNN + RPN
下面这张图的目的是为了显示训练是分阶段的
Faster R-CNN解决的是,“为什么还要用selective search呢?”----将选择性搜索候选框的方法换成Region Proposal Network(RPN)。
- Fast R-CNN存在的问题:存在瓶颈:选择性搜索,找出所有的候选框,这个也非常耗时。
解决方法:加入一个提取边缘的神经网络,也就说找到候选框的工作也交给神经网络来做了,即Region Proposal Network(RPN)。
RPN:
-
用CNN来生成候选窗口,量少质优(~300)
-
RPN用于直接产生区域候选,不需要额外算法产生区域候选
-
让生成候选窗口的CNN和分类的CNN共享卷积层
具体做法:
- 名词介绍:锚点anchors
即特征图上的最小单位点,比如原始图像的大小是256x256,特征提取网络中含有4个pool层,然后最终获得的特征图的大小为 256/16 x 256/16,即获得一个16x16的特征图,该图中的最小单位即是锚点,由于特征图和原始图像之间存在比例关系,在特征图上面密集的点对应到原始图像上面是有16个像素的间隔,如下图所示。所以锚点的点是在原图上呢还是在conv5_3图上呢?我理解的是conv_5图上的点才是锚点。
-
锚点与原图的对应
在conv5_3特征图上用3*3的窗口滑动(stride=1,0填充,即输入输出大小不变,改变的是通道数),每个窗口可能对应原图多种(k=9种)形状(3种大小,三种情况)。每个滑动窗口映射到低维特征(对于ZF是256维,对于VGG是512维),这个特征再连接到两个分支全连接层(reg与cls)。reg层有4k个输出(边框参数),cls层输入2k个得分(评估是/否物体的概率)。比如经过3*3卷积后,channel是256,那么卷积后的图每个点对应256深度,我们进行的全连接是每个点的256深度连接到2*9或4*9的全连接上,这点是与一般全连接不同的地方。而实际上这种全连接,可以用kernel size=1,stride=1的卷积表示。
-
滑动窗口的位置提供了在原图像上定位的参考信息,边界框回归根据滑动窗口的参考信息提供了更精准的定位信息。回归器为每个锚点框计算偏移,分类器评测每个锚点框(回归后的)是物体的可能性
-
如何产生不同大小的窗口?
针对每一个锚点,然后根据不同的尺度(128、256、512pixel)和不同的长宽比(1:1、0.5:1、1:0.5)产生9个BBox,如下图所示,对于16x16的特征图,最终产生16x16x9个候选的ROI,最后原图上的框图下图所示。(可以理解为锚点在conv5_3上,框在原图上)
#在五层conv,poolling,relu之后,取出conv5的输出,送给RPN网络;
layer {
name: "rpn_conv1"
type: "Convolution"
bottom: "conv5" #向上生长
top: "rpn_conv1"
param { lr_mult: 1.0 }
param { lr_mult: 2.0 }
convolution_param {
num_output: 256
kernel_size: 3 pad: 1 stride: 1
weight_filler { type: "gaussian" std: 0.01 }
bias_filler { type: "constant" value: 0 }
}
}
layer {
name: "rpn_relu1"
type: "ReLU"
bottom: "rpn_conv1"
top: "rpn_conv1"
}
'''
我们只需要一个3*3*256*256这样的一个4维的卷积核,就可以将每一个3*3的sliding window 卷积成一个256维的向量,相当于feature map每个点都是256-d。
anchors。按照尺度变换(128×128, 256*256,512*512,2:1, 1:1, 1:2)计算这256维向量每个像素的9个anchor,所谓anchors,实际上就是一组由rpn/generate_anchors.py生成的矩形。直接运行generate_anchors.py得到以下输出:
[[ -84. -40. 99. 55.]
[-176. -88. 191. 103.]
[-360. -184. 375. 199.]
[ -56. -56. 71. 71.]
[-120. -120. 135. 135.]
[-248. -248. 263. 263.]
[ -36. -80. 51. 95.]
[ -80. -168. 95. 183.]
[-168. -344. 183. 359.]]。
'''
Loss Function
Anchors:The k proposals are parameterized relative to k reference boxes,which we call anchors
两种anchors被标记为正类:**(1)与真实box有最高的IoU重合度(也许不到0.7),(2)与任意any真实box的IoU重叠度大于0.7。**这样一个真实box可能被赋给多个anchor正标签。通常第二种情况用来分辨正类已经足够了,但是仍然采用第一种的原因是如果全部框的IoU重合度都不大于0.7,那么我们就没框了,序号1是为了至少有个框。与所有真实box的IoU重叠度小于0.3的anchor被标记为负类。**剩下不是正类也不是负类的anchor对训练没有影响。**由此输出维度为:2*9=18-d,anchor(label和概率)一共18维。
当框的是背景的时候,真实概率p*=0,即框的项为0.
layer {
name: "rpn_cls_score"
type: "Convolution"
bottom: "rpn_conv1"
top: "rpn_cls_score"
param { lr_mult: 1.0 }
param { lr_mult: 2.0 }
convolution_param {
num_output: 18 # 2(bg/fg) *9(anchors)
kernel_size: 1 pad: 0 stride: 1
weight_filler { type: "gaussian" std: 0.01 }
bias_filler { type: "constant" value: 0 }
}
}
L还含有标准化的,,为了分类与回归框对Loss的贡献统一,再加入平衡一下。
模型学习:交替式4步法训练
-
1.基于预训练模型只训练RPN
-
2.基于预训练模型,以及上一步得到的RPN,训练Fast R-CNN
-
3.固定共享的卷积层,训练RPN
-
4.固定共享的卷积层,基于上一步得到的RPN,训练Fast R-CNN
RPN与检测网络共享卷积部分,节省计算。但没能实现实时检测
-
模型学习:端到端训练
- 同时学习RPN和分类网络
- 分类网络的梯度不向RPN回传
Note:
-
只有在train时,cls+reg才能得到强监督信息(来源于ground truth)。即ground truth会告诉cls+reg结构,哪些才是真的前景,从而引导cls+reg结构学得正确区分前后景的能力;在reference阶段,就要靠cls+reg自力更生了。
-
在train阶段,会输出约2000个proposal,但只会抽取其中256个proposal来训练RPN的cls+reg结构;到了reference阶段,则直接输出最高score的300个proposal。此时由于没有了监督信息,所有RPN并不知道这些proposal是否为前景,整个过程只是惯性地推送一波无tag的proposal给后面的Fast R-CNN。
-
RPN的运用使得region proposal的额外开销就只有一个两层网络。
RoI pooling存在的问题:(mask RCNN解决)
由于预选ROI的位置通常是有模型回归得到的,一般来说是浮点数,而赤化后的特征图要求尺度固定,因此ROI Pooling这个操作存在两次数据量化的过程。1)将候选框边界量化为整数点坐标值;2)将量化后的边界区域平均分割成kxk个单元,对每个单元的边界进行量化。事实上,经过上面的两次量化操作,此时的ROI已经和最开始的ROI之间存在一定的偏差,这个偏差会影响检测的精确度。
效果
-
下面的图是RPN的部分
5. Mask R-CNN
mask R-CNN = Faster R-CNN with FCN on RoIs
但是如果只在最后一层 feature map 上映射回原图像,且初始产生的anchor被限定了尺寸下限,那么低于最小anchor尺寸的小目标虽然被anchor圈入,在后面的过程中依然容易被漏检。
但是FPN的出现,大大降低了小目标的漏检率,使得RPN如虎添翼。
resnet +金字塔网络FPN(Feature Pyramid Networks)
作者替换了在faster rcnn中使用的vgg网络,转而使用特征表达能力更强的残差网络。
另外为了挖掘多尺度信息,作者还使用了金字塔网络FPN(Feature Pyramid Networks)。
金字塔网络FPNl:把低分辨率、高语义信息的高层特征和高分辨率、低语义信息的低层特征进行自上而下的侧边连接,使得所有尺度下的特征都有丰富的语义信息。
RoIAlign:由于 RoIPooling 采用的是 INTER_NEAREST(即最近邻插值) ,即在resize时,对于 缩放后坐标不能刚好为整数 的情况,采用了 粗暴的四舍五入,相当于选取离目标点最近的点。这样做会在一定程度上损失 空间对称性(Alignment),所以他们把 最近邻插值 换成了 双线性插值 。换完插值法的 RoIPooling 就有了一个更加高大上的名字 —— RoIAlign
使用方法 | 特点 | 缺点 | 改进 |
---|---|---|---|
R-CNN(Region-based ConvolutionalNeural Networks) | 1、SS提取RP;2、CNN提取特征;3、SVM分类;4、BB盒回归。 | 1、 训练步骤繁琐(微调网络+训练SVM+训练bbox);2、 训练、测试均速度慢 ;3、 训练占空间 | 1、 从DPM HSC的34.3%直接提升到了66%(mAP);2、 引入RP+CNN |
Fast R-CNN(Fast Region-based ConvolutionalNeural Networks) | 1、SS提取RP;2、CNN提取特征;3、softmax分类;4、多任务损失函数边框回归。 | 1、 依旧用SS提取RP(耗时2-3s,特征提取耗时0.32s);2、 无法满足实时应用,没有真正实现端到端训练测试;3、 利用了GPU,但是区域建议方法是在CPU上实现的。 | 1、 由66.9%提升到70%;2、 每张图像耗时约为3s。 |
Faster R-CNN(Fast Region-based ConvolutionalNeural Networks) | 1、RPN提取RP;2、CNN提取特征;3、softmax分类;4、多任务损失函数边框回归。 | 1、 还是无法达到实时检测目标;2、 获取region proposal,再对每个proposal分类计算量还是比较大。3、RoIpool对分类没什么影响,但对分割却影响很大 | 1、 提高了检测精度和速度;2、 真正实现端到端的目标检测框架;3、 生成建 |
Mask R-CNN | 1、引入了RoI Align代替Faster RCNN中的RoI Pooling;2、引入语义分割分支,实现了mask和class预测的关系的解耦;3、使用ResNet | ROI Align在VOC2007数据集上的提升效果并不如在COCO上明显。经过分析为造成这种区别的原因是COCO上小目标的数量更多 | 1、Mask RCNN精度高于Faster RCNN |
与faster RCNN的区别:
1)使用ResNet101网络
2)将 Roi Pooling 层替换成了 RoiAlign;
3)添加并列的 Mask 层;
4)由RPN网络转变成FPN网络
高召回率
It is based on computing hierarchical grouping of similar regions based on color, texture, size and shape compatibility.
Can we use segmented parts in this image as region proposals? The answer is no and there are two reasons why we cannot do that:
-
Most of the actual objects in the original image contain 2 or more segmented parts
实物往往包含几个分割部分
-
Region proposals for occluded objects such as the plate covered by the cup or the cup filled with coffee cannot be generated using this method
完美的分割不是我们的目的,我们只是想要预测很多候选区域,以便其中的一些与实物有高度的重叠。
Felzenszwalb算法把图像中的像素点看做是一个个结点,像素点之间的不相似度作为边的权重,通过将相似的像素聚合到一起,产生同一区域(表现为最小生成树)。那么对于已经生成好的两个相邻区域A和B,算法又是如何判断它们是否可以合并的呢?对于A和B两个区域,取其类内的结点之间的边的权重最大值Wa和Wb,然后取两区域(类间)连接的边中权重最小值Wab,如果Wab比Wa和Wb都要小,则满足两个区域合并的要求。可是,对于一开始单元素区域,类内无边,权重无限小,类间最小权重肯定是无法满足要求的,这样子从一开始就会导致每个元素都“各自为政”。所以,函数中的scale参数就是用来解决这个问题。在计算类内最大权重时,需要额外增加一个偏置scale/c,其中的c就是区域中像素个数,一开始c=1,随着区域增加,偏置的作用也会相应减小。函数的另一个参数min_size,是用来合并像素个数过小的区域,如果区域像素个数少于min_size,则与最相似区域合并。在做这些工作前,为了减少图像不平滑的问题,作者先是用了高斯滤波处理图像,sigma就是高斯核的大小。
过分割图片样张
Selective Search algorithm takes these oversegments as initial input and performs the following steps
-
Add all bounding boxes corresponding to segmented parts to the list of regional proposals
-
Group adjacent segments based on similarity
-
Go to step 1
作用:提取候选区域
相似度计算:可以从颜色、纹理、尺寸、空间交叠这4个参数方面衡量
模块:pip install selective
其中,img是我们输入的原始图片;img_lab是在原图RGB三通道的基础上增加了一个通道,该通道标注了对应像素的类别;
regions是个list,包含了所有proposal region的信息,每个region是个字典,其中的keys为rect、size、label。rect对应的value存放的是region的坐标和宽高(x, y, w ,h),而size指的是该区域中属于该label的像素个数,而不是区域的大小。
图像分割的目的:把同一类别(或相似)的元素或者区域合并到一起,
region proposal algorithm work by grouping pixels into a smaller number of segments.
参考:
B站视频:python tensorflow图像处理
www.zhuanzhi.ai #专知-深度学习:算法到实践
https://cloud.tencent.com/developer/article/1015122
https://blog.****.net/wakojosin/article/details/79363224 #RPN
https://blog.****.net/mllearnertj/article/details/53709766 #RPN
https://blog.****.net/WZZ18191171661/article/details/79439212
http://www.cnblogs.com/zf-blog/p/7286405.html #rpn代码理解
https://www.learnopencv.com/selective-search-for-object-detection-cpp-python/
https://blog.****.net/v1_vivian/article/details/73275259
https://blog.****.net/xiamentingtao/article/details/78598027