我们依然使用之前的MNIST数据集,通过构建简单的LeNet-5卷积神经网络来进行训练。LeNet-5是Yann LeCun教授提出的(今年的图灵奖获得者),它是第一个成功应用于数字识别问题的卷积神经网络。
LeNet-5架构
1. 输入层
我们依然使用之前的mnist数据集,所以每一张图片都是32 * 32 * 1的灰度值图片
2. 卷积层C1
这一层所使用的卷积核尺寸为5 * 5,深度为6。意思是我们使用六个不同的卷积和对图片进行处理每一个核都是5 * 5,所以一共有(5 * 5+1)* 6=156个参数。这里的1代表的是偏差bias。
不使用全0补充,步长为1,所以每一个输入矩阵的输出大小为32-5+1=28,C1层的输出即为28 * 28 * 6。
3. 池化层S2
这一层我们使用2 * 2的池化进行降采样,步长为2,所以输出尺寸为14 * 14 *6。然后使用Sigmoid函数进行非线性处理
4. 卷积层C3
依然使用5 * 5的卷积核,但是深度为16,步长为1,不做全0补充。 所以输出大小为10 * 10 * 16。
5. 池化层S4
和池化层S2相同,所以输出为5 * 5 *16。
6. 卷积层C5
卷积大小依然是5 * 5,但是因为输入的尺寸也是5 * 5,所以相当于是一个全连接层,输出节点数为120。
7. 全连接层F6
输出节点为84个
8. 全连接层
输出节点数为10个,作为全局的输出。
TensorFlow实现
加载TensorFlow和MNIST数据集
和之前一样的输入
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
参数定义
因为隐藏层包含很多参数,我们可以对他们分开定义,也可以定义一个通用表达以便重复使用。
#标准差为0.1的正态分布
def weight_variable(shape):
initial = tf.truncated_normal(shape,stddev=0.1)
return tf.Variable(initial)
#0.1的偏差常数,为了避免死亡节点
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#二维卷积函数
#strides代表卷积模板移动的步长,全是1代表走过所有的值
#padding设为SAME意思是保持输入输出的大小一样,使用全0补充
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')
#ksize [1, height, width, 1] 第一个和最后一个代表对batches和channel做池化,1代表不池化
#strides [1, stride,stride, 1]意思是步长为2,我们使用的最大池化
def max_pool_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1], strides=[1,2,2,1],padding='SAME')
定义输入placeholder,x是特征,y_是label
x = tf.placeholder(tf.float32, [None,784])
y_ = tf.placeholder(tf.float32, [None,10])
#reshape图片成28 * 28 大小,-1代表样本数量不固定,1代表channel
x_image=tf.reshape(x,[-1,28,28,1])
卷积层C1
使用我们之前定义的函数进行赋值
#前面两个5代表卷积核的尺寸,1代表channel,32代表深度
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)
p_conv1 = max_pool_2x2(h_conv1)
卷积层C2
这一层使用64个卷积核
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(p_conv1,W_conv2)+b_conv2)
p_conv2 = max_pool_2x2(h_conv2)
全连接
#输入为7 * 7 * 64, 输出为1024的一维向量
W_fc1 = weight_variable([7*7*64,1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(p_conv2,[-1,7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)
Dropout
为了减轻过拟合问题,我们可以选择加入一个dropout层。我们之前介绍过就是通过一个保留率来随机丢弃一部分节点。
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)
Softmax
和之前一样
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)
损失函数
# 交叉熵
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),reduction_indices=[1]))
#参数优化器Adam代替梯度递减,当然都可以使用,这里只是用一个和以前不一样的
train_step=tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
结果检测
# 正确的预测结果
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
# 计算预测准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
训练
我们使用0.5的保留率,50的mini-batch,20000次迭代
tf.global_variables_initilizer().run()
for i in range(20000):
batch = mnist.train.next_batch(50)
#每一百次打印一次结果,keep_prob设为1意思是用原数据进行检测,所以保留所有数据
if(i%100==0):
train_acuracy = accuracy_eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0})
print("step %d, tranning accuracy %g:"%(i,train_accuracy))
train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})
#训练结束后,在测试集上进行测试
print("Model accuracy: %g:"%accuracy.eval(feed.dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))
最终结果就是99.36%的正确率:
测试结果
优化
还有一些优化的方法,我们会在下一章里进行说明。大家也可以改变这一章节里的参数和方法,尝试更多的优化。
网友评论