diff --git a/docs/zh_cn/tutorials/get_started.md b/docs/zh_cn/tutorials/get_started.md index dbc8c2c22f171783f6343740e93c9db326c8a723..8e9ca6ec2dfa47b2dca92cd8d1a216230b0e9612 100644 --- a/docs/zh_cn/tutorials/get_started.md +++ b/docs/zh_cn/tutorials/get_started.md @@ -22,7 +22,7 @@ MMEngine 将算法模型è®ç»ƒã€æŽ¨ç†ã€æµ‹è¯•å’Œå¯è§†åŒ–过程ä¸çš„å„个 - [è¯„æµ‹æŒ‡æ ‡ä¸Žè¯„æµ‹å™¨ï¼ˆMetrics & Evaluator)](./metric_and_evaluator.md):评测器负责基于数æ®é›†å¯¹æ¨¡åž‹çš„é¢„æµ‹è¿›è¡Œè¯„ä¼°ã€‚è¯„æµ‹å™¨å†…è¿˜æœ‰ä¸€å±‚æŠ½è±¡æ˜¯è¯„æµ‹æŒ‡æ ‡ï¼Œè´Ÿè´£è®¡ç®—å…·ä½“çš„ä¸€ä¸ªæˆ–å¤šä¸ªè¯„æµ‹æŒ‡æ ‡ï¼ˆå¦‚å¬å›žçŽ‡ã€æ£ç¡®çŽ‡ç‰ï¼‰ã€‚ - [æ•°æ®å…ƒç´ (Data Element)](./data_element.md):评测器,模型和数æ®ä¹‹é—´äº¤æµçš„接å£ä½¿ç”¨æ•°æ®å…ƒç´ 进行å°è£…。 - [å‚数调度器(Parameter Scheduler)](./param_scheduler.md):è®ç»ƒè¿‡ç¨‹ä¸ï¼Œå¯¹å¦ä¹ 率ã€åŠ¨é‡ç‰å‚数进行动æ€è°ƒæ•´ã€‚ -- [优化器(Optimizer)](./optimizer.md):优化器负责在è®ç»ƒè¿‡ç¨‹ä¸æ‰§è¡Œåå‘ä¼ æ’优化模型。 +- [优化器(Optimizer)](./optimizer_wrapper.md):优化器负责在è®ç»ƒè¿‡ç¨‹ä¸æ‰§è¡Œåå‘ä¼ æ’优化模型。实际使用过程ä¸ä¼šè¢«ä¼˜åŒ–器å°è£…(OptimWrapper)å°è£…ä¸€å±‚ï¼Œå®žçŽ°æ¢¯åº¦ç´¯åŠ ã€æ··åˆç²¾åº¦è®ç»ƒç‰åŠŸèƒ½ã€‚ - [日志管ç†ï¼ˆLogging Modules)](./logging.md)ï¼šè´Ÿè´£ç®¡ç† Runner è¿è¡Œè¿‡ç¨‹ä¸äº§ç”Ÿçš„å„ç§æ—¥å¿—ä¿¡æ¯ã€‚å…¶ä¸æ¶ˆæ¯æž¢çº½ (MessageHub)负责实现组件与组件ã€æ‰§è¡Œå™¨ä¸Žæ‰§è¡Œå™¨ä¹‹é—´çš„æ•°æ®å…±äº«ï¼Œæ—¥å¿—处ç†å™¨ï¼ˆLog Processor)负责对日志信æ¯è¿›è¡Œå¤„ç†ï¼Œå¤„ç†åŽçš„日志会分别å‘é€ç»™æ‰§è¡Œå™¨çš„日志器(Logger)和å¯è§†åŒ–器(Visualizer)进行日志的管ç†ä¸Žå±•ç¤ºã€‚ - [é…置类(Config)](./config.md):在 OpenMMLab 算法库ä¸ï¼Œç”¨æˆ·å¯ä»¥é€šè¿‡ç¼–写 config æ¥é…ç½®è®ç»ƒã€æµ‹è¯•è¿‡ç¨‹ä»¥åŠç›¸å…³çš„组件。 - [注册器(Registry)](./registry.md):负责管ç†ç®—法库ä¸å…·æœ‰ç›¸åŒåŠŸèƒ½çš„模å—。MMEngine æ ¹æ®å¯¹ç®—法库模å—çš„æŠ½è±¡ï¼Œå®šä¹‰äº†ä¸€å¥—æ ¹æ³¨å†Œå™¨ï¼Œç®—æ³•åº“ä¸çš„注册器å¯ä»¥ç»§æ‰¿è‡ªè¿™å¥—æ ¹æ³¨å†Œå™¨ï¼Œå®žçŽ°æ¨¡å—的跨算法库调用。 diff --git a/docs/zh_cn/tutorials/optim_wrapper.md b/docs/zh_cn/tutorials/optim_wrapper.md new file mode 100644 index 0000000000000000000000000000000000000000..22924300ea45bcefe41dacc590547dbfce4ff910 --- /dev/null +++ b/docs/zh_cn/tutorials/optim_wrapper.md @@ -0,0 +1,485 @@ +# 优化器å°è£…(OptimWrapper) + +MMEngine 实现了优化器å°è£…,为用户æ供了统一的优化器访问接å£ã€‚优化器å°è£…支æŒä¸åŒçš„è®ç»ƒç–略,包括混åˆç²¾åº¦è®ç»ƒã€æ¢¯åº¦ç´¯åŠ 和梯度截æ–。用户å¯ä»¥æ ¹æ®éœ€æ±‚选择åˆé€‚çš„è®ç»ƒç–略。优化器å°è£…è¿˜å®šä¹‰äº†ä¸€å¥—æ ‡å‡†çš„å‚æ•°æ›´æ–°æµç¨‹ï¼Œç”¨æˆ·å¯ä»¥åŸºäºŽè¿™ä¸€å¥—æµç¨‹ï¼Œå®žçŽ°åŒä¸€å¥—代ç ,ä¸åŒè®ç»ƒç–略的切æ¢ã€‚ + +## 优化器å°è£… vs 优化器 + +这里我们分别基于 Pytorch 内置的优化器和 MMEngine 的优化器å°è£…进行å•ç²¾åº¦è®ç»ƒã€æ··åˆç²¾åº¦è®ç»ƒå’Œæ¢¯åº¦ç´¯åŠ ,对比二者实现上的区别。 + +### è®ç»ƒæ¨¡åž‹ + +**1.1 基于 Pytorch çš„ SGD 优化器实现å•ç²¾åº¦è®ç»ƒ** + +```python +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) +optimizer = SGD(model.parameters(), lr=0.01) +optimizer.zero_grad() + +for input, target in zip(inputs, targets): + output = model(input) + loss = F.l1_loss(output, target) + loss.backward() + optimizer.step() + optimizer.zero_grad() +``` + +**1.2 使用 MMEngine 的优化器å°è£…实现å•ç²¾åº¦è®ç»ƒ** + +```python +from mmengine.optim import OptimWrapper + +optim_wrapper = OptimWrapper(optimizer=optimizer) + +for input, target in zip(inputs, targets): + output = model(input) + loss = F.l1_loss(output, target) + optim_wrapper.update_params(loss) +``` + + + +优化器å°è£…çš„ `update_params` å®žçŽ°äº†æ ‡å‡†çš„æ¢¯åº¦è®¡ç®—ã€å‚数更新和梯度清零æµç¨‹ï¼Œå¯ä»¥ç›´æŽ¥ç”¨æ¥æ›´æ–°æ¨¡åž‹å‚数。 + +**2.1 基于 Pytorch çš„ SGD 优化器实现混åˆç²¾åº¦è®ç»ƒ** + +```python +from torch.cuda.amp import autocast + +model = model.cuda() +inputs = [torch.zeros(10, 1, 1, 1)] * 10 +targets = [torch.ones(10, 1, 1, 1)] * 10 + +for input, target in zip(inputs, targets): + with autocast(): + output = model(input.cuda()) + loss = F.l1_loss(output, target.cuda()) + loss.backward() + optimizer.step() + optimizer.zero_grad() +``` + +**2.2 基于 MMEngine çš„ 优化器å°è£…实现混åˆç²¾åº¦è®ç»ƒ** + +```python +from mmengine.optim import AmpOptimWrapper + +optim_wrapper = AmpOptimWrapper(optimizer=optimizer) + +for input, target in zip(inputs, targets): + with optim_wrapper.optim_context(model): + output = model(input.cuda()) + loss = F.l1_loss(output, target.cuda()) + optim_wrapper.update_params(loss) +``` + + + +å¼€åˆæ··åˆç²¾åº¦è®ç»ƒéœ€è¦ä½¿ç”¨ `AmpOptimWrapper`,他的 optim_context 接å£ç±»ä¼¼ `autocast`,会开å¯æ··åˆç²¾åº¦è®ç»ƒçš„上下文。除æ¤ä¹‹å¤–ä»–è¿˜èƒ½åŠ é€Ÿåˆ†å¸ƒå¼è®ç»ƒæ—¶çš„æ¢¯åº¦ç´¯åŠ ï¼Œè¿™ä¸ªæˆ‘ä»¬ä¼šåœ¨ä¸‹ä¸€ä¸ªç¤ºä¾‹ä¸ä»‹ç» + +**3.1 基于 Pytorch çš„ SGD 优化器实现混åˆç²¾åº¦è®ç»ƒå’Œæ¢¯åº¦ç´¯åŠ ** + +```python +for idx, (input, target) in enumerate(zip(inputs, targets)): + with autocast(): + output = model(input.cuda()) + loss = F.l1_loss(output, target.cuda()) + loss.backward() + if idx % 2 == 0: + optimizer.step() + optimizer.zero_grad() +``` + +**3.2 基于 MMEngine 的优化器å°è£…实现混åˆç²¾åº¦è®ç»ƒå’Œæ¢¯åº¦ç´¯åŠ ** + +```python +optim_wrapper = AmpOptimWrapper(optimizer=optimizer, accumulative_counts=2) + +for input, target in zip(inputs, targets): + with optim_wrapper.optim_context(model): + output = model(input.cuda()) + loss = F.l1_loss(output, target.cuda()) + optim_wrapper.update_params(loss) +``` + + + +我们åªéœ€è¦é…ç½® `accumulative_counts` å‚数,并调用 `update_params` 接å£å°±èƒ½å®žçŽ°æ¢¯åº¦ç´¯åŠ 的功能。除æ¤ä¹‹å¤–,分布å¼è®ç»ƒæƒ…况下,如果我们é…ç½®æ¢¯åº¦ç´¯åŠ çš„åŒæ—¶å¼€å¯äº† `optim_wrapper` 上下文,å¯ä»¥é¿å…æ¢¯åº¦ç´¯åŠ é˜¶æ®µä¸å¿…è¦çš„梯度åŒæ¥ã€‚ + +优化器å°è£…åŒæ ·æ供了更细粒度的接å£ï¼Œæ–¹ä¾¿ç”¨æˆ·å®žçŽ°ä¸€äº›è‡ªå®šä¹‰çš„å‚数更新逻辑: + +- `backward`ï¼šä¼ å…¥æŸå¤±ï¼Œç”¨äºŽè®¡ç®—å‚数梯度,。 +- `step`: åŒ `optimizer.step`,用于更新å‚数。 +- `zero_grad`: åŒ `optimizer.zero_grad`,用于å‚数的梯度。 + +我们å¯ä»¥ä½¿ç”¨ä¸Šè¿°æŽ¥å£å®žçŽ°å’Œ Pytorch 优化器相åŒçš„å‚数更新逻辑: + +```python +for idx, (input, target) in enumerate(zip(inputs, targets)): + optimizer.zero_grad() + with optim_wrapper.optim_context(model): + output = model(input.cuda()) + loss = F.l1_loss(output, target.cuda()) + optim_wrapper.backward(loss) + if idx % 2 == 0: + optim_wrapper.step() + optim_wrapper.zero_grad() +``` + +### 获å–å¦ä¹ 率/动é‡ï¼š + +优化器å°è£…æ供了 `get_lr` å’Œ `get_momentum` 接å£ç”¨äºŽèŽ·å–优化器的一个å‚数组的å¦ä¹ 率 + +```python +import torch.nn as nn +from torch.optim import SGD + +from mmengine.optim import OptimWrapper + +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]['momentum']) # 0 +print(optim_wrapper.get_lr()) # {'lr': [0.01]} +print(optim_wrapper.get_momentum()) # {'momentum': [0]} +``` + +``` +0.01 +0 +{'lr': [0.01]} +{'momentum': [0]} +``` + +### 导出/åŠ è½½çŠ¶æ€å—å…¸ + +优化器å°è£…å’Œä¼˜åŒ–å™¨ä¸€æ ·ï¼Œæ供了 `state_dict` å’Œ `load_state_dict` 接å£ï¼Œç”¨äºŽå¯¼å‡º/åŠ è½½ä¼˜åŒ–å™¨çŠ¶æ€ï¼Œå¯¹äºŽ `AmpOptimWrapper`,优化器å°è£…还会é¢å¤–导出混åˆç²¾åº¦è®ç»ƒç›¸å…³çš„å‚数: + +```python +import torch.nn as nn +from torch.optim import SGD +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_state_dict = optim_wapper.state_dict() +amp_optim_state_dict = amp_optim_wapper.state_dict() + +print(optim_state_dict) +print(amp_optim_state_dict) +optim_wapper_new = OptimWrapper(optimizer=optimizer) +amp_optim_wapper_new = AmpOptimWrapper(optimizer=optimizer) + +# åŠ è½½çŠ¶æ€å—å…¸ +amp_optim_wapper_new.load_state_dict(amp_optim_state_dict) +optim_wapper_new.load_state_dict(optim_state_dict) +``` + +``` +{'state': {}, 'param_groups': [{'lr': 0.01, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'maximize': False, 'foreach': None, 'params': [0, 1]}]} +{'state': {}, 'param_groups': [{'lr': 0.01, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'maximize': False, 'foreach': None, 'params': [0, 1]}], 'loss_scaler': {'scale': 65536.0, 'growth_factor': 2.0, 'backoff_factor': 0.5, 'growth_interval': 2000, '_growth_tracker': 0}} +``` + +### 使用多个优化器 + +考虑到生æˆå¯¹æŠ—网络之类的算法通常需è¦ä½¿ç”¨å¤šä¸ªä¼˜åŒ–器æ¥è®ç»ƒç”Ÿæˆå™¨å’Œåˆ¤åˆ«å™¨ï¼Œå› æ¤ä¼˜åŒ–器å°è£…æ供了优化器å°è£…的容器类:`OptimWrapperDict` æ¥ç®¡ç†å¤šä¸ªä¼˜åŒ–器å°è£…。`OptimWrapperDict` 以å—典的形å¼å˜å‚¨ä¼˜åŒ–器å°è£…,并å…许用户åƒå—å…¸ä¸€æ ·è®¿é—®ã€é历其ä¸çš„å…ƒç´ ï¼Œå³ä¼˜åŒ–器å°è£…实例。 + +与普通的优化器å°è£…ä¸åŒï¼Œ`OptimWrapperDict` 没有实现 `update_params`〠`optim_context`, `backward`ã€`step` ç‰æ–¹æ³•ï¼Œæ— 法被直接用于è®ç»ƒæ¨¡åž‹ã€‚我们建议直接访问 `OptimWrapperDict` 管ç†çš„优化器实例,æ¥å®žçŽ°å‚数更新逻辑。 + +ä½ æˆ–è®¸ä¼šå¥½å¥‡ï¼Œæ—¢ç„¶ `OptimWrapperDict` 没有è®ç»ƒçš„功能,那为什么ä¸ç›´æŽ¥ä½¿ç”¨ `dict` æ¥ç®¡ç†å¤šä¸ªä¼˜åŒ–器。事实上,`OptimWrapperDict` çš„æ ¸å¿ƒåŠŸèƒ½æ˜¯æ”¯æŒæ‰¹é‡å¯¼å‡º/åŠ è½½æ‰€æœ‰ä¼˜åŒ–å™¨å°è£…的状æ€å—典;支æŒèŽ·å–多个优化器å°è£…çš„å¦ä¹ 率ã€åŠ¨é‡ã€‚如果没有 `OptimWrapperDict`,`MMEngine` 就需è¦åœ¨å¾ˆå¤šä½ç½®å¯¹ä¼˜åŒ–器å°è£…çš„ç±»åž‹åš `if else` 判æ–,以获å–所有优化器å°è£…的状æ€ã€‚ + +```python +from torch.optim import SGD +import torch.nn as nn + +from mmengine.optim import OptimWrapper, OptimWrapperDict + +gen = nn.Linear(1, 1) +disc = nn.Linear(1, 1) +optimizer_gen = SGD(gen.parameters(), lr=0.01) +optimizer_disc = SGD(disc.parameters(), lr=0.01) + +optim_wapper_gen = OptimWrapper(optimizer=optimizer_gen) +optim_wapper_disc = OptimWrapper(optimizer=optimizer_disc) +optim_dict = OptimWrapperDict(gen=optim_wapper_gen, disc=optim_wapper_disc) + +print(optim_dict.get_lr()) # {'gen.lr': [0.01], 'disc.lr': [0.01]} +print(optim_dict.get_momentum()) # {'gen.momentum': [0], 'disc.momentum': [0]} +``` + +``` +{'gen.lr': [0.01], 'disc.lr': [0.01]} +{'gen.momentum': [0], 'disc.momentum': [0]} +``` + +如上例所示,`OptimWrapperDict` å¯ä»¥éžå¸¸æ–¹ä¾¿çš„导出所有优化器å°è£…çš„å¦ä¹ 率和动é‡ï¼ŒåŒæ ·çš„,优化器å°è£…也能够导出/åŠ è½½æ‰€æœ‰ä¼˜åŒ–å™¨å°è£…的状æ€å—典。 + +## 在[执行器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/runner.html)ä¸é…置优化器å°è£… + +### 简å•é…ç½® + +优化器å°è£…需è¦æŽ¥å— `optimizer` å‚æ•°ï¼Œå› æ¤æˆ‘们首先需è¦ä¸ºä¼˜åŒ–器å°è£…é…ç½® `optimizer`。 +MMEngine 会自动将 PyTorch ä¸çš„æ‰€æœ‰ä¼˜åŒ–å™¨éƒ½æ·»åŠ è¿› `OPTIMIZERS` 注册表ä¸ï¼Œç”¨æˆ·å¯ä»¥ç”¨å—典的形å¼æ¥æŒ‡å®šä¼˜åŒ–器,所有支æŒçš„ä¼˜åŒ–å™¨è§ [PyTorch 优化器列表](https://pytorch.org/docs/stable/optim.html#algorithms)。 + +以é…置一个 SGD 优化器å°è£…为例: + +```python +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001) +optim_wrapper = dict(type='OptimWrapper', optimizer=optimizer) +``` + +è¿™æ ·æˆ‘ä»¬å°±é…置好了一个优化器类型为 SGD 的优化器å°è£…,å¦ä¹ 率ã€åŠ¨é‡ç‰å‚数如é…置所示。考虑到 `OptimWrapper` ä¸ºæ ‡å‡†çš„å•ç²¾åº¦è®ç»ƒï¼Œå› æ¤æˆ‘们也å¯ä»¥ä¸é…ç½® `type` å—段: + +```python +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001) +optim_wrapper = dict(optimizer=optimizer) +``` + +è¦æƒ³å¼€å¯æ··åˆç²¾åº¦è®ç»ƒå’Œæ¢¯åº¦ç´¯åŠ ,需è¦å°† `type` 切æ¢æˆ `AmpOptimWrapper`,并指定 `accumulative_counts` å‚æ•° + +```python +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001) +optim_wrapper = dict(type='AmpOptimWrapper', optimizer=optimizer, accumulative_counts=2) +``` + +### 进阶é…ç½® + +PyTorch 的优化器支æŒå¯¹æ¨¡åž‹ä¸çš„ä¸åŒå‚数设置ä¸åŒçš„超å‚数,例如对一个分类模型的骨干(backbone)和分类头(head)设置ä¸åŒçš„å¦ä¹ 率: + +```python +from torch.optim import SGD +import torch.nn as nn + +model = nn.ModuleDict(dict(backbone=nn.Linear(1, 1), head=nn.Linear(1, 1))) +optimizer = SGD([{'params': model.backbone.parameters()}, + {'params': model.head.parameters(), 'lr': 1e-3}], + lr=0.01, + momentum=0.9) +``` + +上é¢çš„例åä¸ï¼Œæ¨¡åž‹çš„骨干部分使用了 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) 论文ä¸æ到的ä¸å¯¹æ£åˆ™åŒ–层进行æƒå€¼è¡°å‡çš„技巧。 + +具体示例如下,我们将 `ToyModel` ä¸æ‰€æœ‰æ£åˆ™åŒ–层(`head.bn`)的的æƒé‡è¡°å‡ç³»æ•°è®¾ç½®ä¸º 0: + +```python +from mmengine.optim import build_optim_wrapper +from collections import OrderedDict + +class ToyModel(nn.Module): + def __init__(self): + super().__init__() + self.backbone = nn.ModuleDict( + dict(layer0=nn.Linear(1, 1), layer1=nn.Linear(1, 1))) + self.head = nn.Sequential( + OrderedDict( + linear=nn.Linear(1, 1), + bn=nn.BatchNorm1d(1))) + + +optim_wrapper = dict( + optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001), + paramwise_cfg=dict(norm_decay_mult=0)) +optimizer = build_optim_wrapper(ToyModel(), optim_wrapper) +``` + +``` +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:lr=0.01 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.bias:lr=0.01 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.bias:lr=0.01 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.weight:weight_decay=0.0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.bias:weight_decay=0.0 +``` + +除了å¯ä»¥å¯¹æ£åˆ™åŒ–层的æƒé‡è¡°å‡è¿›è¡Œé…置外,MMEngine 的默认优化器å°è£…æž„é€ å™¨çš„ `paramwise_cfg` 还支æŒå¯¹æ›´å¤šä¸åŒç±»åž‹çš„å‚数设置超å‚系数,支æŒçš„é…置如下: + +`lr_mult`:所有å‚æ•°çš„å¦ä¹ 率系数 + +`decay_mult`:所有å‚æ•°çš„è¡°å‡ç³»æ•° + +`bias_lr_mult`:å置的å¦ä¹ 率系数(ä¸åŒ…括æ£åˆ™åŒ–层的å置以åŠå¯å˜å½¢å·ç§¯çš„ offset),默认值为 1 + +`bias_decay_mult`:å置的æƒå€¼è¡°å‡ç³»æ•°ï¼ˆä¸åŒ…括æ£åˆ™åŒ–层的å置以åŠå¯å˜å½¢å·ç§¯çš„ offset),默认值为 1 + +`norm_decay_mult`:æ£åˆ™åŒ–层æƒé‡å’Œå置的æƒå€¼è¡°å‡ç³»æ•°ï¼Œé»˜è®¤å€¼ä¸º 1 + +`dwconv_decay_mult`:Depth-wise å·ç§¯çš„æƒå€¼è¡°å‡ç³»æ•°ï¼Œé»˜è®¤å€¼ä¸º 1 + +`bypass_duplicate`:是å¦è·³è¿‡é‡å¤çš„å‚数,默认为 `False` + +`dcn_offset_lr_mult`:å¯å˜å½¢å·ç§¯ï¼ˆDeformable Convolution)的å¦ä¹ 率系数,默认值为 1 + +#### 为模型ä¸åŒéƒ¨åˆ†çš„å‚数设置ä¸åŒçš„超å‚系数 + +æ¤å¤–,与上文 PyTorch çš„ç¤ºä¾‹ä¸€æ ·ï¼Œåœ¨ MMEngine ä¸æˆ‘们也åŒæ ·å¯ä»¥å¯¹æ¨¡åž‹ä¸çš„ä»»æ„模å—设置ä¸åŒçš„超å‚,åªéœ€è¦åœ¨ `paramwise_cfg` ä¸è®¾ç½® `custom_keys` å³å¯ã€‚ + +例如我们想将 `backbone.layer0` 所有å‚æ•°çš„å¦ä¹ 率设置为 0,衰å‡ç³»æ•°è®¾ç½®ä¸º 0,`backbone` 其余å模å—çš„å¦ä¹ 率设置为 1ï¼›`head` 所欲å‚æ•°çš„å¦ä¹ 率设置为 0.01,å¯ä»¥è¿™æ ·é…置: + +```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) + })) +optimizer = build_optim_wrapper(ToyModel(), optim_wrapper) +``` + +``` +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.weight:lr=0.0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.weight:weight_decay=0.0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.weight:lr_mult=0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.weight:decay_mult=0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:lr=0.0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:weight_decay=0.0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:lr_mult=0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer0.bias:decay_mult=0 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.weight:lr=0.01 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.weight:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.weight:lr_mult=1 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.bias:lr=0.01 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- backbone.layer1.bias:lr_mult=1 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.weight:lr=0.001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.weight:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.weight:lr_mult=0.1 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.bias:lr=0.001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.linear.bias:lr_mult=0.1 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.weight:lr=0.001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.weight:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.weight:lr_mult=0.1 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.bias:lr=0.001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.bias:weight_decay=0.0001 +08/23 22:02:43 - mmengine - INFO - paramwise_options -- head.bn.bias:lr_mult=0.1 +``` + +上例ä¸ï¼Œæ¨¡åž‹çš„状æ€å—典的 `key` 如下: + +```python +for name, val in ToyModel().named_parameters(): + print(name) +``` + +``` +backbone.layer0.weight +backbone.layer0.bias +backbone.layer1.weight +backbone.layer1.bias +head.linear.weight +head.linear.bias +head.bn.weight +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 + +### è‡ªå®šä¹‰ä¼˜åŒ–å™¨æž„é€ ç–ç•¥ + +与 MMEngine ä¸çš„其他模å—ä¸€æ ·ï¼Œä¼˜åŒ–å™¨å°è£…æž„é€ å™¨ä¹ŸåŒæ ·ç”±[注册表](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html)管ç†ã€‚ +我们å¯ä»¥é€šè¿‡å®žçŽ°è‡ªå®šä¹‰çš„优化器å°è£…æž„é€ å™¨æ¥å®žçŽ°è‡ªå®šä¹‰çš„超å‚设置ç–略。 + +例如,我们想实现一个å«åš `LayerDecayOptimWrapperConstructor` 的优化器å°è£…æž„é€ å™¨ï¼Œèƒ½å¤Ÿå¯¹æ¨¡åž‹ä¸åŒæ·±åº¦çš„层自动设置递å‡çš„å¦ä¹ 率: + +```python +from mmengine.optim import DefaultOptimWrapperConstructor +from mmengine.registry import OPTIM_WRAPPER_CONSTRUCTORS +from mmengine.logging import print_log + + +@OPTIM_WRAPPER_CONSTRUCTORS.register_module(force=True) +class LayerDecayOptimWrapperConstructor(DefaultOptimWrapperConstructor): + + def __init__(self, optim_wrapper_cfg, paramwise_cfg=None): + super().__init__(optim_wrapper_cfg, paramwise_cfg=None) + self.decay_factor = paramwise_cfg.get('decay_factor', 0.5) + + super().__init__(optim_wrapper_cfg, paramwise_cfg) + + def add_params(self, params, module, prefix='' ,lr=None): + if lr is None: + lr = self.base_lr + + for name, param in module.named_parameters(recurse=False): + param_group = dict() + param_group['params'] = [param] + param_group['lr'] = lr + params.append(param_group) + full_name = f'{prefix}.{name}' if prefix else name + print_log(f'{full_name} : lr={lr}', logger='current') + + for name, module in module.named_children(): + chiled_prefix = f'{prefix}.{name}' if prefix else name + self.add_params( + params, module, chiled_prefix, lr=lr * self.decay_factor) + + +class ToyModel(nn.Module): + + def __init__(self) -> None: + super().__init__() + self.layer = nn.ModuleDict(dict(linear=nn.Linear(1, 1))) + self.linear = nn.Linear(1, 1) + + +model = ToyModel() + +optim_wrapper = dict( + optimizer=dict(type='SGD', lr=0.01, weight_decay=0.0001), + paramwise_cfg=dict(decay_factor=0.5), + constructor='LayerDecayOptimWrapperConstructor') + +optimizer = build_optim_wrapper(model, optim_wrapper) +``` + +``` +08/23 22:20:26 - mmengine - INFO - layer.linear.weight : lr=0.0025 +08/23 22:20:26 - mmengine - INFO - layer.linear.bias : lr=0.0025 +08/23 22:20:26 - mmengine - INFO - linear.weight : lr=0.005 +08/23 22:20:26 - mmengine - INFO - linear.bias : lr=0.005 +``` + +`add_params` 被第一次调用时,`params` å‚数为空列表(`list`),`module` 为模型(`model`)。详细的é‡è½½è§„则å‚[考优化器å°è£…æž„é€ å™¨æ–‡æ¡£](mmengine.optim.DefaultOptimWrapperConstructor)。 + +ç±»ä¼¼åœ°ï¼Œå¦‚æžœæƒ³æž„é€ å¤šä¸ªä¼˜åŒ–å™¨ï¼Œä¹Ÿéœ€è¦å®žçŽ°è‡ªå®šä¹‰çš„æž„é€ å™¨ï¼š + +```python +@OPTIM_WRAPPER_CONSTRUCTORS.register_module() +class MultipleOptimiWrapperConstructor: + ... +``` + +### 在è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´è¶…å‚ + +优化器ä¸çš„超å‚æ•°åœ¨æž„é€ æ—¶åªèƒ½è®¾ç½®ä¸ºä¸€ä¸ªå®šå€¼ï¼Œä»…仅使用优化器å°è£…,并ä¸èƒ½åœ¨è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´å¦ä¹ 率ç‰å‚数。 +在 MMEngine ä¸ï¼Œæˆ‘们实现了å‚数调度器(Parameter Scheduler),以便能够在è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´å‚数。关于å‚数调度器的用法请è§[优化器å‚数调整ç–ç•¥](./param_scheduler.md) diff --git a/docs/zh_cn/tutorials/optimizer.md b/docs/zh_cn/tutorials/optimizer.md deleted file mode 100644 index 6980a59d186e3c0f27767bd924e55447e422fb0b..0000000000000000000000000000000000000000 --- a/docs/zh_cn/tutorials/optimizer.md +++ /dev/null @@ -1,127 +0,0 @@ -# 优化器(Optimizer) - -在模型è®ç»ƒè¿‡ç¨‹ä¸ï¼Œæˆ‘们需è¦ä½¿ç”¨ä¼˜åŒ–算法对模型的å‚数进行优化。在 PyTorch çš„ `torch.optim` ä¸åŒ…å«äº†å„ç§ä¼˜åŒ–算法的实现,这些优化算法的类被称为优化器。 -在 PyTorch ä¸ï¼Œç”¨æˆ·å¯ä»¥é€šè¿‡æž„建一个优化器对象æ¥ä¼˜åŒ–模型的å‚数,下é¢æ˜¯ä¸€ä¸ªç®€å•çš„例å: - -```python -optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=0.0001) - -for input, target in dataset: - optimizer.zero_grad() - output = model(input) - loss = loss_fn(output, target) - loss.backward() - optimizer.step() -``` - -关于 PyTorch 优化器的详细介ç»å¯ä»¥å‚考 [PyTorch 优化器文档](https://pytorch.org/docs/stable/optim.html#) - -MMEngine 支æŒæ‰€æœ‰çš„ PyTorch 优化器,用户å¯ä»¥ç›´æŽ¥æž„建 PyTorch ä¼˜åŒ–å™¨å¯¹è±¡å¹¶å°†å®ƒä¼ ç»™[执行器(Runner)](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/runner.html) 。 -å’Œ PyTorch 文档ä¸æ‰€ç»™ç¤ºä¾‹ä¸åŒï¼ŒMMEngine ä¸é€šå¸¸ä¸éœ€è¦æ‰‹åŠ¨å®žçŽ°è®ç»ƒå¾ªçŽ¯ä»¥åŠè°ƒç”¨` optimizer.step()`,执行器会自动对æŸå¤±å‡½æ•°è¿›è¡Œåå‘ä¼ æ’并调用优化器的 `step` 方法更新模型å‚数。 - -åŒæ—¶ï¼Œæˆ‘们也支æŒé€šè¿‡é…置文件从注册器ä¸æž„建优化器。更进一æ¥çš„,我们æä¾›äº†ä¼˜åŒ–å™¨æž„é€ å™¨ï¼ˆoptimizer constructor)æ¥å¯¹æ¨¡åž‹çš„优化进行更细粒度的调整。 - -## 使用é…置文件构建优化器 - -MMEngine 会自动将 PyTorch ä¸çš„æ‰€æœ‰ä¼˜åŒ–å™¨éƒ½æ·»åŠ è¿› `OPTIMIZERS` 注册表ä¸ï¼Œç”¨æˆ·å¯ä»¥é€šè¿‡è®¾ç½®é…置文件ä¸çš„ `optimizer` å—段æ¥æŒ‡å®šä¼˜åŒ–器,所有支æŒçš„ä¼˜åŒ–å™¨è§ [PyTorch 优化器列表](https://pytorch.org/docs/stable/optim.html#algorithms)。 - -以é…置一个 SGD 优化器为例: - -```python -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001) -``` - -我们åªéœ€è¦æŒ‡å®š `optimizer` å—段ä¸çš„ `type` 为 SGD, 并设置å¦ä¹ 率ç‰å‚æ•°ï¼Œæ‰§è¡Œå™¨ä¼šæ ¹æ®æ¤å—段以åŠæ‰§è¡Œå™¨ä¸çš„模型å‚数自动构建优化器。 - -## ç»†ç²’åº¦è°ƒæ•´æ¨¡åž‹è¶…å‚ - -PyTorch 的优化器支æŒå¯¹æ¨¡åž‹ä¸çš„ä¸åŒå‚数设置ä¸åŒçš„超å‚数,例如对一个分类模型的骨干(backbone)和分类头(head)设置ä¸åŒçš„å¦ä¹ 率: - -```python -optim.SGD([ - {'params': model.backbone.parameters()}, - {'params': model.head.parameters(), 'lr': 1e-3} - ], lr=0.01, momentum=0.9) -``` - -上é¢çš„例åä¸ï¼Œæ¨¡åž‹çš„骨干部分使用了 0.01 å¦ä¹ 率,而模型的头部则使用了 1e-3 å¦ä¹ 率。 -用户å¯ä»¥å°†æ¨¡åž‹çš„ä¸åŒéƒ¨åˆ†å‚数和对应的超å‚组æˆä¸€ä¸ªå—å…¸çš„åˆ—è¡¨ä¼ ç»™ä¼˜åŒ–å™¨ï¼Œæ¥å®žçŽ°å¯¹æ¨¡åž‹ä¼˜åŒ–的细粒度调整。 - -在 MMEngine ä¸ï¼Œæˆ‘ä»¬é€šè¿‡ä¼˜åŒ–å™¨æž„é€ å™¨ï¼ˆoptimizer 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) 论文ä¸æ到的ä¸å¯¹æ£åˆ™åŒ–层进行æƒå€¼è¡°å‡çš„技巧。 - -示例: - -```python -optimizer = dict(type='SGD', - lr=0.01, - weight_decay=0.0001, - paramwise_cfg=dict(norm_decay_mult=0)) -``` - -除了å¯ä»¥å¯¹å置的æƒé‡è¡°å‡è¿›è¡Œé…置外,MMEngine çš„é»˜è®¤ä¼˜åŒ–å™¨æž„é€ å™¨çš„ `paramwise_cfg` 还支æŒå¯¹æ›´å¤šä¸åŒç±»åž‹çš„å‚数设置超å‚系数,支æŒçš„é…置如下: - -`bias_lr_mult`:å置的å¦ä¹ 率系数(ä¸åŒ…括æ£åˆ™åŒ–层的å置以åŠå¯å˜å½¢å·ç§¯çš„ offset),默认值为 1 - -`bias_decay_mult`:å置的æƒå€¼è¡°å‡ç³»æ•°ï¼ˆä¸åŒ…括æ£åˆ™åŒ–层的å置以åŠå¯å˜å½¢å·ç§¯çš„ offset),默认值为 1 - -`norm_decay_mult`:æ£åˆ™åŒ–层æƒé‡å’Œå置的æƒå€¼è¡°å‡ç³»æ•°ï¼Œé»˜è®¤å€¼ä¸º 1 - -`dwconv_decay_mult`:Depth-wise å·ç§¯çš„æƒå€¼è¡°å‡ç³»æ•°ï¼Œé»˜è®¤å€¼ä¸º 1 - -`bypass_duplicate`:是å¦è·³è¿‡é‡å¤çš„å‚数,默认为 `False` - -`dcn_offset_lr_mult`:å¯å˜å½¢å·ç§¯ï¼ˆDeformable Convolution)的å¦ä¹ 率系数,默认值为 1 - -### 为模型ä¸åŒéƒ¨åˆ†çš„å‚数设置ä¸åŒçš„超å‚系数 - -æ¤å¤–,与上文 PyTorch çš„ç¤ºä¾‹ä¸€æ ·ï¼Œåœ¨ MMEngine ä¸æˆ‘们也åŒæ ·å¯ä»¥å¯¹æ¨¡åž‹ä¸çš„ä»»æ„模å—设置ä¸åŒçš„超å‚,åªéœ€è¦åœ¨ `paramwise_cfg` ä¸è®¾ç½® `custom_keys` å³å¯ï¼š - -```python -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), - } - )) -``` - -上é¢çš„é…置文件实现了对模型的骨干第一层的å¦ä¹ 率和æƒé‡è¡°å‡è®¾ç½®ä¸º 0,骨干的其余部分部分使用 0.01 å¦ä¹ 率,而对模型的头部则使用 1e-3 å¦ä¹ 率。 - -### è¿›é˜¶ç”¨æ³•ï¼šå®žçŽ°è‡ªå®šä¹‰çš„ä¼˜åŒ–å™¨æž„é€ å™¨ - -与 MMEngine ä¸çš„其他模å—ä¸€æ ·ï¼Œä¼˜åŒ–å™¨æž„é€ å™¨ä¹ŸåŒæ ·ç”± [注册表](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html) æ¥ç®¡ç†ã€‚ -用户å¯ä»¥å®žçŽ°è‡ªå·±çš„ä¼˜åŒ–å™¨æž„é€ ç–ç•¥æ¥å®žçŽ°è‡ªå®šä¹‰çš„超å‚设置ç–ç•¥ï¼Œå¹¶æ·»åŠ è¿› `OPTIMIZER_CONSTRUCTORS` 注册表ä¸ã€‚ - -例如,我们想实现一个å«åš`LayerDecayOptimizerConstructor`çš„ä¼˜åŒ–å™¨æž„é€ å™¨ï¼Œæ¥å®žçŽ°å¯¹æ¨¡åž‹çš„ä¸åŒæ·±åº¦çš„层自动设置递å‡çš„å¦ä¹ 率。 -我们å¯ä»¥é€šè¿‡ç»§æ‰¿ `DefaultOptimizerConstructor` æ¥å®žçŽ°è¿™ä¸€ç–ç•¥ï¼Œå¹¶å°†å…¶æ·»åŠ è¿›æ³¨å†Œè¡¨ä¸ï¼š - -```python -@OPTIMIZER_CONSTRUCTORS.register_module() -class LayerDecayOptimizerConstructor(DefaultOptimizerConstructor): - def add_params(self, params, module, prefix='', is_dcn_module=None): - ... -``` - -然åŽå°†ä¼˜åŒ–器é…置文件ä¸çš„ `constructor` å—段设置为类åæ¥æŒ‡å®šä½¿ç”¨è¿™ä¸ªè‡ªå®šä¹‰çš„ä¼˜åŒ–å™¨æž„é€ å™¨ï¼š - -```python -optimizer = dict(type='SGD', - lr=0.01, - weight_decay=0.0001, - constructor='LayerDecayOptimizerConstructor') -``` - -## 在è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´è¶…å‚ - -优化器ä¸çš„超å‚æ•°åœ¨æž„é€ æ—¶åªèƒ½è®¾ç½®ä¸ºä¸€ä¸ªå®šå€¼ï¼Œä»…仅使用优化器,并ä¸èƒ½åœ¨è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´å¦ä¹ 率ç‰å‚数。 -在 MMEngine ä¸ï¼Œæˆ‘们实现了å‚数调度器(Parameter Scheduler),以便能够在è®ç»ƒè¿‡ç¨‹ä¸è°ƒæ•´å‚数。关于å‚数调度器的用法请è§[优化器å‚数调整ç–ç•¥](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html)