Tensorflow中的卷积函数
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=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
第四个参数padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式(后面会介绍)
第五个参数:use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认为true
第六个参数:name参数用以指定该操作的name
返回:Tensor,这个输出,就是我们常说的feature map,shape仍然是[batch, height, width, channels]这种形式。
CNN中卷积的理解
这篇文章对CNN卷积的介绍较为详细, https://buptldy.github.io/2016/10/01/2016-10-01-im2col/
文中的两个图比较直观,图二中,input features,可以理解为一个3x3图片的3通道数据,卷积核为2x2大小三通道 2个卷积核。单通道的图片数据转化为矩阵时,是按照卷积核的大小展开为一维向量。图一的理解,则需要交换input和kernel的位置,output map逆时针旋转90度。
实践
用tensorflow的conv2d来验证图2的数据,其关键是输入和filter的数据的构造。
filter的维数[2, 2, 3, 2], 2x2维,3通道,2个卷积核,构造出的数据,不能直接按照卷积核的原始数据reshape得道,而是要重新排列,排列顺序如图。如果要到如图中第一个卷积核的数据则用filter_arg[:, :, 0, 0](也可见代码中的注释部分,通过直接赋值构建filter的值)。同理可以构造输入图片的数据,最后执行的结果和图中一致。
import numpy as np
import tensorflow as tf
input_arg = tf.constant([
[[1, 0, 1], [2, 2, 2], [0, 1, 1]],
[[1, 0, 0], [1, 3, 1], [3, 2, 3]],
[[0, 1, 3], [2, 1, 3], [2, 0, 2]]], dtype = tf.float32)
# [filter_height, filter_width, in_channels, out_channels]
#filter_arg = np.array(np.arange(24)).astype("float32")
#filter_arg = np.reshape(filter_arg, [2, 2, 3, 2]).astype("float32")
#filter_arg[:, :, 0, 0] = [[1, 1], [2, 2]]
#filter_arg[:, :, 1, 0] = [[1, 1], [1, 1]]
#filter_arg[:, :, 2, 0] = [[0, 1], [1, 0]]
#filter_arg[:, :, 0, 1] = [[1, 0], [0, 1]]
#filter_arg[:, :, 1, 1] = [[2, 1], [2, 1]]
#filter_arg[:, :, 2, 1] = [[1, 2], [2, 0]]
filter_arg = tf.constant([
[[[1, 1],
[1, 2],
[0, 1]],
[[1, 0],
[1, 1],
[1, 2]]],
[[[2, 0],
[1, 2],
[1, 2]],
[[2, 1],
[1, 1],
[0, 0]]]], dtype = tf.float32)
input_arg_normal = tf.reshape(input_arg, [1, 3, 3, 3])
filter_arg_normal = tf.reshape(filter_arg, [2, 2, 3, 2])
op1 = tf.squeeze(tf.nn.conv2d(input_arg_normal, filter_arg_normal, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='VALID'))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
input_out = sess.run(input_arg_normal)
print(input_out.shape)
print(input_out)
print("*" * 20)
filter_out = sess.run(filter_arg_normal)
print(filter_out.shape)
print(filter_out)
print("*" * 20)
print(sess.run(op1))
运行结果
(1, 3, 3, 3)
[[[[1. 0. 1.]
[2. 2. 2.]
[0. 1. 1.]]
[[1. 0. 0.]
[1. 3. 1.]
[0, 1]],
[3. 2. 3.]]
[[0. 1. 3.]
[2. 1. 3.]
[2. 0. 2.]]]]
********************
(2, 2, 3, 2)
[[[[1. 1.]
[1. 2.]
[0. 1.]]
[[1. 0.]
[1. 1.]
[1. 2.]]]
[[[2. 0.]
[1. 2.]
[1. 2.]]
[[2. 1.]
[1. 1.]
[0. 0.]]]]
********************
[[[14. 12.]
[20. 24.]]
[[15. 17.]
[24. 26.]]]
参考文章
http://cs231n.github.io/convolutional-networks/