diff --git a/docs/zh_cn/tutorials/runner.md b/docs/zh_cn/tutorials/runner.md new file mode 100644 index 0000000000000000000000000000000000000000..c7ccea098ca38340b565958f27eb01b13b0c8791 --- /dev/null +++ b/docs/zh_cn/tutorials/runner.md @@ -0,0 +1,257 @@ +# 执行器(Runner) + +OpenMMLab 的算法库ä¸æ供了å„ç§ç®—法模型的è®ç»ƒã€æµ‹è¯•ã€æŽ¨ç†åŠŸèƒ½ï¼Œè¿™äº›åŠŸèƒ½åœ¨ä¸åŒç®—法方å‘上都有ç€ç›¸ä¼¼çš„接å£ã€‚ +å› æ¤ï¼Œ MMEngine 抽象出了执行器æ¥è´Ÿè´£é€šç”¨çš„算法模型的è®ç»ƒã€æµ‹è¯•ã€æŽ¨ç†ä»»åŠ¡ã€‚ +用户一般å¯ä»¥ç›´æŽ¥ä½¿ç”¨ MMEngine ä¸çš„默认执行器,也å¯ä»¥å¯¹æ‰§è¡Œå™¨è¿›è¡Œä¿®æ”¹ä»¥æ»¡è¶³å®šåˆ¶åŒ–需求。 + +在介ç»å¦‚何使用执行器之å‰ï¼Œæˆ‘ä»¬å…ˆä¸¾å‡ ä¸ªä¾‹åæ¥å¸®åŠ©ç”¨æˆ·ç†è§£ä¸ºä»€ä¹ˆéœ€è¦æ‰§è¡Œå™¨ã€‚ + +下é¢æ˜¯ä¸€æ®µä½¿ç”¨ PyTorch 进行模型è®ç»ƒçš„伪代ç : + +```python +model = ResNet() +optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9) +train_dataset = ImageNetDataset(...) +train_dataloader = DataLoader(train_dataset, ...) + +for i in range(max_epochs): + for data_batch in train_dataloader: + optimizer.zero_grad() + outputs = model(data_batch) + loss = loss_func(outputs, data_batch) + loss.backward() + optimizer.step() +``` + +下é¢æ˜¯ä¸€æ®µä½¿ç”¨ PyTorch 进行模型测试的伪代ç : + +```python +model = ResNet() +model.load_state_dict(torch.load(CKPT_PATH)) +model.eval() + +test_dataset = ImageNetDataset(...) +test_dataloader = DataLoader(test_dataset, ...) + +for data_batch in test_dataloader: + outputs = model(data_batch) + acc = calculate_acc(outputs, data_batch) +``` + +下é¢æ˜¯ä¸€æ®µä½¿ç”¨ PyTorch 进行模型推ç†çš„伪代ç : + +```python +model = ResNet() +model.load_state_dict(torch.load(CKPT_PATH)) +model.eval() + +for img in imgs: + prediction = model(img) +``` + +å¯ä»¥ä»Žä¸Šé¢çš„三段代ç 看出,这三个任务的执行æµç¨‹éƒ½å¯ä»¥å½’纳为构建模型ã€è¯»å–æ•°æ®ã€å¾ªçŽ¯è¿ä»£ç‰æ¥éª¤ã€‚上述代ç 都是以图åƒåˆ†ç±»ä¸ºä¾‹ï¼Œä½†ä¸è®ºæ˜¯å›¾åƒåˆ†ç±»è¿˜æ˜¯ç›®æ ‡æ£€æµ‹æˆ–是图åƒåˆ†å‰²ï¼Œéƒ½è„±ç¦»ä¸äº†è¿™å¥—范å¼ã€‚ +å› æ¤ï¼Œæˆ‘们将模型的è®ç»ƒã€éªŒè¯ã€æµ‹è¯•çš„æµç¨‹æ•´åˆèµ·æ¥ï¼Œå½¢æˆäº†æ‰§è¡Œå™¨ã€‚在执行器ä¸ï¼Œæˆ‘们åªéœ€è¦å‡†å¤‡å¥½æ¨¡åž‹ã€æ•°æ®ç‰ä»»åŠ¡å¿…须的模å—或是这些模å—çš„é…置文件,执行器会自动完æˆä»»åŠ¡æµç¨‹çš„准备和执行。 +é€šè¿‡ä½¿ç”¨æ‰§è¡Œå™¨ä»¥åŠ MMEngine ä¸ä¸°å¯Œçš„功能模å—,用户ä¸å†éœ€è¦æ‰‹åŠ¨æ建è®ç»ƒæµ‹è¯•çš„æµç¨‹ï¼Œä¹Ÿä¸å†éœ€è¦åŽ»å¤„ç†åˆ†å¸ƒå¼ä¸Žéžåˆ†å¸ƒå¼è®ç»ƒçš„区别,å¯ä»¥ä¸“注于算法和模型本身。 + +## 如何使用执行器 + +MMEngine ä¸é»˜è®¤çš„执行器支æŒæ‰§è¡Œæ¨¡åž‹çš„è®ç»ƒã€æµ‹è¯•ä»¥åŠæŽ¨ç†ã€‚用户如果需è¦ä½¿ç”¨è¿™å‡ 项功能ä¸çš„æŸä¸€é¡¹ï¼Œå°±éœ€è¦å‡†å¤‡å¥½å¯¹åº”功能所ä¾èµ–的模å—。 +用户å¯ä»¥æ‰‹åŠ¨æž„建这些模å—的实例,也å¯ä»¥é€šè¿‡ç¼–写[é…置文件](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/config.html) , +由执行器自动从[注册器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/registry.html) ä¸æž„建所需è¦çš„模å—。这两ç§ä½¿ç”¨æ–¹å¼ä¸ï¼Œæˆ‘们更推èåŽè€…。 + +### 手动构建模å—æ¥ä½¿ç”¨æ‰§è¡Œå™¨ + +如上文所说,使用执行器的æŸä¸€é¡¹åŠŸèƒ½æ—¶éœ€è¦å‡†å¤‡å¥½å¯¹åº”功能所ä¾èµ–的模å—。以使用执行器的è®ç»ƒåŠŸèƒ½ä¸ºä¾‹ï¼Œç”¨æˆ·éœ€è¦å‡†å¤‡[模型](TODO) ã€[优化器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/optimizer.html) 〠+[å‚数调度器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html) 还有è®ç»ƒ[æ•°æ®é›†](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/basedataset.html) 。 +在创建完符åˆä¸Šè¿°æ–‡æ¡£è§„范的模å—的对象åŽï¼Œå°±å¯ä»¥ä½¿ç”¨è¿™äº›æ¨¡å—åˆå§‹åŒ–执行器: + +```python +# 准备è®ç»ƒä»»åŠ¡æ‰€éœ€è¦çš„æ¨¡å— +model = ResNet() +optimzier = SGD(model.parameters(), lr=0.01, momentum=0.9) +lr_scheduler = MultiStepLR(milestones=[80, 90], by_epoch=True) +train_dataset = ImageNetDataset() +train_dataloader = Dataloader(dataset=train_dataset, batch_size=32, num_workers=4) + +# è®ç»ƒç›¸å…³å‚数设置 +train_cfg = dict(by_epoch=True, max_epoch=100) + +# åˆå§‹åŒ–执行器 +runner = Runner(model=model, optimizer=optimzier, param_scheduler=lr_scheduler, + train_dataloader=train_dataloader, train_cfg=train_cfg) +# 执行è®ç»ƒ +runner.train() +``` + +上é¢çš„例åä¸ï¼Œæˆ‘们手动构建了 ResNet 分类模型和 ImageNet æ•°æ®é›†ï¼Œä»¥åŠè®ç»ƒæ‰€éœ€è¦çš„优化器和å¦ä¹ 率调度器,使用这些模å—åˆå§‹åŒ–了执行器,最åŽé€šè¿‡è°ƒç”¨æ‰§è¡Œå™¨çš„ `train` 函数进行模型è®ç»ƒã€‚ + +å†ä¸¾ä¸€ä¸ªæ¨¡åž‹æµ‹è¯•çš„例å,模型的测试需è¦ç”¨æˆ·å‡†å¤‡æ¨¡åž‹å’Œè®ç»ƒå¥½çš„æƒé‡è·¯å¾„ã€æµ‹è¯•æ•°æ®é›†ä»¥åŠ[评测器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/evaluator.html) : + +```python +model = FasterRCNN() +test_dataset = CocoDataset() +test_dataloader = Dataloader(dataset=test_dataset, batch_size=2, num_workers=2) +evaluator = CocoEvaluator(metric='bbox') + +# åˆå§‹åŒ–执行器 +runner = Runner(model=model, test_dataloader=test_dataloader, evaluator=evaluator, + load_checkpoint='./faster_rcnn.pth') + +# 执行测试 +runner.test() +``` + +这个例åä¸æˆ‘们手动构建了一个 Faster R-CNN 检测模型,以åŠæµ‹è¯•ç”¨çš„ COCO æ•°æ®é›†å’Œå¯¹åº”çš„ COCO 评测器,并使用这些模å—åˆå§‹åŒ–执行器,最åŽé€šè¿‡è°ƒç”¨æ‰§è¡Œå™¨çš„ `test` 函数进行模型测试。 + +### 通过é…置文件使用执行器 + +OpenMMLab çš„å¼€æºé¡¹ç›®æ™®é使用注册器 + é…置文件的方å¼æ¥ç®¡ç†å’Œæž„建模å—,MMEngine ä¸çš„执行器也推è使用é…置文件进行构建。 +下é¢æ˜¯ä¸€ä¸ªé€šè¿‡é…置文件使用执行器的例å: + +```python +from mmengine import Config, Runner + +# åŠ è½½é…置文件 +config = Config.fromfile('configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py/') + +# 通过é…置文件åˆå§‹åŒ–执行器 +runner = Runner.build_from_cfg(config) + +# 执行è®ç»ƒ +runner.train() + +# 执行测试 +runner.test() +``` + +与手动构建模å—æ¥ä½¿ç”¨æ‰§è¡Œå™¨ä¸åŒçš„是,通过调用 Runner 类的 `build_from_cfg` 方法,执行器能够自动读å–é…置文件ä¸çš„模å—é…置,从相应的注册器ä¸æž„建所需è¦çš„模å—,用户ä¸å†éœ€è¦è€ƒè™‘è®ç»ƒå’Œæµ‹è¯•åˆ†åˆ«ä¾èµ–哪些模å—,也ä¸éœ€è¦ä¸ºäº†åˆ‡æ¢è®ç»ƒçš„模型和数æ®è€Œå¤§é‡æ”¹åŠ¨ä»£ç 。 + +下é¢æ˜¯ä¸€ä¸ªå…¸åž‹çš„é…置简å•ä¾‹å: + +```python +# 模型é…ç½® +model = dict(type='ImageClassifier', + backbone=dict(type='ResNet', depth=50), + neck=dict(type='GlobalAveragePooling'), + head=dict(type='LinearClsHead',num_classes=1000)) +# æ•°æ®é…ç½® +train_dataloader = dict(dataset=dict(type='ImageNet', pipeline=[...]), + sampler=dict(type='DefaultSampler', shuffle=True), + batch_size=32, + num_workers=4) +val_dataloader = ... +test_dataloader = ... + +# 优化器é…ç½® +optimizer = dict(type='SGD', lr=0.01) +# å‚数调度器é…ç½® +param_scheduler = dict(type='MultiStepLR', milestones=[80, 90]) +#评测器é…ç½® +evaluator = dict(type='Accuracy') + +# è®ç»ƒã€éªŒè¯ã€æµ‹è¯•æµç¨‹é…ç½® +train_cfg = dict(by_epoch=True, max_epochs=100) +validation_cfg = dict(interval=1) # æ¯éš”一个 epoch è¿›è¡Œä¸€æ¬¡éªŒè¯ +test_cfg = dict() + +# 自定义钩å +custom_hooks = [...] + +# 默认钩å +default_hooks = dict( + timer=dict(type='IterTimerHook'), # 计时器钩å + checkpoint=dict(type='CheckpointHook', interval=1), # 模型ä¿å˜é’©å + logger=dict(type='TextLoggerHook'), # è®ç»ƒæ—¥å¿—é’©å + optimizer=dict(type='OptimzierHook', grad_clip=False), # 优化器钩å + param_scheduler=dict(type='ParamSchedulerHook')) # å‚数调度器执行钩å + +# 环境é…ç½® +env_cfg = dict( + dist_params=dict(backend='nccl'), + mp_cfg=dict(mp_start_method='fork') +) +# 系统日志é…ç½® +log_cfg = dict(log_level='INFO') +``` + +一个完整的é…置文件主è¦ç”±æ¨¡åž‹ã€æ•°æ®ã€ä¼˜åŒ–器ã€å‚数调度器ã€è¯„测器ç‰æ¨¡å—çš„é…置,è®ç»ƒã€éªŒè¯ã€æµ‹è¯•ç‰æµç¨‹çš„é…置,还有执行æµç¨‹è¿‡ç¨‹ä¸çš„å„ç§é’©å模å—çš„é…置,以åŠçŽ¯å¢ƒå’Œæ—¥å¿—ç‰å…¶ä»–é…置的å—段组æˆã€‚ +通过é…置文件构建的执行器采用了懒åˆå§‹åŒ– (lazy initialization),åªæœ‰å½“调用到è®ç»ƒæˆ–测试ç‰æ‰§è¡Œå‡½æ•°æ—¶ï¼Œæ‰ä¼šæ ¹æ®é…置文件去完整åˆå§‹åŒ–所需è¦çš„模å—。 + +## 进阶使用 + +MMEngine ä¸çš„默认执行器能够完æˆå¤§éƒ¨åˆ†çš„深度å¦ä¹ 任务,但ä¸å¯é¿å…会å˜åœ¨æ— æ³•æ»¡è¶³çš„æƒ…å†µã€‚æœ‰çš„ç”¨æˆ·å¸Œæœ›èƒ½å¤Ÿå¯¹æ‰§è¡Œå™¨è¿›è¡Œæ›´å¤šè‡ªå®šä¹‰ä¿®æ”¹ï¼Œå› æ¤ï¼ŒMMEngine 支æŒè‡ªå®šä¹‰æ¨¡åž‹çš„è®ç»ƒã€éªŒè¯ä»¥åŠæµ‹è¯•çš„æµç¨‹ã€‚ +更进一æ¥ï¼Œå¦‚果默认执行器ä¸ä¾ç„¶æœ‰å…¶ä»–æ— æ³•æ»¡è¶³éœ€æ±‚çš„éƒ¨åˆ†ï¼Œç”¨æˆ·å¯ä»¥åƒè‡ªå®šä¹‰å…¶ä»–模å—ä¸€æ ·ï¼Œé€šè¿‡ç»§æ‰¿é‡å†™çš„æ–¹å¼ï¼Œå®žçŽ°è‡ªå®šä¹‰çš„执行器。执行器åŒæ ·ä¹Ÿå¯ä»¥é€šè¿‡æ³¨å†Œå™¨è¿›è¡Œç®¡ç†ã€‚ + +### 自定义执行æµç¨‹ + +在 MMEngine ä¸ï¼Œæˆ‘们将任务的执行æµç¨‹æŠ½è±¡æˆå¾ªçŽ¯ï¼ˆLoopï¼‰ï¼Œå› ä¸ºå¤§éƒ¨åˆ†çš„æ·±åº¦å¦ä¹ 任务执行æµç¨‹éƒ½å¯ä»¥å½’纳为模型在一组或多组数æ®ä¸Šè¿›è¡Œå¾ªçŽ¯è¿ä»£ã€‚ +MMEngine 内æ供了四ç§é»˜è®¤çš„循环: +- EpochBasedTrainLoop 基于轮次的è®ç»ƒå¾ªçŽ¯ +- IterBasedTrainLoop 基于è¿ä»£æ¬¡æ•°çš„è®ç»ƒå¾ªçŽ¯ +- ValLoop æ ‡å‡†çš„éªŒè¯å¾ªçŽ¯ +- TestLoop æ ‡å‡†çš„æµ‹è¯•å¾ªçŽ¯ + + + +用户å¯ä»¥é€šè¿‡ç»§æ‰¿å¾ªçŽ¯åŸºç±»æ¥å®žçŽ°è‡ªå·±çš„è®ç»ƒæµç¨‹ã€‚循环基类需è¦æ供两个输入:`runner` 执行器的实例和 `loader` 循环所需è¦è¿ä»£çš„è¿ä»£å™¨ã€‚ +用户如果有自定义的需求,也å¯ä»¥å¢žåŠ 更多的输入å‚数。MMEngine ä¸åŒæ ·æ供了 LOOPS 注册器对循环类进行管ç†ï¼Œç”¨æˆ·å¯ä»¥å‘注册器内注册自定义的循环模å—, +然åŽåœ¨é…置文件的 `train_cfg`ã€`validation_cfg`ã€`test_cfg` ä¸å¢žåŠ `type` å—段æ¥æŒ‡å®šä½¿ç”¨ä½•ç§å¾ªçŽ¯ã€‚ +用户å¯ä»¥åœ¨è‡ªå®šä¹‰çš„循环ä¸å®žçŽ°ä»»æ„的执行逻辑,也å¯ä»¥å¢žåŠ æˆ–åˆ å‡é’©å(hook)点ä½ï¼Œä½†éœ€è¦æ³¨æ„的是一旦钩å点ä½è¢«ä¿®æ”¹ï¼Œé»˜è®¤çš„é’©å函数å¯èƒ½ä¸ä¼šè¢«æ‰§è¡Œï¼Œå¯¼è‡´ä¸€äº›è®ç»ƒè¿‡ç¨‹ä¸é»˜è®¤å‘生的行为å‘生å˜åŒ–。 +å› æ¤ï¼Œæˆ‘们强烈建议用户按照本文档ä¸å®šä¹‰çš„循环执行æµç¨‹å›¾ä»¥åŠ[é’©å规范](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/hook.html) 去é‡è½½å¾ªçŽ¯åŸºç±»ã€‚ + +```python +from mmengine.registry import LOOPS +from mmengine.runner.loop import BaseLoop + +@LOOPS.register_module() +class CustomValLoop(BaseLoop): + def __init__(self, runner, loader, evaluator, loader2): + super().__init__(runner, loader, evaluator) + self.loader2 = runner.build_dataloader(loader2) + + def run(self): + self.runner.call_hooks('before_val_epoch') + for idx, databatch in enumerate(self.loader): + self.runner.call_hooks('before_val_iter', + args=dict(databatch=databatch)) + outputs = self.run_iter(idx, databatch) + self.runner.call_hooks('after_val_iter', + args=dict(databatch=databatch, outputs=outputs)) + metric = self.evaluator.evaluate() + for idx, databatch in enumerate(self.loader2): + self.runner.call_hooks('before_val_iter2', + args=dict(databatch=databatch)) + self.run_iter(idx, databatch) + self.runner.call_hooks('after_val_iter2', + args=dict(databatch=databatch, outputs=outputs)) + metric2 = self.evaluator.evaluate() + + ... + + self.runner.call_hooks('after_val_epoch') + +``` + +上é¢çš„例åä¸å®žçŽ°äº†ä¸€ä¸ªä¸Žé»˜è®¤éªŒè¯å¾ªçŽ¯ä¸ä¸€æ ·çš„自定义验è¯å¾ªçŽ¯ï¼Œå®ƒåœ¨ä¸¤ä¸ªä¸åŒçš„验è¯é›†ä¸Šè¿›è¡ŒéªŒè¯ï¼ŒåŒæ—¶å¯¹ç¬¬äºŒæ¬¡éªŒè¯å¢žåŠ 了é¢å¤–çš„é’©å点ä½ï¼Œå¹¶åœ¨æœ€åŽå¯¹ä¸¤ä¸ªéªŒè¯ç»“果进行进一æ¥çš„处ç†ã€‚在实现了自定义的循环类之åŽï¼Œ +åªéœ€è¦åœ¨é…置文件的 `validation_cfg` 内设置 `type='CustomValLoop'`ï¼Œå¹¶æ·»åŠ é¢å¤–çš„é…ç½®å³å¯ã€‚ + +```python +validation_cfg = dict(type='CustomValLoop', loader2=dict(dataset=dict(type='ValDataset2'), ...)) +``` + +### 自定义执行器 + +如果自定义执行æµç¨‹ä¾ç„¶æ— 法满足需求,用户åŒæ ·å¯ä»¥å®žçŽ°è‡ªå·±çš„执行器。具体实现æµç¨‹ä¸Žå…¶ä»–模å—æ— å¼‚ï¼šç»§æ‰¿ MMEngine ä¸çš„ Runner,é‡å†™éœ€è¦ä¿®æ”¹çš„å‡½æ•°ï¼Œæ·»åŠ è¿› RUNNERS 注册器ä¸ï¼Œæœ€åŽåœ¨é…置文件ä¸æŒ‡å®š `runner_type` å³å¯ã€‚ + +```python +from mmengine.registry import RUNNERS +from mmengine.runner import Runner + +@RUNNERS.register_module() +class CustomRunner(Runner): + + def setup_env(self): + ... +``` + +上述例å实现了一个自定义的执行器,并é‡å†™äº† `setup_env` 函数,然åŽæ·»åŠ 进了 RUNNERS 注册器ä¸ï¼Œå®Œæˆäº†è¿™äº›æ¥éª¤ä¹‹åŽï¼Œä¾¿å¯ä»¥åœ¨é…置文件ä¸è®¾ç½® `runner_type='CustomRunner'` æ¥æž„建自定义的执行器。