From 53fe85cc816f2471c1c65c5b443287940b5856ce Mon Sep 17 00:00:00 2001 From: RangiLyu <lyuchqi@gmail.com> Date: Mon, 14 Feb 2022 16:17:35 +0800 Subject: [PATCH] [Doc] Add scheduler user doc. (#2) * [Doc] Add scheduler user doc. * reslove comments * add chinese doc * add update en doc * resolve comments * resolve comments * update parameter scheduler * resolve comments * tmp remove en doc * resolve comments * resolve comments * resolve comments Co-authored-by: Kai Chen <chenkaidev@gmail.com> --- docs/zh_cn/tutorials/param_scheduler.md | 150 ++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 docs/zh_cn/tutorials/param_scheduler.md diff --git a/docs/zh_cn/tutorials/param_scheduler.md b/docs/zh_cn/tutorials/param_scheduler.md new file mode 100644 index 00000000..5c0fa530 --- /dev/null +++ b/docs/zh_cn/tutorials/param_scheduler.md @@ -0,0 +1,150 @@ +# 优化器å‚数调整ç–略(Parameter Scheduler) + +在模型è®ç»ƒè¿‡ç¨‹ä¸ï¼Œæˆ‘们往往ä¸æ˜¯é‡‡ç”¨å›ºå®šçš„优化å‚数,例如å¦ä¹ 率ç‰ï¼Œä¼šéšç€è®ç»ƒè½®æ•°çš„å¢žåŠ è¿›è¡Œè°ƒæ•´ã€‚æœ€ç®€å•å¸¸è§çš„å¦ä¹ 率调整ç–略就是阶梯å¼ä¸‹é™ï¼Œä¾‹å¦‚æ¯éš”一段时间将å¦ä¹ 率é™ä½Žä¸ºåŽŸæ¥çš„å‡ åˆ†ä¹‹ä¸€ã€‚PyTorch ä¸æœ‰å¦ä¹ 率调度器 LRScheduler æ¥å¯¹å„ç§ä¸åŒçš„å¦ä¹ 率调整方å¼è¿›è¡ŒæŠ½è±¡ï¼Œä½†æ”¯æŒä»ç„¶æ¯”较有é™ï¼Œåœ¨ MMEngine ä¸ï¼Œæˆ‘们对其进行了拓展,实现了更通用的å‚数调度器,å¯ä»¥å¯¹å¦ä¹ 率ã€åŠ¨é‡ç‰ä¼˜åŒ–器相关的å‚数进行调整,并且支æŒå¤šä¸ªè°ƒåº¦å™¨è¿›è¡Œç»„åˆï¼Œåº”用更å¤æ‚的调度ç–略。 + +## å‚数调度器的使用 + +这里我们先简å•ä»‹ç»ä¸€ä¸‹å¦‚何使用 PyTorch 内置的å¦ä¹ 率调度器æ¥è¿›è¡Œå¦ä¹ 率的调整。下é¢æ˜¯ [PyTorch 官方文档](https://pytorch.org/docs/stable/optim.html) ä¸çš„一个例åï¼Œæˆ‘ä»¬æž„é€ ä¸€ä¸ª ExponentialLR,并且在æ¯ä¸ª epoch 结æŸåŽè°ƒç”¨ `scheduler.step()`ï¼Œå®žçŽ°äº†éš epoch 指数下é™çš„å¦ä¹ 率调整ç–略。 + +```python +model = [Parameter(torch.randn(2, 2, requires_grad=True))] +optimizer = SGD(model, 0.1) +scheduler = ExponentialLR(optimizer, gamma=0.9) + +for epoch in range(20): + for input, target in dataset: + optimizer.zero_grad() + output = model(input) + loss = loss_fn(output, target) + loss.backward() + optimizer.step() + scheduler.step() +``` + +在 MMEngine ä¸ï¼Œæˆ‘们支æŒå¤§éƒ¨åˆ† PyTorch ä¸çš„å¦ä¹ 率调度器,例如 `ExponentialLR`,`LinearLR`,`StepLR`,`MultiStepLR` ç‰ï¼Œä½¿ç”¨æ–¹å¼ä¹ŸåŸºæœ¬ä¸€è‡´ï¼Œæ‰€æœ‰æ”¯æŒçš„调度器è§[调度器接å£æ–‡æ¡£](TODO)。åŒæ—¶å¢žåŠ 了对动é‡çš„调整,在类åä¸å°† `LR` 替æ¢æˆ `Momentum` å³å¯ï¼Œä¾‹å¦‚ `ExponentialMomentum`,`LinearMomentum`。更进一æ¥åœ°ï¼Œæˆ‘们实现了通用的å‚数调度器 ParamScheduler,用于调整优化器的ä¸çš„其他å‚数,包括 weight_decay ç‰ã€‚这个特性å¯ä»¥å¾ˆæ–¹ä¾¿åœ°é…置一些新算法ä¸å¤æ‚的调整ç–略。 + +å’Œ PyTorch 文档ä¸æ‰€ç»™ç¤ºä¾‹ä¸åŒï¼ŒMMEngine ä¸é€šå¸¸ä¸éœ€è¦æ‰‹åŠ¨æ¥å®žçŽ°è®ç»ƒå¾ªçŽ¯ä»¥åŠè°ƒç”¨ `optimizer.step()`,而是在执行器(Runner)ä¸å¯¹è®ç»ƒæµç¨‹è¿›è¡Œè‡ªåŠ¨ç®¡ç†ï¼ŒåŒæ—¶é€šè¿‡ Hook(例如 `SchedulerStepHook`)æ¥æŽ§åˆ¶å‚数调度器的执行。 + +### 使用å•ä¸€çš„å¦ä¹ 率调度器 + +如果整个è®ç»ƒè¿‡ç¨‹åªéœ€è¦ä½¿ç”¨ä¸€ä¸ªå¦ä¹ 率调度器, 那么和 PyTorch 自带的å¦ä¹ 率调度器没有差异。 + +```python +optimizer = SGD(model, 0.1) +scheduler = MultiStepLR(optimizer, by_epoch=True, milestones=[8, 11], gamma=0.1) +``` + +如果é…åˆæ³¨å†Œå™¨å’Œé…置文件使用的è¯ï¼Œæˆ‘们å¯ä»¥è®¾ç½®é…置文件ä¸çš„ `scheduler` å—段æ¥æŒ‡å®šä¼˜åŒ–器, 执行器(Runnerï¼‰ä¼šæ ¹æ®æ¤å—段以åŠæ‰§è¡Œå™¨ä¸çš„优化器自动构建å¦ä¹ 率调度器: + +```python +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 å€ã€‚下é¢æ˜¯ä¸€ä¸ªæŒ‰ç…§è¿ä»£æ¬¡æ•°è¿›è¡Œè°ƒæ•´çš„例å,在第 60000 å’Œ 80000 次è¿ä»£ç»“æŸæ—¶ï¼Œå¦ä¹ 率将会被调整为原æ¥çš„ 0.1 å€ã€‚ + +```python +scheduler = dict(type='MultiStepLR', by_epoch=False, milestones=[60000, 80000], gamma=0.1) +``` + +### 组åˆå¤šä¸ªå¦ä¹ 率调度器(以å¦ä¹ 率预çƒä¸ºä¾‹ï¼‰ + +有些算法在è®ç»ƒè¿‡ç¨‹ä¸ï¼Œå¹¶ä¸æ˜¯è‡ªå§‹è‡³ç»ˆæŒ‰ç…§æŸä¸ªè°ƒåº¦ç–略进行å¦ä¹ 率调整的。最常è§çš„例å是å¦ä¹ 率预çƒï¼Œæ¯”如在è®ç»ƒåˆšå¼€å§‹çš„若干è¿ä»£æ¬¡æ•°ä½¿ç”¨çº¿æ€§çš„调整ç–略将å¦ä¹ 率从一个较å°çš„值增长到æ£å¸¸ï¼Œç„¶åŽæŒ‰ç…§å¦å¤–的调整ç–略进行æ£å¸¸è®ç»ƒã€‚ + +MMEngine 支æŒç»„åˆå¤šä¸ªè°ƒåº¦å™¨ä¸€èµ·ä½¿ç”¨ï¼Œåªéœ€å°†é…置文件ä¸çš„ `scheduler` å—段修改为一组调度器é…置的列表,SchedulerStepHook å¯ä»¥è‡ªåŠ¨å¯¹è°ƒåº¦å™¨åˆ—表进行处ç†ã€‚下é¢çš„例å便实现了å¦ä¹ 率预çƒã€‚ + +```python +scheduler = [ + # 线性å¦ä¹ 率预çƒè°ƒåº¦å™¨ + dict(type='LinearLR', + start_factor=0.001, + by_epoch=False, # 按è¿ä»£æ›´æ–°å¦ä¹ 率 + begin=0, + end=500), # 预çƒå‰ 500 次è¿ä»£ + # 主å¦ä¹ 率调度器 + dict(type='MultiStepLR', + by_epoch=True, # 按轮次更新å¦ä¹ 率 + milestones=[8, 11], + gamma=0.1) +] +``` + +注æ„è¿™é‡Œå¢žåŠ äº† `begin` å’Œ `end` å‚数,这两个å‚数指定了调度器的**生效区间**。生效区间通常åªåœ¨å¤šä¸ªè°ƒåº¦å™¨ç»„åˆæ—¶æ‰éœ€è¦åŽ»è®¾ç½®ï¼Œä½¿ç”¨å•ä¸ªè°ƒåº¦å™¨æ—¶å¯ä»¥å¿½ç•¥ã€‚当指定了 `begin` å’Œ `end` å‚数时,表示该调度器åªåœ¨ [begin, end) 区间内生效,其å•ä½æ˜¯ç”± `by_epoch` å‚数决定。上述例åä¸é¢„çƒé˜¶æ®µ `LinearLR` çš„ `by_epoch` 为 False,表示该调度器åªåœ¨å‰ 500 次è¿ä»£ç”Ÿæ•ˆï¼Œè¶…过 500 次è¿ä»£åŽæ¤è°ƒåº¦å™¨ä¸å†ç”Ÿæ•ˆï¼Œç”±ç¬¬äºŒä¸ªè°ƒåº¦å™¨æ¥æŽ§åˆ¶å¦ä¹ çŽ‡ï¼Œå³ `MultiStepLR`。在组åˆä¸åŒè°ƒåº¦å™¨æ—¶ï¼Œå„调度器的 `by_epoch` å‚æ•°ä¸å¿…相åŒã€‚ + +这里å†ä¸¾ä¸€ä¸ªä¾‹å: + +```python +scheduler = [ + # 在 [0, 1000) è¿ä»£æ—¶ä½¿ç”¨çº¿æ€§å¦ä¹ 率 + dict(type='LinearLR', + start_factor=0.001, + by_epoch=False, + begin=0, + end=1000), + # 在 [1000, 9000) è¿ä»£æ—¶ä½¿ç”¨ä½™å¼¦å¦ä¹ 率 + dict(type='CosineAnnealingLR', + T_max=8000, + by_epoch=False, + begin=1000, + end=9000) +] +``` + +上述例å表示在è®ç»ƒçš„å‰ 1000 次è¿ä»£æ—¶ä½¿ç”¨çº¿æ€§çš„å¦ä¹ 率预çƒï¼Œç„¶åŽåœ¨ç¬¬ 1000 到第 9000 次è¿ä»£æ—¶ä½¿ç”¨å‘¨æœŸä¸º 8000 的余弦退ç«å¦ä¹ 率调度器使å¦ä¹ 率按照余弦函数é€æ¸ä¸‹é™ä¸º 0 。 + +我们å¯ä»¥ç»„åˆä»»æ„多个调度器,既å¯ä»¥ä½¿ç”¨ MMEngine ä¸å·²ç»æ”¯æŒçš„调度器,也å¯ä»¥å®žçŽ°è‡ªå®šä¹‰çš„调度器。 +如果相邻两个调度器的生效区间没有紧邻,而是有一段区间没有被覆盖,那么这段区间的å¦ä¹ 率维æŒä¸å˜ã€‚而如果两个调度器的生效区间å‘生了é‡å ,则对多组调度器å åŠ ä½¿ç”¨ï¼Œå¦ä¹ 率的调整会按照调度器é…置文件ä¸çš„顺åºè§¦å‘(行为与 PyTorch ä¸ [`ChainedScheduler`](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ChainedScheduler.html#chainedscheduler) 一致)。 +在一般情况下,我们推è用户在è®ç»ƒçš„ä¸åŒé˜¶æ®µä½¿ç”¨ä¸åŒçš„å¦ä¹ 率调度ç–ç•¥æ¥é¿å…调度器的生效区间å‘生é‡å 。如果确实需è¦å°†ä¸¤ä¸ªè°ƒåº¦å™¨å åŠ ä½¿ç”¨ï¼Œåˆ™éœ€è¦å分å°å¿ƒï¼Œæˆ‘们推è使用[å¦ä¹ 率å¯è§†åŒ–工具](TODO)æ¥å¯è§†åŒ–å åŠ åŽçš„å¦ä¹ 率,以é¿å…å¦ä¹ 率的调整与预期ä¸ç¬¦ã€‚ + +## 如何调整其他å‚æ•° + +### åŠ¨é‡ + +å’Œå¦ä¹ çŽ‡ä¸€æ ·, 动é‡ä¹Ÿæ˜¯ä¼˜åŒ–器å‚数组ä¸ä¸€ç»„å¯ä»¥è°ƒåº¦çš„å‚数。 动é‡è°ƒåº¦å™¨ï¼ˆmomentum scheduler)的使用方法和å¦ä¹ çŽ‡è°ƒåº¦å™¨å®Œå…¨ä¸€æ ·ã€‚åŒæ ·ä¹Ÿåªéœ€è¦å°†åŠ¨é‡è°ƒåº¦å™¨çš„é…ç½®æ·»åŠ è¿›é…置文件ä¸çš„ `scheduler` å—段的列表ä¸å³å¯ã€‚ + +示例: + +```python +scheduler = [ + # the lr scheduler + dict(type='LinearLR', ...), + # the momentum scheduler + dict(type='LinearMomentum', + start_factor=0.001, + by_epoch=False, + begin=0, + end=1000) +] +``` + +### 通用的å‚数调度器 + +MMEngine 还æ供了一组通用的å‚数调度器用于调度优化器的 `param_groups` ä¸çš„其他å‚数,将å¦ä¹ 率调度器类åä¸çš„ `LR` 改为 `Param` å³å¯ï¼Œä¾‹å¦‚ `LinearParamScheduler`。用户å¯ä»¥é€šè¿‡è®¾ç½®å‚数调度器的 `param_name` å˜é‡æ¥é€‰æ‹©æƒ³è¦è°ƒåº¦çš„å‚数。 + +下é¢æ˜¯ä¸€ä¸ªé€šè¿‡è‡ªå®šä¹‰å‚æ•°åæ¥è°ƒåº¦çš„例å: + +```python +scheduler = [ + dict(type='LinearParamScheduler', + param_name='lr', # 调度 `optimizer.param_groups` ä¸å为 'lr' çš„å˜é‡ + start_factor=0.001, + by_epoch=False, + begin=0, + end=1000) +] +``` + +这里设置的å‚æ•°å是 `lr`ï¼Œå› æ¤è¿™ä¸ªè°ƒåº¦å™¨çš„作用ç‰åŒäºŽç›´æŽ¥ä½¿ç”¨å¦ä¹ 率调度器 `LinearLRScheduler`。 + +除了动é‡ä¹‹å¤–,用户也å¯ä»¥å¯¹ `optimizer.param_groups` ä¸çš„其他å‚æ•°å进行调度,å¯è°ƒåº¦çš„å‚æ•°å–决于所使用的优化器。 例如,当使用带 `weight_decay` çš„ SGD 优化器时,å¯ä»¥æŒ‰ç…§ä»¥ä¸‹ç¤ºä¾‹å¯¹è°ƒæ•´ `weight_decay`: + +```python +scheduler = [ + dict(type='LinearParamScheduler', + param_name='weight_decay', # 调度 `optimizer.param_groups` ä¸å为 'weight_decay' çš„å˜é‡ + start_factor=0.001, + by_epoch=False, + begin=0, + end=1000) +] +``` -- GitLab