今天在学习darknet53这个网络的时候,发现了@wrap(Conv2D)这么一行代码,这里谈一下我对这个函数装饰器的理解。
详细的@wrap装饰器可以参见[这里],这篇文章只谈对参数的操作。(https://www.runoob.com/w3cnote/python-func-decorators.html)
@wrap这个装饰器传入的是一个已经有的函数a,返回的是一个新的函数b。
那么为什么要进行这样的操作呢,两个函数前后可以有什么变化呢?
我这里的理解是:可以通过@wrap这个装饰器,在函数a的基础上,根据自己的需求,修改一些a中已经存在的参数,然后返回一个新的函数b供我们使用,这样做的好处应该就是在沿用函数a的默认参数的基础上,方便进行我们进行一些修改。
下面是一个简单的例子:
from functools import wraps
#定义一个函数Fun_1 返回name1+name2+name3
#默认的结果就是: N1 +' _ ' + N2 + ' _ ' + N3
def Fun_1(name1 ='N1', name2 = 'N2', name3='N3'):
return name1+ ' _ ' + name2 + ' _ ' + name3
@wraps(Fun_1)
def Fun_2(*args, **kwargs):
#在这里 就可以对Fun1中的参数 进行操作
Fun1_kwargs = {'name3':'N3_new'}#这里可以修改默认的参数值
#还可以根据一个参数的值, 去修改其他参数的值
Fun1_kwargs['name1'] = 'N1_new' if kwargs.get('name2') == 'N2' else 'N1_old'
Fun1_kwargs.update(**kwargs)
return Fun_1(*args, **Fun1_kwargs)
print(Fun_1())
print(Fun_2(name2='N2'))
输出结果:
N1 _ N2 _ N3
N1_new _ N2 _ N3_new
结论:
Fun_1中有name1 name2 name3三个参数,默认情况下分别是N1 N2 N3
通过装饰器生成的Fun_2这个函数实现了两个功能:
1.可以对Fun_1里面传入的默认参数进行修改
例子中实现代码:(Fun1_kwargs = {‘name3’:‘N3_new’})
2.可以根据一个参数的值, 去修改其他参数的值
例子中实现代码:Fun1_kwargs[‘name1’] = ‘N1_new’ if kwargs.get(‘name2’) == ‘N2’ else ‘N1_old’
也就是在Fun_2中,如果我传入了name=‘N2’,那么name1的值就会修改为’N1_new’。
这样就可以理解darknet53中DarknetConv2D这段代码了,其实就是对
from keras.layers import Conv2D这里面的Con2D进行了两处修改变为了Darknet中所需要的卷积方式(为什么要这么改呢 可以去看yolo3的论文):
1.正则化方式改为L2正则化,默认是None
2.当步长为2时, padding的方式才为valid。默认情况下是步长为1,padding为valid。
@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
"""Wrapper to set Darknet parameters for Convolution2D."""
darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'
darknet_conv_kwargs.update(kwargs)
return Conv2D(*args, **darknet_conv_kwargs)
#这里是Con2D里面的所有参数
def __init__(self, filters,
kernel_size,
strides=(1, 1),
padding='valid',
data_format=None,
dilation_rate=(1, 1),
activation=None,
use_bias=True,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None,
**kwargs):
其实我感觉如果不用修饰符生成新的函数也是可以实现这些功能的,但是如果不封装成一个函数的话,可能在搭建网络结构的时候就没那么清晰吧。这样用起来比较方便,不用每次都去设置参数了。