目录
1. config配置文件命名规则
2. config 文件概述
3.config 类
3.1 读取配置文件
3.2 修改配置参数
3.3使用中间变量
3.4 打印配置文件
1. config配置文件命名规则
./configs
文件夹下,配置文件都按照统一的规则命名,具体段的含义可以去官方文档自行查阅。
#命名规则
{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}#其中各个字段的含义
{model}
: 模型种类,例如faster_rcnn
,mask_rcnn
等。- [model setting]: 特定的模型,例如 htc 中的without_semantic, reppoints 中的 moment 等。
- {backbone}: 主干网络种类例如 r50 (ResNet-50), x101 (ResNeXt-101) 等。
- {neck}: Neck 模型的种类包括 fpn, pafpn, nasfpn, c4 等。
- [norm_setting]: 默认使用 bn (Batch Normalization),其他指定可以有 gn (Group Normalization), syncbn (Synchronized Batch Normalization) 等。 gn-head/gn-neck 表示 GN 仅应用于网络的 Head 或 Neck, gn-all 表示 GN 用于整个模型, 例如主干网络、Neck 和 Head。
- [misc]: 模型中各式各样的设置/插件,例如 dconv、 gcb、 attention、albu、 mstrain 等。
- [gpu x batch_per_gpu]:GPU 数量和每个 GPU 的样本数,默认使用 8x2。
- {schedule}: 训练方案,选项是 1x、 2x、 20e 等。1x 和 2x 分别代表 12 epoch 和 24 epoch,20e 在级联模型中使用,表示 20 epoch。对于 1x/2x,初始学习率在第 8/16 和第 11/22 epoch 衰减 10 倍;对于 20e ,初始学习率在第 16 和第 19 epoch 衰减 10 倍。
- {dataset}:数据集,例如 coco、 cityscapes、 voc_0712、 wider_face 等。
其中大括号表示必选,中括号表示可选。
./configs/_base_文件夹下,有4个基本组件类型,分别是数据集(datasets)、模型(models)、训练策略(schedules)和运行时的默认配置(default_runtime)。
一般来说,当我们新建一个配置文件时,需要从上述四个组件中分别继承一个原始配置文件,然后根据自己需要进行调整。当然,如果你是在构建一个与任何现有方法都不共享结构的全新方法,也可以直接新建,不继承任何原始配置文件。
2. config 文件概述
config是最重要的,基本上包含了所有的配置文件。
当我们打开mask_rcnn_r101_fpn_2x_coco.py时,你会发现它索引了其他的字段,config中包含了很多的互相引用。其实MMDetection中配置文件是通过继承 + 修改的方式完成用户自定义配置文件的。配置文件开头的_base_ = list()表示你需要继承的配置文件,然后通过重写的方式完成对应属性的修改。
字典dict和变量的定义组成,经由Config.fromfile(filepath)函数加载后会返回一个Config类型的变量(MMCV的一个数据结构),然后MMDetection框架就能根据这个Config调用相关的build_detector()方法构建对应的模块。
具体地,build_detector()方法首先会根据字典中的type找到对应的类(Class),这个类的类名就是type字符串的值,且这个类一定是事先注册(Registry) 好的,MMDetection能够根据type值查询到具体的类,否则就会报错。比如在下面配置文件中,model的type值为mask_rcnn,我们可以在./mmdet/models/detectors/mask_rcnn.py中找到定义。
build_detector()
函数的作用就是根据dict中的type
找到对应的类,然后使用dict中传入的参数来对类进行初始化操作,并返回这个类的句柄。
# 下面两行调用是等价的
model = build_detector(Config{type='mask_rcnn', backbone=xxx, neck=xxx, bbox_head=xxx})
model = mask_rcnn(backbone=xxx, neck=xxx, bbox_head=xxx)
type='ResNet'
一个字典,同理我们也可以在./mmdet/models/backbones/resnet.py
中找到ResNet类的定义,并且字典的键值和构造函数匹配。
mask_rcnn_r101_fpn_2x_coco.py
1. 模型配置
model = dict(
1.1 # 检测器(detector)名称
type='MaskRCNN',
1.2# 主干网络的配置文件
backbone=dict(
type='ResNet', # 主干网络的类别,可用选项请参考 .mmdet/models/backbones/resnet.py
depth=50, # 主干网络的深度,对于 ResNet 和 ResNext 通常设置为 50 或 101。
num_stages=4, # 主干网络状态(stages)的数目,产生的特征图作为后续的 head 的输入。
out_indices=(0, 1, 2, 3), # 每个状态产生的特征图输出的索引。
frozen_stages=1, # 第一个状态的权重被冻结
norm_cfg=dict( # 归一化层(norm layer)的配置项。
type='BN', # 归一化层的类别,通常是 BN 或 GN。
requires_grad=True), # 是否训练归一化里的 gamma 和 beta。
norm_eval=True, # 是否冻结 BN 里的统计项。
style='pytorch', # 主干网络的风格,'pytorch' 意思是步长为2的层为 3x3 卷积, 'caffe' 意思是步长为2的层为 1x1 卷积。
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), # 加载通过 ImageNet 预训练的模型
1.3 # 检测器的 neck 是 FPN,我们同样支持 'NASFPN', 'PAFPN' 等,更多细节可以参考 ./mmdet/models/necks/fpn.py#L10。
neck=dict(
type='FPN',
in_channels=[256, 512, 1024, 2048], # 输入通道数,这与主干网络的输出通道一致
out_channels=256, # 金字塔特征图每一层的输出通道
num_outs=5), # 输出的范围(scales)
rpn_head=dict(
type='RPNHead', # RPN_head 的类型是 'RPNHead', 我们也支持 'GARPNHead' 等,更多细节可以参考 ./mmdet/models/dense_heads/rpn_head.py#L12。
in_channels=256, # 每个输入特征图的输入通道,这与 neck 的输出通道一致。
feat_channels=256, # head 卷积层的特征通道。
anchor_generator=dict( # 锚点(Anchor)生成器的配置。
type='AnchorGenerator', # 大多是方法使用 AnchorGenerator 作为锚点生成器, SSD 检测器使用 `SSDAnchorGenerator`。更多细节请参考 ./mmdet/core/anchor/anchor_generator.py#L10。
scales=[8], # 锚点的基本比例,特征图某一位置的锚点面积为 scale * base_sizes
ratios=[0.5, 1.0, 2.0], # 高度和宽度之间的比率。
strides=[4, 8, 16, 32, 64]), # 锚生成器的步幅。这与 FPN 特征步幅一致。 如果未设置 base_sizes,则当前步幅值将被视为 base_sizes。
# 在训练和测试期间对框进行编码和解码。
bbox_coder=dict(
type='DeltaXYWHBBoxCoder', # 框编码器的类别,'DeltaXYWHBBoxCoder' 是最常用的,更多细节请参考 ./mmdet/core/bbox/coder/delta_xywh_bbox_coder.py#L9。
target_means=[0.0, 0.0, 0.0, 0.0], # 用于编码和解码框的目标均值
target_stds=[1.0, 1.0, 1.0, 1.0]), # 用于编码和解码框的标准差
# 分类分支的损失函数配置
loss_cls=dict(
type='CrossEntropyLoss', # 分类分支的损失类型,我们也支持 FocalLoss 等。
use_sigmoid=True, # RPN通常进行二分类,所以通常使用sigmoid函数。
los_weight=1.0), # 分类分支的损失权重。
# 回归分支的损失函数配置。
loss_bbox=dict(
type='L1Loss', # 损失类型,我们还支持许多 IoU Losses 和 Smooth L1-loss 等,更多细节请参考 ./mmdet/models/losses/smooth_l1_loss.py#L56。
loss_weight=1.0)), # 回归分支的损失权重。
# RoIHead 封装了两步(two-stage)/级联(cascade)检测器的第二步。
roi_head=dict(
type='StandardRoIHead', # RoI head 的类型,更多细节请参考 ./mmdet/models/roi_heads/standard_roi_head.py#L10。
# 用于 bbox 回归的 RoI 特征提取器。
bbox_roi_extractor=dict(
type='SingleRoIExtractor', # RoI 特征提取器的类型,大多数方法使用 SingleRoIExtractor,更多细节请参考 ./mmdet/models/roi_heads/roi_extractors/single_level.py#L10。
# RoI 层的配置
roi_layer=dict(
type='RoIAlign', # RoI 层的类别, 也支持 DeformRoIPoolingPack 和 ModulatedDeformRoIPoolingPack,更多细节请参考 ./mmdet/ops/roi_align/roi_align.py#L79。
output_size=7, # 特征图的输出大小。
sampling_ratio=0), # 提取 RoI 特征时的采样率。0 表示自适应比率。
out_channels=256, # 提取特征的输出通道。
featmap_strides=[4, 8, 16, 32]), # 多尺度特征图的步幅,应该与主干的架构保持一致。
# RoI 层的配置
bbox_head=dict( # RoIHead 中 box head 的配置.
type='Shared2FCBBoxHead', # bbox head 的类别,更多细节请参考 ./mmdet/models/roi_heads/bbox_heads/convfc_bbox_head.py#L177。
in_channels=256, # bbox head 的输入通道。 这与 roi_extractor 中的 out_channels 一致。
fc_out_channels=1024, # FC 层的输出特征通道。
roi_feat_size=7, # 候选区域(Region of Interest)特征的大小。
num_classes=80, # 分类的类别数量。
# 第二阶段使用的框编码器。
bbox_coder=dict(
type='DeltaXYWHBBoxCoder', # 框编码器的类别,大多数情况使用 'DeltaXYWHBBoxCoder'。
target_means=[0.0, 0.0, 0.0, 0.0], # 用于编码和解码框的均值
target_stds=[0.1, 0.1, 0.2, 0.2]), # 编码和解码的标准差。因为框更准确,所以值更小,常规设置时 [0.1, 0.1, 0.2, 0.2]。
reg_class_agnostic=False, # 回归是否与类别无关。
# 分类分支的损失函数配置
loss_cls=dict(
type='CrossEntropyLoss', # 分类分支的损失类型,我们也支持 FocalLoss 等。
use_sigmoid=False, # 是否使用 sigmoid。
loss_weight=1.0), # 分类分支的损失权重。
# 回归分支的损失函数配置。
loss_bbox=dict(
type='L1Loss', # 损失类型,我们还支持许多 IoU Losses 和 Smooth L1-loss 等。
loss_weight=1.0)), # 回归分支的损失权重。
mask_roi_extractor=dict( # 用于 mask 生成的 RoI 特征提取器。
type='SingleRoIExtractor', # RoI 特征提取器的类型,大多数方法使用 SingleRoIExtractor。
roi_layer=dict( # 提取实例分割特征的 RoI 层配置
type='RoIAlign', # RoI 层的类型,也支持 DeformRoIPoolingPack 和 ModulatedDeformRoIPoolingPack。
output_size=14, # 特征图的输出大小。
sampling_ratio=0), # 提取 RoI 特征时的采样率。
out_channels=256, # 提取特征的输出通道。
featmap_strides=[4, 8, 16, 32]), # 多尺度特征图的步幅。
# mask 预测 head 模型
mask_head=dict(
type='FCNMaskHead', # mask head 的类型,更多细节请参考 ./mmdet/models/roi_heads/mask_heads/fcn_mask_head.py#L21。
num_convs=4, # mask head 中的卷积层数
in_channels=256, # 输入通道,应与 mask roi extractor 的输出通道一致。
conv_out_channels=256, # 卷积层的输出通道。
num_classes=80, # 要分割的类别数。
# mask 分支的损失函数配置。
loss_mask=dict(
type='CrossEntropyLoss', # 用于分割的损失类型。
use_mask=True, # 是否只在正确的类中训练 mask。
loss_weight=1.0)))) # mask 分支的损失权重.
1.4# rpn 和 rcnn 训练超参数的配置
train_cfg = dict(
rpn=dict( # rpn 的训练配置
assigner=dict( # 分配器(assigner)的配置
type='MaxIoUAssigner', # 分配器的类型,MaxIoUAssigner 用于许多常见的检测器,更多细节请参考 ./mmdet/core/bbox/assigners/max_iou_assigner.py#L10。
pos_iou_thr=0.7, # IoU >= 0.7(阈值) 被视为正样本。
neg_iou_thr=0.3, # IoU < 0.3(阈值) 被视为负样本。
min_pos_iou=0.3, # 将框作为正样本的最小 IoU 阈值。
match_low_quality=True, # 是否匹配低质量的框(更多细节见 API 文档).
ignore_iof_thr=-1), # 忽略 bbox 的 IoF 阈值。
sampler=dict( # 正/负采样器(sampler)的配置
type='RandomSampler', # 采样器类型,还支持 PseudoSampler 和其他采样器,更多细节请参考 ./mmdet/core/bbox/samplers/random_sampler.py#L8。
num=256, # 样本数量。
pos_fraction=0.5, # 正样本占总样本的比例。
neg_pos_ub=-1, # 基于正样本数量的负样本上限。
add_gt_as_proposals=False), # 采样后是否添加 GT 作为 proposal。
allowed_border=-1, # 填充有效锚点后允许的边框。
pos_weight=-1, # 训练期间正样本的权重。
debug=False), # 是否设置调试(debug)模式
# 在训练期间生成 proposals 的配置
rpn_proposal=dict(
nms_across_levels=False, # 是否对跨层的 box 做 NMS。仅适用于 `GARPNHead` ,naive rpn 不支持 nms cross levels。
nms_pre=2000, # NMS 前的 box 数
nms_post=1000, # NMS 要保留的 box 的数量,只在 GARPNHHead 中起作用。
max_per_img=1000, # NMS 后要保留的 box 数量。
nms=dict( # NMS 的配置
type='nms', # NMS 的类别
iou_threshold=0.7 # NMS 的阈值
),
min_bbox_size=0), # 允许的最小 box 尺寸
# roi head 的配置。
rcnn=dict(
assigner=dict( # 第二阶段分配器的配置,这与 rpn 中的不同
type='MaxIoUAssigner', # 分配器的类型,MaxIoUAssigner 目前用于所有 roi_heads。更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/master/mmdet/core/bbox/assigners/max_iou_assigner.py#L10。
pos_iou_thr=0.5, # IoU >= 0.5(阈值)被认为是正样本。
neg_iou_thr=0.5, # IoU < 0.5(阈值)被认为是负样本。
min_pos_iou=0.5, # 将 box 作为正样本的最小 IoU 阈值
match_low_quality=False, # 是否匹配低质量下的 box(有关更多详细信息,请参阅 API 文档)。
ignore_iof_thr=-1), # 忽略 bbox 的 IoF 阈值
sampler=dict(
type='RandomSampler', #采样器的类型,还支持 PseudoSampler 和其他采样器,更多细节请参考 ./mmdet/core/bbox/samplers/random_sampler.py#L8。
num=512, # 样本数量
pos_fraction=0.25, # 正样本占总样本的比例。.
neg_pos_ub=-1, # 基于正样本数量的负样本上限。.
add_gt_as_proposals=True
), # 采样后是否添加 GT 作为 proposal。
mask_size=28, # mask 的大小
pos_weight=-1, # 训练期间正样本的权重。
debug=False)) # 是否设置调试模式。
1.5 # 用于测试 rpn 和 rcnn 超参数的配置
test_cfg = dict(
rpn=dict( # 测试阶段生成 proposals 的配置
nms_across_levels=False, # 是否对跨层的 box 做 NMS。仅适用于`GARPNHead`,naive rpn 不支持做 NMS cross levels。
nms_pre=1000, # NMS 前的 box 数
nms_post=1000, # NMS 要保留的 box 的数量,只在`GARPNHHead`中起作用。
max_per_img=1000, # NMS 后要保留的 box 数量
nms=dict( # NMS 的配置
type='nms', # NMS 的类型
iou_threshold=0.7 # NMS 阈值
),
min_bbox_size=0), # box 允许的最小尺寸
rcnn=dict( # roi heads 的配置
score_thr=0.05, # bbox 的分数阈值
nms=dict( # 第二步的 NMS 配置
type='nms', # NMS 的类型
iou_thr=0.5), # NMS 的阈值
max_per_img=100, # 每张图像的最大检测次数
mask_thr_binary=0.5)) # mask 预处的阈值
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'), # 第 1 个流程,从文件路径里加载图像。
dict(
type='LoadAnnotations', # 第 2 个流程,对于当前图像,加载它的注释信息。
with_bbox=True, # 是否使用标注框(bounding box), 目标检测需要设置为 True。
with_mask=True, # 是否使用 instance mask,实例分割需要设置为 True。
poly2mask=False), # 是否将 polygon mask 转化为 instance mask, 设置为 False 以加速和节省内存。
dict(
type='Resize', # 变化图像和其注释大小的数据增广的流程。
img_scale=(1333, 800), # 图像的最大规模。
keep_ratio=True
), # 是否保持图像的长宽比。
dict(
type='RandomFlip', # 翻转图像和其注释大小的数据增广的流程。
flip_ratio=0.5), # 翻转图像的概率。
dict(
type='Normalize', # 归一化当前图像的数据增广的流程。
mean=[123.675, 116.28, 103.53], # 这些键与 img_norm_cfg 一致,因为 img_norm_cfg 被
std=[58.395, 57.12, 57.375], # 用作参数。
to_rgb=True),
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'), # 第 1 个流程,从文件路径里加载图像。
dict(
type='MultiScaleFlipAug', # 封装测试时数据增广(test time augmentations)。
img_scale=(1333, 800), # 决定测试时可改变图像的最大规模。用于改变图像大小的流程。
flip=False, # 测试时是否翻转图像。
transforms=[
dict(type='Resize', # 使用改变图像大小的数据增广。
keep_ratio=True), # 是否保持宽和高的比例,这里的图像比例设置将覆盖上面的图像规模大小的设置。
dict(type='RandomFlip'), # 考虑到 RandomFlip 已经被添加到流程里,当 flip=False 时它将不被使用。
dict(
type='Normalize', # 归一化配置项,值来自 img_norm_cfg。
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(
type='Pad', # 将配置传递给可被 32 整除的图像。
size_divisor=32),
dict(
type='ImageToTensor', # 将图像转为张量
keys=['img']),
dict(
type='Collect', # 收集测试时必须的键的收集流程。
keys=['img'])
])
]
2. 数据集配置(datasets) =========================================
data = dict(
samples_per_gpu=2, # 单个 GPU 的 Batch size
workers_per_gpu=2, # 单个 GPU 分配的数据加载线程数
train=dict( # 训练数据集配置
type='CocoDataset', # 数据集的类别, 更多细节请参考 ./mmdet/datasets/coco.py#L19。
ann_file='data/coco/annotations/instances_train2017.json', # 注释文件路径
img_prefix='data/coco/train2017/', # 图片路径前缀
pipeline=[ # 流程, 这是由之前创建的 train_pipeline 传递的。
dict(type='LoadImageFromFile'),
dict(
type='LoadAnnotations',
with_bbox=True,
with_mask=True,
poly2mask=False),
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
dict(type='RandomFlip', flip_ratio=0.5),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='Pad', size_divisor=32),
dict(type='DefaultFormatBundle'),
dict(
type='Collect',
keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks'])
]),
val=dict( # 验证数据集的配置
type='CocoDataset',
ann_file='data/coco/annotations/instances_val2017.json',
img_prefix='data/coco/val2017/',
pipeline=[ # 由之前创建的 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',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='Pad', size_divisor=32),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
])
]),
test=dict( # 测试数据集配置,修改测试开发/测试(test-dev/test)提交的 ann_file
type='CocoDataset',
ann_file='data/coco/annotations/instances_val2017.json',
img_prefix='data/coco/val2017/',
pipeline=[ # 由之前创建的 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',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='Pad', size_divisor=32),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
])
],
samples_per_gpu=2 # 单个 GPU 测试时的 Batch size
3. 训练策略配置(schedules) =========================================
evaluation = dict( # evaluation hook 的配置,更多细节请参考 ./mmdet/core/evaluation/eval_hooks.py#L7。
interval=1, # 验证的间隔。
metric=['bbox', 'segm']) # 验证期间使用的指标。
optimizer = dict( # 用于构建优化器的配置文件。支持 PyTorch 中的所有优化器,同时它们的参数与 PyTorch 里的优化器参数一致。
type='SGD', # 优化器种类,更多细节可参考 ./mmdet/core/optimizer/default_constructor.py#L13。
lr=0.02, # 优化器的学习率,参数的使用细节请参照对应的 PyTorch 文档。
momentum=0.9, # 动量(Momentum)
weight_decay=0.0001) # SGD 的衰减权重(weight decay)。
optimizer_config = dict( # optimizer hook 的配置文件,执行细节请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/optimizer.py#L8。
grad_clip=None) # 大多数方法不使用梯度限制(grad_clip)。
lr_config = dict( # 学习率调整配置,用于注册 LrUpdater hook。
policy='step', # 调度流程(scheduler)的策略,也支持 CosineAnnealing, Cyclic, 等。请从 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/lr_updater.py#L9 参考LrUpdater 的细节。
warmup='linear', # 预热(warmup)策略,也支持 `exp` 和 `constant`。
warmup_iters=500, # 预热的迭代次数
warmup_ratio=0.001, # 用于热身的起始学习率的比率
step=[8, 11]) # 衰减学习率的起止回合数
runner = dict(
type='EpochBasedRunner', # 将使用的 runner 的类别 (例如 IterBasedRunner 或 EpochBasedRunner)。
max_epochs=12) # runner 总回合数, 对于 IterBasedRunner 使用 `max_iters`
4. 运行配置(runtime) =========================================
checkpoint_config = dict( # Checkpoint hook 的配置文件。执行时请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/checkpoint.py。
interval=1) # 保存的间隔是 1。
log_config = dict( # register logger hook 的配置文件。
interval=50, # 打印日志的间隔
hooks=[
# dict(type='TensorboardLoggerHook') # 同样支持 Tensorboard 日志
dict(type='TextLoggerHook')
]) # 用于记录训练过程的记录器(logger)。
dist_params = dict(backend='nccl') # 用于设置分布式训练的参数,端口也同样可被设置。
log_level = 'INFO' # 日志的级别。
load_from = None # 从一个给定路径里加载模型作为预训练模型,它并不会消耗训练时间。
resume_from = None # 从给定路径里恢复检查点(checkpoints),训练模式将从检查点保存的轮次开始恢复训练。
workflow = [('train', 1)] # runner 的工作流程,[('train', 1)] 表示只有一个工作流且工作流仅执行一次。根据 total_epochs 工作流训练 12个回合。
work_dir = 'work_dir' # 用于保存当前实验的模型检查点和日志的目录。
配置文件包含四个基本组件:datasets、models、schedules以及runtime。model除了模型本身网络结构以外,还包含bbox assigner、bbox sampler、nms等配置参数;data主要包含batch_size、训练集、验证集、测试集的相关参数 ;schedules包含优化器、学习率、迭代次数等训练过程相关参数的配置信息;runtime则是一些日志打印、参数保存、分布式训练相关的配置。
3.config 类
MMDetection使用MMCV库中Config类完成对配置文件的解析。Config 类用于操作配置文件,它支持从多种文件格式中加载配置,包括python, json和yaml。 它提供了类似字典对象的接口来获取和设置值。
3.1 读取配置文件
Config.fromfile(filename)
来读取配置文件(也可以直接传入一个dict),返回一个Config类:
from mmcv import Config
cfg = Config.fromfile('../configs/test_config.py')
fromfile()函数源码如下,其核心函数是_file2dict()。_file2dict()会根据文本顺序,按照key = value的格式解析配置文件,得到一个名为cfg_dict的字典,如果存在_base_字段,还会对_base_包含的每个文件路径再调用一次_file2dict()函数,将文件中包含的配置参数加入到cfg_dict中,实现配置文件的继承功能。需要注意的是,_file2dict()内部会对_base_中不同文件包含的键值进行校验,不同base文件中不允许出现重复的键值,否则MMCV不知道以哪个base文件为准。
def fromfile(filename,
use_predefined_variables=True,
import_custom_modules=True):
cfg_dict, cfg_text = Config._file2dict(filename,
use_predefined_variables)
# import_modules_from_strings()是根据字符串列表对应的模块
if import_custom_modules and cfg_dict.get('custom_imports', None):
import_modules_from_strings(**cfg_dict['custom_imports'])
return Config(cfg_dict, cfg_text=cfg_text, filename=filename)
dict数据类型转换为ConfigDict类型进行处理。ConfigDict是第三方库addict中Dict的子类,因为python原生的dict类型不支持.属性的访问方式,特别是dict内部嵌套了多层dict的时候,如果按照key的访问方式,代码写起来非常低效,而Dict类通过重写__getattr__()的方式实现了.属性的访问方式。所以继承了Dict的ConfigDict也支持使用.属性的方式访问字典中的各个成员值。
3.2 修改配置参数
_file2dict()是根据文本顺序构建字典,所以后写的键值可以覆盖原来的值 (如果变量类型是list,会将list进行全部替换,无法实现某一个item的修改)。以修改优化器为例,原来的继承的优化器是SGD,学习率为0.02:现在想要将由_base_
继承的学习率调整为0.001,可以直接在当前配置文件中增加一行:
#原始的优化器
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
#修改学习率
optimizer = dict(lr=0.001)
#修改后的优化器
ptimizer = dict(type='SGD', lr=0.001, momentum=0.9, weight_decay=0.0001)
_delete_=True
来实现:然后就完成了优化器的替换。
# 将原来的SGD替换成AdamW
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.0001)
Mask R-CNN
使用 ResNet-50
,在需要将主干网络改成 HRNet
的时候,因为 HRNet
和 ResNet
中有不同的字段,需要使用 _delete_=True
将新的键去替换 backbone
#原始的model
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(...))
#修改为HRNet的model
_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(...))
3.3使用中间变量
train_pipeline
/test_pipeline
。我们在定义新的 train_pipeline
/test_pipeline
之后,需要将它们传递到 data
里。例如,我们想在训练或测试时,改变 Mask R-CNN 的多尺度策略 (multi scale strategy),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))
如果我们想从 SyncBN
切换到 BN
或者 MMSyncBN
,我们需要修改配置文件里的每一个 norm_cfg
。
_base_ = './mask_rcnn_r50_fpn_1x_coco.py'
norm_cfg = dict(type='BN', requires_grad=True)
model = dict(
backbone=dict(norm_cfg=norm_cfg),
neck=dict(norm_cfg=norm_cfg),
...)
3.4 打印配置文件
./tools/misc/print_config.py来打印配置文件:
我们可以在根目录下通过print config把相关的参数打印出来进行研究。如下我们要打印mask_rcnn的配置文件进行研究:
python tools/misc/print_config.py configs/mask_rcnn/mask_rcnn_r101_fpn_2x_coco.py
configs/mask_rcnn/mask_rcnn_r101_fpn_2x_coco.py 是你要执行的基本model路径
然后就可以看到mask_rcnn_r101_fpn_2x_coco.py对应的完整的配置文件内容。
在此处看配置文件不太方便,所以可以采用将配置文件保存到txt文件,用如下命令
python tools/misc/print_config.py configs/mask_rcnn/mask_rcnn_r101_fpn_2x_coco.py > config.txt
在根目录下可以查看生成的tet文件: