图像识别是深度学习技术的一个普遍具有的功能。
深度学习或机器学习的“hello world”是用MNIST数据集进行手写数字识别。
在这篇文章中,您将了解如何使用Keras深度学习库开发一个深度学习模型,以高性能的实现Python上基于MNIST手写数字识别任务。
通过本次教程,你会知道:
- 如何在Keras中加载MNIST数据集。
- 如何构建和评估MNIST问题的基本神经网络模型。
- 如何实现和评估一个简单的MNIST卷积神经网络。
- 如何进行一个高性能的MNIST深度学习。
让我们开始吧。
MNIST手写数字识别问题的描述
MNIST是由Yann LeCun, Corinna Cortes 和Christopher Burges针对识别手写数字的问题,·对机器学习模型进行评估的数据集。
数据集是从美国国家标准与技术研究院(NIST)提供的众多扫描文件数据中收集的。这也是数据集名称的来源:Modified NIST或MNIST。
这些图像是从各种扫描的文件中收集格式化并居中得到的。这使得它很适合作为评估模型的数据集,它能帮助开发人员节省出时间进行用于机器学习开发,只需进行很少的数据采集或数据准备工作。
每个图像都是28×28像素的(总共784个像素点)。使用数据集的通常用来评估和比较模型,其中使用60,000个图像来训练模型,并且使用另外10,000个图像来评估模型。
这是一个数字识别问题。因此有10个数字(0到9)或者说有10个类别作为预期的输出。使用预测误差来判断结果,只不过是逆分类的准确度。
理想的结果要求达到小于1%的预期错误率。用大型卷积神经网络可以达到约0.2%错误率。Rodrigo Benenson的网站 列出了识别MNIST数据集的最新的论文和最新的结果。
在Keras中加载MNIST数据集
Keras深度学习库为加载MNIST数据集提供了一种方便简洁的方法。
在第一次调用这个函数时,数据集会自动下载,并以15MB文件大小存储在〜/ .keras / datasets / mnist.pkl.gz目录中。
这对开发、测试深度学习模型非常方便。
为了演示加载MNIST数据集是多么容易,我们将首先编写一个脚本来下载数据集,并显示训练数据集中的前4个图像。
代码语言:python代码运行次数:0复制#绘制MNIST实例
from keras.datasets import mnist
import matplotlib.pyplot as plt
#加载(下载,如果需要的话)MNIST数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()
plot 4 images as gray scale
plt.subplot(221)
plt.imshow(X_train0, cmap=plt.get_cmap('gray'))
plt.subplot(222)
plt.imshow(X_train1, cmap=plt.get_cmap('gray'))
plt.subplot(223)
plt.imshow(X_train2, cmap=plt.get_cmap('gray'))
plt.subplot(224)
plt.imshow(X_train3, cmap=plt.get_cmap('gray'))
#绘制图片
plt.show()
你可以看到,下载和加载MNIST数据集就是调用mnist.load_data()函数如此的简单。运行上面的例子,你应该看到下面的图片。
具有多层结构的基本模型
我们真的需要卷积神经网络这样复杂的模型来实现MNIST识别的良好的效果?
你可以使用带有单个层的非常简单的神经网络模型获得非常棒的效果。在本节中,我们将创建一个简单的多层感知器模型,达到仅有1.74%的错误率的效果。我们将用它作为更复杂的卷积神经网络模型的基础。
我们首先导入我们需要的类和函数。
代码语言:js复制import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.utils import np_utils
将随机数产生器初始化为一个常量能让最终的结果是固定的,这是一个好主意。
代码语言:python代码运行次数:0复制# 设定随机数种子
seed = 7
numpy.random.seed(seed)
之后我们可以使用Keras加载MNIST数据集。
代码语言:python代码运行次数:0复制load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
训练数据集被构造为包括图像长宽的三维数组。对于多层感知器模型,我们必须将图像降维为像素矢量。在这种情况下,28×28大小的图像将变为784个像素的输入值。
我们可以使用NumPy数组上的reshape()函数轻松完成这个转换。我们还可以通过强制像素值的灰度值降低到为32位来减少内存需求,原因之一是这是Keras默认的灰度值精度。
代码语言:python代码运行次数:0复制# 对于每一张图片转换 28*28的图像为 784 像素点
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels).astype('float32')
X_test = X_test.reshape(X_test.shape[0], num_pixels).astype('float32')
像素值是0到255之间的灰度值。使用神经网络模型时,对输入值进行缩放是一个好主意。由于按比例缩放是大家所熟悉的,并且变现优异,我们可以通过对每个值除以255这个最大值来非常快速地将像素值控制在0~1的范围。
代码语言:python代码运行次数:0复制# 规范化输入从 0-255 到 0-1
X_train = X_train / 255
X_test = X_test / 255
最终需要输出变量是从0到9的整数。这是一个多类分类输出问题。因此,使用类值的独热编码,将整数向量转换成二进制矩阵是很棒的。
我们可以使用Keras中内置的np_utils.to_categorical()函数完成此操作。
代码语言:python代码运行次数:0复制# 独热编码
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
现在就准备好创建我们简单的模型了。我们用一个函数定义我们的模型。如果你扩展该示例并试图得到更好的效果,这样做会很方便。
代码语言:python代码运行次数:0复制#定义基准模型
def baseline_model():
# 创建模型
model = Sequential()
model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer='normal', activation='relu'))
model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax'))
# 编译模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
该模型是一个简单的神经网络,其中一个层具有与输入相同数量的神经元数量(784)。整流器功能用于处理层中的神经元。
输出层使用softmax函数将输出转换为概率值,并允许选择10个类中的一个作为模型的输出预测。对数的损失用损失函数(在Keras中称为categorical_crossentropy)并用有效的ADAM梯度下降算法来计算权重。
我们现在可以拟合和评估模型。该模型每200幅图像更新一次。测试数据被用作验证数据集,在模型训练时看到模型的进度。具体地说将每个训练时期的结果以两位小数形式有2行的输出。
最后,使用测试数据集来评估模型并输出错误率。
代码语言:python代码运行次数:0复制# 构建模型
model = baseline_model()
# 调整模型
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# 输出错误率
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))
在CPU上运行可能需要几分钟的时间。你应该看到下面的输出。在非常少的代码行中定义的这个非常简单的网络实现了非常可观的仅有1.91%的错误率。
代码语言:js复制Train on 60000 samples, validate on 10000 samples
Epoch 1/10
8s - loss: 0.2797 - acc: 0.9209 - val_loss: 0.1413 - val_acc: 0.9576
Epoch 2/10
8s - loss: 0.1117 - acc: 0.9677 - val_loss: 0.0919 - val_acc: 0.9702
Epoch 3/10
8s - loss: 0.0718 - acc: 0.9796 - val_loss: 0.0782 - val_acc: 0.9774
Epoch 4/10
8s - loss: 0.0505 - acc: 0.9858 - val_loss: 0.0758 - val_acc: 0.9762
Epoch 5/10
8s - loss: 0.0374 - acc: 0.9892 - val_loss: 0.0670 - val_acc: 0.9792
Epoch 6/10
8s - loss: 0.0268 - acc: 0.9929 - val_loss: 0.0630 - val_acc: 0.9803
Epoch 7/10
8s - loss: 0.0210 - acc: 0.9945 - val_loss: 0.0604 - val_acc: 0.9815
Epoch 8/10
8s - loss: 0.0140 - acc: 0.9969 - val_loss: 0.0620 - val_acc: 0.9808
Epoch 9/10
8s - loss: 0.0107 - acc: 0.9978 - val_loss: 0.0598 - val_acc: 0.9812
Epoch 10/10
7s - loss: 0.0080 - acc: 0.9985 - val_loss: 0.0588 - val_acc: 0.9809
Baseline Error: 1.91%
MNIST简单的卷积神经网络
现在我们已经看到了如何加载MNIST数据集并训练一个简单的多层感知器模型,现在是开发一个更复杂的卷积神经网络或CNN模型的时候了。
Keras提供了很多创建卷积神经网络的方法。
在本节中,我们将为MNIST创建一个简单的CNN,演示如何使用CNN实现包括卷积图层,合并图层和压缩图层的方法。
第一步是导入所需的类和函数。
代码语言:python代码运行次数:0复制import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')
同样,我们总是将随机数发生器初始化为一个恒定的值,以获得可重复的结果。
代码语言:python代码运行次数:0复制# 设置随机数种子
seed = 7
numpy.random.seed(seed)
接下来,我们需要加载MNIST数据集并对其进行重新配置,让它适合CNN训练。在Keras中,用于二维卷积的图层理想的输入是具有高维度的像素输入。
在RGB的情况下,红色,绿色和蓝色的像素分量将有三个,并且每个彩色图像将具有3组输入。在MNIST像素值是灰度的情况下,像素维度被设置为1。
代码语言:python代码运行次数:0复制# 加载数据
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 转换数据为 [samples][pixels][width][height]
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32')
像之前一样,将像素值归一化到0和1范围并对输出变量进行独热编码处理。
代码语言:python代码运行次数:0复制# 规范化输入从 0-255 到 0-1
X_train = X_train / 255
X_test = X_test / 255
# 独热编码输出
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
接下来我们定义我们的神经网络模型
卷积神经网络比标准的多层感知器复杂,所以我们从使用所有元素产生理想的结果的简单的结构开始。下面简述一下网络架构。
- 第一层是一个叫做Convolution2D的卷积层。该图层具有32个特征图,其大小为5×5,并具有整流器激活的功能。这是输入图层,期望具有上述结构轮廓像素丰富的图像。
- 第二层我们定义一个采用maxPooling2D最大值的池。它配置的池大小为2×2。
- 第三层是一个使用dropout并称为Dropout的正则化层。它被配置为随机的20%的神经元,用来减少过度拟合。
- 第四层是一个将二维矩阵数据转换为矢量的一个叫Flatten的图层。它允许通过标准完全连通层进行输出处理。
- 第五层是具有128个神经元和整流器激活功能的完全连通层。
- 第六次(输出层)有10个神经元用于区别10个类,softmax激活函数为每个类输出概率估测。
如前所述,使用对数损失和ADAM梯度下降算法来训练模型。
代码语言:python代码运行次数:0复制def baseline_model():
# 创建模型
model = Sequential()
model.add(Conv2D(32, (5, 5), input_shape=(1, 28, 28), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
# 编译模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
我们用与多层感知器相同的方法评估模型。CNN配置10个周期,每批数量为200。
代码语言:python代码运行次数:0复制# 构造
model = baseline_model()
# 调整
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# 最终输出
scores = model.evaluate(X_test, y_test, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))
运行这个实例,训练和验证测试的准确性被打印在每个时期,并且在结束时打印出错率。
在GPU上运行时期可能需要大约45秒(如在AWS上)。你可以看到,神经卷积网络达到了1.03%的错误率,这比我们上面简单的多层感知器模型更好。
代码语言:js复制Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 ============================== - 120s - loss: 0.2346 - acc: 0.9334 - val_loss: 0.0774 - val_acc: 0.9762
Epoch 2/10
60000/60000 ============================== - 42s - loss: 0.0716 - acc: 0.9782 - val_loss: 0.0454 - val_acc: 0.9856
Epoch 3/10
60000/60000 ============================== - 42s - loss: 0.0520 - acc: 0.9842 - val_loss: 0.0429 - val_acc: 0.9853
Epoch 4/10
60000/60000 ============================== - 42s - loss: 0.0406 - acc: 0.9868 - val_loss: 0.0369 - val_acc: 0.9876
Epoch 5/10
60000/60000 ============================== - 42s - loss: 0.0331 - acc: 0.9898 - val_loss: 0.0345 - val_acc: 0.9884
Epoch 6/10
60000/60000 ============================== - 42s - loss: 0.0265 - acc: 0.9917 - val_loss: 0.0323 - val_acc: 0.9905
Epoch 7/10
60000/60000 ============================== - 42s - loss: 0.0220 - acc: 0.9931 - val_loss: 0.0337 - val_acc: 0.9894
Epoch 8/10
60000/60000 ============================== - 42s - loss: 0.0201 - acc: 0.9934 - val_loss: 0.0316 - val_acc: 0.9892
Epoch 9/10
60000/60000 ============================== - 42s - loss: 0.0163 - acc: 0.9947 - val_loss: 0.0281 - val_acc: 0.9908
Epoch 10/10
60000/60000 ============================== - 42s - loss: 0.0135 - acc: 0.9956 - val_loss: 0.0327 - val_acc: 0.9897
CNN Error: 1.03%
更大的MNIST卷积神经网络
现在我们已经看到了如何创建一个简单的CNN,让我们来看看一个能够接近最先进的模型结果。
我们导入类和函数,然后加载、准备与之前CNN示例相同的数据。
代码语言:python代码运行次数:0复制# 加载 CNN 使用 MNIST Dataset
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')
#设定随机数
seed = 7
numpy.random.seed(seed)
# 加载数据
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 转换数据 [samples][pixels][width][height]
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32')
#调整输入范围 从0-255 到 0-1
X_train = X_train / 255
X_test = X_test / 255
#独热编码
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
这一次,我们定义一个大的CNN架构,其中包含额外的卷积、最大池层和完全连接层。网络结构整理如下。
- 具有30个尺寸为5×5的特征图的卷积层。
- 汇聚层数超过2 * 2个块。
- 具有15个尺寸为3×3的特征图的卷积层。
- 汇聚层数超过2 * 2个块。
- Dropout层的概率为20%。
- Flatten 层。
- 与128个神经元和整流器完全连接层。
- 与50个神经元和整流器完全连接层。
- 输出层。
# 定义模型
def larger_model():
# 创建
model = Sequential()
model.add(Conv2D(30, (5, 5), input_shape=(1, 28, 28), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(15, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(50, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
# 编译
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
像前两个实验一样,该模型适合10个周期,每批200。
代码语言:python代码运行次数:0复制# 构造
model = larger_model()
# 调整
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200)
# 最终输出
scores = model.evaluate(X_test, y_test, verbose=0)
print("Large CNN Error: %.2f%%" % (100-scores[1]*100))
运行该实例打印每个时期的训练阶段并验证数据集的准确性以及最终的错误率。
模型需要大约100秒运行每个层。这个较大的模型达到了0.89%的可观的错误率。
代码语言:js复制Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 ============================== - 45s - loss: 0.3912 - acc: 0.8798 - val_loss: 0.0874 - val_acc: 0.9726
Epoch 2/10
60000/60000 ============================== - 43s - loss: 0.0944 - acc: 0.9713 - val_loss: 0.0603 - val_acc: 0.9800
Epoch 3/10
60000/60000 ============================== - 43s - loss: 0.0697 - acc: 0.9781 - val_loss: 0.0377 - val_acc: 0.9880
Epoch 4/10
60000/60000 ============================== - 44s - loss: 0.0558 - acc: 0.9819 - val_loss: 0.0331 - val_acc: 0.9885
Epoch 5/10
60000/60000 ============================== - 44s - loss: 0.0480 - acc: 0.9852 - val_loss: 0.0300 - val_acc: 0.9900
Epoch 6/10
60000/60000 ============================== - 44s - loss: 0.0430 - acc: 0.9862 - val_loss: 0.0293 - val_acc: 0.9897
Epoch 7/10
60000/60000 ============================== - 44s - loss: 0.0385 - acc: 0.9877 - val_loss: 0.0260 - val_acc: 0.9911
Epoch 8/10
60000/60000 ============================== - 44s - loss: 0.0349 - acc: 0.9895 - val_loss: 0.0264 - val_acc: 0.9910
Epoch 9/10
60000/60000 ============================== - 44s - loss: 0.0332 - acc: 0.9898 - val_loss: 0.0222 - val_acc: 0.9931
Epoch 10/10
60000/60000 ============================== - 44s - loss: 0.0289 - acc: 0.9908 - val_loss: 0.0226 - val_acc: 0.9918
Large CNN Error: 0.82%
这不是一个优化的网络结果。最近的论文中也没有重构网络结构。你有很多机会调整和改进这个模型。
MNIST上的资源
MNIST数据集被很好地研究。以下是您可能想要了解的其他一些资源。
- The Official MNIST dataset webpage.
- Rodrigo Benenson’s webpage that lists state of the art results.
- Kaggle competition that uses this dataset(查看示例代码的脚本和论坛部分)
- Read-only model trained on MNIST that you can test in your browser(非常酷)
总结
在这篇文章中,我们了解了MNIST手写数字识别的问题以及使用Keras库在Python中开发的深度学习模型的方法,这些模型能够得到出色的效果。
通过本教程的学习,我们了解:
- 如何加载Keras中的MNIST数据集并生成数据集的图表。
- 如何重新构建MNIST数据集,并针对问题开发一个简单但性能良好的多层感知器模型。
- 如何使用Keras为MNIST创建卷积神经网络模型。
- 如何开发和评估具有近乎世界一流水平的更大的CNN模型。