As mentioned in the previous blog , MMDetection only needs 3 steps to build the training algorithm: 1) Prepare the data set 2) Write the configuration file 3) Execute train.py
the file to start training. But the last blog only briefly introduced the general process. This article will analyze the configuration file construction mechanism from the perspective of source code. The main reference is the official documentation (it has to be said that there are so many tutorials on the Internet, and finally found that the best is the official document) .
1. File structure
The configuration files that MMDetection has implemented are all located in ./configs
folders, and the configuration files are named according to uniform rules. You can refer to the official documentation for the meaning of specific fields.
# 配置文件命名规则
{
model}_[model setting]_{
backbone}_{
neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{
schedule}_{
dataset}
These configuration files are obtained by inheriting the original configuration file./configs/_base_
. The original configuration file is located under the folder. There are 4 basic component types, namely datasets, models, training strategies (schedules) and runtime defaults. configuration (default_runtime). Generally speaking, when we create a new configuration file, we need to inherit an original configuration file from the above four components, and then adjust it according to our own needs. Of course, if you are building a brand new method that does not share structure with any existing methods, you can also create a new one without inheriting any original configuration files.
In order to understand what necessary information is contained in a configuration file, here I create a new one test_config.py
, which inherits at least 4 original configuration files:
_base_ = [
'mmdetection/configs/_base_/models/fast_rcnn_r50_fpn.py', # models
'mmdetection/configs/_base_/datasets/coco_detection.py', # datasets
'mmdetection/configs/_base_/schedules/schedule_1x.py', # schedules
'mmdetection/configs/_base_/default_runtime.py', # defualt_runtime
]
Then use to mmdetection/tools/misc/print_config.py
print the complete configuration file information. In order to make the entire configuration file structure look clearer, I replaced some unimportant information with ellipses:
# 1. 模型配置(models) =========================================
model = dict(
type='FastRCNN', # 模型名称是FastRCNN
backbone=dict( # BackBone是ResNet
type='ResNet',
...,
),
neck=dict( # Neck是FPN
type='FPN',
...,
),
roi_head=dict( # Head是StandardRoIHead
type='StandardRoIHead',
...,
loss_cls=dict(...), # 分类损失函数
loss_bbox=dict(...), # 回归损失函数
),
train_cfg=dict( # 训练参数配置
assigner=dict(...), # BBox Assigner
sampler=dict(...), # BBox Sampler
...
),
test_cfg =dict( # 测试参数配置
nms=dict(...), # NMS后处理
...,
)
)
# 2. 数据集配置(datasets) =========================================
dataset_type = '...' # 数据集名称
data_root = '...' # 数据集根目录
img_norm_cfg = dict(...) # 图像归一化参数
train_pipeline = [ # 训练数据处理Pipeline
...,
dict(type='Normalize', **img_norm_cfg),
...
]
test_pipeline = [...] # 测试数据处理Pipeline
data = dict(
samples_per_gpu=2, # batch_size
workers_per_gpu=2, # GPU数量
train=dict( # 训练集配置
type=dataset_type,
ann_file=data_root + 'annotations/instances_train2017.json', # 标注问加你
img_prefix=data_root + 'train2017/', # 图像前缀
pipline=trian_pipline, # 数据预处理pipeline
),
val=dict( # 验证集配置
...,
pipline=test_pipline,
),
test=dict( # 测试集配置
...,
pipline=test_pipline,
)
)
# 3. 训练策略配置(schedules) =========================================
evaluation = dict(interval=1, metric='bbox')
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=0.001,
step=[8, 11])
runner = dict(type='EpochBasedRunner', max_epochs=12)
# 4. 运行配置(runtime) =========================================
checkpoint_config = dict(interval=1)
log_config = dict(interval=50, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]
The configuration file contains four basic components: datasets, models, schedules, and runtime. In addition to the network structure of the model itself, the model also includes configuration parameters such as bbox assigner, bbox sampler, and nms; data mainly includes relevant parameters of batch_size, training set, verification set, and test set; schedules include training such as optimizer, learning rate, and number of iterations Configuration information of process-related parameters; runtime is some configuration related to log printing, parameter saving, and distributed training. Combined with the algorithm flow chart in the previous blog post, the corresponding configuration parameters of each module can be found in the configuration file: the modules from Backbone to Loss are all in the configuration parameters of the model, and tricks are reflected in the configuration of schedules . So as long as the configuration file is written, the subsequent MMDetection framework will automatically parse the configuration file and construct the entire algorithm process.
In general, the configuration file is dict
a text file composed of a series of type variables, which also contains some intermediate variables, such as data_root
, , test_pipeline
etc. These intermediate variables can be dict
referenced by others. The configuration file can _base_ = ['...']
inherit the parameters in other configuration files through the method, and then complete the modification of specific parameters by rewriting, and also create a blank file, and then complete the assignment of each parameter from scratch.
2. Config class
MMDetection uses the Config class in the MMCV library to complete the parsing of configuration files. The Config class is used to manipulate configuration files, and it supports loading configuration from a variety of file formats, including python, json and yaml. It provides a dictionary object-like interface to get and set values.
2.1 Read the configuration file
Generally used Config.fromfile(filename)
to read configuration files (or pass in a dict directly) and return a Config class:
from mmcv import Config
cfg = Config.fromfile('../configs/test_config.py')
fromfile()
The source code of the function is as follows, and its core function is _file2dict()
. _file2dict()
According to the text order, the configuration file will be parsed according to the format of key = value, and a cfg_dict
dictionary named will be obtained. If there is a field, the function will be called again _base_
for each file path included , and the configuration parameters contained in the file will be added to the , to implement the inheritance function of the configuration file. It should be noted that the key values contained in different files will be verified internally , and duplicate key values are not allowed in different base files , otherwise MMCV does not know which base file to take as the standard._base_
_file2dict()
cfg_dict
_file2dict()
_base_
@staticmethod
def fromfile(filename,
use_predefined_variables=True,
import_custom_modules=True):
cfg_dict, cfg_text = Config._file2dict(filename,
use_predefined_variables)
# import_modules_from_strings()是根据字符串列表对应的模块
if import_custom_modules and cfg_dict.get('custom_imports', None):
import_modules_from_strings(**cfg_dict['custom_imports'])
return Config(cfg_dict, cfg_text=cfg_text, filename=filename)
test_config.py
For example, after passing through the function built above _file2dict()
, the following dictionary data will be obtained, and then a Config object will be initialized and returned to fromfile()
the superior call.
There are two other points that need to be added. One is that when constructing the Config object, the python dict
data type will be converted into ConfigDict
a type for processing. ConfigDict
It is a subclass in the third-party library addict Dict
, because python's native dict
type does not support .属性
the access method, especially dict
when multiple layers of dict are nested inside, if the access method is based on the key, the code is very inefficient to write, and Dict
the class passes The overridden __getattr__()
method implements .属性
the access method. Therefore, the inherited Dict
ones ConfigDict
also support .属性
access to each member value in the dictionary.
from mmcv import ConfigDict
model = ConfigDict(dict(backbone=dict(type='ResNet', depth=50)))
print(model.backbone.type) # 输出 'ResNet'
The second is that, in order to be compatible with the decimal point in the configuration file name, MMCV _file2dict()
will 'C:/Users/ADMINI~1/AppData/Local/Temp/'
create a temporary folder. If the C drive has access rights, an error may appear, but this problem will only appear in the Windows system.
2.2 Modify configuration parameters
After knowing the internal logic of MMCV parsing configuration files, it is naturally clear how to modify the values of configuration parameters. Because the dictionary _file2dict()
is built according to the order of the text , the key value written later can overwrite the original value (if the variable type is list, the list will be replaced entirely, and a certain item cannot be modified) . Taking modifying the optimizer as an example, the original inherited optimizer is SGD with a learning rate of 0.02:
# 原来继承的优化器
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
Now if you want to _base_
adjust the inherited learning rate to 0.001, you can directly add a line to the current configuration file:
# 修改学习率
optimizer = dict(lr=0.001)
This will only modify the parameters in the optimizer key value lr
, and other parameters will not be affected. The current optimizer configuration becomes:
# 修改学习率后的SGD
optimizer = dict(type='SGD', lr=0.001, momentum=0.9, weight_decay=0.0001)
If you want to change to a new optimizer now, but the parameters of the two optimizers are not compatible, you need to delete the original key value and replace it with a new set of key values, which can be achieved through configuration _delete_=True
:
# 将原来的SGD替换成AdamW
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.0001)
Then the optimizer replacement is completed. The optimizer parameters of the current configuration file are as follows:
# 当前优化器变为AdamW
optimizer = dict(type='AdamW', lr=0.0001, weight_decay=0.0001)
2.3 View configuration file information
After the call, a Config object will be returned, which contains the two member variables besides the type Config.fromfile()
obtained by parsing the configuration file mentioned above .ConfigDict
_cfg_dict
text
pretty_text
text
What is stored is _base_
the original text information in each configuration file (files inherited in the include), which identifies the path of the configuration file.
pretty_text
It is _cfg_dict
the formatted text of the dictionary content. MMCV internally uses Google's YAPF library to format the dictionary object so that the output conforms to people's reading habits. You can directly view the print(cfg.pretty_text)
complete configuration file information, which mmdetection/tools/misc/print_config.py
is the same as MMDetection. .
In addition, you can also view it conveniently by cfg.dump(filepath)
storing pretty_text
it in a file.