快捷键

本教程收集了有关“如何使用 MMDetection 进行 xxx”的解答。 如果您遇到有关“如何”的新问题并找到了答案,请随时更新本文档!

通过 MMPretrain 使用主干网络

MMDet、MMPreTrain 和 MMSeg 中的模型注册表都继承了 MMEngine 中的根注册表。 这使这些存储库可以直接使用彼此已实现的模块。 因此,用户可以在 MMDetection 中使用 MMPretrain 中的主干网络,而无需实现 MMPretrain 中已存在的网络。

使用 MMPretrain 中实现的主干网络

假设您想使用 MobileNetV3-small 作为 RetinaNet 的主干网络,示例配置如下所示。

_base_ = [
    '../_base_/models/retinanet_r50_fpn.py',
    '../_base_/datasets/coco_detection.py',
    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]
# please install mmpretrain
# import mmpretrain.models to trigger register_module in mmpretrain
custom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)
pretrained = 'https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth'
model = dict(
    backbone=dict(
        _delete_=True, # Delete the backbone field in _base_
        type='mmpretrain.MobileNetV3', # Using MobileNetV3 from mmpretrain
        arch='small',
        out_indices=(3, 8, 11), # Modify out_indices
        init_cfg=dict(
            type='Pretrained',
            checkpoint=pretrained,
            prefix='backbone.')), # The pre-trained weights of backbone network in mmpretrain have prefix='backbone.'. The prefix in the keys will be removed so that these weights can be normally loaded.
    # Modify in_channels
    neck=dict(in_channels=[24, 48, 96], start_level=0))

通过 MMPretrain 在 TIMM 中使用主干网络

MMPretrain 还为 PyTorch 图像模型 (timm) 主干网络提供了一个包装器,用户可以直接通过 MMPretrain 在 timm 中使用主干网络。 假设您想使用 EfficientNet-B1 作为 RetinaNet 的主干网络,示例配置如下所示。

# https://github.com/open-mmlab/mmdetection/blob/main/configs/timm_example/retinanet_timm-efficientnet-b1_fpn_1x_coco.py

_base_ = [
    '../_base_/models/retinanet_r50_fpn.py',
    '../_base_/datasets/coco_detection.py',
    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]

# please install mmpretrain
# import mmpretrain.models to trigger register_module in mmpretrain
custom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)
model = dict(
    backbone=dict(
        _delete_=True, # Delete the backbone field in _base_
        type='mmpretrain.TIMMBackbone', # Using timm from mmpretrain
        model_name='efficientnet_b1',
        features_only=True,
        pretrained=True,
        out_indices=(1, 2, 3, 4)), # Modify out_indices
    neck=dict(in_channels=[24, 40, 112, 320])) # Modify in_channels

optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)

type='mmpretrain.TIMMBackbone' 表示在 MMDetection 中使用 MMPretrain 中的 TIMMBackbone 类,使用的模型是 EfficientNet-B1,其中 mmpretrain 表示 MMPretrain 存储库,TIMMBackbone 表示 MMPretrain 中实现的 TIMMBackbone 包装器。

有关层次结构注册表的原理,请参考 MMEngine 文档。 有关如何在 MMPretrain 中使用其他主干,您可以参考 MMPretrain 文档

使用 Mosaic 增强

如果您想在训练中使用 Mosaic,请确保同时使用 MultiImageMixDataset。 以“Faster R-CNN”算法为例,您应该修改配置中 train_pipelinetrain_dataset 的值,如下所示

# Open configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py directly and add the following fields
data_root = 'data/coco/'
dataset_type = 'CocoDataset'
img_scale=(1333, 800)

train_pipeline = [
    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),
    dict(
        type='RandomAffine',
        scaling_ratio_range=(0.1, 2),
        border=(-img_scale[0] // 2, -img_scale[1] // 2)), # The image will be enlarged by 4 times after Mosaic processing,so we use affine transformation to restore the image size.
    dict(type='RandomFlip', prob=0.5),
    dict(type='PackDetInputs')
]

train_dataset = dict(
    _delete_ = True, # remove unnecessary Settings
    type='MultiImageMixDataset',
    dataset=dict(
        type=dataset_type,
        ann_file=data_root + 'annotations/instances_train2017.json',
        img_prefix=data_root + 'train2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(type='LoadAnnotations', with_bbox=True)
        ],
        filter_empty_gt=False,
    ),
    pipeline=train_pipeline
    )

data = dict(
    train=train_dataset
    )

在配置中冻结主干网络后解冻主干网络

如果您在配置中冻结了主干网络,并且希望在某些 epochs 后解冻它,您可以编写一个 hook 函数来完成此操作。 以具有 resnet 主干的 Faster R-CNN 为例,您可以冻结主干网络的一个阶段,并在配置中添加一个 custom_hooks,如下所示

_base_ = [
    '../_base_/models/faster-rcnn_r50_fpn.py',
    '../_base_/datasets/coco_detection.py',
    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]
model = dict(
    # freeze one stage of the backbone network.
    backbone=dict(frozen_stages=1),
)
custom_hooks = [dict(type="UnfreezeBackboneEpochBasedHook", unfreeze_epoch=1)]

同时,在 mmdet/core/hook/unfreeze_backbone_epoch_based_hook.py 中编写 hook 类 UnfreezeBackboneEpochBasedHook

from mmengine.model import is_model_wrapper
from mmengine.hooks import Hook
from mmdet.registry import HOOKS


@HOOKS.register_module()
class UnfreezeBackboneEpochBasedHook(Hook):
    """Unfreeze backbone network Hook.

    Args:
        unfreeze_epoch (int): The epoch unfreezing the backbone network.
    """

    def __init__(self, unfreeze_epoch=1):
        self.unfreeze_epoch = unfreeze_epoch

    def before_train_epoch(self, runner):
        # Unfreeze the backbone network.
        # Only valid for resnet.
        if runner.epoch == self.unfreeze_epoch:
            model = runner.model
            if is_model_wrapper(model):
                model = model.module
            backbone = model.backbone
            if backbone.frozen_stages >= 0:
                if backbone.deep_stem:
                    backbone.stem.train()
                    for param in backbone.stem.parameters():
                        param.requires_grad = True
                else:
                    backbone.norm1.train()
                    for m in [backbone.conv1, backbone.norm1]:
                        for param in m.parameters():
                            param.requires_grad = True

            for i in range(1, backbone.frozen_stages + 1):
                m = getattr(backbone, f'layer{i}')
                m.train()
                for param in m.parameters():
                    param.requires_grad = True

获取新主干的通道

如果您想获取新主干的通道,可以单独构建该主干,并输入一个伪图像来获取每个阶段的输出。

ResNet 为例

from mmdet.models import ResNet
import torch
self = ResNet(depth=18)
self.eval()
inputs = torch.rand(1, 3, 32, 32)
level_outputs = self.forward(inputs)
for level_out in level_outputs:
    print(tuple(level_out.shape))

上面脚本的输出如下所示

(1, 64, 8, 8)
(1, 128, 4, 4)
(1, 256, 2, 2)
(1, 512, 1, 1)

用户可以通过将此脚本中的 ResNet(depth=18) 替换为他们自定义的主干来获取新主干的通道。

在 MMDetection 中使用 Detectron2 模型

用户可以使用 Detectron2Wrapper 在 MMDetection 中运行 Detectron2 的模型。 我们在 MMDetection 中提供了 Faster R-CNNMask R-CNNRetinaNet 的示例。

配置文件中的算法组件应与 Detectron2 中的相同。 在设置过程中,我们将首先初始化默认设置,这些设置可以在 Detectron2 中找到。 然后,配置文件中的设置将覆盖默认设置,并且模型将使用这些设置构建。 输入数据将首先转换为 Detectron2 的类型并馈送到 Detectron2 的模型中。 在推理过程中,从 Detectron2 的模型中计算出的结果将重新转换为 MMDetection 的类型。

使用 Detectron2 的预训练权重

Detectron2Wrapper 中的权重初始化将不会使用 MMDetection 的逻辑。 用户可以设置 model.d2_detector.weights=xxx 来加载预训练权重。 例如,我们可以使用 model.d2_detector.weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl' 来加载预训练的 ResNet-50,或者使用 model.d2_detector.weights='detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x/137260431/model_final_a54504.pkl' 来加载 Detectron2 中提出的预训练的 Mask R-CNN 权重。

注意: Detectron2 的预训练模型不能通过使用 load_from 直接加载,它应该首先通过 tools/model_converters/detectron2_to_mmdet.py 进行转换

对于已发布的 detectron2 检查点的推理,用户应首先使用 tools/model_converters/detectron2_to_mmdet.py 将 Detectron2 检查点转换为 MMDetection。

python tools/model_converters/detectron2_to_mmdet.py ${Detectron2 ckpt path} ${MMDetectron ckpt path}