一.LeNet概述
LeNet由Yann Lecun 创建,并将该网络用于邮局的邮政的邮政编码识别,有着良好的学习和识别能力。LeNet又称LeNet-5,具有一个输入层,两个卷积层,两个池化层,3个全连接层(其中最后一个全连接层为输出层)。LeNet网络结构图如下:
LeNet-5共有7层(不包含输入),每层都包含可训练参数。
输入图像大小为32*32,比MNIST数据集的图片要大一些,这么做的原因是希望潜在的明显特征如笔画断点或角能够出现在最高层特征检测子感受野(receptive field)的中心。因此在训练整个网络之前,需要对28*28的图像加上paddings(即周围填充0)。
C1层:该层是一个卷积层。使用6个大小为5*5的卷积核,步长为1,对输入层进行卷积运算,特征图尺寸为32-5+1=28,因此产生6个大小为28*28的特征图。这么做够防止原图像输入的信息掉到卷积核边界之外。
S2层:该层是一个池化层(pooling,也称为下采样层)。这里采用max_pool(最大池化),池化的size定为2*2,经池化后得到6个14*14的特征图,作为下一层神经元的输入。
C3层:该层仍为一个卷积层,我们选用大小为5*5的16种不同的卷积核。这里需要注意:C3中的每个特征图,都是S2中的所有6个或其中几个特征图进行加权组合得到的。输出为16个10*10的特征图。
S4层:该层仍为一个池化层,size为2*2,仍采用max_pool。最后输出16个5*5的特征图,神经元个数也减少至16*5*5=400。
C5层:该层我们继续用5*5的卷积核对S4层的输出进行卷积,卷积核数量增加至120。这样C5层的输出图片大小为5-5+1=1。最终输出120个1*1的特征图。这里实际上是与S4全连接了,但仍将其标为卷积层,原因是如果LeNet-5的输入图片尺寸变大,其他保持不变,那该层特征图的维数也会大于1*1。
F6层:该层与C5层全连接,输出84张特征图。为什么是84?下面有论文的解释(感谢翻译)。
输出层:该层与F6层全连接,输出长度为10的张量,代表所抽取的特征属于哪个类别。(例如[0,0,0,1,0,0,0,0,0,0]的张量,1在index=3的位置,故该张量代表的图片属于第三类)
二.MNIST手写体数字识别代码实现
该代码是用搭建好的LeNet-5网络来实现MNIST手写体数字的识别
from keras.datasets import mnist
from keras.utils import np_utils
from keras.layers.core import Dense,Activation ,Dropout
from keras.models import Sequential
from keras.layers import Convolution2D
from keras.layers import MaxPool2D,Flatten
from keras.utils.vis_utils import plot_model
from matplotlib import pyplot as plt
#图像数据预处理
(X_train,Y_train),(X_test,Y_test)=mnist.load_data()
X_test1=X_test #备份未经处理的测试数据,为最后预测阶段做准备
Y_test1=Y_test #备份未经处理的测试标签,为最后预测阶段做准备
X_train=X_train.reshape(-1,28,28,1).astype("float32")/255.0 #四维张量
X_test=X_test.reshape(-1,28,28,1).astype("float32")/255.0
#标签预处理
nb_classes=10
Y_train=np_utils.to_categorical(Y_train,nb_classes)
Y_test=np_utils.to_categorical(Y_test,nb_classes)
#简单cnn的搭建(一个输入层,两个隐藏层,一个全连接层,一个输出层)
#是基于序惯式Sequential()搭建网络模型的
model=Sequential()
#第一层卷积层,搭建层c1层,卷积核大小5*5,个数为6
model.add(Convolution2D(
filters=6, #卷积核个数
kernel_size=(5,5), #卷积核大小5*5
strides=1,
padding="same",#像素填充方式选择same
activation="tanh",
input_shape=(28, 28, 1)#输入数据28*28*1
))
#model.add(Activation("relu"))
#第一层池化层,搭建s2层,池化池化窗口大小2*2
model.add(MaxPool2D(
pool_size=(2,2),#池化窗口大小2*2
strides=1,#池化步长为2
padding="same" #像素填充方式为same
))
#第二层隐藏层,搭建c3层,卷积核大小5*5,个数为16
model.add(Convolution2D(
filters=16, #卷积核的个数
kernel_size=(5,5),#卷积核的大小
padding="same",#填充方式
activation="tanh"
))
#第二层池化层,搭建s4层,池化窗口大小为2*2
model.add(MaxPool2D(pool_size=(2,2),#池化窗口大小
strides=1,#池化步长
padding="same"
))
#将卷积层输出为二维数组,全连接层输入为一维数组,Flatten默认按行降维,返回一个一维数组
model.add(Flatten())
#全连接层
model.add(Dense(120)) #全连接层,该层有120个神经元,一般都是1024,2048,4096个神经元
model.add(Activation("tanh")) #激活函数
#model.add(Dropout(0.2)) #设置为0.2,该层会随机忽略百分之20的神经元
model.add(Dense(84)) #全连接层,该层有84个神经元,一般都是1024,2048,4096个神经元
model.add(Activation("tanh")) #激活函数
#输出层
model.add(Dense(nb_classes)) #输出层,神经元个数为10,对mnist数据集时分类
model.add(Activation("softmax")) #激活函数
#显示模型摘要
model.summary()
#plot_model(model=model, to_file="model_cnn.png", show_shapes=True)
#编译
model.compile(
optimizer="adam",
loss="categorical_crossentropy",
metrics=["accuracy"]
)
#训练
nb_epoch=10
batchsize=1024
training =model.fit(
x=X_train,
y=Y_train,
epochs=nb_epoch,
batch_size=batchsize,
verbose=2,
validation_split=0.2
)
#画出训练过程随着时期(epoc)准确率的变化图
def show_accuracy1(train_history,train,validation):
plt.plot(training.history[train],linestyle="-",color="b") #训练数据结果,“-”代表实线,“b“代表蓝色
plt.plot(training.history[validation],linestyle="--",color="r")#训练数据结果,“--”代表虚线,“r“代表红色
plt.title("Training_accurancy")
plt.xlabel("epoch") #显示x轴标签epoc
plt.ylabel("train") #显示y轴标签train
plt.legend(["train","validation"],loc="lower right")
plt.show()
show_accuracy1(training,"accuracy","val_accuracy")
#画出训练过程中随着时期(epoch)误差的变化图
def show_accuracy2(train_history,train,validation):
plt.plot(training.history[train],linestyle="-",color="b") #训练数据结果,“-”代表实线,“b“代表蓝色
plt.plot(training.history[validation],linestyle="--",color="r")#训练数据结果,“--”代表虚线,“r“代表红色
plt.title("Training_loss")
plt.xlabel("epoch") #显示x轴标签epoc
plt.ylabel("train") #显示y轴标签train
plt.legend(["train","validation"],loc="upper right")
plt.show()
show_accuracy2(training,"loss","val_loss")
#模型评估
test=model.evaluate(X_test,Y_test,verbose=1)
print("误差:",test[0])
print("准确率:",test[1])
#预测
prediction=model.predict_classes(X_test) #将预测结果存到prediction
def plot_image(image): #输入为原始图像
fig=plt.gcf() #获取当前的图像
fig.set_size_inches(2,2)#设置图像大小
plt.imshow(image,cmap="binary") #使用plt.inshow显示图像
plt.show() #开始绘图
#查看图像,真是标签及与测值
def pre_result(i):
plot_image(X_test1[i])
print("真实值:",Y_test1[i])
print("预测值:",prediction[i])
pre_result(4) #即第五项测试项预测