LeNet架构实施
我们将使用简单的LeNet在网络经过MNIST数据集训练后对其进行序列化。
LeNet实施:
让我们构建一个简单的最基本的卷积神经网络(CNN)架构--LeNet(识别手写数字)。在许多方面,LeNet + MNIST是应用于图像分类的“Hello,World”等效的深度学习。LeNet架构简单明了,体积小(就内存占用而言),非常适合教授卷积神经网络(CNN)的基础知识。
LeNet架构由以下层组成,使用CONV => ACT => POOL的模式
LeNet架构总结
上表总结了LeNet架构的参数。我们的输入层采用28行,28列和单个通道(灰度)的输入图像作为深度(即MNIST数据集内图像的尺寸)。然后我们有20个filters,每个filter都是5×5。CONV层之后是ReLU激活,然后是2×2大小和2×2步幅的最大池。该架构的下一个块遵循相同的模式,这次学习50个5×5 filters。随着实际空间输入尺寸的减小,在网络的更深层中看到CONV层的数量增加是常见的。然后我们有两个FC层。第一个FC包含500个隐藏的音符,然后是ReLU激活。最后的FC层控制输出类标签的数量(0-9;每个可能的十个数字一个)。最后,原始架构:
INPUT => CONV => TANH => POOL => CONV => TANH => POOL => FC => TANH => FC
在今天实施LeNet时,将TANH替换为RELU是很常见的 - 我们将遵循相同的准则并使用ReLU作为我们的激活函数。
创建一个文件夹结构,并在新创建的文件夹cnn中添加一个名为lenet.py的新文件,并将其插入下面的Python代码中。
#lenet.py # import the necessary packages from keras.models import Sequential from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.layers.core import Activation from keras.layers.core import Flatten from keras.layers.core import Dense from keras import backend as K class LeNet: @staticmethod def build(width, height, depth, classes): model = Sequential() inputShape =(height, width, depth) # if we are using "channels first", update the input shape #if K.image_data_format() == "channels_first": # inputShape = (depth, height, width) # first set of CONV => RELU => POOL layers model.add(Conv2D(20, (5, 5), padding="same",input_shape=inputShape)) model.add(Activation("relu")) model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) # second set of CONV => RELU => POOL layers model.add(Conv2D(50, (5, 5), padding="same")) model.add(Activation("relu")) model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) # second set of CONV => RELU => POOL layers model.add(Conv2D(50, (5, 5), padding="same")) model.add(Activation("relu")) model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) # first (and only) set of FC => RELU layers model.add(Flatten()) model.add(Dense(500)) model.add(Activation("relu")) # softmax classifier model.add(Dense(classes)) model.add(Activation("softmax")) # return the constructed network architecture return model
现在在cnn文件夹中,添加另一个名为lenet_mnist.py的文件并开始使用。lenet_mnist.py是一个驱动程序脚本,负责:
- 从磁盘加载MNIST数据集。
- 实例化LeNet架构。
- 训练LeNet。
- 评估网络性能。
# lenet_mnist.py # USAGE # python lenet_mnist.py # import the necessary packages from lenet import LeNet from keras.optimizers import SGD from sklearn.preprocessing import LabelBinarizer from sklearn.datasets import fetch_mldata from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from sklearn import datasets from keras import backend as K import matplotlib.pyplot as plt import numpy as np # grab the MNIST dataset (if this is your first time using this # dataset then the 55MB download may take a minute) print("[INFO] accessing MNIST...") dataset = datasets.fetch_mldata('MNIST original') data = dataset.data # if we are using "channels first" ordering, then reshape the # design matrix such that the matrix is: # num_samples x depth x rows x columns if K.image_data_format() == "channels_first": data = data.reshape(data.shape[0], 1, 28, 28) # otherwise, we are using "channels last" ordering, so the design # matrix shape should be: num_samples x rows x columns x depth else: data = data.reshape(data.shape[0], 28, 28, 1) # scale the input data to the range [0, 1] and perform a train/test # split (trainX, testX, trainY, testY) = train_test_split(data / 255.0, dataset.target.astype("int"), test_size=0.25, random_state=42) # convert the labels from integers to vectors le = LabelBinarizer() trainY = le.fit_transform(trainY) testY = le.transform(testY) # initialize the optimizer and model print("[INFO] compiling model...") opt = SGD(lr=0.01) model = LeNet.build(width=28, height=28, depth=1, classes=10) model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"]) # train the network print("[INFO] training network...") H = model.fit(trainX, trainY, validation_data=(testX, testY), batch_size=128, epochs=20, verbose=1) # evaluate the network print("[INFO] evaluating network...") predictions = model.predict(testX, batch_size=128) print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=[str(x) for x in le.classes_])) # plot the training loss and accuracy plt.style.use("ggplot") plt.figure() plt.plot(np.arange(0, 20), H.history["loss"], label="train_loss") plt.plot(np.arange(0, 20), H.history["val_loss"], label="val_loss") plt.plot(np.arange(0, 20), H.history["acc"], label="train_acc") plt.plot(np.arange(0, 20), H.history["val_acc"], label="val_acc") plt.title("Training Loss and Accuracy") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend() plt.show()
要执行我们的脚本,只需发出以下命令:
$ python lenet_mnist.py
然后应从磁盘下载和/或加载MNIST数据集,并开始训练:
这个图表显示了LeNet在MNIST上的损失和准确性,可以说是我们正在寻找的典型图:训练和验证损失和准确性相互模拟(几乎)完全没有过度拟合的迹象。正如我们所看到的,通常很难获得这种行为非常好的训练图,表明我们的网络正在学习基础模式而不会过度拟合。
还有一个问题是,MNIST的数据集是经过大量预处理的,这不能代表我们在现实世界中遇到的图像分类问题。研究人员倾向于使用MNIST数据集作为评估新分类算法的基准。如果他们的方法不能获得> 95%的分类准确度,那么(1)算法的逻辑或(2)实现本身存在缺陷。