GhostNet
GhostNet: More Features from Cheap Operations
该论文为CVPR 2020的一篇论文,作者来自华为诺亚方舟实验室。
在这篇论文中提出了一个ghost模块,可以通过廉价操作来生成更多的特征图。作者认为,在现有的cnn模型中,会包含丰富的冗余信息,即一些特征图可以由其它特征图廉价操作(线性变换)得到。下面具体介绍下GhostNet模块的实现方式。
ghost module
首先论文中介绍了Ghost Module,通过普通卷积得到m个通道的特征,通过对这m个通道做可分离的线性变换,我们可以得到 n = m × s n=m\times s n=m×s个通道的特征。
kernel_initializer = tf.contrib.layers.variance_scaling_initializer(2.0)
@layer_register(log_shape=True)
def MyDepthConv(x, kernel_shape, channel_mult=1, padding='SAME', stride=1, rate=1, data_format='NHWC',
W_init=None, activation=tf.identity):
in_shape = x.get_shape().as_list()
if data_format=='NHWC':
in_channel = in_shape[3]
stride_shape = [1, stride, stride, 1]
elif data_format=='NCHW':
in_channel = in_shape[1]
stride_shape = [1, 1, stride, stride]
out_channel = in_channel * channel_mult
if W_init is None:
W_init = kernel_initializer
kernel_shape = shape2d(kernel_shape) #[kernel_shape, kernel_shape]
filter_shape = kernel_shape + [in_channel, channel_mult]
W = tf.get_variable('DW', filter_shape, initializer=W_init)
conv = tf.nn.depthwise_conv2d(x, W, stride_shape, padding=padding, rate=[rate,rate], data_format=data_format)
if activation is None:
return conv
else:
return activation(conv, name='output')
def GhostModule(name, x, filters, kernel_size, dw_size, ratio, padding='SAME', strides=1, data_format='NHWC', use_bias=False,
activation=tf.identity):
with tf.variable_scope(name):
init_channels = math.ceil(filters / ratio)
x = Conv2D('conv1', x, init_channels, kernel_size, strides=strides, activation=activation, data_format=data_format,
kernel_initializer=kernel_initializer, use_bias=use_bias)
if ratio == 1:
return x #activation(x, name='output')
dw1 = MyDepthConv('dw1', x, [dw_size,dw_size], channel_mult=ratio-1, stride=1, data_format=data_format, activation=activation)
dw1 = dw1[:,:,:,:filters-init_channels] if data_format=='NHWC' else dw1[:,:filters-init_channels,:,:]
x = tf.concat([x, dw1], 3 if data_format=='NHWC' else 1)
return x
ghost block
在本文中有两种ghost block结构,一种为步长为1,一种为步长为2.两种block的结构分别如下如所示。
其中在步长等于2时,shortcut通过一个步长为2的深度可分离卷积进行降维计算。代码如下:
res = DepthConv(end_point, net, conv_def.kernel, stride=layer_stride,
data_format='NHWC', activation=BNNoReLU)
res = MyConv2D(end_point, res, depth(conv_def.depth), [1, 1], strides=1, data_format='NHWC',activation=BNNoReLU, use_bias=False)
def MyConv2D(
inputs,
filters,
kernel_size,
strides=(1, 1),
padding='same',
data_format='channels_last',
dilation_rate=(1, 1),
activation=None,
use_bias=True,
kernel_initializer=kernel_initializer,#tf.contrib.layers.variance_scaling_initializer(2.0),
bias_initializer=tf.zeros_initializer(),
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
):
"""
A wrapper around `tf.layers.Conv2D`.
Some differences to maintain backward-compatibility:
1. Default kernel initializer is variance_scaling_initializer(2.0).
2. Default padding is 'same'.
Variable Names:
* ``W``: weights
* ``b``: bias
"""
with rename_get_variable({'kernel': 'W', 'bias': 'b'}):
layer = tf.layers.Conv2D(
filters,
kernel_size,
strides=strides,
padding=padding,
data_format=data_format,
dilation_rate=dilation_rate,
activation=activation,
use_bias=use_bias,
kernel_initializer=kernel_initializer,
bias_initializer=bias_initializer,
kernel_regularizer=kernel_regularizer,
bias_regularizer=bias_regularizer,
activity_regularizer=activity_regularizer,
_reuse=tf.get_variable_scope().reuse)
ret = layer.apply(inputs, scope=tf.get_variable_scope())
ret = tf.identity(ret, name='output')
ret.variables = VariableHolder(W=layer.kernel)
if use_bias:
ret.variables.b = layer.bias
return ret
def DepthConv(x, kernel_shape, padding='SAME', stride=1, data_format='NHWC',
W_init=None, activation=tf.identity):
in_shape = x.get_shape().as_list()
if data_format=='NHWC':
in_channel = in_shape[3]
stride_shape = [1, stride, stride, 1]
elif data_format=='NCHW':
in_channel = in_shape[1]
stride_shape = [1, 1, stride, stride]
out_channel = in_channel
channel_mult = out_channel // in_channel
if W_init is None:
W_init = kernel_initializer
kernel_shape = shape2d(kernel_shape) #[kernel_shape, kernel_shape]
filter_shape = kernel_shape + [in_channel, channel_mult]
W = tf.get_variable('W', filter_shape, initializer=W_init)
conv = tf.nn.depthwise_conv2d(x, W, stride_shape, padding=padding, data_format=data_format)
return activation(conv, name='output')