参数:
input: 指需要做卷积的输入图像,要求是一个4维Tensor,具有[batch, height, width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]
filter: 相当于CNN中的卷积核,要求是一个4维Tensor,具有[filter_height, filter_width, in_channels, channel_multiplier]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,输入通道数,输出卷积乘子],同理这里第三维in_channels,就是参数value的第四维
strides: 卷积的滑动步长。
padding: string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同边缘填充方式。
rate: 这里的rate和空洞卷积中的rate作用相同
给定4D输入张量('NHWC'或'NCHW'数据格式)和形状为[filter_height,filter_width,in_channels,channel_multiplier]的卷积核(卷积核包含in_channels个深度为1的卷积核),depthwise_conv2d对每个输入通道应用不同的卷积核(对于每个通道卷积后的结果,从1个通道扩展到channel_multiplier个通道,然后将结果连接在一起。输出具有in_channels * channel_multiplier通道。
可以看做,深度卷积tf.nn.depthwise_conv2d的扩展,除去name参数用以指定该操作的name,data_format指定数据格式,与方法有关的一共六个参数:
input: 指需要做卷积的输入图像,要求是一个4维Tensor,具有[batch, height, width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]
depthwise_filter: 用来做depthwise_conv2d的卷积核,也就是说这个函数对输入首先做了一个深度卷积。它的shape规定是[filter_height, filter_width, in_channels, channel_multiplier]
pointwise_filter: 用来做pointwise卷积的卷积核,什么是pointwise卷积呢?我们可以把它和GoogLeNet最原始版本Inception结构中后面的1*1卷积核做channel降维来做对比,这里也是用1*1的卷积核,输入通道是depthwise_conv2d的输出通道也就是in_channels * channel_multiplier,输出通道数可以自己定义。depthwise_conv2d是对输入图像的每一个channel分别做卷积输出的,那么这个操作我们可以看做是将深度卷积得到的分离的各个channel的信息做一个融合。它的shape规定是[1, 1, channel_multiplier * in_channels, out_channels]
strides: 卷积的滑动步长。
padding: string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同边缘填充方式。
rate:这里的rate和空洞卷积中的rate作用相同
举例来说,假设输入通道数64,输出通道数64,传统的Conv2D方法的参数数量为3*3*64*64;而SeparableConv2D的参数数量为3*3*64+1*1*64*64。3*3*64:对输入的64个通道分别进行卷积 ,1*1*64*64对concat后的64个通道进行1*1卷积(pointwise Convolution)。
img1 = tf.constant(value=[[[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]]]],dtype=tf.float32)
img2 = tf.constant(value=[[[[1],[1],[1],[1]],[[1],[1],[1],[1]],[[1],[1],[1],[1]],[[1],[1],[1],[1]]]],dtype=tf.float32)
img = tf.concat(values=[img1,img2],axis=3)
filter1 = tf.constant(value=0, shape=[3,3,1,1],dtype=tf.float32)
filter2 = tf.constant(value=1, shape=[3,3,1,1],dtype=tf.float32)
filter3 = tf.constant(value=2, shape=[3,3,1,1],dtype=tf.float32)
filter4 = tf.constant(value=3, shape=[3,3,1,1],dtype=tf.float32)
filter_out1 = tf.concat(values=[filter1,filter2],axis=2)
filter_out2 = tf.concat(values=[filter3,filter4],axis=2)
filter = tf.concat(values=[filter_out1,filter_out2],axis=3)
# 这里为普通卷积
out_img = tf.nn.conv2d(input=img, filter=filter, strides=[1,1,1,1], padding='VALID')
输出:
[[[[ 9. 63.]
[ 9. 81.]]
[[ 9. 63.]
[ 9. 81.]]]]
好了,用一张图来详细展示这个过程(普通卷积) :
这是普通的卷积过程,我们再来看深度卷积。
out_img = tf.nn.depthwise_conv2d(input=img, filter=filter, strides=[1,1,1,1], rate=[1,1], padding='VALID')
输出:
[[[[ 0. 36. 9. 27.]
[ 0. 54. 9. 27.]]
[[ 0. 36. 9. 27.]
[ 0. 54. 9. 27.]]]]
现在我们可以形象的解释一下depthwise_conv2d卷积了。看普通的卷积,我们对卷积核每一个out_channel的两个通道分别和输入的两个通道做卷积相加,得到feature map的一个channel,而depthwise_conv2d卷积,我们对每一个对应的in_channel,分别卷积生成两个out_channel,所以获得的feature map的通道数量可以用in_channel* channel_multiplier来表达,这个channel_multiplier,就可以理解为卷积核的第四维。
我们对这个结果做pointwise卷积:
point_filter = tf.constant(value=1, shape=[1,1,4,4],dtype=tf.float32)
out_img = tf.nn.conv2d(input=out_img, filter=point_filter, strides=[1,1,1,1], padding='VALID')
输出:
[[[[72. 72. 72. 72.]
[90. 90. 90. 90.]]
[[72. 72. 72. 72.]
[90. 90. 90. 90.]]]]
现在我们用tf.nn.separable_conv2d来代替上面的两部操作:
out_img = tf.nn.separable_conv2d(input=img, depthwise_filter=filter, pointwise_filter=point_filter,strides=[1,1,1,1], rate=[1,1], padding='VALID')
输出:
[[[[72. 72. 72. 72.]
[90. 90. 90. 90.]]
[[72. 72. 72. 72.]
[90. 90. 90. 90.]]]]
参考:https://blog.csdn.net/mao_xiao_feng/article/details/78003476
https://blog.csdn.net/mao_xiao_feng/article/details/78002811
https://www.cnblogs.com/ranjiewen/p/9278631.html
https://www.tensorflow.org/api_docs/python/tf/nn/depthwise_conv2d