关于python版本的Faster Rcnn的使用

来源:互联网 发布:java代码流程图 编辑:程序博客网 时间:2024/06/06 01:11

第一篇博客,水平有限,如有错误之处还望各位指出,有问题欢迎大家一起讨论。大笑大笑大笑

本文主要写了一些关于py-faster rcnn的使用心得,用它来训练自己的数据集,同时使用官方提供的一些工具,来完成一些检测任务。

0、前期准备

已搭建好caffe、已安装python,opencv等。

已安装好Py-Faster-Rcnn,并已完成配置,可使用官方提供的模型和demo.py来检测图片。

我们使用的系统是ubuntu 14.04版本,GPU为GTX980ti ,6G显存,亲测可跑VGG16(batchsize 16).(注,该显卡在windows下的matlab版本的Faster Rcnn无法跑VGG16,显存不够。。。。敲打)

一、准备数据集
我们使用的是pascal VOC2007 格式的数据集。需要准备图片,包含图片标注信息的xml文件,设置的训练验证集和测试集的txt文档。
在路径py-faster-rcnn/data/VOCdevkit2007/VOC2007/ 下:
Annotations:放入xml文件。
JPEGImages:放入图片。
ImageSets/Main:放入 trainval.txt 和 test.txt
我们是直接使用自己的数据集代替官方的pascal VOC2007数据集来进行训练的。所以在数据集的准备上参考pascal voc2007的格式即可。

二、训练方式选择
py-faster rcnn 提供了两种训练方式:
1.四步交替训练方法(官方的matlab版本只有这种训练方式)

该训练方法第一步是训练RPN网络,第二步训练Fast-rcnn网络,第三步固定卷积层(开始共享卷积)只训练RPN独有的层,第四部只训练Fast-Rcnn特有的层。详细过程可参考论文,和训练代码: py-faster-rcnn/tools/train_faster_rcnn_alt_opt.py。该方法训练时间相对较久一些,且以后的使用中参数设置,和网络设置相对麻烦一点。但其检测效果稍微优于端对端的训练方法。

2.端对端的近似训练方法

该方法直接将RPN网络和Fast-Rcnn网络融合为一个整体进行端对端的训练。训练收敛速度快,网络设置修改简单,效果稍微差于四步训练的方法。详细过程可参考论文,以及训练代码:py-faster-rcnn/tools/train_net.py。

综上所属推荐使用端对端的训练方式,操作简单,易于修改,训练速度快。

三、网络的选择

官方提供了ZF网络和VGG网络(VGG16和VGG_M_1024)的网络结构,和相应的在Imagenet数据集下的预训练网络模型。(只要熟悉以后我们也可以自己设置网络模型,不用预训练模型从初始状态开始训练)
在确定了训练方式之后,进入所选择的训练方式的模型下可查看相应的网络模型,以及训练超参数。
例如:我们选择使用端对端的训练方式,ZF网络。
则在路径:py-faster-rcnn/models/pascal_voc/ZF/faster_rcnn_end2end/ 下有solver.prototxt,test.prototxt,train.prototxt 三个文件。其中,train.prototxt 即是ZF网络的训练模型。test.prototxt 即ZF网络的测试模型。修改模型可在这两个文件中修改,同时在多类别检测任务中,类别数量需要在这两个文件中修改。若训练中需加载预训练模型来Fine-turning,则所需加载的网络层的名称应该与预训练模型的网络层名称相同。solver.prototxt中则是CNN的训练超参数。
可将test.prototxt或train.prototxt中的内容复制网页 http://ethereon.github.io/netscope/#/editor 中来观看网络结构的可视化情况,用于直观的理解网络结构,以及检查修改后的网络结构是否正确。

由于类别数量改变所需修改处:(由于类别数量的改变,在加载与训练模型时需要修改与类别相关层的名称)
在test.prototxt和train.prototxt中,类别数量和bbox_pred处需要修改。



四、开始训练

(以端对端训练为例,四步训练法同理)
在选择好训练方式,修改好网络模型,设置好训练超参数后。可开始训练。推荐使用官方例程的脚本来启动训练。
端对端训练脚本位于:py-faster-rcnn/experiments/scripts/faster_rcnn_end2end.sh (四步训练则为faster_rcnn_alt_opt.sh)。该脚本程序集成了模型训练,测试集测试,日志保存这几个功能,同时也可以设置训练函数py-faster-rcnn/tools/train_net.py 中所需输入的参数(例如最大迭代次数,是否使用GPU等)。
在该脚本文件中,我们针对自己的数据级需要一些修改:
1.第30行最大迭代次数:ITERS=40000 ;由于我们使用的是pascal-voc的格式,所以修改此处。
2.第53行预训练模型加载地址:  --weights data/imagenet_models/ZF_faster_rcnn_final.caffemodel \(此处表示加载了一个ZF模型为预训练模型,用于做迁移训练。)
其他地方需要注意几个路径地址即可。
训练命令: 在Faster-Rcnn的根目录下打开终端,输入./experiments/scripts/faster_rcnn_end2end.sh 0 ZF pascal_voc  开始使用GPU 0 和ZF网络,以及pascal_voc的格式启动faster_rcnn_end2end.sh脚本程序开始训练我们的数据集。
除此之外,我们也可以使用脚本所调用的训练代码:py-faster-rcnn/tools/train_net.py 来直接进行训练。这样我们需要在命令行中提供一些参数。
例如:python tools/train_./tools/demo.py --gpu 0 --net zffaster_rcnn_alt_opt.py --gpu 0 --net_name ZF --weights data/faster_rcnn_models/ZF_faster_rcnn_final.caffemodel --cfg experiments/cfgs/faster_rcnn_alt_opt.yml --imdb voc_2007_trainval (这是一个四步训练的命令,端对端的方式同理,注意根据train_net.py 中所需的参数来输入)
注:在训练前需清空之前训练的缓存,py-faster-rcnn/data/cache 中的内容 和 py-faster-rcnn/data/VOCdevkit2007/annotations_cache 中的内容。且保证输出py-faster-rcnn/output 为空,或将之前的训练结果保存在其他文件。
训练完成后,模型保存于py-faster-rcnn/output 下。日志保存于py-faster-rcnn/experiments/logs下。

下面是faster_rcnn_end2end.sh:

#!/bin/bash# Usage:# ./experiments/scripts/faster_rcnn_end2end.sh GPU NET DATASET [options args to {train,test}_net.py]# DATASET is either pascal_voc or coco.## Example:# ./experiments/scripts/faster_rcnn_end2end.sh 0 VGG_CNN_M_1024 pascal_voc \#   --set EXP_DIR foobar RNG_SEED 42 TRAIN.SCALES "[400, 500, 600, 700]"set -xset -eexport PYTHONUNBUFFERED="True"GPU_ID=$1NET=$2NET_lc=${NET,,}DATASET=$3array=( $@ )len=${#array[@]}EXTRA_ARGS=${array[@]:3:$len}EXTRA_ARGS_SLUG=${EXTRA_ARGS// /_}case $DATASET in  pascal_voc)    TRAIN_IMDB="voc_2007_trainval"    TEST_IMDB="voc_2007_test"    PT_DIR="pascal_voc"    ITERS=40000    ;;  coco)    # This is a very long and slow training schedule    # You can probably use fewer iterations and reduce the    # time to the LR drop (set in the solver to 350,000 iterations).    TRAIN_IMDB="coco_2014_train"    TEST_IMDB="coco_2014_minival"    PT_DIR="coco"    ITERS=500    ;;  *)    echo "No dataset given"    exit    ;;esacLOG="experiments/logs/faster_rcnn_end2end_${NET}_${EXTRA_ARGS_SLUG}.txt.`date +'%Y-%m-%d_%H-%M-%S'`"exec &> >(tee -a "$LOG")echo Logging output to "$LOG"time ./tools/train_net.py --gpu ${GPU_ID} \  --solver models/${PT_DIR}/${NET}/faster_rcnn_end2end/solver.prototxt \  --weights data/imagenet_models/VGG16_faster_rcnn_final.caffemodel \  --imdb ${TRAIN_IMDB} \  --iters ${ITERS} \  --cfg experiments/cfgs/faster_rcnn_end2end.yml \  ${EXTRA_ARGS}set +xNET_FINAL=`grep -B 1 "done solving" ${LOG} | grep "Wrote snapshot" | awk '{print $4}'`set -xtime ./tools/test_net.py --gpu ${GPU_ID} \  --def models/${PT_DIR}/${NET}/faster_rcnn_end2end/test.prototxt \  --net ${NET_FINAL} \  --imdb ${TEST_IMDB} \  --cfg experiments/cfgs/faster_rcnn_end2end.yml \  ${EXTRA_ARGS}



五、图片检测

图片检测代码位于:py-faster-rcnn/tools/demo.py  。将待测图片存放于:py-faster-rcnn/data/demo/  下。
若直接使用该程序检测,首先需要将我们训练保存的模型复制到py-faster-rcnn/data/faster_rcnn_models/ 路径下。然后再在demo.py中修改模型名称,类别名称,CONF_THRESH,NMS_THRESH阈值等。详细参考demo.py(此处已非原版,现可一张图检测显示多个类别)
注:第132行,端对端训练方式和四步训练方式是不同的,此处是加载网络结构,使用不同训练方式时需要修改。
调用命令:./tools/demo.py --gpu 0 --net zf

下面是我的demo.py

#!/usr/bin/env python#coding=utf8# 因为代码中我加了中文注释,所以 上面这行用于指定编码 ,否则python代码执行会报错 (一张图显示多个类别)# --------------------------------------------------------# Faster R-CNN# Copyright (c) 2015 Microsoft# Licensed under The MIT License [see LICENSE for details]# Written by Ross Girshick# --------------------------------------------------------"""Demo script showing detections in sample images.See README.md for installation instructions before running."""import _init_pathsfrom fast_rcnn.config import cfgfrom fast_rcnn.test import im_detectfrom fast_rcnn.nms_wrapper import nmsfrom utils.timer import Timerimport matplotlib.pyplot as pltimport numpy as npimport scipy.io as sioimport caffe, os, sys, cv2import argparse#CLASSES = ('__background__',#           'aeroplane', 'bicycle', 'bird', 'boat',#           'bottle', 'bus', 'car', 'cat', 'chair',#           'cow', 'diningtable', 'dog', 'horse',#           'motorbike', 'person', 'pottedplant',#           'sheep', 'sofa', 'train', 'tvmonitor')CLASSES = ('__background__',           'standing','sitting','sternal_recumb','ventral_recumb','lateral_recumb')NETS = {'vgg16': ('VGG16',                  'vgg16_faster_rcnn_iter_40000.caffemodel'),        'zf': ('ZF',                  'zf_faster_rcnn_iter_40000.caffemodel')}#zf_fast_rcnn_iter_1000.caffemodel  ZF_faster_rcnn_final-1.caffemodel  VGG16_faster_rcnn_final.caffemodel#增加axdef vis_detections(im, class_name, dets, ax, thresh=0.5):    """Draw detected bounding boxes."""    inds = np.where(dets[:, -1] >= thresh)[0]    if len(inds) == 0:        return# 删除这三行#    im = im[:, :, (2, 1, 0)]#    fig, ax = plt.subplots(figsize=(12, 12))#    ax.imshow(im, aspect='equal')    for i in inds:        bbox = dets[i, :4]        score = dets[i, -1]        ax.add_patch(            plt.Rectangle((bbox[0], bbox[1]),                          bbox[2] - bbox[0],                          bbox[3] - bbox[1], fill=False,                          edgecolor='red', linewidth=3.5)            )        ax.text(bbox[0], bbox[1] - 2,                '{:s} {:.3f}'.format(class_name, score),                bbox=dict(facecolor='blue', alpha=0.5),                fontsize=14, color='white')    ax.set_title(('{} detections with '                  'p({} | box) >= {:.1f}').format(class_name, class_name,                                                  thresh),                  fontsize=14)# 删除这三行#    plt.axis('off')#    plt.tight_layout()#    plt.draw()def demo(net, image_name):    """Detect object classes in an image using pre-computed object proposals."""    # Load the demo image    im_file = os.path.join(cfg.DATA_DIR, 'demo', image_name)    im = cv2.imread(im_file)    # Detect all object classes and regress object bounds    timer = Timer()    timer.tic()    scores, boxes = im_detect(net, im)    timer.toc()    print ('Detection took {:.3f}s for '           '{:d} object proposals').format(timer.total_time, boxes.shape[0])    # Visualize detections for each class    CONF_THRESH = 0.8    NMS_THRESH = 0.3    # 将vis_detections 函数中for 循环之前的3行代码移动到这里    im = im[:, :, (2, 1, 0)]    fig,ax = plt.subplots(figsize=(12, 12))    ax.imshow(im, aspect='equal')    for cls_ind, cls in enumerate(CLASSES[1:]):        cls_ind += 1 # because we skipped background        cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]        cls_scores = scores[:, cls_ind]        dets = np.hstack((cls_boxes,                          cls_scores[:, np.newaxis])).astype(np.float32)        keep = nms(dets, NMS_THRESH)        dets = dets[keep, :]        vis_detections(im, cls, dets, ax, thresh=CONF_THRESH)    # 将vis_detections 函数中for 循环之后的3行代码移动到这里    plt.axis('off')    plt.tight_layout()    plt.draw()def parse_args():    """Parse input arguments."""    parser = argparse.ArgumentParser(description='Faster R-CNN demo')    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',                        default=0, type=int)    parser.add_argument('--cpu', dest='cpu_mode',                        help='Use CPU mode (overrides --gpu)',                        action='store_true')    parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16]',                        choices=NETS.keys(), default='vgg16')    args = parser.parse_args()    return argsif __name__ == '__main__':    cfg.TEST.HAS_RPN = True  # Use RPN for proposals    args = parse_args()    prototxt = os.path.join(cfg.MODELS_DIR, NETS[args.demo_net][0],                            'faster_rcnn_end2end', 'test.prototxt')    caffemodel = os.path.join(cfg.DATA_DIR, 'faster_rcnn_models',                              NETS[args.demo_net][1])    if not os.path.isfile(caffemodel):        raise IOError(('{:s} not found.\nDid you run ./data/script/'                       'fetch_faster_rcnn_models.sh?').format(caffemodel))    if args.cpu_mode:        caffe.set_mode_cpu()    else:        caffe.set_mode_gpu()        caffe.set_device(args.gpu_id)        cfg.GPU_ID = args.gpu_id    net = caffe.Net(prototxt, caffemodel, caffe.TEST)    print '\n\nLoaded network {:s}'.format(caffemodel)    # Warmup on a dummy image    im = 128 * np.ones((300, 500, 3), dtype=np.uint8)    for i in xrange(2):        _, _= im_detect(net, im)#    im_names = ['000456.jpg', '000542.jpg', '001150.jpg',#                '001763.jpg', '004545.jpg']    im_names = ['8172355(13).jpg','12238055(11).jpg',                '8185056(15).jpg', '8499359(12).jpg','8193000(14).jpg']    for im_name in im_names:        print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'        print 'Demo for data/demo/{}'.format(im_name)        demo(net, im_name)    plt.show()


六、测试集评估

测试集评估代码位于:py-faster-rcnn/tools/test_net.py 。该函数用于检测整个测试集的图片,计算评价指标AP和mAP。
使用它,(由于def parse_args():)同样需要在终端给它一些所需的参数。
例如:调用命令 python tools/test_net.py --gpu 0 --def models/pascal_voc/ZF/faster_rcnn_alt_opt/faster_rcnn_test.pt --net output/faster_rcnn_alt_opt/voc_2007_trainval/ZF_faster_rcnn_final.caffemodel --cfg experiments/cfgs/faster_rcnn_alt_opt.yml --imdb voc_2007_test


七、卷积特征可视化

训练好的模型每一层输出特征的可视化,这是一种检验是否训练了一个好模型的参考指标之一。如果卷积图片后特征明显,干净则说明网络有学习到特征,这往往有不错的检测效果表现。如果卷积图片后,特征显示杂乱无章,则往往是网络未收敛,参数设置不当,或网络设计不当等原因,这往往导致很差的检测效果。
代码位于:py-faster-rcnn/tools/feature_visualize.py 。
使用它,需要根据自己的数据修改四个地方:
1.29行等,需要修改类别名,网络地址等。
2.87行,输出卷积特征图的地址
3.183行,带测图片名(图片地址和检测时的地址相同)
4.155行,针对所选择的是四步训练还是端对端训练来修改。

下面是我的feature_visualize.py:

#!/usr/bin/env python#coding=utf8# 因为代码中我加了中文注释,所以 上面这行用于指定编码 ,否则python代码执行会报错 (一张图显示多个类别)# --------------------------------------------------------# Faster R-CNN# Copyright (c) 2015 Microsoft# Licensed under The MIT License [see LICENSE for details]# Written by Ross Girshick# --------------------------------------------------------"""Demo script showing detections in sample images.See README.md for installation instructions before running."""import _init_pathsfrom fast_rcnn.config import cfgfrom fast_rcnn.test import im_detectfrom fast_rcnn.nms_wrapper import nmsfrom utils.timer import Timerimport matplotlib.pyplot as pltimport numpy as npimport scipy.io as sioimport caffe, os, sys, cv2import argparseimport mathCLASSES = ('__background__',           'standing','sitting','sternal_recumb','ventral_recumb','lateral_recumb')#CLASSES = ('__background__',#           'mango')NETS = {'vgg16': ('VGG16',                  '/home/snowflake/Downloads/Faster-Rcnn-Muzy/py-faster-rcnn/tools/vgg16_faster_rcnn_iter_40000.caffemodel'),        'zf': ('ZF',                  '/home/snowflake/Downloads/Faster-Rcnn-Muzy/py-faster-rcnn/tools/zf_faster_rcnn_iter_40000.caffemodel')} #修改1 #zf_fast_rcnn_iter_1000  ZF_faster_rcnn_finaldef vis_detections(im, class_name, dets, thresh=0.5):    """Draw detected bounding boxes."""    inds = np.where(dets[:, -1] >= thresh)[0]    if len(inds) == 0:        return    im = im[:, :, (2, 1, 0)]    fig, ax = plt.subplots(figsize=(12, 12))    ax.imshow(im, aspect='equal')    for i in inds:        bbox = dets[i, :4]        score = dets[i, -1]        ax.add_patch(            plt.Rectangle((bbox[0], bbox[1]),                          bbox[2] - bbox[0],                          bbox[3] - bbox[1], fill=False,                          edgecolor='red', linewidth=3.5)            )        ax.text(bbox[0], bbox[1] - 2,                '{:s} {:.3f}'.format(class_name, score),                bbox=dict(facecolor='blue', alpha=0.5),                fontsize=14, color='white')    ax.set_title(('{} detections with '                  'p({} | box) >= {:.1f}').format(class_name, class_name,                                                  thresh),                  fontsize=14)    plt.axis('off')    plt.tight_layout()    #plt.draw()def save_feature_picture(data, name, image_name=None, padsize = 1, padval = 1):    data = data[0]    #print "data.shape1: ", data.shape    n = int(np.ceil(np.sqrt(data.shape[0])))    padding = ((0, n ** 2 - data.shape[0]), (0, 0), (0, padsize)) + ((0, 0),) * (data.ndim - 3)    #print "padding: ", padding    data = np.pad(data, padding, mode='constant', constant_values=(padval, padval))    #print "data.shape2: ", data.shape        data = data.reshape((n, n) + data.shape[1:]).transpose((0, 2, 1, 3) + tuple(range(4, data.ndim + 1)))    #print "data.shape3: ", data.shape, n    data = data.reshape((n * data.shape[1], n * data.shape[3]) + data.shape[4:])    #print "data.shape4: ", data.shape    plt.figure()    plt.imshow(data,cmap='gray')    plt.axis('off')    #plt.show() #修改2    if image_name == None:        img_path = './data/feature_picture/'     else:        img_path = './data/feature_picture/' + image_name + "/"        check_file(img_path)    plt.savefig(img_path + name + ".jpg", dpi = 400, bbox_inches = "tight")def check_file(path):    if not os.path.exists(path):        os.mkdir(path)def demo(net, image_name):    """Detect object classes in an image using pre-computed object proposals."""    # Load the demo image    im_file = os.path.join(cfg.DATA_DIR, 'demo', image_name)    im = cv2.imread(im_file)    #im = cv2.imread('/home/snowflake/Downloads/Faster-Rcnn-Muzy/py-faster-rcnn/tools/8478075(11).jpg')    # Detect all object classes and regress object bounds    timer = Timer()    timer.tic()    scores, boxes = im_detect(net, im)    for k, v in net.blobs.items():        if k.find("conv")>-1 or k.find("pool")>-1 or k.find("rpn")>-1:            save_feature_picture(v.data, k.replace("/", ""), image_name)#net.blobs["conv1_1"].data, "conv1_1")     timer.toc()    print ('Detection took {:.3f}s for '           '{:d} object proposals').format(timer.total_time, boxes.shape[0])    # Visualize detections for each class    CONF_THRESH = 0.8    NMS_THRESH = 0.3    for cls_ind, cls in enumerate(CLASSES[1:]):        cls_ind += 1 # because we skipped background        cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]        cls_scores = scores[:, cls_ind]        dets = np.hstack((cls_boxes,                          cls_scores[:, np.newaxis])).astype(np.float32)        keep = nms(dets, NMS_THRESH)        dets = dets[keep, :]        vis_detections(im, cls, dets, thresh=CONF_THRESH)def parse_args():    """Parse input arguments."""    parser = argparse.ArgumentParser(description='Faster R-CNN demo')    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',                        default=0, type=int)    parser.add_argument('--cpu', dest='cpu_mode',                        help='Use CPU mode (overrides --gpu)',                        action='store_true')    parser.add_argument('--net', dest='demo_net', help='Network to use [zf]',                        choices=NETS.keys(), default='zf')    args = parser.parse_args()    return argsdef print_param(net):    for k, v in net.blobs.items():print (k, v.data.shape)    print ""    for k, v in net.params.items():print (k, v[0].data.shape)  if __name__ == '__main__':    cfg.TEST.HAS_RPN = True  # Use RPN for proposals    args = parse_args()#修改4    prototxt = os.path.join(cfg.MODELS_DIR, NETS[args.demo_net][0],                            'faster_rcnn_end2end', 'test.prototxt')    #print "prototxt: ", prototxt    caffemodel = os.path.join(cfg.DATA_DIR, 'faster_rcnn_models',                              NETS[args.demo_net][1])    if not os.path.isfile(caffemodel):        raise IOError(('{:s} not found.\nDid you run ./data/script/'                       'fetch_faster_rcnn_models.sh?').format(caffemodel))    if args.cpu_mode:        caffe.set_mode_cpu()    else:        caffe.set_mode_gpu()        caffe.set_device(args.gpu_id)        cfg.GPU_ID = args.gpu_id    net = caffe.Net(prototxt, caffemodel, caffe.TEST)        #print_param(net)    print '\n\nLoaded network {:s}'.format(caffemodel)    # Warmup on a dummy image    im = 128 * np.ones((300, 500, 3), dtype=np.uint8)    for i in xrange(2):        _, _= im_detect(net, im)#    im_names = ['8478075(11).jpg','8185056(15).jpg','8193000(14).jpg','8499359(12).jpg']    im_names = ['8478075(11).jpg']    # 修改 3    for im_name in im_names:        print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'        print 'Demo for data/demo/{}'.format(im_name)        demo(net, im_name)    #plt.show()