一、LeNet网络简介
1.1 LeNet网络的背景
闪光点:定义了CNN的基本组件,是CNN的鼻祖。
LeNet是卷积神经网络的祖师爷LeCun在1998年提出,用于解决手写数字识别的视觉任务。自那时起,CNN的最基本的架构就定下来了:卷积层、池化层、全连接层。如今各大深度学习框架中所使用的LeNet都是简化改进过的LeNet-5(-5表示具有5个层),和原始的LeNet有些许不同,比如把激活函数改为了现在很常用的ReLu。
但是具体没一个网络层的参数设置会稍有不同。比如卷积层卷积核的大小,输出特征图的数量这些都可以自定义的。
1.2 LeNet的网络结构
LeNet一共包含5个网络层,如果算上池化层,就是8个网络层。
输入层:输入层的图像大小为28x28x1
卷积层1:卷积核大小为3x3x1x32,相当于是32个3x3x1的卷积核,移动步幅长为1,故而得到的输出为32个特征图,也可以说成一幅图像有32个通道,28x28x32.
池化层1:采用最大化池化层,为2x2,移动步幅为2,故而输出为:14x14x32
卷积层2:卷积核大小为3x3x32x64,相当于是64个3x3x32的卷积核,移动步幅长为1,故而得到的输出为64个特征图,也可以说成一幅图像有64个通道,14x14x64.
池化层2:采用最大化池化层,为2x2,移动步幅为2,故而输出为:7x7x64
卷积层3:卷积核大小为3x3x64x128,相当于是128个3x3x64的卷积核,移动步幅长为1,故而得到的输出为128个特征图,也可以说成一幅图像有128个通道,7x7x128.
池化层3:采用最大化池化层,为2x2,移动步幅为2,故而输出为:4x4x128
全连接层:输入为128x4x4,需要将其展开成一维的,输出为625.
输出层:输入为625,输出为10.
1.3 LeNet的结构图
二、tensorflow实现LeNet
2.1 实现代码及注释
1import numpy as np
2import tensorflow as tf
3from tensorflow.examples.tutorials.mnist import input_data #导入数据的模块
4
5batch_size=100 #表示每一批训练100组数据,因为训练集共有数据55000组,故而训练一个周期需要经过550次迭代
6test_size=256 #作为验证数据,验证集有10000组数据,但这里只验证256组,因为数据太多,运算太慢
7img_size=28 #手写字图像的大小
8num_class=10 #图像的类别
9
10X=tf.placeholder(dtype=tf.float32,shape=[None,img_size,img_size,1],name='input')
11Y=tf.placeholder(dtype=tf.float32,shape=[None,num_class])
12
13p_keep=tf.placeholder(tf.float32,name='p_keep_rate') #后面用到dropout层的保留的参数
14
15mnist=input_data.read_data_sets('mnist_data',one_hot=True) #导入数据集
16train_X,train_Y,test_X,test_Y=mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels
17
18train_X=train_X.reshape(-1,img_size,img_size,1) #训练数据和测试数据都需要重塑一下形状,因为导入的是724长度的
19test_X=test_X.reshape(-1,img_size,img_size,1)
20
21#第一个卷积层
22with tf.name_scope('cnn_layer_01') as cnn_01:
23 w1=tf.Variable(tf.random_normal(shape=[3,3,1,32],stddev=0.01))
24 conv1=tf.nn.conv2d(X,w1,strides=[1,1,1,1],padding="SAME")
25 conv_y1=tf.nn.relu(conv1)
26
27#第一个池化层
28with tf.name_scope('pool_layer_01') as pool_01:
29 pool_y2=tf.nn.max_pool(conv_y1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
30 pool_y2=tf.nn.dropout(pool_y2,p_keep)
31
32#第二个卷积层
33with tf.name_scope('cnn_layer_02') as cnn_02:
34 w2=tf.Variable(tf.random_normal(shape=[3,3,32,64],stddev=0.01))
35 conv2=tf.nn.conv2d(pool_y2,w2,strides=[1,1,1,1],padding="SAME")
36 conv_y3=tf.nn.relu(conv2)
37
38#第二个池化层
39with tf.name_scope('pool_layer_02') as pool_02:
40 pool_y4=tf.nn.max_pool(conv_y3,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
41 pool_y4=tf.nn.dropout(pool_y4,p_keep)
42
43#第三个卷积层
44with tf.name_scope('cnn_layer_03') as cnn_03:
45 w3=tf.Variable(tf.random_normal(shape=[3,3,64,128],stddev=0.01))
46 conv3=tf.nn.conv2d(pool_y4,w3,strides=[1,1,1,1],padding="SAME")
47 conv_y5=tf.nn.relu(conv3)
48
49#第三个池化层
50with tf.name_scope('pool_layer_03') as pool_03:
51 pool_y6=tf.nn.max_pool(conv_y5,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
52
53#全连接层
54with tf.name_scope('full_layer_01') as full_01:
55 w4=tf.Variable(tf.random_normal(shape=[128*4*4,625],stddev=0.01))
56 FC_layer=tf.reshape(pool_y6,[-1,w4.get_shape().as_list()[0]])
57 FC_layer=tf.nn.dropout(FC_layer,p_keep)
58 FC_y7=tf.matmul(FC_layer,w4)
59 FC_y7=tf.nn.relu(FC_y7)
60 FC_y7=tf.nn.dropout(FC_y7,p_keep)
61
62#输出层,model_Y则为神经网络的预测输出
63with tf.name_scope('output_layer') as output_layer:
64 w5=tf.Variable(tf.random_normal(shape=[625,num_class]))
65 model_Y=tf.matmul(FC_y7,w5,name='output')
66
67#损失函数
68Y_=tf.nn.softmax_cross_entropy_with_logits(logits=model_Y,labels=Y)
69cost=tf.reduce_mean(Y_)
70
71#准确率
72correct_prediction=tf.equal(tf.argmax(model_Y,axis=1),tf.argmax(Y,axis=1))
73accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
74
75#优化方式
76optimizer=tf.train.RMSPropOptimizer(0.001,0.9).minimize(cost)
77
78#将相关的参数写入tensorboard
79#-----------------------------------------------------------------------
80tf.summary.scalar('loss',cost)
81tf.summary.scalar('accuracy',accuracy)
82tf.summary.histogram('w1',w1)
83tf.summary.histogram('w2',w2)
84tf.summary.histogram('w3',w3)
85tf.summary.histogram('w4',w4)
86tf.summary.histogram('w5',w5)
87merge=tf.summary.merge_all()
88#--------------------------------------------------------------------------
89#构建会话任务
90with tf.Session() as sess:
91 tf.global_variables_initializer().run()
92 writer=tf.summary.FileWriter('mnist_cnn_summary_01',graph=sess.graph)
93 #将索引重组一下,得到【(0,100),(100,200),(200,300)。。。。】的形式
94 training_batch=zip(range(0,len(train_X),batch_size),range(batch_size,len(train_X)+1,batch_size))
95
96 run_metadata=tf.RunMetadata()
97 #开始训练,此处只训练一个epoch,若想训练多个epoch,可以再添加一个循环
98 for start,end in training_batch:
99 opti,summary,loss,acc=sess.run([optimizer,merge,cost,accuracy],\
100 feed_dict={X:train_X[start:end],Y:train_Y[start:end],p_keep:0.8},\
101 run_metadata=run_metadata) #这是最关键的一步,optimizer,summary,loss,accuracy均放在一起进行训练
102
103 writer.add_run_metadata(run_metadata,tag='step{0}'.format(start),global_step=(start/batch_size)+1)
104 writer.add_summary(summary,global_step=(start/batch_size)+1)
105
106 print(f'第 {(start/batch_size)+1} 次迭代时,准确度为 {acc},误差为 {loss}',end='\r',flush=True)
107
108 print('===================下面开始进行测试==============================')
109 #下面开始对测试集上的数据进行验证
110 test_index=np.arange(len(test_X))
111 np.random.shuffle(test_index) #将测试集上的10000组数据顺序随即大乱
112 test_index=test_index[0:test_size] #只选择打乱顺序之后的256组样本数据进行测试
113 test_acc=sess.run(accuracy,feed_dict={X:test_X[test_index],Y:test_Y[test_index],p_keep:1})
114 print(f'测试集上面的准确率为 :{test_acc}')
115 print('===================下面是保存模型================================')
116 #保存模型
117 saver=tf.train.Saver()
118 path=saver.save(sess,'mnist_cnn_model/medel.ckpt')
119 print(f'模型欧保存到 {path}')
120 print('训练完毕!')
TensorFlow实现LeNet
2.2 程序运行结果分析
2.2.1 结果打印
1===================下面开始进行测试==============================857185366
2测试集上面的准确率为 :0.9765625
3===================下面是保存模型================================
4模型欧保存到 mnist_cnn_model/medel.ckpt
5训练完毕!
从上面可以看出,在256组测试数据及上面的准确率为0.9765625.只进行了一个epoch的训练能达到这样的效果已经很不错了。
2.2.1 误差曲线及准确率
2.2.2 模型graph
三、模型的加载以及预测
3.1 预测结果
一个魔性训练完成之后,需要将模型保存,这样就可以避免每次使用模型的时候都重新训练,本文只讲实现,具体关于模型如何保存,如何加载以及相关保存文件的具体含义后面会继续讲到。
1import random
2import matplotlib.pyplot as plt
3import tensorflow as tf
4from tensorflow.examples.tutorials.mnist import input_data #导入数据的模块
5
6mnist=input_data.read_data_sets('mnist_data',one_hot=True)
7train_X,train_Y,test_X,test_Y=mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels
8
9
10test_image=mnist.test.images[101] #选取第101张图片
11img_size=28
12test_image=test_image.reshape(-1,img_size,img_size,1)
13
14with tf.Session() as sess: #
15 new_saver=tf.train.import_meta_graph('mnist_cnn_model/medel.ckpt.meta') #第二步:导入模型的图结构
16 new_saver.restore(sess,'mnist_cnn_model/medel.ckpt') #第三步:将这个会话绑定到导入的图中
17 #new_saver.restore(sess,tf.train.latest_checkpoint('mymodel')) #第三步也可以是这样操作,因为会从mymodel文件夹中获取checkpoint,而checkpoint中存储了最新存档的文件路径
18
19 print('=======================模型加载完成=============================')
20 X=sess.graph.get_tensor_by_name('input:0') #从模型中获取输入的那个节点
21 p_keep=sess.graph.get_tensor_by_name('p_keep_rate:0')
22 model_y=sess.graph.get_tensor_by_name('output_layer/output:0')
23 print('=======================输入输出加载完成=============================')
24 result=sess.run(model_y,feed_dict={X:test_image,p_keep:1}) #需要的就是模型预测值model_Y,这里存为result
25 print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
26 print(result)
27 print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
28 soft_result=tf.nn.softmax(result)
29 result1=sess.run(soft_result)
30 print(result1)
31
34 plt.imshow(test_image.reshape([28,28]),cmap='Greys')
35 plt.show()
模型的加载预测
3.2 预测结果分析
本次只选择了一张测试图片,选择的是第101张照片,程序的运行显示为如下:
原始测试图片如下:
运行结果为:
1=======================模型加载完成=============================
2=======================输入输出加载完成=============================
3++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4[[15.647467 -6.0671535 -0.9249869 -5.6294613 -3.727207 0.5262224
5 2.540439 -1.8248326 -1.2350063 3.0618703]]
6++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7[[9.9999416e-01 3.7107148e-10 6.3485118e-08 5.7483751e-10 3.8519756e-09
8 2.7097201e-07 2.0308953e-06 2.5815121e-08 4.6562104e-08 3.4209170e-06]]
从上面的运行结果可以看出,第一个数字为0.99999416,后面的其他数子基本上非常接近于0,说明该数字是0的概率为99.9999%。
全文总结
本文基于TensorFlow实现了经典的LeNet网络,准确率较高,如果您看过之后觉得有什么问题和需要改进的地方,欢迎您留言。另外,后面我会陆续实现相关的经典网络,包括VGGNet,GoogleNet,ResNet,DenseNet等,如果想在深度学习方面一起学习的可以持续关注一下。