diff --git a/docs/zh_cn/tutorials/param_scheduler.md b/docs/zh_cn/tutorials/param_scheduler.md index 86b16034bcb6c285cab3365e05e7d019b4927e36..e18cb6aa4610f074c16e851c4d9eaa466230d773 100644 --- a/docs/zh_cn/tutorials/param_scheduler.md +++ b/docs/zh_cn/tutorials/param_scheduler.md @@ -4,7 +4,12 @@ ## å‚数调度器的使用 -这里我们先简å•ä»‹ç»ä¸€ä¸‹å¦‚何使用 PyTorch 内置的å¦ä¹ 率调度器æ¥è¿›è¡Œå¦ä¹ 率的调整。下é¢æ˜¯å‚考 [PyTorch 官方文档](https://pytorch.org/docs/stable/optim.html)实现的一个例åï¼Œæˆ‘ä»¬æž„é€ ä¸€ä¸ª [ExponentialLR](mmengine.optim.ExponentialLR),并且在æ¯ä¸ª epoch 结æŸåŽè°ƒç”¨ `scheduler.step()`ï¼Œå®žçŽ°äº†éš epoch 指数下é™çš„å¦ä¹ 率调整ç–略。 +我们先简å•ä»‹ç»ä¸€ä¸‹å¦‚何使用 PyTorch 内置的å¦ä¹ 率调度器æ¥è¿›è¡Œå¦ä¹ 率的调整: + +<details> +<summary>如何使用 PyTorch 内置的å¦ä¹ 率调度器调整å¦ä¹ 率</summary> + +下é¢æ˜¯å‚考 [PyTorch 官方文档](https://pytorch.org/docs/stable/optim.html) 实现的一个例åï¼Œæˆ‘ä»¬æž„é€ ä¸€ä¸ª [ExponentialLR](mmengine.optim.ExponentialLR),并且在æ¯ä¸ª epoch 结æŸåŽè°ƒç”¨ `scheduler.step()`ï¼Œå®žçŽ°äº†éš epoch 指数下é™çš„å¦ä¹ 率调整ç–略。 ```python import torch @@ -26,7 +31,9 @@ for epoch in range(10): scheduler.step() ``` -在 MMEngine ä¸ï¼Œæˆ‘们支æŒå¤§éƒ¨åˆ† PyTorch ä¸çš„å¦ä¹ 率调度器,例如 `ExponentialLR`,`LinearLR`,`StepLR`,`MultiStepLR` ç‰ï¼Œä½¿ç”¨æ–¹å¼ä¹ŸåŸºæœ¬ä¸€è‡´ï¼Œæ‰€æœ‰æ”¯æŒçš„调度器è§[调度器接å£æ–‡æ¡£](https://mmengine.readthedocs.io/zh_CN/latest/api/optim.html#scheduler)。åŒæ—¶å¢žåŠ 了对动é‡çš„调整,在类åä¸å°† `LR` 替æ¢æˆ `Momentum` å³å¯ï¼Œä¾‹å¦‚ `ExponentialMomentum`,`LinearMomentum`。更进一æ¥åœ°ï¼Œæˆ‘们实现了通用的å‚数调度器 ParamScheduler,用于调整优化器的ä¸çš„其他å‚数,包括 weight_decay ç‰ã€‚这个特性å¯ä»¥å¾ˆæ–¹ä¾¿åœ°é…置一些新算法ä¸å¤æ‚的调整ç–略。 +</details> + +在 `mmengine.optim.scheduler` ä¸ï¼Œæˆ‘们支æŒå¤§éƒ¨åˆ† PyTorch ä¸çš„å¦ä¹ 率调度器,例如 `ExponentialLR`,`LinearLR`,`StepLR`,`MultiStepLR` ç‰ï¼Œä½¿ç”¨æ–¹å¼ä¹ŸåŸºæœ¬ä¸€è‡´ï¼Œæ‰€æœ‰æ”¯æŒçš„调度器è§[调度器接å£æ–‡æ¡£](https://mmengine.readthedocs.io/zh_CN/latest/api/optim.html#scheduler)。åŒæ—¶å¢žåŠ 了对动é‡çš„调整,在类åä¸å°† `LR` 替æ¢æˆ `Momentum` å³å¯ï¼Œä¾‹å¦‚ `ExponentialMomentum`,`LinearMomentum`。更进一æ¥åœ°ï¼Œæˆ‘们实现了通用的å‚数调度器 ParamScheduler,用于调整优化器的ä¸çš„其他å‚数,包括 weight_decay ç‰ã€‚这个特性å¯ä»¥å¾ˆæ–¹ä¾¿åœ°é…置一些新算法ä¸å¤æ‚的调整ç–略。 å’Œ PyTorch 文档ä¸æ‰€ç»™ç¤ºä¾‹ä¸åŒï¼ŒMMEngine ä¸é€šå¸¸ä¸éœ€è¦æ‰‹åŠ¨æ¥å®žçŽ°è®ç»ƒå¾ªçŽ¯ä»¥åŠè°ƒç”¨ `optimizer.step()`,而是在执行器(Runner)ä¸å¯¹è®ç»ƒæµç¨‹è¿›è¡Œè‡ªåŠ¨ç®¡ç†ï¼ŒåŒæ—¶é€šè¿‡ `ParamSchedulerHook` æ¥æŽ§åˆ¶å‚数调度器的执行。 @@ -35,18 +42,30 @@ for epoch in range(10): 如果整个è®ç»ƒè¿‡ç¨‹åªéœ€è¦ä½¿ç”¨ä¸€ä¸ªå¦ä¹ 率调度器, 那么和 PyTorch 自带的å¦ä¹ 率调度器没有差异。 ```python +# 基于手动构建å¦ä¹ 率调度器的例å +from torch.optim import SGD +from mmengine.runner import Runner from mmengine.optim.scheduler import MultiStepLR optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9) -scheduler = MultiStepLR(optimizer, milestones=[8, 11], gamma=0.1) +param_scheduler = MultiStepLR(optimizer, milestones=[8, 11], gamma=0.1) + +runner = Runner( + model=model, + optim_wrapper=dict( + optimizer=optimizer), + param_scheduler=param_scheduler, + ... + ) ```  -如果é…åˆæ³¨å†Œå™¨å’Œé…置文件使用的è¯ï¼Œæˆ‘们å¯ä»¥è®¾ç½®é…置文件ä¸çš„ `scheduler` å—段æ¥æŒ‡å®šä¼˜åŒ–器, 执行器(Runnerï¼‰ä¼šæ ¹æ®æ¤å—段以åŠæ‰§è¡Œå™¨ä¸çš„优化器自动构建å¦ä¹ 率调度器: +如果é…åˆæ³¨å†Œå™¨å’Œé…置文件使用的è¯ï¼Œæˆ‘们å¯ä»¥è®¾ç½®é…置文件ä¸çš„ `param_scheduler` å—段æ¥æŒ‡å®šè°ƒåº¦å™¨, 执行器(Runnerï¼‰ä¼šæ ¹æ®æ¤å—段以åŠæ‰§è¡Œå™¨ä¸çš„优化器自动构建å¦ä¹ 率调度器: ```python -scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0.1) +# 在é…置文件ä¸è®¾ç½®å¦ä¹ 率调度器å—段 +param_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 å€ã€‚ @@ -54,28 +73,28 @@ scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0. 当修改了å¦ä¹ 率调整频率åŽï¼Œè°ƒåº¦å™¨ä¸ä¸Žè®¡æ•°ç›¸å…³è®¾ç½®çš„å«ä¹‰ä¹Ÿä¼šç›¸åº”被改å˜ã€‚当 `by_epoch=True` 时,milestones ä¸çš„æ•°å—表示在哪些轮次进行å¦ä¹ 率衰å‡ï¼Œè€Œå½“ `by_epoch=False` æ—¶åˆ™è¡¨ç¤ºåœ¨è¿›è¡Œåˆ°ç¬¬å‡ æ¬¡è¿ä»£æ—¶è¿›è¡Œå¦ä¹ 率衰å‡ã€‚下é¢æ˜¯ä¸€ä¸ªæŒ‰ç…§è¿ä»£æ¬¡æ•°è¿›è¡Œè°ƒæ•´çš„例å,在第 600 å’Œ 800 次è¿ä»£ç»“æŸæ—¶ï¼Œå¦ä¹ 率将会被调整为原æ¥çš„ 0.1 å€ã€‚ ```python -scheduler = dict(type='MultiStepLR', by_epoch=False, milestones=[600, 800], gamma=0.1) +param_scheduler = dict(type='MultiStepLR', by_epoch=False, milestones=[600, 800], gamma=0.1) ```  -若用户希望在é…置调度器时按轮次填写å‚æ•°çš„åŒæ—¶ä½¿ç”¨åŸºäºŽè¿ä»£çš„更新频率,MMEngine 的调度器也æ供了自动æ¢ç®—çš„æ–¹å¼ã€‚用户å¯ä»¥è°ƒç”¨ build_iter_from_epoch 方法,并æä¾›æ¯ä¸ªè®ç»ƒè½®æ¬¡çš„è¿ä»£æ¬¡æ•°ï¼Œå³å¯æž„é€ æŒ‰è¿ä»£æ¬¡æ•°æ›´æ–°çš„调度器对象: +若用户希望在é…置调度器时按轮次填写å‚æ•°çš„åŒæ—¶ä½¿ç”¨åŸºäºŽè¿ä»£çš„更新频率,MMEngine 的调度器也æ供了自动æ¢ç®—çš„æ–¹å¼ã€‚用户å¯ä»¥è°ƒç”¨ `build_iter_from_epoch` 方法,并æä¾›æ¯ä¸ªè®ç»ƒè½®æ¬¡çš„è¿ä»£æ¬¡æ•°ï¼Œå³å¯æž„é€ æŒ‰è¿ä»£æ¬¡æ•°æ›´æ–°çš„调度器对象: ```python epoch_length = len(train_dataloader) -scheduler = MultiStepLR.build_iter_from_epoch(optimizer, milestones=[8, 11], gamma=0.1, epoch_length=epoch_length) +param_scheduler = MultiStepLR.build_iter_from_epoch(optimizer, milestones=[8, 11], gamma=0.1, epoch_length=epoch_length) ``` 如果使用é…置文件构建调度器,åªéœ€è¦åœ¨é…ç½®ä¸åŠ å…¥ `convert_to_iter_based=True`,执行器会自动调用 `build_iter_from_epoch` 将基于轮次的é…置文件转æ¢ä¸ºåŸºäºŽè¿ä»£æ¬¡æ•°çš„调度器对象: ```python -scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0.1, convert_to_iter_based=True) +param_scheduler = dict(type='MultiStepLR', by_epoch=True, milestones=[8, 11], gamma=0.1, convert_to_iter_based=True) ``` 为了能直观感å—这两ç§æ¨¡å¼çš„区别,我们这里å†ä¸¾ä¸€ä¸ªä¾‹å。下é¢æ˜¯ä¸€ä¸ªæŒ‰è½®æ¬¡æ›´æ–°çš„余弦退ç«ï¼ˆCosineAnnealing)å¦ä¹ 率调度器,å¦ä¹ 率仅在æ¯ä¸ªè½®æ¬¡ç»“æŸåŽè¢«ä¿®æ”¹ï¼š ```python -scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12) +param_scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12) ```  @@ -83,7 +102,7 @@ scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12) 而在使用自动æ¢ç®—åŽï¼Œå¦ä¹ 率会在æ¯æ¬¡è¿ä»£åŽè¢«ä¿®æ”¹ã€‚从下图å¯ä»¥çœ‹å‡ºï¼Œå¦ä¹ 率的å˜åŒ–更为平滑。 ```python -scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12, convert_to_iter_based=True) +param_scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12, convert_to_iter_based=True) ```  @@ -95,7 +114,7 @@ scheduler = dict(type='CosineAnnealingLR', by_epoch=True, T_max=12, convert_to_i MMEngine 支æŒç»„åˆå¤šä¸ªè°ƒåº¦å™¨ä¸€èµ·ä½¿ç”¨ï¼Œåªéœ€å°†é…置文件ä¸çš„ `scheduler` å—段修改为一组调度器é…置的列表,SchedulerStepHook å¯ä»¥è‡ªåŠ¨å¯¹è°ƒåº¦å™¨åˆ—表进行处ç†ã€‚下é¢çš„例å便实现了å¦ä¹ 率预çƒã€‚ ```python -scheduler = [ +param_scheduler = [ # 线性å¦ä¹ 率预çƒè°ƒåº¦å™¨ dict(type='LinearLR', start_factor=0.001, @@ -117,7 +136,7 @@ scheduler = [ 这里å†ä¸¾ä¸€ä¸ªä¾‹å: ```python -scheduler = [ +param_scheduler = [ # 在 [0, 100) è¿ä»£æ—¶ä½¿ç”¨çº¿æ€§å¦ä¹ 率 dict(type='LinearLR', start_factor=0.001, @@ -137,19 +156,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) 一致)。 +在一般情况下,我们推è用户在è®ç»ƒçš„ä¸åŒé˜¶æ®µä½¿ç”¨ä¸åŒçš„å¦ä¹ 率调度ç–ç•¥æ¥é¿å…调度器的生效区间å‘生é‡å 。如果确实需è¦å°†ä¸¤ä¸ªè°ƒåº¦å™¨å åŠ ä½¿ç”¨ï¼Œåˆ™éœ€è¦å分å°å¿ƒï¼Œé¿å…å¦ä¹ 率的调整与预期ä¸ç¬¦ã€‚ ## 如何调整其他å‚æ•° ### åŠ¨é‡ -å’Œå¦ä¹ çŽ‡ä¸€æ ·, 动é‡ä¹Ÿæ˜¯ä¼˜åŒ–器å‚数组ä¸ä¸€ç»„å¯ä»¥è°ƒåº¦çš„å‚数。动é‡è°ƒåº¦å™¨ï¼ˆmomentum scheduler)的使用方法和å¦ä¹ çŽ‡è°ƒåº¦å™¨å®Œå…¨ä¸€æ ·ã€‚åŒæ ·ä¹Ÿåªéœ€è¦å°†åŠ¨é‡è°ƒåº¦å™¨çš„é…ç½®æ·»åŠ è¿›é…置文件ä¸çš„ `scheduler` å—段的列表ä¸å³å¯ã€‚ +å’Œå¦ä¹ çŽ‡ä¸€æ ·, 动é‡ä¹Ÿæ˜¯ä¼˜åŒ–器å‚数组ä¸ä¸€ç»„å¯ä»¥è°ƒåº¦çš„å‚数。 动é‡è°ƒåº¦å™¨ï¼ˆmomentum scheduler)的使用方法和å¦ä¹ çŽ‡è°ƒåº¦å™¨å®Œå…¨ä¸€æ ·ã€‚åŒæ ·ä¹Ÿåªéœ€è¦å°†åŠ¨é‡è°ƒåº¦å™¨çš„é…ç½®æ·»åŠ è¿›é…置文件ä¸çš„ `param_scheduler` å—段的列表ä¸å³å¯ã€‚ 示例: ```python -scheduler = [ - # å¦ä¹ 率调度器 +param_scheduler = [ + # the lr scheduler dict(type='LinearLR', ...), # 动é‡è°ƒåº¦å™¨ dict(type='LinearMomentum', @@ -167,7 +188,7 @@ MMEngine 还æ供了一组通用的å‚数调度器用于调度优化器的 `par 下é¢æ˜¯ä¸€ä¸ªé€šè¿‡è‡ªå®šä¹‰å‚æ•°åæ¥è°ƒåº¦çš„例å: ```python -scheduler = [ +param_scheduler = [ dict(type='LinearParamScheduler', param_name='lr', # 调度 `optimizer.param_groups` ä¸å为 'lr' çš„å˜é‡ start_factor=0.001, @@ -182,7 +203,7 @@ scheduler = [ 除了动é‡ä¹‹å¤–,用户也å¯ä»¥å¯¹ `optimizer.param_groups` ä¸çš„其他å‚æ•°å进行调度,å¯è°ƒåº¦çš„å‚æ•°å–决于所使用的优化器。例如,当使用带 `weight_decay` çš„ SGD 优化器时,å¯ä»¥æŒ‰ç…§ä»¥ä¸‹ç¤ºä¾‹å¯¹è°ƒæ•´ `weight_decay`: ```python -scheduler = [ +param_scheduler = [ dict(type='LinearParamScheduler', param_name='weight_decay', # 调度 `optimizer.param_groups` ä¸å为 'weight_decay' çš„å˜é‡ start_factor=0.001,