Keras深度学习快速指南



Keras深度学习 - 简介

近年来,深度学习已成为人工智能(AI)领域的一个热门词汇。多年来,我们一直使用机器学习(ML)来赋予机器智能。近年来,由于深度学习在预测方面优于传统的机器学习技术,因此变得越来越流行。

深度学习本质上意味着使用海量数据训练人工神经网络(ANN)。在深度学习中,网络可以自我学习,因此需要海量数据进行学习。而传统的机器学习本质上是一组解析数据并从中学习的算法。然后,它们利用这些学习成果做出明智的决策。

现在,谈到Keras,它是一个高级神经网络API,运行在TensorFlow之上——一个端到端的开源机器学习平台。使用Keras,您可以轻松定义复杂的人工神经网络架构,以在您的海量数据上进行实验。Keras还支持GPU,这对于处理海量数据和开发机器学习模型至关重要。

在本教程中,您将学习如何在构建深度神经网络中使用Keras。我们将通过实践示例进行教学。手头的问题是使用经过深度学习训练的神经网络识别手写数字。

为了让您对深度学习更加兴奋,以下是Google趋势中关于深度学习的截图:

screenshot Google trends

从图中可以看出,过去几年人们对深度学习的兴趣稳步增长。深度学习已成功应用于许多领域,例如计算机视觉、自然语言处理、语音识别、生物信息学、药物设计等等。本教程将帮助您快速入门深度学习。

所以请继续阅读!

Keras深度学习 - 深度学习

如引言中所述,深度学习是使用海量数据训练人工神经网络的过程。训练完成后,网络将能够对未见数据进行预测。在进一步解释深度学习是什么之前,让我们快速了解一下训练神经网络中使用的一些术语。

神经网络

人工神经网络的想法源于我们大脑中的神经网络。典型的神经网络包含三层——输入层、输出层和隐藏层,如下面的图片所示。

Neural Networks

这也被称为浅层神经网络,因为它只包含一层隐藏层。您可以在上述架构中添加更多隐藏层以创建更复杂的架构。

深度网络

下图显示了一个包含四个隐藏层、一个输入层和一个输出层的深度网络。

Deep Networks

随着向网络添加更多隐藏层,其训练在所需资源和完全训练网络所需的时间方面变得更加复杂。

网络训练

定义网络架构后,您需要对其进行训练以执行某些类型的预测。训练网络是一个为网络中每个链接找到合适权重的过程。在训练期间,数据通过各个隐藏层从输入层流向输出层。由于数据始终沿一个方向从输入到输出移动,因此我们将此网络称为前馈网络,并将数据传播称为前向传播。

激活函数

在每一层,我们计算输入的加权和并将其馈送到激活函数。激活函数为网络引入了非线性。它只是一个将输出离散化的数学函数。一些最常用的激活函数包括sigmoid、双曲正切(tanh)、ReLU和Softmax。

反向传播

反向传播是一种用于监督学习的算法。在反向传播中,误差从输出层反向传播到输入层。给定一个误差函数,我们计算误差函数相对于分配给每个连接的权重的梯度。梯度的计算从网络向后进行。首先计算最后一层权重的梯度,最后计算第一层权重的梯度。

在每一层,梯度的部分计算都会在计算前一层的梯度时重复使用。这称为梯度下降。

在本项目教程中,您将定义一个前馈深度神经网络,并使用反向传播和梯度下降技术对其进行训练。幸运的是,Keras为我们提供了所有高级API,用于定义网络架构并使用梯度下降对其进行训练。接下来,您将学习如何在Keras中做到这一点。

手写数字识别系统

在这个小型项目中,您将应用前面描述的技术。您将创建一个深度学习神经网络,用于训练识别手写数字。在任何机器学习项目中,第一个挑战都是收集数据。特别是对于深度学习网络,您需要海量数据。幸运的是,对于我们试图解决的问题,有人已经创建了一个用于训练的数据集。这称为mnist,它作为Keras库的一部分提供。数据集包含多个28x28像素的手写数字图像。您将在该数据集的大部分数据上训练您的模型,其余数据将用于验证您训练的模型。

项目描述

mnist数据集包含70000张手写数字图像。这里复制了一些示例图像供您参考

Mnist Dataset

每张图像的大小为28 x 28像素,总共有768个不同灰度级别的像素。大多数像素倾向于黑色阴影,而只有少数像素倾向于白色。我们将这些像素的分布放入数组或向量中。例如,典型数字4和5图像的像素分布如下图所示。

每张图像的大小为28 x 28像素,总共有768个不同灰度级别的像素。大多数像素倾向于黑色阴影,而只有少数像素倾向于白色。我们将这些像素的分布放入数组或向量中。例如,典型数字4和5图像的像素分布如下图所示。

Project Description

显然,您可以看到像素的分布(特别是那些倾向于白色色调的像素)有所不同,这区分了它们所代表的数字。我们将这784个像素的分布作为输入馈送到我们的网络。网络的输出将包含10个类别,表示0到9之间的数字。

我们的网络将包含4层——一个输入层、一个输出层和两个隐藏层。每个隐藏层将包含512个节点。每一层都完全连接到下一层。当我们训练网络时,我们将计算每个连接的权重。我们通过应用前面讨论的反向传播和梯度下降来训练网络。

Keras深度学习 - 项目设置

有了这些背景知识,让我们现在开始创建项目。

项目设置

我们将通过Anaconda导航器使用Jupyter来进行我们的项目。由于我们的项目使用TensorFlow和Keras,您需要在Anaconda设置中安装它们。要在控制台窗口中安装Tensorflow,请运行以下命令

>conda install -c anaconda tensorflow

要安装Keras,请使用以下命令:

>conda install -c anaconda keras

您现在可以开始使用Jupyter了。

启动Jupyter

当您启动Anaconda导航器时,您将看到以下启动屏幕。

Starting Jupyter

单击“Jupyter”以启动它。屏幕将显示驱动器上现有的项目(如果有)。

启动新项目

通过选择以下菜单选项,在Anaconda中启动一个新的Python 3项目:

File | New Notebook | Python 3

菜单选择的屏幕截图显示在下面,供您快速参考:

Starting New Project

一个新的空白项目将显示在您的屏幕上,如下所示:

Digit Recognition

通过单击并编辑默认名称“UntitledXX”将项目名称更改为DeepLearningDigitRecognition

Keras深度学习 - 导入库

我们首先导入项目代码中所需的各种库。

数组处理和绘图

像往常一样,我们使用numpy进行数组处理,使用matplotlib进行绘图。这些库使用以下import语句导入到我们的项目中

import numpy as np
import matplotlib
import matplotlib.pyplot as plot

抑制警告

由于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、DropoutActivation包以定义网络架构。我们使用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作为后端。此阶段的屏幕截图显示如下:

Keras

现在,由于我们已经拥有项目所需的所有导入,我们将继续为深度学习网络定义架构。

创建深度学习模型

我们的神经网络模型将包含一个线性层堆栈。要定义这样的模型,我们调用Sequential函数:

model = Sequential()

输入层

我们使用以下程序语句定义输入层,它是网络中的第一层:

model.add(Dense(512, input_shape=(784,)))

这会创建一个包含 512 个节点(神经元)的层,以及 784 个输入节点。如下图所示:

Input Layer

请注意,所有输入节点都完全连接到第一层,即每个输入节点都连接到第一层的全部 512 个节点。

接下来,我们需要为第一层的输出添加激活函数。我们将使用 ReLU 作为我们的激活函数。激活函数使用以下程序语句添加:

model.add(Activation('relu'))

接下来,我们使用下面的语句添加 20% 的 Dropout。Dropout 是一种用于防止模型过拟合的技术。

model.add(Dropout(0.2))

至此,我们的输入层已完全定义。接下来,我们将添加一个隐藏层。

隐藏层

我们的隐藏层将包含 512 个节点。隐藏层的输入来自我们之前定义的输入层。所有节点都像之前的情况一样完全连接。隐藏层的输出将传递到网络中的下一层,这将是我们的最终输出层。我们将使用与上一层相同的 ReLU 激活函数和 20% 的 Dropout。添加此层的代码如下所示:

model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.2))

此时,网络可以可视化为:

Input Layer

接下来,我们将向我们的网络添加最终层,即输出层。请注意,您可以使用类似于此处使用的代码添加任意数量的隐藏层。添加更多层会使网络训练变得复杂;但是,在许多情况下(尽管并非所有情况)都能带来更好的结果的显著优势。

输出层

输出层仅包含 10 个节点,因为我们希望将给定的图像分类为 10 个不同的数字。我们使用以下语句添加此层:

model.add(Dense(10))

由于我们希望将输出分类为 10 个不同的单元,因此我们使用 softmax 激活函数。在 ReLU 的情况下,输出是二进制的。我们使用以下语句添加激活函数:

model.add(Activation('softmax'))

此时,我们的网络可以可视化为下图所示:

Output Layer

此时,我们的网络模型已在软件中完全定义。运行代码单元,如果没有任何错误,您将在屏幕上看到确认消息,如下面的屏幕截图所示:

network model

接下来,我们需要编译模型。

使用 Keras 进行深度学习 - 编译模型

编译是使用一个名为 **compile** 的方法调用执行的。

model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')

**compile** 方法需要几个参数。loss 参数指定为类型 **'categorical_crossentropy'**。metrics 参数设置为 **'accuracy'**,最后我们使用 **adam** 优化器来训练网络。此阶段的输出如下所示:

compile method

现在,我们准备将数据馈送到我们的网络中。

加载数据

如前所述,我们将使用 Keras 提供的 **mnist** 数据集。当我们将数据加载到我们的系统中时,我们将将其拆分为训练数据和测试数据。数据通过调用 **load_data** 方法加载,如下所示:

(X_train, y_train), (X_test, y_test) = mnist.load_data()

此阶段的输出如下所示:

Loading 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 轴标记。运行代码时,您将看到以下输出:

Examining Data Points

接下来,我们将准备数据以将其馈送到我们的网络中。

使用 Keras 进行深度学习 - 准备数据

在将数据馈送到网络之前,必须将其转换为网络所需的形式。这称为为网络准备数据。它通常包括将多维输入转换为一维向量并标准化数据点。

重塑输入向量

我们数据集中图像包含 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 的一维向量。

标准化数据

输入向量当前包含的数据具有 0 到 255 之间的离散值 - 灰度级。将这些像素值标准化为 0 到 1 之间有助于加快训练速度。由于我们将使用随机梯度下降,因此标准化数据还有助于减少陷入局部最优解的可能性。

为了标准化数据,我们将其表示为浮点类型并将其除以 255,如下面的代码片段所示:

X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255

现在让我们看看标准化后的数据是什么样子。

检查标准化数据

要查看标准化后的数据,我们将调用直方图函数,如下所示:

plot.hist(X_train[0])
plot.title("Digit: {}".format(y_train[0]))

在这里,我们绘制了 **X_train** 向量第一个元素的直方图。我们还打印了此数据点表示的数字。运行上述代码的输出如下所示:

Normalized Data

您会注意到值接近零的点的密度很大。这些是图像中的黑点,显然是图像的主要部分。其余的灰度点,即接近白色,表示数字。您可以查看另一个数字的像素分布。以下代码打印训练数据集中索引为 2 的数字的直方图。

plot.hist(X_train[2])
plot.title("Digit: {}".format(y_train[2])

运行上述代码的输出如下所示:

training dataset

比较以上两张图,您会注意到两张图像中白色像素的分布不同,表明表示不同的数字 - 上述两张图片中的“5”和“4”。

接下来,我们将检查完整训练数据集中数据的分布。

检查数据分布

在使用我们的数据集中训练机器学习模型之前,我们应该了解数据集中唯一数字的分布。我们的图像表示 10 个不同的数字,范围从 0 到 9。我们想知道数据集中数字 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 次,依此类推。此处显示输出的屏幕截图:

distinct values

作为数据准备的最后一步,我们需要对数据进行编码。

编码数据

我们的数据集中有十个类别。因此,我们将使用独热编码将我们的输出编码为这十个类别。我们使用 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)

此时,您的数据已完全准备好馈送到网络中。

接下来,是最重要的部分,即训练我们的网络模型。

使用 Keras 进行深度学习 - 训练模型

模型训练在一个名为 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 个 epochs(迭代)内收敛。训练后的模型在测试数据上进行验证,如最后一个参数中指定的那样。

运行上述命令的部分输出如下所示:

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

为了方便参考,下面给出了输出的屏幕截图:

epochs

现在,由于模型已在我们的训练数据上进行了训练,我们将评估其性能。

评估模型性能

为了评估模型性能,我们调用 **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% 的情况下,手写数字将无法正确分类。我们还将绘制准确率和损失指标,以查看模型在测试数据上的表现。

绘制准确率指标

我们使用训练期间记录的 **history** 来获取准确率指标的图表。以下代码将绘制每个 epoch 的准确率。我们选择训练数据准确率 (“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')

输出图如下所示:

Plotting Accuracy Metrics

如您在图中看到的,准确率在前两个 epoch 中迅速提高,表明网络学习速度很快。之后,曲线趋于平缓,表明不需要太多 epoch 来进一步训练模型。通常,如果训练数据准确率 (“acc”) 持续提高,而验证数据准确率 (“val_acc”) 却变差,则说明您遇到了过拟合。这表明模型开始记忆数据。

我们还将绘制损失指标来检查模型的性能。

绘制损失指标

同样,我们绘制训练 (“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')

此代码的输出如下所示:

Plotting Loss Metrics

如您在图中看到的,训练集上的损失在前两个 epoch 中迅速下降。对于测试集,损失下降的速度不如训练集,但在多个 epoch 中保持几乎平坦。这意味着我们的模型能够很好地推广到未见过的数据。

现在,我们将使用训练好的模型来预测测试数据中的数字。

测试数据预测

预测未见过数据中的数字非常简单。您只需调用 **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

现在,由于您已经成功训练了模型,我们将保存它以备将来使用。

使用 Keras 进行深度学习 - 保存模型

我们将训练后的模型保存在本地驱动器中的 models 文件夹中,该文件夹位于当前工作目录中。要保存模型,请运行以下代码:

directory = "./models/"
name = 'handwrittendigitrecognition.h5'
path = os.path.join(save_dir, name)
model.save(path)
print('Saved trained model at %s ' % path)

运行代码后的输出如下所示:

Saving Model

现在,由于您已保存了一个训练好的模型,您可以稍后使用它来处理未知数据。

加载模型进行预测

要预测未见数据,您首先需要将训练好的模型加载到内存中。这可以通过以下命令完成:

model = load_model ('./models/handwrittendigitrecognition.h5')

请注意,我们只是将 .h5 文件加载到内存中。这会在内存中设置整个神经网络,以及分配给每一层的权重。

现在,要对未见数据进行预测,请将数据(可以是一个或多个项目)加载到内存中。对数据进行预处理以满足模型的输入要求,就像您之前对训练和测试数据所做的那样。预处理后,将其馈送到您的网络。模型将输出其预测结果。

使用 Keras 进行深度学习 - 结论

Keras 提供了一个高级 API 用于创建深度神经网络。在本教程中,您学习了如何创建一个用于查找手写文本中数字的深度神经网络。为此创建了一个多层网络。Keras 允许您在每一层定义您选择的激活函数。使用梯度下降,网络在训练数据上进行了训练。在测试数据上测试了训练后的网络预测未见数据的准确性。您学习了如何绘制准确性和误差指标。网络完全训练后,您将网络模型保存以备将来使用。

广告