文章目录
openmmlab教程2-MMCV使用
2. MMCV 使用
项目核心组成可以分为config、builder、runner这三部分。
MMCV 中存在两个非常核心的基础类: Registry 和 Config。其中 Registry 用于提供全局类注册器功能,而 Config 则主要是提供各种格式的配置文件解析功能。
2.1 读取配置文件 config
Config 类用于解析 OpenMMLab 开源框架的各种格式配置文件,不仅如此还实现了很多方便使用的 API,例如配置读写、配置打印、配置合并等等功能,非常实用。其功能简单归纳如下:
- 通过 dict 生成 config
- 通过配置文件生成 config
- 自动替换预定义变量
- 导入自定义模块
- 合并多个配置文件
- 从 base 文件中合并
- 从多个 base 文件中合并
- 合并字典到配置
- allow_list_keys 模式合并
- 允许删掉特定内容
- pretty_text 和 dump
1) 简单使用
填写配置文件test.py
a = 1
b = dict(b1=[0, 1, 2], b2=None)
c = (1, 2)
d = 'string'
测试
from mmcv.utils import Config
cfg = Config.fromfile('test.py')
print(cfg)
结果如下
Config (path: test.py): {
'a': 1, 'b': {
'b1': [0, 1, 2], 'b2': None}, 'c': (1, 2), 'd': 'string'}
2) 继承配置文件
创建 config_a.py
a = 1
b = dict(b1=[0, 1, 2], b2=None)
创建 config_b.py
_base_ = './config_a.py'
c = (1, 2)
d = 'string'
创建 train.py
from mmcv.utils import Config
cfg = Config.fromfile('config_b.py')
print(cfg)
结果
Config (path: config_b.py): {
'a': 1, 'b': {
'b1': [0, 1, 2], 'b2': None}, 'c': (1, 2), 'd': 'string'}
config_b.py继承了config_a.py的变量
这个也就是官方给的配置文件中,为啥会有这么**_base_**
2.2 注册器的使用
MMCV 核心组件分析(五): Registry - 知乎 (zhihu.com)
我们使用注册器的目的,就是为了注册
自己的模型
和自己的数据集
。
我们常常看到如下类似代码
backbone=dict(
type='ResNet', # 待实例化的类名
depth=50, # 后面的都是对于的类初始化参数
num_stages=4,
out_indices=(0, 1, 2, 3),
frozen_stages=1,
norm_cfg=dict(type='BN', requires_grad=True),
norm_eval=True,
style='pytorch'),
这个呢?就是在注册一个 backbone为resnet的网络。如果我们想自己注册一个自己设计的网络,该怎么办?
1) python中装饰器的作用
首先得有装饰器知识的概念,如果懂了。可以直接跳过
python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能
装饰器的作用就是用一个新函数封装旧函数(是旧函数代码不变的情况下增加功能)然后会返回一个新函数,新函数就叫做装饰器,一般为了简化装饰器会用语法糖@新函数来简化
例子:
这是一段代码,但功能太少,要对这个进行增强,但又不能改变代码。
def hello():
return "hello world!"
现在我们的需求是要增强hello()函数的功能,希望给返回加上HTML标签,比如hello world,但要求我们不得改变hello()函数原来的定义。
def makeitalic(fun):#makitalic传了一个新函数
def wrapped():#内部函数
return "<i>"+fun()+"</i>"#要加的新功能
return wrapped#返回的是wrapped函数功能
def hello():#对这个功能进行增强
return "hello world!"
#makeitalic里面传入了hello函数,然后内部函数fun()函数也就相当于hello函数了
hello_2=makeitalic(hello)
#打印新函数,返回的就是<i>hello world!</i>
print(hello_2())
结果如下
<i>hello world</i>
为了增强原函数hello的功能,定义了一个函数,它接收原函数作为参数,并返回一个新的函数,在这个返回的函数中,执行了原函数,并对原函数的功能进行了增强。
事实上,makeitalic就是一个装饰器(decorator),它封装了原函数hello,并返回了一个新函数,用于增强原函数的功能,并将其赋值给hello。
一般情况下,我们使用装饰器提供的**@语法糖(Syntactic Sugar)**,来简化上面的操作。
####使用@语法糖
def makeitalic(fun):
def wrapped():
return "<i>" + fun() + "</i>"
return wrapped
import torch.nn as nn
from mmcv.cnn import xavier_init
conv1 = nn.Conv2d(3, 3, 1)
# xavier_init(module, gain=1, bias=0, distribution='normal')
@makeitalic#使用了装饰器可以直接调用,不需要赋值了
def hello():
return "hello world"
print(hello())#使用了装饰器可以直接调用,不需要赋值了
2) 简单例子(自定义网络)
使用 registry
(注册器)管理代码库中的模型,需要以下三个步骤。
- 创建一个构建方法(可选,在大多数情况下您可以只使用默认方法)
- 创建注册器
- 使用此注册器来管理模块
1. 创建构建器
创建builder.py。内容如下
from mmcv.utils import Registry
# 创建转换器(converter)的注册器(registry)
MODELS = Registry('models')
# DATASETS = Registry('dataset')
2. 创建注册器
创建注册器resnet.py。意思为网络的内容
from builder import MODELS
# 使用注册器管理模块
@MODELS.register_module()
class my_ResNet(object):
def __init__(self, a, b):
self.a = a
self.b = b
def my_print(self):
print(self.a, self.b)
# 使用注册器管理模块
@MODELS.register_module()
def my_resnet50(a, b):
return my_ResNet(a, b)
**3.使用此注册器来管理模块 **
创建train.py
from builder import MODELS
from mmcv.utils import build_from_cfg
from resnet import my_resnet50,my_ResNet # 注册进系统
# 配置文件
resnet_cfg = dict(type='my_ResNet', a=0, b="这个是类注册")
resnet1_cfg = dict(type='my_resnet50', a=1, b="这个返回的是实例")
# 创建配置文件
class_converter = build_from_cfg(resnet_cfg,MODELS)
converter=build_from_cfg(resnet1_cfg,MODELS)
print(class_converter)
print(converter)
# 调用函数
class_converter.my_print()
converter.my_print()
结果
<resnet.my_ResNet object at 0x000001911C081A90>
<resnet.my_ResNet object at 0x000001912B25E970>
0 这个是类注册
1 这个返回的是实例
具体文件如下图所示
3) 自定义构建函数
没看懂啥意思
from mmcv.utils import Registry
# 创建一个构建函数
def build_converter(cfg, registry, *args, **kwargs):
cfg_ = cfg.copy()
converter_type = cfg_.pop('type')
if converter_type not in registry:
raise KeyError(f'Unrecognized converter type {
converter_type}')
else:
converter_cls = registry.get(converter_type)
converter = converter_cls(*args, **kwargs, **cfg_)
return converter
# 创建一个用于转换器(converters)的注册器,并传递(registry)``build_converter`` 函数
CONVERTERS = Registry('converter', build_func=build_converter)
print(CONVERTERS)
4) Register实现
register源码,类如何实现的。看下面两个文件
2.3 Hook
MMCV 核心组件分析(六): Hook - 知乎 (zhihu.com)
2.4 Runner
[MMCV 核心组件分析(七): Runner - 知乎 (zhihu.com)](