TensorFlow的 tf.nn.conv2d 函数详解
tf.nn.conv2d函数是TensorFlow中最常用的卷积函数,它用于执行2D卷积操作。下面我们针对tf.nn.conv2d函数做详细的解释。
函数参数
tf.nn.conv2d(
input,
filter,
strides,
padding,
use_cudnn_on_gpu=None,
data_format=None,
dilations=[1, 1, 1, 1],
name=None,
input_quantizer=None,
filter_quantizer=None,
output_quantizer=None
)
上述函数共有11个参数,下面我们逐一解释。
-
input
: 卷积操作的输入张量,是一个四维张量,其形状为 $[batch, height, width, channels]$,具体含义是:batch
表示一次输入多少个样本(比如一次送入50张图片,即batch_size为50),height
表示输入图片的高度,width
表示输入图片的宽度,channels
表示输入的图片通道数,比如RGB三通道则为3
。 -
filter
: 卷积操作的卷积核,也是一个四维张量,其形状为 $[filter_height, filter_width, in_channels, out_channels]$, 其中filter_height
表示卷积核的高度,filter_width
表示卷积核的宽度,in_channels
表示卷积核的输入通道数,表示卷积操作的输入张量中最后一个维度上的值,与卷积操作输入的channels
的值相同;out_channels
表示卷积核的输出通道数,表示卷积操作输出的channels
的值。 -
strides
: 一个四维列表($[1, stride, stride, 1]$),其中stride
指卷积核按照步长移动的步长,对于图片上下左右的移动都是相同的,所以总共四个维度上设置的步长的值均相同。 -
padding
:一个字符串,指定卷积层的填充方式,共有两种方式:'SAME'
和'VALID'
,其中'SAME'
表示进行填充后,输出大小与输入大小一致,而'VALID'
表示尽可能多地进行卷积操作,输出大小一般会缩小。 -
use_cudnn_on_gpu
:一个可选的布尔值,默认为True
,表示是否要使用显卡计算。 -
data_format
:一个字符串,指定输入的数据格式,默认值为NHWC
(即“N,height,width,channels”),其内在的含义与我们在input
参数中提到过的是一致的,即数据为已经输入好的样本。 -
dilations
:一个四维列表,指定卷积的扩张率,一般为 $[1, 1, 1, 1]$。 -
name
:操作的名称,默认值为None
,即不指定操作名称。 -
input_quantizer
:一个命名参数,可以为输入张量设置一个 Quantizer(量子化),TensorFlow 的专门的卷积量化操作值得探究。 -
filter_quantizer
:一个命名参数,可以为卷积核设置一个 Quantizer(量子化)。 -
output_quantizer
:一个命名参数,可以为卷积层设置一个 Quantizer(量子化)。
两个实例
下面我们通过两个实例来了解 tf.nn.conv2d
函数的使用方法。
实例一
在这个示例中,我们将使用 tf.nn.conv2d
函数来实现一个简单的卷积层,它将输入的 $28\times28\times1$ 的张量卷积成 $28\times28\times32$ 的张量,其中卷积核的大小为 $3\times3$,步长为 $1$。
import tensorflow as tf
input = tf.placeholder(tf.float32, [None, 28, 28, 1])
filter = tf.Variable(tf.random_normal([3, 3, 1, 32]))
conv = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
代码解释:
首先我们先创建了一个占位符input
,它表示输入数据的占位符,其内部尺寸为[None, 28, 28, 1]
,其中None
表示该维度可以是任意值,一般我们用于表示输入的样本数目,’28’表示输入图片的宽和高的尺寸大小,1
表示输入通道数(因为后面我们要对 MNIST 手写数字数据集进行训练,所以这里一张输入图片的通道数为1)。
然后我们使用 tf.Variable
生成一个随机初始化的权值矩阵,该矩阵的大小为 $[3, 3, 1, 32]$,其中 $3 \times 3$ 表示卷积核的大小,1
表示输入的通道数,32
表示输出的通道数。
最后,我们使用 tf.nn.conv2d
来进行卷积操作,其中参数的意义如上所述。
实例二
在这个示例中,我们使用 tf.nn.conv2d
来定义一个简单的卷积神经网络(Convolutional Neural Network,简称 CNN),使用该 CNN 对 CIFAR-10 数据集进行分类。
import tensorflow as tf
# 输入数据
input = tf.placeholder(tf.float32, [None, 32, 32, 3])
# 第一层卷积
filter_conv1 = tf.Variable(tf.truncated_normal([5, 5, 3, 64]))
conv1 = tf.nn.conv2d(input, filter_conv1, strides=[1, 1, 1, 1], padding='SAME')
# ReLU
relu1 = tf.nn.relu(conv1)
# 最大池化,步长为2
pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 第二层卷积
filter_conv2 = tf.Variable(tf.truncated_normal([5, 5, 64, 64]))
conv2 = tf.nn.conv2d(pool1, filter_conv2, strides=[1, 1, 1, 1], padding='SAME')
# ReLU
relu2 = tf.nn.relu(conv2)
# 最大池化,步长为2
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 全连接层1
fc1 = tf.layers.dense(tf.reshape(pool2, [-1, 4096]), 384, activation=tf.nn.relu)
# 全连接层2
fc2 = tf.layers.dense(fc1, 192, activation=tf.nn.relu)
# 输出层
out = tf.layers.dense(fc2, 10)
代码解释:
首先我们定义输入的数据张量 input
,这里定义的 input
大小为 $[None, 32, 32, 3]$,其中 $None$ 表示可以在后面根据数据集定义的 batch size 自动指定大小。
接下来,我们添加两个卷积层,每个卷积层都由一个 3×3 的卷积核和一个 ReLU 激活函数组成,它们后面都跟着一个 2×2 的最大池化层。第一个卷积层将输入张量变为大小为 $32\times32\times64$,第二个卷积层将输入张量变为大小为 $16\times16\times64$。
之后,我们添加两个全连接层,每个全连接层都由一个ReLU激活函数、一些节点、和一些权重共同组成。输出层由10个神经元组成,用于对CIFAR-10数据集进行分类。
最终模型在训练时,使用损失函数、优化方法等算法对模型进行训练优化,得到一个在 CIFAR-10 分类问题上的良好性能。