如上图所示,假设我们有32*32的RBG图片,也就是神经网络的 input 是 32*32*3,表示输入是一个有3个图层的32*32的图片。
假设现在有一个 5*5的 filter处理一个32*32的图层,那么处理后的输出就是一个28*28的图层。现在总共有3个图层,filter需要覆盖这三个图层,因此对于32*32*3的input,filter需要设置为 5*5*3,表示是一个深度为3,长和宽都为5的卷积核。
需要注意的是,使用1个5*5*3的filter处理1个32*32*3的input,输出还是 28*28*1的大小(表示大小为28*28,深度为1的图层)。
在实验中,一个filter对应用来提取 1 种特征,因此一般需要设置多个卷积核提取多个特征。比如上图的第一个CONV层 就是 6 个 5*5*3 的卷积核 ,用于提取 6 种特征。上文已经说过,使用1个filter,则output是一个深度为1的图层。那么这里使用6个filter,则output是一个深度为6的图层。
因此对应上图的第一个CONV层,Input是32*32*3,使用6个5*5*3的filter,output是 28*28*6。
input是一个4为tensor =[batch, height, width, channels]= [样本数量,样本长,样本宽,样本深度(对应图片通道数)],RGB图像通道数为3,黑白图像通道数为1
strides=[ batch 维度上的滑动步长,样本height方向的滑动步长,样本width方向的滑动步长, channels 维度上的滑动步长 ]
一种常用的经典设置是 strides[0]=strides[3]=1。
strides[0] = 1,也即在 batch 维度上的移动为 1,也就是不跳过任何一个样本,否则当初也不该把它们作为输入(input)
strides[3] = 1,也即在 channels 维度上的移动为 1,也就是不跳过任何一个颜色通道;
padding=’SAME’,卷积操作后样本的长和宽不发生改变。
padding=’VALID’,卷积操作后,将样本中不能进行卷积的部分丢掉:
w=[filter的长,filter的宽,input的深度,output的深度]。上文已经介绍,output的深度等于filter的个数。
假设我们使用k*k大小的卷积核对1个n*m的图像运算,在tensorflow里面,我们并不用设置计算图片卷积后的输出大小。我们只需要设置上一层的卷积核个数,及对应下一层的卷积核个数。
1. Input层
例如,我们将一张32*32的RBG图像作为输入,则input = [ 1,32,32,3 ],第一个1表示图片的数量。
2. 第一个CONV层
接受input的第一个卷积层的filter设置为 w=[filter的长,filter的宽,input的深度,output的深度] = [ 3,3,3,32 ],其中input的深度就是图片的图层数(RBG图片有3层)。output的深度为32(等于卷积核的个数)。
3. 第二个CONV层
上一个的CONV层的output的深度作为第二层的input深度。假设第二层的卷积核的大小是3*3,output的深度为64,则w=[ 3,3,32,64 ]
(卷积层 - 池化层) - (卷积层 - 池化层) - (卷积层 - 池化层) - 全连接层 - 输出层
'''
这里,我们首先读取数据MNIST,并分别得到训练集的图片和标记的矩阵,以及测试集的图片和标记的矩阵。代码如下:
'''
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)
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images,mnist.test.labels
'''
接着,需要处理输入的数据,把上述 trX 和 teX 的形状变为[-1,28,28,1],-1 表示不格虑输入图片
的数量,28×28 是图片的长和宽的像素数,1 是通道(channel)数量,因为 MNIST 的图片是黑白的,所以
通道是 1,如果是 RGB 彩色图像,通道是 3。
'''
trX = trX.reshape(-1, 28, 28, 1) # 28x28x1 input img
teX = teX.reshape(-1, 28, 28, 1) # 28x28x1 input img
X = tf.placeholder("float", [None, 28, 28, 1])
Y = tf.placeholder("float", [None, 10])
''''
这里,我们将要构建一个拥有 3 个卷积层和 3 个池化层,随后接 1 个全连接层和 1 个输出层的卷积神经网络。
'''
# 首先定义初始化权重的函数:
def init_weights(shape):
return tf.Variable(tf.random_normal(shape, stddev=0.01))
'''
初始化权重方法如下,我们设置卷积核的大小为 3×3:
'''
# w,patch 大小为 3×3,输入维度为 1,输出维度为 32
w = init_weights([3, 3, 1, 32])
# w2,patch 大小为 3×3,输入维度为 32,输出维度为 64
w2 = init_weights([3, 3, 32, 64])
# w3,patch 大小为 3×3,输入维度为 64,输出维度为 128
w3 = init_weights([3, 3, 64, 128])
# w4,全连接层,输入维度为 128 × 4 × 4(是由上一层的三维输出数据`4*4*128`转变成一维),输出维度为 625
w4 = init_weights([128 * 4 * 4, 625])
# w_o,输出层,输入维度为 625, 输出维度为 10,代表 10 类(labels)
w_o = init_weights([625, 10])
'''
神经网络模型的构建函数,传入以下参数
X:输入数据
w:每一层的权重
p_keep_conv,p_keep_hidden:dropout 要保留的神经元比例
'''
def model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden):
# 第一组卷积层及池化层,最后 dropout 一些神经元
l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
# l1a shape=(?, 28, 28, 32)
l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l1 shape=(?, 14, 14, 32)
l1 = tf.nn.dropout(l1, p_keep_conv)
# 第二组卷积层及池化层,最后 dropout 一些神经元
l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
# l2a shape=(?, 14, 14, 64)
l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l2 shape=(?, 7, 7, 64)
l2 = tf.nn.dropout(l2, p_keep_conv)
# 第三组卷积层及池化层,最后 dropout 一些神经元
l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
# l3a shape=(?, 7, 7, 128)
l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l3 shape=(?, 4, 4, 128)
l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]]) # reshape to (?, 2048)
l3 = tf.nn.dropout(l3, p_keep_conv)
# 全连接层,最后 dropout 一些神经元
l4 = tf.nn.relu(tf.matmul(l3, w4))
l4 = tf.nn.dropout(l4, p_keep_hidden)
# 输出层
pyx = tf.matmul(l4, w_o)
return pyx #返回预测值
'''
我们定义 dropout 的占位符 — keep_conv,它表示在一层中有多少比例的神经元被保留下
来。生成网格模型,得到预测值,如下:
'''
p_keep_conv = tf.placeholder("float")
p_keep_hidden = tf.placeholder("float")
py_x = model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden) #得到预测值
'''
接下来,定义损失函数,这里我们仍然采用 tf.nn.softmax_cross_entropy_with_logits 来比较
预测值和真实值的差异,并做均值处理;定义训练的操作(train_op),采用实现 RMSProp 算法
的优化器 tf.train.RMSPropOptimizer,学习率为 0.001,衰减值为 0.9,使损失最小;定义预测的
操作(predict_op)。具体如下:
'''
cost = tf.reduce_mean(tf.nn. softmax_cross_entropy_with_logits_v2(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
'''
最后输出的`py_x`是一个shape=(?,10)的评分矩阵。每一行对应一个图片属于10个的概率值,tf.argmax(py_x, 1)
就是取出每一行概率值最大的下标。
'''
predict_op = tf.argmax(py_x, 1)
'''
接下来训练模型和评估模型:
'''
# 先定义训练时的批次大小和评估时的批次大小
batch_size = 128
test_size = 256
# 在一个会话中启动图,开始训练和评估
with tf.Session() as sess:
# you need to initialize all variables
tf. global_variables_initializer().run()
for i in range(10):
training_batch = zip(range(0, len(trX), batch_size),
range(batch_size, len(trX)+1, batch_size))
for start, end in training_batch:
sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end],
p_keep_conv: 0.8, p_keep_hidden: 0.5})
test_indices = np.arange(len(teX)) # Get A Test Batch
np.random.shuffle(test_indices)
test_indices = test_indices[0:test_size]
print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
sess.run(predict_op, feed_dict={X: teX[test_indices],
p_keep_conv: 1.0,
p_keep_hidden: 1.0})))
结果:
0 0.953125
1 0.98828125
2 0.9765625
3 0.9921875
4 0.98828125
5 0.99609375
6 0.98828125
7 0.9921875
8 0.9921875
9 0.98828125