(六)深度学习实战 | MMDetection笔记(3)


1. 简介

配置文件是 M M D e t e c t i o n {\rm MMDetection} MMDetection中的核心要素,各模块的组建主要通过配置文件完成,而我们在添加或修改网络结构时也主要是针对配置文件的修改。本文将详细介绍 M M D e t e c t i o n {\rm MMDetection} MMDetection中配置文件的使用。


2. 配置文件

M M D e t e c t i o n {\rm MMDetection} MMDetection中为我们提供了查看配置文件的函数,位于tools/print_config.py中,例如执行以下命令:

python tools/print_config.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py

屏幕上会打印出该配置文件的详细信息。

2.1 配置文件结构

M M D e t e c t i o n {\rm MMDetection} MMDetection中存在四个基本的配置文件,它们是其他配置文件的。位于config/_base_,分别有数据集dataset、模型model、学习策略schedule和运行时runtime

此外,在config下位于同一文件夹内有且仅有一个基文件,其他配置文件都继承自该文件。如在configs/fcos文件夹内,fcos_r50_caffe_fpn_gn-head_4x4_1x_coco.py是其他所有同文件夹内的基文件。所以,如果要在 M M D e t e c t i o n {\rm MMDetection} MMDetection中集成新的算法,需要在configs文件夹下建立新的文件夹,然后按照格式新建配置文件。

2.2 配置文件命名方式

M M D e t e c t i o n {\rm MMDetection} MMDetection中,配置文件的命名方式遵循以下规则:

{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}
  • {model}:表示具体模型,如faster_rcnnmask_rcnn等;
  • [model setting]:某些模型的具体设置,如htcwithout_semanticreppointsmoment等;
  • {backbone}:骨干网络,如r50x101等;
  • {neck}:网络颈,如fpnpafpnnasfpnc4等;
  • [norm setting]:正则化设置,如bngnsyncbngn-head/gn-neckgn-all等;
  • [misc]:其他设置,如dconvgcbattentionalbumstrain等;
  • [gpu x batch_per_gou]:使用的总 G P U {\rm GPU} GPU数以及每块 G P U {\rm GPU} GPU的采样数;
  • {schedule}:设置学习策略,如1x2x20e等;
  • {dataset}:指示具体的数据集,如cococityscapesvoc_0712wider_face等。

2.3 例子:Mask R-CNN

为了更加直观地了解 M M D e t e c t i o n {\rm MMDetection} MMDetection中的配置文件的结构,本部分以 M a s k {\rm Mask} Mask- R C N N {\rm RCNN} RCNN为例来说明配置文件的内容,文件位于configs/mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py

_base_ = [
    '../_base_/models/mask_rcnn_r50_fpn.py',
    '../_base_/datasets/coco_instance.py',
    '../_base_/schedules/schedule_1x.py', 
    '../_base_/default_runtime.py'
]

可以看到,该配置文件继承自四个部分。第一部分是有关模型的配置:

# model settings
model = dict(	# 模型设置
    type='MaskRCNN',	# 当前检测器的名称
    pretrained='torchvision://resnet50',	# ImageNet数据集的预训练骨干网络
    backbone=dict(	# 骨干网络部分设置
        type='ResNet',	# 使用ResNet作为骨干网络
        depth=50,	# 使用ResNet50作为骨干网络
        num_stages=4,	# 骨干网络的阶段数
        out_indices=(0, 1, 2, 3),	# 每个阶段输出特征图的索引
        frozen_stages=1,	# 冻结网络第一阶段的权重
        norm_cfg=dict(type='BN', requires_grad=True),	# 归一化层的设置
        norm_eval=True,		# 是否冻结BN层
        style='pytorch'),	# 深度学习框架
    neck=dict(	# 网络颈部分设置
        type='FPN',	# 使用FPN
        in_channels=[256, 512, 1024, 2048],	# FPN各层的输入通道数,与骨干网络部分对应
        out_channels=256,	# FPN中的输出通道数
        num_outs=5),	# 输出的多尺度数
    rpn_head=dict(	#RPN网络头设置
        type='RPNHead',	# RPN头
        in_channels=256,	# RPN部分的输入通道数
        feat_channels=256,	# RPN部分的输出通道数
        anchor_generator=dict(	# Anchor产生器设置
            type='AnchorGenerator',	# Anchor产生器,主要是针对Anchor-Based方法
            scales=[8],	# Anchor的大小
            ratios=[0.5, 1.0, 2.0],	# Anchor的宽高比
            strides=[4, 8, 16, 32, 64]),	# 产生Anchor的特征图的下采样倍数
        bbox_coder=dict(	# 边界框的编解码设置
            type='DeltaXYWHBBoxCoder',	# 常用的编解码方法
            target_means=[.0, .0, .0, .0],	# 均值
            target_stds=[1.0, 1.0, 1.0, 1.0]),	# 标准差
        loss_cls=dict(	# 分类分支的损失函数设置
            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),	# 交叉熵损失
        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),	# 回归分支的损失函数设置
    roi_head=dict(	# 两阶段检测算法中的RoI处理部分
        type='StandardRoIHead',	# 标准RoI头
        bbox_roi_extractor=dict(	# RoI特征提取器
            type='SingleRoIExtractor',	# 指定RoI特征提取器
            roi_layer=dict(type='RoIAlign', out_size=7, sample_num=0),	# 使用RoIAlign
            out_channels=256,	# 得到的特征图的通道数
            featmap_strides=[4, 8, 16, 32]),	# 特征图的下采样倍数
        bbox_head=dict(	# 回归分支设置
            type='Shared2FCBBoxHead',	# 常用的回归分支类型
            in_channels=256,	# 输入通道数
            fc_out_channels=1024,	# 全连接层的输出通道数
            roi_feat_size=7,	# RoI特征图大小
            num_classes=80,	# 分类分支的类别数
            bbox_coder=dict(	# 边界框编码器
                type='DeltaXYWHBBoxCoder',	# 常用的编码方法
                target_means=[0., 0., 0., 0.],	# 均值
                target_stds=[0.1, 0.1, 0.2, 0.2]),	# 标准差
            reg_class_agnostic=False,	# 回归边界框时是否指定具体类别
            loss_cls=dict(	# 分类分支的损失函数设置
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),	# 回归分支的损失函数设置
        mask_roi_extractor=dict(	# 同上
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', out_size=14, sample_num=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        mask_head=dict(	# mask分支的设置
            type='FCNMaskHead',	# Mask分支的检测头
            num_convs=4,	# 连续使用四次卷积
            in_channels=256,	# 输入通道数
            conv_out_channels=256,	# 输出通道数
            num_classes=80,	# 类别数
            loss_mask=dict(	# mask分支的损失函数设置
                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))
# model training and testing settings
train_cfg = dict(	# 训练设置
    rpn=dict(	# RPN部分
        assigner=dict(	# 候选框采样设置
            type='MaxIoUAssigner',	# 基于IoU对候选框采样
            pos_iou_thr=0.7,	# 正样本阈值
            neg_iou_thr=0.3,	# 负样本阈值
            min_pos_iou=0.3,	# 最小正样本阈值,防止正样本过少造成样本数的不平衡
            match_low_quality=True,	# 是否使用低质量的候选框
            ignore_iof_thr=-1),	# 忽略样本阈值
        sampler=dict(	# 候选框采样设置
            type='RandomSampler',	# 常用的随机采样方式
            num=256,	# 总采样样本数
            pos_fraction=0.5,	# 正负样本比例
            neg_pos_ub=-1,	# 最大负样本数
            add_gt_as_proposals=False),	# 采样后是否添加真实框作为候选框
        allowed_border=-1,	# 有效候选框的边界填充
        pos_weight=-1,	# 训练时正样本的权重
        debug=False),	# 是否设置debug模式
    rpn_proposal=dict(	# RPN设置
        nms_across_levels=False,	# 是否跨层使用NMS
        nms_pre=2000,	# NMS前的候选框数
        nms_post=1000,	# NMS后的候选框数
        max_num=1000,	# NMS后的候选框数
        nms_thr=0.7,	# NMS使用的阈值
        min_bbox_size=0),	# 允许的最小候选框尺寸
    rcnn=dict(	# RoI检测头部分设置
        assigner=dict(	# 同上
            type='MaxIoUAssigner',
            pos_iou_thr=0.5,
            neg_iou_thr=0.5,
            min_pos_iou=0.5,
            match_low_quality=True,
            ignore_iof_thr=-1),
        sampler=dict(	# 同上
            type='RandomSampler',
            num=512,
            pos_fraction=0.25,
            neg_pos_ub=-1,
            add_gt_as_proposals=True),
        mask_size=28,	# mask大小
        pos_weight=-1,	# 训练时正样本的权重
        debug=False))	# 是否设置debug模式
test_cfg = dict(	# 测试设置
    rpn=dict(	# RPN部分设置,同上
        nms_across_levels=False,
        nms_pre=1000,
        nms_post=1000,
        max_num=1000,
        nms_thr=0.7,
        min_bbox_size=0),
    rcnn=dict(	# RoI检测头部分设置,同上
        score_thr=0.05,
        nms=dict(type='nms', iou_thr=0.5),
        max_per_img=100,
        mask_thr_binary=0.5))

第二部分是有关数据集的配置:

_base_ = 'coco_detection.py'	# 继承自coco_detection.py
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)	# 均值和标准差
train_pipeline = [	# 上文提到的训练管道设置
    dict(type='LoadImageFromFile'),	# 加载图像
    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),	# 加载标准
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),	# 固定尺寸
    dict(type='RandomFlip', flip_ratio=0.5),	# 水平翻转
    dict(type='Normalize', **img_norm_cfg),		# 归一化
    dict(type='Pad', size_divisor=32),	# 填充
    dict(type='DefaultFormatBundle'),	# 数据格式
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),	# 整合输出
]
data = dict(train=dict(pipeline=train_pipeline))	# 字典
evaluation = dict(metric=['bbox', 'segm'])	# 评价指标

其中,基文件coco_detection.py如下:

dataset_type = 'CocoDataset'	# 数据集
data_root = 'data/coco/'	# 数据集根目录
img_norm_cfg = dict(	
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)	# 均值和标准差
train_pipeline = [	# 同上
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [	# 同上
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img']),
        ])
]
data = dict(
    samples_per_gpu=2,	# 每块GPU采样数
    workers_per_gpu=2,	# 每块GPU线程数
    train=dict(	# 训练数据集信息
        type=dataset_type,
        ann_file=data_root + 'annotations/instances_train2017.json',
        img_prefix=data_root + 'train2017/',
        pipeline=train_pipeline),
    val=dict(	# 验证数据集信息
        type=dataset_type,
        ann_file=data_root + 'annotations/instances_val2017.json',
        img_prefix=data_root + 'val2017/',
        pipeline=test_pipeline),
    test=dict(	# 测试数据集信息
        type=dataset_type,
        ann_file=data_root + 'annotations/instances_val2017.json',
        img_prefix=data_root + 'val2017/',
        pipeline=test_pipeline))
evaluation = dict(interval=1, metric='bbox')	# 评价指标

第三部分是有关训练策略的配置:

# 优化器
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# 学习率
lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[8, 11])
total_epochs = 12

第四部分是有关运行时的配置:

checkpoint_config = dict(interval=1)
# yapf:disable
log_config = dict(
    interval=50,
    hooks=[
        dict(type='TextLoggerHook'),
        # dict(type='TensorboardLoggerHook')
    ])
# yapf:enable
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]

3. Q&A

3.1 忽略父配置文件中的某个部分

有时候,我们想修改父配置文件中的某一部分,比如修改继承自mask_rcnn_r50_fpn_1x_coco.py的骨干网络部分。除了自定义父配置文件外,我们也可以在此配置文件上设置关键字_delete=True,然后根据实际需要添加自己的模块。如:

model = dict(
    type='MaskRCNN',
    pretrained='torchvision://resnet50',
    backbone=dict(
        type='ResNet',
        depth=50,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        norm_eval=True,
        style='pytorch'),
    neck=dict(...),
    rpn_head=dict(...),
    roi_head=dict(...))

这里,我们将ResNet替换成HRNet

_base_ = '../mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py'
model = dict(
    pretrained='open-mmlab://msra/hrnetv2_w32',
    backbone=dict(
        _delete_=True,
        type='HRNet',
        extra=dict(
            stage1=dict(
                num_modules=1,
                num_branches=1,
                block='BOTTLENECK',
                num_blocks=(4, ),
                num_channels=(64, )),
            stage2=dict(
                num_modules=1,
                num_branches=2,
                block='BASIC',
                num_blocks=(4, 4),
                num_channels=(32, 64)),
            stage3=dict(
                num_modules=4,
                num_branches=3,
                block='BASIC',
                num_blocks=(4, 4, 4),
                num_channels=(32, 64, 128)),
            stage4=dict(
                num_modules=3,
                num_branches=4,
                block='BASIC',
                num_blocks=(4, 4, 4, 4),
                num_channels=(32, 64, 128, 256)))),
    neck=dict(...))

该配置文件虽然继承自mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py,但是通过在backbone中设置关键字_delete=True可以使定义的骨干网络部分替换父配置文件中相应的部分,其他模块的替换方式一样。

3.2 在配置文件中使用中间变量

当我们需要修改子配置文件中的相关变量时,必须将修改的变量再次定义。如我们需要在 M a s k {\rm Mask} Mask- R C N N {\rm RCNN} RCNN中加入多尺度策略,我们需要对train_pipeline/test_pipeline做如下修改:

_base_ = './mask_rcnn_r50_fpn_1x_coco.py'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
    dict(
        type='Resize',
        img_scale=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),
                   (1333, 768), (1333, 800)],
        multiscale_mode="value",
        keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img']),
        ])
]
data = dict(
    train=dict(pipeline=train_pipeline),
    val=dict(pipeline=test_pipeline),
    test=dict(pipeline=test_pipeline))

4. 总结

本文介绍了 M M D e t e c t i o n {\rm MMDetection} MMDetection中的配置文件系统,在 M M D e t e c t i o n {\rm MMDetection} MMDetection中配置文件是构建整个模型的关键。结合上文介绍的如何在 M M D e t e c t i o n {\rm MMDetection} MMDetection中添加自定义部分,下文将介绍 M M D e t e c t i o n {\rm MMDetection} MMDetection配套的 m m c v {\rm mmcv} mmcv库。


参考

  1. https://github.com/open-mmlab/mmdetection.


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值