MMdetection官方文档:
https://mmdetection.readthedocs.io/zh_CN/latest/article.html
官方有原文https://zhuanlan.zhihu.com/p/337375549
我只提取我在意的
源码在mmdet/models/backbones
如果要自己新建backbone,使用mmcv/utils/registry.py的注册机制,在class前加@BACKBONES.register_module(),然后再init文件中导入并加入__all__的声明。
和detectron2相似。
通过 dict 形式的配置来实例化任何已经注册的类,detectron2是通过yaml配置文件的方式
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'),
mmdet/models/necks
对backbone之后的特征进行加工
init文件中可以看到,常用的FPN
__all__ = [
'FPN', 'BFP', 'ChannelMapper', 'HRFPN', 'NASFPN', 'FPN_CARAFE', 'PAFPN',
'NASFCOS_FPN', 'RFP', 'YOLOV3Neck', 'FPG', 'DilatedEncoder',
'CTResNetNeck', 'SSDNeck', 'YOLOXPAFPN', 'DyHead'
]
目标检测的head,输出一般是分类和回归,所有的 one-stage 算法的 head 模块都在mmdet/models/dense_heads
中,mmdet的好处在于每个算法都是独立的head。
two-stage 算法还包括额外的mmdet/models/roi_heads
mmdet/models/roi_heads/roi_extractors
:two-stage 或者 mutli-stage 算法,会额外包括一个区域提取器 roi extractor,用于将不同大小的 RoI 特征图统一成相同大小。
mmdet/models/roi_heads/mask_heads
存放maskhead
由于正负样本属性定义、正负样本采样和 bbox 编解码模块都在 head 模块中进行组合调用,故 MMDetection 中最复杂的模块就是 head。
enhance 是即插即用、能够对特征进行增强的模块,其具体代码可以通过 dict 形式注册到 backbone、neck 和 head 中。常用的 enhance 模块是 SPP、ASPP、RFB、Dropout、Dropblock、DCN 和各种注意力模块 SeNet、Non_Local、CBA 等
目前mmdet/models/plugins
中只有 ‘DropBlock’, ‘PixelDecoder’, ‘TransformerEncoderPixelDecoder’,
‘MSDeformAttnPixelDecoder’
mmdet/core/bbox/assigners
分配正负样本的策略。
__all__ = [
'BaseAssigner', 'MaxIoUAssigner', 'ApproxMaxIoUAssigner', 'AssignResult',
'PointAssigner', 'ATSSAssigner', 'CenterRegionAssigner', 'GridAssigner',
'HungarianAssigner', 'RegionAssigner', 'UniformAssigner', 'SimOTAAssigner',
'TaskAlignedAssigner', 'MaskHungarianAssigner'
]
mmdet/core/bbox/samplers
本模块作用是对前面定义的正负样本不平衡进行采样
mmdet/core/bbox/coder
__all__ = [
'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy',
'mask_cross_entropy', 'CrossEntropyLoss', 'sigmoid_focal_loss',
'FocalLoss', 'smooth_l1_loss', 'SmoothL1Loss', 'balanced_l1_loss',
'BalancedL1Loss', 'mse_loss', 'MSELoss', 'iou_loss', 'bounded_iou_loss',
'IoULoss', 'BoundedIoULoss', 'GIoULoss', 'DIoULoss', 'CIoULoss', 'GHMC',
'GHMR', 'reduce_loss', 'weight_reduce_loss', 'weighted_loss', 'L1Loss',
'l1_loss', 'isr_p', 'carl_loss', 'AssociativeEmbeddingLoss',
'GaussianFocalLoss', 'QualityFocalLoss', 'DistributionFocalLoss',
'VarifocalLoss', 'KnowledgeDistillationKLDivLoss', 'SeesawLoss', 'DiceLoss'
]
训练时候进行了编码,那么对应的测试环节需要进行解码。根据编码的不同,解码也是不同的。举个简单例子:假设训练时候对宽高是直接除以图片宽高进行归一化的,那么解码过程也仅仅需要乘以图片宽高即可。其代码和 bbox encoder 放在一起,在mmdet/core/bbox/coder
中。
后处理
mmdet/core/post_processing
mmdet/models/detectors/single_stage.py
和mmdet/models/detectors/two_stage.py
是构建单阶段和两件段检测的核心,是每个detector的父类
class SingleStageDetector(BaseDetector):
def __init__(...):
# 构建骨架、neck和head
self.backbone = build_backbone(backbone)
if neck is not None:
self.neck = build_neck(neck)
self.bbox_head = build_head(bbox_head)
def forward_train(---):
# 先运行backbone+neck进行特征提取
x = self.extract_feat(img)
# 对head进行forward train,输出loss
losses = self.bbox_head.forward_train(x, img_metas, gt_bboxes,
gt_labels, gt_bboxes_ignore)
return losses
def simple_test(---):
# 先运行backbone+neck进行特征提取
x = self.extract_feat(img)
# head输出预测特征图
outs = self.bbox_head(x)
# bbox解码和还原
bbox_list = self.bbox_head.get_bboxes(
*outs, img_metas, rescale=rescale)
# 重组结果返回
bbox_results = [
bbox2result(det_bboxes, det_labels, self.bbox_head.num_classes)
for det_bboxes, det_labels in bbox_list
]
return bbox_results
可以发现训练部分最核心的就是bbox_head.forward_train,测试部分最核心的是bbox_head.get_bboxes
要理解MMDetection的算法实现流程,必须要吃透Config、Registry、Runner和Hook这四个类。
configs/
中base是基本模型的配置文件,
configs/_base_/datasets
是数据集设置
configs/_base_/models
基本模型的构建
configs/_base_/schedules
是训练策略,optimizer和lr
mmdet/apis
中train.py是训练的核心代码,一般不会改,除非有自定义需求,在def train_detector中,重点是build_runner和register_training_hooks
mmdet/core
中bbox中比较重要,上面提到的关于bbox的代码都在这里
mmdet/datasets
数据集的构建和读取
https://zhuanlan.zhihu.com/p/341954021
mmdet/datasets/pipelines
mmdet/datasets/pipelines/loading.py
图片和标签加载
mmdet/datasets/pipelines/transforms.py
有resize,flip等操作
# optimizer
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=0.01,
step=[8, 11])
runner = dict(type='EpochBasedRunner', max_epochs=12)
optimizer 的 lr = 0.00125*batch_size
8 gpus、imgs_per_gpu = 2:lr = 0.02;
2 gpus、imgs_per_gpu = 2 或 4 gpus、imgs_per_gpu = 1:lr = 0.005;
4 gpus、imgs_per_gpu = 2:lr = 0.01
lr_config指的是学习率变化策略,图片中给出的是线性增加策略,准确来说,初始学习率为warmup_ratio的值0.001,在前500个iters线性增加学习率,之后的iter保持optimizer设置的0.02不变,直到第8个epoch开始学习率降低。
如果使用Adam是自适应初始化lr
data = dict(
samples_per_gpu=2,
#每张gpu训练多少张图片 batch_size = 训练使用gpu数量 * imgs_per_gpu
workers_per_gpu=2,
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))
在mmdet/datasets
中注册自己的数据集,可以辅助coco.py,然后修改里面的CLASSES和PALETTE
如果只有一个CLASSES,要加上一个逗号,否则将会报错。
(‘person’,)
然后在configs/_base_/datasets
中设置数据集配置文件
https://www.csdn.net/tags/NtTaggysOTE1ODctYmxvZwO0O0OO0O0O.html
解决思路2:每次训练完清空cuda的缓存
在环境中mmcv 的 runner中epoch_based_runner.py
RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one. This error indicates that your module has parameters that were not used in producing loss. You can enable unused parameter detection by (1) passing the keyword argument find_unused_parameters=True to torch.nn.parallel.DistributedDataParallel; (2) making sure all forward function outputs participate in calculating loss. If you already have done the above two steps, then the distributed data parallel module wasn’t able to locate the output tensors in the return value of your module’s forward function. Please include the loss function and the structure of the return value of forward of your module when reporting this issue (e.g. list, dict, iterable).
解决方案:https://github.com/open-mmlab/mmaction/issues/150
在mmdet/apis/train.py
find_unused_parameters = cfg.get('find_unused_parameters', False)
False 变True
https://blog.csdn.net/weixin_41693877/article/details/116134263
其中模型瘦身技巧
mmdetection在保存模型时,除了保存权重,还保存了原始数据和优化参数。但是,模型在测试时,有些参数是没有用的,怎样去掉这些无用的参数使模型减小(大约减小50%)呢?见下面的代码:
import torch
model_path = "epoch_30.pth"
checkpoint = torch.load(model_path)
checkpoint['meta'] = None
checkpoint['optimizer'] = None
weights = checkpoint['state_dict']
state_dict = {"state_dict":weights}
torch.save(state_dict, './epoch_30_new.pth')
后续在使用中的学到的tips会记录