TensorFlow实现卷积,tf.nn.conv2d介绍

CNN中的channels 该如何理解?

首先,是 tensorflow 中给出的,对于输入样本中 channels 的含义。一般的RGB图片,channels 数量是 3 (红、绿、蓝);而monochrome图片,channels 数量是 1 。

如下图,假设现有一个为 6×6×3 的图片样本,使用 3×3×3 的卷积核(filter)进行卷积操作。此时输入图片的 channels 为 3 ,而卷积核中的 in_channels 与 需要进行卷积操作的数据的 channels 一致(这里就是图片样本,为3)。
这里写图片描述

接下来,进行卷积操作,卷积核中的27个数字与分别与样本对应相乘后,再进行求和,得到第一个结果。依次进行,最终得到 4×4 的结果。
这里写图片描述

上面步骤完成后,由于只有一个卷积核,所以最终得到的结果为 4×4×1 , out_channels 为 1 。

在实际应用中,都会使用多个卷积核。这里如果再加一个卷积核,就会得到 4×4×2 的结果。
这里写图片描述

tf.nn.conv2d

tf.nn.conv2d是TensorFlow里面实现卷积的函数,参考文档对它的介绍并不是很详细,实际上这是搭建卷积神经网络比较核心的一个方法,非常重要

这里写图片描述
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
除去name参数用以指定该操作的name,与方法有关的一共五个参数:
input:指需要做卷积的输入图像,它要求是一个Tensor,具有[batch, in_height, in_width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],注意这是一个4维的Tensor,要求类型为float32和float64其中之一

filter:相当于CNN中的卷积核,它要求是一个Tensor,具有[filter_height, filter_width, in_channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],要求类型与参数input相同,有一个地方需要注意,第三维in_channels,就是参数input的第四维

strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4

padding:string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同的卷积方式(后面会介绍)

use_cudnn_on_gpu: bool类型,是否使用cudnn加速,默认为true

结果返回一个Tensor,这个输出,就是我们常说的feature map


那么TensorFlow的卷积具体是怎样实现的呢,用一些例子去解释它:

1.考虑一种最简单的情况,现在有一张3×3单通道的图像(对应的shape:[1,3,3,1]),用一个1×1的卷积核(对应的shape:[1,1,1,1])去做卷积,最后会得到一张3×3的feature map

2.增加图片的通道数,使用一张3×3五通道的图像(对应的shape:[1,3,3,5]),用一个1×1的卷积核(对应的shape:[1,1,1,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')

3.把卷积核扩大,现在用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')

4.使用更大的图片将情况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')

注意我们可以把这种情况看成情况2和情况3的中间状态,卷积核以步长1滑动遍历全图,以下x表示的位置,表示卷积核停留的位置,每停留一个,输出feature map的一个像素

.....
.xxx.
.xxx.
.xxx.
.....

5.上面我们一直令参数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')
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx

6.如果卷积核有多个

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')

此时输出7张5×5的feature map
7.步长不为1的情况,文档里说了对于图片,因为只有两维,通常strides取[1,stride,stride,1]

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')

此时,输出7张3×3的feature map

x.x.x
.....
x.x.x
.....
x.x.x

8.如果batch值不为1,同时输入10张图

input = tf.Variable(tf.random_normal([10,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')

每张图,都有7张3×3的feature map,输出的shape就是[10,3,3,7]

最后,把程序总结一下:

import tensorflow as tf
#case 2
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op2 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 3
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op3 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 4
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op4 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 5
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op5 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
#case 6
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op6 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
#case 7
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op7 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
#case 8
input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op8 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

init = tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)
    print("case 2")
    print(sess.run(op2))
    print("case 3")
    print(sess.run(op3))
    print("case 4")
    print(sess.run(op4))
    print("case 5")
    print(sess.run(op5))
    print("case 6")
    print(sess.run(op6))
    print("case 7")
    print(sess.run(op7))
    print("case 8")
    print(sess.run(op8))

补充

1.2 tf.nn.conv2d函数介绍
官网说明文档:https://www.tensorflow.org/api_docs/python/tf/nn/conv2d

函数原型:

conv2d(input,
filter,
strides,
padding,
use_cudnn_on_gpu=None,
data_format=None,
name=None
)

函数参数说明:

input:是需要做卷积的输入图像,要求是一个Tensor,具有[batch, in_height, in_width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],注意这是一个4维的Tensor,要求类型为float32和float64其中之一。

filter:相当于CNN中的卷积滤波器,要求是一个Tensor,具有[filter_height, filter_width, in_channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],要求类型与参数input相同。注意,第三维in_channels,就是参数input的第四维。

strides:卷积时在图像每一维滑动的步长,这是一个一维的向量,长度4,一般是[1,x,x,1]的形式。

padding:string类型的量,取“SAME”或“VALID”,其中SAME表示需要在输入input高和宽维度的周围添加像素点,卷积率抱起会停留在原图边缘,保证得到的新图与原图的shape相同;VALID则表示不需要填充。

use_cudnn_on_gpu:可选,bool类型,是否使用cudnn加速,默认为true。

data_format:可选,string类型,取“NHWC”或“NCHW”,表示输入数据input的存储形式,“NHWC” the d表示[batch, height, width, channels],NCHW”表示[batch, channels, height, width]。

name:可选,表示操作的名字。

函数返回值:一个tensor,与input类型一致,维度顺序取决于data_format。

1.3 代码解释
1、操作一:
input:使用一张3×3、5通道的图像(对应的shape:[1,3,3,5])。filter:用一个1×1的卷积核(对应的shape:[1,1,5,1])。
padding:VALID。
strides:[1,1,1,1]。
返回tensor:是一张3×3、单通道的feature map(对应的shape:[1,3,3,1])。
这就相当于每一个像素点,卷积核都与该像素点的每一个通道做点积。

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op1 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

2、操作二:
input:使用一张3×3、5通道的图像(对应的shape:[1,3,3,5])。filter:用一个3×3的卷积核(对应的shape:[3,3,5,1])。
padding:VALID。
strides:[1,1,1,1]。
返回tensor:是一张1×1、单通道的feature map(对应的shape:[1,1,1,1])。

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op2 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

3、操作三:
input:使用一张5×5、5通道的图像(对应的shape:[1,5,5,5])。filter:用一个3×3的卷积核(对应的shape:[3,3,5,1])。
padding:VALID。
strides:[1,1,1,1]。
返回tensor:是一张3×3、单通道的feature map(对应的shape:[1,3,3,1])。

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op3 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

4、操作四:
input:使用一张5×5、5通道的图像(对应的shape:[1,5,5,5])。filter:用一个3×3的卷积核(对应的shape:[3,3,5,1])。
padding:SAME。
strides:[1,1,1,1]。
返回tensor:是一张5×5、单通道的feature map(对应的shape:[1,5,5,1])。

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op4 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='SAME')

5、操作五:
input:使用一张5×5、5通道的图像(对应的shape:[1,5,5,5])。filter:用7个3×3的卷积核(对应的shape:[3,3,5,7])。
padding:SAME。
strides:[1,1,1,1]。
返回tensor:是一张5×5、7通道的feature map(对应的shape:[1,5,5,7])。

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op5 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='SAME')

6、操作六:
input:使用一张5×5、5通道的图像(对应的shape:[1,5,5,5])。filter:用7个3×3的卷积核(对应的shape:[3,3,5,7])。
padding:SAME。
strides:[1,2,2,1]。
返回tensor:是一张3×3、7通道的feature map(对应的shape:[1,3,3,7])。

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op6 = tf.nn.conv2d(input, filter, strides=[1,2,2,1], padding='SAME')

7、操作七:
input:使用10张5×5、5通道的图像(对应的shape:[10,5,5,5])。
filter:用7个3×3的卷积核(对应的shape:[3,3,5,7])。
padding:SAME。
strides:[1,2,2,1]。
返回tensor:是10张3×3、7通道的feature map(对应的shape:[10,3,3,7])。

input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op7 = tf.nn.conv2d(input, filter, strides=[1,2,2,1], padding='SAME')

全部代码:

import tensorflow as tf

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op1 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op2 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op3 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='VALID')

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op4 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='SAME')

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op5 = tf.nn.conv2d(input, filter, strides=[1,1,1,1], padding='SAME')

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op6 = tf.nn.conv2d(input, filter, strides=[1,2,2,1], padding='SAME')

input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op7 = tf.nn.conv2d(input, filter, strides=[1,2,2,1], padding='SAME')

init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    print "images:"  
    print sess.run(input)

    print "filter:"
    print sess.run(filter)

    print "after conv1:"
    print sess.run(op1)

    print "after conv2:"
    print sess.run(op2)

    print "after conv3:"
    print sess.run(op3)

    print "after conv4:"
    print sess.run(op4)

    print "after conv5:"
    print sess.run(op5)

    print "after conv6:"
    print sess.run(op6)

    print "after conv7:"
    print sess.run(op7)

猜你喜欢

转载自blog.csdn.net/qq_35203425/article/details/81193293