以RBG大神的Faster RCNN源码与一部分自己修改的多图片多gpu的faster rcnn源码为例,介绍下整个网络的计算流程与中间涉及的blobs维度,以防止自己以后再忘记其中的细节
Data的读取格式
要想对模型进行训练,首先需要准备好数据,确定训练数据的格式,包括如何读入、如何遍历、数据增强与label形式等方面。
对于Faster RCNN,其数据的读取定义为一个基类imdb,即image database,在这个基类上定义基于自己数据集的子类,即可无需重写读取、遍历等函数。imdb类主要需要完成如下函数:
1、image_path_at(i):给定图片的索引,得到图片的绝对地址
2、_get_widths():所有图片的宽度
3、roidb():其实际为通过@property装饰器变为属性的方法,返回roidb,也即下面介绍的roidb
标签的读取则为一个list,roidb,其每一个元素是一个字典,包含了第i个图片的长、宽、bbox与area。在使用以coco为基准的标注方法时,可以使用cocoapi中的getAnnIds与loadAnns轻松得到图片的标注信息。
但须注意的有:
1、coco中bbox的标注形式是[x, y, h, w],需要转换成[x1, y1, x2, y2]
2、有些图片中并不包含bbox,需要将这些图片从roidb中删除。之前我们提过imdb与roidb都是为了数据的读取,但是为什么不需要同时对imdb更新呢?看下条
3、在从imdb获得roidb后,roidb仅包含有boxes
、gt_classes
、gt_overlaps
、flipped
信息,在训练前,要将roidb中的信息进行更新与丰富,函数位于lib/roi_data_layer/roidb.py
中,目的是训练将不再依靠imdb,而是通过roidb来进行图片与label的抽取
4、对bbox的归一化与bbox回归target的计算,均在训练前完成,然而在有RPN的情况下,并不需要这一步,因为bbox targets是由RPN给出,并通过anchor_target_layer
来计算出bbox targets的
统一了image和label的格式后,数据便可以通过RoiDataLayer来进行batch的读取与处理了。
RoiDataLayer
参数
num_class: 类别数
Blobs
top[0]: data # 每个batch的图片信息,格式为(N, C, H, W)
top[1]: im_info # 图片的大小信息与缩放信息
top[2]: gt_boxes # 图片中的ground truth bboxes
forward
在每次forward过程中,首先会取出当前batch的图片与label。每次取的时候并不直接取图片与label,而是先取index,根据index进入lib/roi_data_layer/minibatch.py/get_minibatch
函数中对图片进行和label进行处理。
get_minibatch(roidb, num_classes)
功能:
给定当前batch的index下的roidb,从中采样出下一个batch的数据
步骤:
1、从给定的多个尺度中选择一个作为当前尺度的最短边,根据长宽比确定resize后图片的大小
2、由于使用了Inception作为Faster RCNN的Backbone,为了保证下采样中concat层维度不出问题,需要保证resize后的大小为网络feat_stride
的整数倍
3、读取图片,对图片进行resize、argumentation、归一化、channel_swap等
4、根据图片的缩放比例,得到im_scale
数据,用来对bbox进行同样尺度的缩放
5、图片读取完毕后,要对图片中包含的bbox进行尺度的缩放
6、将bbox存储进gt_boxes
,gt_boxes
是维度为(#bboxes_in_cur_image, 6)的矩阵,其中(i, 0)代表图片中第i个bbox所属于的图片在当前batch中的index,(i, 1:5)存储了bbox的坐标,(i, 5)存储了第i个bbox所属的类别
7、将图片信息存储进im_info
中,每个元素保存了当前图片的长宽与缩放信息
数据的流动
明白了数据的格式与输入的处理,便可以知道数据是如何在网络中流动的了。如下图所示,数据的流动以红线表示,label的流动以蓝线表示,粗线则代表inference时候的数据流动,不过inference的时候会跳过proposal target layer,proposal的结果直接送给ROI Pooling。
这部分将会对网络的整体进行笼统的介绍,并在后面对每个层进行说明。
RoiDataLayer
总共有三个输出,分别是data
、gt_boxes
与im_info
,分别包含了图片、bbox与图片信息三部分。其中图片被送入Backbone中,进行特征的提取,并输出为原图的feat_stride
分之一的特征图。
RPN以特征图作为输入,输出一系列region proposal。对于大小为 H × W H \times W H×W的特征图,若每个空间位置有 k k k个anchor,将会分别得到维度为 H × W × 2 k H \times W \times 2k H×W×2k的anchor的分类结果与维度为 H × W × 4 k H \times W \times 4k H×W×4k的bbox回归结果。根据anchor的分类结果、anchor的bbox回归结果与im_info
中的图片缩放信息,可以得到一定数量的region proposal。为了对RPN进行有效的训练,需要对RPN引入监督信息,实现的时候则是对RPN的每个anchor的分类结果与回归结果计算loss并反向传播误差。
根据RPN给出的region proposal区域,根据设定好的阈值从中筛选出一定数量的foreground与background,再将这些筛选过的区域与图像中的gt_boxes
进行比较,得到回归需要的偏移量作为对bbox回归的标签,同时提取出gt_boxes
包含的类别信息,作为对bbox分类的标签。
上面筛选出的所有foreground与background区域将会被从Backbone输出的特征图上切下来,进行分类与回归。这里切割使用的就是ROIPooling或ROIAlign,一般会切出来 7 × 7 7 \times 7 7×7大小的区域,将这部分特征图将会进入fc进行分类与回归。分类的结果与上面提取出来的类别进行交叉熵求loss,回归的结果将会与上面求出的偏移量进行对比,计算其Smooth L1 loss。这样,整个Faster RCNN的框架就搭建起来了。
AnchorTargetLayer
为了能够对RPN输出的每个anchor的分类结果与回归结果进行计算,需要把gt_boxes
转换成能够与各个anchor进行loss计算的形式,这部分的操作是在anchor_target_layer
中完成的。
参数
scales: 大小尺度列表
ratios: 长宽比列表
feat_stride: 特征图相比于原图的大小比例
allow_border: 允许anchor超出图像边界多少
Blobs
bottom[0]: rpn_cls_score # RPN输出的分类结果,即每个位置的每个anchor是前景还是背景的概率
bottom[1]: gt_boxes # 图片中的ground truth bboxes
bottom[2]: im_info # 图片的长宽与缩放比例
top[0]: rpn_labels
top[1]: rpn_bbox_targets
Setup
根据定义的长宽比与大小生成一系列anchor,这里的anchor只是基准anchor,若scale=[2, 4, 8, 16], ratio=[0.5, 1, 2]
则生成的anchor数量为 3 × 4