当今最为流行且性能较好的物体检测模型,从论文中给出的性能上看,主要是: faster R-CNN,RetinaNet,YOLO V3,性能应该是越来越好,然而,作为 two-stage的state of the art且非常typical的方法,对于比较饶人的 faster R-CNN模型的学习似乎无法跳过,由于想用 faster R-CNN模型训练自己的数据,所以果断入坑,先想详细学习https://github.com/jwyang/faster-rcnn.pytorch 代码实现。
加载训练模型所需要的数据。
Faster R-CNN的模型主要由两个部分组成:与R-CNN和Fast R-CNN中候选框提取算法——selective search算法不同,Faster R-CNN希望通过对于RPN的训练使得卷积神经网络能够自动学习出大约2000个region proposals。
先给出https://github.com/jwyang/faster-rcnn.pytorch 的代码文件夹结构:
其中faster-rcnn-pytorch-master文件夹直接包含了trainval_net.py和test.py,cfgs文件中给出的是对于网络的配置文件,以cfgs文件夹中的res101.yml为例子
RPN_POSITIVE_OVERLAP: 0.7 表示训练RPN时,anchor boxes与ground truth boxes之间的IOU大于0.7则标记为正样本。
RPN_BATCHSIZE: 256 表示训练RPN计算损失函数时,将按照faster R-CNN论文中所说,先按照anchor boxes与gt框之间的IOU进行对所有anchor boxes的正负样本划分,所有IOU阈值(overlap)大于0.7被标记为正样本,IOU阈值小于0.3则被标记为负样本,然后随机从所有正样本集和所有负样本集中分别选出128个正样本和128个负样本(比例1:1),计算损失函数。也就是说,对于RPN而言如果是输入一张图像进行训练,则训练RPN的batch size=256,相当于是输入了256个anchor框,而那一张训练图像仅仅是提供所有anchor 框抠取特征的shared feature map。
BATCH_SIZE: 128
POOLING_SIZE: 7 将所有尺寸不同的bounding boxes(这里指的是经过RPN输出后,可以认为此时RPN已经训练好,RPN的功能就类似于selective search算法的作用,为后面fast R-CNN的训练提供大约2000个region proposal)映射到shared feature map,从共享卷积特征图上抠取出特征图后,进行7*7的ROI align或者ROI pooling(这里插句题外话,今天偶尔看到一个问题,为什么Fast R-CNN中的ROI pooling操作效果会比SPPnet的 spatial pyramid pooling效果好,一时间竟无法回答出来,看来是时候有必要总结下从 R-CNN SPPnet Fast R-CNN Faster R-CNN 这一系列two stage method for object detection的发展了,这是题外话,以后有机会再总结)
下面直接看到trainval_net.py的main函数部分,如果我们是想要训练一个分类或者分割网络,(其实在这想感叹,平时也常常听老板说,计算机视觉CV三大任务,分类检测分割,只有检测对于工程能力的要求会很高,分类和分割相对可以花更多精力在研究网络模型上面,我也渐渐体会到了,就单单从输入图像来说,分类分割任务输入的就是一张图像,具体来说可以做些数据增强变换之类的,但是检测任务如果输入的是一张图像,以two stage method为例,就拿训练faster R-CNN来说,训练RPN——region proposal network实际上并不复杂,它就相当于训练只有一个尺度特征图的SSD,输入到RPN是输入图像变换得到的那张特征图,然后和所有anchor boxes的ground truth计算分类损失和回归损失;而对于后面的Fast R-CNN则有很大不同,它的输入既不是原始的rgb输入图像,也不是rgb图像经过卷积网络得到的特征图,而是fast R-CNN网络将RPN看作是selective search的作用,在看fast R-CNN模型的训练过程的时候,就在心里想着假设这时RPN已经训练好了,把它当作参数固定的模型,然后RPN会给出2000个region proposal的绝对坐标值)则网络的输入是一张图像。
imdb, roidb, ratio_list, ratio_index = combined_roidb(args.imdb_name)
一、def combined_roidb(imdb_names, training=True):
先不管 def combined_roidb(imdb_names, training=True):中的另外两个子函数 def get_training_roidb(imdb): 和
def get_roidb(imdb_name):,直接跳到下面的部分,此时传入实参imdb_name=args.imdb_name='voc_2007_trainval'
其中def combined_roidb(imdb_names, training=True)的函数操作主要由以下4个部分组成。
1.roidbs=get_roidb(imdb_names)
(1)imdb = get_imdb(imdb_name)
def get_imdb(name):将会返回一个pascal_voc类,在factory.py函数中有一个字典__sets,字典中的key对应于输入的训练数据集名称,value对应于实例化后的数据集类名称。所有数据集的类class都是imdb class的子类,能够继承imdb父类的所有方法,把比如现在就会实例化pascal_voc类,而get_imdb方法就会返回实例化之后的对象。注意,对于python中的class类,一旦实例化了这个类之后,就会调用class的__init__方法,在实例化之后的对象的相应属性位置填充contents,而并不会调用class中所包含的其他方法。
class pascal_voc(imdb):中重要的几个attributes是:
类的属性 | 数据类型 | 含义 |
self._data_path | string | 包含训练数据集所有图像的绝对路径 |
self._class_to_ind | dict | 在python中将两个列表转换成一个字典 将self._classes和xrange(self.num_classes)两个列表合并成一个字典,{'__background__':0,aeroplane':1,'bicycle':2} |
self._image_ext | string | 图像后缀名 '.jpg' |
self._image_index | list | 从包含训练数据集所有图像的txt文件中读取每一行,向列表中append一个表项,列表中的元素顺序与txt文件中行数顺序一致,列表长度为训练数据集中图像数 |
(2)imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
self.roidb_handler = self.gt_roidb
这里需要注意,由于self.gt_roidb是类中的方法,则这样的赋值之后是说self.roidb_handler方法与self.gt_roidb方法相同,如果改写成
self.roidb_handler = self.gt_roidb()
则所要表达的意思是self.roidb_handler的值是self.gt_roidb()方法的返回值,则self.roidb_handler可能就是属性而不是方法。