概述
Detectron2中的函数装饰器(decorator)
在__init__()函数上通常可以看到@configurable。它的作用是用config文件中的参数去初始化模型。简单来说,它没有首先执行__init__()函数,而是而是直接进入config.py的configurable函数,通过from_config函数取出取出变量,初始化__init__()。
我们先整理一下Python中的decorator
使用函数装饰器A()装饰另一个函数B(),底层执行了2步操作:(1)将B作为参数传给A()函数;(2)将A()函数执行完成的返回值反馈回B。参考[1],我们进行了实验:
def funA(fn):
print("A_1")
fn("B")
print("A_2")
return "Result"
@funA
def funB(str):
print(str)
可以看到,此时funB在Python中本质上是个字符串,相当于funB = funA(funB),函数装饰器在这里有赋值的作用,被装饰函数的属性发生变化。因此,在detectron2中,被configurable装饰的__init__()函数,应为:__init__ = configurable(__init__)。
那他有什么用呢?他的用处就如同他的名字一样,包装修饰这个函数。简单来说,就是在configurable()函数里,通常会定义funC,funC会在执行funB之前,执行一些事情,然后执行funB,然后再执行一些事情,最后将funC返回回去,使得funB = funC。举例如下:
def funA(fn):
def wrapped(str):
print("do something before funA...")
fn(str)
print("do something after funA...")
return wrapped
@funA
def funB(str):
print(str)
此时只剩下一个问题,那就是原函数的名字和注释文档(docstring)被重写了[2](上图funB的名字变成了wrapped)。这可以用Python的functools.wraps来解决,具体如下:
from functools import wraps
def funA(fn):
@wraps(fn)
def wrapped(str):
print("do something before funA...")
fn(str)
print("do something after funA...")
return wrapped
@funA
def funB(str):
print(str)
和上一段代码的唯一区别是给wrapped()函数添加了修饰符@wraps(fn)
这样保证被装饰的函数仍有原来的属性,消除了装饰函数的副作用。
回到Detectron2中:
def configurable(init_func=None, *, from_config=None):
if init_func is not None:
assert (
inspect.isfunction(init_func)
and from_config is None
and init_func.__name__ == "__init__"
), "Incorrect use of @configurable. Check API documentation for examples."
@functools.wraps(init_func)
def wrapped(self, *args, **kwargs):
try:
from_config_func = type(self).from_config
except AttributeError as e:
raise AttributeError(
"Class with @configurable must have a 'from_config' classmethod."
) from e
if not inspect.ismethod(from_config_func):
raise TypeError("Class with @configurable must have a 'from_config' classmethod.")
if _called_with_cfg(*args, **kwargs):
explicit_args = _get_args_from_config(from_config_func, *args, **kwargs)
init_func(self, **explicit_args)
else:
init_func(self, *args, **kwargs)
return wrapped
else:
if from_config is None:
return configurable
assert inspect.isfunction(
from_config
), "from_config argument of configurable must be a function!"
def wrapper(orig_func):
@functools.wraps(orig_func)
def wrapped(*args, **kwargs):
if _called_with_cfg(*args, **kwargs):
explicit_args = _get_args_from_config(from_config, *args, **kwargs)
return orig_func(**explicit_args)
else:
return orig_func(*args, **kwargs)
return wrapped
return wrapper
可以看到,实际上被重写后,__init__实际上执行的是wrapped函数。具体来说,通过type(self).from_config函数拿到__init__所需参数,初始化__init__。
以box_head.py中的FastRCNNConvFChead()为例,它首先被@ROI_BOX_HEAD_REGISTRY.register()装饰(fvcore.common.registry),之后可以通过ROI_BOX_HEAD_REGISTRY.get("FastRCNNConvFChead")()来调用。因此真正的实例化部分实现在:
def build_box_head(cfg, input_shape):
"""
Build a box head defined by `cfg.MODEL.ROI_BOX_HEAD.NAME`.
"""
name = cfg.MODEL.ROI_BOX_HEAD.NAME
return ROI_BOX_HEAD_REGISTRY.get(name)(cfg, input_shape)
在roi_head.py中,box_head = build_box_head(cfg, ShapeSpec())来获取。
#1 这里的*args和**kwargs对应参数(1, 2, 3,a=1,b=2)中的前半部分和键值对的后半部分。
#2 self是实例(instance),cls是类(class)本身。
参考:
[1] https://www.cnblogs.com/MorStar/p/14956423.html
[2] https://www.runoob.com/w3cnote/python-func-decorators.html
最后
以上就是感性黄蜂为你收集整理的Detectron2中的模型初始化@configurable和from_config的全部内容,希望文章能够帮你解决Detectron2中的模型初始化@configurable和from_config所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复