1、函数介绍:
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
指需要做卷积的输入图像,它要求是一个Tensor(张量),具有 [batch, in_height, in_width, in_channels] 这样的shape,具体含义是 [训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数] ,注意这是一个4维的Tensor,要求类型为float32和float64其中之一。
关于通道: 彩色图片每个像素值是由R,G,B三个原色组合得到的,RGB三原色就是这里的通道。那么此时的图像通道数为3。
相当于CNN中的卷积核,它要求是一个Tensor,具有 [filter_height, filter_width, in_channels, out_channels] 这样的shape,具体含义是 [卷积核的高度,卷积核的宽度,图像通道数,卷积核个数] ,要求类型与参数input相同,有一个地方需要注意,这里的第三维in_channels,就是参数input的第四维in_channels。
卷积时在图像每一维的步长,这是一个一维的向量,长度4。
正如前面所述,strides 是另外一个极其重要的参数,其为一个长度为4 的一维整数类型数组,每一位对应input中每一位对应的移动步长.。步长为一的卷积操作,不补零。
string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同的卷积方式。
padding=’SAME’ 时,TensorFlow会自动对原图像进行补零,从而使输入输出的图像大小一致 。
padding=’VALID’ 时,则会缩小原图像的大小。
SAME:越过边缘取样,取样的面积和输入图像的像素宽度一致。
VALID:不越过边缘取样,取样的面积小于输入人的图像的像素宽度
这里关于”SAME”和”VALID”具体的区别,见视频(斯坦福深度视觉识别课堂)
链接:https://study.163.com/course/courseLearn.htm?courseId=1004697005#/learn/video?lessonId=1050213450&courseId=1004697005
注:请先"食用"以上视频,视频内一些关于”SAME”和”VALID”的公式讲解对于后面代码示例的理解起着很重要的作用!
bool类型,是否使用cudnn加速,默认为true
结果返回一个Tensor,这个输出,就是我们常说的feature map
2、公式计算
条件:padding=’SAME’ 时
举例说明:
数据:一张 [28,28,1] 的图片,卷积层取:100个filter,5*5,步长为1,padding = 1
结果:
- H2 = (28 - 5 + 2*1) / 1 + 1 = 26
- W2 = (28 - 5 + 2*1) / 1 + 1 = 26
3、例子详解:
那么TensorFlow的卷积具体是怎样实现的呢,用一些例子去解释它:
- 考虑一种最简单的情况,现在有一张 3×3 单通道的图像(对应的shape:[1,3,3,1]),用一个1×1的卷积核(对应的shape:[1,1,1,1])去做卷积,最后会得到一张3×3的feature map。
input = tf.Variable(tf.random_normal([1,3,3,1]))
filter = tf.Variable(tf.random_normal([1,1,1,1]))
op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
Tensor("Conv2D_1:0", shape=(1, 3, 3, 1), dtype=float32)
- 增加图片的通道数,使用一张 3×3 五通道的图像(对应的shape:[1,3,3,5]),用一个1×1的卷积核(对应的shape:[1,1,5,1])去做卷积,仍然是一张 3×3 的feature map,这就相当于每一个像素点,卷积核都与该像素点的每一个通道做点积。
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')
Tensor("Conv2D_2:0", shape=(1, 3, 3, 1), dtype=float32)
- 把卷积核扩大,现在用3×3的卷积核做卷积,最后的输出是一个值,相当于情况2的feature map所有像素点的值求和。
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')
Tensor("Conv2D_3:0", shape=(1, 1, 1, 1), dtype=float32)
- 使用更大的图片将情况2的图片扩大到5×5,仍然是3×3的卷积核,令步长为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')
Tensor("Conv2D_4:0", shape=(1, 3, 3, 1), dtype=float32)
- 上面我们一直令参数padding的值为‘VALID’,当其为‘SAME’时,表示卷积核可以停留在图像边缘,如下,输出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')
Tensor("Conv2D_5:0", shape=(1, 5, 5, 1), dtype=float32)
- 如果卷积核有多个。
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')
Tensor("Conv2D_6:0", shape=(1, 5, 5, 7), dtype=float32)
- 步长不为1的情况,文档里说了对于图片,因为只有两维,通常strides取[1,stride,stride,1]。此时,输出7张3×3的feature map。
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')
Tensor("Conv2D_7:0", shape=(1, 3, 3, 7), dtype=float32)
- 如果batch值不为1,同时输入10张图。每张图,都有7张3×3的feature map,输出的shape就是[10,3,3,7]。
input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable