Skip to main content

Customize Runtime Settings

我们已经支持使用 PyTorch 实现的所有优化器,唯一的修改是改变配置文件中的优化器字段。例如,如果您想使用Adam(请注意,性能可能会下降很多),可以做如下修改。

optimizer = dict(type='Adam', lr=0.0003, weight_decay=0.0001)

要修改模型的学习率,用户只需要在优化器的配置中修改 lr。用户可以按照 PyTorch 的 API 文档直接设置参数。

自定义自己实现的优化器

首先定义一个新的优化器,一个自定义的优化器可以定义如下。

假设你想添加一个名为 MyOptimizer 的优化器,它有参数 a、b 和 c。你需要创建一个名为 mmdet/core/optimizer 的新目录。然后在一个文件中实现新的优化器,例如,在 mmdet/core/optimizer/my_optimizer.py 中。

from .registry import OPTIMIZERS
from torch.optim import Optimizer


@OPTIMIZERS.register_module()
class MyOptimizer(Optimizer):

def __init__(self, a, b, c)

将优化器添加到 Registry:

要找到上面定义的模块,首先应该把这个模块导入到主命名空间。有两个选项可以实现。

  • 修改 mmdet/core/optimizer/__init__.py 来导入(需要重新编译源代码)

    新定义的模块应该在 mmdet/core/optimizer/__init__.py 中导入,这样 Registry 就会找到新的模块并将其加入。

    from .my_optimizer import MyOptimizer
  • 使用 custom_imports 来手动导入

    custom_imports = dict(imports=['mmdet.core.optimizer.my_optimizer'], allow_failed_imports=False)

mmdet.core.optimizer.my_optimizer 模块将在程序开始时被导入,然后 MyOptimizer 类会被自动注册。注意,只有包含 MyOptimizer 类的包应该被导入。mmdet.core.optimizer.my_optimizer.MyOptimizer 不能被直接导入。

实际上用户可以用这种导入方式使用完全不同的文件目录结构,只要模块根部能位于 PYTHONPATH 中。

最后,指定使用的优化器:

optimizer = dict(type='MyOptimizer', a=a_value, b=b_value, c=c_value)

构造自定义优化器的构造器

一些模型可能有一些特定的优化参数设置,例如 BatchNorm 层的权重衰减。用户可以通过定制优化器构造器来进行这些细粒度的参数调整。

from mmcv.utils import build_from_cfg

from mmcv.runner.optimizer import OPTIMIZER_BUILDERS, OPTIMIZERS
from mmdet.utils import get_root_logger
from .my_optimizer import MyOptimizer


@OPTIMIZER_BUILDERS.register_module()
class MyOptimizerConstructor(object):

def __init__(self, optimizer_cfg, paramwise_cfg=None):

def __call__(self, model):

return my_optimizer

这里实现了默认的优化器构造函数,它也可以作为新的优化器构造函数的模板。

可选的设置

没有被优化器实现的技巧应该通过优化器构造器(例如,设置参数化的学习率)或钩子来实现。我们列出了一些可以稳定训练或加速训练的常用设置。欢迎创建PR,为更多的设置提出问题。

  • 使用梯度裁剪来稳定训练。有些模型需要用梯度夹来夹住梯度以稳定训练过程。下面是一个例子。

    optimizer_config = dict(
    _delete_=True, grad_clip=dict(max_norm=35, norm_type=2))

    如果你的配置继承了已经设置了 optimizer_config 的基础配置,你可能需要 _delete_=True 来覆盖不必要的设置。更多细节请参见配置文档。

  • 使用 momentum schedule 来加速模型的收敛。我们支持 momentum schedule,根据学习速率修改模型的动量,这可以使模型以更快的方式收敛。动量调度器通常与 LR 调度器一起使用,例如,在 3D 检测中使用以下配置来加速收敛。更多细节,请参考 CyclicLrUpdater 和 CyclicMomentumUpdater 的实现。

    lr_config = dict(
    policy='cyclic',
    target_ratio=(10, 1e-4),
    cyclic_times=1,
    step_ratio_up=0.4,
    )
    momentum_config = dict(
    policy='cyclic',
    target_ratio=(0.85 / 0.95, 1),
    cyclic_times=1,
    step_ratio_up=0.4,
    )

自定义训练 schedules

默认情况下,我们使用 1x 计划的阶梯学习率,这在 MMCV 中调用 StepLRHook。我们在这里支持许多其他的学习率计划,如 CosineAnnealing 和 Poly 计划。下面是一些例子

  • poly schedule

    lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False)
  • ConsineAnnealing schedule

    lr_config = dict(
    policy='CosineAnnealing',
    warmup='linear',
    warmup_iters=1000,
    warmup_ratio=1.0 / 10,
    min_lr_ratio=1e-5)

自定义 workflow

workflow 是一个(阶段,历时)的列表,用于指定运行顺序和历时。默认情况下,它被设置为

workflow = [('train', 1)]

这意味着运行1个历时进行训练。有时,用户可能想在验证集上检查一些关于模型的指标(如损失、准确度)。在这种情况下,我们可以将工作流程设置为

[('train', 1), ('val', 1)]

这样,1个历时的训练和1个历时的验证将被反复运行。

caution
  1. 模型的参数在估值时代不会被更新。
  2. 配置中的关键词total_epochs只控制训练周期的数量,不会影响验证工作流程。
  3. [('train', 1), ('val', 1)][('train', 1)] 不会改变 EvalHook 的行为,因为 EvalHook 是由 after_train_epoch 调用的,验证工作流程只影响通过 after_val_epoch 调用的 hook。因此,[('train', 1), ('val', 1)][('train', 1)] 之间的唯一区别是,运行者将在每个训练epoch 之后计算验证集的损失。

自定义 hook

自定义自己实现的 hook

  1. 创建新的 hook 文件

    在某些情况下,用户可能需要实现一个新的钩子。从 v2.3.0 开始,MMDetection 支持训练中的自定义钩子(#3395)。因此,用户可以直接在 mmdet 或其基于 mmdet 的代码库中实现一个 hook,只需修改训练中的配置即可使用该钩子。在 v2.3.0 版之前,用户需要修改代码,以便在训练开始前注册钩子。这里我们给出一个在 mmdet 中创建一个新钩子并在训练中使用它的例子。

    mmdet/core/utils/my_hook.py 创建 MyHook 的类

    from mmcv.runner import HOOKS, Hook


    @HOOKS.register_module()
    class MyHook(Hook):

    def __init__(self, a, b):
    pass

    def before_run(self, runner):
    pass

    def after_run(self, runner):
    pass

    def before_epoch(self, runner):
    pass

    def after_epoch(self, runner):
    pass

    def before_iter(self, runner):
    pass

    def after_iter(self, runner):
    pass

    根据钩子的功能,用户需要在 before_run、after_run、before_epoch、after_epoch、before_iter 和 after_iter 中指定钩子在训练的每个阶段将做什么。

  2. 注册新的 hook

    修改 mmdet/core/optimizer/__init__.py 来导入(需要重新编译源代码)

    新定义的模块应该在 mmdet/core/optimizer/__init__.py 中导入,这样 Registry 就会找到新的模块并将其加入。

    from .my_hook import MyHook

    或者使用 custom_imports 来手动导入

    custom_imports = dict(imports=['mmdet.core.utils.my_hook'], allow_failed_imports=False)
  3. 修改你的 config

    custom_hooks = [
    dict(type='MyHook', a=a_value, b=b_value)
    ]

    你也可以添加 prioirity 字段修改 hook 的优先级,有 NORMALHIGHEST

    custom_hooks = [
    dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL')
    ]

    默认优先级为 NORMAL

使用 MMCV 实现的 Hook

如果钩子已经在 MMCV 中实现,你可以直接修改配置来使用这个钩子,如下所示

我们实现了一个名为 NumClassCheckHook 的自定义钩子,以检查 head 中的 num_classes 是否与数据集中的 CLASSES 长度一致。在 default_runtime.py 中:

custom_hooks = [dict(type='NumClassCheckHook')]

修改默认使用的 hook

有一些常见的钩子没有通过 custom_hooks 注册,它们是

  • log_config
  • checkpoint_config
  • evaluation
  • lr_config
  • optimizer_config
  • momentum_config

在这些钩子中,只有 logger 钩子的优先级是 VERY_LOW,其他的优先级是 NORMAL。上述教程已经涵盖了如何修改optimizer_config、momentum_config 和 lr_config。在这里,我们揭示了我们可以如何使用 log_config、checkpoint_config 和 evaluation。

Checkpoint config

MMCV运行器将使用 checkpoint_config 来初始化 CheckpointHook。

checkpoint_config = dict(interval=1)

用户可以设置 max_keep_ckpts 来只保存少量的检查点,或者决定是否通过 save_optimizer 存储优化器的状态 dict。参数的更多细节在这里

Log config

log_config 封装了多个记录器钩子,并能够设置间隔时间。现在 MMCV 支持 WandbLoggerHook, MlflowLoggerHook, 和 TensorboardLoggerHook。详细的使用方法可以在文档中找到。

log_config = dict(
interval=50,
hooks=[
dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')
])

Evaluation config

评价的配置将被用来初始化 EvalHook。除了 interval 不会被传递,其他参数如 metric 将被传递给 dataset.evaluation()

evaluation = dict(interval=1, metric='bbox')