MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《8》

Posted 寅恪光潜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《8》相关的知识,希望对你有一定的参考价值。

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《1》:论文源地址,克隆MXNet版本的源码,安装环境与测试,以及对下载的源码的每个目录做什么用的,做个解释。

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《2》:对论文中的区域提议、平移不变锚、多尺度预测等概念的了解,对损失函数、边界框回归的公式的了解,以及共享特征的训练网络的方法。

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《3》:加载模型参数,对参数文件的了解,以及感兴趣区域ROI和泛洪填充的方法(FLOODFILL_FIXED_RANGE,FLOODFILL_MASK_ONLY)

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《4》:下载与熟悉Pascal VOC2007,2012语义分割数据集,明白实例分割除了分类之外,还可以细分到像素级别的所属类别。

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《5》:主要就是熟悉转置卷积与大家所熟知的卷积有什么区别,作用是什么,以及双线性插值等相关知识

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《6》:主要讲解关于参数解析的安全执行(ast.literal_eval),ROI池化以及计算图的可视化的处理

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《7》:打印内容(比如参数文件里的东西)的三种方式以及对奇异值分解(SVD,Singular Value Decomposition)的熟悉,了解SVD的作用和运用

这篇文章接着从训练模型来看下有哪些相关知识点,主要是对整个模型架构的熟悉,有哪些可选数据集和可选网络模型,使用的是符号式编程,和以往接触的命令式编程有很大不同,初学者可能有点懵,不过不要怕,慢慢积累。因为符号式编程可以很好的提高效率,在深度学习的模型中就是一个计算图,类似建房子的设计图,根据设计图来建造房子。通过这些知识点,我们进一步熟悉这个Faster R-CNN模型。

训练命令:python train.py --dataset voc --network vgg16 --pretrained model/vgg16-0000.params

当然这里由于本人配置低(内存:16G,GPU:GTX 1050,显存大小只有2G【查看命令:dxdiag】),会出现下面的错误:

cudaMalloc failed: out of memory

或者
Check failed: err == cudaSuccess (2 vs. 0) : Name: Softmax3DKernel ErrStr:out of memory

将很多参数都调到了很低,也是没办法训练,只能后期升级硬件配置的时候再回过头来训练了。
所以目前只能将具体的这块训练先注释掉,训练的目的主要是优化参数,所以也不影响我们接下来的学习

'''
    mod.fit(train_data, eval_metric=eval_metrics, epoch_end_callback=epoch_end_callback,
            batch_end_callback=batch_end_callback, kvstore='device',
            optimizer='sgd', optimizer_params=optimizer_params,
            arg_params=arg_params, aux_params=aux_params, begin_epoch=args.start_epoch, num_epoch=args.epochs)
'''

注释掉之后我们来看下训练模型的时候,程序做了哪些操作,训练大模型除了需要数据集外,一般会选择一个恰当的网络。在这里有两个可选的数据集:Pascal VOC,MS COCO和三个可选的网络:VGG16、ResNet-50、ResNet-101

voc_2007_trainval_roidb.pkl参数文件

我们在训练的时候,首先会在data目录下生成了一个cache目录,里面生成了一个voc_2007_trainval_roidb.pkl参数文件,对于pkl文件我们在前面很多文章中都有介绍,大家应该比较熟悉了。我们来读取看下里面保存的是一些什么内容。

import pickle
import pprint

with open('../data/cache/voc_2007_trainval_roidb.pkl','rb') as f:
    params=pickle.load(f)
    #print('参数文件内容如下:\\n'.format(pprint.pformat(params)))
    print(type(params),len(params))#<class 'list'> 5011
    print(params[0])
    print(params[1])
    print(params[2])

'''
'index': '000005', 'objs': ['name': 'chair', 'difficult': 0, 'bbox': [263, 211, 324, 339], 'name': 'chair', 'difficult': 0, 'bbox': [165, 264, 253, 372], 'name': 'chair', 'difficult': 1, 'bbox': [5, 244, 67, 374], 'name': 'chair', 'difficult': 0, 'bbox': [241, 194, 295, 299], 'name': 'chair', 'difficult': 1, 'bbox': [277, 186, 312, 220]], 'image': '../data/VOCdevkit\\\\VOC2007\\\\JPEGImages\\\\000005.jpg', 'height': 375, 'width': 500, 'boxes': array([[262, 210, 323, 338],
       [164, 263, 252, 371],
       [240, 193, 294, 298]], dtype=uint16), 'gt_classes': array([9, 9, 9]), 'flipped': False
'index': '000007', 'objs': ['name': 'car', 'difficult': 0, 'bbox': [141, 50, 500, 330]], 'image': '../data/VOCdevkit\\\\VOC2007\\\\JPEGImages\\\\000007.jpg', 'height': 333, 'width': 500, 'boxes': array([[140,  49, 499, 329]], dtype=uint16), 'gt_classes': array([7]), 'flipped': False
'index': '000009', 'objs': ['name': 'horse', 'difficult': 0, 'bbox': [69, 172, 270, 330], 'name': 'person', 'difficult': 0, 'bbox': [150, 141, 229, 284], 'name': 'person', 'difficult': 0, 'bbox': [285, 201, 327, 331], 'name': 'person', 'difficult': 0, 'bbox': [258, 198, 297, 329]], 'image': '../data/VOCdevkit\\\\VOC2007\\\\JPEGImages\\\\000009.jpg', 'height': 375, 'width': 500, 'boxes': array([[ 68, 171, 269, 329],
       [149, 140, 228, 283],
       [284, 200, 326, 330],
       [257, 197, 296, 328]], dtype=uint16), 'gt_classes': array([13, 15, 15, 15]), 'flipped': False
'''

打印前3个看下,图片索引(名称)、宽高、多个检测目标(名称、边界框、图片、类别索引)以及是否翻转等信息。这些信息来自下面这几个出处:

self._image_index_file = os.path.join(devkit_path, 'VOC' + year, 'ImageSets', 'Main', image_set + '.txt')
self._image_file_tmpl = os.path.join(devkit_path, 'VOC' + year, 'JPEGImages', '.jpg')
self._image_anno_tmpl = os.path.join(devkit_path, 'VOC' + year, 'Annotations', '.xml')

文档图片名称索引,图片名称,和标注文件,其中标注文件xml的读取,将这些文件内容的信息保存到pkl之后对于效率的提升有帮助,有兴趣的可以参阅:Python基础知识之读取XML文件

args参数设置

训练模型有很多参数需要设置,尤其是大模型,参数就特别多了,比如,常见的CPU或GPU环境设置、迭代次数、学习率、学习率衰减迭代epoch、图片相关的边长、像素均值、标准差、预训练模型的参数文件、权重偏置等等。
我们打印看下args大概有哪些东西,使用日志的方式:

logger.info('called with args\\n'.format(pprint.pformat(vars(args))))

内容如下:

INFO:root:loading cache ../data\\cache\\voc_2007_trainval_roidb.pkl
INFO:root:voc_2007_trainval num_images 5011
INFO:root:filter roidb: 5011 -> 5011
INFO:root:voc_2007_trainval append flipped images to roidb
INFO:root:called with args
'dataset': 'voc',
 'epochs': 10,
 'gpus': '0',
 'imageset': '2007_trainval',
 'img_long_side': 1000,
 'img_pixel_means': (123.68, 116.779, 103.939),
 'img_pixel_stds': (1.0, 1.0, 1.0),
 'img_short_side': 600,
 'log_interval': 100,
 'lr': 0.001,
 'lr_decay_epoch': '7',
 'net_fixed_params': ['conv1', 'conv2'],
 'network': 'vgg16',
 'pretrained': 'model/vgg16-0000.params',
 'rcnn_batch_rois': 128,
 'rcnn_batch_size': 1,
 'rcnn_bbox_stds': (0.1, 0.1, 0.2, 0.2),
 'rcnn_feat_stride': 16,
 'rcnn_fg_fraction': 0.25,
 'rcnn_fg_overlap': 0.5,
 'rcnn_num_classes': 21,
 'rcnn_pooled_size': (7, 7),
 'resume': '',
 'rpn_allowed_border': 0,
 'rpn_anchor_ratios': (0.5, 1, 2),
 'rpn_anchor_scales': (8, 16, 32),
 'rpn_batch_rois': 256,
 'rpn_bg_overlap': 0.3,
 'rpn_feat_stride': 16,
 'rpn_fg_fraction': 0.5,
 'rpn_fg_overlap': 0.7,
 'rpn_min_size': 16,
 'rpn_nms_thresh': 0.7,
 'rpn_post_nms_topk': 2000,
 'rpn_pre_nms_topk': 12000,
 'save_prefix': 'model/vgg16',
 'start_epoch': 0
INFO:root:max input shape
'bbox_target': (1, 36, 62, 62),
 'bbox_weight': (1, 36, 62, 62),
 'data': (1, 3, 1000, 1000),
 'gt_boxes': (1, 100, 5),
 'im_info': (1, 3),
 'label': (1, 1, 558, 62)
INFO:root:max output shape
'bbox_loss_reshape_output': (1, 128, 84),
 'blockgrad0_output': (1, 128),
 'cls_prob_reshape_output': (1, 128, 21),
 'rpn_bbox_loss_output': (1, 36, 62, 62),
 'rpn_cls_prob_output': (1, 2, 558, 62)
INFO:root:locking params
['conv1_1_weight',
 'conv1_1_bias',
 'conv1_2_weight',
 'conv1_2_bias',
 'conv2_1_weight',
 'conv2_1_bias',
 'conv2_2_weight',
 'conv2_2_bias']
INFO:root:lr 0.001000 lr_epoch_diff [7] lr_iters [70154]

这些信息都是来自logger.info的打印,比如打印最大输入和输出形状:

logger.info('max input shape\\n%s' % pprint.pformat(data_shape_dict))
logger.info('max output shape\\n%s' % pprint.pformat(out_shape_dict)) 

训练模型

这里由于本人配置太低,训练不了,我们来了解下训练的过程,这里是符号式编程,关于符号式编程可以搜索本人博客,有好几篇介绍的,跟这节最密切的可以查阅:MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《6》,这里还讲解了如何将符号式编程的计算图给可视化。
我们先打印整个网络看下有哪些层:

sym = get_network(args.network, args)
print(sym)

发现是一个group,那就是几个的组合了

<Symbol group [rpn_cls_prob, rpn_bbox_loss, cls_prob_reshape, bbox_loss_reshape, blockgrad0]> 

打印它们的列表参数名:print(sym.list_arguments())

['data', 'conv1_1_weight', 'conv1_1_bias', 'conv1_2_weight', 'conv1_2_bias', 'conv2_1_weight', 'conv2_1_bias', 'conv2_2_weight', 'conv2_2_bias', 'conv3_1_weight', 'conv3_1_bias', 'conv3_2_weight', 'conv3_2_bias', 'conv3_3_weight', 'conv3_3_bias', 'conv4_1_weight', 'conv4_1_bias', 'conv4_2_weight', 'conv4_2_bias', 'conv4_3_weight', 'conv4_3_bias', 'conv5_1_weight', 'conv5_1_bias', 'conv5_2_weight', 'conv5_2_bias', 'conv5_3_weight', 'conv5_3_bias', 'rpn_conv_3x3_weight', 'rpn_conv_3x3_bias', 'rpn_cls_score_weight', 'rpn_cls_score_bias', 'label', 'bbox_weight', 'rpn_bbox_pred_weight', 'rpn_bbox_pred_bias', 'bbox_target', 'im_info', 'gt_boxes', 'fc6_weight', 'fc6_bias', 'fc7_weight', 'fc7_bias', 'cls_score_weight', 'cls_score_bias', 'bbox_pred_weight', 'bbox_pred_bias']

打印看下有哪些输出层:print(sym.list_outputs())

['rpn_cls_prob_output', 'rpn_bbox_loss_output', 'cls_prob_reshape_output', 'bbox_loss_reshape_output', 'blockgrad0_output']

区域提议网络的类别预测输出、区域提议网络边界框损失输出、类别预测形状变换输出、边界框损失形状变换输出、背景输出
比如这里我们查看下输出的形状如下:

INFO:root:max output shape
'bbox_loss_reshape_output': (1, 4, 84),
 'blockgrad0_output': (1, 4),
 'cls_prob_reshape_output': (1, 4, 21),
 'rpn_bbox_loss_output': (1, 36, 31, 31),
 'rpn_cls_prob_output': (1, 2, 279, 31)

对应的输入形状也贴出来如下:

INFO:root:max input shape
'bbox_target': (1, 36, 31, 31),
 'bbox_weight': (1, 36, 31, 31),
 'data': (1, 3, 500, 500),
 'gt_boxes': (1, 100, 5),
 'im_info': (1, 3),
 'label': (1, 1, 279, 31)

当然这里我们可以可视化来看下整个的计算图,显得更加的清晰明了

import mxnet as mx
mx.viz.plot_network(symbol=sym).view()

生成的pdf,计算图特别的长,如下就是整个模型的架构了:

自定义评价函数

train.py的源码中我们看到有这样一些函数

rpn_eval_metric = RPNAccMetric()#RPN网络的分类准确率
rpn_cls_metric = RPNLogLossMetric()
rpn_bbox_metric = RPNL1LossMetric()
eval_metric = RCNNAccMetric()#RCNN的分类准确率
cls_metric = RCNNLogLossMetric()
bbox_metric = RCNNL1LossMetric()
eval_metrics = mx.metric.CompositeEvalMetric()
for child_metric in [rpn_eval_metric, rpn_cls_metric, rpn_bbox_metric, eval_metric, cls_metric, bbox_metric]:
    eval_metrics.add(child_metric)

这些都属于自定义的评价函数,然后通过mx.metric.CompositeEvalMetric()这个方法将这些自定义函数分别添加进来,这里我们可以看到有6个自定义评价函数,具体的定义大家可以去源码查看,这里看名称相信大家都明白是一些什么函数,一些精度评价以及对边界框的log损失和L1损失,分别是两个网络,RPN和RCNN,区域提议网络和区域卷积神经网络,定义好这些评价函数之后,在训练模型的时候mod.fit(train_data, eval_metric=eval_metrics...)就可以使用它了

我们来熟悉mx.metric里的一些方法的使用,看个示例,来自源码

import mxnet as mx

predicts = [mx.nd.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
labels = [mx.nd.array([0, 1, 1])]#后面两个预测正确,也就是3个预测对了2个
eval_metrics_1 = mx.metric.Accuracy()
eval_metrics_2 = mx.metric.F1()#F1分数相当于精度和召回率的加权平均(这个F1分数只支持二元分类)
eval_metrics = mx.metric.CompositeEvalMetric()
for child_metric in [eval_metrics_1, eval_metrics_2]:
    eval_metrics.add(child_metric)
eval_metrics.update(labels=labels, preds=predicts)

#返回当前的评估结果
print(eval_metrics_1)#EvalMetric: 'accuracy': 0.6666666666666666
print(eval_metrics_2)#EvalMetric: 'f1': 0.8

print(eval_metrics.get())#(['accuracy', 'f1'], [0.6666666666666666, 0.8])

以上是关于MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《8》的主要内容,如果未能解决你的问题,请参考以下文章

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《10》(尾)

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《7》

R-CNN , Fast R-CNN , Faster R-CNN原理及区别

R-CNN/Fast R-CNN/Faster R-CNN

Faster R-CNN 学习

Faster R-CNN:使用RPN实时目标检测