Tensorflow】tf.nn.atrous_conv2d如何实现空洞卷积?

  • value: 
    指需要做卷积的输入图像,要求是一个4维Tensor,具有[batch, height, width, channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]

  • filters: 
    相当于CNN中的卷积核,要求是一个4维Tensor,具有[filter_height, filter_width, channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],同理这里第三维channels,就是参数value的第四维

  • rate: 
    要求是一个int型的正数,正常的卷积操作应该会有stride(即卷积核的滑动步长),但是空洞卷积是没有stride参数的,这一点尤其要注意。取而代之,它使用了新的rate参数,那么rate参数有什么用呢?它定义为我们在输入图像上卷积时的采样间隔,你可以理解为卷积核当中穿插了(rate-1)数量的“0”,把原来的卷积核插出了很多“洞洞”,这样做卷积时就相当于对原图像的采样间隔变大了。具体怎么插得,可以看后面更加详细的描述。此时我们很容易得出rate=1时,就没有0插入,此时这个函数就变成了普通卷积。

  • padding: 
    string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同边缘填充方式。

ok,完了,到这就没有参数了,或许有的小伙伴会问那“stride”参数呢。其实这个函数已经默认了stride=1,也就是滑动步长无法改变,固定为1。

结果返回一个Tensor,填充方式为“VALID”时,返回[batch,height-2*(filter_width-1),width-2*(filter_height-1),out_channels]的Tensor,填充方式为“SAME”时,返回[batch, height, width, out_channels]的Tensor,这个结果怎么得出来的?先不急,我们通过一段程序形象的演示一下空洞卷积。

实验

首先创建一张2通道图

img = tf.constant(value=[[[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]]]],dtype=tf.float32)
img = tf.concat(values=[img,img],axis=3)
  • 1
  • 2

然后用一个3*3卷积核去做卷积

filter = tf.constant(value=1, shape=[3,3,2,5], dtype=tf.float32)
out_img = tf.nn.atrous_conv2d(value=img, filters=filter, rate=1)
  • 1
  • 2

建立好了img和filter,就可以做卷积了

out_img = tf.nn.conv2d(input=img, filter=filter, strides=[1,1,1,1], padding='VALID')
  • 1

输出5个channel,我们设置rate=1,此时空洞卷积可以看做普通的卷积,分别在SAME和VALID模式下输出如下:

这里写图片描述

ok,调整rate=2,继续运行程序

out_img = tf.nn.atrous_conv2d(value=img, filters=filter, rate=2, padding='SAME')
  • 1

查看输出结果

[[[[ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]
   [ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]]

  [[ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]
   [ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]]

  [[ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]
   [ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]]

  [[ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]
   [ 16.  16.  16.  16.  16.]
   [ 24.  24.  24.  24.  24.]]]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这个结果怎么出来的呢?再用一张图 
这里写图片描述

这里我们看到rate=2时,通过穿插“0”,卷积核由3*3膨胀到了5*5。再看看“VALID”模式下,会发生什么?

这里写图片描述

直接报错了。因为卷积核的大小已经超过了原图大小

好了,看到这里相信大家对于空洞卷积有了基本的了解了。那么,填充方式为“VALID”时,返回[batch,height-2*(filter_width-1),width-2*(filter_height-1),out_channels]的Tensor,这个结果,相信大家就可以证明了。


代码清单

import tensorflow as tf


img = tf.constant(value=[[[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]],[[1],[2],[3],[4]]]],dtype=tf.float32)
img = tf.concat(values=[img,img],axis=3)
filter = tf.constant(value=1, shape=[3,3,2,5], dtype=tf.float32)
out_img1 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=1, padding='SAME')
out_img2 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=1, padding='VALID')
out_img3 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=2, padding='SAME')

#error
#out_img4 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=2, padding='VALID')

with tf.Session() as sess:
    print 'rate=1, SAME mode result:'
    print(sess.run(out_img1))

    print 'rate=1, VALID mode result:'
    print(sess.run(out_img2))

    print 'rate=2, SAME mode result:'
    print(sess.run(out_img3))

    # error
    #print 'rate=2, VALID mode result:'
    #print(sess.run(out_img4))

猜你喜欢

转载自blog.csdn.net/qq_41044525/article/details/82687990