File size: 13,272 Bytes
d3dbf03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# 自定义优化器

在本教程中,我们将介绍一些构建优化器和学习率策略的方法,以用于你的任务。

- [自定义优化器](#自定义优化器)
  - [使用 optim_wrapper 构建优化器](#使用-optim_wrapper-构建优化器)
    - [使用 PyTorch 支持的优化器](#使用-pytorch-支持的优化器)
    - [参数化精细配置](#参数化精细配置)
    - [梯度裁剪](#梯度裁剪)
    - [梯度累积](#梯度累积)
  - [自定义参数策略](#自定义参数策略)
    - [自定义学习率策略](#自定义学习率策略)
    - [自定义动量策略](#自定义动量策略)
  - [添加新的优化器或构造器](#添加新的优化器或构造器)
    - [添加新的优化器](#添加新的优化器)
      - [1. 实现一个新的优化器](#1-实现一个新的优化器)
      - [2. 导入优化器](#2-导入优化器)
      - [3. 在配置文件中指定优化器](#3-在配置文件中指定优化器)
    - [添加新的优化器构造器](#添加新的优化器构造器)

## 使用 optim_wrapper 构建优化器



我们使用 `optim_wrapper` 字段来配置优化策略,其中包括选择优化器、参数逐个配置、梯度裁剪和梯度累积。一个简单的示例可以是:

```python

optim_wrapper = dict(

    type='OptimWrapper',

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

)

```

在上面的示例中,我们构建了一个学习率为 0.0003,权重衰减为 0.0001 的 SGD 优化器。

### 使用 PyTorch 支持的优化器

我们支持 PyTorch 实现的所有优化器。要使用不同的优化器,只需更改配置文件中的 `optimizer` 字段。例如,如果想使用 `torch.optim.Adam`,可以在配置文件中进行如下修改。

```python

optim_wrapper = dict(

    type='OptimWrapper',

    optimizer = dict(

        type='Adam',

        lr=0.001,

        betas=(0.9, 0.999),

        eps=1e-08,

        weight_decay=0,

        amsgrad=False),

)

```

首先,我们需要将 `type` 的值更改为 `torch.optim` 支持的期望优化器名称。然后,将该优化器的必要参数添加到 `optimizer` 字段中。上述配置将构建以下优化器:

```python

torch.optim.Adam(lr=0.001,

                 betas=(0.9, 0.999),

                 eps=1e-08,

                 weight_decay=0,

                 amsgrad=False)

```

### 参数化精细配置

一些模型可能对优化有特定的参数设置,例如对于 BatchNorm 层不使用权重衰减,或者对不同网络层使用不同的学习率。为了对其进行细致配置,我们可以使用 `optim_wrapper` 中的 `paramwise_cfg` 参数。

- **为不同类型的参数设置不同的超参数倍数。**

  例如,我们可以在 `paramwise_cfg` 中设置 `norm_decay_mult=0.`,将归一化层的权重衰减设置为零。

  ```python

  optim_wrapper = dict(

      optimizer=dict(type='SGD', lr=0.8, weight_decay=1e-4),

      paramwise_cfg=dict(norm_decay_mult=0.))

  ```

  还支持设置其他类型的参数,包括:

  - `lr_mult`:所有参数的学习率乘数。
  - `decay_mult`:所有参数的权重衰减乘数。
  - `bias_lr_mult`:偏置项的学习率乘数(不包括归一化层的偏置项和可变形卷积层的偏移量)。默认为 1。
  - `bias_decay_mult`:偏置项的权重衰减乘数(不包括归一化层的偏置项和可变形卷积层的偏移量)。默认为 1。
  - `norm_decay_mult`:归一化层权重和偏置项的权重衰减乘数。默认为 1。
  - `dwconv_decay_mult`:深度卷积层的权重衰减乘数。默认为 1。
  - `bypass_duplicate`:是否跳过重复的参数。默认为 `False`  - `dcn_offset_lr_mult`:可变形卷积层的学习率乘数。默认为 1。

- **为特定参数设置不同的超参数倍数。**

  MMAction2 可以使用 `paramwise_cfg` 中的 `custom_keys` 来指定不同的参数使用不同的学习率或权重衰减。

  例如,要将 `backbone.layer0` 的所有学习率和权重衰减设置为 0,而保持 `backbone` 的其余部分与优化器相同,并将 `head` 的学习率设置为 0.001,可以使用以下配置:

  ```python

  optim_wrapper = dict(

      optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001),

      paramwise_cfg=dict(

          custom_keys={

              'backbone.layer0': dict(lr_mult=0, decay_mult=0),

              'backbone': dict(lr_mult=1),

              'head': dict(lr_mult=0.1)

          }))

  ```

### 梯度裁剪

在训练过程中,损失函数可能接近悬崖区域,导致梯度爆炸。梯度裁剪有助于稳定训练过程。梯度裁剪的更多介绍可以在[这个页面](https://paperswithcode.com/method/gradient-clipping)找到。

目前,我们支持 `optim_wrapper` 中的 `clip_grad` 选项进行梯度裁剪,参考[PyTorch 文档](torch.nn.utils.clip_grad_norm_)。

以下是一个示例:

```python

optim_wrapper = dict(

    optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001),

    # norm_type: 使用的 p-范数的类型,这里 norm_type 为 2。

    clip_grad=dict(max_norm=35, norm_type=2))

```

### 梯度累积

当计算资源有限时,批量大小只能设置为较小的值,这可能会影响模型的性能。可以使用梯度累积来解决这个问题。我们支持 `optim_wrapper` 中的 `accumulative_counts` 选项进行梯度累积。

以下是一个示例:

```python

train_dataloader = dict(batch_size=64)

optim_wrapper = dict(

    optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001),

    accumulative_counts=4)

```

表示在训练过程中,每 4 个迭代执行一次反向传播。上述示例等价于:

```python

train_dataloader = dict(batch_size=256)

optim_wrapper = dict(

    optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001))

```

## 新增优化器或者优化器构造器

在训练中,优化参数(如学习率、动量等)通常不是固定的,而是随着迭代或周期的变化而变化。PyTorch 支持几种学习率策略,但对于复杂的策略可能不足够。在 MMAction2 中,我们提供 `param_scheduler` 来更好地控制不同参数的学习率策略。

### 配置学习率调整策略

调整学习率策略被广泛用于提高性能。我们支持大多数 PyTorch 学习率策略,包括 `ExponentialLR``LinearLR``StepLR``MultiStepLR` 等。

所有可用的学习率策略可以在[这里](https://mmaction2.readthedocs.io/en/latest/schedulers.html)找到,学习率策略的名称以 `LR` 结尾。

- **单一学习率策略**

  在大多数情况下,我们只使用一个学习策略以简化问题。例如,`MultiStepLR` 被用作 ResNet 的默认学习率策略。在这里,`param_scheduler` 是一个字典。

  ```python

  param_scheduler = dict(

      type='MultiStepLR',

      by_epoch=True,

      milestones=[100, 150],

      gamma=0.1)

  ```

  或者,我们想使用 `CosineAnnealingLR` 策略来衰减学习率:

  ```python

  param_scheduler = dict(

      type='CosineAnnealingLR',

      by_epoch=True,

      T_max=num_epochs)

  ```

- **多个学习率策略**

  在某些训练案例中,为了提高准确性,会应用多个学习率策略。例如,在早期阶段,训练容易不稳定,预热是一种减少不稳定性的技术。学习率将从一个较小的值逐渐增加到预期值,通过预热进行衰减和其他策略进行衰减。

  在 MMAction2 中,通过将所需的策略组合成 `param_scheduler` 的列表即可实现预热策略。

  以下是一些示例:

  1. 在前 50 个迭代中进行线性预热。

  ```python

    param_scheduler = [

        # 线性预热

        dict(type='LinearLR',

            start_factor=0.001,

            by_epoch=False,  # 按迭代

            end=50),  # 仅在前 50 个迭代中进行预热

        # 主要的学习率策略

        dict(type='MultiStepLR',

            by_epoch=True,

            milestones=[8, 11],

            gamma=0.1)

    ]

  ```

  2. 在前 10 个周期中进行线性预热,并在每个周期内按迭代更新学习率。

  ```python

    param_scheduler = [

        # 线性预热 [0, 10) 个周期

        dict(type='LinearLR',

            start_factor=0.001,

            by_epoch=True,

            end=10,

            convert_to_iter_based=True,  # 按迭代更新学习率

        ),

        # 在 10 个周期后使用 CosineAnnealing 策略

        dict(type='CosineAnnealingLR', by_epoch=True, begin=10)

    ]

  ```

  注意,我们在这里使用 `begin``end` 参数来指定有效范围,该范围为 \[`begin`, `end`)。范围的单位由 `by_epoch` 参数定义。如果未指定,则 `begin` 为 0,`end` 为最大周期或迭代次数。

  如果所有策略的范围都不连续,则学习率将在忽略的范围内保持不变,否则所有有效的策略将按特定阶段的顺序执行,这与 PyTorch [`ChainedScheduler`](torch.optim.lr_scheduler.ChainedScheduler) 的行为相同。

### 自定义动量策略

我们支持使用动量策略根据学习率修改优化器的动量,这可以使损失以更快的方式收敛。使用方法与学习率策略相同。

所有可用的学习率策略可以在[这里](https://mmaction2.readthedocs.io/en/latest/schedulers.html)找到,动量策略的名称以 `Momentum` 结尾。

以下是一个示例:

```python

param_scheduler = [

    # 学习率策略

    dict(type='LinearLR', ...),

    # 动量策略

    dict(type='LinearMomentum',

         start_factor=0.001,

         by_epoch=False,

         begin=0,

         end=1000)

]

```

## 添加新的优化器或构造器

本部分将修改 MMAction2 源代码或向 MMAction2 框架中添加代码,初学者可以跳过此部分。

### 添加新的优化器

在学术研究和工业实践中,可能需要使用 MMAction2 未实现的优化方法,可以通过以下方法进行添加。

#### 1. 实现一个新的优化器

假设要添加一个名为 `MyOptimizer` 的优化器,它具有参数 `a``b``c`。需要在 `mmaction/engine/optimizers` 下创建一个新文件,并在文件中实现新的优化器,例如在 `mmaction/engine/optimizers/my_optimizer.py` 中:

```python

from torch.optim import Optimizer

from mmaction.registry import OPTIMIZERS





@OPTIMIZERS.register_module()

class MyOptimizer(Optimizer):



    def __init__(self, a, b, c):

        ...



    def step(self, closure=None):

        ...

```

#### 2. 导入优化器

为了找到上述定义的模块,需要在运行时导入该模块。首先,在 `mmaction/engine/optimizers/__init__.py` 中导入该模块,将其添加到 `mmaction.engine` 包中。

```python

# In mmaction/engine/optimizers/__init__.py

...

from .my_optimizer import MyOptimizer # MyOptimizer 可能是其他类名



__all__ = [..., 'MyOptimizer']

```

在运行时,我们将自动导入 `mmaction.engine` 包,并同时注册 `MyOptimizer`#### 3. 在配置文件中指定优化器

然后,可以在配置文件的 `optim_wrapper.optimizer` 字段中使用 `MyOptimizer````python

optim_wrapper = dict(

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

```

### 添加新的优化器构造器

一些模型可能对优化有一些特定的参数设置,例如所有 `BatchNorm` 层的不同权重衰减率。

尽管我们已经可以使用[优化器教程](#参数化精细配置)中的 `optim_wrapper.paramwise_cfg` 字段来配置各种特定参数的优化器设置,但可能仍无法满足需求。

当然,你可以修改它。默认情况下,我们使用 [`DefaultOptimWrapperConstructor`](mmengine.optim.DefaultOptimWrapperConstructor) 类来处理优化器的构造。在构造过程中,它根据 `paramwise_cfg` 对不同参数的优化器设置进行细致配置,这也可以作为新优化器构造器的模板。

你可以通过添加新的优化器构造器来覆盖这些行为。

```python

# In mmaction/engine/optimizers/my_optim_constructor.py

from mmengine.optim import DefaultOptimWrapperConstructor

from mmaction.registry import OPTIM_WRAPPER_CONSTRUCTORS





@OPTIM_WRAPPER_CONSTRUCTORS.register_module()

class MyOptimWrapperConstructor:



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

        ...



    def __call__(self, model):

        ...

```

然后,导入它并几乎像[优化器教程](#添加新的优化器)中那样使用它。

1.`mmaction/engine/optimizers/__init__.py` 中导入它,将其添加到 `mmaction.engine` 包中。

   ```python

   # In mmaction/engine/optimizers/__init__.py

   ...

   from .my_optim_constructor import MyOptimWrapperConstructor



   __all__ = [..., 'MyOptimWrapperConstructor']

   ```

2. 在配置文件的 `optim_wrapper.constructor` 字段中使用 `MyOptimWrapperConstructor````python

   optim_wrapper = dict(

       constructor=dict(type='MyOptimWrapperConstructor'),

       optimizer=...,

       paramwise_cfg=...,

   )

   ```