# 训练器(Solvers)
## 总览
Solver是对模型训练、验证和测试过程的一个流程定义。
在Solver中,会根据配置yaml文件的设置,对数据(data),模型(model),优化器(optimizer)和调度(scheduler)等需要的模块进行逐一初始化。
每一个具体的任务的自定义Solver都要继承自BaseSolver。
在某些特殊场景下,还需要初始化一些自定义的模块。例如,在训练过程中记录保存中间结果需要用到Hooks;在训练过程中验证需要定义Metrics等等。
## 基础用法
子Solver继承时的用法:
```python
from scepter.modules.solver.registry import SOLVERS
from scepter.modules.solver import BaseSolver
@SOLVERS.register_class()
class XxxSolver(BaseSolver):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
```
实际启动Solver的使用方式见scepter.modules/task/cate_recognition中run_task.py和run_inference.py:
```python
solver = SOLVERS.build(cfg.SOLVER, logger=std_logger)
```
## **scepter.modules.solver.BaseSolver**
Solver的基类,是通过元类ABCMeta定义的抽象基类,支持注册操作。自定义的solver均应该是该类的子类,并且进行注册。
BaseSolver是一个具体实现Solver的案例,展示了使用pytorch_lightning和不使用时两种Solver的写法。
在实际使用中情况各异,因此Solver的使用也比较灵活,其中大部分的成员函数均可以按照需求在子类中,被复写或者新加好用的功能,甚至直接写新的函数代替。
### function **\_\_init\_\_**
(cfg: *scepter.modules.utils.config.Config*, logger = None) -> None
初始化Solver类,从cfg中获取并定义一些必要的参数。
部分在__init__中初始化的参数,详情见代码:
**Configs**
- **FILE_SYSTEM** —— (cfg) 文件系统配置,默认None.
- **WORK_DIR** —— (str) 工作路径定义.
- **LOG_FILE** —— (str) Log文件的位置.
- **RESUME_FROM** —— (str) 恢复训练模型保存的中间结果.
- **MAX_EPOCHS** —— (int) 训练最大epochs数.
- **ACCU_STEP** —— (int) When use ddp, the grad accumulate steps for each process,默认1.
- **NUM_FOLDS** —— (int) Num folds for training,默认1.
- **EVAL_INTERVAL** —— (int) Eval the model interval,默认1.
- **EXTRA_KEYS** —— (List) The extra keys for metrics,默认[].
- **TRAIN_DATA** —— (cfg) 训练数据配置.
- **EVAL_DATA** —— (cfg) 验证数据配置.
- **TEST_DATA** —— (cfg) 测试数据配置.
- **TRAIN_HOOKS** —— (List) 训练HOOKS.
- **EVAL_HOOKS** —— (List) 验证HOOKS.
- **TEST_HOOKS** —— (List) 测试HOOKS.
- **MODEL** —— (cfg) 模型配置.
- **OPTIMIZER** —— (cfg) 优化器配置.
- **LR_SCHEDULER** —— (cfg) 学习率Scheduler配置.
- **METRICS** —— (List) Metrics.
**Parameters**
- **cfg** —— The Config used to build solver.
- **logger** —— Instantiated Logger to print or save log.
### function **set_up_pre**
() -> None
配置环境、日志路径,调用construct_hook来初始化hook等等。
与pytorch_lightning二选一,在不使用pytorch_lightning(use_pl=Flase)时调用。需要在启动其他所有操作之前调用。
### function **set_up**
() -> None
配置数据、模型、metrics、优化器及pytorch_lightning环境(如果使用的话)。
### function **construct_data**
() -> None
实际数据的构建方法,默认会在self.set_up中被调用,包括TRAIN_DATA、EVAL_DATA、TEST_DATA。将实例化的结果写入self.datas中。
### function **construct_hook**
() -> None
实际Hook的构建方法,默认会在self.set_up_pre中被调用,包括TRAIN_HOOKS、EVAL_HOOKS、TEST_HOOKS。将实例化的结果写入self.hooks_dict中。
### function **construct_model**
() -> None
实际Hook的构建方法,默认会在self.set_up中被调用,将实例化的结果作为self.model。
### function **model_to_device**
() -> None
实际Metrics的构建方法,默认会在self.set_up中被调用,将实例化的结果写入self.metrics。
### function **model_to_device**
(tg_model_ins=None) -> None or MODELS
模型的配置方法,包括模型分片等分布式配置,默认会在self.set_up中被调用。
**Parameters**
- **tg_model_ins** —— 待配置的model,如果为None,默认使用self.model。
**Returns**
- **tg_model_ins** —— 配置好的model。如果tg_model_ins为None,则无返回值。
### function **init_opti**
() -> None
优化器的配置方法,默认会在self.set_up中被调用。将实例化的optimizer和lr_scheduler分别作为self.optimizer和self.lr_scheduler。
### function **solve**
(epoch = None, every_epoch = False) -> None
执行实际的训练、验证或者测试操作,执行self.solve_train、self.solve_eval、self.solve_test等。
并且在执行前后分别调用self.before_solve、self.after_solve来进行Hook的记录。
**Parameters**
- **epoch** —— 设定epoch数量。
- **every_epoch** —— 与self.solve_train、self.solve_eval、self.solve_test的实现方式及Data的配置有关,标记是否每个epoch都需要重新调用一遍。
以self.solve_train为例,如果实现方式是调用一次执行一个epoch,则every_epoch应为True;如果调用一次调用会执行到所有epoch都结束,则应为False。
### function **solve_train**
() -> None
调用self.run_train,执行train。
并且在执行前后分别调用self.before_epoch、self.after_epoch来进行Hook的记录。
### function **solve_eval**
() -> None
调用self.run_eval,执行eval。
并且在执行前后分别调用self.before_epoch、self.after_epoch来进行Hook的记录。
### function **solve_test**
() -> None
调用self.run_test,执行test。
并且在执行前后分别调用self.before_epoch、self.after_epoch来进行Hook的记录。
### function **before_solve**
() -> None
在实际执行run_xxx之前,进行Hook的记录。
### function **after_solve**
() -> None
在执行run_xxx之后,再次进行Hook的记录。
### function **run_train**
() -> None
循环调用self.run_step_train,执行一个epoch或者所有epoch的训练过程。
并且在循环开始及结束后分别调用self.before_all_iter、self.after_all_iter来进行Hook的记录。
并且在每次循环调用self.run_step_train前后,分别调用self.before_iter、self.after_iter来进行Hook的记录。
### function **run_eval**
() -> None
循环调用self.run_step_eval,执行一个epoch或者所有epoch的验证过程。
并且在循环开始及结束后分别调用self.before_all_iter、self.after_all_iter来进行Hook的记录。
并且在每次循环调用self.run_step_eval前后,分别调用self.before_iter、self.after_iter来进行Hook的记录。
### function **run_test**
() -> None
循环调用self.run_step_test,执行一个epoch或者所有epoch的验证过程。
并且在循环开始及结束后分别调用self.before_all_iter、self.after_all_iter来进行Hook的记录。
并且在每次循环调用self.run_step_test前后,分别调用self.before_iter、self.after_iter来进行Hook的记录。
### function **run_step_train**
(batch_data, batch_idx = 0, step = None, rank = None) -> None
执行一个batch的模型推理。
### function **run_step_eval**
(batch_data, batch_idx = 0, step = None, rank = None) -> None
执行一个batch的模型推理。
### function **run_step_test**
(batch_data, batch_idx = 0, step = None, rank = None) -> None
执行一个batch的模型推理。
### function **register_flops**
(Dict data, keys = []) -> None
使用fvcore进行flops计算,结果保存在self._model_flops中。
**Parameters**
- **data** —— 输入模型的data,格式为key-value结构,value为Tensor或者List,默认只取batch维度的第一个元素来保证batchsize=1。
- **keys** —— 指定使用keys中的元素所对应的value作为模型输入。如果keys为空,则去data中所有的value。
### function **before_epoch**
() -> None
在每个epoch开始前执行。run_xxx前执行。
### function **before_all_iter**
() -> None
在每个epoch开始前执行。循环调用run_step_xxx的循环前执行。
### function **before_iter**
() -> None
在每个step开始前执行。run_step_xxx前执行。
### function **after_epoch**
() -> None
在每个epoch开始后执行。run_xxx后执行。
### function **after_all_iter**
() -> None
在每个epoch开始后执行。循环调用run_step_xxx的循环后执行。
### function **after_iter**
() -> None
在每个step开始后执行。run_step_xxx后执行。
### function **collect_log_vars**
() -> OrderedDict
获取需要在log中保存的变量。
**Returns**
- **ret** —— 返回需要的变量。
# Hooks
## 总览
在Solver执行过程中,需要打印日志、记录Tensorboard、梯度计算和更新、保存中间模型参数、保存测试结果等,这些都需要Hook去执行。
## 基础用法
新建Hook的方法:
```python
from scepter.modules.solver.hooks.hook import Hook
from scepter.modules.solver.hooks.registry import HOOKS
@HOOKS.register_class()
class XxxHook(Hook):
def __init__(self, cfg, logger=None):
super(XxxHook, self).__init__(cfg, logger=logger)
```
## **scepter.modules.solvers.hooks.Hook**
定义了一个标准的Hook类的基类,定义了详细的成员函数列表。继承自该类的子类均需要在这些成员函数中挑选部分进行实现。
成员函数包括:
### function **__init__**
(cfg: *scepter.modules.utils.config.Config*, logger = None) -> None
初始化logger。
### function **before_solve**
(solver) -> None
开始solve之前执行。
### function **after_solve**
(solver) -> None
结束solve之后执行。
### function **before_epoch**
(solver) -> None
每个epoch之前执行。
### function **after_epoch**
(solver) -> None
每个epoch之后执行。
### function **before_all_iter**
(solver) -> None
迭代开始之前执行。
### function **after_all_iter**
(solver) -> None
迭代结束之后执行。
### function **before_iter**
(solver) -> None
每个step之前执行。
### function **after_iter**
(solver) -> None
每个step之后执行。
## **scepter.modules.solver.hooks.CheckpointHook**
在solve开始之前,加载checkpoint。加载模型的路径来自Solver的RESUME_FROM参数。依赖于Solver中实现的load_checkpoint成员函数。
每个epoch结束之后,保存checkpoint。
**Configs**
- **PRIORITY** —— (int)默认为_DEFAULT_CHECKPOINT_PRIORITY=300
- **INTERVAL** —— (int)保存checkpoint的epoch间隔,默认为1
- **SAVE_NAME_PREFIX** —— (str)保存checkpoint的前缀名,默认为'ldm_step'
- **SAVE_LAST** —— (bool)是否保存最后iter的checkpoint,默认为False,作用于after_iter中。
- **SAVE_BEST** —— (bool)是否保存最好的checkpoint,默认为True,作用于after_epoch中。需要同时设置SAVE_BEST_BY,否则退回默认False。
- **SAVE_BEST_BY** —— (str)判断最好的指标,默认越大越好
## **scepter.modules.solver.hooks.BackwardHook**
在每步迭代之后执行的内容。包括loss的反传,optimizer的步数配置等等。
**Configs**
- **PRIORITY** —— (int)_DEFAULT_BACKWARD_PRIORITY=0
- **GRADIENT_CLIP** —— (int)torch.nn.utils.clip_grad_norm_中max_norm参数,默认为-1。小于等于0时不生效。
- **ACCUMULATE_STEP** —— (int)用于设置梯度累计的步数
- **EMPTY_CACHE_STEP** —— (int)torch.cuda.empty_cache每多少步清除一下memory,默认为-1。小于等于0时不生效。
-
## **scepter.modules.solver.hooks.LogHook**
日志Hook。
**Configs**
- **PRIORITY** —— (int)__DEFAULT_LOG_PRIORITY=100
- **SHOW_GPU_MEM** —— (bool)判断是否打印内存使用情况
- **LOG_INTERVAL** —— (int)打印log的步数间隔,默认为10。小于等于0时不生效。
## **scepter.modules.solver.hooks.TensorboardLogHook**
Tensorboard日志Hook。
**Configs**
- **PRIORITY** —— (int)__DEFAULT_LOG_PRIORITY=100
- **LOG_DIR** —— (str)存储Tensorboard log的路径。
- **LOG_INTERVAL** —— (int)打印log的步数间隔,默认为1000。
## **scepter.modules.solver.hooks.LrHook**
学习率变化Hook。
**Configs**
- **PRIORITY** —— (int)_DEFAULT_LR_PRIORITY=200
- **WARMUP_FUNC** —— (str)warmup的方式:仅支持"linear",默认为"linear"。
- **WARMUP_EPOCHS** —— (int)warmup epoch数,默认为1。
- **WARMUP_START_LR** —— (float)warmup初始学习率,默认为0.0001。
- **SET_BY_EPOCH** —— (bool)是否每个epoch设置一次学习率,默认为True。False则每个step设置一次。
## **scepter.modules.solver.hooks.DistSamplerHook**
每个epoch开始之前,采样一个epoch的数据。
**Configs**
- **PRIORITY** —— (int)__DEFAULT_SAMPLER_PRIORITY=400
## **scepter.modules.solver.hooks.ProbeDataHook**
用于打印probe存储的train/eval中间(可视化)结果。
**Configs**
- **PRIORITY** —— (int)_DEFAULT_LR_PRIORITY=200
- **PROB_INTERVAL** —— (int)打印probe的步数间隔,默认为1000。
## **scepter.modules.solver.hooks.SafetensorsHook**
用于存储.safetensors格式的模型文件。
**Configs**
- **PRIORITY** —— (int)_DEFAULT_LR_PRIORITY=200
- **INTERVAL** —— (int)存储的步数间隔,默认为1000。
- **SAVE_NAME_PREFIX** —— (str)保存文件的前缀名。