本文跟着数据流动,从每一步分析Faster RCNN的原理。
看完可能要花上10分钟.
---------------------------------------------------------
原理:是技术实现的逻辑抽象。
数据输入的resize
卷积层的特征计算(卷积操作、padding~补0)
再到RPN(anchor、分类、回归)
ROI-Pooling
全连接层(flatten)
最终回归、分类
Loss计算
梯度传播
SGD
以上9点用能明白数据流动过程中的变化,就说明逻辑已经能抽象出来,每一个都能解释通。下面一点点来分析数据流动、变换。
1、对输入数据的处理
输入数据是一个batch,在代码上是一个Blob块。faser首先计算:设置最小边/短边=scale。然后计算:长边*scalle。如果小于max_size=1000,就采用这个scale,对图像进行等比缩放,把短边放到600,长边等比例缩小一点。如果大于max_size=1000,则重新计算scale=max_size/长边。同样进行等比缩放。这是对每一个图像的处理。最后batch个图像组成blob块,作为网络的输入。对于Blob,不够补0(黑边)
以上。
2、卷积层计算
【卷积核 】blob大小每一个Batch可能是不一样的。因为图像resize后的大小不一样。所以feature大小不一样,神经元个数也不一样,连接总数也不一样。但是,因为cnn是权重共享的,使得他可以接受任意尺寸的输入,每一个feature不管大小怎么变,都是由一个固定大小的神经元模板计算得到,这个模板就是卷积核。不管是40*40还是20*20的feature,每一个神经元的连接都是同一个卷积核的参数。这说明2点:卷积层可以接受不同大小的输入以处理而不报错;卷积层的权重,是卷积核里的参数。问题:梯度到这里怎么更新的?
ceil (H = ( 长 - 卷积核大小+1) / 步长)
ceil( W = ( 宽 -卷积核大小+1) / 步长 )
【特征图计算】25*25的计算和25*27的计算方式一样,都是按单边算,然后相乘。当输入不能够被卷积核与步长完美的走一遍,就会有padding问题:假设走到了边上,但只有一列数据,卷积核是两列的,那么就会无法计算,如果不用padding,就停止,不往下计算了,如果用Padding就补0,使得剩下的部分被计算,feature则会大一点。加入了padding之后,feature map大小计算公式修正如下:
ceil H=(长+2*pad-卷积核大小)/步长
ceil W=(宽+2*pad-卷积核大小)/步长
这里面有两种计算方式,一种是这是固定的pad=0、1、2,直接在图像外面加上一圈圈的0,或者自动去判断,采用padding的same模式,先计算fmap大小,然后再回去计算上下左右补多少0;或者直接要多的边,用vaild模式。
【多通道卷积】
如下图所示:多通道卷积,每个卷积核,在前一层的每一个通道上,都进行单通道卷积的运算,结果加和得到这个卷积核对应的feature map。
【feature map】一个卷积核对应了一个feauture,因为权重共享,所以可以用固定参数接收任意输入。backbone可以是res101\vgg16等很多。vgg16的结构如下。实际上faster实现只用到了conv5/conv5_1的输出。也只用训练到这里,后面的冻结。
【卷积核】有很多卷积核被开发出来,比如:非对称卷积、残差卷积、扩张卷积、可变卷积、滚动卷积等等,目的是研究适用于不同特点目标的卷积。
3、RPN
【输入】Backbone作为通用特征提取器,将conv5/conv5_1的feature map作为RPN输入,另外,这个feature map与原图的映射关系,也会被输入,后者用于计算这个特征图上每一个点映射到原图中来计算anchor center。
【Anchor机制】
是一种定义目标存在范围的基本机制。假设输入的feature map只有一张,按RPN输入的feature map上的每一个点,返回到原图作为一个中心点,以这个点为中心,按设置的参数(大小和比例)生成k个框。对于5x5的feature,就有一共25个中心点(anchor center)以及一共25xk=25k个框。因为feature map大小都是一样的,所以一个中心点,其实对应着一组feature map上的同一个点,比如vgg16有512个feature map,那么一个中心点,就对应了512个点,这512个点就是原图中这个中心点(anchor center)对应的特征向量。
到这里可以发现,跟一个中心点以及他对应的k个框相对应的特征,其实只有feature map上的一点,但512个通道,其实就是512个点,也就是一个一维的长度为512的向量。这个向量代表了他属于9个不同尺寸框的可能性,以及属于前景还是背景的可能性。其实这些通过anchor机制得到的仅仅是一些坐标,到后期计算Loss的时候,会用到这些坐标。
从conv5/conv5_1的feature map计算出框开始,到anchor机制结束之后。现在特征还在继续流入。首先是经过了一个3x3的卷积,加入了padding,保证3x3卷积前后这些点的位置还能对应上,比如左上角第一个中心点对应的feature map上的点,在3x3前 对应的是输入feature map的左上角第一个点,而经过3x3卷积之后得到的feature map,左上角第一点还是对应这个中心点,这样卷积之后的每一个点,都可以和原来的点对应起来。而3x3卷积的作用,就是使得特征更加集中,因为3x3后的f map上,每一个点,都是由原来的9个点构成,其实就相当于扩大感受野。也3x3卷积就是下面的红色框框。因为用的Backbone不同,512是vgg16的。256好像是zf的。
然后分类和回归两个任务分支。分别经过1x1的卷积,把特征进行映射,什么映射呢?把一个很长很长的特征向量,映射到要预测的输出结果上。比如512维的特征向量,最后要计算两个类,就映射到2个点上得到两个输出,而每个特征向量前面说了,对应9个框的特征(不用怀疑,9个框用的是同一个特征向量,也就是同一块feature map上的区域,神奇吧?)针对每一个框,要区分前景和背景,就是2k个点(k=9)。长长的特征向量,被压缩成要求的2k个输出,经过softmax之后,会计算得到概率。这里需要注意的是,这2k个输出不是一起丢进softmax的。假设k=9,那么其实有18个1x1的卷积核,把512张feature map映射为18个feature map,这样就相当于一个中心点的特征向量从512变为18。这18个点两个两个一组,为别对应着这个中心点,9个不同尺寸框分别是前景和背景的概率。是两个两个丢进sortmax算的。
关于reshape。从下图中右图可以看到,经过1x1卷积得到18个feature map之后(18通道,原因之前解释过了),还进行了reshape,目的就是把一个中心点的512特征得到的18个输出,转换为9对,2个2个一对,分别是前景和背景,来丢入softmax,方便进行前景和背景的二分类。就是说,18xWxH的输出,被映射为2x9xWxH。这样看看不出什么,我们以一个中心点来解释。一个中心点的特征向量有512个值,映射出来要计算9个框的前景和背景两类的得分,就是映射为18个取值,然后这18个值分为2个2个一对,就变成了2x9。其实就是[1,2,3,4,5,6,7,8......]变成了[【1,2】,【3,4】,【5,6】,【7,8】......]。就是这么简单。那么softmax之后的reshape也很简单了,就是把这个2x9再变回18。也就是1x2x9变为1x18。道理相同。然后我们就可以得到每一个框分别是背景和前景的概率向量啦。它经过最后一个reshape之后又是一维的,就比较好处理了。
回归:回归与分类的输入同理,只是回归的是四个偏移量,具体是水平偏移量x\y和缩放偏移w和h,一共4个回归结果。输出也就从2k变成了4k的。因为每个框,有了4个回归值。回归计算Loss的时候,就会用到前面anchor生成的特征。可能比较疑惑的是,为什么9个框用了同一个参数来回归和分类,却还能得到不同框的可能性?也就是同一个区域的特征,我咋就能判断出9个不同区域的类别和回归出偏移量了呢?不应该不同的框对应不同的特征吗?这是个令人费解的地方。其实 是因为Loss强迫模型去学到这种区别。比如这个区域的特征判断这个目标是一块黑白条纹,可能他就会选择很大的那个框,而不是选择小的那个框,因为这个条纹很可能是斑马的一部分。其实也就是模型学习的Loss强迫模型学到了这种特征,在目标上是局部还是全局。从而判断应该选大框还是小框。不同于分类,回归的输出也是连续的量。至于为什么不用reshape,当然是因为没有softmax这种需要东西对输入的要求。回归就直接y=Wx+b出来了。而softmax每次要求输出的必须是几个类,且每一个类只有一个输入,如果是9个框,那前景这一类就有9个输入了不是嘛,这是不允许的。softmax不能这样工作。当然你也可以改改它,不过没什么意义。
loss:如何计算Loss?首先我要知道RPN最后检测出来的结果,才能和groudtrue比对,以计算Loss对吧?这些框的得分都计算完了之后,并不是最终结果,下右图proposal灰色那一块,就是proposal处理过程。现在我已经得到了分类和回归的结果,这么多个框,我先进行一个iou计算,把与Label集合中重合率(准确来说是交并比)达到一定阈值的标记为正样本,不满足也不关系,以一个groundtrue为单位,计算与她重合率最大的,那怕不满足阈值,我也认为他是正样本。然后阈值特别低的是负样本,标记为0.有那种不正不负的,就标记为-1,在计算的时候,不计入loss,舍弃掉。(PRCV2018有人改进了这个,这些不正不负的样本,再挑选挑选,其实增加了计算量,换取了结果的轻微轻微提升。)到了这里,我们会发现,一个gt会对应了很多框,他们贴得很近,都是重复很大,没啥意义,这个时候就经过nms选取最好的,又进行了一次筛选。现在所有框都被打上了标记,正样本和负样本都有了,但是可能还是太多了,所以设置一个每张图片最多框参数,比如100,就意味着这些正负样本不能超过100张,具体就是排序后,筛选,反正就是再筛选一遍,保证训练数据的质量。最后训练的时候,正负样本的数量基本是不平衡的,负样本一般都比正样本多,所以要做样本平衡,随机挑选100个正样本和100个负样本(可能不是100,faster初版可能没加这个机制,我没查,但标准应该这样处理),然后就可以计算了Loss了。分别计算回归误差和分类误差。计算方式就不说了。网上一大堆。
RPN结果:在端到端训练的时候,RPN的结果还要继续往下传,传出的和测试的时候一样,是一堆判断为正样本的框。这些框坐标,以原图为参考系的坐标。输出到roi-pooling
4、ROI-Pooling
输入有两个:share_conv(前一个conv层的feature map),一个是RPN输入的4个坐标+索引。四个坐标是原图中的坐标,而不是前一个conv的feature map上的,之后会对坐标进行转换,投射到feature map上。投射之后的区域被crop输入到roi-pooling层,进行一个特殊的Pooling,将所有crop的roi feature 池化为固定的大小feature(因为全连接层的神经元数量、每个神经元的连接数都是确定的,所以只能接受固定大小的输入,)。这个特殊的操作如下:
首先我们知道了pooling之后的大小是多少,然后按这个已知结果大小去计算feature patch中那一块对应输入的哪一个值,然后在这个块中取最大或者平均。问题:为什么不取最小?0是黑色,我们的关键特征是黑色,是不是应该取最小?
5.全连接层
最后,如下图所示:数据经过几个全连接(紫色)和relu激活函数,特征向量输入到回归器和分类器里面。全连接的每一个连接都是权重,与cnn的权重不一样。
二维的特征图要输入到一维的全连接层,得到一维的特征向量,这个过程中,feature map首先是被拉成一个向量,这个操作叫flatten。flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。效果如下图所示:
6.回归、分类
【回归】回归器的作用是产生一个偏移量,使得输入的固定区域的anchor能够变换到更贴近目标实际的位置。设想本来anchor9个框的位置就是固定的,那么图上为什么会出现不在设定好的anchor框位置上的框呢?因为回归器对anchor的固定框进行了修正,使图上的框可以出现到更多位置。(回归与分类的区别就是,回归是连续的拟合,即:回归计算得到的数据是连续的变量,分类结果的变量是离散的,虽然softmax做了概率分布转换)。如下图所示,回归器输入feature map上crop抠出来的roi特征块,目标是计算平移的两个偏移量x\y以及尺度缩放的偏移量w\h。这个公式是线性的。所以要求ground true 和region proposal 长宽近似,这就表明回归只能是微调。RCNN里面通过限制iou的比例,来控制是否计算回归误差,从而来保证这个gt和proposal长宽近似的约束条件。faster的我倒是没看,但这个约束肯定要有,不过不一定是iou,只要能保证长宽近似的约束,都可以。实际上,回归器不等于这个层,而是这个层加上cnn网络,整个叫做回归器。强迫网络学习到这种映射,从anchor映射到真实框。这种映射也是一种关系,所以网络学习到的是一种转化关系,转化anchor固定位置的框到更贴近实际的框。然后如前所述,因为计算是线性的,所以做了约束。实际计算中还用了Log。是因为这样能保证放缩偏移量的计算结果具有非负性质。这个映射在计算上,就是连接权重w。
7.LOSS计算:前面说过一次了,不过经过后面的分类和回归,其实loss是加了点东西的,计算方式一样,原理一样,只是带入了后面的分类和回归结果来计算Loss,最后两部分的Loss加和,得到最终loass
8.梯度更新:全连接层的梯度更新也卷积层的梯度更新也是一样的,Bp算法,没什么可说的。
9.SGD:SGD是随机梯度下降优化方法。当然也会有鞍点和局部平衡点的问题。也没什么好说的,神经网络基本问题了。
----------------------------
到这里结束。把原理全部弄懂之后,就可以看代码了。