学习了卷积神经网络的特殊之处之后,下面,利用Keras深度学习框架定义LetNet网络,并实现对MNIST数据集的手写数字识别。首先需要了解LetNet模型中各层需要设置的网络参数。
第一层卷积层C1采用6个5×5卷积核对输入图像进行卷积,填充为0,步长为1,输入为32×32的灰度图像,输出则为6个大小为28×28的特征图。C1层每个卷积核有25个权重参数和一个偏置参数,激活函数为Tanh,一共6个卷积核,共有156个可训练参数。
第二层S2为2×2的最大值池化层,步长为2,S2中每个特征图的大小是C1中特征图大小的1/4,即14×14,由于池化运算不影响通道数,因此S2层共有6个特征图。
第三层C3也是一个卷积层,填充为0,步长为1。它同样通过5×5的卷积核去卷积S2层,然后得到的特征图大小为10×10,但是它有16种不同的卷积核,所以就存在16个特征图。原论文中S2层和C3层之间为部分连接,在此为方便实现,设置为全部连接,激活函数为Tanh。
第四层S4为2×2的最大值池化层,步长为2。特征图中的每个单元与C3中相应特征图的2×2窗口相连接,跟C1和S2之间的连接一样,因此,S4共16个5×5特征图。
第五层C5为卷积层,有120个5×5卷积核,每个卷积核与S4层的全部16个5×5的特征图进行卷积运行,故C5特征图的大小为1×1,数量为120个,激活函数为Tanh。C5层可以看作将输入的特征图展开为一维向量,然后和下一层全连。
第六层F6为全连接层,共84个节点,激活函数为Tanh。
第七层OUTPUT为全连接层,激活函数为softmax,共10个节点,代表输出数字识别结果。
下面用代码建立LeNet网络模型。
#建立LeNet网络模型
from keras. models import Sequential
from keras. layers import Dense#全连接层
from keras. layers import Conv2D, MaxPooling2D, Flatten
from keras. layers import ZeroPadding2D
from keras. optimizers import SGD
model=Sequential()
model. add(ZeroPadding2D(2,input_shape=(28,28,1)))
model. add(Conv2D(filters=6,kernel_size=(5,5),padding="valid",input_shape=(32,32,1),activation='Tanh'))model.add(MaxPooling2D(pool_size=(2,2)))
model. add(Conv2D(filters=16,kernel_size=(5,5),padding='valid',acti-vation='Tanh'))
model. add(MaxPooling2D(pool_size=(2,2)))
model. add(Conv2D(filters=120,kernel_size=(5,5),padding='valid',acti-vation='Tanh'))
model. add(Flatten())
model. add(Dense(84,activation='Tanh'))
model. add(Dense(10,activation='softmax'))
model. summary()
在上面代码的最后一行是实现网络结构输出,输出结果如图5-12所示,可以看出各参数值与上面分析相吻合。值得注意的是,卷积神经网络的训练参数共有61706个,远远少于上一章全连接网络的203530个。
图5-12 LeNet网络结构
了解并定义了网络结构之后,在进行网络训练之前,同样还要对数据进行预处理,这里采用的预处理方法和上一章类似,具体细节不再赘述。
#数据预处理
import numpy as np#导入Numpy类库
import keras
from keras. utils import np_utils
from keras. callbacks import EarlyStopping, ModelCheckpoint
np. random.seed(10)#设置产生的随机数据
from keras. datasets import mnist#导入模块,下载MNIST数据
(x_train_image, y_train_label),(x_test_image, y_test_label)=mnist.load_data()
x_Train=x_train_image.reshape(60000,28,28,1).astype('float32')
x_Test=x_test_image.reshape(10000,28,28,1).astype('float32')(www.xing528.com)
x_Train_normalize=x_Train/255#将数据归一化
x_Test_normalize=x_Test/255
#将训练数据和测试数据的标签进行one-hot转化
y_Train_OneHot=np_utils.to_categorical(y_train_label)
y_Test_OneHot=np_utils.to_categorical(y_test_label)
对训练模型的损失函数、优化器、监控指标等参数进行设置之后,通过model.fit函数对网络进行训练。图5-13显示了训练过程。
sgd=SGD(lr=0.05,decay=1e-6,momentum=0.9,nesterov=True)
model. compile(optimizer=sgd, loss='categorical_crossentropy',metrics=['accuracy'])
train_history=model.fit(x=x_Train_normalize, y=y_Train_OneHot, vali-dation_split=0.2,batch_size=500,epochs=20,verbose=1,shuffle=True)
图5-13 训练过程
同样为了更加直观地显示网络训练过程中损失函数和准确率等各项指标的变化,采用类似于上一章的代码绘制训练曲线(图5-14)。
#显示训练过程
import matplotlib. pyplot as plt
def show_train_history(train_history, train, validation):
plt. plot(train_history.history[train])
plt. plot(train_history.history[validation])
plt. title('Train History')
plt. ylabel(train)
plt. xlabel('Epoch')
plt. legend(['train','validation'],loc='upper left')#显示左上角标签
plt. show()
show_train_history(train_history,'acc','val_acc')#画出准确率评估结果
show_train_history(train_history,'loss','val_loss')#画出误差执行结果
图5-14 训练历史曲线
对比上一章的全连接网络的训练历史曲线可以发现,在迭代后期,卷积神经网络在训练集和验证集上的损失曲线及准确率曲线非常接近,也说明卷积神经网络在一定程度上缓解了过拟合问题。
最后可以把测试数据输入网络,以测试数据评估模型准确率。代码如下:
test_loss, test_acc=model.evaluate(x_Test_normalize, y_Test_OneHot)#创建变量存储评估后的准确率数据,(特征值,真实值)
print('accuracy',test_acc)
#进行预测
prediction=model.predict_classes(x_Test)
index=np.arange(0,10000)
index_dif=index[prediction!=y_test_label]
输出结果为:
10000/10000[==============================]-2s 243us/step
accuracy 0. 9894
从测试结果看出,相对于上一章全连接网络的0.9785(可看作97.85%)的准确率,卷积神经网络的准确率有了明显提升。由此可见,尽管卷积神经网络貌似比传统的神经网络具有更复杂的架构和网络连接,但是它的训练参数却是最少的,而且卷积神经网络的学习更加高效,能够自动从数据中提取特征,泛化能力也有所提升。
LeNet和目前主流的卷积神经网络相比,有几个不同点。第一个不同点在于激活函数。LeNet中使用Tanh函数,而现在的卷积神经网络主要使用ReLU函数。此外,原始的LeNet中使用子采样(subsampling)缩小中间数据的大小,而现在的卷积神经网络多采用最大池化。尽管LeNet是一种二十多年前提出的元老级的卷积神经网络,但通过在MNIST数据集上的实验结果可以看出,其性能要明显优于全连接神经网络。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。