[Docs] Refine docs/zh_cn/tutorials (#694)
parent
0f62a6c091
commit
a530c0d671
|
@ -74,8 +74,7 @@ Config (path: learn_read_config.py): {'test_int': 1, 'test_list': [1, 2, 3], 'te
|
|||
|
||||
## 配置文件的使用
|
||||
|
||||
通过读取配置文件来初始化配置对象后,就可以像使用普通字典或者 Python 类一样来使用这个变量了。
|
||||
我们提供了两种访问接口,即类似字典的接口 `cfg['key']` 或者类似 Python 对象属性的接口 `cfg.key`。这两种接口都支持读写。
|
||||
通过读取配置文件来初始化配置对象后,就可以像使用普通字典或者 Python 类一样来使用这个变量了。我们提供了两种访问接口,即类似字典的接口 `cfg['key']` 或者类似 Python 对象属性的接口 `cfg.key`。这两种接口都支持读写。
|
||||
|
||||
```python
|
||||
print(cfg.test_int)
|
||||
|
@ -312,9 +311,7 @@ print(cfg.a)
|
|||
|
||||
## 配置文件的导出
|
||||
|
||||
在启动训练脚本时,用户可能通过传参的方式来修改配置文件的部分字段,为此我们提供了 `dump`
|
||||
接口来导出更改后的配置文件。与读取配置文件类似,用户可以通过 `cfg.dump('config.xxx')` 来选择导出文件的格式。`dump`
|
||||
同样可以导出有继承关系的配置文件,导出的文件可以被独立使用,不再依赖于 `_base_` 中定义的文件。
|
||||
在启动训练脚本时,用户可能通过传参的方式来修改配置文件的部分字段,为此我们提供了 `dump` 接口来导出更改后的配置文件。与读取配置文件类似,用户可以通过 `cfg.dump('config.xxx')` 来选择导出文件的格式。`dump` 同样可以导出有继承关系的配置文件,导出的文件可以被独立使用,不再依赖于 `_base_` 中定义的文件。
|
||||
|
||||
基于继承一节定义的 `resnet50.py`,我们将其加载后导出:
|
||||
|
||||
|
@ -526,8 +523,7 @@ print(custom_optim)
|
|||
|
||||
### 跨项目继承配置文件
|
||||
|
||||
为了避免基于已有算法库开发新项目时需要复制大量的配置文件,MMEngine 的配置类支持配置文件的跨项目继承。例如我们基于 MMDetection
|
||||
开发新的算法库,需要使用以下 MMDetection 的配置文件:
|
||||
为了避免基于已有算法库开发新项目时需要复制大量的配置文件,MMEngine 的配置类支持配置文件的跨项目继承。例如我们基于 MMDetection 开发新的算法库,需要使用以下 MMDetection 的配置文件:
|
||||
|
||||
```text
|
||||
configs/_base_/schedules/schedule_1x.py
|
||||
|
@ -536,8 +532,7 @@ configs/_base_/default_runtime.py
|
|||
configs/_base_/models/faster-rcnn_r50_fpn.py
|
||||
```
|
||||
|
||||
如果没有配置文件跨项目继承的功能,我们就需要把 MMDetection 的配置文件拷贝到当前项目,而我们现在只需要安装 MMDetection
|
||||
(如使用 `mim install mmdet`),在新项目的配置文件中按照以下方式继承 MMDetection 的配置文件:
|
||||
如果没有配置文件跨项目继承的功能,我们就需要把 MMDetection 的配置文件拷贝到当前项目,而我们现在只需要安装 MMDetection(如使用 `mim install mmdet`),在新项目的配置文件中按照以下方式继承 MMDetection 的配置文件:
|
||||
|
||||
`cross_repo.py`
|
||||
|
||||
|
@ -561,15 +556,13 @@ print(cfg.train_cfg)
|
|||
{'type': 'EpochBasedTrainLoop', 'max_epochs': 12, 'val_interval': 1, '_scope_': 'mmdet'}
|
||||
```
|
||||
|
||||
通过指定 `mmdet::` ,Config 类会去检索 mmdet 包中的配置文件目录,并继承指定的配置文件。
|
||||
实际上,只要算法库的 `setup.py` 文件符合 [MMEngine 安装规范](todo),在正确安装算法库以后,新的项目就可以使用上述用法去继承已有算法库的配置文件而无需拷贝。
|
||||
通过指定 `mmdet::`,Config 类会去检索 mmdet 包中的配置文件目录,并继承指定的配置文件。实际上,只要算法库的 `setup.py` 文件符合 [MMEngine 安装规范](todo),在正确安装算法库以后,新的项目就可以使用上述用法去继承已有算法库的配置文件而无需拷贝。
|
||||
|
||||
### 跨项目获取配置文件
|
||||
|
||||
MMEngine 还提供了 `get_config` 和 `get_model` 两个接口,支持对符合 [MMEngine 安装规范](todo) 的算法库中的模型和配置文件做索引并进行 API 调用。通过 `get_model` 接口可以获得构建好的模型。通过 `get_config` 接口可以获得配置文件。
|
||||
|
||||
`get_model` 的使用样例如下所示,使用和跨项目继承配置文件相同的语法,指定 `mmdet::`,即可在 mmdet 包中检索对应的配置文件并构建和初始化相应模型。
|
||||
用户可以通过指定 `pretrained=True` 获得已经加载预训练权重的模型以进行训练或者推理。
|
||||
`get_model` 的使用样例如下所示,使用和跨项目继承配置文件相同的语法,指定 `mmdet::`,即可在 mmdet 包中检索对应的配置文件并构建和初始化相应模型。用户可以通过指定 `pretrained=True` 获得已经加载预训练权重的模型以进行训练或者推理。
|
||||
|
||||
```python
|
||||
from mmengine.hub import get_model
|
||||
|
@ -584,9 +577,7 @@ http loads checkpoint from path: https://download.openmmlab.com/mmdetection/v2.0
|
|||
<class 'mmdet.models.detectors.faster_rcnn.FasterRCNN'>
|
||||
```
|
||||
|
||||
`get_config` 的使用样例如下所示,使用和跨项目继承配置文件相同的语法,指定 `mmdet::`,即可实现去 mmdet 包中检索并加载对应的配置文件。
|
||||
用户可以基于这样得到的配置文件进行推理修改并自定义自己的算法模型。
|
||||
同时,如果用户指定 `pretrained=True` ,得到的配置文件中会新增 `model_path` 字段,指定了对应模型预训练权重的路径。
|
||||
`get_config` 的使用样例如下所示,使用和跨项目继承配置文件相同的语法,指定 `mmdet::`,即可实现去 mmdet 包中检索并加载对应的配置文件。用户可以基于这样得到的配置文件进行推理修改并自定义自己的算法模型。同时,如果用户指定 `pretrained=True`,得到的配置文件中会新增 `model_path` 字段,指定了对应模型预训练权重的路径。
|
||||
|
||||
```python
|
||||
from mmengine.hub import get_config
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
## 使用数据变换类
|
||||
|
||||
在 MMEngine 中,我们使用各种可调用的数据变换类来进行数据的操作。这些数据变换类可以接受若干配置参数进行实例化,之后通过调用的方式对输入的数据字典进行处理。
|
||||
同时,我们约定所有数据变换都接受一个字典作为输入,并将处理后的数据输出为一个字典。一个简单的例子如下:
|
||||
在 MMEngine 中,我们使用各种可调用的数据变换类来进行数据的操作。这些数据变换类可以接受若干配置参数进行实例化,之后通过调用的方式对输入的数据字典进行处理。同时,我们约定所有数据变换都接受一个字典作为输入,并将处理后的数据输出为一个字典。一个简单的例子如下:
|
||||
|
||||
```{note}
|
||||
MMEngine 中仅约定了数据变换类的规范,常用的数据变换类实现及基类都在 MMCV 中,因此在本篇教程需要提前安装好 MMCV,参见 MMCV 的[安装教程](https://mmcv.readthedocs.io/en/2.x/get_started/installation.html)。
|
||||
|
@ -24,14 +23,12 @@ MMEngine 中仅约定了数据变换类的规范,常用的数据变换类实
|
|||
|
||||
## 在配置文件中使用
|
||||
|
||||
在配置文件中,我们将一系列数据变换组合成为一个列表,称为数据流水线(Data Pipeline),传给数据集的 `pipeline`
|
||||
参数。通常数据流水线由以下几个部分组成:
|
||||
在配置文件中,我们将一系列数据变换组合成为一个列表,称为数据流水线(Data Pipeline),传给数据集的 `pipeline` 参数。通常数据流水线由以下几个部分组成:
|
||||
|
||||
1. 数据加载,通常使用 [`LoadImageFromFile`](mmcv.transforms.LoadImageFromFile)
|
||||
2. 标签加载,通常使用 [`LoadAnnotations`](mmcv.transforms.LoadAnnotations)
|
||||
3. 数据处理及增强,例如 [`RandomResize`](mmcv.transforms.RandomResize)
|
||||
4. 数据格式化,根据任务不同,在各个仓库使用自己的变换操作,通常名为 `PackXXXInputs`,其中 XXX 是任务的名称,如
|
||||
分类任务中的 `PackClsInputs`。
|
||||
4. 数据格式化,根据任务不同,在各个仓库使用自己的变换操作,通常名为 `PackXXXInputs`,其中 XXX 是任务的名称,如分类任务中的 `PackClsInputs`。
|
||||
|
||||
以分类任务为例,我们在下图展示了一个典型的数据流水线。对每个样本,数据集中保存的基本信息是一个如图中最左侧所示的字典,之后每经过一个由蓝色块代表的数据变换操作,数据字典中都会加入新的字段(标记为绿色)或更新现有的字段(标记为橙色)。
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
在模型验证和模型测试中,通常需要对模型精度做定量评测。在 MMEngine 中实现了评测指标(Metric)和评测器(Evaluator)模块来完成这一功能:
|
||||
|
||||
- 评测指标: 用于根据测试数据和模型预测结果,完成模型特定精度指标的计算。在 OpenMMLab 各算法库中提供了对应任务的常用评测指标,如 [MMClassification](https://github.com/open-mmlab/mmclassification) 中提供了[分类正确率指标(Accuracy)](https://mmclassification.readthedocs.io/zh_CN/dev-1.x/generated/mmcls.evaluation.Accuracy.html) 用于计算分类模型的 Top-k 分类正确率。
|
||||
- 评测指标:用于根据测试数据和模型预测结果,完成模型特定精度指标的计算。在 OpenMMLab 各算法库中提供了对应任务的常用评测指标,如 [MMClassification](https://github.com/open-mmlab/mmclassification) 中提供了[分类正确率指标(Accuracy)](https://mmclassification.readthedocs.io/zh_CN/dev-1.x/generated/mmcls.evaluation.Accuracy.html)用于计算分类模型的 Top-k 分类正确率。
|
||||
|
||||
- 评测器: 是评测指标的上层模块,用于在数据输入评测指标前完成必要的格式转换,并提供分布式支持。在模型训练和测试中,评测器由[执行器(Runner)](runner.md)自动构建。用户亦可根据需求手动创建评测器,进行离线评测。
|
||||
- 评测器:是评测指标的上层模块,用于在数据输入评测指标前完成必要的格式转换,并提供分布式支持。在模型训练和测试中,评测器由[执行器(Runner)](runner.md)自动构建。用户亦可根据需求手动创建评测器,进行离线评测。
|
||||
|
||||
## 在模型训练或测试中进行评测
|
||||
|
||||
|
|
|
@ -75,54 +75,54 @@ runner.train()
|
|||
- 保存最优权重
|
||||
- 指定保存权重的路径
|
||||
|
||||
如需了解其他功能,请阅读[CheckpointHook API 文档](mmengine.hooks.CheckpointHook)。
|
||||
如需了解其他功能,请阅读 [CheckpointHook API 文档](mmengine.hooks.CheckpointHook)。
|
||||
|
||||
下面介绍上面提到的 4 个功能。
|
||||
|
||||
- 按照间隔保存权重,支持按 epoch 数或者 iteration 数保存权重
|
||||
|
||||
假设我们一共训练 20 个 epoch 并希望每隔 5 个 epoch 保存一次权重,下面的配置即可帮我们实现该需求。
|
||||
假设我们一共训练 20 个 epoch 并希望每隔 5 个 epoch 保存一次权重,下面的配置即可帮我们实现该需求。
|
||||
|
||||
```python
|
||||
# by_epoch 的默认值为 True
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, by_epoch=True))
|
||||
```
|
||||
```python
|
||||
# by_epoch 的默认值为 True
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, by_epoch=True))
|
||||
```
|
||||
|
||||
如果想以迭代次数作为保存间隔,则可以将 `by_epoch` 设为 False,`interval=5` 则表示每迭代 5 次保存一次权重。
|
||||
如果想以迭代次数作为保存间隔,则可以将 `by_epoch` 设为 False,`interval=5` 则表示每迭代 5 次保存一次权重。
|
||||
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, by_epoch=False))
|
||||
```
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, by_epoch=False))
|
||||
```
|
||||
|
||||
- 保存最新的多个权重
|
||||
|
||||
如果只想保存一定数量的权重,可以通过设置 `max_keep_ckpts` 参数实现最多保存 `max_keep_ckpts` 个权重,当保存的权重数超过 `max_keep_ckpts` 时,前面的权重会被删除。
|
||||
如果只想保存一定数量的权重,可以通过设置 `max_keep_ckpts` 参数实现最多保存 `max_keep_ckpts` 个权重,当保存的权重数超过 `max_keep_ckpts` 时,前面的权重会被删除。
|
||||
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, max_keep_ckpts=2))
|
||||
```
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, max_keep_ckpts=2))
|
||||
```
|
||||
|
||||
上述例子表示,假如一共训练 20 个 epoch,那么会在第 5, 10, 15, 20 个 epoch 保存模型,但是在第 15 个 epoch 的时候会删除第 5 个 epoch 保存的权重,在第 20 个 epoch 的时候会删除第 10 个 epoch 的权重,最终只有第 15 和第 20 个 epoch 的权重才会被保存。
|
||||
上述例子表示,假如一共训练 20 个 epoch,那么会在第 5, 10, 15, 20 个 epoch 保存模型,但是在第 15 个 epoch 的时候会删除第 5 个 epoch 保存的权重,在第 20 个 epoch 的时候会删除第 10 个 epoch 的权重,最终只有第 15 和第 20 个 epoch 的权重才会被保存。
|
||||
|
||||
- 保存最优权重
|
||||
|
||||
如果想要保存训练过程验证集的最优权重,可以设置 `save_best` 参数,如果设置为 `'auto'`,则会根据验证集的第一个评价指标(验证集返回的评价指标是一个有序字典)判断当前权重是否最优。
|
||||
如果想要保存训练过程验证集的最优权重,可以设置 `save_best` 参数,如果设置为 `'auto'`,则会根据验证集的第一个评价指标(验证集返回的评价指标是一个有序字典)判断当前权重是否最优。
|
||||
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', save_best='auto'))
|
||||
```
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', save_best='auto'))
|
||||
```
|
||||
|
||||
也可以直接指定 `save_best` 的值为评价指标,例如在分类任务中,可以指定为 `save_best='top-1'`,则会根据 `'top-1'` 的值判断当前权重是否最优。
|
||||
也可以直接指定 `save_best` 的值为评价指标,例如在分类任务中,可以指定为 `save_best='top-1'`,则会根据 `'top-1'` 的值判断当前权重是否最优。
|
||||
|
||||
除了 `save_best` 参数,和保存最优权重相关的参数还有 `rule`,`greater_keys` 和 `less_keys`,这三者用来判断 `save_best` 的值是越大越好还是越小越好。例如指定了 `save_best='top-1'`,可以指定 `rule='greater'`,则表示该值越大表示权重越好。
|
||||
除了 `save_best` 参数,和保存最优权重相关的参数还有 `rule`,`greater_keys` 和 `less_keys`,这三者用来判断 `save_best` 的值是越大越好还是越小越好。例如指定了 `save_best='top-1'`,可以指定 `rule='greater'`,则表示该值越大表示权重越好。
|
||||
|
||||
- 指定保存权重的路径
|
||||
|
||||
权重默认保存在工作目录(work_dir),但可以通过设置 `out_dir` 改变保存路径。
|
||||
权重默认保存在工作目录(work_dir),但可以通过设置 `out_dir` 改变保存路径。
|
||||
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, out_dir='/path/of/directory'))
|
||||
```
|
||||
```python
|
||||
default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=5, out_dir='/path/of/directory'))
|
||||
```
|
||||
|
||||
### LoggerHook
|
||||
|
||||
|
@ -168,12 +168,11 @@ runner.train()
|
|||
custom_hooks = [dict(type='EMAHook', ema_type='StochasticWeightAverage')]
|
||||
```
|
||||
|
||||
更多用法请阅读[EMAHook API 文档](mmengine.hooks.EMAHook)。
|
||||
更多用法请阅读 [EMAHook API 文档](mmengine.hooks.EMAHook)。
|
||||
|
||||
### EmptyCacheHook
|
||||
|
||||
[EmptyCacheHook](mmengine.hooks.EmptyCacheHook) 调用 `torch.cuda.empty_cache()` 释放未被使用的显存。
|
||||
可以通过设置 `before_epoch`, `after_iter` 以及 `after_epoch` 参数控制释显存的时机,第一个参数表示在每个 epoch 开始之前,第二参数表示在每次迭代之后,第三个参数表示在每个 epoch 之后。
|
||||
[EmptyCacheHook](mmengine.hooks.EmptyCacheHook) 调用 `torch.cuda.empty_cache()` 释放未被使用的显存。可以通过设置 `before_epoch`, `after_iter` 以及 `after_epoch` 参数控制释显存的时机,第一个参数表示在每个 epoch 开始之前,第二参数表示在每次迭代之后,第三个参数表示在每个 epoch 之后。
|
||||
|
||||
```python
|
||||
# 每一个 epoch 结束都会执行释放操作
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
|
||||
对于检测、识别、分割一类的深度学习任务,上述方法通常为标准的流程,例如在 `train_step` 里更新参数,返回损失;`val_step` 和 `test_step` 返回预测结果。因此 MMEngine 抽象出模型基类 [BaseModel](mmengine.model.BaseModel),实现了上述接口的标准流程。我们只需要让模型继承自模型基类,并按照一定的规范实现 `forward`,就能让模型在执行器中运行起来。
|
||||
|
||||
模型基类继承自[模块基类](./initialize.md),能够通过配置 `init_cfg` 灵活的选择初始化方式。
|
||||
模型基类继承自[模块基类](../advanced_tutorials/initialize.md),能够通过配置 `init_cfg` 灵活的选择初始化方式。
|
||||
|
||||
## 接口约定
|
||||
|
||||
[forward](mmengine.model.BaseModel.forward): `forward` 的入参需要和 [DataLoader](https://pytorch.org/tutorials/beginner/basics/data_tutorial.html) 的输出保持一致 (自定义[数据预处理器](数据预处理器(DataPreprocessor))除外),如果 `DataLoader` 返回元组类型的数据 `data`,`forward` 需要能够接受 `*data` 的解包后的参数;如果返回字典类型的数据 `data`,`forward` 需要能够接受 `**data` 解包后的参数。 `mode` 参数用于控制 forward 的返回结果:
|
||||
[forward](mmengine.model.BaseModel.forward):`forward` 的入参需要和 [DataLoader](https://pytorch.org/tutorials/beginner/basics/data_tutorial.html) 的输出保持一致(自定义[数据预处理器](数据预处理器(DataPreprocessor))除外),如果 `DataLoader` 返回元组类型的数据 `data`,`forward` 需要能够接受 `*data` 的解包后的参数;如果返回字典类型的数据 `data`,`forward` 需要能够接受 `**data` 解包后的参数。 `mode` 参数用于控制 forward 的返回结果:
|
||||
|
||||
- `mode='loss'`:`loss` 模式通常在训练阶段启用,并返回一个损失字典。损失字典的 key-value 分别为损失名和可微的 `torch.Tensor`。字典中记录的损失会被用于更新参数和记录日志。模型基类会在 `train_step` 方法中调用该模式的 `forward`。
|
||||
- `mode='predict'`: `predict` 模式通常在验证、测试阶段启用,并返回列表/元组形式的预测结果,预测结果需要和 [process](mmengine.evaluator.Evaluator) 接口的参数相匹配。OpenMMLab 系列算法对 `predict` 模式的输出有着更加严格的约定,需要输出列表形式的[数据元素](./data_element.md)。模型基类会在 `val_step`,`test_step` 方法中调用该模式的 `forward`。
|
||||
- `mode='predict'`:`predict` 模式通常在验证、测试阶段启用,并返回列表/元组形式的预测结果,预测结果需要和 [process](mmengine.evaluator.Evaluator) 接口的参数相匹配。OpenMMLab 系列算法对 `predict` 模式的输出有着更加严格的约定,需要输出列表形式的[数据元素](../advanced_tutorials/data_element.md)。模型基类会在 `val_step`,`test_step` 方法中调用该模式的 `forward`。
|
||||
- `mode='tensor'`:`tensor` 和 `predict` 模式均返回模型的前向推理结果,区别在于 `tensor` 模式下,`forward` 会返回未经后处理的张量,例如返回未经非极大值抑制(nms)处理的检测结果,返回未经 `argmax` 处理的分类结果。我们可以基于 `tensor` 模式的结果进行自定义的后处理。
|
||||
|
||||
[train_step](mmengine.model.BaseModel.train_step): 调用 `loss` 模式的 `forward` 接口,得到损失字典。模型基类基于[优化器封装](./optim_wrapper.md) 实现了标准的梯度计算、参数更新、梯度清零流程。
|
||||
[train_step](mmengine.model.BaseModel.train_step):调用 `loss` 模式的 `forward` 接口,得到损失字典。模型基类基于[优化器封装](./optim_wrapper.md)实现了标准的梯度计算、参数更新、梯度清零流程。
|
||||
|
||||
[val_step](mmengine.model.BaseModel.val_step): 调用 `predict` 模式的 `forward`,返回预测结果,预测结果会被进一步传给[评测器](./metric_and_evaluator.md)的 [process](mmengine.evaluator.Evaluator.process) 接口和[钩子(Hook)](./hook.md)的 `after_val_iter` 接口。
|
||||
[val_step](mmengine.model.BaseModel.val_step):调用 `predict` 模式的 `forward`,返回预测结果,预测结果会被进一步传给[评测器](./evaluation.md)的 [process](mmengine.evaluator.Evaluator.process) 接口和[钩子(Hook)](./hook.md)的 `after_val_iter` 接口。
|
||||
|
||||
[test_step](mmengine.model.BaseModel.test_step): 同 `val_step`,预测结果会被进一步传给 `after_test_iter` 接口。
|
||||
[test_step](mmengine.model.BaseModel.test_step):同 `val_step`,预测结果会被进一步传给 `after_test_iter` 接口。
|
||||
|
||||
基于上述接口约定,我们定义了继承自模型基类的 `NeuralNetwork`,配合执行器来训练 `FashionMNIST`:
|
||||
|
||||
|
@ -159,7 +159,7 @@ model.val_step(data)
|
|||
model.test_step(data)
|
||||
```
|
||||
|
||||
上例中,我们实现了 `BaseModel.train_step`、`BaseModel.val_step` 和 `BaseModel.test_step` 的简化版。数据经 `NormalizeDataPreprocessor.forward` 归一化处理,解包后传给 `NeuralNetwork.forward`,进一步返回损失或者预测结果。如果想实现自定义的参数优化或预测逻辑,可以自行实现 `train_step`、`val_step` 和 `test_step`,具体例子可以参考:[使用 MMEngine 训练生成对抗网络](../examples/train_a_gan.md)
|
||||
上例中,我们实现了 `BaseModel.train_step`、`BaseModel.val_step` 和 `BaseModel.test_step` 的简化版。数据经 `NormalizeDataPreprocessor.forward` 归一化处理,解包后传给 `NeuralNetwork.forward`,进一步返回损失或者预测结果。如果想实现自定义的参数优化或预测逻辑,可以自行实现 `train_step`、`val_step` 和 `test_step`,具体例子可以参考:[使用 MMEngine 训练生成对抗网络](../examples/train_a_gan.md)。
|
||||
|
||||
```{note}
|
||||
上例中数据预处理器的 training 参数用于区分训练、测试阶段不同的批增强策略,`train_step` 会传入 `training=True`,`test_step` 和 `val_step` 则会传入 `trainig=Fasle`。
|
||||
|
|
|
@ -15,7 +15,7 @@ import torch
|
|||
from torch.optim import SGD
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
****
|
||||
|
||||
inputs = [torch.zeros(10, 1, 1)] * 10
|
||||
targets = [torch.ones(10, 1, 1)] * 10
|
||||
model = nn.Linear(1, 1)
|
||||
|
@ -81,7 +81,7 @@ for input, target in zip(inputs, targets):
|
|||
|
||||

|
||||
|
||||
开合混合精度训练需要使用 `AmpOptimWrapper`,他的 optim_context 接口类似 `autocast`,会开启混合精度训练的上下文。除此之外他还能加速分布式训练时的梯度累加,这个我们会在下一个示例中介绍
|
||||
开启混合精度训练需要使用 `AmpOptimWrapper`,他的 optim_context 接口类似 `autocast`,会开启混合精度训练的上下文。除此之外他还能加速分布式训练时的梯度累加,这个我们会在下一个示例中介绍。
|
||||
|
||||
**3.1 基于 Pytorch 的 SGD 优化器实现混合精度训练和梯度累加**
|
||||
|
||||
|
@ -114,9 +114,9 @@ for input, target in zip(inputs, targets):
|
|||
|
||||
优化器封装同样提供了更细粒度的接口,方便用户实现一些自定义的参数更新逻辑:
|
||||
|
||||
- `backward`:传入损失,用于计算参数梯度,。
|
||||
- `step`: 同 `optimizer.step`,用于更新参数。
|
||||
- `zero_grad`: 同 `optimizer.zero_grad`,用于参数的梯度。
|
||||
- `backward`:传入损失,用于计算参数梯度。
|
||||
- `step`:同 `optimizer.step`,用于更新参数。
|
||||
- `zero_grad`:同 `optimizer.zero_grad`,用于参数的梯度。
|
||||
|
||||
我们可以使用上述接口实现和 Pytorch 优化器相同的参数更新逻辑:
|
||||
|
||||
|
@ -144,9 +144,9 @@ optim_wrapper = AmpOptimWrapper(
|
|||
optimizer=optimizer, clip_grad=dict(clip_value=0.2))
|
||||
```
|
||||
|
||||
### 获取学习率/动量:
|
||||
### 获取学习率/动量
|
||||
|
||||
优化器封装提供了 `get_lr` 和 `get_momentum` 接口用于获取优化器的一个参数组的学习率
|
||||
优化器封装提供了 `get_lr` 和 `get_momentum` 接口用于获取优化器的一个参数组的学习率:
|
||||
|
||||
```python
|
||||
import torch.nn as nn
|
||||
|
@ -158,7 +158,7 @@ model = nn.Linear(1, 1)
|
|||
optimizer = SGD(model.parameters(), lr=0.01)
|
||||
optim_wrapper = OptimWrapper(optimizer)
|
||||
|
||||
print(optimizer.param_groups[0]['lr']) # -1.01
|
||||
print(optimizer.param_groups[0]['lr']) # 0.01
|
||||
print(optimizer.param_groups[0]['momentum']) # 0
|
||||
print(optim_wrapper.get_lr()) # {'lr': [0.01]}
|
||||
print(optim_wrapper.get_momentum()) # {'momentum': [0]}
|
||||
|
@ -183,21 +183,21 @@ from mmengine.optim import OptimWrapper, AmpOptimWrapper
|
|||
model = nn.Linear(1, 1)
|
||||
optimizer = SGD(model.parameters(), lr=0.01)
|
||||
|
||||
optim_wapper = OptimWrapper(optimizer=optimizer)
|
||||
amp_optim_wapper = AmpOptimWrapper(optimizer=optimizer)
|
||||
optim_wrapper = OptimWrapper(optimizer=optimizer)
|
||||
amp_optim_wrapper = AmpOptimWrapper(optimizer=optimizer)
|
||||
|
||||
# 导出状态字典
|
||||
optim_state_dict = optim_wapper.state_dict()
|
||||
amp_optim_state_dict = amp_optim_wapper.state_dict()
|
||||
optim_state_dict = optim_wrapper.state_dict()
|
||||
amp_optim_state_dict = amp_optim_wrapper.state_dict()
|
||||
|
||||
print(optim_state_dict)
|
||||
print(amp_optim_state_dict)
|
||||
optim_wapper_new = OptimWrapper(optimizer=optimizer)
|
||||
amp_optim_wapper_new = AmpOptimWrapper(optimizer=optimizer)
|
||||
optim_wrapper_new = OptimWrapper(optimizer=optimizer)
|
||||
amp_optim_wrapper_new = AmpOptimWrapper(optimizer=optimizer)
|
||||
|
||||
# 加载状态字典
|
||||
amp_optim_wapper_new.load_state_dict(amp_optim_state_dict)
|
||||
optim_wapper_new.load_state_dict(optim_state_dict)
|
||||
amp_optim_wrapper_new.load_state_dict(amp_optim_state_dict)
|
||||
optim_wrapper_new.load_state_dict(optim_state_dict)
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -211,7 +211,7 @@ optim_wapper_new.load_state_dict(optim_state_dict)
|
|||
|
||||
与普通的优化器封装不同,`OptimWrapperDict` 没有实现 `update_params`、 `optim_context`, `backward`、`step` 等方法,无法被直接用于训练模型。我们建议直接访问 `OptimWrapperDict` 管理的优化器实例,来实现参数更新逻辑。
|
||||
|
||||
你或许会好奇,既然 `OptimWrapperDict` 没有训练的功能,那为什么不直接使用 `dict` 来管理多个优化器。事实上,`OptimWrapperDict` 的核心功能是支持批量导出/加载所有优化器封装的状态字典;支持获取多个优化器封装的学习率、动量。如果没有 `OptimWrapperDict`,`MMEngine` 就需要在很多位置对优化器封装的类型做 `if else` 判断,以获取所有优化器封装的状态。
|
||||
你或许会好奇,既然 `OptimWrapperDict` 没有训练的功能,那为什么不直接使用 `dict` 来管理多个优化器?事实上,`OptimWrapperDict` 的核心功能是支持批量导出/加载所有优化器封装的状态字典;支持获取多个优化器封装的学习率、动量。如果没有 `OptimWrapperDict`,`MMEngine` 就需要在很多位置对优化器封装的类型做 `if else` 判断,以获取所有优化器封装的状态。
|
||||
|
||||
```python
|
||||
from torch.optim import SGD
|
||||
|
@ -243,8 +243,7 @@ print(optim_dict.get_momentum()) # {'gen.momentum': [0], 'disc.momentum': [0]}
|
|||
|
||||
### 简单配置
|
||||
|
||||
优化器封装需要接受 `optimizer` 参数,因此我们首先需要为优化器封装配置 `optimizer`。
|
||||
MMEngine 会自动将 PyTorch 中的所有优化器都添加进 `OPTIMIZERS` 注册表中,用户可以用字典的形式来指定优化器,所有支持的优化器见 [PyTorch 优化器列表](https://pytorch.org/docs/stable/optim.html#algorithms)。
|
||||
优化器封装需要接受 `optimizer` 参数,因此我们首先需要为优化器封装配置 `optimizer`。MMEngine 会自动将 PyTorch 中的所有优化器都添加进 `OPTIMIZERS` 注册表中,用户可以用字典的形式来指定优化器,所有支持的优化器见 [PyTorch 优化器列表](https://pytorch.org/docs/stable/optim.html#algorithms)。
|
||||
|
||||
以配置一个 SGD 优化器封装为例:
|
||||
|
||||
|
@ -260,7 +259,7 @@ optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
|
|||
optim_wrapper = dict(optimizer=optimizer)
|
||||
```
|
||||
|
||||
要想开启混合精度训练和梯度累加,需要将 `type` 切换成 `AmpOptimWrapper`,并指定 `accumulative_counts` 参数
|
||||
要想开启混合精度训练和梯度累加,需要将 `type` 切换成 `AmpOptimWrapper`,并指定 `accumulative_counts` 参数:
|
||||
|
||||
```python
|
||||
optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
|
||||
|
@ -282,17 +281,15 @@ optimizer = SGD([{'params': model.backbone.parameters()},
|
|||
momentum=0.9)
|
||||
```
|
||||
|
||||
上面的例子中,模型的骨干部分使用了 0.01 学习率,而模型的头部则使用了 1e-3 学习率。
|
||||
用户可以将模型的不同部分参数和对应的超参组成一个字典的列表传给优化器,来实现对模型优化的细粒度调整。
|
||||
上面的例子中,模型的骨干部分使用了 0.01 学习率,而模型的头部则使用了 1e-3 学习率。用户可以将模型的不同部分参数和对应的超参组成一个字典的列表传给优化器,来实现对模型优化的细粒度调整。
|
||||
|
||||
在 MMEngine 中,我们通过优化器封装构造器(optimizer wrapper constructor),让用户能够直接通过设置优化器封装配置文件中的 `paramwise_cfg` 字段而非修改代码来实现对模型的不同部分设置不同的超参。
|
||||
|
||||
#### 为不同类型的参数设置不同的超参系数
|
||||
|
||||
MMEngine 提供的默认优化器封装构造器支持对模型中不同类型的参数设置不同的超参系数。
|
||||
例如,我们可以在 `paramwise_cfg` 中设置 `norm_decay_mult=0` ,从而将正则化层(normalization layer)的权重(weight)和偏置(bias)的权值衰减系数(weight decay)设置为 0,来实现 [Bag of Tricks](https://arxiv.org/abs/1812.01187) 论文中提到的不对正则化层进行权值衰减的技巧。
|
||||
MMEngine 提供的默认优化器封装构造器支持对模型中不同类型的参数设置不同的超参系数。例如,我们可以在 `paramwise_cfg` 中设置 `norm_decay_mult=0`,从而将正则化层(normalization layer)的权重(weight)和偏置(bias)的权值衰减系数(weight decay)设置为 0,来实现 [Bag of Tricks](https://arxiv.org/abs/1812.01187) 论文中提到的不对正则化层进行权值衰减的技巧。
|
||||
|
||||
具体示例如下,我们将 `ToyModel` 中所有正则化层(`head.bn`)的的权重衰减系数设置为 0:
|
||||
具体示例如下,我们将 `ToyModel` 中所有正则化层(`head.bn`)的权重衰减系数设置为 0:
|
||||
|
||||
```python
|
||||
from mmengine.optim import build_optim_wrapper
|
||||
|
@ -348,7 +345,7 @@ optimizer = build_optim_wrapper(ToyModel(), optim_wrapper)
|
|||
|
||||
此外,与上文 PyTorch 的示例一样,在 MMEngine 中我们也同样可以对模型中的任意模块设置不同的超参,只需要在 `paramwise_cfg` 中设置 `custom_keys` 即可。
|
||||
|
||||
例如我们想将 `backbone.layer0` 所有参数的学习率设置为 0,衰减系数设置为 0,`backbone` 其余子模块的学习率设置为 1;`head` 所欲参数的学习率设置为 0.01,可以这样配置:
|
||||
例如我们想将 `backbone.layer0` 所有参数的学习率设置为 0,衰减系数设置为 0,`backbone` 其余子模块的学习率设置为 0.01;`head` 所有参数的学习率设置为 0.001,可以这样配置:
|
||||
|
||||
```python
|
||||
optim_wrapper = dict(
|
||||
|
@ -411,14 +408,13 @@ head.bn.bias
|
|||
|
||||
custom_keys 中每一个字段的含义如下:
|
||||
|
||||
1. `'backbone': dict(lr_mult=1)`:将名字前缀为 `backbone` 的参数的学习率设置为 1
|
||||
2. `'backbone.layer0': dict(lr_mult=0, decay_mult=0)`:将名字前缀为 `backbone.layer0` 的参数学习率设置为 0,衰减系数设置为 0,该配置优先级比第一条高
|
||||
3. `'head': dict(lr_mult=0.1)`:将名字前缀为 `head` 的参数的学习率设置为 0.1
|
||||
1. `'backbone': dict(lr_mult=1)`:将名字前缀为 `backbone` 的参数的学习率系数设置为 1
|
||||
2. `'backbone.layer0': dict(lr_mult=0, decay_mult=0)`:将名字前缀为 `backbone.layer0` 的参数学习率系数设置为 0,衰减系数设置为 0,该配置优先级比第一条高
|
||||
3. `'head': dict(lr_mult=0.1)`:将名字前缀为 `head` 的参数的学习率系数设置为 0.1
|
||||
|
||||
### 自定义优化器构造策略
|
||||
|
||||
与 MMEngine 中的其他模块一样,优化器封装构造器也同样由[注册表](./param_scheduler.md)管理。
|
||||
我们可以通过实现自定义的优化器封装构造器来实现自定义的超参设置策略。
|
||||
与 MMEngine 中的其他模块一样,优化器封装构造器也同样由[注册表](./param_scheduler.md)管理。我们可以通过实现自定义的优化器封装构造器来实现自定义的超参设置策略。
|
||||
|
||||
例如,我们想实现一个叫做 `LayerDecayOptimWrapperConstructor` 的优化器封装构造器,能够对模型不同深度的层自动设置递减的学习率:
|
||||
|
||||
|
@ -492,5 +488,4 @@ class MultipleOptimiWrapperConstructor:
|
|||
|
||||
### 在训练过程中调整超参
|
||||
|
||||
优化器中的超参数在构造时只能设置为一个定值,仅仅使用优化器封装,并不能在训练过程中调整学习率等参数。
|
||||
在 MMEngine 中,我们实现了参数调度器(Parameter Scheduler),以便能够在训练过程中调整参数。关于参数调度器的用法请见[优化器参数调整策略](./param_scheduler.md)
|
||||
优化器中的超参数在构造时只能设置为一个定值,仅仅使用优化器封装,并不能在训练过程中调整学习率等参数。在 MMEngine 中,我们实现了参数调度器(Parameter Scheduler),以便能够在训练过程中调整参数。关于参数调度器的用法请见[优化器参数调整策略](./param_scheduler.md)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## 参数调度器的使用
|
||||
|
||||
这里我们先简单介绍一下如何使用 PyTorch 内置的学习率调度器来进行学习率的调整。下面是参考 [PyTorch 官方文档](https://pytorch.org/docs/stable/optim.html) 实现的一个例子,我们构造一个 [ExponentialLR](mmengine.optim.ExponentialLR),并且在每个 epoch 结束后调用 `scheduler.step()`,实现了随 epoch 指数下降的学习率调整策略。
|
||||
这里我们先简单介绍一下如何使用 PyTorch 内置的学习率调度器来进行学习率的调整。下面是参考 [PyTorch 官方文档](https://pytorch.org/docs/stable/optim.html)实现的一个例子,我们构造一个 [ExponentialLR](mmengine.optim.ExponentialLR),并且在每个 epoch 结束后调用 `scheduler.step()`,实现了随 epoch 指数下降的学习率调整策略。
|
||||
|
||||
```python
|
||||
import torch
|
||||
|
@ -49,8 +49,7 @@ scheduler = MultiStepLR(optimizer, milestones=[8, 11], gamma=0.1)
|
|||
scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0.1)
|
||||
```
|
||||
|
||||
注意这里增加了初始化参数 `by_epoch`,控制的是学习率调整频率,当其为 True 时表示按轮次(epoch) 调整,为 False 时表示按迭代次数(iteration)调整,默认值为 True。
|
||||
在上面的例子中,表示按照轮次进行调整,此时其他参数的单位均为 epoch,例如 `milestones` 中的 \[8, 11\] 表示第 8 和 11 轮次结束时,学习率将会被调整为上一轮次的 0.1 倍。
|
||||
注意这里增加了初始化参数 `by_epoch`,控制的是学习率调整频率,当其为 True 时表示按轮次(epoch)调整,为 False 时表示按迭代次数(iteration)调整,默认值为 True。在上面的例子中,表示按照轮次进行调整,此时其他参数的单位均为 epoch,例如 `milestones` 中的 \[8, 11\] 表示第 8 和 11 轮次结束时,学习率将会被调整为上一轮次的 0.1 倍。
|
||||
|
||||
当修改了学习率调整频率后,调度器中与计数相关设置的含义也会相应被改变。当 `by_epoch=True` 时,milestones 中的数字表示在哪些轮次进行学习率衰减,而当 `by_epoch=False` 时则表示在进行到第几次迭代时进行学习率衰减。下面是一个按照迭代次数进行调整的例子,在第 600 和 800 次迭代结束时,学习率将会被调整为原来的 0.1 倍。
|
||||
|
||||
|
@ -73,7 +72,7 @@ scheduler = MultiStepLR.build_iter_from_epoch(optimizer, milestones=[8, 11], gam
|
|||
scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0.1, convert_to_iter_based=True)
|
||||
```
|
||||
|
||||
为了能直观感受这两种模式的区别,我们中这里再举一个例子。下面是一个按轮次更新的余弦退火(CosineAnnealing)学习率调度器,学习率仅在每个轮次结束后被修改:
|
||||
为了能直观感受这两种模式的区别,我们这里再举一个例子。下面是一个按轮次更新的余弦退火(CosineAnnealing)学习率调度器,学习率仅在每个轮次结束后被修改:
|
||||
|
||||
```python
|
||||
scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12)
|
||||
|
@ -138,23 +137,21 @@ scheduler = [
|
|||
|
||||
上述例子表示在训练的前 100 次迭代时使用线性的学习率预热,然后在第 100 到第 900 次迭代时使用周期为 800 的余弦退火学习率调度器使学习率按照余弦函数逐渐下降为 0 。
|
||||
|
||||
我们可以组合任意多个调度器,既可以使用 MMEngine 中已经支持的调度器,也可以实现自定义的调度器。
|
||||
如果相邻两个调度器的生效区间没有紧邻,而是有一段区间没有被覆盖,那么这段区间的学习率维持不变。而如果两个调度器的生效区间发生了重叠,则对多组调度器叠加使用,学习率的调整会按照调度器配置文件中的顺序触发(行为与 PyTorch 中 [`ChainedScheduler`](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ChainedScheduler.html#chainedscheduler) 一致)。
|
||||
在一般情况下,我们推荐用户在训练的不同阶段使用不同的学习率调度策略来避免调度器的生效区间发生重叠。如果确实需要将两个调度器叠加使用,则需要十分小心,我们推荐使用[学习率可视化工具](TODO)来可视化叠加后的学习率,以避免学习率的调整与预期不符。
|
||||
我们可以组合任意多个调度器,既可以使用 MMEngine 中已经支持的调度器,也可以实现自定义的调度器。如果相邻两个调度器的生效区间没有紧邻,而是有一段区间没有被覆盖,那么这段区间的学习率维持不变。而如果两个调度器的生效区间发生了重叠,则对多组调度器叠加使用,学习率的调整会按照调度器配置文件中的顺序触发(行为与 PyTorch 中 [`ChainedScheduler`](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ChainedScheduler.html#chainedscheduler) 一致)。在一般情况下,我们推荐用户在训练的不同阶段使用不同的学习率调度策略来避免调度器的生效区间发生重叠。如果确实需要将两个调度器叠加使用,则需要十分小心,我们推荐使用[学习率可视化工具](TODO)来可视化叠加后的学习率,以避免学习率的调整与预期不符。
|
||||
|
||||
## 如何调整其他参数
|
||||
|
||||
### 动量
|
||||
|
||||
和学习率一样, 动量也是优化器参数组中一组可以调度的参数。 动量调度器(momentum scheduler)的使用方法和学习率调度器完全一样。同样也只需要将动量调度器的配置添加进配置文件中的 `scheduler` 字段的列表中即可。
|
||||
和学习率一样, 动量也是优化器参数组中一组可以调度的参数。动量调度器(momentum scheduler)的使用方法和学习率调度器完全一样。同样也只需要将动量调度器的配置添加进配置文件中的 `scheduler` 字段的列表中即可。
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
scheduler = [
|
||||
# the lr scheduler
|
||||
# 学习率调度器
|
||||
dict(type='LinearLR', ...),
|
||||
# the momentum scheduler
|
||||
# 动量调度器
|
||||
dict(type='LinearMomentum',
|
||||
start_factor=0.001,
|
||||
by_epoch=False,
|
||||
|
@ -182,7 +179,7 @@ scheduler = [
|
|||
|
||||
这里设置的参数名是 `lr`,因此这个调度器的作用等同于直接使用学习率调度器 `LinearLRScheduler`。
|
||||
|
||||
除了动量之外,用户也可以对 `optimizer.param_groups` 中的其他参数名进行调度,可调度的参数取决于所使用的优化器。 例如,当使用带 `weight_decay` 的 SGD 优化器时,可以按照以下示例对调整 `weight_decay`:
|
||||
除了动量之外,用户也可以对 `optimizer.param_groups` 中的其他参数名进行调度,可调度的参数取决于所使用的优化器。例如,当使用带 `weight_decay` 的 SGD 优化器时,可以按照以下示例对调整 `weight_decay`:
|
||||
|
||||
```python
|
||||
scheduler = [
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
# 注册器(Registry)
|
||||
|
||||
OpenMMLab 的算法库支持了丰富的算法和数据集,因此实现了很多功能相近的模块。例如 ResNet 和 SE-ResNet 的算法实现分别基于 `ResNet` 和 `SEResNet` 类,这些类有相似的功能和接口,都属于算法库中的模型组件。
|
||||
为了管理这些功能相似的模块,MMEngine 实现了 [注册器](mmengine.registry.Registry)。
|
||||
OpenMMLab 大多数算法库均使用注册器来管理它们的代码模块,包括 [MMDetection](https://github.com/open-mmlab/mmdetection), [MMDetection3D](https://github.com/open-mmlab/mmdetection3d),[MMClassification](https://github.com/open-mmlab/mmclassification) 和 [MMEditing](https://github.com/open-mmlab/mmediting) 等。
|
||||
OpenMMLab 的算法库支持了丰富的算法和数据集,因此实现了很多功能相近的模块。例如 ResNet 和 SE-ResNet 的算法实现分别基于 `ResNet` 和 `SEResNet` 类,这些类有相似的功能和接口,都属于算法库中的模型组件。为了管理这些功能相似的模块,MMEngine 实现了 [注册器](mmengine.registry.Registry)。OpenMMLab 大多数算法库均使用注册器来管理它们的代码模块,包括 [MMDetection](https://github.com/open-mmlab/mmdetection), [MMDetection3D](https://github.com/open-mmlab/mmdetection3d),[MMClassification](https://github.com/open-mmlab/mmclassification) 和 [MMEditing](https://github.com/open-mmlab/mmediting) 等。
|
||||
|
||||
## 什么是注册器
|
||||
|
||||
MMEngine 实现的[注册器](mmengine.registry.Registry)可以看作一个映射表和模块构建方法(build function)的组合。映射表维护了一个字符串到**类或者函数的映射**,使得用户可以借助字符串查找到相应的类或函数,例如维护字符串 `"ResNet"` 到 `ResNet` 类或函数的映射,使得用户可以通过 `"ResNet"` 找到 `ResNet` 类;而模块构建方法则定义了如何根据字符串查找到对应的类或函数以及如何实例化这个类或者调用这个函数,例如,通过字符串 `"bn"` 找到 `nn.BatchNorm2d` 并实例化 `BatchNorm2d` 模块;又或者通过字符串 `"build_batchnorm2d"` 找到 `build_batchnorm2d` 函数并返回该函数的调用结果。
|
||||
MMEngine 中的注册器默认使用 [build_from_cfg](mmengine.registry.build_from_cfg) 函数来查找并实例化字符串对应的类或者函数。
|
||||
MMEngine 实现的[注册器](mmengine.registry.Registry)可以看作一个映射表和模块构建方法(build function)的组合。映射表维护了一个字符串到**类或者函数的映射**,使得用户可以借助字符串查找到相应的类或函数,例如维护字符串 `"ResNet"` 到 `ResNet` 类或函数的映射,使得用户可以通过 `"ResNet"` 找到 `ResNet` 类;而模块构建方法则定义了如何根据字符串查找到对应的类或函数以及如何实例化这个类或者调用这个函数,例如,通过字符串 `"bn"` 找到 `nn.BatchNorm2d` 并实例化 `BatchNorm2d` 模块;又或者通过字符串 `"build_batchnorm2d"` 找到 `build_batchnorm2d` 函数并返回该函数的调用结果。MMEngine 中的注册器默认使用 [build_from_cfg](mmengine.registry.build_from_cfg) 函数来查找并实例化字符串对应的类或者函数。
|
||||
|
||||
一个注册器管理的类或函数通常有相似的接口和功能,因此该注册器可以被视作这些类或函数的抽象。例如注册器 `MODELS` 可以被视作所有模型的抽象,管理了 `ResNet`, `SEResNet` 和 `RegNetX` 等分类网络的类以及 `build_ResNet`, `build_SEResNet` 和 `build_RegNetX` 等分类网络的构建函数。
|
||||
一个注册器管理的类或函数通常有相似的接口和功能,因此该注册器可以被视作这些类或函数的抽象。例如注册器 `MODELS` 可以被视作所有模型的抽象,管理了 `ResNet`,`SEResNet` 和 `RegNetX` 等分类网络的类以及 `build_ResNet`, `build_SEResNet` 和 `build_RegNetX` 等分类网络的构建函数。
|
||||
|
||||
## 入门用法
|
||||
|
||||
|
@ -84,6 +81,7 @@ print(ACTIVATION.module_dict)
|
|||
|
||||
```python
|
||||
import torch
|
||||
|
||||
input = torch.randn(2)
|
||||
|
||||
act_cfg = dict(type='Sigmoid')
|
||||
|
@ -99,7 +97,7 @@ print(output)
|
|||
act_cfg = dict(type='ReLU', inplace=True)
|
||||
activation = ACTIVATION.build(act_cfg)
|
||||
output = activation(input)
|
||||
# call Sigmoid.forward
|
||||
# call ReLU.forward
|
||||
print(output)
|
||||
```
|
||||
|
||||
|
@ -207,6 +205,7 @@ class RReLU(nn.Module):
|
|||
|
||||
```python
|
||||
from mmengine import Registry, MODELS as MMENGINE_MODELS
|
||||
|
||||
MODELS = Registry('model', parent=MMENGINE_MODELS, scope='mmalpha')
|
||||
```
|
||||
|
||||
|
@ -220,6 +219,7 @@ MODELS = Registry('model', parent=MMENGINE_MODELS, scope='mmalpha')
|
|||
|
||||
```python
|
||||
from mmengine.registry import count_registered_modules
|
||||
|
||||
count_registered_modules()
|
||||
```
|
||||
|
||||
|
@ -254,6 +254,7 @@ model = MODELS.build(cfg=dict(type='mmengine.RReLU'))
|
|||
|
||||
```python
|
||||
import torch
|
||||
|
||||
input = torch.randn(2)
|
||||
output = model(input)
|
||||
# call RReLU.forward
|
||||
|
@ -268,6 +269,7 @@ print(output)
|
|||
|
||||
```python
|
||||
from mmengine import Registry, MODELS as MMENGINE_MODELS
|
||||
|
||||
MODELS = Registry('model', parent=MMENGINE_MODELS, scope='mmbeta')
|
||||
```
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 执行器(Runner)
|
||||
|
||||
深度学习算法的训练、验证和测试通常都拥有相似的流程,因此 MMEngine 提供了执行器以帮助用户简化这些任务的实现流程。 用户只需要准备好模型训练、验证、测试所需要的模块构建执行器,便能够通过简单调用执行器的接口来完成这些任务。用户如果需要使用这几项功能中的某一项,只需要准备好对应功能所依赖的模块即可。
|
||||
深度学习算法的训练、验证和测试通常都拥有相似的流程,因此 MMEngine 提供了执行器以帮助用户简化这些任务的实现流程。用户只需要准备好模型训练、验证、测试所需要的模块构建执行器,便能够通过简单调用执行器的接口来完成这些任务。用户如果需要使用这几项功能中的某一项,只需要准备好对应功能所依赖的模块即可。
|
||||
|
||||
用户可以手动构建这些模块的实例,也可以通过编写[配置文件](./config.md),由执行器自动从[注册器](./registry.md)中构建所需要的模块,我们推荐使用后一种方式。
|
||||
|
||||
|
@ -8,8 +8,7 @@
|
|||
|
||||
### 手动构建模块进行训练
|
||||
|
||||
如上文所说,使用执行器的某一项功能时需要准备好对应功能所依赖的模块。以使用执行器的训练功能为例,用户需要准备[模型](TODO) 、[优化器](./optim_wrapper.md) 、
|
||||
[参数调度器](./param_scheduler.md) 还有训练[数据集](../advanced_tutorials/basedataset.md) 。
|
||||
如上文所说,使用执行器的某一项功能时需要准备好对应功能所依赖的模块。以使用执行器的训练功能为例,用户需要准备[模型](./model.md)、[优化器](./optim_wrapper.md)、[参数调度器](./param_scheduler.md)还有训练[数据集](../advanced_tutorials/basedataset.md)。
|
||||
|
||||
```python
|
||||
# 准备训练任务所需要的模块
|
||||
|
@ -56,7 +55,7 @@ train_dataloader = DataLoader(dataset=train_dataset, batch_size=10, num_workers=
|
|||
from mmengine.runner import Runner
|
||||
|
||||
|
||||
# 训练相关参数设置,按轮次训练,训练3轮
|
||||
# 训练相关参数设置,按轮次训练,训练 3 轮
|
||||
train_cfg = dict(by_epoch=True, max_epochs=3)
|
||||
|
||||
# 初始化执行器
|
||||
|
@ -70,18 +69,18 @@ runner = Runner(model,
|
|||
runner.train()
|
||||
```
|
||||
|
||||
上面的例子中,我们手动构建了一个多层感知机网络和手写数字识别 (MNIST) 数据集,以及训练所需要的优化器和学习率调度器,使用这些模块初始化了执行器,并且设置了训练配置 `train_cfg`,让执行器将模型训练3个轮次,最后通过调用执行器的 `train` 方法进行模型训练。
|
||||
上面的例子中,我们手动构建了一个多层感知机网络和手写数字识别 (MNIST) 数据集,以及训练所需要的优化器和学习率调度器,使用这些模块初始化了执行器,并且设置了训练配置 `train_cfg`,让执行器将模型训练 3 个轮次,最后通过调用执行器的 `train` 方法进行模型训练。
|
||||
|
||||
用户也可以修改 `train_cfg` 使执行器按迭代次数控制训练:
|
||||
|
||||
```python
|
||||
# 训练相关参数设置,按迭代次数训练,训练9000次迭代
|
||||
# 训练相关参数设置,按迭代次数训练,训练 9000 次迭代
|
||||
train_cfg = dict(by_epoch=False, max_iters=9000)
|
||||
```
|
||||
|
||||
### 手动构建模块进行测试
|
||||
|
||||
再举一个模型测试的例子,模型的测试需要用户准备模型和训练好的权重路径、测试数据集以及[评测器](./evaluation.md) :
|
||||
再举一个模型测试的例子,模型的测试需要用户准备模型和训练好的权重路径、测试数据集以及[评测器](./evaluation.md):
|
||||
|
||||
```python
|
||||
from mmengine.evaluator import BaseMetric
|
||||
|
@ -109,11 +108,11 @@ runner = Runner(model=model, test_dataloader=test_dataloader, test_evaluator=tes
|
|||
runner.test()
|
||||
```
|
||||
|
||||
这个例子中我们重新手动构建了一个多层感知机网络,以及测试用的手写数字识别数据集和使用 (Accuracy) 指标的评测器,并使用这些模块初始化执行器,最后通过调用执行器的 `test` 函数进行模型测试。
|
||||
这个例子中我们重新手动构建了一个多层感知机网络,以及测试用的手写数字识别数据集和使用 Accuracy 指标的评测器,并使用这些模块初始化执行器,最后通过调用执行器的 `test` 函数进行模型测试。
|
||||
|
||||
### 手动构建模块在训练过程中进行验证
|
||||
|
||||
在模型训练过程中,通常会按一定的间隔在验证集上对模型进行验证。在使用 MMEngine 时,只需要构建训练和验证的模块,并在训练配置中设置验证间隔即可
|
||||
在模型训练过程中,通常会按一定的间隔在验证集上对模型进行验证。在使用 MMEngine 时,只需要构建训练和验证的模块,并在训练配置中设置验证间隔即可:
|
||||
|
||||
```python
|
||||
# 准备训练任务所需要的模块
|
||||
|
@ -128,12 +127,11 @@ val_dataloader = Dataloader(dataset=val_dataset)
|
|||
metric = MnistAccuracy()
|
||||
val_evaluator = Evaluator(metric)
|
||||
|
||||
|
||||
# 训练相关参数设置
|
||||
train_cfg = dict(by_epoch=True, # 按轮次训练
|
||||
max_epochs=5, # 训练5轮
|
||||
max_epochs=5, # 训练 5 轮
|
||||
val_begin=2, # 从第 2 个 epoch 开始验证
|
||||
val_interval=1) # 每隔1轮进行1次验证
|
||||
val_interval=1) # 每隔1轮进行 1 次验证
|
||||
|
||||
# 初始化执行器
|
||||
runner = Runner(model=model, optim_wrapper=dict(optimizer=optimzier), param_scheduler=lr_scheduler,
|
||||
|
@ -145,8 +143,7 @@ runner.train()
|
|||
|
||||
## 通过配置文件使用执行器
|
||||
|
||||
OpenMMLab 的开源项目普遍使用注册器 + 配置文件的方式来管理和构建模块,MMEngine 中的执行器也推荐使用配置文件进行构建。
|
||||
下面是一个通过配置文件使用执行器的例子:
|
||||
OpenMMLab 的开源项目普遍使用注册器 + 配置文件的方式来管理和构建模块,MMEngine 中的执行器也推荐使用配置文件进行构建。下面是一个通过配置文件使用执行器的例子:
|
||||
|
||||
```python
|
||||
from mmengine import Config
|
||||
|
@ -193,7 +190,7 @@ optim_wrapper = dict(
|
|||
# 参数调度器配置
|
||||
param_scheduler = dict(
|
||||
type='MultiStepLR', by_epoch=True, milestones=[30, 60, 90], gamma=0.1)
|
||||
#验证和测试的评测器配置
|
||||
# 验证和测试的评测器配置
|
||||
val_evaluator = dict(type='Accuracy')
|
||||
test_evaluator = dict(type='Accuracy')
|
||||
|
||||
|
@ -207,10 +204,10 @@ train_cfg = dict(
|
|||
val_cfg = dict()
|
||||
test_cfg = dict()
|
||||
|
||||
# 自定义钩子 (可选)
|
||||
# 自定义钩子(可选)
|
||||
custom_hooks = [...]
|
||||
|
||||
# 默认钩子 (可选,未在配置文件中写明时将使用默认配置)
|
||||
# 默认钩子(可选,未在配置文件中写明时将使用默认配置)
|
||||
default_hooks = dict(
|
||||
runtime_info=dict(type='RuntimeInfoHook'), # 运行时信息钩子
|
||||
timer=dict(type='IterTimerHook'), # 计时器钩子
|
||||
|
@ -220,25 +217,24 @@ default_hooks = dict(
|
|||
checkpoint=dict(type='CheckpointHook', interval=1), # 模型保存钩子
|
||||
)
|
||||
|
||||
# 环境配置 (可选,未在配置文件中写明时将使用默认配置)
|
||||
# 环境配置(可选,未在配置文件中写明时将使用默认配置)
|
||||
env_cfg = dict(
|
||||
cudnn_benchmark=False, # 是否使用 cudnn_benchmark
|
||||
dist_cfg=dict(backend='nccl'), # 分布式通信后端
|
||||
mp_cfg=dict(mp_start_method='fork') # 多进程设置
|
||||
)
|
||||
# 日志处理器 (可选,未在配置文件中写明时将使用默认配置)
|
||||
# 日志处理器(可选,未在配置文件中写明时将使用默认配置)
|
||||
log_processor = dict(type='LogProcessor', window_size=50, by_epoch=True)
|
||||
# 日志等级配置
|
||||
log_level = 'INFO'
|
||||
|
||||
# 加载权重的路径 (None 表示不加载)
|
||||
# 加载权重的路径(None 表示不加载)
|
||||
load_from = None
|
||||
# 从加载的权重文件中恢复训练
|
||||
resume = False
|
||||
```
|
||||
|
||||
一个完整的配置文件主要由模型、数据、优化器、参数调度器、评测器等模块的配置,训练、验证、测试等流程的配置,还有执行流程过程中的各种钩子模块的配置,以及环境和日志等其他配置的字段组成。
|
||||
通过配置文件构建的执行器采用了懒初始化 (lazy initialization),只有当调用到训练或测试等执行函数时,才会根据配置文件去完整初始化所需要的模块。
|
||||
一个完整的配置文件主要由模型、数据、优化器、参数调度器、评测器等模块的配置,训练、验证、测试等流程的配置,还有执行流程过程中的各种钩子模块的配置,以及环境和日志等其他配置的字段组成。通过配置文件构建的执行器采用了懒初始化 (lazy initialization),只有当调用到训练或测试等执行函数时,才会根据配置文件去完整初始化所需要的模块。
|
||||
|
||||
关于配置文件的更详细的使用方式,请参考[配置文件教程](./config.md)
|
||||
|
||||
|
|
Loading…
Reference in New Issue