我们先回忆一下普通的卷积操纵,对于一个输入张量,[N,H,W,C]分别表示图片数目、图片高度、宽度和通道数,如果我们设定卷积核[H_kernal,W_kernal,C_in,C_out]分别表示卷积核的高度、宽度、输入通道数也就是卷积核厚度、输出通道数也就是卷积数目。通常卷积核的厚度是等于图片通道数的,也就是说一个卷积核可以是把一片区域内的所有通道都包住。所以平时卷积函数里面写kernal_size都只有[H_kernal,W_kernal]两个参数,因为厚度默认是图片的通道数了,输出通道数也就是卷积核数量会单独作为一个参数设置。例如函数:
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)其中的:filter——是输出通道数目,即卷积核的个数。kernel_size: 两个整数的元组或列表,表示卷积核在高、宽尺寸。
而卷积核的厚度是多少呢?函数已经帮你设置好了,就是按照输入张量的通道数来的。
但是,我们的可分卷积就不一样了,它比较复杂,是由一个depthwise卷积再接一个pointwise卷积完成的。
对depthwise卷积,它的卷积核厚度不是输入张量的通道数,而是1,这个也是函数帮你设置好的。通俗地理解就是卷积核厚度只有一层,然后在输入张量上一层一层地滑动,所以一个卷积核就对应了num_channel_in个输出通道,所以总的输出通道数是num_channel_in*depth_multiple。而pointwise卷积其实就是一个1*1卷积,它的卷积核厚度是num_channel_in*depth_multiple,这就很好了,最终函数的输出通道由filter决定。
所以说,饶了半天,哪怕您压根就不知道什么depthwise卷积、pointwise卷积,您就记住一条,函数输出通道就是filter数目。
tf.layers.separable_conv2d( inputs, filters, kernel_size, strides=(1, 1), padding='valid', data_format='channels_last', dilation_rate=(1, 1), depth_multiplier=1, activation=None, use_bias=True, depthwise_initializer=None, pointwise_initializer=None, bias_initializer=tf.zeros_initializer(), depthwise_regularizer=None, pointwise_regularizer=None, bias_regularizer=None, activity_regularizer=None, depthwise_constraint=None, pointwise_constraint=None, bias_constraint=None, trainable=True, name=None, reuse=None )
关键参数说明:
inputs: 输入张量.
filters:输出通道数目,即卷积核的个数。 kernel_size: 两个整数的元组或列表,表示卷积核在高、宽尺寸。这个卷积核尺寸是参加depthwise卷积时采用的。用法和普通卷积核没区别。strides: 两个整数的元组或列表,表示卷积核在高、宽上移动的步长。padding: "valid" 或"same" depth_multiplier: 通道扩大倍数.其他参数一般为默认值
函数的官方说明:tf.layers.separable_conv2d