tf.nn.conv2d()是TensorFlow中用於創建卷積層的函數,這個函數的調用格式如下:
def conv2d(input: Any,
filter: Any,
strides: Any,
padding: Any,
use_cudnn_on_gpu: bool = True,
data_format: str = "NHWC",
dilations: List[int] = [1, 1, 1, 1],
name: Any = None) -> Any
其中,比較重要的參數是 input, filter, strides, padding。
input 就是輸入的數據,格式就是TensorFlow的標準,使用四維矩陣的形式,分別是Btach_size(可以說是要處理的圖片數量),height, width,deepth(或者說是channel也就是通道數,比如RGB,3個通道)。
filter 在TensorFlow中稱爲濾波器,本質就相當於卷積核權重矩陣,這裏要注意filter的形式,也是四維數組的形式 ,分別是
height高度(卷積核的高度),
width寬度(卷積核的寬度),
deepth(channel)深度(這個與input的deepth一致),
Feature Map的數目,也可以說是卷積核的數目,也就是最後生成的特種圖的數目。
stride 也就是步長了,按照上面的 “一貫作風”,也是四維數組的形式,分別表示
在batch_size上的步長,
高度的步長,
寬度的步長以
深度的步長,
對應的是input的四個維度,一般對於圖片輸入來說,只需要改變中間兩個值。這個步長一定層度上決定了輸出特徵圖的大小。
padding 是填充,這裏只有兩個值 SAME 和 VALID,
後面的參數就是加速之類的選項,不是很重要。
stride和padding兩個參數應該結合在一起來說。
1、padding取值爲’SAME’
由於圖片大小和卷積核大小不一定是倍數關係,在SAME模式下,會通過周圍補零來保證所有數據都能被掃描到。那到底補多少零,最後輸出的特徵圖的大小爲多少,在這個函數裏,是由stride來決定的。先看代碼:
import tensorflow as tf
data=tf.Variable(tf.random_normal([64,43,43,3]),dtype=tf.float32)
weight=tf.Variable(tf.random_normal([3,3,3,64]),dtype=tf.float32)
sess=tf.InteractiveSession()
tf.global_variables_initializer().run()
conv1=tf.nn.conv2d(data,weight,strides=[1,1,1,1],padding='SAME')
conv2=tf.nn.conv2d(data,weight,strides=[1,2,2,1],padding='SAME')
conv3=tf.nn.conv2d(data,weight,strides=[1,3,3,1],padding='SAME')
conv4=tf.nn.conv2d(data,weight,strides=[1,4,4,1],padding='SAME')
print(conv1)
print(conv2)
print(conv3)
print(conv4)
輸出爲
Tensor(“Conv2D_8:0”, shape=(64, 43, 43, 64), dtype=float32)
Tensor(“Conv2D_9:0”, shape=(64, 22, 22, 64), dtype=float32)
Tensor(“Conv2D_10:0”, shape=(64, 15, 15, 64), dtype=float32)
Tensor(“Conv2D_11:0”, shape=(64, 11, 11, 64), dtype=float32)
可以看出輸出的尺寸大小與stride的第二第三個參數是倍數關係:
當strides=[1,1,1,1]時,輸出尺寸與原始尺寸相同
當strides=[1,2,2,1]時,43不是2的倍數,先把43增加到44,再除2,得22
當strides=[1,3,3,1]時,43不是3的倍數,先把43增加到45,再除3,得15
當strides=[1,4,4,1]時,43不是4的倍數,先把43增加到44,再除4,得11
依次類推
我們再來看看,輸出的特徵圖尺寸與卷積核的大小有沒有關係:
import tensorflow as tf
data=tf.Variable(tf.random_normal([64,43,43,3]),dtype=tf.float32)
weight=tf.Variable(tf.random_normal([5,5,3,64]),dtype=tf.float32)
sess=tf.InteractiveSession()
tf.global_variables_initializer().run()
conv1=tf.nn.conv2d(data,weight,strides=[1,1,1,1],padding='SAME')
conv2=tf.nn.conv2d(data,weight,strides=[1,2,2,1],padding='SAME')
conv3=tf.nn.conv2d(data,weight,strides=[1,3,3,1],padding='SAME')
conv4=tf.nn.conv2d(data,weight,strides=[1,4,4,1],padding='SAME')
print(conv1)
print(conv2)
print(conv3)
print(conv4)
輸出爲
Tensor(“Conv2D_16:0”, shape=(64, 43, 43, 64), dtype=float32)
Tensor(“Conv2D_17:0”, shape=(64, 22, 22, 64), dtype=float32)
Tensor(“Conv2D_18:0”, shape=(64, 15, 15, 64), dtype=float32)
Tensor(“Conv2D_19:0”, shape=(64, 11, 11, 64), dtype=float32)
通過上面可以知道,在SAME模式下,輸出的尺寸與卷積核的尺寸沒有關係,只與strides有關係
2、padding取值爲VALID
在VALID模式下,不會補零,掃描不到的數據會被直接拋棄。
import tensorflow as tf
data=tf.Variable(tf.random_normal([64,43,43,3]),dtype=tf.float32)
weight=tf.Variable(tf.random_normal([5,5,3,64]),dtype=tf.float32)
sess=tf.InteractiveSession()
tf.global_variables_initializer().run()
conv1=tf.nn.conv2d(data,weight,strides=[1,1,1,1],padding='VALID')
conv2=tf.nn.conv2d(data,weight,strides=[1,2,2,1],padding='VALID')
conv3=tf.nn.conv2d(data,weight,strides=[1,3,3,1],padding='VALID')
conv4=tf.nn.conv2d(data,weight,strides=[1,4,4,1],padding='VALID')
print(conv1)
print(conv2)
print(conv3)
print(conv4)
輸出爲:
Tensor(“Conv2D_20:0”, shape=(64, 39, 39, 64), dtype=float32)
Tensor(“Conv2D_21:0”, shape=(64, 20, 20, 64), dtype=float32)
Tensor(“Conv2D_22:0”, shape=(64, 13, 13, 64), dtype=float32)
Tensor(“Conv2D_23:0”, shape=(64, 10, 10, 64), dtype=float32)
import tensorflow as tf
data=tf.Variable(tf.random_normal([64,43,43,3]),dtype=tf.float32)
weight=tf.Variable(tf.random_normal([3,3,3,64]),dtype=tf.float32)
sess=tf.InteractiveSession()
tf.global_variables_initializer().run()
conv1=tf.nn.conv2d(data,weight,strides=[1,1,1,1],padding='VALID')
conv2=tf.nn.conv2d(data,weight,strides=[1,2,2,1],padding='VALID')
conv3=tf.nn.conv2d(data,weight,strides=[1,3,3,1],padding='VALID')
conv4=tf.nn.conv2d(data,weight,strides=[1,4,4,1],padding='VALID')
print(conv1)
print(conv2)
print(conv3)
print(conv4)
Tensor(“Conv2D_28:0”, shape=(64, 41, 41, 64), dtype=float32)
Tensor(“Conv2D_29:0”, shape=(64, 21, 21, 64), dtype=float32)
Tensor(“Conv2D_30:0”, shape=(64, 14, 14, 64), dtype=float32)
Tensor(“Conv2D_31:0”, shape=(64, 11, 11, 64), dtype=float32)
從上面兩組數據對比可以看出,在VALID模式下,輸出的尺寸與卷積核的尺寸,步長都相關。計算公式如下:
由於是VALID模式,padding=0