在TensorFlow中,通过tf.nn.conv2d
函数可以方便地实现2D卷积运算。tf.nn.conv2d
基于输入 X : [ b , h , w , c i n ] \boldsymbol X:[b,h,w,c_{in}] X:[b,h,w,cin]和卷积核 W : [ k , k , c i n , c o u t ] \boldsymbol W:[k,k,c_{in},c_{out}] W:[k,k,cin,cout]进行卷积运算,得到输出 O : [ b , h ′ , w ′ , c o u t ] \boldsymbol O:[b,h',w',c_{out}] O:[b,h′,w′,cout],其中 c i n c_{in} cin表示输入通道数, c o u t c_{out} cout表示卷积核的数量,也是输出特征图的通道数。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
# 需要根据[k,k,cin,cout]格式创建w张量,4个3*3大小的卷积核
w = tf.random.normal([3, 3, 3, 4])
# 步长为1,padding为0
out = tf.nn.conv2d(x, w, strides=1, padding=[[0, 0], [1, 1], [1, 1], [0, 0]])
# 输出张量的shape
print('out.shape=', out.shape)
其中padding参数的设置格式为:
padding=[[0, 0], [上, 下], [左, 右], [0, 0]]
例如,上下左右各填充一个单位,则padding参数设置为 [ [ 0 , 0 ] , [ 1 , 1 ] , [ 1 , 1 ] , [ 0 , 0 ] ] [[0,0],[1,1],[1,1],[0,0]] [[0,0],[1,1],[1,1],[0,0]],实现如下:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
# 需要根据[k,k,cin,cout]格式创建w张量,4个3*3大小的卷积核
w = tf.random.normal([3, 3, 3, 4])
# 步长为1,padding为1
out = tf.nn.conv2d(x, w, strides=1, padding=[[0, 0], [1, 1], [1, 1], [0, 0]])
# 输出张量的shape
print('out.shape=', out.shape)
特别地,通过设置参数padding=‘SAME’
、strides=1
可以直接得到输入、输出同大小的卷积层,其中padding的具体数量由TensorFlow自动计算并完成填充操作。例如:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
# 需要根据[k,k,cin,cout]格式创建w张量,4个3*3大小的卷积核
w = tf.random.normal([3, 3, 3, 4])
# 步长为1,padding设置为输入、输出同大小
# 需要注意的是,padding=same只有在strides=1时才是同大小
out = tf.nn.conv2d(x, w, strides=1, padding='SAME')
# 输出张量的shape
print('out.shape=', out.shape)
当s<1时,设置padding=‘SAME’
将使得输出高、宽将成 1 s \frac{1}{s} s1倍地减少。例如:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
# 需要根据[k,k,cin,cout]格式创建w张量,4个3*3大小的卷积核
w = tf.random.normal([3, 3, 3, 4])
# 高宽先padding成可以整除3的最小整数6,然后6按3倍减少,得到2*2
out = tf.nn.conv2d(x, w, strides=3, padding='SAME')
# 输出张量的shape
print('out.shape=', out.shape)
卷积神经网络层与全连接层一样,可以设置网络带偏置向量。tf.nn.conv2d
函数是没有实现偏置向量计算的,添加偏置只需要手动累加偏置向量即可。例如:
# 根据[out]格式创建偏置向量
b = tf.zeros([4])
# 在卷积输出上叠加偏置向量,它会自动broadcasting为[b,h',w',cout]
out = out + b
通过卷积层类layers.Conv2D可以不需要手动定义卷积核 W \boldsymbol W W和偏置 b \boldsymbol b b张量,直接调用类实例即可完成卷积层的向前计算,实现更加高层和快捷。在TensorFlow中,API的命名有一定的规律,首字母大写的对象一般表示类,全部小写的一般表示函数,如layers.Conv2D表示卷积层类,nn.conv2d表示卷积运算函数。使用类方式会(在创建类时或build时)自动创建需要的权值张量和偏置向量等,用户不需要记忆卷积核张量的定义格式,因此使用起来更简单方便,但是灵活性也略低。函数方式的接口需要自行定义权值和偏置等,更加灵活和底层。
在新建卷积层类时,只需要指定卷积核数量参数filters,卷积核大小kernel_size,步长strides,填充padding等即可。如下创建了4个 3 × 3 3×3 3×3大小的卷积核的卷积层,步长为1,padding方案为‘SAME’:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
layer = layers.Conv2D(4, kernel_size=3, strides=1, padding='SAME')
out = layer(x)
print(out.shape)
果卷积核高宽不等,步长行列方向不等,此时需要将kernel_size参数设计为tuple格式 ( k h , k w ) (k_h,k_w) (kh,kw),strides参数设计为 ( s h , s w ) (s_h,s_w) (sh,sw)。如下创建4个 3 × 4 3×4 3×4大小的卷积核,竖直方向移动步长 s h = 2 s_h=2 sh=2,水平方向移动步长为 s w = 1 s_w=1 sw=1:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
layer = layers.Conv2D(4, kernel_size=(3, 4), strides=(2, 1), padding='SAME')
out = layer(x)
print(out.shape)
运行结果如下图所示:
创建完成后,通过调用实例即可完成向前计算,例如:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
layer = layers.Conv2D(4, kernel_size=3, strides=1, padding='SAME')
out = layer(x)
print(out.shape)
在类Conv2D中,保存了卷积核张量 W \boldsymbol W W和偏置 b \boldsymbol b b,可以通过类成员trainable_variables直接返回 W \boldsymbol W W和 b \boldsymbol b b的列表,例如:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
x = tf.random.normal([2, 5, 5, 3]) # 模拟输入,3通道,高宽为5
layer = layers.Conv2D(4, kernel_size=3, strides=1, padding='SAME')
out = layer(x)
print(out.shape)
# 输出所有待优化张量列表
print(layer.trainable_variables)
运行结果如下所示:
(2, 5, 5, 4)
[, ]
通过调用`layer.trainable_variables可以返回Conv2D类维护的 W \boldsymbol W W和 b \boldsymbol b b张量,这个类成员在获取网络层的待优化变量时非常有用。也可以直接调用类实例layer.kernel、layer.bias名访问 W \boldsymbol W W和 b \boldsymbol b b张量。