四、技术细节
在这一节,我们将介绍训练检测器的主要工具:数据管道,模型和迭代管道。
(一)数据管道
根据标准协议,我们使用Dataset和DataLoader作为多工作数据加载。Dataset返回一个数据字典,数据字典的参数是模型前向算法中的参数。由于被检测的数据对象可能大小不一样(比如图像大小,bbox大小等),我们推荐一个MMCV中新的DataContainer类型,来收集和分发不同大小的数据。点击这个链接来获得更多的细节。
数据预处理管道和数据集是分开进行的。通常一个数据集定义了注释的处理,数据管道定义了所有准备数据字典的步骤。一个数据管道包括一系列的操作。每一个操作将使用一个字典作为输入,同时输出一个字典作为下一次转换的开始。
下图中,我们发布了一个经典的数据管道。蓝色方块是数据管道操作。随着数据管道往后运行,每一个操作可以向字典结果增加新的键值(绿色标志的),或者是更新已出现的键值(橙色标注的)。
数据管道图
上述操作在数据加载、预处理、格式化和测试时间提高等模块中被分类进行。
下面是Faster R-CNN数据管道的例子。
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’]),
])
]
为每一个操作,我们都列出了added/updated/removed(增加,更新、删除)相关的字典域。
1.数据加载
LoadImageFromFile
增加:img, img_shape, ori_shape
LoadAnnotations
增加:gt_bboxes, gt_bboxes_ignore, gt_labels, gt_masks, gt_semantic_seg, bbox_fields, mask_fields
LoadProposals
增加:proposals
2.预处理
Resize
增加:flip
更新:img, *bbox_fields, *mask_fields, *seg_fields
Pad
增加:pad_fixed_size, pad_size_divisor
更新:img, pad_shape, *mask_fields, *seg_fields
RandomCrop
更新:img, pad_shape, gt_bboxes, gt_labels, gt_masks, *bbox_fields
Normalize
增加:img_norm_cfg
更新:img
SegRescale
更新:gt_semantic_seg
PhotoMetricDistortion
更新:img
Expand
更新:img, gt_bboxes
MinIoURandomCrop
更新:img, gt_bboxes, gt_labels
Corrupt
更新:img
2.格式化
ToTensor
更新:由keys定义
ImageToTensor
更新:由keys定义
Transpose
更新:由keys定义
ToDataContainer
更新:由fields定义
DefaultFormatBundle
更新:img, proposals, gt_bboxes, gt_bboxes_ignore, gt_labels, gt_masks, gt_semantic_seg
Collect
增加:
更新:img_meta(img_meta关键字由meta_keys明确)
删除:除了keys定义过的所有其他值
3.测试时间优化
MultiScaleFlipAug
(二)模型
在MMDetection,我们大体上将模型组件分为四类。
Backbone(主干):通常一个FCN网络来抽出特征图,比如ResNet
Neck(脖子):这个组件介于主干和头之间,比如FPN,ASPP
Head(头):具体工作任务组件。比如bbox预测和mask预测。
Roi extractor(兴趣区提取器):从特征图中提取ROI特征的部分,比如ROI Align
为上面的组件,我们写了一些检测管道的工具,比如SingleStageDetector和TwoStageDetector。
1.使用基本组件建立一个模型
依照这些基本管道(比如二阶检测器),可以很容易的通过config文件来定制模型的结构。
如果我们想要定制一些新的组件,比如在Path Aggregation Network for Instance Segmentation(点击链接获取)中列出的FPN结构,那么有两步要完成:
1)中建立一个新的文件mmdet/models/necks/pafpn.py
from …registry import NECKS
@NECKS.register
class PAFPN(nn.Module):
def __init__(self,
in_channels,
out_channels,
num_outs,
start_level=0,
end_level=-1,
add_extra_convs=False):
pass
def forward(self, inputs):
# implementation is ignored
pass
2)在mmdet/models/necks/init.py导入模型
from .pafpn import PAFPN
3)按照下面所示,将config文件中的
neck=dict(
type=‘FPN’,
in_channels=[256, 512, 1024, 2048],
out_channels=256,
num_outs=5)
修改为:
neck=dict(
type=‘PAFPN’,
in_channels=[256, 512, 1024, 2048],
out_channels=256,
num_outs=5)
我们将为研究目的发布更多的组件(backbones, necks, heads)
2.写一个新的模型
写一个新的检测管道,你需要继承BaseDetector,来定义下面的抽象方法。
extract_feat():给一个图像形状批处理shape(n,c,h,w),抽出特征图。
forward_train():训练模型的前向方法。
simple_test():未加增强的单模检测。
aug_test():增强的检测(比如multi-scale, flip:多尺度,图像增强)
TwoStageDetector将更好的说明如何去做。
(三)管道迭代
我们为单机器和多机器选择了分布式训练。支持上面的服务需要8个GPU,8个进程将同时开始,每一个进程运行在一个GPU上。
每个进程维护了一个独立的模型、数据加载和优化方法。模型参数只是在开始的时候会同步。在前向传播和后向传播结束后,梯度将会在所有的GPU上减少,优化器将更新模型参数。梯度被减少,在所有进程中,在迭代以后的模型参数保持不变。
(四)其他信息
你可以参考我们的技术文档,来获得更多信息。