原文链接:Tutorial 5: Customize Runtime Settings — mmflow documentation
教程 5:自定义运行时设置
在本教程中,我们将介绍一些使用本项目使用过程中需要运行自定义设置时相关的方法,包括如何自定义优化器、训练策略、工作流和串联方法。
自定义优化方法
自定义Pytorch支持的优化器
我们目前支持使用所有Pytorch已经实现的优化器,唯一要做的修改就是改变配置文件中的optimizer字段。例如,如果你想使用Adam优化器,对应的改动如下:
optimizer = dict(type='Adam', lr=0.0003, weight_decay=0.0001)
要想修改模型的学习率,使用者仅需修改优化器配置中的lr字段。你也可以按Pytorch的API文档直接设定参数。
例如,如果你想在Pytorch中通过使用这样的设置torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)来使用Adam,对应的修改如下:
optimizer = dict(type='Adam', lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
自定义个人实现的优化器
1.定义一个新优化器
一个自定义的优化器可以被定义位下面这样:
假定你想添加一个名为MyOptimizer的优化器,它有参数a, b,c.你需要创建一个新的名为mmflow/core/optimizer的目录,然后在一个文件中(例如mmflow/core/optimizer/my_optimizer.py)实现这个新的优化器。
from .builder import OPTIMIZERS
from torch.optim import Optimizer
@OPTIMIZERS.register_module()
class MyOptimizer(Optimizer):
def __init__(self, a, b, c):
2.将这个优化器添加到注册表
为了找到之前定义的上述模块,首先需要将该模块导入到主命名空间中。有两种方式可以实现:
(1)通过修改mmflow/core/optimizer/__init__.py来导入
新定义的模块应该在mmflow/core/optimizer/__init__.py导入,这样注册表就能找到并添加新的模块
(2)在配置文件中使用custom_imports手动导入
custom_imports可以手动导入模块,只要这个模块在PYTHONPATH环境变量中就行,这样的话不需要修改源代码
custom_imports = dict(imports=['mmflow.core.optimizer.my_optimizer'], allow_failed_imports=False)
模块mmflow.core.optimizer.my_optimizer将在程序开始的时候被导入,MyOptimizer类也将自动注册。注意,只有MyOptimizer类中涵盖的包应该被导入。mmflow.core.optimizer.my_optimizer.MyOptimizer不能直接导入。
3.在配置文件中指定优化器
你可以在配置文件的optimizer字段中使用MyOptimizer。配置文件中,优化器一般定义为如下所示:
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
为了使用自己的优化器,对应的字段可以变为:
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 mmflow.utils import get_root_logger
from .my_optimizer import MyOptimizer
@OPTIMIZER_BUILDERS.register_module()
class MyOptimizerConstructor:
def __init__(self, optimizer_cfg, paramwise_cfg=None):
pass
def __call__(self, model):
return my_optimizer
默认的优化器构造函数在这里实现,当然也可以作为新优化器构造函数的模板。
额外的设置
没有被优化器实现的一些技巧应该在优化器的构造函数中实现(例如,设定参数智能parameter-wise的学习率)或连接。我们列出了一些公共的设置,可以用于稳定或加速训练过程。Feel free to create PR, issue for more settings.(这句,纠结了好久也没想出来怎么翻译=-=)
(1)利用梯度下降来稳定训练:一些模型需要通过梯度裁剪来稳定训练过程。下面是一个例子:
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
(2)使用动量策略来加速模型收敛:我们支持栋梁策略来根据学习率修改模型的动量,可以使得模型以更快的方式收敛。动量策略通常和学习率策略一起使用,例如,下面的配置使用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,
)
自定义训练策略
在配置文件中,我们默认使用阶梯学习率,它调用MMCV中的StepLRHook。我们在这里支持许多其他的学习率策略,像是CosineAnnealing
和 Poly策略。举个例子:
*Poly策略
lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False)
*ConsineAnnealing
策略
lr_config = dict(
policy='CosineAnnealing',
warmup='linear',
warmup_iters=1000,
warmup_ratio=1.0 / 10,
min_lr_ratio=1e-5)
自定义工作流程
工作流程是由(阶段Phase,周期Epochs)组成的列表,用于指定执行顺序和训练轮数。默认为:
workflow = [('train', 1)]
即训练1轮。有时,使用者可能像检查一些关于模型在验证集上的指标(例如:损失函数、精度)。这种情况下,我们把工作流设为
[('train', 1), ('val', 1)]
这样就是训练1轮,然后验证1轮,依次迭代运行。
注意:
1.模型参数在验证轮不会被更新;
2.配置文件中的total_epochs关键字仅仅控制训练轮数,不会影响验证流程;
3.工作流[('train', 1), ('val', 1)]
和 [('train', 1)]不会改变EpochEvalHook的行为,因为EpochEvalHook被
after_train_epoch调用,并且验证流程仅仅影响通过after_val_epoch调用的连接。因此,[('train', 1), ('val', 1)]
和 [('train', 1)]的唯一区别在于运行器会在每一轮训练后在验证集上计算损失函数。
自定义连接
自定义自己实现的连接
1.实现一个新的连接
这里我们给了一个在mmflow中创建的新连接,并在训练中使用它。
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
, and after_iter函数中实现
2.注册新连接
接着我们需要导入MyHook。假定文件在mmflow/core/hooks/my_hook.py中,有两种方式可以实现:
(1)修改mmflow/core/hooks/__init__.py来导入
新定义的模块应该被导入mmflow/core/hooks/__init__.py,所以注册表将找到并添加新的模块。
from .my_hook import MyHook
(2)在配置文件中使用custrom_imports手动导入
custom_imports = dict(imports=['mmflow.core.hooks.my_hook'], allow_failed_imports=False)
3.修改配置
custom_hooks = [dict(type='MyHook', a=a_value, b=b_value)]
你也可以设置连接的优先级,具体可通过添加如下所示的关键字priority为”NORMAL"或“HIGHEST”
custom_hooks = [dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL')]
在注册时默认的优先级为NORMAL.
使用MMCV中实现的连接
如果MMCV中实现了该连接,你可以直接通过修改配置来使用它:
mmcv_hooks = [dict(type='MMCVHook', a=a_value, b=b_value, priority='NORMAL')]
修改默认运行时连接
有些通用连接没有被custom_hooks注册,但是当导入MMCV时已经注册了,它们是:
-
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_skpts来保存较小的检查点或者通过save_optimizer决定是否存储优化器的状态字典。更多细节和参数参考这里。
Log config
Log_config封装了多个日志连接,可以设置间隔。MMCV目前支持WandLiggerHook,MlflowLoggerHook和TensorboardLoggerHook。使用细节详见文档。
log_config = dict(
interval=50,
hooks=[
dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')
])
Evaluation config
evaluation的配置将被用于初始化EvalHook.除了关键字interval,其他参数像metric将通过online_evaluation()传递。
evaluation = dict(interval=50000, metric='EPE')