• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

MMDetection报错解决:(1)在验证和测试时CUDA out of memory;(2)验证和测试时mAP全为0

武飞扬头像
咿喃小记
帮助1

目录

一.环境

二.验证和测试时OOM(CUDA out of memory)

2.1问题描述

2.2初步分析

2.3初步解决

2.3.1gpu->cpu(OK但巨慢)

2.3.2no-validate(不起作用,离线测试时依旧OOM)

2.3.3rescale(OK但mAP=0)

三.验证和测试时mAP全为0

3.1原因

3.2RLE编码

3.3实现效果

3.4存在问题

3.5修改细节


一.环境

  • OS:Ubuntu18.04
  • CUDA:11.0
  • mmcv-full:1.7.0
  • mmdet:2.25.1
  • GPU:1080Ti * 4

二.验证和测试时OOM(CUDA out of memory)

2.1问题描述

        在使用MMDetection训练MaskRCNN实例分割模型时,训练一切正常,但是在对第一张验证集图片进行验证时,就出现了OOM爆显存的问题。

2.2初步分析

        通常遇到OOM问题时的第一反应是将batch_size调小,或者是将模型的输入尺寸设置的小一点,进而减小显存的占用。但是,有时batch_size设置的不合适/过小,会导致无法复现出原本的精度,在不减小batch_size的情况下解决OOM:RuntimeError: CUDA out of memory在不减小batch_size的前提下的解决方案

        不过,在验证或测试时,batch_size是为1的,不存在batch_size过大的问题,但是减小网络的输入尺寸或许可以解决这个问题,但是由于数据集中图像原本的尺寸比较大(4000*3000),如果缩小到一个很小的尺寸,可能会造成很大的精度损失,所以保持默认的(1333,800)不动去寻找解决方案。

2.3初步解决

2.3.1gpu->cpu(OK但巨慢)

        参考下面这篇的做法:【pytorch】mmdetection 做eval / test时弹出OOM(Out of Memory / CUDA out of memory)的解决过程记录

(1)清显存(不起作用)

        伪代码如下:

  1.  
    def test():
  2.  
    torch.cuda.empty_cache()
  3.  
    with torch.no_grad():
  4.  
    model.eval()
  5.  
    xxxxxx

(2)改网络超参数(不起作用)

        “max_per_img的数量从1000调低到500”:

  1.  
    test_cfg=dict(
  2.  
    rpn=dict(
  3.  
    nms_pre=1000,
  4.  
    max_per_img=500,
  5.  
    nms=dict(type='nms', iou_threshold=0.7),
  6.  
    min_bbox_size=0),
  7.  
    rcnn=dict(
  8.  
    score_thr=0.05,
  9.  
    nms=dict(type='nms', iou_threshold=0.5),
  10.  
    max_per_img=100,
  11.  
    mask_thr_binary=0.5))

(3)更换为cpu计算(OK但巨慢)

        修改mmdet\models\roi_heads\mask_heads\fcn_mask_head.py中_do_paste_mask函数:

  1.  
    #gx = img_x[:, None, :].expand(N, img_y.size(1), img_x.size(1))
  2.  
    #gy = img_y[:, :, None].expand(N, img_y.size(1), img_x.size(1))
  3.  
    gx = img_x[:, None, :].expand(N, img_y.size(1), img_x.size(1)).cpu()
  4.  
    gy = img_y[:, :, None].expand(N, img_y.size(1), img_x.size(1)).cpu()
  5.  
     
  6.  
    grid = torch.stack([gx, gy], dim=3)
  7.  
     
  8.  
    #img_masks = F.grid_sample(
  9.  
    # masks.to(dtype=torch.float32), grid, align_corners=False)
  10.  
    img_masks = F.grid_sample(
  11.  
    masks.cpu().to(dtype=torch.float32), grid, align_corners=False)
  12.  
     
  13.  
    #if skip_empty:
  14.  
    # return img_masks[:, 0], (slice(y0_int, y1_int), slice(x0_int, x1_int))
  15.  
    #else:
  16.  
    # return img_masks[:, 0], ()
  17.  
    if skip_empty:
  18.  
    return img_masks[:, 0].cuda(), (slice(y0_int, y1_int), slice(x0_int, x1_int))
  19.  
    else:
  20.  
    return img_masks[:, 0].cuda(), ()
学新通

2.3.2no-validate(不起作用,离线测试时依旧OOM)

        想法是,离线测试或许会因为没有训练的数据占用显存而OK。在运行train.py时,指定--no-validate

  1.  
    parser.add_argument(
  2.  
    '--no-validate',
  3.  
    action='store_true',
  4.  
    help='whether not to evaluate the checkpoint during training')

2.3.3rescale(OK但mAP=0)

        修改mmdet/apis/test.py中的single_gpu_test和multi_gpu_test两个函数中的rescale=False

  1.  
    with torch.no_grad():
  2.  
    result = model(return_loss=False, rescale=False, **data)

三.验证和测试时mAP全为0

3.1原因

        由于2.3.3中设置了rescale=False,导致模型输出的bbox和segm结果都是相对于网络输入尺寸(1333,800)的,但是测试集的gt都是在原尺寸下的。此外还需要注意RLE编码格式,下面会详细说明。

3.2RLE编码

        根据3.1的描述,将det结果和gt标签做一次尺度对齐似乎就可以了,但是事情并没有这么简单。由于在计算mask的iou时是先将Polygon格式的mask标签转化为RLE格式,而这个转化的操作涉及到图像的尺寸,所以需要修改coco_gt中img的尺寸。在pycocotools/cocoeval.py中COCOeval类内的_prepare函数中的_toMask函数说明了这一点:

  1.  
    def _toMask(anns, coco):
  2.  
    # modify ann['segmentation'] by reference
  3.  
    for ann in anns:
  4.  
    rle = coco.annToRLE(ann)
  5.  
    ann['segmentation'] = rle

        RLE编码。参考Segmentaion标签的三种表示:poly、mask、rle

        使用mask图像的编码方式会有很多的信息冗余,大部分像素值都是0,少部分为1。RLE编码是一种压缩存储的思想,例如将mask图像平铺之后的向量[0,1,1,1,0,1,1,0,1,0] 编码之后为1,3,5,2,8,1。其中的奇数位表示像素值1出现的对应的向量中的index,而偶数位表示在向量中从该index开始像素值1重复的个数。

3.3实现效果

        对MMDetection框架进行简单修改,使得在设置rescale=False的情况下也可以计算出bbox和segm的mAP.

(1)验证时默认rescale为False,需要设置为True时则修改mmdet/apis/test.py中的single_gpu_test和multi_gpu_test的缺省形参rescale为True(取决于单卡或多卡训练)

(2)离线测试时,设置tools/test.py的rescale的值(True/False),设置--show即可绘制可视化结果

3.4存在问题

  • 数据集中各个图片原始尺寸不一致时会有问题
  • 可视化结果和网络输出都是依照网络输入尺寸的,并不是原图尺寸
  • 验证时应该是需要手动指定mmdet/datasets/coco.py中evaluate函数的scale_factor参数值(待测试

3.5修改细节

(1)在tools/test.py的parse_args函数中新建一个参数rescale

parser.add_argument('--rescale',default=False,choices=[True,False],help='whether rescale when evaluating')

(2)在tools/test.py的main函数中,新加三处代码

  1.  
    if not distributed:
  2.  
    model = build_dp(model, cfg.device, device_ids=cfg.gpu_ids)
  3.  
    outputs = single_gpu_test(model, data_loader, args.show, args.show_dir,
  4.  
    args.show_score_thr,args.rescale)#1.新加args.rescale
  5.  
    else:
  6.  
    model = build_ddp(
  7.  
    model,
  8.  
    cfg.device,
  9.  
    device_ids=[int(os.environ['LOCAL_RANK'])],
  10.  
    broadcast_buffers=False)
  11.  
    outputs = multi_gpu_test(
  12.  
    model, data_loader, args.tmpdir, args.gpu_collect
  13.  
    or cfg.evaluation.get('gpu_collect', False),args.rescale)#2.新加args.rescale
  14.  
     
  15.  
    xxx
  16.  
    xxx
  17.  
    xxx
  18.  
     
  19.  
    if args.eval:
  20.  
    eval_kwargs = cfg.get('evaluation', {}).copy()
  21.  
    # hard-code way to remove EvalHook args
  22.  
    for key in [
  23.  
    'interval', 'tmpdir', 'start', 'gpu_collect', 'save_best',
  24.  
    'rule', 'dynamic_intervals'
  25.  
    ]:
  26.  
    eval_kwargs.pop(key, None)
  27.  
    eval_kwargs.update(dict(metric=args.eval, **kwargs))
  28.  
     
  29.  
     
  30.  
    #3.新加
  31.  
    eval_kwargs['rescale'] = args.rescale
  32.  
    for i, data in enumerate(data_loader):
  33.  
    eval_kwargs['scale_factor'] = data['img_metas'][0]._data[0][0]['scale_factor']
  34.  
    break
学新通

(3)修改mmdet/apis/test.py中的single_gpu_test和multi_gpu_test两个函数共五处,第三处是为了保证绘制结果时的尺寸对齐

学新通

 学新通

(4) 修改mmdet/datasets/coco.py中evaluate函数两处

学新通

(5)修改mmdet/datasets/coco.py中evaluate_det_segm函数形参(添加rescale和scale_factor),以及在cocoEval = COCOeval(coco_gt1, coco_det, iou_type)一行上面添加缩放代码

  1.  
    def evaluate_det_segm(self,
  2.  
    results,
  3.  
    result_files,
  4.  
    coco_gt,
  5.  
    metrics,
  6.  
    logger=None,
  7.  
    classwise=False,
  8.  
    proposal_nums=(100, 300, 1000),
  9.  
    iou_thrs=None,
  10.  
    metric_items=None,
  11.  
    rescale=False,
  12.  
    scale_factor=None):
  1.  
    # 以下全为新加
  2.  
    coco_gt1 = COCO(self.ann_file)
  3.  
    if not rescale and scale_factor is not None:
  4.  
    for index in coco_gt1.anns.keys():
  5.  
    now_bbox = coco_gt1.anns[index]['bbox'].copy()
  6.  
    now_segm = coco_gt1.anns[index]['segmentation'][0].copy()
  7.  
    coco_gt1.anns[index]['bbox'] = [int(float(value)*scale_factor[0]) if index%2==0 else int(float(value)*scale_factor[1]) for index,value in enumerate(now_bbox)]
  8.  
    coco_gt1.anns[index]['segmentation'][0] = [int(float(value)*scale_factor[0]) if index%2==0 else int(float(value)*scale_factor[1]) for index,value in enumerate(now_segm)]
  9.  
     
  10.  
    tmp_w = 0
  11.  
    tmp_h = 0
  12.  
    for index in coco_gt1.imgs.keys():
  13.  
    coco_gt1.imgs[index]['width'] = np.round(coco_gt1.imgs[index]['width'] * scale_factor[0].item()).astype(np.int32)
  14.  
    coco_gt1.imgs[index]['height'] = np.round(coco_gt1.imgs[index]['height'] * scale_factor[1].item()).astype(np.int32)
  15.  
    tmp_w = coco_gt1.imgs[index]['width']
  16.  
    tmp_h = coco_gt1.imgs[index]['height']
  17.  
     
  18.  
    for index in coco_det.imgs.keys():
  19.  
    coco_det.imgs[index]['width'] = tmp_w
  20.  
    coco_det.imgs[index]['height'] = tmp_h
  21.  
     
  22.  
    #注意这里换为coco_gt1
  23.  
    cocoEval = COCOeval(coco_gt1, coco_det, iou_type)
  24.  
     
  25.  
    # 原有
  26.  
    #cocoEval = COCOeval(coco_gt, coco_det, iou_type)
  27.  
    cocoEval.params.catIds = self.cat_ids
  28.  
    cocoEval.params.imgIds = self.img_ids
  29.  
    cocoEval.params.maxDets = list(proposal_nums)
  30.  
    cocoEval.params.iouThrs = iou_thrs
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfijcbc
系列文章
更多 icon
同类精品
更多 icon
继续加载