diff --git a/docs/resources/config/config_sgd.py b/docs/resources/config/config_sgd.py new file mode 100644 index 0000000000000000000000000000000000000000..9afaf8e54ee62d58c6308af8e90f7a46e38c6dbf --- /dev/null +++ b/docs/resources/config/config_sgd.py @@ -0,0 +1 @@ +optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001) diff --git a/docs/resources/config/cross_repo.py b/docs/resources/config/cross_repo.py new file mode 100644 index 0000000000000000000000000000000000000000..947266895640192e34ca57c4c2d56f013428e00b --- /dev/null +++ b/docs/resources/config/cross_repo.py @@ -0,0 +1,6 @@ +_base_ = [ + 'mmdet::_base_/schedules/schedule_1x.py', + 'mmdet::_base_/datasets/coco_instance.py', + 'mmdet::_base_/default_runtime.py', + 'mmdet::_base_/models/faster-rcnn_r50_fpn.py', +] diff --git a/docs/resources/config/custom_imports.py b/docs/resources/config/custom_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..adb5d0489ae3884e5e17c02f3056103039856762 --- /dev/null +++ b/docs/resources/config/custom_imports.py @@ -0,0 +1,2 @@ +custom_imports = dict(imports=['my_module'], allow_failed_imports=False) +optimizer = dict(type='CustomOptim') diff --git a/docs/resources/config/demo_train.py b/docs/resources/config/demo_train.py new file mode 100644 index 0000000000000000000000000000000000000000..411ef6a98d905ed9e6993a5782fe2b0c2b76d169 --- /dev/null +++ b/docs/resources/config/demo_train.py @@ -0,0 +1,33 @@ +import argparse + +from mmengine.config import Config, DictAction + + +def parse_args(): + parser = argparse.ArgumentParser(description='Train a model') + parser.add_argument('config', help='train config file path') + parser.add_argument( + '--cfg-options', + nargs='+', + action=DictAction, + help='override some settings in the used config, the key-value pair ' + 'in xxx=yyy format will be merged into config file. If the value to ' + 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' + 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' + 'Note that the quotation marks are necessary and that no white space ' + 'is allowed.') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + cfg = Config.fromfile(args.config) + if args.cfg_options is not None: + cfg.merge_from_dict(args.cfg_options) + print(cfg) + + +if __name__ == '__main__': + main() diff --git a/docs/resources/config/example.py b/docs/resources/config/example.py new file mode 100644 index 0000000000000000000000000000000000000000..d3df48c6a6445b42c477c6d10790dea908ccfe2c --- /dev/null +++ b/docs/resources/config/example.py @@ -0,0 +1,2 @@ +model = dict(type='CustomModel', in_channels=[1, 2, 3]) +optimizer = dict(type='SGD', lr=0.01) diff --git a/docs/resources/config/learn_read_config.py b/docs/resources/config/learn_read_config.py new file mode 100644 index 0000000000000000000000000000000000000000..822aa66257eb1428d1075699005e556a9875633f --- /dev/null +++ b/docs/resources/config/learn_read_config.py @@ -0,0 +1,3 @@ +test_int = 1 +test_list = [1, 2, 3] +test_dict = dict(key1='value1', key2=0.1) diff --git a/docs/resources/config/my_module.py b/docs/resources/config/my_module.py new file mode 100644 index 0000000000000000000000000000000000000000..ddb9538b0e99aee86c3b9aa7e0d4c013be3f21fd --- /dev/null +++ b/docs/resources/config/my_module.py @@ -0,0 +1,6 @@ +from mmengine.registry import OPTIMIZERS + + +@OPTIMIZERS.register_module() +class CustomOptim: + pass diff --git a/docs/resources/config/optimizer_cfg.py b/docs/resources/config/optimizer_cfg.py new file mode 100644 index 0000000000000000000000000000000000000000..b6f55cd3c51fc955f7c70f658b082d381613a44e --- /dev/null +++ b/docs/resources/config/optimizer_cfg.py @@ -0,0 +1 @@ +optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) diff --git a/docs/resources/config/predefined_var.py b/docs/resources/config/predefined_var.py new file mode 100644 index 0000000000000000000000000000000000000000..f072068c05041eed3614d2c92819591f74118020 --- /dev/null +++ b/docs/resources/config/predefined_var.py @@ -0,0 +1 @@ +work_dir = './work_dir/{{fileBasenameNoExtension}}' diff --git a/docs/resources/config/refer_base_var.py b/docs/resources/config/refer_base_var.py new file mode 100644 index 0000000000000000000000000000000000000000..1e98a1ef08285877ad702252810b438369428ccf --- /dev/null +++ b/docs/resources/config/refer_base_var.py @@ -0,0 +1,2 @@ +_base_ = ['resnet50.py'] +a = {{_base_.model}} diff --git a/docs/resources/config/resnet50.py b/docs/resources/config/resnet50.py new file mode 100644 index 0000000000000000000000000000000000000000..43f0187d9aebb91c74ec1c33c2d9593dfd943017 --- /dev/null +++ b/docs/resources/config/resnet50.py @@ -0,0 +1,2 @@ +_base_ = ['optimizer_cfg.py'] +model = dict(type='ResNet', depth=50) diff --git a/docs/resources/config/resnet50_delete_key.py b/docs/resources/config/resnet50_delete_key.py new file mode 100644 index 0000000000000000000000000000000000000000..b7ada136a775170f65a4d4c141a079e1dc189edd --- /dev/null +++ b/docs/resources/config/resnet50_delete_key.py @@ -0,0 +1,3 @@ +_base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] +model = dict(type='ResNet', depth=50) +optimizer = dict(_delete_=True, type='SGD', lr=0.01) diff --git a/docs/resources/config/resnet50_lr0.01.py b/docs/resources/config/resnet50_lr0.01.py new file mode 100644 index 0000000000000000000000000000000000000000..47a83994b610d77277a6d17254c4c606e96b1f93 --- /dev/null +++ b/docs/resources/config/resnet50_lr0.01.py @@ -0,0 +1,3 @@ +_base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] +model = dict(type='ResNet', depth=50) +optimizer = dict(lr=0.01) diff --git a/docs/resources/config/resnet50_runtime.py b/docs/resources/config/resnet50_runtime.py new file mode 100644 index 0000000000000000000000000000000000000000..4b7c8e1a43111dcdf14109ff19263ecdc283e7ab --- /dev/null +++ b/docs/resources/config/resnet50_runtime.py @@ -0,0 +1,2 @@ +_base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] +model = dict(type='ResNet', depth=50) diff --git a/docs/resources/config/runtime_cfg.py b/docs/resources/config/runtime_cfg.py new file mode 100644 index 0000000000000000000000000000000000000000..3286f6973610a171ba983af2de8602b3f2a0bc42 --- /dev/null +++ b/docs/resources/config/runtime_cfg.py @@ -0,0 +1 @@ +gpu_ids = [0, 1] diff --git a/docs/zh_cn/tutorials/config.md b/docs/zh_cn/tutorials/config.md index 71a2ceb0381fdeca4dd261aa90eed93ee048b793..d961d6f011868356ea96559aa776506e40cce346 100644 --- a/docs/zh_cn/tutorials/config.md +++ b/docs/zh_cn/tutorials/config.md @@ -1,6 +1,27 @@ # é…置(Config) -MMEngine 实现了抽象的é…置类,为用户æ供统一的é…置访问接å£ã€‚é…置类能够支æŒä¸åŒæ ¼å¼çš„é…置文件,包括 `python`,`json`,`yaml`,用户å¯ä»¥æ ¹æ®éœ€æ±‚选择自己åå¥½çš„æ ¼å¼ã€‚é…置类æ供了类似å—典或者 Python 对象属性的访问接å£ï¼Œç”¨æˆ·å¯ä»¥å分自然地进行é…ç½®å—段的读å–和修改。为了方便算法框架管ç†é…置文件,é…置类也实现了一些特性,例如é…置文件的å—段继承ç‰ã€‚ +MMEngine 实现了抽象的é…置类(Config),为用户æ供统一的é…置访问接å£ã€‚é…置类能够支æŒä¸åŒæ ¼å¼çš„é…置文件,包括 `python`,`json`,`yaml`,用户å¯ä»¥æ ¹æ®éœ€æ±‚选择自己åå¥½çš„æ ¼å¼ã€‚é…置类æ供了类似å—典或者 Python 对象属性的访问接å£ï¼Œç”¨æˆ·å¯ä»¥å分自然地进行é…ç½®å—段的读å–和修改。为了方便算法框架管ç†é…置文件,é…置类也实现了一些特性,例如é…置文件的å—段继承ç‰ã€‚ + +在开始教程之å‰ï¼Œæˆ‘们先将教程ä¸éœ€è¦ç”¨åˆ°çš„é…置文件下载到本地(建议在临时目录下执行,方便åŽç»åˆ 除示例é…置文件): + +```bash +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/config_sgd.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/cross_repo.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/custom_imports.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/demo_train.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/example.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/learn_read_config.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/my_module.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/optimizer_cfg.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/predefined_var.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/refer_base_var.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/resnet50_delete_key.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/resnet50_lr0.01.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/resnet50_runtime.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/resnet50.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/runtime_cfg.py +wget https://raw.githubusercontent.com/open-mmlab/mmengine/HAOCHENY/config_docs/docs/resources/config/modify_base_var.py +``` ## é…ç½®æ–‡ä»¶è¯»å– @@ -36,13 +57,19 @@ test_dict: key2: 0.1 ``` -对于以上三ç§æ ¼å¼çš„文件,å‡è®¾æ–‡ä»¶å分别为 `config.py`,`config.json`,`config.yml`,则我们调用 `Config.fromfile('config.xxx')` 接å£éƒ½ä¼šå¾—到相åŒçš„ç»“æžœï¼Œæž„é€ äº†åŒ…å« 3 个å—段的é…置对象。我们以 `config.py` 为例: +对于以上三ç§æ ¼å¼çš„文件,å‡è®¾æ–‡ä»¶å分别为 `config.py`,`config.json`,`config.yml`,调用 `Config.fromfile('config.xxx')` 接å£åŠ 载这三个文件都会得到相åŒçš„ç»“æžœï¼Œæž„é€ äº†åŒ…å« 3 个å—段的é…置对象。我们以 `config.py` 为例,我们先将示例é…置文件下载到本地: + +然åŽé€šè¿‡é…置类的 `fromfile` 接å£è¯»å–é…置文件: ```python -from mmengine import Config +from mmengine.config import Config -cfg = Config.fromfile('/path/to/config.py') -# Config (path: config.py): {'test_int': 1, 'test_list': [1, 2, 3], 'test_dict': {'key1: 'value1', "key2": 0.1} +cfg = Config.fromfile('learn_read_config.py') +print(cfg) +``` + +``` +Config (path: learn_read_config.py): {'test_int': 1, 'test_list': [1, 2, 3], 'test_dict': {'key1': 'value1', 'key2': 0.1}} ``` ## é…置文件的使用 @@ -51,20 +78,29 @@ cfg = Config.fromfile('/path/to/config.py') 我们æ供了两ç§è®¿é—®æŽ¥å£ï¼Œå³ç±»ä¼¼å—å…¸çš„æŽ¥å£ `cfg['key']` 或者类似 Python å¯¹è±¡å±žæ€§çš„æŽ¥å£ `cfg.key`。这两ç§æŽ¥å£éƒ½æ”¯æŒè¯»å†™ã€‚ ```python -cfg = Config.fromfile('config.py') +print(cfg.test_int) +print(cfg.test_list) +print(cfg.test_dict) +cfg.test_int = 2 -cfg.test_int # 1 -cfg.test_list # [1, 2, 3] -cfg.test_dict # ConfigDict(key1='value1', key2=0.1) -cfg.test_int = 2 # 这里å‘生了é…ç½®å—段修改,test_int å—段的值å˜æˆäº† 2 +print(cfg['test_int']) +print(cfg['test_list']) +print(cfg['test_dict']) +cfg['test_list'][1] = 3 +print(cfg['test_list']) +``` -cfg['test_int'] # 2 -cfg['test_list'] # [1, 2, 3] -cfg['test_dict'] # ConfigDict(key1='value1', key2=0.1) -cfg['test_list'][1] = 3 # 这里å‘生了å—段修改,test_list å—段的值å˜æˆäº† [1, 3, 3] +``` +1 +[1, 2, 3] +{'key1': 'value1', 'key2': 0.1} +2 +[1, 2, 3] +{'key1': 'value1', 'key2': 0.1} +[1, 3, 3] ``` -注æ„,é…置文件ä¸å®šä¹‰çš„嵌套å—段(å³ç±»ä¼¼å—典的å—段),在 Config ä¸ä¼šå°†å…¶è½¬åŒ–为 ConfigDict 类,该类具有和 Python 内置å—典类型相åŒçš„接å£ï¼Œå¯ä»¥ç›´æŽ¥å½“åšæ™®é€šå—典使用。 +注æ„,é…置文件ä¸å®šä¹‰çš„嵌套å—段(å³ç±»ä¼¼å—典的å—段),在 Config ä¸ä¼šå°†å…¶è½¬åŒ–为 ConfigDict 类,该类继承了 Python 内置å—典类型的全部接å£ï¼ŒåŒæ—¶ä¹Ÿæ”¯æŒä»¥å¯¹è±¡å±žæ€§çš„æ–¹å¼è®¿é—®æ•°æ®ã€‚ 在算法库ä¸ï¼Œå¯ä»¥å°†é…置与注册器结åˆèµ·æ¥ä½¿ç”¨ï¼Œè¾¾åˆ°é€šè¿‡é…置文件æ¥æŽ§åˆ¶æ¨¡å—æž„é€ çš„ç›®çš„ã€‚è¿™é‡Œä¸¾ä¸€ä¸ªåœ¨é…置文件ä¸å®šä¹‰ä¼˜åŒ–器的例å。 @@ -78,14 +114,30 @@ optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001) ```python from mmengine import Config -from mmengine.Registry import OPTIMIZERS +from mmengine.registry import OPTIMIZERS + +import torch.nn as nn cfg = Config.fromfile('config_sgd.py') + +model = nn.Conv2d(1, 1, 1) +cfg.optimizer.params = model.parameters() optimizer = OPTIMIZERS.build(cfg.optimizer) -# 这里 optimizer 就是一个 torch.optim.SGD 对象 +print(optimizer) ``` -è¿™æ ·ï¼Œæˆ‘ä»¬å°±å¯ä»¥åœ¨ä¸æ”¹åŠ¨ç®—法库代ç 的情况下,仅通过修改é…置文件,æ¥ä½¿ç”¨ä¸åŒçš„优化器。 +``` +SGD ( +Parameter Group 0 + dampening: 0 + foreach: None + lr: 0.1 + maximize: False + momentum: 0.9 + nesterov: False + weight_decay: 0.0001 +) +``` ## é…置文件的继承 @@ -114,7 +166,11 @@ model = dict(type='ResNet', depth=50) ```python cfg = Config.fromfile('resnet50.py') -cfg.optimizer # ConfigDict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) +print(cfg.optimizer) +``` + +``` +{'type': 'SGD', 'lr': 0.02, 'momentum': 0.9, 'weight_decay': 0.0001} ``` 这里 `_base_` 是é…置文件的ä¿ç•™å—段,指定了该é…置文件的继承æ¥æºã€‚支æŒç»§æ‰¿å¤šä¸ªæ–‡ä»¶ï¼Œå°†åŒæ—¶èŽ·å¾—这多个文件ä¸çš„所有å—段,但是è¦æ±‚继承的多个文件ä¸**没有**相åŒå称的å—段,å¦åˆ™ä¼šæŠ¥é”™ã€‚ @@ -125,14 +181,23 @@ cfg.optimizer # ConfigDict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00 gpu_ids = [0, 1] ``` -`resnet50.py`: +`resnet50_runtime.py`: ```python _base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] model = dict(type='ResNet', depth=50) ``` -这时,读å–é…置文件 `resnet50.py` 会获得 3 个å—段 `model`,`optimizer`,`gpu_ids`。 +这时,读å–é…置文件 `resnet50_runtime.py` 会获得 3 个å—段 `model`,`optimizer`,`gpu_ids`。 + +```python +cfg = Config.fromfile('resnet50_runtime.py') +print(cfg.optimizer) +``` + +``` +{'type': 'SGD', 'lr': 0.02, 'momentum': 0.9, 'weight_decay': 0.0001} +``` 通过这ç§æ–¹å¼ï¼Œæˆ‘们å¯ä»¥å°†é…置文件进行拆分,定义一些通用é…置文件,在实际é…置文件ä¸ç»§æ‰¿å„ç§é€šç”¨é…置文件,å¯ä»¥å‡å°‘具体任务的é…ç½®æµç¨‹ã€‚ @@ -154,7 +219,11 @@ optimizer = dict(lr=0.01) ```python cfg = Config.fromfile('resnet50_lr0.01.py') -cfg.optimizer # ConfigDict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001) +print(cfg.optimizer) +``` + +``` +{'type': 'SGD', 'lr': 0.01, 'momentum': 0.9, 'weight_decay': 0.0001} ``` 对于éžå—典类型的å—段,例如整数,å—符串,列表ç‰ï¼Œé‡æ–°å®šä¹‰å³å¯å®Œå…¨è¦†ç›–,例如下é¢çš„写法就将 `gpu_ids` 这个å—段的值修改æˆäº† `[0]`。 @@ -169,7 +238,7 @@ gpu_ids = [0] 有时候我们对于继承过æ¥çš„å—典类型å—段,ä¸ä»…仅是想修改其ä¸æŸäº› key,å¯èƒ½è¿˜éœ€è¦åˆ 除其ä¸çš„一些 key。这时候在é‡æ–°å®šä¹‰è¿™ä¸ªå—典时,需è¦æŒ‡å®š `_delete_=True`,表示将没有在新定义的å—å…¸ä¸å‡ºçŽ°çš„ key å…¨éƒ¨åˆ é™¤ã€‚ -`resnet50.py`: +`resnet50_delete_key.py`: ```python _base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] @@ -180,56 +249,92 @@ optimizer = dict(_delete_=True, type='SGD', lr=0.01) 这时候,`optimizer` 这个å—å…¸ä¸å°±åªæœ‰ `type` å’Œ `lr` 这两个 key,`momentum` å’Œ `weight_decay` å°†ä¸å†è¢«ç»§æ‰¿ã€‚ ```python -cfg = Config.fromfile('resnet50_lr0.01.py') -cfg.optimizer # ConfigDict(type='SGD', lr=0.01) +cfg = Config.fromfile('resnet50_delete_key.py') +print(cfg.optimizer) +``` + +``` +{'type': 'SGD', 'lr': 0.01} ``` ### 引用被继承文件ä¸çš„å˜é‡ 有时我们想é‡å¤åˆ©ç”¨ `_base_` ä¸å®šä¹‰çš„å—段内容,就å¯ä»¥é€šè¿‡ `{{_base_.xxxx}}` 获å–æ¥èŽ·å–对应å˜é‡çš„æ‹·è´ã€‚例如: +`refer_base_var.py` + ```python _base_ = ['resnet50.py'] a = {{_base_.model}} -# ç‰ä»·äºŽ a = dict(type='ResNet', depth=50) ``` -## é…置文件的导出 +解æžåŽå‘现,`a` 的值å˜æˆäº† `resnet50.py` ä¸å®šä¹‰çš„ `model` -在å¯åŠ¨è®ç»ƒè„šæœ¬æ—¶ï¼Œç”¨æˆ·å¯èƒ½é€šè¿‡ä¼ å‚çš„æ–¹å¼æ¥ä¿®æ”¹é…置文件的部分å—段,为æ¤æˆ‘们æ供了 `dump` -接å£æ¥å¯¼å‡ºæ›´æ”¹åŽçš„é…置文件。与读å–é…置文件类似,用户å¯ä»¥é€šè¿‡ `cfg.dump('config.xxx')` æ¥é€‰æ‹©å¯¼å‡ºæ–‡ä»¶çš„æ ¼å¼ã€‚`dump` -åŒæ ·å¯ä»¥å¯¼å‡ºæœ‰ç»§æ‰¿å…³ç³»çš„é…置文件,导出的文件å¯ä»¥è¢«ç‹¬ç«‹ä½¿ç”¨ï¼Œä¸å†ä¾èµ–于 `_base_` ä¸å®šä¹‰çš„文件。 +```python +cfg = Config.fromfile('refer_base_var.py') +print(cfg.a) +``` -基于继承一节定义的 `resnet50.py` +``` +{'type': 'ResNet', 'depth': 50} +``` + +我们å¯ä»¥åœ¨ `json`ã€`yaml`ã€`python` 三ç§ç±»åž‹çš„é…置文件ä¸ï¼Œä½¿ç”¨è¿™ç§æ–¹å¼æ¥èŽ·å– `_base_` ä¸å®šä¹‰çš„å˜é‡ã€‚ + +尽管这ç§èŽ·å– `_base_` ä¸å®šä¹‰å˜é‡çš„æ–¹å¼éžå¸¸é€šç”¨ï¼Œä½†æ˜¯åœ¨è¯æ³•ä¸Šå˜åœ¨ä¸€äº›é™åˆ¶ï¼Œæ— 法充分利用 `python` ç±»é…置文件的动æ€ç‰¹æ€§ã€‚比如我们想在 `python` ç±»é…置文件ä¸ï¼Œä¿®æ”¹ `_base_` ä¸å®šä¹‰çš„å˜é‡ï¼š ```python -_base_ = ['optimizer_cfg.py', 'runtime_cfg.py'] -model = dict(type='ResNet', depth=50) +_base_ = ['resnet50.py'] +a = {{_base_.model}} +a['type'] = 'MobileNet' +``` + +é…ç½®ç±»æ˜¯æ— æ³•è§£æžè¿™æ ·çš„é…置文件的(解æžæ—¶æŠ¥é”™ï¼‰ã€‚é…置类æ供了一ç§æ›´ `pythonic` çš„æ–¹å¼ï¼Œè®©æˆ‘们能够在 `python` ç±»é…置文件ä¸ä¿®æ”¹ `_base_` ä¸å®šä¹‰çš„å˜é‡ï¼ˆ`python` ç±»é…置文件专属特性,目å‰ä¸æ”¯æŒåœ¨ `json`ã€`yaml` é…置文件ä¸ä¿®æ”¹ `_base_` ä¸å®šä¹‰çš„å˜é‡ï¼‰ã€‚ + +`modify_base_var.py`: + +```python +_base_ = ['resnet50.py'] +a = _base_.model +a.type = 'MobileNet' +``` + +```python +cfg = Config.fromfile('modify_base_var.py') +print(cfg.a) ``` -æˆ‘ä»¬å°†å…¶åŠ è½½åŽå¯¼å‡º: +``` +{'type': 'MobileNet', 'depth': 50} +``` + +解æžåŽå‘现,`a` çš„ type å˜æˆäº† `MobileNet。` + +## é…置文件的导出 + +在å¯åŠ¨è®ç»ƒè„šæœ¬æ—¶ï¼Œç”¨æˆ·å¯èƒ½é€šè¿‡ä¼ å‚çš„æ–¹å¼æ¥ä¿®æ”¹é…置文件的部分å—段,为æ¤æˆ‘们æ供了 `dump` +接å£æ¥å¯¼å‡ºæ›´æ”¹åŽçš„é…置文件。与读å–é…置文件类似,用户å¯ä»¥é€šè¿‡ `cfg.dump('config.xxx')` æ¥é€‰æ‹©å¯¼å‡ºæ–‡ä»¶çš„æ ¼å¼ã€‚`dump` +åŒæ ·å¯ä»¥å¯¼å‡ºæœ‰ç»§æ‰¿å…³ç³»çš„é…置文件,导出的文件å¯ä»¥è¢«ç‹¬ç«‹ä½¿ç”¨ï¼Œä¸å†ä¾èµ–于 `_base_` ä¸å®šä¹‰çš„文件。 + +基于继承一节定义的 `resnet50.py`,æˆ‘ä»¬å°†å…¶åŠ è½½åŽå¯¼å‡º: ```python cfg = Config.fromfile('resnet50.py') cfg.dump('resnet50_dump.py') ``` -`dumped_resnet50.py` +`resnet50_dump.py` ```python optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) -gpu_ids = [0, 1] model = dict(type='ResNet', depth=50) ``` 类似的,我们å¯ä»¥å¯¼å‡º jsonã€yaml æ ¼å¼çš„é…置文件 -`dumped_resnet50.yaml` +`resnet50_dump.yaml` ```yaml -gpu_ids: -- 0 -- 1 model: depth: 50 type: ResNet @@ -240,20 +345,20 @@ optimizer: weight_decay: 0.0001 ``` -`dumped_resnet50.json` +`resnet50_dump.json` ```json -{"optimizer": {"type": "SGD", "lr": 0.02, "momentum": 0.9, "weight_decay": 0.0001}, "gpu_ids": [0, 1], "model": {"type": "ResNet", "depth": 50}} +{"optimizer": {"type": "SGD", "lr": 0.02, "momentum": 0.9, "weight_decay": 0.0001}, "model": {"type": "ResNet", "depth": 50}} ``` æ¤å¤–,`dump` ä¸ä»…èƒ½å¯¼å‡ºåŠ è½½è‡ªæ–‡ä»¶çš„ `cfg`ï¼Œè¿˜èƒ½å¯¼å‡ºåŠ è½½è‡ªå—典的 `cfg` ```python cfg = Config(dict(a=1, b=2)) -cfg.dump('demo.py') +cfg.dump('dump_dict.py') ``` -`demo.py` +`dump_dict.py` ```python a=1 @@ -266,119 +371,233 @@ b=2 ### 预定义å—段 -有时候我们希望é…置文件ä¸çš„一些å—段和当å‰è·¯å¾„或者文件åç‰ç›¸å…³ï¼Œè¿™é‡Œä¸¾ä¸€ä¸ªå…¸åž‹ä½¿ç”¨åœºæ™¯çš„例å。在è®ç»ƒæ¨¡åž‹æ—¶ï¼Œæˆ‘们会在é…置文件ä¸å®šä¹‰ä¸€ä¸ªå·¥ä½œç›®å½•ï¼Œå˜æ”¾è¿™ç»„实验é…置的模型和日志,那么对于ä¸åŒçš„é…置文件,我们期望定义ä¸åŒçš„工作目录。用户的一ç§å¸¸è§é€‰æ‹©æ˜¯ï¼Œç›´æŽ¥ä½¿ç”¨é…置文件å作为工作目录å的一部分,例如对于é…置文件 `config_setting1.py`,工作目录就是 `./work_dir/config_setting1`。 +有时候我们希望é…置文件ä¸çš„一些å—段和当å‰è·¯å¾„或者文件åç‰ç›¸å…³ï¼Œè¿™é‡Œä¸¾ä¸€ä¸ªå…¸åž‹ä½¿ç”¨åœºæ™¯çš„例å。在è®ç»ƒæ¨¡åž‹æ—¶ï¼Œæˆ‘们会在é…置文件ä¸å®šä¹‰ä¸€ä¸ªå·¥ä½œç›®å½•ï¼Œå˜æ”¾è¿™ç»„实验é…置的模型和日志,那么对于ä¸åŒçš„é…置文件,我们期望定义ä¸åŒçš„工作目录。用户的一ç§å¸¸è§é€‰æ‹©æ˜¯ï¼Œç›´æŽ¥ä½¿ç”¨é…置文件å作为工作目录å的一部分,例如对于é…置文件 `predefined_var.py`,工作目录就是 `./work_dir/predefined_var`。 -使用预定义å—段å¯ä»¥æ–¹ä¾¿åœ°å®žçŽ°è¿™ç§éœ€æ±‚,在é…置文件 `config_setting1.py` ä¸å¯ä»¥è¿™æ ·å†™ï¼š +使用预定义å—段å¯ä»¥æ–¹ä¾¿åœ°å®žçŽ°è¿™ç§éœ€æ±‚,在é…置文件 `predefined_var.py` ä¸å¯ä»¥è¿™æ ·å†™ï¼š ```Python -work_dir = './work_dir/{{ fileBasenameNoExtension }}' +work_dir = './work_dir/{{fileBasenameNoExtension}}' ``` -这里 `{{ fileBasenameNoExtension }}` 表示该é…置文件的文件å(ä¸å«æ‹“展å),在é…置类读å–é…置文件的时候,会将这ç§ç”¨åŒèŠ±æ‹¬å·åŒ…èµ·æ¥çš„å—符串自动解æžä¸ºå¯¹åº”的实际值。 +这里 `{{fileBasenameNoExtension}}` 表示该é…置文件的文件å(ä¸å«æ‹“展å),在é…置类读å–é…置文件的时候,会将这ç§ç”¨åŒèŠ±æ‹¬å·åŒ…èµ·æ¥çš„å—符串自动解æžä¸ºå¯¹åº”的实际值。 -```Python -cfg = Config.fromfile('./config_setting1.py') -cfg.work_dir # "./work_dir/config_setting1" +```python +cfg = Config.fromfile('./predefined_var.py') +print(cfg.work_dir) +``` + +``` +./work_dir/predefined_var ``` ç›®å‰æ”¯æŒçš„预定义å—段有以下四ç§ï¼Œå˜é‡åå‚考自 [VS Code](https://code.visualstudio.com/docs/editor/variables-reference) ä¸çš„相关å—段: -- `{{ fileDirname }}` - 当å‰æ–‡ä»¶çš„目录å,例如 `/home/your-username/your-project/folder` -- `{{ fileBasename }}` - 当å‰æ–‡ä»¶çš„文件å,例如 `file.py` -- `{{ fileBasenameNoExtension }}` - 当å‰æ–‡ä»¶ä¸åŒ…å«æ‰©å±•å的文件å,例如 file -- `{{ fileExtname }}` - 当å‰æ–‡ä»¶çš„扩展å,例如 `.py` +- `{{fileDirname}}` - 当å‰æ–‡ä»¶çš„目录å,例如 `/home/your-username/your-project/folder` +- `{{fileBasename}}` - 当å‰æ–‡ä»¶çš„文件å,例如 `file.py` +- `{{fileBasenameNoExtension}}` - 当å‰æ–‡ä»¶ä¸åŒ…å«æ‰©å±•å的文件å,例如 file +- `{{fileExtname}}` - 当å‰æ–‡ä»¶çš„扩展å,例如 `.py` -### 跨项目继承é…置文件 +### 命令行修改é…ç½® -为了é¿å…基于已有算法库开å‘的新项目å¤åˆ¶å¤§é‡çš„é…置文件,MMEngine ä¸çš„ config 支æŒé…置文件的跨项目继承。 -å‡å®š MMDetection 项目ä¸å˜åœ¨å¦‚下é…置文件 +有时候我们åªå¸Œæœ›ä¿®æ”¹éƒ¨åˆ†é…置,而ä¸æƒ³ä¿®æ”¹é…置文件本身,例如实验过程ä¸æƒ³æ›´æ¢å¦ä¹ 率,但是åˆä¸æƒ³é‡æ–°å†™ä¸€ä¸ªé…置文件,常用的åšæ³•æ˜¯åœ¨å‘½ä»¤è¡Œä¼ å…¥å‚æ•°æ¥è¦†ç›–相关é…置。考虑到我们想修改的é…置通常是一些内层å‚数,如优化器的å¦ä¹ 率ã€æ¨¡åž‹å·ç§¯å±‚的通é“æ•°ç‰ï¼Œå› æ¤ MMEngine æä¾›äº†ä¸€å¥—æ ‡å‡†çš„æµç¨‹ï¼Œè®©æˆ‘们能够在命令行里轻æ¾ä¿®æ”¹é…置文件ä¸ä»»æ„层级的å‚数。 -```text -configs/_base_/schedules/schedule_1x.py -configs/_base_/datasets.coco_instance.py -configs/_base_/default_runtime.py -configs/_base_/models/faster_rcnn_r50_fpn.py +1. 使用 `argparser` 解æžè„šæœ¬è¿è¡Œçš„å‚æ•° +2. 使用 `argparse.ArgumentParser.add_argument` 方法时,让 `action` å‚数的值为 [DictAction](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.config.DictAction),用它æ¥è¿›ä¸€æ¥è§£æžå‘½ä»¤è¡Œå‚æ•°ä¸ç”¨äºŽä¿®æ”¹é…置文件的å‚æ•° +3. 使用é…置类的 `merge_from_dict` 方法æ¥æ›´æ–°é…ç½® + +å¯åŠ¨è„šæœ¬ç¤ºä¾‹å¦‚下: + +`demo_train.py` + +```python +import argparse + +from mmengine.config import Config, DictAction + + +def parse_args(): + parser = argparse.ArgumentParser(description='Train a model') + parser.add_argument('config', help='train config file path') + parser.add_argument( + '--cfg-options', + nargs='+', + action=DictAction, + help='override some settings in the used config, the key-value pair ' + 'in xxx=yyy format will be merged into config file. If the value to ' + 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' + 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' + 'Note that the quotation marks are necessary and that no white space ' + 'is allowed.') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + cfg = Config.fromfile(args.config) + if args.cfg_options is not None: + cfg.merge_from_dict(args.cfg_options) + print(cfg) + + +if __name__ == '__main__': + main() ``` -在 MMDetection 被安装进环境(如使用 `pip install mmdet`)以åŽï¼Œæ–°çš„项目å¯ä»¥ç›´æŽ¥åœ¨è‡ªå·±çš„é…置文件ä¸ç»§æ‰¿ MMDetection çš„é…ç½®æ–‡ä»¶è€Œæ— éœ€æ‹·è´ï¼Œä½¿ç”¨æ–¹å¼å¦‚下所示 +示例é…置文件如下: + +`example.py` ```python -_base_ = [ - 'mmdet::_base_/schedules/schedule_1x.py', - 'mmdet::_base_/datasets.coco_instance.py', - 'mmdet::_base_/default_runtime.py' - 'mmdet::_base_/models/faster_rcnn_r50_fpn.py', -] +model = dict(type='CustomModel', in_channels=[1, 2, 3]) +optimizer = dict(type='SGD', lr=0.01) ``` -通过指定 `mmdet::` ,Config 类会去检索 mmdet 包ä¸çš„é…置文件目录,并继承指定的é…置文件。 -实际上,åªè¦ç®—法库的 `setup.py` æ–‡ä»¶ç¬¦åˆ [MMEngine 安装规范](todo),在æ£ç¡®å®‰è£…算法库以åŽï¼Œæ–°çš„项目就å¯ä»¥ä½¿ç”¨ä¸Šè¿°ç”¨æ³•åŽ»ç»§æ‰¿å·²æœ‰ç®—法库的é…ç½®æ–‡ä»¶è€Œæ— éœ€æ‹·è´ã€‚ +我们在命令行里通过 `.` çš„æ–¹å¼æ¥è®¿é—®é…置文件ä¸çš„深层é…置,例如我们想修改å¦ä¹ 率,åªéœ€è¦åœ¨å‘½ä»¤è¡Œæ‰§è¡Œï¼š -### 跨项目使用é…置文件 +```bash +python demo_train.py ./example.py --cfg-options optimizer.lr=0.1 +``` -MMEngine 还æ供了 `get_config` å’Œ `get_model` 两个接å£ï¼Œæ”¯æŒå¯¹ç¬¦åˆ [MMEngine 安装规范](todo) 的算法库ä¸çš„模型和é…置文件åšç´¢å¼•å¹¶è¿›è¡Œ API 调用。通过 `get_model` 接å£å¯ä»¥èŽ·å¾—构建好的模型。通过 `get_config` 接å£å¯ä»¥èŽ·å¾—é…置文件。 +``` +Config (path: ./example.py): {'model': {'type': 'CustomModel', 'in_channels': [1, 2, 3]}, 'optimizer': {'type': 'SGD', 'lr': 0.1}} +``` -`get_model` çš„ä½¿ç”¨æ ·ä¾‹å¦‚ä¸‹æ‰€ç¤ºï¼Œä½¿ç”¨å’Œè·¨é¡¹ç›®ç»§æ‰¿é…置文件相åŒçš„è¯æ³•ï¼ŒæŒ‡å®š `mmdet::`,å³å¯åœ¨ mmdet 包ä¸æ£€ç´¢å¯¹åº”çš„é…置文件并构建和åˆå§‹åŒ–相应模型。 -用户å¯ä»¥é€šè¿‡æŒ‡å®š `pretrained=True` 获得已ç»åŠ 载预è®ç»ƒæƒé‡çš„模型以进行è®ç»ƒæˆ–者推ç†ã€‚ +我们æˆåŠŸåœ°æŠŠå¦ä¹ 率从 0.01 ä¿®æ”¹æˆ 0.1。如果想改å˜åˆ—表ã€å…ƒç»„类型的é…置,如上例ä¸çš„ `in_channels`,则需è¦åœ¨å‘½ä»¤è¡Œèµ‹å€¼æ—¶ç»™ `()`,`[]` å¤–åŠ ä¸ŠåŒå¼•å·ï¼š -```python -from mmengine import get_model -model = get_model('mmdet::faster_rcnn/faster_rcnn_r50_fpn_1x_coco', pretrained=True) +```bash +python demo_train.py ./example.py --cfg-options model.in_channels="[1, 1, 1]" ``` -`get_config` çš„ä½¿ç”¨æ ·ä¾‹å¦‚ä¸‹æ‰€ç¤ºï¼Œä½¿ç”¨å’Œè·¨é¡¹ç›®ç»§æ‰¿é…置文件相åŒçš„è¯æ³•ï¼ŒæŒ‡å®š `mmdet::`,å³å¯å®žçŽ°åŽ» mmdet 包ä¸æ£€ç´¢å¹¶åŠ 载对应的é…置文件。 -用户å¯ä»¥åŸºäºŽè¿™æ ·å¾—到的é…置文件进行推ç†ä¿®æ”¹å¹¶è‡ªå®šä¹‰è‡ªå·±çš„算法模型。 -åŒæ—¶ï¼Œå¦‚果用户指定 `pretrained=True` ,得到的é…置文件ä¸ä¼šæ–°å¢ž `model_path` å—段,指定了对应模型预è®ç»ƒæƒé‡çš„路径。 +``` +Config (path: ./example.py): {'model': {'type': 'CustomModel', 'in_channels': [1, 1, 1]}, 'optimizer': {'type': 'SGD', 'lr': 0.01}} +``` -```python -from mmengine import get_config -cfg = get_config('mmdet::faster_rcnn/faster_rcnn_r50_fpn_1x_coco', pretrained=True) -model_path = cfg.model_path +`model.in_channels` å·²ç»ä»Ž \[1, 2, 3\] ä¿®æ”¹æˆ \[1, 1, 1\]。 -from mmdet.models import build_model -model = build_model(cfg.model) -load_checkpoint(model, model_path) +```{note} +上述æµç¨‹åªæ”¯æŒåœ¨å‘½ä»¤è¡Œé‡Œä¿®æ”¹å—符串ã€æ•´åž‹ã€æµ®ç‚¹åž‹ã€å¸ƒå°”åž‹ã€Noneã€åˆ—表ã€å…ƒç»„类型的é…置项。对于列表ã€å…ƒç»„类型的é…置,里é¢æ¯ä¸ªå…ƒç´ 的类型也必须为上述七ç§ç±»åž‹ä¹‹ä¸€ã€‚ ``` ### 导入自定义 Python æ¨¡å— å°†é…置与注册器结åˆèµ·æ¥ä½¿ç”¨æ—¶ï¼Œå¦‚果我们往注册器ä¸æ³¨å†Œäº†ä¸€äº›è‡ªå®šä¹‰çš„类,就å¯èƒ½ä¼šé‡åˆ°ä¸€äº›é—®é¢˜ã€‚å› ä¸ºè¯»å–é…置文件的时候,这部分代ç å¯èƒ½è¿˜æ²¡æœ‰è¢«æ‰§è¡Œåˆ°ï¼Œæ‰€ä»¥å¹¶æœªå®Œæˆæ³¨å†Œè¿‡ç¨‹ï¼Œä»Žè€Œå¯¼è‡´æž„建自定义类的时候报错。 -例如我们新实现了一ç§ä¼˜åŒ–器 `SuperOptim`,相应代ç 在 my_package/my_module.py ä¸ã€‚ +例如我们新实现了一ç§ä¼˜åŒ–器 `CustomOptim`,相应代ç 在 `my_module.py` ä¸ã€‚ ```python from mmengine.registry import OPTIMIZERS @OPTIMIZERS.register_module() -class SuperOptim: +class CustomOptim: pass ``` -我们为这个优化器的使用写了一个新的é…置文件 `optimizer_cfg.py`: +我们为这个优化器的使用写了一个新的é…置文件 `custom_imports.py`: ```python -optimizer = dict(type='SuperOptim') +optimizer = dict(type='CustomOptim') ``` -那么就需è¦åœ¨è¯»å–é…ç½®æ–‡ä»¶å’Œæž„é€ ä¼˜åŒ–å™¨ä¹‹å‰ï¼Œå¢žåŠ 一行 `from my_package import my_module` æ¥ä¿è¯å°†è‡ªå®šä¹‰çš„ç±» `SuperOptim` 注册到 OPTIMIZERS 注册器ä¸ï¼š +那么就需è¦åœ¨è¯»å–é…ç½®æ–‡ä»¶å’Œæž„é€ ä¼˜åŒ–å™¨ä¹‹å‰ï¼Œå¢žåŠ 一行 `import my_module` æ¥ä¿è¯å°†è‡ªå®šä¹‰çš„ç±» `CustomOptim` 注册到 OPTIMIZERS 注册器ä¸ï¼š +为了解决这个问题,我们给é…置文件定义了一个ä¿ç•™å—段 `custom_imports`,用于将需è¦æå‰å¯¼å…¥çš„ Python 模å—,直接写在é…置文件ä¸ã€‚对于上述例å,就å¯ä»¥å°†é…置文件写æˆå¦‚下: + +`custom_imports.py` ```python -from mmengine import Config -from mmengine.Registry import OPTIMIZERS +custom_imports = dict(imports=['my_module'], allow_failed_imports=False) +optimizer = dict(type='CustomOptim') +``` -from my_package import my_module +è¿™æ ·æˆ‘ä»¬å°±ä¸ç”¨åœ¨è®ç»ƒä»£ç ä¸å¢žåŠ 对应的 import è¯å¥ï¼Œåªéœ€è¦ä¿®æ”¹é…置文件就å¯ä»¥å®žçŽ°éžä¾µå…¥å¼å¯¼å…¥è‡ªå®šä¹‰æ³¨å†Œæ¨¡å—。 -cfg = Config.fromfile('config_super_optim.py') -optimizer = OPTIMIZERS.build(cfg.optimizer) +```python +cfg = Config.fromfile('custom_imports.py') + +from mmengine.registry import OPTIMIZERS + +custom_optim = OPTIMIZERS.build(cfg.optimizer) +print(custom_optim) ``` -è¿™æ ·å°±ä¼šå¯¼è‡´é™¤äº†ä¿®æ”¹é…置文件之外,还需è¦æ ¹æ®é…置文件的内容,æ¥å¯¹åº”修改è®ç»ƒæºä»£ç (å³å¢žåŠ 一些 import è¯å¥ï¼‰ï¼Œè¿èƒŒäº†æˆ‘们希望仅通过修改é…置文件就能控制模å—æž„é€ å’Œä½¿ç”¨çš„åˆè¡·ã€‚ +``` +<my_module.CustomOptim object at 0x7f6983a87970> +``` -为了解决这个问题,我们给é…置文件定义了一个ä¿ç•™å—段 `custom_imports`,用于将需è¦æå‰å¯¼å…¥çš„ Python 模å—,直接写在é…置文件ä¸ã€‚对于上述例å,就å¯ä»¥å°†é…置文件写æˆå¦‚下: +### 跨项目继承é…置文件 + +为了é¿å…基于已有算法库开å‘新项目时需è¦å¤åˆ¶å¤§é‡çš„é…置文件,MMEngine çš„é…置类支æŒé…置文件的跨项目继承。例如我们基于 MMDetection +å¼€å‘新的算法库,需è¦ä½¿ç”¨ä»¥ä¸‹ MMDetection çš„é…置文件: + +```text +configs/_base_/schedules/schedule_1x.py +configs/_base_/datasets.coco_instance.py +configs/_base_/default_runtime.py +configs/_base_/models/faster_rcnn_r50_fpn.py +``` + +如果没有é…置文件跨项目继承的功能,我们就需è¦æŠŠ MMDetection çš„é…置文件拷è´åˆ°å½“å‰é¡¹ç›®ï¼Œè€Œæˆ‘们现在åªéœ€è¦å®‰è£… MMDetection +(如使用 `mim install mmdet`),在新项目的é…置文件ä¸æŒ‰ç…§ä»¥ä¸‹æ–¹å¼ç»§æ‰¿ MMDetection çš„é…置文件: + +`cross_repo.py` ```python -custom_imports = dict(imports=['my_package.my_module'], allow_failed_imports=False) -optimizer = dict(type='SuperOptim') +_base_ = [ + 'mmdet::_base_/schedules/schedule_1x.py', + 'mmdet::_base_/datasets/coco_instance.py', + 'mmdet::_base_/default_runtime.py', + 'mmdet::_base_/models/faster_rcnn_r50_fpn.py', +] ``` -è¿™æ ·æˆ‘ä»¬å°±ä¸ç”¨åœ¨è®ç»ƒä»£ç ä¸å¢žåŠ 对应的 import è¯å¥ï¼Œåªéœ€è¦ä¿®æ”¹é…置文件就å¯ä»¥å®žçŽ°éžä¾µå…¥å¼å¯¼å…¥è‡ªå®šä¹‰æ³¨å†Œæ¨¡å—。 +我们å¯ä»¥åƒåŠ 载普通é…ç½®æ–‡ä»¶ä¸€æ ·åŠ è½½ `cross_repo.py` + +```python +cfg = Config.fromfile('cross_repo.py') +print(cfg.train_cfg) +``` + +``` +{'type': 'EpochBasedTrainLoop', 'max_epochs': 12, 'val_interval': 1, '_scope_': 'mmdet'} +``` + +通过指定 `mmdet::` ,Config 类会去检索 mmdet 包ä¸çš„é…置文件目录,并继承指定的é…置文件。 +实际上,åªè¦ç®—法库的 `setup.py` æ–‡ä»¶ç¬¦åˆ [MMEngine 安装规范](todo),在æ£ç¡®å®‰è£…算法库以åŽï¼Œæ–°çš„项目就å¯ä»¥ä½¿ç”¨ä¸Šè¿°ç”¨æ³•åŽ»ç»§æ‰¿å·²æœ‰ç®—法库的é…ç½®æ–‡ä»¶è€Œæ— éœ€æ‹·è´ã€‚ + +### 跨项目获å–é…置文件 + +MMEngine 还æ供了 `get_config` å’Œ `get_model` 两个接å£ï¼Œæ”¯æŒå¯¹ç¬¦åˆ [MMEngine 安装规范](todo) 的算法库ä¸çš„模型和é…置文件åšç´¢å¼•å¹¶è¿›è¡Œ API 调用。通过 `get_model` 接å£å¯ä»¥èŽ·å¾—构建好的模型。通过 `get_config` 接å£å¯ä»¥èŽ·å¾—é…置文件。 + +`get_model` çš„ä½¿ç”¨æ ·ä¾‹å¦‚ä¸‹æ‰€ç¤ºï¼Œä½¿ç”¨å’Œè·¨é¡¹ç›®ç»§æ‰¿é…置文件相åŒçš„è¯æ³•ï¼ŒæŒ‡å®š `mmdet::`,å³å¯åœ¨ mmdet 包ä¸æ£€ç´¢å¯¹åº”çš„é…置文件并构建和åˆå§‹åŒ–相应模型。 +用户å¯ä»¥é€šè¿‡æŒ‡å®š `pretrained=True` 获得已ç»åŠ 载预è®ç»ƒæƒé‡çš„模型以进行è®ç»ƒæˆ–者推ç†ã€‚ + +```python +from mmengine import get_model + +model = get_model( + 'mmdet::faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py', pretrained=True) +print(type(model)) +``` + +``` +http loads checkpoint from path: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth +<class 'mmdet.models.detectors.faster_rcnn.FasterRCNN'> +``` + +`get_config` çš„ä½¿ç”¨æ ·ä¾‹å¦‚ä¸‹æ‰€ç¤ºï¼Œä½¿ç”¨å’Œè·¨é¡¹ç›®ç»§æ‰¿é…置文件相åŒçš„è¯æ³•ï¼ŒæŒ‡å®š `mmdet::`,å³å¯å®žçŽ°åŽ» mmdet 包ä¸æ£€ç´¢å¹¶åŠ 载对应的é…置文件。 +用户å¯ä»¥åŸºäºŽè¿™æ ·å¾—到的é…置文件进行推ç†ä¿®æ”¹å¹¶è‡ªå®šä¹‰è‡ªå·±çš„算法模型。 +åŒæ—¶ï¼Œå¦‚果用户指定 `pretrained=True` ,得到的é…置文件ä¸ä¼šæ–°å¢ž `model_path` å—段,指定了对应模型预è®ç»ƒæƒé‡çš„路径。 + +```python +from mmengine import get_config + +cfg = get_config( + 'mmdet::faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py', pretrained=True) +print(cfg.model_path) + +``` + +``` +https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth +``` diff --git a/mmengine/config/config.py b/mmengine/config/config.py index 18c7694336125eeccff9842d27ab1c207d351467..789901f48bce3dfa7119b3cf2c4e2d63522f0586 100644 --- a/mmengine/config/config.py +++ b/mmengine/config/config.py @@ -882,23 +882,22 @@ class Config: Defaults to True. Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} + >>> from mmengine import Config + >>> # Merge dictionary element + >>> options = {'model.backbone.depth': 50, 'model.backbone.with_cp': True} >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - + >>> cfg._cfg_dict + {'model': {'backbone': {'type': 'ResNet', 'depth': 50, 'with_cp': True}}} >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) + >>> cfg = Config( + >>> dict(pipeline=[dict(type='LoadImage'), + >>> dict(type='LoadAnnotations')])) >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - """ + >>> cfg._cfg_dict + {'pipeline': [{'type': 'SelfLoadImage'}, {'type': 'LoadAnnotations'}]} + """ # noqa: E501 option_cfg_dict: dict = {} for full_key, v in options.items(): d = option_cfg_dict