【Tensorflow】tf.nn.atrous_conv2d如何實現空洞卷積?膨脹卷積


介紹
關於空洞卷積的理論可以查看以下鏈接,這里我們不詳細講理論:

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
來源:CSDN
原文:https://blog.csdn.net/mao_xiao_feng/article/details/78003730
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM