华为ModelArts-Lab拓展试验记录(二)

cnlile 发表于 2020/07/28 00:48:33 2020/07/28
【摘要】 华为ModelArts-Lab拓展试验记录(二) 在2019年7月至2019年12月期间,参加了华为云ModelArt训练营活动,在ModelArt平台上,做了一些AI实验,现在整理一下资料,把我主要做的几个拓展实验内容做了记录,方便以后查阅,且分享给那些对使用华为云ModelArt开发有兴趣的朋友。第二个拓展实验就是训练营第五期内容的拓展,关于物体检测的Faster R-CNN算法的...

华为ModelArts-Lab拓展试验记录(二)

    在2019年7月至2019年12月期间,参加了华为云ModelArt训练营活动,在ModelArt平台上,做了一些AI实验,现在整理一下资料,把我主要做的几个拓展实验内容做了记录,方便以后查阅,且分享给那些对使用华为云ModelArt开发有兴趣的朋友。第二个拓展实验就是训练营第五期内容的拓展,关于物体检测的Faster R-CNN算法的使用mAP评价指标。


1、概述

    本次拓展实验内容为对Faster R-CNN算法模型使用mAP评价指标进行衡量,在第五期的notebook案例中,并没有对物体检测模型的精度进行评估,物体检测模型的精度使用mAP评价指标进行衡量。要求在notebook案例中,开发代码,计算出模型的mAP。

    和以前一样,从最基本概念入手,由浅入深,介绍本次实验的过程及其内容,并且对实验代码做解析。

1.1、Faster RCNN简介

    机器视觉领域的核心问题之一就是目标检测(Object Detection),它的任务是找出图像当中所有感兴趣的目标(物体),确定其位置和大小。Faster RCNN是由Ross Girshick由何凯明等人在2016年将其用于目标检测任务中,能够完成高效的与传统的RCNN相比,利用RPN(Region Proposal Networks)完成候选框的选择。其发展历程是:R-CNN——Fast R-CNN——Faster  R-CNN——Mask R-CNN,都是将神经网络应用于目标检测的典型代表,Faster  R-CNN具有性能较好,速度较快的优点。

    Faster  R-CNN是在R-CNN,Fast R-CNN基础之上发展而来,原理图如下:

   

    

    算法步骤

    1.Conv layers:首先使用一组基础conv+relu+pooling层提取image的feture map。接层。

    2.Region Proposal Networks:RPN网络用于生成region proposcals.该层通过softmax判断anchors属于foreground或者background,再利用box regression修正anchors获得精确的propocals

    3.Roi Pooling:该层收集输入的feature map 和 proposcal,综合这些信息提取proposal feature map,送入后续的全连接层判定目标类别。

    4.Classification。利用proposal feature map计算proposcal类别,同时再次bounding box regression获得检验框的最终精确地位置。

     具体细节这里就不阐述了,以上几个步骤,在这次实验中都有涉及。


1.2、mAP评价指标

     Mean Average Precision(MAP):平均精度均值。P(Precision)精度,正确率。定义为: precision=返回结果中相关文档的数目/返回结果的数目。 Rec(Recall)召回率:定义为:Recall=返回结果中相关文档的数目/所有相关文档的数目。 数学公式理解: 

    1)True Positive(真正,TP):将正类预测为正类数;2)True Negative(真负,TN):将负类预测为负类数;3)False Positive(假正,FP):将负类预测为正类数误报 (Type I error);4)False Negative(假负,FN):将正类预测为负类数→漏报 (Type II error)。

    精确率(precision):P=TP/(TP+FP)(分类后的结果中正类的占比),召回率(recall):recall=TP/(TP+FN)(所有正例被分对的比例。

    在图像中,主要计算两个指标:precision和recall。precision,recall都是选多少个样本k的函数,如果我总共有1000个样本,那么我就可以像这样计算1000对P-R,并且把他们画出来,这就是PR曲线:这里有一个趋势,recall越高,precision越低。 平均精度AP(average precision):就是PR曲线下的面积,这里average,等于是对recall取平均。而mean average precision的mean,是对所有类别取平均(每一个类当做一次二分类任务)。现在的图像分类论文基本都是用mAP作为标准。AP是把准确率在recall值为Recall = {0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1}时(总共11个rank水平上),求平均值。mAP是均精度均值:只是把每个类别的AP都算了一遍,再取平均值。

    AP是针对单个类别的,mAP是针对所有类别的。

2、mAP检测实验

    根据本期实验内容,结合mAP方式,在modelArt平台上,先制定检测流程,在实验代码中添加mAP检测代码,然后对实验数据进行mAP检测。


2.1、定义检测数据集

    首先,需要获取测试数据,依然从VOC2007数据里面获取,定义一个imdb(检测数据),作为检测mAP用。也是利用原来的实验部分代码的写法构成。定义了加载函数 def combined_roidb(imdb_names),里面处理比较常规,选用数据集,对图像进行了反转处理。最后返回两个数据:imdb 和 roidb。

    这里要用到一个python类:pascal_voc,它实际上是集成了imdb类的一个类,实现了各种功能,有兴趣的话,可以看它的源代码。首先要导入一个它的功能函数:

 from datasets.factory import get_imdb

      为了计算mAP,需要修改数据集参数:

 imdb_name = "voc_2007_test"

      在训练之前,我们先要定义好imdb数据,代码如下:

#定义加载数据集函数
def combined_roidb(imdb_names):
    
    def get_roidb(imdb_name):
        # 加载数据集
        imdb = get_imdb(imdb_name) #记录了数据集的路径之类的信息,函数通过给数据集的名字返回了该数据集对应的类的对象。
        print('Loaded dataset `{:s}` for training'.format(imdb.name))
        # 使用ground truth作为数据集策略
        #对每张影像名调用_load_pascal_annotation,即可以知道该函数读取的是影像对应存储目标的xml文件。最后返回了“boxes”,“gt_classes”等构成的字典。
        imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
        print('Set proposal method: {:s}'.format(cfg.TRAIN.PROPOSAL_METHOD))
        #增加翻转,对每张影像在上面进行了求最大处理,并记录影像路径还有影像高宽等信息在roidb中并返回,返回的roidb是一个长度与影像数一致的列表
        roidb = get_training_roidb(imdb) 
        return roidb
    
    #对于每个数据集返回了带有该数据集信息的imdb和包含每张影像感兴趣区域信息的roidb(包括每张影像点目标和影像宽高等信息)
    roidbs = [get_roidb(s) for s in imdb_names.split('+')]
    roidb = roidbs[0] #roidb包括数据集所有影像的影像路径,所有影像对应的目标,以及宽高等信息。
    if len(roidbs) > 1:
        for r in roidbs[1:]:
            roidb.extend(r)
        tmp = get_imdb(imdb_names.split('+')[1])
        imdb = datasets.imdb.imdb(imdb_names, tmp.classes)
    else:
        imdb = get_imdb(imdb_names)
    return imdb, roidb

     在这里,这个部分主要还是提取VOC2007 的测试数据,作为检测mAP用。也是利用原来的实验部分代码的写法构成。

    定义了加载函数 def combined_roidb(imdb_names),里面处理比较常规,选用数据集,对图像进行了反转处理。最后返回两个数据:imdb 和 roidb。

    imdb的定义方式:imdb = get_imdb(imdb_name),它是一个基于pascal_voc.py 的类的实例对象,imdb这里的数据集获取的同时,还做了数据的初始化,get_imdb(imdb_name)将会返回的就是pascal_voc(split, year)这样一个对象。它们关系:get_imdb->factory->pascal_voc->(继承)imdb。

     最后,不要忘记imdb, roidb = combined_roidb(imdb_name),这行代码是加载测试集数据。

2.2、定义神经网络

    在测试计算时,还是需要建立神经网络,用来检测数据。这里还是根据案例情况,这里还是选择Vgg16网络,并且加载我们生成的训练模型,并且配置正确的参数。

  from nets.vgg16 import vgg16
  net = vgg16()

     这里需要定义一下anchor的参数,主要是定义了anchor的尺寸,这里设置anchor_scales = [8, 16, 32]。对base anchor的宽高分别乘以尺寸,从而得到各个不同尺寸的anchor。

 'ANCHOR_SCALES': [8, 16, 32],

    

    接着做以下的工作:

    1)net.create_architecture,构建faster rcnn进行计算图的操作。这里要注意,modelart环境中的参数和其它场景略有差异,是不需要传sess这个参数的(这个坑了我好久)。这里的参数中,参数ANCHOR_SCALES必须,参数ANCHOR_RATIOS可以省略,其它都按常规传。                               

    net.create_architecture(imdb.num_classes,tag='default',anchor_scales=cfg.ANCHOR_SCALES)

    2)加载权重文件,net.load_state_dict方法,主要是加载的文件位于"./models/vgg16-voc0712/vgg16_faster_rcnn_iter_110000.pth",这个就是训练模型文件。

    net.load_state_dict(torch.load(saved_model, map_location=lambda storage, loc: storage))    
    net.eval()

    3)选择计算设施:net.to(net._device)。

        

2.3、图像检测和提取

    这个是mAP计算的核心功能,主要是形成可以被计算mAP的图像文件。以下分三个步骤:

    步骤一、定义目标图像框容器

    根据每一类文件,每一张图片,都定义个容器,就是定义一个数据集存储单元。代码为:

 all_boxes = [[[] for _ in range(num_images)]
 for _ in range(imdb.num_classes)]

    这里的只有存储单元,里面都空值。

    步骤二、分类图像数据组织和填充,这里首先根据图像分类,按照类别对检测数据进行处理。前期处理:

  from model.test import im_detect
  im = cv2.imread(imdb.image_path_at(i))
  scores, boxes = im_detect( net, im)

    作用是返回这张图片中的多个目标和分数,存放在boxes和scores中。

    主要处理:先获取上述过程中的为每一个图像分配的存储单元的尺寸数据,和图像数据集的数据,检测后,将数据进行填充。这里代码主要是:

 inds = np.where(scores[:, j] > thresh)[0]
 cls_scores = scores[inds, j]
 cls_boxes = boxes[inds, j*4:(j+1)*4]
 cls_dets = np.hstack((cls_boxes, cls_scores[:, np.newaxis])) \
     .astype(np.float32, copy=False)
 keep = nms(torch.from_numpy(cls_boxes), torch.from_numpy(cls_scores), NMS_THRESH)
 cls_dets = cls_dets[keep, :]
 all_boxes [j][i] = cls_dets

    以上步骤完成后,all_boxes 里面已经有了数据。需要注意,这里的计算时间略长,大概要几分钟时间,需要等待。

    步骤三、我们要把all_boxes的数据写入文件,这里调用imdb的函数:

    imdb._write_voc_results_file(all_boxes)

    write_voc_results_file 函数可以在pascal_voc.py的源代码中找到,完成之后,我们打开目录,/data/VOCdevkit2007/result/VOC2007/Main/下面目录,可以看到,每一类文件都做了单独存储,这里要注意,因为是modelart环境,每次产生文件名,里面有个计算机ID代码,每次启动,不一定是一样的。

2.4、计算AP和mAP

    这里有两种方式,便捷的方式就是直接调用imdb.evaluate_detections(all_boxes, output_dir),可以忽略一些细节,使用默认设置,直接给出mAP。

    第二种方式,我这里的做法是自己写了个文件voc_eval.py,提供voc_eval的函数供调用,这样更加灵活,也能更加符合我进行实验的目的,可以获得更加丰富的数据。voc_eval函数:

  def voc_eval(detpath,
        annopath,
        imagesetfile,
        classname,
        cachedir,
        ovthresh=0.5,
        use_07_metric=False,
        use_diff=False):

     关键理如下:        

    1)读取文件数据,存入数据字典,这里的文件就是上述保存的文件位置,代码如下:

  recs = {}#生成一个字典
  for i, imagename in enumerate(imagenames): #对于每一张图像进行循环
    recs[imagename] = parse_rec(annopath.format(imagename))#在字典里面放入每个图像缓存的标签路径
    if i % 100 == 0:#在这里输出标签读取进度。
        print('Reading annotation for {:d}/{:d}'.format(i + 1, len(imagenames)))#从这里可以看出来imagenames是什么,是一个测试集合的名字列表,这个Print输出进度。# save
  print('Saving cached annotations to {:s}'.format(cachefile))#读取的标签保存到一个文件里面
  with open(cachefile, 'wb+') as f:#打开缓存文件
  pickle.dump(recs, f)#dump是序列化保存,load是反序列化解析

    2)从以下代码开始就是具体计算了:

  nd = len(image_ids) #统计检测出来的目标数量 
  tp = np.zeros(nd)#tp = true positive 就是检测结果中检测对的-检测是A类,结果是A类  
  fp = np.zeros(nd)#fp = false positive 检测结果中检测错的-检测是A类,结果gt是B类。
  if BB.shape[0] > 0:#。shape是numpy里面的函数,用于计算矩阵的行数shape[0]和列数shape[1]

      最后,每一类数据都会返回三个值:

      rec: 召回率

      prec:精确度

      ap:平均精度

      最后得出mAP:0.7020。

    

  



    



     


    

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请发送邮件至:cloudbbs@huaweicloud.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。