原文:www.cnblogs.com/welhzh/p/6607581.html
tf.nn.conv2d是TensorFlow里面實(shí)現(xiàn)卷積的函數(shù),參考文檔對(duì)它的介紹并不是很詳細(xì),實(shí)際上這是搭建卷積神經(jīng)網(wǎng)絡(luò)比較核心的一個(gè)方法,非常重要
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
除去name參數(shù)用以指定該操作的name,與方法有關(guān)的一共五個(gè)參數(shù):
第一個(gè)參數(shù)input:指需要做卷積的輸入圖像,它要求是一個(gè)Tensor,具有[batch, in_height, in_width, in_channels]這樣的shape,具體含義是[訓(xùn)練時(shí)一個(gè)batch的圖片數(shù)量, 圖片高度, 圖片寬度, 圖像通道數(shù)],注意這是一個(gè)4維的Tensor,要求類型為float32和float64其中之一
第二個(gè)參數(shù)filter:相當(dāng)于CNN中的卷積核,它要求是一個(gè)Tensor,具有[filter_height, filter_width, in_channels, out_channels]這樣的shape,具體含義是[卷積核的高度,卷積核的寬度,圖像通道數(shù),卷積核個(gè)數(shù)],要求類型與參數(shù)input相同,有一個(gè)地方需要注意,第三維in_channels,就是參數(shù)input的第四維
第三個(gè)參數(shù)strides:卷積時(shí)在圖像每一維的步長(zhǎng),這是一個(gè)一維的向量,長(zhǎng)度4,strides取[1,stride,stride,1]
第四個(gè)參數(shù)padding:string類型的量,只能是"SAME","VALID"其中之一,這個(gè)值決定了不同的卷積方式(后面會(huì)介紹)
第五個(gè)參數(shù):use_cudnn_on_gpu:bool類型,是否使用cudnn加速,默認(rèn)為true
結(jié)果返回一個(gè)Tensor,這個(gè)輸出,就是我們常說(shuō)的feature map,shape仍然是[batch, height, width, channels]這種形式。
那么TensorFlow的卷積具體是怎樣實(shí)現(xiàn)的呢,用一些例子去解釋它:
1.考慮一種最簡(jiǎn)單的情況,現(xiàn)在有一張3×3單通道的圖像(對(duì)應(yīng)的shape:[1,3,3,1]),用一個(gè)1×1的卷積核(對(duì)應(yīng)的shape:[1,1,1,1])去做卷積,最后會(huì)得到一張3×3的feature map
2.增加圖片的通道數(shù),使用一張3×3五通道的圖像(對(duì)應(yīng)的shape:[1,3,3,5]),用一個(gè)1×1的卷積核(對(duì)應(yīng)的shape:[1,1,1,1])去做卷積,仍然是一張3×3的feature map,這就相當(dāng)于每一個(gè)像素點(diǎn),卷積核都與該像素點(diǎn)的每一個(gè)通道做卷積。
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter= tf.Variable(tf.random_normal([1,1,5,1]))
op= tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
3.把卷積核擴(kuò)大,現(xiàn)在用3×3的卷積核做卷積,最后的輸出是一個(gè)值,相當(dāng)于情況2的feature map所有像素點(diǎn)的值求和
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter= tf.Variable(tf.random_normal([3,3,5,1]))
op= tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
4.使用更大的圖片將情況2的圖片擴(kuò)大到5×5,仍然是3×3的卷積核,令步長(zhǎng)為1,輸出3×3的feature map
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter= tf.Variable(tf.random_normal([3,3,5,1]))
op= tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
注意我們可以把這種情況看成情況2和情況3的中間狀態(tài),卷積核以步長(zhǎng)1滑動(dòng)遍歷全圖,以下x表示的位置,表示卷積核停留的位置,每停留一個(gè),輸出feature map的一個(gè)像素
.....
.xxx.
.xxx.
.xxx.
.....
5.上面我們一直令參數(shù)padding的值為‘VALID’,當(dāng)其為‘SAME’時(shí),表示卷積核可以停留在圖像邊緣,如下,輸出5×5的feature map
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter= tf.Variable(tf.random_normal([3,3,5,1]))
op= tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
6.如果卷積核有多個(gè)
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter= tf.Variable(tf.random_normal([3,3,5,7]))
op= tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
此時(shí)輸出7張5×5的feature map
7.步長(zhǎng)不為1的情況,文檔里說(shuō)了對(duì)于圖片,因?yàn)橹挥袃删S,通常strides取[1,stride,stride,1]
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter= tf.Variable(tf.random_normal([3,3,5,7]))
op= tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
此時(shí),輸出7張3×3的feature map
x.x.x
.....
x.x.x
.....
x.x.x
8.如果batch值不為1,同時(shí)輸入10張圖
input = tf.Variable(tf.random_normal([10,5,5,5]))
filter= tf.Variable(tf.random_normal([3,3,5,7]))
op= tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
每張圖,都有7張3×3的feature map,輸出的shape就是[10,3,3,7]
最后,把程序總結(jié)一下:
import tensorflow as tf
# tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
# 除去name參數(shù)用以指定該操作的name,與方法有關(guān)的一共五個(gè)參數(shù):
#
# 第一個(gè)參數(shù)input:指需要做卷積的輸入圖像,它要求是一個(gè)Tensor,具有[batch, in_height, in_width, in_channels]這樣的shape,具體含義是[訓(xùn)練時(shí)一個(gè)batch的圖片數(shù)量, 圖片高度, 圖片寬度, 圖像通道數(shù)],注意這是一個(gè)4維的Tensor,要求類型為float32和float64其中之一
#
# 第二個(gè)參數(shù)filter:相當(dāng)于CNN中的卷積核,它要求是一個(gè)Tensor,具有[filter_height, filter_width, in_channels, out_channels]這樣的shape,具體含義是[卷積核的高度,卷積核的寬度,圖像通道數(shù),卷積核個(gè)數(shù)],要求類型與參數(shù)input相同,有一個(gè)地方需要注意,第三維in_channels,就是參數(shù)input的第四維
#
# 第三個(gè)參數(shù)strides:卷積時(shí)在圖像每一維的步長(zhǎng),這是一個(gè)一維的向量,長(zhǎng)度4
#
# 第四個(gè)參數(shù)padding:string類型的量,只能是"SAME","VALID"其中之一,這個(gè)值決定了不同的卷積方式(后面會(huì)介紹)
#
# 第五個(gè)參數(shù):use_cudnn_on_gpu:bool類型,是否使用cudnn加速,默認(rèn)為true
#
# 結(jié)果返回一個(gè)Tensor,這個(gè)輸出,就是我們常說(shuō)的feature map
oplist=[]
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 3, 3, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([1 ,1 , 5 ,1]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='VALID')
oplist.append([op2, "case 2"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 3, 3, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,1]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='VALID')
oplist.append([op2, "case 3"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 5, 5, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,1]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='VALID')
oplist.append([op2, "case 4"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 5, 5, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,1]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='SAME')
oplist.append([op2, "case 5"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 5, 5, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,7]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='SAME')
oplist.append([op2, "case 6"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([1, 5, 5, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,7]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,2,2,1], use_cudnn_on_gpu=False, padding='SAME')
oplist.append([op2, "case 7"])
# [batch, in_height, in_width, in_channels]
input_arg? = tf.Variable(tf.ones([4, 5, 5, 5]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 5 ,7]))
op2 = tf.nn.conv2d(input_arg, filter_arg, strides=[1,2,2,1], use_cudnn_on_gpu=False, padding='SAME')
oplist.append([op2, "case 8"])
with tf.Session() as a_sess:
a_sess.run(tf.global_variables_initializer())
for aop in oplist:
print("----------{}---------".format(aop[1]))
print(a_sess.run(aop[0]))
print('---------------------\n\n')
結(jié)果是這樣的:
----------case 6---------
[[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]]
---------------------
----------case 7---------
[[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]]
---------------------
----------case 8---------
[[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]
[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]
[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]
[[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]
[[ 30. 30. 30. 30. 30. 30. 30.]
[ 45. 45. 45. 45. 45. 45. 45.]
[ 30. 30. 30. 30. 30. 30. 30.]]
[[ 20. 20. 20. 20. 20. 20. 20.]
[ 30. 30. 30. 30. 30. 30. 30.]
[ 20. 20. 20. 20. 20. 20. 20.]]]]
---------------------