Deep Learning With Keras 简明教程
Deep Learning with Keras - Introduction
近年来,在人工智能(AI)领域,深度学习已成为热门词汇。多年来,我们使用机器学习(ML)为机器赋予智能。近年来,深度学习因其在预测方面优于传统 ML 技术而变得更加流行。
深度学习本质上是指使用大量数据训练人工神经网络(ANN)。在深度学习中,网络会自己学习,因此需要大量数据用于学习。而传统机器学习本质上是一组分析数据并从中学习的算法。然后,他们利用这种学习做出明智的决策。
现在,谈论 Keras,它是一个高级神经网络 API,运行在 TensorFlow 之上——一个端到端的开源机器学习平台。使用 Keras,您可以轻松定义复杂的人工神经网络架构,以对您的海量数据进行实验。Keras 还支持 GPU,这对于处理海量数据和开发机器学习模型至关重要。
在本教程中,您将学习在构建深度神经网络中使用 Keras。我们将研究用于教学的实际示例。手头的问题是使用经过深度学习训练的神经网络识别手写数字。
为了让您对深度学习更加兴奋,以下是在此处关于深度学习的谷歌趋势的屏幕截图:
正如您在图表中看到的,过去几年对深度学习的兴趣一直在稳步增长。计算机视觉、自然语言处理、语音识别、生物信息学、药物设计等许多领域已成功应用了深度学习。本教程将让您快速入门深度学习。
因此请继续往下读!
Deep Learning with Keras - Deep Learning
如引言所述,深度学习是一个用大量数据训练人工神经网络的过程。一旦经过训练,网络将能够对看不见的数据给我们预测。在我进一步解释深度学习是什么之前,让我们快速浏览一下在训练神经网络时使用的一些术语。
Neural Networks
人工神经网络的想法源自我们大脑中的神经网络。典型的神经网络由三层组成——输入层、输出层和隐藏层,如下图所示。
这也称为 {a0} 神经网络,因为它仅包含一个隐藏层。您可以在上述架构中添加更多隐藏层,以创建更复杂的架构。
Network Training
在你定义了网络架构后,可以为该网络进行训练,以进行特定类型的预测。训练网络是一个查找网络中每个链接的适当权重过程。在训练过程中,数据将通过各种隐藏层从输入层流向输出层。由于数据总是从输入到输出朝一个方向移动,我们将此网络称为前馈网络并将数据传播称为前向传播。
Handwritten Digit Recognition System
在此迷你项目中,您将应用前面描述的技术。您将创建一个用于识别手写数字的深度学习神经网络。在任何机器学习项目中,第一个挑战都是收集数据。特别是,对于深度学习网络来说,您需要大量数据。幸运的是,对于我们试图解决的问题,有人已经创建了一个用于训练的数据集。这被称为 mnist,它作为 Keras 库的一部分提供。该数据集包含几个 28x28 像素的手写数字图像。您将在该数据集的主要部分训练您的模型,而其余数据将用于验证您的训练模型。
Project Description
mnist 数据集包含 70000 个手写数字图像。这里复制了几幅示例图像供您参考
每一幅图像大小为 28 x 28 像素,总共为 768 个像素,具有各个灰度级别。大多数像素偏向于黑色阴影,而只有少数像素偏向于白色。我们将把这些像素的分布放入一个数组或一个向量中。例如,数字 4 和 5 的典型图像的像素分布如下所示。
每一幅图像大小为 28 x 28 像素,总共为 768 个像素,具有各个灰度级别。大多数像素偏向于黑色阴影,而只有少数像素偏向于白色。我们将把这些像素的分布放入一个数组或一个向量中。例如,数字 4 和 5 的典型图像的像素分布如下所示。
显然,您可以看到像素的分布(尤其是偏向白色调的像素)不同,这可以区分它们所代表的数字。我们将把这 784 个像素的分布作为输入馈送至我们的网络。该网络的输出将包含 10 个类别,表示介于 0 到 9 之间的数字。
我们的网络将包含 4 层 — 一层输入层、一层输出层和两层隐藏层。每个隐藏层将包含 512 个节点。每一层都与下一层完全连接。当我们训练网络时,我们将计算每个连接的权重。我们通过应用反向传播和梯度下降来训练网络,我们之前讨论过这些技术。
Deep Learning with Keras - Setting up Project
Deep Learning with Keras - Importing Libraries
我们首先导入项目代码所需的各种库。
Array Handling and Plotting
通常,我们将 numpy 用于数组处理并将 matplotlib 用于绘图。在我们的项目中,通过以下 import 语句导入了这些库
import numpy as np
import matplotlib
import matplotlib.pyplot as plot
Suppressing Warnings
由于 Tensorflow 和 Keras 会持续修改,如果您未在项目中同步它们的适当版本,在运行时您会看到许多警告错误。为了让您可以专心学习而不被这些错误分散注意力,我们将抑制该项目中的所有警告。这是通过以下几行代码完成的:
# silent all warnings
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='3'
import warnings
warnings.filterwarnings('ignore')
from tensorflow.python.util import deprecation
deprecation._PRINT_DEPRECATION_WARNINGS = False
Keras
我们使用 Keras 库导入数据集。我们将使用 mnist 手写数字数据集。我们使用以下语句导入所需的软件包
from keras.datasets import mnist
我们将使用 Keras 软件包定义我们的深度学习神经网络。我们导入 Sequential, Dense, Dropout 和 Activation 软件包来定义网络架构。我们使用 load_model 软件包来保存并检索我们的模型。我们还使用 np_utils 来获得项目中需要的几个实用工具。这些导入通过以下程序语句完成:
from keras.models import Sequential, load_model
from keras.layers.core import Dense, Dropout, Activation
from keras.utils import np_utils
运行此代码时,您将在控制台上看到一条消息,表明 Keras 在后端使用 TensorFlow。此阶段的屏幕截图如下所示:
现在,由于我们已经有了项目所需的所有导入,我们将继续为我们的深度学习网络定义架构。
Creating Deep Learning Model
我们的神经网络模型将包含一个线性层堆栈。要定义这样的模型,我们调用 Sequential 函数 -
model = Sequential()
Input Layer
我们的网络中使用以下程序语句定义的输入层是第一层 −
model.add(Dense(512, input_shape=(784,)))
这将创建具有 512 个节点(神经元)和 784 个输入节点的层。这在下面的图中表示 −
请注意,所有输入节点都完全连接到第 1 层,即每个输入节点都连接到第 1 层的所有 512 个节点。
接下来,我们需要添加第 1 层的输出激活函数。我们将使用 ReLU 作为我们的激活。使用以下程序语句添加激活函数 −
model.add(Activation('relu'))
接下来,我们使用下面的语句添加 20% 的 Dropout。Dropout 是一种用于防止模型过拟合的技术。
model.add(Dropout(0.2))
此时,我们的输入层已完全定义。接下来,我们将添加一个隐藏层。
Hidden Layer
我们的隐藏层将包含 512 个节点。隐藏层的输入来自我们之前定义的输入层。所有节点都完全连接,如同之前的情况一样。隐藏层的输出将进入网络中的下一层,它将成为我们的最终输出层。我们将使用与前一层相同的 ReLU 激活,并且 Dropout 为 20%。在此处给出添加此层的代码 −
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.2))
此时,网络可以可视化为以下内容 −
接下来,我们将向我们的网络添加最终层,即输出层。请注意,您可以使用与此处使用的类似代码添加任意数量的隐藏层。添加更多层会使网络难以训练;然而,在很多情况下(尽管不是全部)给出了更好的结果的明确优势。
Deep Learning with Keras - Compiling the Model
编译使用一个称为 compile 的单方法调用执行。
model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')
compile 方法需要多个参数。loss 参数指定为类型 'categorical_crossentropy' 。metrics 参数设置为 'accuracy' ,最后我们使用 adam 优化器训练网络。此阶段的输出如下所示:
现在,我们可以将数据馈送到我们的网络中。
Loading Data
如前所述,我们将使用 Keras 提供的 mnist 数据集。当我们将数据加载到我们的系统中时,我们将把它分成训练数据和测试数据。通过按如下方式调用 load_data 方法来加载数据 −
(X_train, y_train), (X_test, y_test) = mnist.load_data()
此阶段的输出如下所示:
现在,我们将学习加载数据集的结构。
提供给我们的数据是尺寸为 28 x 28 像素的图形图像,每个图像都包含一个介于 0 和 9 之间的单独数字。我们将在控制台上显示前十张图片。执行此操作的代码如下:
# printing first 10 images
for i in range(10):
plot.subplot(3,5,i+1)
plot.tight_layout()
plot.imshow(X_train[i], cmap='gray', interpolation='none')
plot.title("Digit: {}".format(y_train[i]))
plot.xticks([])
plot.yticks([])
在 10 个计数的迭代循环中,我们在每次迭代上创建一个子图,并在其中显示 X_train 矢量中的图像。我们给每张图像使用 y_train 矢量中的相应标题命名。请注意, y_train 矢量包含 X_train 矢量中相应图像的实际值。通过使用两个方法 xticks 和 yticks 在没有参数的情况下调用,来移除 x 和 y 轴标记。运行代码时,您将看到以下输出:
接下来,我们准备数据将其馈送到我们的网络中。
Deep Learning with Keras - Preparing Data
在将数据馈送到我们的网络之前,必须将其转换为网络所需的格式。这称为为网络准备数据。它通常包括将多维输入转换为单维矢量并标准化数据点。
Reshaping Input Vector
我们数据集中的图像由 28 x 28 像素组成。在将其馈送到我们的网络之前,这必须转换为大小为 28 * 28 = 784 的单维矢量。我们通过在矢量上调用 reshape 方法来执行此操作。
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
现在,我们的训练矢量将包含 60000 个数据点,每个数据点都包含大小为 784 的单维矢量。类似地,我们的测试矢量将包含 10000 个数据点,每个数据点都是大小为 784 的单维矢量。
Normalizing Data
输入矢量当前包含的数据在 0 和 255 之间的离散值——灰度级。将这些像素值标准化到 0 和 1 之间有助于加快训练速度。由于我们要使用随机梯度下降,标准化数据还有助于降低陷入局部最优的可能性。
要标准化数据,我们将其表示为浮点类型,并除以 255,如以下代码片段中所示:
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
让我们现在看看标准化数据的样子。
Examining Normalized Data
要查看标准化数据,我们将调用直方图函数,如下所示:
plot.hist(X_train[0])
plot.title("Digit: {}".format(y_train[0]))
在这里,我们绘制 X_train 函数的第一个元素的直方图。我们还打印此数据点表示的数字。运行以上代码的输出显示如下:
您会注意到,大量密点数的值接近于零。这些是图像中的黑色圆点,显然是图像的主要部分。其余靠近白色的灰度级点代表数字。您可以查看另一个数字的像素分布。以下代码打印训练集中索引为 2 的数字的直方图。
plot.hist(X_train[2])
plot.title("Digit: {}".format(y_train[2])
运行以上代码的输出显示如下:
比较以上两幅图,您会注意到两幅图像中白色像素的分布不同,表示不同的数字——上方两幅图像中的“5”和“4”。
接下来,我们将检查我们整个训练集中数据的分布。
Examining Data Distribution
在我们的机器学习模型针对我们的数据集进行训练之前,我们应该知道我们数据集中唯一数字的分布。我们的图像表示了从 0 到 9 的 10 个不同的数字。我们想知道我们数据集中数字 0、1 等的数量。我们可以使用 NumPy 的 unique 方法获取此信息。
使用以下命令打印唯一值的数目及每个数字出现的次数:
print(np.unique(y_train, return_counts=True))
运行上述命令后,您将看到以下输出:
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([5923, 6742, 5958, 6131, 5842, 5421, 5918, 6265, 5851, 5949]))
它表明有 10 个不同的值——0 到 9。数字 0 出现 5923 次,数字 1 出现 6742 次,依此类推。输出的屏幕截图显示如下:
作为数据准备的最后一步,我们需要对数据进行编码。
Encoding Data
我们的数据集中有十个类别。因此,我们使用独热编码将我们的输出编码到这十个类别中。我们使用 Numpy 实用程序的 to_categorial 方法来执行编码。对输出数据进行编码后,每个数据点都会转换为大小为 10 的单维向量。例如,数字 5 现在将表示为 [0,0,0,0,0,1,0,0,0,0]。
使用以下代码段对数据进行编码 -
n_classes = 10
Y_train = np_utils.to_categorical(y_train, n_classes)
您可以通过打印分类的 Y_train 向量的前 5 个元素来查看编码结果。
使用以下代码打印前 5 个向量 -
for i in range(5):
print (Y_train[i])
您将看到以下输出 −
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
第一个元素代表数字 5,第二个元素代表数字 0,依此类推。
最后,您还必须对测试数据进行分类,该操作使用以下语句完成 -
Y_test = np_utils.to_categorical(y_test, n_classes)
在此阶段,您的数据已完全准备好馈送到网络中。
接下来,是最重要的部分,即训练我们的网络模型。
Deep Learning with Keras - Training the Model
模型训练在一次方法调用中完成,称为 fit,它接受几个参数,如以下代码中所示 -
history = model.fit(X_train, Y_train,
batch_size=128, epochs=20,
verbose=2,
validation_data=(X_test, Y_test)))
传递给 fit 方法的前两个参数指定训练数据集的特征和输出。
epochs 设置为 20;我们假设训练将在最大 20 个 epoch 内收敛 - 迭代。训练后的模型根据最后一个参数中指定的内容在测试数据上进行验证。
以下是运行上述命令的部分输出 -
Train on 60000 samples, validate on 10000 samples
Epoch 1/20
- 9s - loss: 0.2488 - acc: 0.9252 - val_loss: 0.1059 - val_acc: 0.9665
Epoch 2/20
- 9s - loss: 0.1004 - acc: 0.9688 - val_loss: 0.0850 - val_acc: 0.9715
Epoch 3/20
- 9s - loss: 0.0723 - acc: 0.9773 - val_loss: 0.0717 - val_acc: 0.9765
Epoch 4/20
- 9s - loss: 0.0532 - acc: 0.9826 - val_loss: 0.0665 - val_acc: 0.9795
Epoch 5/20
- 9s - loss: 0.0457 - acc: 0.9856 - val_loss: 0.0695 - val_acc: 0.9792
以下是此输出的屏幕截图,供您快速参考 -
现在,当模型在我们的训练数据上进行训练时,我们将评估其性能。
Evaluating Model Performance
要评估模型性能,我们调用 evaluate 方法如下 −
loss_and_metrics = model.evaluate(X_test, Y_test, verbose=2)
要评估模型性能,我们调用 evaluate 方法如下 −
loss_and_metrics = model.evaluate(X_test, Y_test, verbose=2)
我们将使用以下两个语句打印损失和准确率 −
print("Test Loss", loss_and_metrics[0])
print("Test Accuracy", loss_and_metrics[1])
运行以上语句时,您会看到以下输出 −
Test Loss 0.08041584826191042
Test Accuracy 0.9837
这显示了 98% 的测试准确率,这对于我们来说是可接受的。这对于我们来说意味着,在 2% 的情况下,手写数字将无法正确分类。我们还将绘制准确率和损失指标,以查看模型在测试数据上的表现。
Plotting Accuracy Metrics
我们在训练期间记录了 history 来获取准确性指标的绘图。以下代码将在每个时期绘制准确性。我们挑选训练数据准确率 (“acc”) 和验证数据准确率 (“val_acc”) 来作图。
plot.subplot(2,1,1)
plot.plot(history.history['acc'])
plot.plot(history.history['val_acc'])
plot.title('model accuracy')
plot.ylabel('accuracy')
plot.xlabel('epoch')
plot.legend(['train', 'test'], loc='lower right')
输出图如下所示:
正如您在图表中所看到的,准确性在前两个历元中迅速提高,表明网络正在快速学习。之后,曲线变平,表明训练模型无需太多历元。通常,如果训练数据准确性(“acc”)持续提高,而验证数据准确性(“val_acc”)变差,则表明遇到过度拟合。它表明模型开始记住数据。
我们还将绘制损失指标以检查模型的性能。
Plotting Loss Metrics
同样,我们绘制训练(“loss”)和测试(“val_loss”)数据集上的损失。这是使用以下代码完成的:
plot.subplot(2,1,2)
plot.plot(history.history['loss'])
plot.plot(history.history['val_loss'])
plot.title('model loss')
plot.ylabel('loss')
plot.xlabel('epoch')
plot.legend(['train', 'test'], loc='upper right')
此代码的输出如下所示:
正如您在图表中所看到的,训练集上的损失在前两个历元中迅速下降。对于测试集,损失不会像训练集那样以相同速率下降,而是保持在多个历元中几乎持平。这意味着我们的模型可以很好地推广到看不见的数据。
现在,我们将使用训练好的模型来预测测试数据中的数字。
Predicting on Test Data
预测未知数据中的数字很容易。您只需调用 model 的 predict_classes 方法,方法是将其传递给包含未知数据点的向量。
predictions = model.predict_classes(X_test)
该方法调用在一个向量中返回预测,这个向量可以针对实际值进行 0 和 1 的测试。这是通过以下两个语句完成的:
correct_predictions = np.nonzero(predictions == y_test)[0]
incorrect_predictions = np.nonzero(predictions != y_test)[0]
最后,我们将使用以下两个程序语句打印预测正确和不正确的计数:
print(len(correct_predictions)," classified correctly")
print(len(incorrect_predictions)," classified incorrectly")
运行代码时,您将获得以下输出:
9837 classified correctly
163 classified incorrectly
现在,您已经对模型进行了满意的训练,我们将保存它以备将来使用。
Deep Learning with Keras - Saving Model
我们将把训练好的模型保存在当前工作目录的 models 文件夹中的本地驱动器中。要保存模型,请运行以下代码:
directory = "./models/"
name = 'handwrittendigitrecognition.h5'
path = os.path.join(save_dir, name)
model.save(path)
print('Saved trained model at %s ' % path)
运行代码后的输出如下所示:
现在,由于您已经保存了训练好的模型,以后可以将其用于处理未知数据。