详解TensorFlow的 tf.nn.conv2d 函数:二维卷积操作

  • Post category:Python

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个参数,下面我们逐一解释。

  1. input: 卷积操作的输入张量,是一个四维张量,其形状为 $[batch, height, width, channels]$,具体含义是:batch 表示一次输入多少个样本(比如一次送入50张图片,即batch_size为50), height 表示输入图片的高度, width 表示输入图片的宽度, channels 表示输入的图片通道数,比如RGB三通道则为3

  2. filter: 卷积操作的卷积核,也是一个四维张量,其形状为 $[filter_height, filter_width, in_channels, out_channels]$, 其中 filter_height 表示卷积核的高度, filter_width 表示卷积核的宽度, in_channels 表示卷积核的输入通道数,表示卷积操作的输入张量中最后一个维度上的值,与卷积操作输入的 channels 的值相同; out_channels 表示卷积核的输出通道数,表示卷积操作输出的 channels 的值。

  3. strides: 一个四维列表($[1, stride, stride, 1]$),其中 stride 指卷积核按照步长移动的步长,对于图片上下左右的移动都是相同的,所以总共四个维度上设置的步长的值均相同。

  4. padding:一个字符串,指定卷积层的填充方式,共有两种方式:'SAME''VALID',其中 'SAME' 表示进行填充后,输出大小与输入大小一致,而 'VALID' 表示尽可能多地进行卷积操作,输出大小一般会缩小。

  5. use_cudnn_on_gpu:一个可选的布尔值,默认为 True,表示是否要使用显卡计算。

  6. data_format:一个字符串,指定输入的数据格式,默认值为NHWC(即“N,height,width,channels”),其内在的含义与我们在 input 参数中提到过的是一致的,即数据为已经输入好的样本。

  7. dilations:一个四维列表,指定卷积的扩张率,一般为 $[1, 1, 1, 1]$。

  8. name:操作的名称,默认值为None,即不指定操作名称。

  9. input_quantizer:一个命名参数,可以为输入张量设置一个 Quantizer(量子化),TensorFlow 的专门的卷积量化操作值得探究。

  10. filter_quantizer:一个命名参数,可以为卷积核设置一个 Quantizer(量子化)。

  11. 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 分类问题上的良好性能。