|
1 | 1 | # 教程 6: 自定义运行设定 |
| 2 | + |
| 3 | +## 自定义优化设定 |
| 4 | + |
| 5 | +### 自定义 PyTorch 支持的优化器 |
| 6 | + |
| 7 | +我们已经支持 PyTorch 自带的所有优化器,唯一需要修改的地方是在配置文件里的 `optimizer` 域里面。 |
| 8 | +例如,如果您想使用 `ADAM` (注意如下操作可能会让模型表现下降),可以使用如下修改: |
| 9 | + |
| 10 | +```python |
| 11 | +optimizer = dict(type='Adam', lr=0.0003, weight_decay=0.0001) |
| 12 | +``` |
| 13 | + |
| 14 | +为了修改模型的学习率,使用者仅需要修改配置文件里 optimizer 的 `lr` 即可。 |
| 15 | +使用者可以参照 PyTorch 的 [API 文档](https://pytorch.org/docs/stable/optim.html?highlight=optim#module-torch.optim) |
| 16 | +直接设置参数。 |
| 17 | + |
| 18 | +### 自定义 自己实现的优化器 |
| 19 | + |
| 20 | +#### 1. 定义一个新的优化器 |
| 21 | + |
| 22 | +一个自定义的优化器可以按照如下去定义: |
| 23 | + |
| 24 | +假如您想增加一个叫做 `MyOptimizer` 的优化器,它的参数分别有 `a`, `b`, 和 `c`。 |
| 25 | +您需要创建一个叫 `mmseg/core/optimizer` 的新文件夹。 |
| 26 | +然后再在文件,即 `mmseg/core/optimizer/my_optimizer.py` 里面去实行这个新优化器: |
| 27 | + |
| 28 | +```python |
| 29 | +from .registry import OPTIMIZERS |
| 30 | +from torch.optim import Optimizer |
| 31 | + |
| 32 | + |
| 33 | +@OPTIMIZERS.register_module() |
| 34 | +class MyOptimizer(Optimizer): |
| 35 | + |
| 36 | + def __init__(self, a, b, c) |
| 37 | + |
| 38 | +``` |
| 39 | + |
| 40 | +#### 2. 增加优化器到注册表 (registry) |
| 41 | + |
| 42 | +为了让上述定义的模块被框架发现,首先这个模块应该被导入到主命名空间 (main namespace) 里。 |
| 43 | +有两种方式可以实现它。 |
| 44 | + |
| 45 | +- 修改 `mmseg/core/optimizer/__init__.py` 来导入它。 |
| 46 | + |
| 47 | + 新的被定义的模块应该被导入到 `mmseg/core/optimizer/__init__.py` 这样注册表将会发现新的模块并添加它。 |
| 48 | + |
| 49 | +```python |
| 50 | +from .my_optimizer import MyOptimizer |
| 51 | +``` |
| 52 | + |
| 53 | +- 在配置文件里使用 `custom_imports` 去手动添加它。 |
| 54 | + |
| 55 | +```python |
| 56 | +custom_imports = dict(imports=['mmseg.core.optimizer.my_optimizer'], allow_failed_imports=False) |
| 57 | +``` |
| 58 | + |
| 59 | +`mmseg.core.optimizer.my_optimizer` 模块将会在程序运行的开始被导入,并且 `MyOptimizer` 类将会自动注册。 |
| 60 | +需要注意只有包含 `MyOptimizer` 类的包 (package) 应当被导入。 |
| 61 | +而 `mmseg.core.optimizer.my_optimizer.MyOptimizer` **不能** 被直接导入。 |
| 62 | + |
| 63 | +事实上,使用者完全可以用另一个按这样导入方法的文件夹结构,只要模块的根路径已经被设置到 `PYTHONPATH` 里面。 |
| 64 | + |
| 65 | +#### 3. 在配置文件里定义优化器 |
| 66 | + |
| 67 | +之后您可以在配置文件的 `optimizer` 域里面使用 `MyOptimizer` |
| 68 | +在配置文件里,优化器被定义在 `optimizer` 域里,如下所示: |
| 69 | + |
| 70 | +```python |
| 71 | +optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) |
| 72 | +``` |
| 73 | + |
| 74 | +为了使用您自己的优化器,这个域可以被改成: |
| 75 | + |
| 76 | +```python |
| 77 | +optimizer = dict(type='MyOptimizer', a=a_value, b=b_value, c=c_value) |
| 78 | +``` |
| 79 | + |
| 80 | +### 自定义优化器的构造器 (constructor) |
| 81 | + |
| 82 | +有些模型可能需要在优化器里有一些特别参数的设置,例如 批归一化层 (BatchNorm layers) 的 权重衰减 (weight decay)。 |
| 83 | +使用者可以通过自定义优化器的构造器去微调这些细粒度参数。 |
| 84 | + |
| 85 | +```python |
| 86 | +from mmcv.utils import build_from_cfg |
| 87 | + |
| 88 | +from mmcv.runner.optimizer import OPTIMIZER_BUILDERS, OPTIMIZERS |
| 89 | +from mmseg.utils import get_root_logger |
| 90 | +from .my_optimizer import MyOptimizer |
| 91 | + |
| 92 | + |
| 93 | +@OPTIMIZER_BUILDERS.register_module() |
| 94 | +class MyOptimizerConstructor(object): |
| 95 | + |
| 96 | + def __init__(self, optimizer_cfg, paramwise_cfg=None): |
| 97 | + |
| 98 | + def __call__(self, model): |
| 99 | + |
| 100 | + return my_optimizer |
| 101 | + |
| 102 | +``` |
| 103 | + |
| 104 | +默认的优化器构造器的实施可以参照 [这里](https://github.com/open-mmlab/mmcv/blob/9ecd6b0d5ff9d2172c49a182eaa669e9f27bb8e7/mmcv/runner/optimizer/default_constructor.py#L11) ,它也可以被用作新的优化器构造器的模板。 |
| 105 | + |
| 106 | +### 额外的设置 |
| 107 | + |
| 108 | +优化器没有实施的一些技巧应该通过优化器构造器 (optimizer constructor) 或者钩 (hook) 去实施,如设置基于参数的学习率 (parameter-wise learning rates)。我们列出一些常见的设置,它们可以稳定或加速模型的训练。 |
| 109 | +如果您有更多的设置,欢迎在 PR 和 issue 里面提交。 |
| 110 | + |
| 111 | +- __使用梯度截断 (gradient clip) 去稳定训练__: |
| 112 | + 一些模型需要梯度截断去稳定训练过程,如下所示: |
| 113 | + |
| 114 | + ```python |
| 115 | + optimizer_config = dict( |
| 116 | + _delete_=True, grad_clip=dict(max_norm=35, norm_type=2)) |
| 117 | + ``` |
| 118 | + |
| 119 | + 如果您的配置继承自已经设置了 `optimizer_config` 的基础配置 (base config),您可能需要 `_delete_=True` 来重写那些不需要的设置。更多细节请参照 [配置文件文档](https://mmsegmentation.readthedocs.io/en/latest/config.html) 。 |
| 120 | + |
| 121 | +- __使用动量计划表 (momentum schedule) 去加速模型收敛__: |
| 122 | + 我们支持动量计划表去让模型基于学习率修改动量,这样可能让模型收敛地更快。 |
| 123 | + 动量计划表经常和学习率计划表 (LR scheduler) 一起使用,例如如下配置文件就在 3D 检测里经常使用以加速收敛。 |
| 124 | + 更多细节请参考 [CyclicLrUpdater](https://github.com/open-mmlab/mmcv/blob/f48241a65aebfe07db122e9db320c31b685dc674/mmcv/runner/hooks/lr_updater.py#L327) 和 [CyclicMomentumUpdater](https://github.com/open-mmlab/mmcv/blob/f48241a65aebfe07db122e9db320c31b685dc674/mmcv/runner/hooks/momentum_updater.py#L130) 的实施。 |
| 125 | + |
| 126 | + ```python |
| 127 | + lr_config = dict( |
| 128 | + policy='cyclic', |
| 129 | + target_ratio=(10, 1e-4), |
| 130 | + cyclic_times=1, |
| 131 | + step_ratio_up=0.4, |
| 132 | + ) |
| 133 | + momentum_config = dict( |
| 134 | + policy='cyclic', |
| 135 | + target_ratio=(0.85 / 0.95, 1), |
| 136 | + cyclic_times=1, |
| 137 | + step_ratio_up=0.4, |
| 138 | + ) |
| 139 | + ``` |
| 140 | + |
| 141 | +## 自定义训练计划表 |
| 142 | + |
| 143 | +我们根据默认的训练迭代步数 40k/80k 来设置学习率,这在 MMCV 里叫做 [`PolyLrUpdaterHook`](https://github.com/open-mmlab/mmcv/blob/826d3a7b68596c824fa1e2cb89b6ac274f52179c/mmcv/runner/hooks/lr_updater.py#L196) 。 |
| 144 | +我们也支持许多其他的学习率计划表:[这里](https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/lr_updater.py) ,例如 `CosineAnnealing` 和 `Poly` 计划表。下面是一些例子: |
| 145 | + |
| 146 | +- 步计划表 Step schedule: |
| 147 | + |
| 148 | + ```python |
| 149 | + lr_config = dict(policy='step', step=[9, 10]) |
| 150 | + ``` |
| 151 | + |
| 152 | +- 余弦退火计划表 ConsineAnnealing schedule: |
| 153 | + |
| 154 | + ```python |
| 155 | + lr_config = dict( |
| 156 | + policy='CosineAnnealing', |
| 157 | + warmup='linear', |
| 158 | + warmup_iters=1000, |
| 159 | + warmup_ratio=1.0 / 10, |
| 160 | + min_lr_ratio=1e-5) |
| 161 | + ``` |
| 162 | + |
| 163 | +## 自定义工作流 (workflow) |
| 164 | + |
| 165 | +工作流是一个专门定义运行阶数和轮数 (running order and epochs) 的阶段或轮数 (phase, epochs) 的列表。 |
| 166 | +默认情况下它设置成: |
| 167 | + |
| 168 | +```python |
| 169 | +workflow = [('train', 1)] |
| 170 | +``` |
| 171 | + |
| 172 | +意思是训练是跑 1 个 epoch。有时候使用者可能想检查模型在验证集上的一些指标(如 损失 loss,精确性 accuracy),我们可以这样设置工作流: |
| 173 | + |
| 174 | +```python |
| 175 | +[('train', 1), ('val', 1)] |
| 176 | +``` |
| 177 | + |
| 178 | +于是 1 个 epoch 训练,1 个 epoch 验证将交替运行。 |
| 179 | + |
| 180 | +**注意**: |
| 181 | + |
| 182 | +1. 模型的参数在验证的阶段不会被自动更新。 |
| 183 | +2. 配置文件里的关键词 `total_epochs` 仅控制训练的 epochs 数目,而不会影响验证时的工作流。 |
| 184 | +3. 工作流 `[('train', 1), ('val', 1)]` 和 `[('train', 1)]` 将不会改变 `EvalHook` 的行为,因为 `EvalHook` 被 `after_train_epoch` |
| 185 | + 召唤而且验证的工作流仅仅影响通过 `after_val_epoch` 召唤的钩 (hooks)。因此, `[('train', 1), ('val', 1)]` 和 `[('train', 1)]` |
| 186 | + 的区别仅在于 runner 将在每次训练 epoch 结束后计算在验证集上的损失。 |
| 187 | + |
| 188 | +## 自定义钩 (hooks) |
| 189 | + |
| 190 | +### 使用 MMCV 实施的 钩 (hooks) |
| 191 | + |
| 192 | +如果钩已经在 MMCV 里被实施,如下所示,您可以直接修改配置文件来使用钩: |
| 193 | + |
| 194 | +```python |
| 195 | +custom_hooks = [ |
| 196 | + dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL') |
| 197 | +] |
| 198 | +``` |
| 199 | + |
| 200 | +### 修改默认的运行时间钩 (runtime hooks) |
| 201 | + |
| 202 | +以下的常用的钩没有被 `custom_hooks` 注册: |
| 203 | + |
| 204 | +- log_config |
| 205 | +- checkpoint_config |
| 206 | +- evaluation |
| 207 | +- lr_config |
| 208 | +- optimizer_config |
| 209 | +- momentum_config |
| 210 | + |
| 211 | +在这些钩里,只有 logger hook 有 `VERY_LOW` 优先级,其他的优先级都是 `NORMAL`。 |
| 212 | +上述提及的教程已经包括了如何修改 `optimizer_config`,`momentum_config` 和 `lr_config`。 |
| 213 | +这里我们展示我们如何处理 `log_config`, `checkpoint_config` 和 `evaluation`。 |
| 214 | + |
| 215 | +#### 检查点配置文件 (Checkpoint config) |
| 216 | + |
| 217 | +MMCV runner 将使用 `checkpoint_config` 去初始化 [`CheckpointHook`](https://github.com/open-mmlab/mmcv/blob/9ecd6b0d5ff9d2172c49a182eaa669e9f27bb8e7/mmcv/runner/hooks/checkpoint.py#L9). |
| 218 | + |
| 219 | +```python |
| 220 | +checkpoint_config = dict(interval=1) |
| 221 | +``` |
| 222 | + |
| 223 | +使用者可以设置 `max_keep_ckpts` 来仅保存一小部分检查点或者通过 `save_optimizer` 来决定是否保存优化器的状态字典 (state dict of optimizer)。 更多使用参数的细节请参考 [这里](https://mmcv.readthedocs.io/en/latest/api.html#mmcv.runner.CheckpointHook) 。 |
| 224 | + |
| 225 | +#### 日志配置文件 (Log config) |
| 226 | + |
| 227 | +`log_config` 包裹了许多日志钩 (logger hooks) 而且能去设置间隔 (intervals)。现在 MMCV 支持 `WandbLoggerHook`, `MlflowLoggerHook` 和 `TensorboardLoggerHook`。 |
| 228 | +详细的使用请参照 [文档](https://mmcv.readthedocs.io/en/latest/api.html#mmcv.runner.LoggerHook) 。 |
| 229 | + |
| 230 | +```python |
| 231 | +log_config = dict( |
| 232 | + interval=50, |
| 233 | + hooks=[ |
| 234 | + dict(type='TextLoggerHook'), |
| 235 | + dict(type='TensorboardLoggerHook') |
| 236 | + ]) |
| 237 | +``` |
| 238 | + |
| 239 | +#### 评估配置文件 (Evaluation config) |
| 240 | + |
| 241 | +`evaluation` 的配置文件将被用来初始化 [`EvalHook`](https://github.com/open-mmlab/mmsegmentation/blob/e3f6f655d69b777341aec2fe8829871cc0beadcb/mmseg/core/evaluation/eval_hooks.py#L7) 。 |
| 242 | +除了 `interval` 键,其他的像 `metric` 这样的参数将被传递给 `dataset.evaluate()` 。 |
| 243 | + |
| 244 | +```python |
| 245 | +evaluation = dict(interval=1, metric='mIoU') |
| 246 | +``` |
0 commit comments