刚开始接触深度学习目标检测的时候,买了一本书“深度学习卷积神经网络从入门到精通”,这本书里面介绍的一些经典目标检测的理论和基于tensorflow的代码。但是当时根本就代码根本就看不懂,虽然有好多人说要学会看代码,但根本就进不去,所以我就练习吴恩达深度学习和cs231n的习题作业。当时好像花了挺长时间,应该是两个月,但是现在回想起来感觉很不值。因为里面的练习都是很基础的,简单的神经网络和卷积神经网络搭建和分类,对于二阶段目标检测的训练没有用。当我想到github上看一些目标检测的pytorch代码时,发现文件夹结构太复杂,文件夹的意义搞不懂,文件夹里面的文件内容也搞不懂,而且感觉文件的名字有好多差不多,看代码也看不出来区别在哪,根本就看不下去。后来我就像从最基本的RCNN的代码开始看起吧,因为没有更简单的了,但是在github上没有找到RCNN的,基本都是FASTER-RCNN的,而且在网上查到陈云的simple-faster-rcnn-pytorch项目比较简单易懂,于是从这个项目开始看。这里总结一下。

首先是dataset类,dataset类表示数据集。(Dataloader  前面提到过,在训练神经网络时,最好是对一个batch的数据进行操作,同时还需要对数据进行shuffle和并行加速等。对此,PyTorch提供了DataLoader帮助我们实现这些功能。这里先不谈这个)dataset类里面有对图像的预处理和数据的读取。(对于数据标签的制作和索引文件的制作这里先不谈)。dataset类会根据索引文件里面的图像路径和xml文件读取信息。在该项目里,dataset类在data文件夹里(有的项目不在data文件夹里,我们应该首先找到dataset类相关,无论怎样,肯定要有这个类)。我们也看到,在data文件夹里,有dataset.py和voc_dataset.py。我第一次看到的时候,觉得问什么会有两个dataset文件。voc_dataset.py是要从xml文件里读取图像、目标框和标签。在dataset.py里,有一些对图像的预处理和相应的目标框的变化。然后在getitem里调用voc_dataset里的getexample(类似于getitem)。就是在dataset.py里面实例化调用voc_dataset.py里面的函数。

表示数据集的函数好了之后,就是模型的函数了。我的第一直觉就是在model文件夹中找fasterrcnn的文件。但是进入文件夹一看,除了faster_rcnn.py还有faster_rcnn_vgg16.py。前者提供了faster-rcnn的基础类,是一个接口,包含了特征提取、RPN和ROIHEAD。此外,还定义了预测等函数。这里提一下def _suppress,经过该函数的处理,ROIHEAD才得到最终的输出。faster_rcnn_vgg16.py里面的类继承了faster_rcnn.py的接口,进行实现,并定义了特征提取、RPN和ROIHEAD的类。

特征提取是将VGG16结构进行分解,VGG16模型在配置文件中设置,这里不懂,先放这。

RPN网络对应的是region_proposal_network.py 。 RPN的逻辑顺序是con_5特征上的每个点产生9个anchor。先找生成9个anchor的函数。这个函数在model.utils.bbox_tools里,def generate_anchor_base。(我觉得这个函数应该也放在region_proposal_network.py 里,但是人家的结构是这么弄的,我们需要知道要有生成anchor的函数)。def generate_anchor_base返回anchor_base,大小为(9,4),9个anchor的左上角坐标和右下角坐标。anchor是映射回原图上的。里面h和w是原图上anchor的长和宽,公式是可以可以推导出来的,这里懒得写了,如果不理解没关系,直接用就可以了。有了anchor_base之后,我们就要将anchor_base在con_5特征图上进行滑动。(论文里说是用一个3*3的卷积滑动,然后con_5上的每个点产生9个anchor。这里可以这样理解。con_5之后,先用3*3的卷积进行正常卷积,得到的结果再用两个1*1的卷积进行分类和边框回归。)滑动的函数是region_proposal_network.py里的def _enumerate_shifted_anchor(这个不能用cuda加速,作者又给出了def _enumerate_shifted_anchor_torch可以用cuda加速)。def _enumerate_shifted_anchor的思想就是:最初的anchor就是anchor_base,在特征图的左上角,特征图有多大,就分别往右和往下移动多少次,每次移动的大小是原图到该特征图缩小的比例。最后返回所有的anchor。得到所有的anchor之后,需要计算所有anchor的得分和偏移量。从测试的角度看,之后应该生成传入ROIHEAD的ROI。生成ROI的函数为model/utils/creator_tool.py里面的class ProposalCreator。

ProposalCreator的逻辑顺序是,根据得到的所有anchor的loc,将原图上的anchor进行调整。然后将原图上对应位置的图进行裁剪,我们会设置一个最小值,只有裁剪图的长和宽都大于等于最小值,裁剪图才会被保留。将保留下来的裁剪图的得分进行排序。在进行NMS前会设置anchor数量的大小,设为n(训练和测试的时候数量大小不一样。)。取得分在前n个的anchor进行NMS。然后将这些anchor进行NMS,最后得到ROI。

接下来就是RPN的训练问题了。class AnchorTargetCreator是生成训练用的得分和偏移量的真实值的标签。首先应该保留大小都在图像范围内的anchor。然后为这些anchor赋类别的标签。赋标签的过程,先计算anchor与真实框的iou。然后每个anchor选择出与哪个真实框的iou(argmax_iou)最大和该iou的值,对于n个真实框,记录下哪个anchor与该真实框有最大iou。将这三个值返回创建label的函数。对于iou大于阈值的和与真实框有最大值的anchor的值设为1,iou小于设置的另一个阈值的anchor设为0.然后将label和argmax_iou返回。计算anchor与对应的最大iou的那个bbox的偏移量作为真实值。现在得到的label和loc都是在图像范围内的anchor,我们还要填充剩余anchor的数值,返回所有anchor的值。