Model initialization @ configurable and from in Detectron2_ config

Function decorator in Detectron2

In__ init__ You can usually see @ configurable on the () function. Its function is to initialize the model with the parameters in the config file. Simply put, it does not execute first__ init__ () function, but directly enter config Py's configurable function through from_ The config function takes out variables and initializes__ init__ ().

Let's tidy up the decorator in Python first

The function decorator A() is used to decorate another function B(), and the bottom layer performs two steps: (1) pass B as a parameter to the A() function; (2) Feed back the return value of the completion of the A() function to B. Referring to [1], we conducted experiments:

def funA(fn):
    print("A_1")
    fn("B")
    print("A_2")
    return "Result"

@funA
def funB(str):
    print(str)

It can be seen that funB is essentially a string in Python, which is equivalent to funB = funA(funB). The function decorator has the function of assignment here, and the attributes of the decorated function change. Therefore, in detectron2, it is decorated by configurable__ init__ () function, should be:__ init__ = configurable(__init__).

What's his use? His use is like his name, wrapping and decorating this function. To put it simply, funC is usually defined in the configurable() function. funC will execute some things before funB, then execute funB, then execute some things, and finally return funC so that funB = funC. Examples are as follows:

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)

At this time, there is only one problem left, that is, the name of the original function and the comment document (docstring) are rewritten [2] (the name of funB in the above figure becomes wrapped). This can be solved with functools.wraps of Python, as follows:

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)

The only difference from the previous code is the addition of the modifier @ wraps(fn) to the wrapped() function

This ensures that the decorated function still has the original properties and eliminates the side effects of the decorated function.

Back to 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

As you can see, after being actually rewritten__ init__ In fact, the wrapped function is executed. Specifically, through type (self) from_ Config function__ init__ Required parameters, initialization__ init__.

With box_ head. Take FastRCNNConvFChead() in py as an example, which is first used by @ ROI_BOX_HEAD_REGISTRY.register() decoration, and then through ROI_BOX_HEAD_REGISTRY.get("FastRCNNConvFChead") (). Therefore, the real instantiation part is implemented in:

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)

In roi_head.py, box_head = build_box_head(cfg, ShapeSpec()).

#1 * args and * * kwargs here correspond to the first half of the parameters (1, 2, 3,a=1,b=2) and the second half of the key value pair.

#2 self is the instance, and cls is the class itself.

Reference:

[1] https://www.cnblogs.com/MorStar/p/14956423.html

[2] https://www.runoob.com/w3cnote/python-func-decorators.html

Keywords: Python

Added by ahsanmani on Sat, 18 Dec 2021 02:14:53 +0200