介绍
关于空洞卷积的理论可以查看以下链接, 这里我们不详细讲理论:
1.Long J, Shelhamer E, Darrell T, et al. Fully convolutional networks for semantic segmentation[C]. Computer Vision and Pattern Recognition, 2015.
2.Yu, Fisher, and Vladlen Koltun. "Multi-scale context aggregation by dilated convolutions." arXiv preprint arXiv:1511.07122 (2015).
3. 如何理解空洞卷积(dilated convolution)?
其实用一句话概括就是, 在不用 pooling 的情况下扩大感受野(pooling 层会导致信息损失)
为了阅读方便再贴一些相关链接:
[TensorFlow] tf.nn.conv2d 是怎样实现卷积的?
[TensorFlow] tf.nn.conv2d_transpose 是怎样实现反卷积的?
惯例先展示函数:
- tf.nn.atrous_conv2d(value,filters,rate,padding,name=None)
- 1
除去 name 参数用以指定该操作的 name, 与方法有关的一共四个参数:
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))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
TensorFlow 实现卷积, 反卷积和空洞卷积
阅读数 1532
TensorFlow 实现卷积, 反卷积和空洞卷积 TensorFlow 已经实现了卷积 (tf.nn.conv2d 卷积函数), 反卷积(tf.nn.conv2d_transpose 反卷积函数) 以及空洞卷积(tf...
博文
来自: pan_jinquan 的博客
- wsdgh: Nice, but `height - [filter_width + (filter_width - 1) * (rate - 1)] + 1` make more sense, when `padding=VALID`.
- import tensorflow as tf
- import numpy as np
- kernel_height = 3
- kernel_width = kernel_height
- img_height = 9
- img_width = img_height
- rate = 2
- padding = 'VALID'
- sz_same = img_height
- sz_valid = img_height - ((kernel_height - 1)*(rate - 1) + kernel_height) + 1
- img = np.random.randn(1, img_height, img_width, 3)
- kernel = np.random.randn(kernel_height, kernel_width, 3, 1)
- imgT = tf.constant(img)
- kernelT = tf.constant(kernel)
- imgO1 = tf.nn.atrous_conv2d(imgT, kernel, rate=rate, padding=padding)
- print(imgO1.shape)
- print(sz_same if padding.upper() == 'SAME' else sz_valid)
- # (1, 5, 5, 1)
- # 5
- ---------------------
作者: xf__mao
来源: http://www.bubuko.com/infodetail-3123387.html