您的位置:首页 > 其它

TensorFlow学习---tf.nn.conv2d实现卷积操作

2017-06-21 15:09 656 查看
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。对于图片,因为只有两维,通常strides取[1,stride,stride,1]
第四个参数padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式。若是"SAME",表示卷积核可以停留在图像边缘,也就是输出与输入形状相同。若为"VALID",则输出形状为:(in_height-
filter_height+1)*(in_width- filter_width+1)
第五个参数:use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认为true
结果返回一个Tensor,这个输出,就是我们常说的featuremap
 
那么TensorFlow的卷积具体是怎样实现的呢,下面将举例说明。
详细的卷积过程可以看帖子:http://blog.csdn.net/huahuazhu/article/details/73469491

1单通道、一个卷积核

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

图像矩阵

1
1
1
0
0
0
1
1
1
0
0
0
1
1
1
0
0
1
1
0
0
1
1
0
0
 

卷积核矩阵

1
0
1
0
1
0
1
0
1
 

卷积过程:

 

2. 将1中的padding的值改为‘SAME’时,表示卷积核可以停留在图像边缘,也就是输出与输入形状相同,则输出5×5的feature map。

在计算时可以认为是图像的边缘补0,再进行滑动计算点积。

 

0
0
0
0
0
0
0
 
1
0
1
0
1
1
1
0
0
0
 
0
1
0
0
0
1
1
1
0
0
 
1
0
1
0
0
0
1
1
1
0
 
0
0
0
1
1
0
0
 
0
0
1
1
0
0
0
 
0
0
0
0
0
0
0
 
 代码实现如下:

#!/usr/bin/env python2.7
#coding=utf-8

import tensorflow as tf
#输入的图像矩阵
input = tf.constant([[1, 1, 1, 0,0],
[0,1,1,1,0],
[0,0,1,1,1],
[0,0,1,1,0],
[0,1,1,0,0]],shape=[1,5,5,1],dtype=tf.float32)
#卷积核矩阵
filter = tf.constant([[1,0,1],
[0,1,0],
[1,0,1]],shape=[3,3,1,1],dtype=tf.float32)
op1 = tf.nn.conv2d(input,filter,strides = [1,1,1,1],padding ='VALID')
#卷积计算
op2 = tf.nn.conv2d(input,filter,strides = [1,1,1,1],padding = 'SAME')
with tf.Session() as sess:
result1 = sess.run(op1)
result2 = sess.run(op2)
print(result1)
print('###############')
print(result2)


运行结果如下:

[[[[4.]

   [ 3.]

   [ 4.]]

 

  [[ 2.]

   [ 4.]

   [ 3.]]

 

  [[ 2.]

   [ 3.]

   [ 4.]]]]

###############

[[[[2.]

   [ 2.]

   [ 3.]

   [ 1.]

   [ 1.]]

 

  [[ 1.]

   [ 4.]

   [ 3.]

   [ 4.]

   [ 1.]]

 

  [[ 1.]

   [ 2.]

   [ 4.]

   [ 3.]

   [ 3.]]

 

  [[ 1.]

   [ 2.]

   [ 3.]

   [ 4.]

   [ 1.]]

 

  [[ 0.]

   [ 2.]

   [ 2.]

   [ 1.]

   [ 1.]]]]

 

2 多通道多卷积核

下面对http://blog.csdn.net/huahuazhu/article/details/73469491中多通道多卷积核的例子进行实现。

下图中左侧为输入的三通道(R\G\B)的7*7图片,中间为两个核函数(即滤波器)Filter W0,Filter W1,右侧为输出Output Volume为两张3*3的feature map



代码实现如下:

#!/usr/bin/env python2.7
#coding=utf-8

import tensorflow as tf
#输入的图像矩阵:下面每行数据对应图像矩阵的行;每行中的每组值,分别对应0,1,2三个通道相应位置的值
input = tf.constant([[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
[[0,0,0],[0,0,1],[1,0,0],[2,1,1],[0,0,2],[1,0,2],[0,0,0]],
[[0,0,0],[2,0,0],[0,1,1],[1,1,2],[0,2,0],[0,1,1],[0,0,0]],
[[0,0,0],[0,0,0],[0,2,1],[2,1,0],[2,0,2],[1,0,2],[0,0,0]],
[[0,0,0],[2,2,0],[2,1,1],[0,2,1],[1,1,2],[0,0,0],[0,0,0]],
[[0,0,0],[1,2,2],[2,1,2],[0,1,2],[0,1,1],[0,2,1],[0,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]],shape=[1,7,7,3],dtype=tf.float32)
#input = tf.truncated_normal([1,5,5,3],stddev=0.1)
#卷积核矩阵1
#filter = tf.truncated_normal([3,3,3,2], stddev=0.1)
bias = tf.constant([1,0],shape=[2],dtype=tf.float32)
#滤波器(卷积核)矩阵:两个卷积核,每个卷积核有三个通道,共六个矩阵。第一行[[-1,1],[0,0],[0,1]]为六个矩阵第0行0列的值,[-1,1]第一个数-1是第一个卷积核W0第0通道中(也就是第一个矩阵)第0行第0列的值,第二个数1是第二个卷积核W1第0通道中第0行第0列的值;[0,0] 第一个数0是第一个卷积核W0第1通道中第0行第0列的值,第二个数0是第二个卷积核W1第1通道中第0行第0列的值;以此类推。
filter = tf.constant([[[[-1,1],[0,0],[0,1]],
[[0,1],[-1,-1],[-1,0]],
[[1,0],[-1,0],[1,0]]],
[[[0,-1],[-1,-1],[0,-1]],
[[1,-1],[1,0],[0,-1]],
[[0,0],[-1,-1],[-1,0]]],
[[[0,1],[0,-1],[0,1]],
[[-1,1],[-1,-1],[1,-1]],
[[-1,-1],[1,1],[-1,-1]]]],  shape=[3,3,3,2],dtype=tf.float32)
#卷积计算,两个维度窗口滑动步长分别都为2
op1 = tf.nn.conv2d(input,filter,strides = [1,2,2,1],padding ='VALID')+bias
op2 = tf.nn.conv2d(input,filter,strides = [1,2,2,1],padding = 'SAME')+bias
with tf.Session() as sess:
result1 = sess.run(op1)
result2 = sess.run(op2)
print(sess.run(input))
print(sess.run(filter))
print(result1)
print('###############')


 

运行结果如下:

[[[[0.  0. 0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 0. 0.  1.]

   [ 1. 0.  0.]

   [ 2. 1.  1.]

   [ 0. 0.  2.]

   [ 1. 0.  2.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 2. 0.  0.]

   [ 0. 1.  1.]

   [ 1. 1.  2.]

   [ 0. 2.  0.]

   [ 0. 1.  1.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 2.  1.]

   [ 2. 1.  0.]

   [ 2. 0.  2.]

   [ 1. 0.  2.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 2. 2.  0.]

   [ 2. 1.  1.]

   [ 0. 2.  1.]

   [ 1. 1.  2.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 1. 2.  2.]

   [ 2. 1.  2.]

   [ 0. 1.  2.]

   [ 0. 1.  1.]

   [ 0. 2.  1.]

   [ 0. 0.  0.]]

 

  [[ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]

   [ 0. 0.  0.]]]]

[[[[-1.  1.]

   [ 0. 0.]

   [ 0. 1.]]

 

  [[ 0. 1.]

   [-1. -1.]

   [-1. 0.]]

 

  [[ 1. 0.]

   [-1. 0.]

   [ 1. 0.]]]

 

 

 [[[ 0. -1.]

   [-1. -1.]

   [ 0. -1.]]

 

  [[ 1. -1.]

   [ 1. 0.]

   [ 0. -1.]]

 

  [[ 0. 0.]

   [-1. -1.]

   [-1. 0.]]]

 

 

 [[[ 0. 1.]

   [ 0. -1.]

   [ 0. 1.]]

 

  [[-1. 1.]

   [-1. -1.]

   [ 1. -1.]]

 

  [[-1. -1.]

   [ 1. 1.]

   [-1. -1.]]]]

[[[[-1.  1.]

   [ 4. -4.]

   [ 2. -9.]]

 

  [[-8. -2.]

   [-8. -7.]

   [ 0. -6.]]

 

  [[ 1. -4.]

   [-4. -7.]

   [ 1. 0.]]]]

 

对比一下,和理论分析的一致。

参考文档

【1】http://blog.csdn.net/mao_xiao_feng/article/details/53444333
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: