(ps:在写conv2d和max_pool之前在网上有位朋友已经把这个方法讲解得很透彻,我把连接奉上,http://blog.csdn.net/mao_xiao_feng/article/details/53444333 ,http://blog.csdn.net/mao_xiao_feng/article/details/53453926 开头部分我先作下简单的表述)
首先看下官方文档的关于conv2d
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
除去name和use_cudnn_on_gpu,与方法有关的一共四个参数:
- 第一个参数input:指需要做卷积的输入图像,它要求是一个Tensor,具有[batch, in_height, in_width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]
比方我们这么写意味着:输入值为数量为10的28x28x像素的有一个图像通道的图片
input_data = tf.Variable(tf.random_uniform([10, 28, 28, 1]))
(关于图像的通道,在做测试的时候mnist就是只有一个颜色通道的灰色图,而CIFAR-10则拥有RGB三种颜色的通道)
- 第二个参数filter:是CNN中的卷积核,它要求是一个Tensor,具有[filter_height, filter_width, in_channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],有一个地方需要注意,第三维in_channels,就是参数input的第四维。而第四纬的out_channels则代表卷积核需要提取的特征。
以下的写法卷积核截取5x5的大小的图片,第三个纬度1与input_data对应,提取32种特征
W_con = tf.Variable(tf.random_uniform([5, 5, 1, 32]))
第三个参数strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
第四个参数padding:只能写"SAME","VALID"其中之一,写SAME的话在做滑动遇到元素不足时允许补全,VALID遇到此情况则多余会被抛弃
我们用代码对mnist做实际的测试,首先引入tensorflow、mnist和numpy
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
首先对于mnist手写数字测试集,相当多的资料讲述已经很多了,简单的说它包含两部分数据,一部分是像素28x28的灰色调图片,另一部分是这些这些图片的指代数字。
比如我们首取一个数据,输出batch_x的shape,batch_y我们暂不理会
batch_x,batch_y=mnist.train.next_batch(1)
print np.shape(batch_x)
输出结果:
(1, 784)
可见batch_x是由28x28=784像素组成的图片,并且我们还发现,原始的数据的纬度不符合运算规则,这时候我们需要进行reshape,然后才能得到我们一开始所说的 [图片数量、宽度、高度、颜色通道]
input_data=tf.reshape(batch_x,[1,28,28,1])
为了便于我们观察卷积神经的处理过程中维度shape的变化,我们先定义两个通用的卷积和池化方法:
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
#第一层卷积,提取32种特征
W_conv1 = tf.Variable(tf.random_uniform([5, 5, 1, 32]))
b_conv1 = tf.constant(0.1, shape=[32])
h_conv1 = conv2d(input_data, W_conv1)
conv2d_relu1 = tf.nn.relu(h_conv1 + b_conv1)
pooling1 = max_pool_2x2(conv2d_relu1)
#第二层卷积,提取64种特征
W_conv2 = tf.Variable(tf.random_uniform([5, 5, 32, 64]))
b_conv2 = tf.constant(0.1, shape=[64])
h_conv2 = conv2d(pooling1, W_conv2)
conv2d_relu2 = tf.nn.relu(h_conv2 + b_conv2)
pooling2 = max_pool_2x2(conv2d_relu2)
打印一下两层卷积处理过程的数据变化,可以为:
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
print 'input_data: ', np.shape(input_data)
print 'conv2d_relu1:', np.shape(sess.run(conv2d_relu1))
print 'pooling1: ', np.shape(sess.run(pooling1))
print 'conv2d_relu2:', np.shape(sess.run(conv2d_relu2))
print 'pooling2: ', np.shape(sess.run(pooling2))
输出结果:
input_data: (1, 28, 28, 1)
conv2d_relu1: (1, 28, 28, 32)
pooling1: (1, 14, 14, 32)
conv2d_relu2: (1, 14, 14, 64)
pooling2: (1, 7, 7, 64)
我们发现每一步运行conv2d,数据末尾的纬度值都会变成我们预先设定的特征值,而结果经过池化pooling,图片的尺寸也是成半的减少