dl-keras-ws-merge-2

50 阅读1小时+

Keras 深度学习研讨会(三)

原文:annas-archive.org/md5/b451b2e8c375a884062f55f9c0743702

译者:飞龙

协议:CC BY-NC-SA 4.0

第七章:7. 使用卷积神经网络的计算机视觉

概览

本章涵盖了计算机视觉以及如何通过神经网络实现这一目标。你将学习如何构建图像处理应用程序,并使用卷积神经网络进行模型分类。你还将研究卷积神经网络的架构,并学习如何利用最大池化、展平、特征映射和特征检测等技术。在本章结束时,你不仅能构建自己的图像分类器,还能有效地评估它们,以便应用到自己的实际项目中。

引言

在上一章中,我们详细探讨了模型评估。我们介绍了准确率,以及为什么在某些数据集上,尤其是类别高度不平衡的分类任务中,准确率可能会产生误导。像预测太平洋地区飓风或预测某人是否会违约信用卡贷款这样的数据集,正类实例相较于负类实例较为稀有,因此准确率分数会产生误导,因为空准确率如此之高。

为了应对类别不平衡问题,我们学习了可以用来适当评估模型的技术,包括计算模型评估指标,如灵敏度、特异性、假阳性率和AUC 分数,以及绘制ROC 曲线。在本章中,我们将学习如何分类另一种类型的数据集——即图像。图像分类非常有用,并且有很多现实世界的应用,我们将会发现。

计算机视觉是机器学习和人工智能中最重要的概念之一。随着智能手机在拍摄、分享和上传图片方面的广泛使用,通过图像生成的数据量正在呈指数级增长。因此,专注于计算机视觉领域的专家需求达到了历史最高水平。像医疗保健行业这样的行业,由于医学影像领域的进展,正处于一场革命的边缘。

本章将介绍计算机视觉及其应用的各个行业。你还将学习ANNs(人工神经网络),它使用向量作为输入,而 CNN 则使用图像作为输入。在本章中,我们将更详细地研究CNNs(卷积神经网络),以及与之相关的概念,如最大池化展平特征图特征选择。我们将使用 Keras 作为工具,运行实际图像上的图像处理算法。

计算机视觉

为了理解计算机视觉,我们先来讨论一下人类视觉。人类视觉是指人眼和大脑看见并识别物体的能力。计算机视觉是给机器提供一种类似的,甚至是更好的理解,能够看到和识别现实世界中的物体。

对人眼来说,准确识别一个动物是老虎还是狮子相对简单,但对于计算机系统来说,要区分这些物体需要大量的训练。计算机视觉也可以定义为构建能够模仿人眼和大脑功能的数学模型。基本上,就是训练计算机理解和处理图像与视频。

计算机视觉是许多前沿机器人技术领域的重要组成部分:医疗健康(X 光片、MRI 扫描、CT 扫描等)、无人机、自动驾驶汽车、体育和娱乐等。几乎所有的企业都需要计算机视觉才能成功运营。

想象一下,由全球的 CCTV 视频、我们智能手机每天捕捉的照片、每天在 YouTube 等互联网网站上传播的视频,以及我们在 Facebook 和 Instagram 等社交网站上分享的图片,所生成的大量数据。所有这些都会产生巨量的图像数据。为了处理和分析这些数据,使计算机在处理上变得更智能,这些数据需要高水平的计算机视觉专家来处理。计算机视觉是机器学习中一个高回报的领域。以下章节将描述如何通过神经网络——特别是卷积神经网络——来实现计算机视觉任务,而这些神经网络在计算机视觉任务中表现出色。

卷积神经网络

当我们谈论计算机视觉时,我们几乎总是提到 CNN。CNN 是一类深度神经网络,主要用于计算机视觉和图像领域。CNN 用于识别图像,将它们按相似性进行聚类,并在场景中实现物体识别。CNN 有不同的层次——即输入层、输出层和多个隐藏层。这些隐藏层包括全连接层、卷积层、作为激活函数ReLU 层归一化层和池化层。在一个非常简单的层面上,CNN 帮助我们识别图像并进行适当的标注;例如,一张老虎图像会被识别为老虎:

图 7.1:一个通用的 CNN

图 7.1:一个通用的 CNN

以下是一个 CNN 分类老虎的例子:

图 7.2:一个 CNN 将一张老虎图像分类为“老虎”类别

图 7.2:一个 CNN 将一张老虎图像分类为“老虎”类别

CNN 的架构

CNN 架构的主要组成部分如下:

  • 输入图像

  • 卷积层

  • 池化层

  • 扁平化

输入图像

输入图像是 CNN 架构的第一个组成部分。图像可以是任何类型的:人类、动物、风景、医学 X 光图像等等。每张图像都会被转换成一个由零和一组成的数学矩阵。以下图解释了计算机如何看待字母T的图像。

所有值为 1 的块表示数据,而值为 0 的块表示空白区域:

图 7.3:字母‘T’的矩阵

图 7.3:字母‘T’的矩阵

卷积层

卷积层是图像处理开始的地方。卷积层由两个部分组成:

  • 特征检测器滤波器

  • 特征图

特征检测器滤波器:这是一个矩阵或模式,将其应用到图像上,可以将其转化为特征图:

图 7.4:特征检测器

图 7.4:特征检测器

如我们所见,这个特征检测器被(叠加)放置在原始图像上,并对相应的元素进行计算。计算是通过乘以相应的元素完成的,如下图所示。这个过程会对所有单元进行重复。最终得到一个新的处理过的图像—— (0x0+0x0+0x1) + (0x1+1x0+0x0) + (0x0+0x1+0x1) = 0

图 7.5:特征检测器在图像中的掩膜

图 7.5:特征检测器在图像中的掩膜

特征图:这是通过将图像特征检测器卷积得到的缩小图像。我们必须将特征检测器放在原始图像的所有可能位置,并从中得出一个更小的图像;这个生成的图像就是输入图像的特征图:

图 7.6:特征图

图 7.6:特征图

注意

在这里,特征检测器是滤波器,特征图是缩小后的图像。在图像缩小时,一些信息会丢失。

在实际的卷积神经网络(CNN)中,会使用多个特征检测器生成多个特征图,如下图所示:

图 7.7:多个特征检测器和特征图

图 7.7:多个特征检测器和特征图

池化层

池化层帮助我们忽略图像中不太重要的数据,并进一步缩小图像的大小,同时保留其重要特征。考虑以下三张包含四只猫的图像:

图 7.8:猫图像示例

图 7.8:猫图像示例

为了识别图像中是否包含猫,神经网络分析图片。它可能会查看耳朵形状、眼睛形状等。同时,图像中包含许多与猫无关的特征。前两张图中的树和叶子在猫的识别中是无用的。池化机制帮助算法理解图像中哪些部分是相关的,哪些部分是无关的。

从卷积层得到的特征图会通过池化层进一步减少图像的尺寸,同时保留图像中最相关的部分。池化层由最大池化、最小池化和平均池化等功能组成。这意味着我们选择一个矩阵大小,比如2x2,然后扫描特征图,选择适合该块的2x2矩阵中的最大数值。下图清晰地展示了最大池化是如何工作的。请参考颜色;从特征图中,每个彩色框中的最大值会被选中并放入池化后的特征图:

图 7.9:池化

图 7.9:池化

考虑一个包含数字4的框。假设数字4表示一只猫的耳朵,而耳朵周围的空白部分是01。因此,我们忽略该块中的01,只选择4。以下是我们用来添加池化层的示例代码;这里使用Maxpool2D进行最大池化,它有助于识别最重要的特征:

classifier.add(MaxPool2D(2,2))

展平

Flattening是 CNN 的一部分,其中图像被处理为 ANN 的输入。顾名思义,经过池化的图像被展平,并转换成一个单一的列。每一行被转化为一列,并一个接一个地堆叠在一起。在这里,我们将一个3x3矩阵转换成了一个1xn矩阵,其中n在我们的例子中为9

图 7.10:展平

图 7.10:展平

在实时处理中,我们会得到多个池化后的特征图,并将它们展平成一个单一的列。这个单一的列会作为 ANN 的输入。下图展示了多个池化层被展平为单一列:

图 7.11:池化与展平

图 7.11:池化与展平

以下是我们用来添加展平层的示例代码;这里使用Flatten来展平 CNN:

classifier.add(Flatten())

现在,让我们来看一下卷积神经网络(CNN)的整体结构:

图 7.12:CNN 架构

图 7.12:CNN 架构

以下是我们用来为 CNN 添加第一层的示例代码:

classifier.add(Conv2D(32,3,3,input_shape=(64,64,3),activation='relu'))

32,3,3表示有323x3大小的特征检测器。作为一个好的实践,建议从32开始,之后可以添加64128

Input_shape:由于所有图像的形状和大小都不同,这个input_image将所有图像转换成统一的形状和大小。(64,64)是转换后的图像的尺寸。它可以设置为128256,但如果你在笔记本电脑的 CPU 上工作,建议使用64x64。最后一个参数3是因为图像是彩色图像(使用红、绿、蓝编码,或 RGB)。如果图像是黑白的,那么可以将该参数设置为 1。使用的激活函数是 ReLU。

注意

本书中,我们使用 Keras 并以 TensorFlow 作为后端。如果后端是 Theano,那么input_image将被编码为(3,64,64)。

最后一步是拟合已经创建的数据。这里是我们用来执行此操作的代码:

classifier.fit_generator(training_set,steps_per_epoch = 5000,\
                         epochs = 25,validation_data = test_set,\
                         validation_steps = 1000)

注意

steps_per_epoch 是训练图像的数量。validation_steps 是测试图像的数量。

图像增强

增强这个词意味着使某物变大或增多的动作或过程。图像数据增强以类似的方式工作。图像/数据增强创建了许多我们图像的批次。然后,它对批次中的随机图像应用随机转换。数据转换可以是旋转图像、平移图像、翻转图像等。通过应用这种转换,我们在批次中获得了更多样化的图像,并且我们也拥有了比原来更多的数据。

一个圆柱体可以从不同角度旋转并呈现出不同的视角。在以下图像中,单个圆柱体可以从五个不同的角度看到。因此,我们实际上从一张图像中创建了五张不同的图像:

图 7.13:圆柱体的图像增强

图 7.13:圆柱体的图像增强

以下是我们将用于图像增强的一些示例代码;在这里,ImageDataGenerator 类用于处理。shear_rangezoom_rangehorizontal_flip 都用于转换图像:

from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale = 1./255.0,\
                                   shear_range = 0.3,\
                                   zoom_range = 0.3,\
                                   horizontal_flip = False)
test_datagen = ImageDataGenerator(rescale = 1./255.0)

图像增强的优势

图像增强是图像处理中的一个重要部分:

  • 减少过拟合:通过创建相同图像的多个版本,并将其旋转一个给定角度,从而有助于减少过拟合。

  • 增加图像数量:一张图像作为多张图像处理。因此,本质上,数据集中的图像较少,但每张图像都可以通过图像增强转换为多张图像。图像增强将增加图像的数量,并且每张图像将被算法以不同的方式处理。

  • 容易预测新图像:想象一张足球的图像从不同角度拍摄,每个角度都被认为是一张不同的图像。这意味着算法在预测新图像时将更加准确:

图 7.14:足球图像的图像增强

图 7.14:足球图像的图像增强

现在我们已经学习了计算机视觉与卷积神经网络(CNN)的概念和理论,让我们做一些实际的例子。

首先,我们将从一个练习开始,构建一个简单的 CNN。在接下来的练习和活动中,我们将通过排列组合以下内容来调整我们的 CNN:

  • 添加更多 CNN 层

  • 添加更多 ANN 层

  • 改变优化器函数

  • 改变激活函数

让我们开始创建我们的第一个 CNN,以便将汽车和花卉图像分类到各自的类别中。

练习 7.01:构建 CNN 并识别汽车和花卉的图像

本次练习中,我们有汽车和花朵的图像,这些图像已经分为训练集和测试集,我们需要构建一个 CNN 模型,用于识别图像是汽车还是花朵。

注意

本章中的所有练习和活动将在 Jupyter notebooks 中进行。请从packt.live/39tID2C下载本书的 GitHub 仓库,以及所有准备好的模板。

在开始之前,确保已从本书的 GitHub 仓库下载了图像数据集到自己的工作目录。您将需要一个training_set文件夹来训练模型,一个test_set文件夹来测试模型。这些文件夹中都将包含一个cars文件夹,里面是汽车图像,以及一个flowers文件夹,里面是花朵图像。

完成此练习的步骤如下:

  1. 导入numpy库和所需的 Keras 库及类:

    # Import the Libraries
    from keras.models import Sequential
    from keras.layers import Conv2D, MaxPool2D, Flatten, Dense
    import numpy as np
    from tensorflow import random
    
  2. 现在,设置种子并使用Sequential类初始化模型:

    # Initiate the classifier
    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    classifier = Sequential()
    
  3. 添加CNN的第一层,将输入形状设置为(64, 64, 3),即每个图像的维度,并设置激活函数为ReLU

    classifier.add(Conv2D(32,3,3, input_shape=(64,64,3), \
                   activation='relu'))
    

    32,3,3表示有323x3大小的特征探测器。

  4. 现在,添加池化层,图像大小为2x2

    classifier.add(MaxPool2D(2,2))
    
  5. 通过添加展平层到CNN模型中,展平池化层的输出:

    classifier.add(Flatten())
    
  6. 添加 ANN 的第一层Dense。这里,128是节点的输出数量。作为一个好的实践,128是一个不错的起点。activationrelu,作为一个好的实践,最好选择 2 的幂次方:

    classifier.add(Dense(128, activation='relu')) 
    
  7. 添加 ANN 的输出层。由于这是一个二分类问题,输出大小为1,激活函数为sigmoid

    classifier.add(Dense(1, activation='sigmoid')) 
    
  8. 使用adam优化器编译网络,并在训练过程中计算准确率:

    #Compile the network
    classifier.compile(optimizer='adam', loss='binary_crossentropy', \
                       metrics=['accuracy'])
    
  9. 创建训练和测试数据生成器。将训练和测试图像按1/255进行重缩放,使所有值都在01之间。仅为训练数据生成器设置以下参数——shear_range=0.2zoom_range=0.2horizontal_flip=True

    from keras.preprocessing.image import ImageDataGenerator
    train_datagen = ImageDataGenerator(rescale = 1./255,\
                                       shear_range = 0.2,\
                                       zoom_range = 0.2,\
                                       horizontal_flip = True)
    test_datagen = ImageDataGenerator(rescale = 1./255)
    
  10. training set文件夹创建训练集。'../dataset/training_set'是我们存放数据的文件夹。我们的 CNN 模型的图像大小为64x64,因此这里也应传入相同的大小。batch_size是每个批次中的图像数量,设为32Class_mode设置为binary,因为我们正在处理二分类问题:

    training_set = train_datagen.flow_from_directory(\
                   '../dataset/training_set',\
                   target_size = (64, 64),\
                   batch_size = 32,\
                   class_mode = 'binary')
    
  11. 对测试集重复第 10 步,同时将文件夹设置为测试图像所在的位置,即'../dataset/test_set'

    test_set = test_datagen.flow_from_directory(\
               '../dataset/test_set',\
               target_size = (64, 64),\
               batch_size = 32,\
               class_mode = 'binary')
    
  12. 最后,拟合数据。将steps_per_epoch设置为10000validation_steps设置为2500。以下步骤可能需要一些时间来执行:

    classifier.fit_generator(training_set,steps_per_epoch = 10000,\
                             epochs = 2,validation_data = test_set,\
                             validation_steps = 2500,shuffle=False)
    

    上述代码将产生以下输出:

    Epoch 1/2
    10000/10000 [==============================] - 1994s 199ms/step - loss: 0.2474 - accuracy: 0.8957 - val_loss: 1.1562 - val_accuracy: 0.8400
    Epoch 2/2
    10000/10000 [==============================] - 1695s 169ms/step - loss: 0.0867 - accuracy: 0.9689 - val_loss: 1.4379 - val_accuracy: 0.8422
    

    验证集上的准确率为84.22%

    注意

    为了获得更准确的结果,尝试将 epochs 的数量增加到大约25。这将增加处理数据所需的时间,总时间取决于机器的配置。

    要访问此特定部分的源代码,请参考packt.live/38njqHU

    你还可以在packt.live/3iqFpSN上在线运行这个示例。

这完成了关于处理图像和识别图像内容的练习。这里需要记住的一件重要事是,这段代码对于任何二分类问题都是健壮的。这意味着即使图像数据发生变化,代码也保持不变。我们将在下一个活动中通过修改模型的一些参数并评估模型的性能来测试我们对此的理解。

活动 7.01:用多层和使用 softmax 修改我们的模型

由于我们已经成功运行了CNN 模型,下一步是尝试提高我们算法的性能。有很多方法可以提高其性能,其中一种最直接的方法是向模型中添加多个 ANN 层,我们将在本活动中学习这个方法。我们还将激活函数从 sigmoid 改为 softmax。通过这样做,我们可以将结果与上一个练习的结果进行比较。请按照以下步骤完成此活动:

  1. 要构建 CNN 导入库,设置种子并创建Sequential类,导入Conv2DMaxPool2DFlattenDenseConv2D用于构建卷积层。由于我们的图片是二维的,所以这里使用了二维卷积。类似地,MaxPool2D用于最大池化,Flatten用于将 CNN 展开,Dense用于向 ANN 添加全连接层。

  2. 使用前面的库开始构建 CNN 架构。在添加第一层之后,向 CNN 中添加两层额外的层。

  3. 向其中添加一个池化和展平层,它将作为 ANN 的输入。

  4. 构建一个全连接的 ANN,其输入将是 CNN 的输出。在添加 ANN 的第一层后,再添加三层。对于 ANN 的输出层,使用 softmax 激活函数。编译模型。

  5. 执行图像增强以处理和转换数据。ImageDataGenerator类用于处理。shear_rangezoom_rangehorizontal_flip都用于图像的转换。

  6. 创建训练集和测试集数据。

  7. 最后,拟合已创建的数据。

在实现这些步骤之后,你应该得到以下预期输出:

Epoch 1/2
10000/10000 [==============================] - 2452s 245ms/step - loss: 8.1783 - accuracy: 0.4667 - val_loss: 11.4999 - val_accuracy: 0.4695
Epoch 2/2
10000/10000 [==============================] - 2496s 250ms/step - loss: 8.1726 - accuracy: 0.4671 - val_loss: 10.5416 - val_accuracy: 0.4691

注意

这个活动的解决方案可以在第 439 页找到。

在此活动中,我们修改了 CNN 模型,尝试提高图像分类器的准确性。我们添加了额外的卷积层和额外的 ANN 全连接层,并更改了输出层的激活函数。这样做后,我们的准确性反而下降了。在下一个练习中,我们将激活函数更改回 sigmoid。我们将通过观察在验证数据集上评估的准确性来评估性能。

练习 7.02:通过恢复为 Sigmoid 激活函数来修改我们的模型

在这个练习中,我们将重建模型,但将激活函数从 softmax 恢复为 sigmoid。通过这样做,我们可以与之前的模型进行准确度比较。按照以下步骤完成此练习:

  1. 导入numpy库和必要的 Keras 库及类:

    # Import the Libraries 
    from keras.models import Sequential
    from keras.layers import Conv2D, MaxPool2D, Flatten, Dense
    import numpy as np
    from tensorflow import random
    
  2. 现在,设置随机种子并使用Sequential类初始化模型:

    # Initiate the classifier
    seed = 43
    np.random.seed(seed)
    random.set_seed(seed)
    classifier = Sequential()
    
  3. 添加 CNN 的第一层,设置输入形状为(64, 64, 3),即每张图像的维度,并将激活函数设置为 ReLU。然后,添加32个大小为(3, 3)的特征检测器。再添加两层具有32(3, 3)大小特征检测器的卷积层,且同样使用 ReLU 激活函数:

    classifier.add(Conv2D(32,3,3,input_shape=(64,64,3),\
                          activation='relu'))
    classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
    classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
    
  4. 现在,添加池化层,图像大小为2x2

    classifier.add(MaxPool2D(2,2))
    
  5. 再添加一个Conv2D层,参数与步骤 3中的相同,再加一个池化层,用于补充步骤 4中使用的相同参数:

    classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
    classifier.add(MaxPool2D(pool_size = (2, 2)))
    
  6. 通过向CNN model中添加一个 flatten 层,扁平化池化层的输出:

    classifier.add(Flatten())
    
  7. 添加 ANN 的第一个Dense层。这里,128是节点的输出数量。作为一个好的实践,128是一个很好的起点。activationrelu。作为一个好的实践,最好选择二的幂次。再添加三个具有相同参数的额外层:

    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    
  8. 添加 ANN 的输出层。这是一个二分类问题,因此输出为1,激活函数为sigmoid

    classifier.add(Dense(1,activation='sigmoid')) 
    
  9. 使用 Adam 优化器编译网络,并在训练过程中计算准确度:

    classifier.compile(optimizer='adam', loss='binary_crossentropy', \
                       metrics=['accuracy'])
    
  10. 创建训练和测试数据生成器。将训练和测试图像按1/255缩放,以使所有值位于01之间。只为训练数据生成器设置以下参数——shear_range=0.2zoom_range=0.2,以及horizontal_flip=True

    from keras.preprocessing.image import ImageDataGenerator
    train_datagen = ImageDataGenerator(rescale = 1./255,
                                       shear_range = 0.2,
                                       zoom_range = 0.2,
                                       horizontal_flip = True)
    test_datagen = ImageDataGenerator(rescale = 1./255)
    
  11. training set文件夹创建训练集。../dataset/training_set是我们存放数据的文件夹。我们的 CNN 模型图像大小为 64x64,因此这里也应该传入相同的大小。batch_size是单个批次中的图像数量,为32class_mode为二进制,因为我们正在处理二分类问题:

    training_set = \
    train_datagen.flow_from_directory('../dataset/training_set',\
                                      target_size = (64, 64),\
                                      batch_size = 32,\
                                      class_mode = 'binary')
    
  12. 对测试集重复步骤 11,通过设置文件夹为测试图像所在位置,即'../dataset/test_set'

    test_set = \
    test_datagen.flow_from_directory('../dataset/test_set',\
                                     target_size = (64, 64),\
                                     batch_size = 32,\
                                     class_mode = 'binary')
    
  13. 最后,拟合数据。将steps_per_epoch设置为10000validation_steps设置为2500。以下步骤可能需要一些时间来执行:

    classifier.fit_generator(training_set,steps_per_epoch = 10000,\
                             epochs = 2,validation_data = test_set,\
                             validation_steps = 2500,shuffle=False)
    

    上述代码产生以下输出:

    Epoch 1/2
    10000/10000 [==============================] - 2241s 224ms/step - loss: 0.2339 - accuracy: 0.9005 - val_loss: 0.8059 - val_accuracy: 0.8737
    Epoch 2/2
    10000/10000 [==============================] - 2394s 239ms/step - loss: 0.0810 - accuracy: 0.9699 - val_loss: 0.6783 - val_accuracy: 0.8675
    

模型的准确率为86.75%,显然高于我们在上一个练习中构建的模型的准确率。这表明激活函数的重要性。仅仅将输出激活函数从 softmax 改为 sigmoid,就将准确率从46.91%提高到了86.75%

注意

要访问此特定部分的源代码,请参阅 packt.live/2ZD9nKM

您也可以在网上运行此示例:packt.live/3dPZiiQ

在下一个练习中,我们将尝试不同的优化器,并观察它如何影响模型的性能。

注意

在二分类问题中(在我们的案例中是汽车与花朵),通常更好的做法是将 sigmoid 作为输出的激活函数。

练习 7.03:将优化器从 Adam 更改为 SGD

在这个练习中,我们将再次修改模型,将优化器更改为SGD。通过这样做,我们可以比较与之前模型的准确度。请按照以下步骤完成本练习:

  1. 导入numpy库以及所需的 Keras 库和类:

    # Import the Libraries 
    from keras.models import Sequential
    from keras.layers import Conv2D, MaxPool2D, Flatten, Dense
    import numpy as np
    from tensorflow import random
    
  2. 现在,使用Sequential类初始化模型:

    # Initiate the classifier
    seed = 42
    np.random.seed(seed)
    random.set_seed(seed)
    classifier = Sequential()
    
  3. 添加CNN的第一层,将输入形状设置为(64, 64, 3),即每个图像的维度,并将激活函数设置为ReLU。然后,添加32个大小为(3, 3)的特征检测器。再添加两个相同特征检测器和相同大小的卷积层:

    classifier.add(Conv2D(32,(3,3),input_shape=(64,64,3),\
                   activation='relu'))
    classifier.add(Conv2D(32,(3,3),activation='relu'))
    classifier.add(Conv2D(32,(3,3),activation='relu'))
    
  4. 现在,添加池化层,图像大小为2x2

    classifier.add(MaxPool2D(pool_size=(2, 2)))
    
  5. 添加另一个Conv2D,参数与步骤 3中的相同,并添加一个池化层来补充它,使用与步骤 4中相同的参数:

    classifier.add(Conv2D(32, (3, 3), input_shape = (64, 64, 3), \
                   activation = 'relu'))
    classifier.add(MaxPool2D(pool_size=(2, 2)))
    
  6. 添加一个Flatten层以完成 CNN 架构:

    classifier.add(Flatten())
    
  7. 添加 ANN 的第一层Dense,大小为128。再向网络添加三个相同参数的Dense层:

    classifier.add(Dense(128,activation='relu')) 
    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    
  8. 添加 ANN 的输出层。这是一个二分类问题,所以输出为1,激活函数为sigmoid

    classifier.add(Dense(1,activation='sigmoid')) 
    
  9. 使用SGD 优化器编译网络,并在训练过程中计算准确度:

    classifier.compile(optimizer='SGD', loss='binary_crossentropy', \
                       metrics=['accuracy'])
    
  10. 创建训练和测试数据生成器。通过1/255对训练和测试图像进行重新缩放,使得所有值都在01之间。仅为训练数据生成器设置以下参数:shear_range=0.2zoom_range=0.2,以及horizontal_flip=True

    from keras.preprocessing.image import ImageDataGenerator
    train_datagen = ImageDataGenerator(rescale = 1./255,\
                                       shear_range = 0.2,\
                                       zoom_range = 0.2,\
                                       horizontal_flip = True)
    test_datagen = ImageDataGenerator(rescale = 1./255)
    
  11. training set文件夹创建一个训练集。../dataset/training_set是我们存放数据的文件夹。我们的 CNN 模型图像大小为64x64,因此这里也应该传递相同的大小。batch_size是每批次的图像数量,即32class_mode是二分类模式,因为我们正在创建一个二分类器:

    training_set = \
    train_datagen.flow_from_directory('../dataset/training_set',\
                                      target_size = (64, 64),\
                                      batch_size = 32,\
                                      class_mode = 'binary')
    
  12. 对测试集重复步骤 11,将文件夹设置为测试图像所在的位置,即'../dataset/test_set'

    test_set = \
    test_datagen.flow_from_directory('../dataset/test_set',\
                                     target_size = (64, 64),\
                                     batch_size = 32,\
                                     class_mode = 'binary')
    
  13. 最后,拟合数据。将steps_per_epoch设置为10000validation_steps设置为2500。以下步骤可能需要一些时间来执行:

    classifier.fit_generator(training_set,steps_per_epoch = 10000,\
                             epochs = 2,validation_data = test_set,\
                             validation_steps = 2500,shuffle=False)
    

    上述代码会产生以下输出:

    Epoch 1/2
    10000/10000 [==============================] - 4376s 438ms/step - loss: 0.3920 - accuracy: 0.8201 - val_loss: 0.3937 - val_accuracy: 0.8531
    Epoch 2/2
    10000/10000 [==============================] - 5146s 515ms/step - loss: 0.2395 - accuracy: 0.8995 - val_loss: 0.4694 - val_accuracy: 0.8454
    

    准确率为84.54%,因为我们使用了多个ANNsSGD作为优化器。

    注意

    要访问此特定部分的源代码,请参阅packt.live/31Hu9vm

    您还可以在packt.live/3gqE9x8上在线运行这个示例。

到目前为止,我们已经尝试了模型的多种不同排列和组合。看起来,针对该数据集,获得最佳准确率的做法是:

  • 添加多个 CNN 层。

  • 添加多个 ANN 层。

  • 使用 sigmoid 激活函数。

  • 使用 Adam 作为优化器。

  • 将 epoch 大小增加到大约25(这需要大量的计算时间——确保您有 GPU 进行处理)。这将提高预测的准确性。

最后,我们将继续预测一个新的未知图像,将其传递给算法,并验证该图像是否被正确分类。在下一个练习中,我们将演示如何使用模型对新图像进行分类。

练习 7.04:分类新图像

在本练习中,我们将尝试对新图像进行分类。该图像尚未接触过算法,因此我们将使用此练习来测试我们的算法。您可以运行本章中的任何算法(尽管推荐使用准确率最高的那个),然后使用模型对图像进行分类。

注意

本练习中使用的图像可以在本书的 GitHub 仓库中找到,地址为packt.live/39tID2C

在我们开始之前,请确保您已经从本书的 GitHub 仓库下载了test_image_1并将其保存到您的工作目录中。这个练习是基于之前的练习,所以请确保您已经在工作区中准备好本章中的算法并可以运行。

完成本练习的步骤如下:

  1. 加载图像。'test_image_1.jpg'是测试图像的路径。请将路径更改为您在系统中保存数据集的位置。查看图像以验证它是什么:

    from keras.preprocessing import image
    new_image = image.load_img('../test_image_1.jpg', \
                               target_size = (64, 64))
    new_image
    
  2. 打印训练集的class_indices属性中的类别标签:

    training_set.class_indices
    
  3. 处理图像:

    new_image = image.img_to_array(new_image)
    new_image = np.expand_dims(new_image, axis = 0)
    
  4. 预测新图像:

    result = classifier.predict(new_image)
    
  5. prediction 方法将输出图像的10。为了将10映射到flower(花)或car(车),可以使用class_indices方法和if…else语句,示例如下:

    if result[0][0] == 1:
        prediction = 'It is a flower'
    else:
        prediction = 'It is a car'
    print(prediction)
    

    上述代码会产生以下输出:

    It is a car
    

    test_image_1 是一张汽车的图像(您可以通过查看图像确认),并且模型正确预测它是汽车。

在这个练习中,我们训练了我们的模型,然后给模型提供了一张汽车的图像。通过这样做,我们发现算法正确地对图像进行了分类。你可以使用相同的过程训练模型来处理任何类型的图像。例如,如果你使用肺部感染和健康肺部的扫描训练模型,那么模型将能够分类新扫描是否代表感染的肺部或健康的肺部。

注意

要访问此特定部分的源代码,请参阅 packt.live/31I6B9F

你也可以在 packt.live/2BzmEMx 上在线运行此示例。

在下一个活动中,我们将把我们的知识付诸实践,使用我们在 Exercise 7.04Classifying a New Image 中训练的模型。

活动 7.02:分类新图像

在这个活动中,你将尝试像我们在前一个练习中所做的那样分类另一张新图像。由于图像没有暴露给算法,因此我们将利用此活动来测试我们的算法。你可以运行本章中的任何一个算法(尽管最高准确率的算法更可取),然后使用模型来对你的图像进行分类。实施此活动的步骤如下:

  1. 运行本章中的任何一个算法。

  2. 从你的目录加载图像(test_image_2)。

  3. 使用算法处理图像。

  4. 预测新图像的主题。你可以查看图像本身以检查预测是否正确。

    注意

    这个活动中使用的图像可以在这本书的 GitHub 仓库中找到,链接地址为 packt.live/39tID2C

在开始之前,请确保已经将 test_image_2 从这本书的 GitHub 仓库下载到你自己的工作目录中。此活动直接跟进前面的练习,因此请确保已经准备好本章中的一个算法以在你的工作空间中运行。

实施这些步骤后,你应该得到以下预期输出:

It is a flower

注意

此活动的解决方案可以在第 442 页找到。

在此活动中,我们根据在验证数据集上的准确性修改了各种参数,包括优化器和输出层中的激活函数,从而训练了本章中最高性能的模型。我们对一个测试图像进行了分类,发现其分类是正确的。

摘要

在本章中,我们研究了为什么需要计算机视觉以及其工作原理。我们了解到为什么计算机视觉是机器学习中最热门的领域之一。然后,我们使用卷积神经网络进行了工作,了解了它们的架构,并探讨了如何在现实应用中构建 CNN。我们还通过增加更多的 ANN 和 CNN 层以及更改激活和优化器函数来尝试改进我们的算法。最后,我们尝试了不同的激活函数和损失函数。

最终,我们成功地通过该算法对新图像(如汽车和花卉)进行了分类。请记住,汽车和花卉的图像可以替换为任何其他图像,例如老虎和鹿,或带有和不带有肿瘤的大脑 MRI 扫描图像。任何二分类的计算机图像问题都可以采用相同的方法来解决。

在下一章,我们将学习一种更高效的计算机视觉技术,它不仅节省时间,而且更容易实现。接下来的章节将教我们如何微调预训练模型,以便应用于我们的实际需求,从而帮助创建更加准确、训练速度更快的模型。将要使用的模型是 VGG-16 和 ResNet50,它们是常用的预训练模型,广泛用于图像分类。

第八章:8. 迁移学习与预训练模型

概述

本章介绍了预训练模型的概念及其在不同应用中的使用,这种应用不同于它们最初训练时的目标,被称为迁移学习。到本章结束时,你将能够将特征提取应用于预训练模型,利用预训练模型进行图像分类,并通过微调预训练模型,将花卉和汽车图像分类到各自的类别中。我们将看到,这样做能够完成与上一章相同的任务,但准确度更高且训练时间更短。

介绍

在上一章中,我们学习了如何使用 Keras 从头创建卷积神经网络CNN)。我们通过添加更多卷积层和全连接层、修改激活函数,来实验不同的架构。我们通过将汽车和花卉的图像分类到各自类别,并比较它们的准确率,来评估每个模型的性能。

然而,在实际项目中,你几乎从不从头开始编码一个卷积神经网络。你总是根据需求对其进行调整和训练。本章将向你介绍迁移学习预训练网络(也称为预训练模型)这两个在行业中使用的重要概念。

我们将使用图像,并且不是从头开始构建 CNN,而是将这些图像与预训练模型匹配,尝试对其进行分类。我们还将调整模型,使其更加灵活。本章将使用的模型是VGG16ResNet50,我们将在本章后续部分讨论它们。在开始使用预训练模型之前,我们需要了解迁移学习。

预训练集与迁移学习

人类通过经验学习。我们将从一个情境中获得的知识应用到未来面对的类似情境中。假设你想学习如何驾驶 SUV,你从未驾驶过 SUV,你所知道的仅仅是如何驾驶一辆小型掀背车。

SUV 的尺寸比掀背车大得多,因此在交通中驾驶 SUV 肯定是一个挑战。不过,一些基本系统(如离合器、油门和刹车)与掀背车相似。所以,学习驾驶 SUV 时,掌握了如何驾驶掀背车的技能肯定会对你大有帮助。在学习驾驶大型 SUV 时,所有你在驾驶掀背车时获得的知识都可以派上用场。

这正是迁移学习的概念。迁移学习在机器学习中是指在学习一个类似的活动时,我们存储并使用在另一个活动中获得的知识。从掀背车到 SUV 的学习模式完全符合这一定义。

假设我们想知道一张照片是狗还是猫;在这种情况下,我们可以有两种方法。其一是从头开始构建一个深度学习模型,然后将新的图片传递给网络。另一种选择是使用一个已经通过猫狗图片构建好的预训练深度学习神经网络模型,而不是从零开始创建神经网络。

使用预训练模型可以节省计算时间和资源。使用预训练网络可能会带来一些意想不到的好处。例如,几乎所有的狗和猫的照片中都会包含一些其他物体,如树木、天空和家具。我们甚至可以使用这个预训练网络来识别树木、天空和家具等物体。

因此,预训练网络是一个保存的网络(在深度学习的情况下是神经网络),它是在一个非常大的数据集上训练的,主要用于图像分类问题。要使用预训练网络,我们需要理解特征提取和微调的概念。

特征提取

要理解特征提取,我们需要回顾卷积神经网络的架构。

你可能还记得,CNN的完整架构在高层次上由以下几个部分组成:

  • 一个卷积层

  • 一个池化和扁平化层

  • 一个人工神经网络ANN

下图展示了完整的 CNN 架构:

图 8.1: CNN 架构

图 8.1: CNN 架构

现在,让我们将这个架构分为两个部分。第一部分包含除ANN之外的所有内容,而第二部分仅包含ANN。下图展示了一个拆分的CNN架构:

图 8.2: CNN 拆分架构 - 卷积基础层和分类器

图 8.2: CNN 拆分架构 - 卷积基础层和分类器

第一部分称为卷积基础层,而第二部分称为分类器

在特征提取中,我们不断重用卷积基础层,而分类器则被替换。所以,我们保留了卷积层的学习成果,并可以将不同的分类器添加到卷积层之上。分类器可以是狗与猫、摩托车与汽车,甚至是医学 X 光图像来分类肿瘤、感染等。下图展示了一些用于不同分类器的卷积基础层:

图 8.3: 可重用的卷积基础层

图 8.3: 可重用的卷积基础层

显而易见的下一个问题是,难道我们不能像基础层那样重用分类器吗?一般来说,答案是否定的。原因在于,从卷积基础部分学习可能更为通用,因此更具可重用性。然而,分类器的学习通常是特定于模型训练时所用的类别。因此,建议仅重用卷积基础层,而不是分类器。

从卷积基础层获得的泛化学习量取决于该层的深度。例如,在猫的情况下,模型的初始层学习一般的特征,如边缘和背景,而更高层可能学习更具体的细节,如眼睛、耳朵或鼻子的形状。因此,如果你的新数据集与原始数据集非常不同——例如,如果你想识别水果而不是猫——那么最好只使用卷积基础层的一些初始层,而不是使用整个层。

CNN),网络顶部的许多全连接层是随机初始化的,并且可能由于反向传播的原因,网络的初始层学习会被完全破坏。

为了避免信息衰减,我们冻结了一些层。这是通过将这些层设为不可训练来实现的。冻结一些层并训练其他层的过程称为微调网络。

微调预训练网络

微调意味着调整我们的神经网络,使其更适应当前的任务。我们可以冻结网络的一些初始层,这样就不会丢失存储在这些层中的信息。这些信息是通用且有用的。然而,如果我们可以在分类器学习的同时冻结这些层,然后再解冻它们,我们可以稍微调整它们,使其更好地适应当前的问题。假设我们有一个预训练网络,可以识别动物。如果我们想要识别特定的动物,比如狗和猫,我们可以稍微调整这些层,让它们学习狗和猫的外观。这就像是使用整个预训练网络,然后在其上添加一个新层,其中包含狗和猫的图像。我们将通过使用一个预构建的网络并在其上添加一个分类器来做类似的活动,分类器将基于狗和猫的图片进行训练。

有一个三步法则来进行微调:

  1. 在预训练系统的顶部添加一个分类器(ANN)。

  2. 冻结 卷积基础 并训练网络。

  3. 联合训练已添加的分类器和未冻结的卷积基础部分。

ImageNet 数据集

在实际工作经验中,你几乎永远不需要自己构建基础卷积模型。你总是会使用预训练模型。但数据从哪里来呢?对于视觉计算,答案是 ImageNet。ImageNet 数据集是一个大型视觉数据库,用于视觉对象识别。它包含超过 1400 万张标注图像及其对象名称。ImageNet 包含超过 20,000 个类别。

Keras 中的一些预训练网络

以下预训练网络可以被视为基础卷积层。你可以使用这些网络并为其拟合一个分类器(ANN):

  • VGG16

  • Inception V3

  • Xception

  • ResNet50

  • MobileNet

不同的供应商创建了前面的预训练网络。例如,ResNet50是由Microsoft创建的,而Inception V3MobileNet是由Google创建的。在本章中,我们将使用VGG16ResNet50模型。

VGG16是一个具有 16 层的卷积神经网络模型,由牛津大学的 K. Simonyan 和 A. Zisserman 提出。该模型于 2014 年提交到ImageNet 大规模视觉识别挑战赛ILSVRC)——这是一个用于测试最新技术的模型的挑战,模型使用了ImageNet数据集。ResNet50是另一种在ImageNet数据集上训练的卷积神经网络,具有 50 层,并在 2015 年的ILSVRC中获得了第一名。

现在我们了解了这些网络的原理,我们将练习利用这些预训练的神经网络,使用VGG16模型来分类一张披萨片的图像。

注意

本章中的所有练习和活动都将在 Jupyter 笔记本中开发。请从packt.live/2uI63CC下载本书的 GitHub 仓库以及所有准备好的模板。

练习 8.01:使用 VGG16 网络识别图像

我们有一张披萨片的图片。我们将使用VGG16网络来处理并识别这张图像。在完成以下步骤之前,请确保从 GitHub 下载了pizza图像并将其保存到你的工作目录中:

  1. 导入库:

    import numpy as np
    from keras.applications.vgg16 import VGG16
    from keras.preprocessing import image
    from keras.applications.vgg16 import preprocess_input
    
  2. 初始化模型(这可能需要一些时间):

    classifier = VGG16()
    

    注意

    预测的最后一层(Dense)有 1000 个值。这意味着VGG16总共有 1000 个标签,我们的图像将是这 1000 个标签中的一个。

  3. 加载图像。'../Data/Prediction/pizza.jpg.jpg'是我们系统中图像的路径;在你的系统中可能会有所不同:

    new_image= image.load_img('../Data/Prediction/pizza.jpg', \
                              target_size=(224, 224))
    new_image
    

    下图显示了前面代码的输出:

    图 8.4:披萨片的图像

    图 8.4:披萨片的图像

    目标大小应该是224x224,因为VGG16仅接受(224,224)大小。

  4. 使用img_to_array函数将图像转换为数组:

    transformed_image = image.img_to_array(new_image)
    transformed_image.shape
    

    前面的代码产生了以下输出:

    (224, 224, 3)
    
  5. 图像必须是四维形式,以便VGG16能够进行进一步处理。扩展图像的维度,如下所示:

    transformed_image = np.expand_dims(transformed_image, axis=0)
    transformed_image.shape
    

    前面的代码产生了以下输出:

    (1, 224, 224, 3)
    
  6. 使用preprocess_input函数对图像进行预处理:

    transformed_image = preprocess_input(transformed_image)
    transformed_image
    

    下图显示了前面代码的输出:

    图 8.5:图像预处理的屏幕截图

    图 8.5:图像预处理的屏幕截图

  7. 创建predictor变量:

    y_pred = classifier.predict(transformed_image)
    y_pred
    
  8. 检查图像的形状。它应该是(1,1000)。1000是因为ImageNet数据库有1000个类别的图像。预测变量显示了我们图像属于这些图像之一的概率:

    y_pred.shape
    

    前面的代码产生了以下输出:

    (1, 1000)
    
  9. 使用decode_predictions函数打印出我们的图像是什么的前五个概率,并传递预测变量y_pred的函数和预测数量及相应的标签输出:

    from keras.applications.vgg16 import decode_predictions
    decode_predictions(y_pred,top=5)
    

    前述代码生成以下输出:

    [[('n07873807', 'pizza', 0.97680503),
      ('n07871810', 'meat_loaf', 0.012848727),
      ('n07880968', 'burrito', 0.0019428912),
      ('n04270147', 'spatula', 0.0019108421),
      ('n03887697', 'paper_towel', 0.0009799759)]]
    

    数组的第一列是内部代码编号。第二列是可能的标签,第三列是图片为该标签的概率。

  10. 将预测结果以人类可读的形式呈现。从decode_predictions函数的输出中打印出最有可能的标签:

    label = decode_predictions(y_pred)
    """
    Most likely result is retrieved, for example, the highest probability
    """
    decoded_label = label[0][0]
    # The classification is printed 
    print('%s (%.2f%%)' % (decoded_label[1], \
          decoded_label[2]*100 ))
    

    前述代码生成以下输出:

    pizza (97.68%)
    

在这个练习中,我们预测了一个图片,显示(以97.68%的概率)这张图片是披萨。显然,更高的准确率意味着我们的图片在 ImageNet 数据库中存在一个相对相似的对象,并且我们的算法成功识别了这张图片。

注意

要访问本节的源代码,请参阅packt.live/3dXqdsQ

您还可以在packt.live/3dZMZAq上在线运行这个示例。

在接下来的活动中,我们将通过使用VGG16网络来对不在 ImageNet 数据库中的图片进行分类,将我们的知识付诸实践。

活动 8.01:使用 VGG16 网络训练深度学习网络以识别图像

你有一张摩托车的图片。使用VGG16网络来预测这张图片。在开始之前,请确保已将图片(test_image_1)下载到你的工作目录中。要完成这个活动,请按照以下步骤操作:

  1. 导入必要的库,包括VGG16网络。

  2. 初始化预训练的VGG16模型。

  3. 加载将要分类的图片。

  4. 通过应用转换来预处理图片。

  5. 创建一个预测变量来预测图像。

  6. 给图片贴上标签并进行分类。

    注意

    这个活动的解决方案可以在第 444 页找到。

通过这些步骤,我们完成了这个活动。与第七章使用卷积神经网络进行计算机视觉不同,我们没有从头开始构建CNN。相反,我们使用了一个预训练的模型。我们刚刚上传了一张需要分类的图片。从中可以看出,以84.33%的准确率预测它是一辆踏板车。在下一个练习中,我们将使用 ImageNet 数据库中没有匹配图片的图片。

练习 8.02:对不在 ImageNet 数据库中的图片进行分类

现在,让我们处理一张不在VGG16网络的1000个标签中的图像。在这个练习中,我们将处理一张竹节虫的图片,而我们的预训练网络中没有竹节虫的标签。让我们看看我们得到什么结果:

  1. 导入numpy库和必要的Keras库:

    import numpy as np
    from keras.applications.vgg16 import VGG16
    from keras.preprocessing import image
    from keras.applications.vgg16 import preprocess_input
    
  2. 初始化模型并打印模型的摘要:

    classifier = VGG16()
    classifier.summary()
    

    classifier.summary()显示了网络的架构。以下是需要注意的点 - 它具有四维输入形状(None, 224, 224, 3),并且具有三个卷积层。以下图显示了输出的最后四层:

    图 8.6:使用 VGG16 分类器对图像的摘要

    图 8.6:使用 VGG16 分类器对图像的摘要

    注意

    预测的最后一层(Dense)有1000个值。这意味着VGG161000个标签,而我们的图像将是这些标签中的一个。

  3. 加载图像。'../Data/Prediction/stick_insect.jpg'是我们系统上的图像路径。在您的系统上可能会有所不同:

    new_image = \
    image.load_img('../Data/Prediction/stick_insect.jpg', \
                   target_size=(224, 224))
    new_image
    

    以下图显示了前面代码的输出:

    图 8.7:用于预测的示例竹节虫图像

    图 8.7:用于预测的示例竹节虫图像

    目标大小应为224x224,因为VGG16仅接受(224,224)。

  4. 使用img_to_array函数将图像转换为数组:

    transformed_image = image.img_to_array(new_image)
    transformed_image.shape
    
  5. 图像必须以四维形式存在,以便VGG16允许进一步处理。使用expand_dims函数沿着第 0 轴扩展图像的维度:

    transformed_image = np.expand_dims(transformed_image, axis=0)
    transformed_image.shape
    
  6. 使用preprocess_input函数预处理图像:

    transformed_image = preprocess_input(transformed_image)
    transformed_image
    

    以下图显示了前面代码的输出:

    图 8.8:显示图像预处理的几个实例

    图 8.8:显示图像预处理的几个实例

  7. 创建predictor变量:

    y_pred = classifier.predict(transformed_image)
    y_pred
    

    以下图显示了前面代码的输出:

    图 8.9:创建预测变量

    图 8.9:创建预测变量

  8. 检查图像的形状。它应该是(1,1000)。这是因为,正如我们之前提到的,ImageNet 数据库有1000个图像类别。预测变量显示了我们的图像属于这些图像之一的概率:

    y_pred.shape
    

    前面的代码产生了以下代码:

    (1, 1000)
    
  9. 选择出VGG16网络具有的1000个标签中,我们图像标签的前五个最高概率:

    from keras.applications.vgg16 import decode_predictions
    decode_predictions(y_pred, top=5)
    

    前面的代码产生了以下代码:

    [[('n02231487', 'walking_stick', 0.30524516),
      ('n01775062', 'wolf_spider', 0.26035702),
      ('n03804744', 'nail', 0.14323168),
      ('n01770081', 'harvestman', 0.066652186),
      ('n01773549', 'barn_spider', 0.03670299)]]
    

    数组的第一列是内部代码编号。第二列是标签,而第三列是图像成为该标签的概率。

  10. 将预测结果以人类可读的格式呈现出来。从decode_predictions函数的输出中打印出最可能的标签:

    label = decode_predictions(y_pred)
    """
    Most likely result is retrieved, for example, the highest probability
    """
    decoded_label = label[0][0]
    # The classification is printed
    print('%s (%.2f%%)' % (decoded_label[1], decoded_label[2]*100 ))
    

    前面的代码产生了以下代码:

    walking_stick (30.52%)
    

    在这里,您可以看到网络预测我们的图像是竹节虫,准确率为30.52%。显然,这张图像不是竹节虫,而是一只竹节虫;在VGG16网络包含的所有标签中,竹节虫是最接近的事物。以下图片展示了一只竹节虫:

    图 8.10:竹节虫

图 8.10:竹节虫

为了避免这种输出,我们可以冻结VGG16现有的层,并添加我们自己的层。我们还可以添加一个包含拐杖和竹节虫图像的层,以便我们能够获得更好的输出。

如果你有大量的拐杖和竹节虫的图像,你可以进行类似的操作来提升模型在分类这些图像时的能力。然后你可以通过重新运行之前的练习来测试它。

注意

要访问此特定部分的源代码,请参见 packt.live/31I7bnR

你也可以通过在线运行这个例子,访问 packt.live/31Hv1QE

为了更详细地理解这一点,我们来看一个不同的例子,在这个例子中,我们冻结网络的最后一层,并添加自己的层,包含汽车和花卉的图像。这将帮助网络提高分类汽车和花卉图像的准确性。

练习 8.03:微调 VGG16 模型

让我们来进行VGG16模型的微调。在这个练习中,我们将冻结网络并移除VGG16的最后一层,该层包含1000个标签。移除最后一层后,我们将建立一个新的花卉-汽车分类器ANN,就像我们在第七章《卷积神经网络与计算机视觉》中做的那样,并将这个ANN连接到VGG16,而不是原始的具有1000个标签的模型。基本上,我们所做的就是用用户自定义的层替换VGG16的最后一层。

在开始之前,请确保你已经从本书的 GitHub 仓库下载了图像数据集到自己的工作目录。你将需要一个training_set文件夹和一个test_set文件夹来测试你的模型。每个文件夹里都会包含一个cars文件夹,里面是汽车图像,还有一个flowers文件夹,里面是花卉图像。

完成此练习的步骤如下:

注意

与原始的新模型(具有1000个标签,涵盖100个不同的物体类别)不同,这个新的微调模型仅包含花卉或汽车的图像。因此,不管你向模型输入什么图像,它都会根据预测概率将其分类为花卉或汽车。

  1. 导入numpy库、TensorFlow 的random库以及所需的Keras库:

    import numpy as np
    import keras
    from keras.layers import Dense
    from tensorflow import random
    
  2. 启动VGG16模型:

    vgg_model = keras.applications.vgg16.VGG16()
    
  3. 检查模型summary

    vgg_model.summary()
    

    下图展示了前述代码的输出:

    图 8.11:启动模型后的模型总结

    图 8.11:启动模型后的模型总结

  4. 从模型总结中移除最后一层,即前述图像中的labeled predictions。创建一个新的 Keras 顺序模型,并遍历 VGG 模型的所有层。将所有层添加到新模型中,除了最后一层:

    last_layer = str(vgg_model.layers[-1])
    np.random.seed(42)
    random.set_seed(42)
    classifier= keras.Sequential()
    for layer in vgg_model.layers:
        if str(layer) != last_layer:
            classifier.add(layer)
    

    在这里,我们创建了一个新的模型分类器名称,而不是vgg_model。所有层,除了最后一层,即vgg_model,都已经包含在分类器中。

  5. 打印新创建模型的summary

    classifier.summary()
    

    以下图显示了上述代码的输出:

    图 8.12:移除最后一层后重新检查摘要

    图 8.12:移除最后一层后重新检查摘要

    最后一层的预测层(Dense)已被删除。

  6. 通过遍历各层并将trainable参数设置为False来冻结这些层:

    for layer in classifier.layers:
        layer.trainable=False
    
  7. 添加一个大小为1的新输出层,使用sigmoid激活函数并打印模型摘要:

    classifier.add(Dense(1, activation='sigmoid'))
    classifier.summary()
    

    以下函数显示了上述代码的输出:

    图 8.13:添加新层后重新检查摘要

    图 8.13:添加新层后重新检查摘要

    现在,最后一层是新创建的用户定义层。

  8. 使用adam优化器和二元交叉熵损失函数来编译网络,并在训练过程中计算accuracy

    classifier.compile(optimizer='adam', loss='binary_crossentropy', \
                       metrics=['accuracy'])
    

    创建一些训练和测试数据生成器,就像我们在第七章使用卷积神经网络进行计算机视觉中所做的那样。将训练和测试图像重新缩放为1/255,确保所有值都在01之间。仅为训练数据生成器设置以下参数:shear_range=0.2zoom_range=0.2horizontal_flip=True

  9. 接下来,从training set文件夹中创建训练集。../Data/dataset/training_set是我们存放数据的文件夹。我们的 CNN 模型的图像大小为224x224,所以这里也应该传入相同的大小。batch_size是每个批次中的图像数量,即32class_mode是二进制的,因为我们正在创建一个二分类器。

    from keras.preprocessing.image import ImageDataGenerator
    generate_train_data = \
    ImageDataGenerator(rescale = 1./255,\
                       shear_range = 0.2,\
                       zoom_range = 0.2,\
                       horizontal_flip = True)
    generate_test_data = ImageDataGenerator(rescale =1./255)
    training_dataset = \
    generate_train_data.flow_from_directory(\
        '../Data/Dataset/training_set',\
        target_size = (224, 224),\
        batch_size = 32,\
        class_mode = 'binary')
    test_datasetset = \
    generate_test_data.flow_from_directory(\
        '../Data/Dataset/test_set',\
        target_size = (224, 224),\
        batch_size = 32,\
        class_mode = 'binary')
    classifier.fit_generator(training_dataset,\
                             steps_per_epoch = 100,\
                             epochs = 10,\
                             validation_data = test_datasetset,\
                             validation_steps = 30,\
                             shuffle=False)
    

    这里有 100 张训练图像,所以设置steps_per_epoch = 100,设置validation_steps=30,并设置shuffle=False

    100/100 [==============================] - 2083s 21s/step - loss: 0.5513 - acc: 0.7112 - val_loss: 0.3352 - val_acc: 0.8539
    
  10. 预测新图像(代码与第七章使用卷积神经网络进行计算机视觉中的相同)。首先,从'../Data/Prediction/test_image_2.jpg'加载图像,并将目标大小设置为(224, 224),因为VGG16模型接受该大小的图像。

    from keras.preprocessing import image
    new_image = \
    image.load_img('../Data/Prediction/test_image_2.jpg', \
                   target_size = (224, 224))
    new_image
    

    此时,你可以通过执行代码new_image来查看图像,通过运行training_dataset.class_indices来查看类标签。

    接下来,先通过img_to_array函数将图像转换为数组,再使用expand_dims函数沿着第 0 轴添加一个新的维度。最后,使用分类器的predict方法进行预测,并以人类可读格式打印输出:

    new_image = image.img_to_array(new_image)
    new_image = np.expand_dims(new_image, axis = 0)
    result = classifier.predict(new_image)
    if result[0][0] == 1:
        prediction = 'It is a flower'
    else:
        prediction = 'It is a car'
    print(prediction)
    

    上述代码生成了以下输出:

    It is a car
    
  11. 最后一步,你可以通过运行classifier.save('car-flower-classifier.h5')来保存分类器。

在这里,我们可以看到算法通过识别汽车图像完成了正确的图像分类。我们仅使用了一个预先构建的VGG16模型,通过调整其层并根据我们的需求进行定制,完成了图像分类。这是一种非常强大的图像分类技术。

注意

若要访问此特定部分的源代码,请参考packt.live/2ZxCqzA

本节目前没有在线交互示例,需要在本地运行。

在下一个练习中,我们将使用另一个预训练模型 ResNet50,并展示如何使用该模型进行图像分类。

练习 8.04:使用 ResNet 进行图像分类

最后,在结束本章之前,让我们来做一个关于 ResNet50 网络的练习。我们将使用一张纳斯卡赛车手的图像并尝试通过网络预测。按照以下步骤完成该练习:

  1. 导入必要的库:

    import numpy as np
    from keras.applications.resnet50 import ResNet50, preprocess_input
    from keras.preprocessing import image 
    
  2. 启动 ResNet50 模型并打印该模型的 summary

    classifier = ResNet50()
    classifier.summary()
    

    以下图表显示了前面代码的输出:

    图 8.14:模型的总结

    图 8.14:模型的总结

  3. 加载图像。'../Data/Prediction/test_image_3.jpg' 是我们系统中图像的路径。您系统中的路径会有所不同:

    new_image = \
    image.load_img('../Data/Prediction/test_image_3.jpg', \
                   target_size=(224, 224))
    new_image
    

    以下图表显示了前面代码的输出:

    图 8.15:用于预测的纳斯卡赛车手图像示例

    图 8.15:用于预测的纳斯卡赛车手图像示例

    请注意,目标大小应为 224x224,因为 ResNet50 仅接受 (224,224) 的输入。

  4. 使用 img_to_array 函数将图像转换为数组:

    transformed_image = image.img_to_array(new_image)
    transformed_image.shape
    
  5. 为了让 ResNet50 进行进一步处理,图像必须是四维形式。使用 expand_dims 函数沿着第 0 轴扩展维度:

    transformed_image = np.expand_dims(transformed_image, axis=0)
    transformed_image.shape
    
  6. 使用 preprocess_input 函数预处理图像:

    transformed_image = preprocess_input(transformed_image)
    transformed_image
    
  7. 使用分类器的 predict 方法创建预测变量,通过该方法预测图像:

    y_pred = classifier.predict(transformed_image)
    y_pred
    
  8. 检查图像的形状。它应该是 (1,1000):

    y_pred.shape
    

    前面的代码会产生以下输出:

    (1, 1000)
    
  9. 使用 decode_predictions 函数选择图像的前五个概率,并通过传递预测变量 y_pred 作为参数,得到前几个预测和相应的标签:

    from keras.applications.resnet50 import decode_predictions
    decode_predictions(y_pred, top=5)
    

    前面的代码会产生以下输出:

    [[('n04037443', 'racer', 0.8013074),
      ('n04285008', 'sports_car', 0.06431753),
      ('n02974003', 'car_wheel', 0.024077434),
      ('n02504013', 'Indian_elephant', 0.019822922),
      ('n04461696', 'tow_truck', 0.007778575)]]
    

    数组的第一列是内部代码编号,第二列是标签,第三列是图像为该标签的概率。

  10. 将预测结果以人类可读的格式输出。从 decode_predictions 函数的结果中打印最可能的标签:

    label = decode_predictions(y_pred)
    """
    Most likely result is retrieved, for example, the highest probability
    """
    decoded_label = label[0][0]
    # The classification is printed
    print('%s (%.2f%%)' % (decoded_label[1], \
          decoded_label[2]*100 ))
    

    前面的代码会产生以下输出:

    racer (80.13%)
    

在这里,模型明确显示(概率为80.13%)图片是赛车手的照片。这就是预训练模型的强大之处,Keras 使我们可以灵活地使用和调整这些模型。

注意

若要访问此特定部分的源代码,请参考packt.live/2BzvTMK

您也可以在packt.live/3eWelJh 上在线运行此示例。

在下一个活动中,我们将使用预训练的 ResNet50 模型分类另一张图像。

活动 8.02:使用 ResNet 进行图像分类

现在,让我们进行一个使用另一个预训练网络(ResNet)的活动。我们有一张电视图像,位于../Data/Prediction/test_image_4。我们将使用ResNet50网络来预测这张图像。要实现该活动,请按以下步骤操作:

  1. 导入所需的库。

  2. 启动ResNet模型。

  3. 加载需要分类的图像。

  4. 通过应用适当的转换来预处理图像。

  5. 创建一个预测变量来预测图像。

  6. 给图像贴上标签并进行分类。

    注意

    本活动的解决方案可以在第 448 页找到。

因此,网络以接近100%的准确率判断这张图像是电视的图像。这次,我们使用了ResNet50预训练模型来对电视图像进行分类,获得了与使用VGG16模型预测披萨切片图像时相似的结果。

总结

在本章中,我们讲解了迁移学习的概念以及它与预训练网络的关系。我们通过使用预训练的深度学习网络VGG16ResNet50来预测各种图像,应用了这些知识。我们练习了如何利用预训练网络,通过特征提取和微调等技术,加速模型训练并提高准确性。最后,我们学习了通过调整现有模型并使其根据我们的数据集工作这一强大技术。构建自己的ANN基于现有的CNN,是业界最强大的技术之一。

在下一章,我们将通过研究一些 Google Assistant 的真实案例来学习顺序建模和顺序记忆。此外,我们还将学习顺序建模与循环神经网络RNN)的关系。我们将详细了解消失梯度问题,并学习如何使用LSTM比简单的RNN更好地克服消失梯度问题。我们将把所学应用于时间序列问题,通过预测股票趋势,得出相当准确的结果。

第九章:9. 使用循环神经网络进行序列建模

概述

本章将介绍序列建模——创建模型来预测序列中的下一个值或一系列值。在本章结束时,你将能够构建序列模型,解释RNNLSTM架构,并预测 Alphabet 和 Amazon 未来股价的值。

介绍

在上一章,我们学习了预训练网络以及如何通过迁移学习将它们应用于我们自己的应用。我们实验了VGG16ResNet50,这两种用于图像分类的预训练网络,并利用它们对新图像进行分类,并为我们的应用进行微调。通过利用预训练网络,我们能够比在之前的章节中训练的卷积神经网络更快地训练出更准确的模型。

在传统神经网络(以及前几章中介绍的所有神经网络架构)中,数据从输入层开始,顺序通过网络,经过隐藏层(如果有的话),直到输出层。信息只通过一次网络,输出被认为是相互独立的,只依赖于模型的输入。然而,在某些情况下,特定的输出依赖于系统之前的输出。

以公司的股价为例:某一天结束时的输出与前一天的输出相关。类似地,在自然语言处理NLP)中,为了使句子语法正确,句子中的最后几个词依赖于前面出现的词。如果要使句子合乎语法,最终的词语往往与前面的词语高度相关。NLP 是序列建模的一个特殊应用,在该应用中,处理和分析的数据集是自然语言数据。为了解决这些类型的问题,使用了一种特殊类型的神经网络——循环神经网络RNN)。RNN 能够记住之前的输出,并用于处理这类问题。

本章介绍并探讨了 RNN 的概念和应用。还解释了 RNN 如何与标准的前馈神经网络不同。你还将理解梯度消失问题以及长短期记忆LSTM)网络。本章还将介绍序列数据及其处理方式。我们将通过使用股市数据进行股价预测来学习这些概念。

序列记忆与序列建模

如果我们分析Alphabet过去 6 个月的股价,如下图所示,我们可以看到一个趋势。为了预测或预报未来的股价,我们需要理解这个趋势,然后在进行数学计算时牢记这一趋势:

图 9.1:Alphabet 过去 6 个月的股价

图 9.1:Alphabet 过去 6 个月的股价

这一趋势与顺序记忆和顺序建模密切相关。如果你有一个可以记住之前输出并基于这些输出预测下一个输出的模型,我们说这个模型具有顺序记忆。

处理这种顺序记忆的建模过程被称为 顺序建模。这不仅适用于股市数据,也适用于自然语言处理(NLP)应用;我们将在下一节研究 RNNs 时看到一个这样的例子。

循环神经网络(RNNs)

RNNs 是一种基于顺序记忆概念构建的神经网络。与传统神经网络不同,RNN 预测顺序数据中的结果。目前,RNN 是处理顺序数据的最强大技术。

如果你有一部能使用 Google Assistant 的智能手机,试着打开它并问:“联合国是什么时候成立的?” 答案将在以下截图中显示:

图 9.2:Google Assistant 的输出

图 9.2:Google Assistant 的输出

现在,问第二个问题:“为什么它被成立?”,如下:

图 9.3:Google Assistant 的上下文输出

图 9.3:Google Assistant 的上下文输出

现在,问第三个问题:“它的总部在哪里?”,你应该得到如下答案:

图 9.4:Google Assistant 的输出

图 9.4:Google Assistant 的输出

这里有一个有趣的事情值得注意,我们在第一个问题中只提到了“联合国”。在第二和第三个问题中,我们分别问了助手 为什么它被成立总部在哪里。Google Assistant 理解到,由于前一个问题是关于联合国的,接下来的问题也与联合国相关。这对于机器来说并不是一件简单的事情。

机器能够显示预期结果,因为它已经以序列的形式处理了数据。机器明白当前的问题与前一个问题有关,因此本质上,它记住了之前的问题。

让我们考虑另一个简单的例子。假设我们想要预测以下序列中的下一个数字:789?。我们希望下一个输出是 9 + 1。另外,如果我们提供序列 369?,我们希望得到 9 + 3 作为输出。虽然在这两种情况下,最后一个数字都是 9,但是预测结果应该是不同的(也就是说,当我们考虑到前一个值的上下文信息,而不仅仅是最后一个值时)。这里的关键是记住从前一个值中获取的上下文信息。

在高层次上,这些能够记住先前状态的网络被称为递归网络。为了完全理解RNN,我们先回顾一下传统的神经网络,也就是前馈神经网络。这是一种神经网络,其中神经网络的连接不形成循环;也就是说,数据仅沿一个方向流动,如下图所示:

图 9.5:一个前馈神经网络

图 9.5:一个前馈神经网络

在一个前馈神经网络中,如上图所示,输入层(左侧的绿色圆圈)接收数据并将其传递给隐藏层(带权重的蓝色圆圈)。然后,隐藏层的数据传递给输出层(右侧的红色圆圈)。根据阈值,数据会进行反向传播,但在隐藏层中没有数据的循环流动。

RNN中,网络的隐藏层允许数据和信息的循环。如下图所示,结构类似于前馈神经网络;然而,在这里,数据和信息也在循环流动:

图 9.6:一个 RNN

图 9.6:一个 RNN

在这里,RNN的定义特性是隐藏层不仅给出输出,而且还将输出的信息反馈到自身。在深入了解 RNN 之前,我们先讨论一下为什么我们需要RNN,以及为什么卷积神经网络CNN)或普通的人工神经网络ANN)在处理序列数据时存在不足。假设我们正在使用CNN来识别图像;首先,我们输入一张狗的图片,CNN会将该图像标记为“狗”。然后,我们输入一张芒果的图片,CNN会将该图像标记为“芒果”。我们假设在时间t输入狗的图片,如下所示:

图 9.7:一个使用 CNN 的狗的图像

图 9.7:一个使用 CNN 的狗的图像

现在,我们输入时间t + 1的芒果图像,如下所示:

图 9.8:一个使用 CNN 的芒果图像

图 9.8:一个使用 CNN 的芒果图像

在这里,你可以清楚地看到,时间t的狗图像输出和时间t + 1的芒果图像输出是完全独立的。因此,我们不需要我们的算法记住先前的输出实例。然而,正如我们在 Google 助手的例子中提到的,当我们问“联合国何时成立”和“为什么成立”时,算法必须记住先前实例的输出,才能处理序列数据。CNNANN无法做到这一点,因此我们需要使用RNN

RNN中,我们可以在多个时间点上得到多个输出。下面的图示表示一个RNN的示意图。它展示了网络从时间t – 1到时间t + n的状态:

图 9.9:在不同时间戳下展开的 RNN

图 9.9:在不同时间戳下展开的 RNN

在训练RNN时,你可能会遇到一些与RNN独特架构相关的问题。这些问题涉及梯度的值,因为随着RNN深度的增加,梯度可能会消失或爆炸,正如我们将在下一节中学习的那样。

消失梯度问题

如果有人问你:“你昨晚吃了什么?”你可能很容易记住并正确回答他们。现在,如果有人问你:“过去 30 天你吃了什么?”你可能能记住过去 3 到 4 天的菜单,但之前的菜单就很难记得清楚了。能够回忆过去的信息是消失梯度问题的基础,我们将在本节中研究这个问题。简而言之,消失梯度问题指的是在一段时间内丢失或衰减的信息。

下面的图示表示RNN在不同时间点t的状态。顶部的点(红色)表示输出层,中间的点(蓝色)表示隐藏层,底部的点(绿色)表示输入层:

图 9.10:信息随时间衰减

图 9.10:信息随时间衰减

如果你处在t + 10时刻,你很难记得 10 天前(即时间t)的晚餐菜单。此外,如果你处在t + 100时刻,你很可能根本记不起 100 天前的晚餐菜单,假设你做的晚餐没有规律可循。在机器学习的上下文中,消失梯度问题是在使用基于梯度的学习方法和反向传播训练 ANN 时遇到的一个困难。让我们回顾一下神经网络是如何工作的,如下所示:

  1. 首先,我们用随机的权重和偏置值初始化网络。

  2. 我们得到一个预测输出;这个输出与实际输出进行比较,二者的差异被称为代价。

  3. 训练过程利用梯度,它衡量的是代价相对于权重或偏置变化的速率。

  4. 然后,我们通过在训练过程中反复调整权重和偏置,尽可能降低代价,直到获得最低的可能值。

例如,如果你把一个球放在陡坡上,球会迅速滚动;然而,如果你把球放在平坦的表面上,它会滚得很慢,甚至不滚动。类似地,在深度神经网络中,当梯度较大时,模型学习得很快。然而,如果梯度较小,模型的学习速度就会变得非常慢。记住,在任何时刻,梯度是到该点为止所有梯度的乘积(即遵循微积分链式法则)。

此外,梯度通常是一个介于 01 之间的小数,而两个介于 01 之间的数字相乘会得到一个更小的数字。你的网络越深,网络初始层的梯度就越小。在某些情况下,它会变得非常小,以至于网络根本无法进行训练;这就是梯度消失问题。下图展示了遵循微积分链式法则的梯度:

图 9.11:带有成本 C 和微积分链式法则的梯度消失

图 9.11:带有成本 C 和微积分链式法则的梯度消失

参见图 9.10,假设我们处于 t + 10 时刻,我们得到一个输出,这个输出将反向传播到 t,即 10 步之遥。现在,当权重被更新时,会有 10 个梯度(它们本身就非常小),当它们相乘时,结果变得非常小,以至于几乎可以忽略不计。这就是梯度消失问题。

梯度爆炸问题的简要解释

如果权重不是很小,而是大于 1,那么随后的乘法将使梯度指数级增长;这就是梯度爆炸问题。梯度爆炸实际上是梯度消失的对立面,梯度消失时值变得太小,而梯度爆炸时值变得非常大。结果,网络会受到严重影响,无法做出任何预测。虽然梯度爆炸问题比梯度消失问题少见,但了解梯度爆炸是什么还是有必要的。

有一些方法可以帮助我们克服在面临梯度消失或梯度爆炸问题时遇到的挑战。我们将在这里学习的一种方法是长短时记忆(LSTM),它通过记住长期的信息来克服梯度问题。

长短时记忆(LSTM)

LSTMRNN 的一种,主要目标是克服梯度消失和梯度爆炸问题的缺点。它的架构设计使得它能够长时间记住数据和信息。

LSTM 旨在克服消失梯度和爆炸梯度问题的限制。LSTM 网络是一种特殊的 RNN,能够学习长期依赖关系。它们被设计来避免长期依赖问题;能够记住长时间间隔的信息就是它们的设计方式。下图展示了一个标准的递归网络,其中重复模块具有 tanh 激活 函数。这是一个简单的 RNN。在这种架构中,我们通常需要面对消失梯度问题:

图 9.12:一个简单的 RNN 模型

图 9.12:一个简单的 RNN 模型

LSTM 架构与简单的 RNN 相似,但它们的重复模块包含不同的组件,如下图所示:

图 9.13:LSTM 模型架构

图 9.13:LSTM 模型架构

除了简单的 RNNLSTM 还包括以下内容:

  • Sigmoid 激活 函数(σ

  • 数学计算功能(带有 + 和 x 的黑色圆圈)

  • 有门控单元(或称为门):

图 9.14:LSTM 详细解析

图 9.14:LSTM 详细解析

简单的 RNNLSTM 之间的主要区别是是否存在门控单元。你可以将门视为计算机内存,在内存中可以进行信息的写入、读取或存储。前面的图展示了 LSTM 的详细图像。门中的单元(由黑色圆圈表示)决定了存储什么信息以及何时允许读取或写入值。这些门接受从 01 的任何信息;也就是说,如果是 0,那么信息会被阻止;如果是 1,则所有信息都会流通。如果输入值介于 01 之间,则仅部分信息会流通。

除了这些输入门,网络的梯度还依赖于两个因素:权重和激活函数。门决定了哪些信息需要在 LSTM 单元中保持,哪些需要被遗忘或删除。这样,门就像水阀一样;也就是说,网络可以选择哪个阀门允许水流通,哪个阀门不允许水流通。

这些阀门的调整方式使得输出值永远不会导致梯度(消失或爆炸)问题。例如,如果值变得过大,那么会有一个遗忘门将忘记该值,并不再将其考虑用于计算。遗忘门的作用本质上是将信息乘以 01。如果信息需要进一步处理,遗忘门将信息乘以 1,如果需要忘记,则将信息乘以 0。每个门都由一个 sigmoid 函数辅助,该函数将信息压缩到 01 之间。为了更好地理解这一点,我们来看一些活动和练习。

注意

本章中的所有活动和练习将在 Jupyter notebook 中进行。您可以在packt.live/2vtdA8o下载本书的 GitHub 仓库,并获取所有准备好的模板。

练习 9.01:使用 50 个单元(神经元)的 LSTM 预测 Alphabet 股票价格的趋势

在本练习中,我们将研究 Alphabet 股票在 5 年期间的价格——即从 2014 年 1 月 1 日到 2018 年 12 月 31 日。在此过程中,我们将尝试使用RNNs预测并预测公司 2019 年 1 月的未来趋势。我们拥有 2019 年 1 月的实际值,因此稍后我们将能够将我们的预测与实际值进行比较。按照以下步骤完成本练习:

  1. 导入所需的库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    from tensorflow import random
    
  2. 使用 pandas 的 read_csv 函数导入数据集,并使用 head 方法查看数据集的前五行:

    dataset_training = pd.read_csv('../GOOG_train.csv')
    dataset_training.head()
    

    下图展示了前述代码的输出:

    图 9.15:GOOG_Training 数据集的前五行

    图 9.15:GOOG_Training 数据集的前五行

  3. 我们将使用 Open 股票价格进行预测;因此,从数据集中选择 Open 股票价格列并打印其值:

    training_data = dataset_training[['Open']].values
    training_data
    

    前述代码生成了以下输出:

    array([[ 555.647278],
           [ 555.418152],
           [ 554.42688 ],
           ...,
           [1017.150024],
           [1049.619995],
           [1050.959961]])
    
  4. 然后,通过使用 MinMaxScaler 进行数据标准化来执行特征缩放,并设置特征范围,使它们的最小值为 0,最大值为 1。在训练数据上使用缩放器的 fit_transform 方法:

    from sklearn.preprocessing import MinMaxScaler
    sc = MinMaxScaler(feature_range = (0, 1))
    training_data_scaled = sc.fit_transform(training_data)
    training_data_scaled
    

    前述代码生成了以下输出:

    array([[0.08017394],
           [0.07987932],
           [0.07860471],
           ...,
           [0.67359064],
           [0.71534169],
           [0.71706467]])
    
  5. 创建数据以获取当前实例的 60 个时间戳。我们在这里选择了60,因为这样可以提供足够数量的前实例,帮助我们理解趋势;从技术上讲,这个值可以是任何数字,但60是最优值。此外,这里的上限值是1258,即训练集中的行数(或记录数)的索引或计数:

    X_train = []
    y_train = []
    for i in range(60, 1258):
        X_train.append(training_data_scaled[i-60:i, 0])
        y_train.append(training_data_scaled[i, 0])
    X_train, y_train = np.array(X_train), \
                       np.array(y_train)
    
  6. 接下来,使用 NumPy 的 reshape 函数调整数据形状,为 X_train 的末尾添加一个额外的维度:

    X_train = np.reshape(X_train, (X_train.shape[0], \
                                   X_train.shape[1], 1))
    X_train
    

    下图展示了前述代码的输出:

    图 9.16:当前实例中几个时间戳的数据

    图 9.16:当前实例中几个时间戳的数据

  7. 导入以下 Keras 库以构建 RNN

    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Dropout
    
  8. 设置随机种子并初始化顺序模型,如下所示:

    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  9. 向网络中添加一个包含 50 个单元的 LSTM 层,设置 return_sequences 参数为 True,并将 input_shape 参数设置为 (X_train.shape[1], 1)。再添加三个包含 50 个单元的 LSTM 层,并为前两个设置 return_sequences 参数为 True,如下所示:

    model.add(LSTM(units = 50, return_sequences = True, \
                   input_shape = (X_train.shape[1], 1)))
    # Adding a second LSTM layer
    model.add(LSTM(units = 50, return_sequences = True))
    # Adding a third LSTM layer
    model.add(LSTM(units = 50, return_sequences = True))
    # Adding a fourth LSTM layer
    model.add(LSTM(units = 50))
    # Adding the output layer
    model.add(Dense(units = 1))
    
  10. 使用 adam 优化器编译网络,并使用 均方误差(Mean Squared Error)作为损失函数。将模型拟合到训练数据上,训练 100 个周期,每批次大小为 32

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')
    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 100, batch_size = 32)
    
  11. 加载并处理test数据(这里将其视为实际数据),并选择表示Open股票数据的列:

    dataset_testing = pd.read_csv("../GOOG_test.csv")
    actual_stock_price = dataset_testing[['Open']].values
    actual_stock_price
    

    以下图展示了前面代码的输出:

    图 9.17:实际处理过的数据

    图 9.17:实际处理过的数据

  12. 拼接数据;我们需要60个前期实例来获得每一天的股票价格。因此,我们将需要训练和测试数据:

    total_data = pd.concat((dataset_training['Open'], \
                            dataset_testing['Open']), axis = 0)
    
  13. 重新调整和缩放输入,以准备测试数据。请注意,我们预测的是 1 月的月度趋势,它有21个交易日,因此为了准备测试集,我们将下限值设为 60,上限值设为 81。这确保了21天的差异得以保持:

    inputs = total_data[len(total_data) \
             - len(dataset_testing) - 60:].values
    inputs = inputs.reshape(-1,1)
    inputs = sc.transform(inputs)
    X_test = []
    for i in range(60, 81):
        X_test.append(inputs[i-60:i, 0])
    X_test = np.array(X_test)
    X_test = np.reshape(X_test, (X_test.shape[0], \
                        X_test.shape[1], 1))
    predicted_stock_price = model.predict(X_test)
    predicted_stock_price = sc.inverse_transform(\
                            predicted_stock_price)
    
  14. 通过绘制实际股票价格和预测股票价格来可视化结果:

    # Visualizing the results
    plt.plot(actual_stock_price, color = 'green', \
             label = 'Real Alphabet Stock Price',\
             ls='--')
    plt.plot(predicted_stock_price, color = 'red', \
             label = 'Predicted Alphabet Stock Price',\
             ls='-')
    plt.title('Predicted Stock Price')
    plt.xlabel('Time in days')
    plt.ylabel('Real Stock Price')
    plt.legend()
    plt.show()
    

    请注意,您的结果可能与实际的 Alphabet 股票价格略有不同。

    期望的输出

    图 9.18:实际股票价格与预测股票价格的对比

图 9.18:实际股票价格与预测股票价格的对比

这就是练习 9.01使用具有 50 个单元(神经元)的 LSTM 预测 Alphabet 股票价格的趋势的总结,在此我们通过LSTM预测了 Alphabet 的股票趋势。如前述图所示,趋势已被较好地捕捉。

注意

若要访问该特定部分的源代码,请参考packt.live/2ZwdAzW

您还可以在packt.live/2YV3PvX在线运行此示例。

在下一个活动中,我们将测试我们的知识并练习通过预测亚马逊过去 5 年股票价格的趋势来构建RNNsLSTM层。

活动 9.01:使用具有 50 个单元(神经元)的 LSTM 预测亚马逊股票价格的趋势

在本活动中,我们将考察亚马逊过去 5 年的股票价格——即从 2014 年 1 月 1 日到 2018 年 12 月 31 日。通过这种方式,我们将尝试使用 RNN 和 LSTM 预测并预测该公司 2019 年 1 月的未来趋势。我们已经有了 2019 年 1 月的实际值,因此稍后可以将我们的预测与实际值进行比较。按照以下步骤完成此活动:

  1. 导入所需的库。

  2. 从完整的数据集中提取Open列,因为预测将基于开盘股票值。可以从本书的 GitHub 库下载数据集。数据集可以在packt.live/2vtdA8o找到。

  3. 将数据规范化到 0 和 1 之间。

  4. 然后,创建时间戳。2019 年 1 月每一天的股票价格将由前 60 天的值来预测;因此,如果 1 月 1 日的预测是使用第n天到 12 月 31 日的值,那么 1 月 2 日将使用第n + 1 天和 1 月 1 日的值来预测,以此类推。

  5. 将数据重新调整为三维,因为网络需要三维数据。

  6. Keras中构建一个包含50个单元的RNN模型(这里,单元指的是神经元),并使用四个LSTM层。第一步应该提供输入形状。请注意,最后一个LSTM层总是会添加return_sequences=True,因此不必显式定义。

  7. 处理并准备测试数据,即 2019 年 1 月的实际数据。

  8. 合并并处理训练数据和测试数据。

  9. 可视化结果。

在实现这些步骤后,您应该看到以下预期输出:

图 9.19:实际股票价格与预测股票价格的对比

图 9.19:实际股票价格与预测股票价格的对比

注意

该活动的解决方案可以在第 452 页找到。

现在,让我们尝试通过调整LSTM来提升性能。关于如何构建LSTM没有绝对标准;然而,以下的排列组合可以尝试,以改进性能:

  • 构建一个具有适中单元数的LSTM,例如50

  • 构建一个包含超过100个单元的LSTM模型

  • 使用更多数据;也就是说,除了5年的数据外,获取10年的数据

  • 使用100个单元应用正则化

  • 使用50个单元应用正则化

  • 使用更多数据和50个单元应用正则化

这个列表可以有多种组合;任何能够提供最佳结果的组合都可以视为该数据集的好算法。在下一个练习中,我们将通过增加LSTM层的单元数来探索其中的一个选项,并观察性能表现。

练习 9.02:使用 100 个单元的 LSTM 预测字母表公司股票价格的趋势

在本练习中,我们将研究字母表公司在过去 5 年的股票价格,从 2014 年 1 月 1 日到 2018 年 12 月 31 日。在此过程中,我们将尝试使用 RNN 预测和预测该公司在 2019 年 1 月的未来趋势。我们有 2019 年 1 月的实际数据,因此稍后我们将把预测值与实际值进行比较。这与第一个练习相同,但这次我们使用了 100 个单元。确保与练习 9.01使用 50 个单元(神经元)的 LSTM 预测字母表公司股票价格的趋势进行比较。按照以下步骤完成此练习:

  1. 导入所需的库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    from tensorflow import random
    
  2. 使用 pandas 的read_csv函数导入数据集,并使用head方法查看数据集的前五行:

    dataset_training = pd.read_csv('../GOOG_train.csv')
    dataset_training.head()
    
  3. 我们将使用Open股票价格进行预测;因此,从数据集中选择Open股票价格列并打印值:

    training_data = dataset_training[['Open']].values
    training_data
    
  4. 然后,通过使用MinMaxScaler对数据进行标准化,执行特征缩放,并设置特征的范围,使其最小值为零,最大值为一。对训练数据使用标准化器的fit_transform方法:

    from sklearn.preprocessing import MinMaxScaler
    sc = MinMaxScaler(feature_range = (0, 1))
    training_data_scaled = sc.fit_transform(training_data)
    training_data_scaled
    
  5. 创建数据以从当前实例中获取60个时间戳。我们在这里选择60,因为它将提供足够多的前期实例以便理解趋势;从技术角度讲,这可以是任何数字,但60是最佳值。此外,这里的上限值为1258,它表示training集中的行数或记录数:

    X_train = []
    y_train = []
    for i in range(60, 1258):
        X_train.append(training_data_scaled[i-60:i, 0])
        y_train.append(training_data_scaled[i, 0])
    X_train, y_train = np.array(X_train), np.array(y_train)
    
  6. 使用 NumPy 的reshape函数调整数据的形状,在X_train的末尾添加一个额外的维度:

    X_train = np.reshape(X_train, (X_train.shape[0], \
                                   X_train.shape[1], 1))
    
  7. 导入以下Keras库以构建RNN

    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Dropout
    
  8. 设置种子并初始化序列模型,如下所示:

    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  9. 向网络中添加一个具有50个单元的LSTM层,将return_sequences参数设置为True,并将input_shape参数设置为(X_train.shape[1], 1)。再添加三个具有50个单元的LSTM层,并将前两个LSTM层的return_sequences参数设置为True。最后添加一个大小为1的输出层:

    model.add(LSTM(units = 100, return_sequences = True, \
                   input_shape = (X_train.shape[1], 1)))
    # Adding a second LSTM
    model.add(LSTM(units = 100, return_sequences = True))
    # Adding a third LSTM layer
    model.add(LSTM(units = 100, return_sequences = True))
    # Adding a fourth LSTM layer
    model.add(LSTM(units = 100))
    # Adding the output layer
    model.add(Dense(units = 1))
    
  10. 使用adam优化器编译网络,并使用均方误差作为损失函数。用32的批量大小在100个 epochs 上拟合训练数据:

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')
    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 100, batch_size = 32)
    
  11. 加载并处理测试数据(在此视为实际数据),并选择代表Open股价数据的列:

    dataset_testing = pd.read_csv("../GOOG_test.csv")
    actual_stock_price = dataset_testing[['Open']].values
    actual_stock_price
    
  12. 将数据连接起来,因为我们需要60个前期实例来预测每一天的股价。因此,我们将需要训练数据和测试数据:

    total_data = pd.concat((dataset_training['Open'], \
                            dataset_testing['Open']), axis = 0)
    
  13. 调整和缩放输入以准备测试数据。请注意,我们正在预测 1 月的月度趋势,1 月有21个交易日,因此为了准备测试集,我们将下限值设为60,上限值设为81。这样可以确保21的差值保持不变:

    inputs = total_data[len(total_data) \
                        - len(dataset_testing) - 60:].values
    inputs = inputs.reshape(-1,1)
    inputs = sc.transform(inputs)
    X_test = []
    for i in range(60, 81):
        X_test.append(inputs[i-60:i, 0])
    X_test = np.array(X_test)
    X_test = np.reshape(X_test, (X_test.shape[0], \
                        X_test.shape[1], 1))
    predicted_stock_price = model.predict(X_test)
    predicted_stock_price = sc.inverse_transform(\
                            predicted_stock_price)
    
  14. 通过绘制实际股价和预测股价来可视化结果:

    # Visualizing the results
    plt.plot(actual_stock_price, color = 'green', \
             label = 'Real Alphabet Stock Price',ls='--')
    plt.plot(predicted_stock_price, color = 'red', \
             label = 'Predicted Alphabet Stock Price',ls='-')
    plt.title('Predicted Stock Price')
    plt.xlabel('Time in days')
    plt.ylabel('Real Stock Price')
    plt.legend()
    plt.show()
    

    预期输出

    图 9.20:实际与预测的股价

图 9.20:实际与预测的股价

注意

要访问此特定部分的源代码,请参见 packt.live/2ZDggf4

你也可以在网上运行这个例子,访问 packt.live/2O4ZoJ7

现在,如果我们将练习 9.01中的LSTM,即使用 50 个单元(神经元)的 LSTM 预测字母股价的趋势,它有50个神经元(单元),与这个使用100个单元的LSTM进行比较,我们可以看到,与亚马逊股价的情况不同,使用100个单元的LSTM能够更好地捕捉到字母股价的趋势:

图 9.21:与练习 9.01 的 LSTM 输出比较

图 9.21:与练习 9.01 的 LSTM 输出比较

因此,我们可以清楚地看到,100个单元的LSTM预测的趋势比50个单元的LSTM更准确。请记住,100个单元的LSTM虽然需要更多的计算时间,但在这种情况下提供了更好的结果。除了通过增加单元数来修改我们的模型,我们还可以添加正则化。接下来的活动将测试添加正则化是否能使我们的亚马逊模型更加准确。

活动 9.02:使用添加正则化的 LSTM 预测亚马逊股票价格

在这个活动中,我们将研究亚马逊股票在过去 5 年的价格,从 2014 年 1 月 1 日到 2018 年 12 月 31 日。在此过程中,我们将尝试使用 RNN 和 LSTM 预测并预报公司 2019 年 1 月的未来趋势。我们已经有 2019 年 1 月的实际值,因此稍后可以将我们的预测与实际值进行比较。最初,我们使用 50 个单元(或神经元)的 LSTM 预测了亚马逊股票的趋势。在这里,我们还将添加 dropout 正则化,并将结果与活动 9.01使用 50 个单元(神经元)的 LSTM 预测亚马逊股票价格的趋势进行比较。请按照以下步骤完成此活动:

  1. 导入所需的库。

  2. 从完整的数据集中提取Open列,因为预测将基于开盘股票值进行。你可以从本书的 GitHub 仓库下载数据集,链接为packt.live/2vtdA8o

  3. 将数据规范化到 0 和 1 之间。

  4. 然后,创建时间戳。2019 年 1 月的每一天的值将通过前60天的数据进行预测。所以,如果 1 月 1 日的预测是通过第n天到 12 月 31 日的数据进行的,那么 1 月 2 日将通过第n+1天和 1 月 1 日的数据进行预测,依此类推。

  5. 将数据重新塑形为三维,因为网络需要三维数据。

  6. 使用 Keras 构建一个包含四个 LSTM 层的 RNN,每个 LSTM 层有50个单元(此处,单元指神经元),每个 LSTM 层后有 20%的 dropout。第一步应提供输入形状。请注意,最后一个 LSTM 层总是设置return_sequences=True

  7. 处理并准备测试数据,即 2019 年 1 月的实际数据。

  8. 合并并处理训练数据和测试数据。

  9. 最后,可视化结果。

在实现这些步骤后,你应该得到以下预期的输出:

图 9.22:实际股票价格与预测股票价格

图 9.22:实际股票价格与预测股票价格

注意

本活动的解决方案可以在第 457 页找到。

在下一个活动中,我们将尝试构建每个LSTM层有100个单元的RNN,并将其与只有50个单元的RNN表现进行比较。

活动 9.03:使用逐渐增加 LSTM 神经元数量(100 个单元)预测亚马逊股票价格的趋势

在这个活动中,我们将研究亚马逊过去 5 年的股票价格,从 2014 年 1 月 1 日到 2018 年 12 月 31 日。在此过程中,我们将尝试使用 RNN 预测和预测该公司 2019 年 1 月的未来趋势。我们拥有 2019 年 1 月的实际值,因此稍后可以将我们的预测与实际值进行比较。你还可以将输出的差异与活动 9.01,“使用 50 个单元(神经元)的 LSTM 预测亚马逊股价趋势”进行比较。按照以下步骤完成此活动:

  1. 导入所需的库。

  2. 从完整的数据集中提取Open列,因为预测将基于Open股价进行。

  3. 将数据归一化到 0 到 1 之间。

  4. 然后,创建时间戳。2019 年 1 月每一天的值将由前60天的数据预测;因此,如果 1 月 1 日是根据第 n 天到 12 月 31 日的数据预测的,那么 1 月 2 日将根据第n + 1天和 1 月 1 日的数据来预测,依此类推。

  5. 将数据重塑为三维,因为网络需要三维数据。

  6. 在 Keras 中构建一个包含 100 个单元的 LSTM(此处的单元指的是神经元)。第一步应该提供输入形状。请注意,最终的LSTM层始终需要设置return_sequences=True。编译并将模型拟合到训练数据中。

  7. 处理并准备测试数据,即 2019 年 1 月的实际数据。

  8. 合并和处理训练数据与测试数据。

  9. 可视化结果。

完成这些步骤后,你应该得到以下预期输出:

图 9.23:实际股价与预测股价

图 9.23:实际股价与预测股价

注意

本活动的解决方案可以在第 462 页找到。

在这个活动中,我们创建了一个包含四个LSTM层的RNN,每层有100个单元。我们将其与活动 9.02,“使用添加正则化的 LSTM 预测亚马逊股价”中的结果进行了比较,在该活动中每层有50个单元。两个模型之间的差异很小,因此,具有较少单元的模型更为优选,因为它减少了计算时间,并且更不容易出现过拟合训练数据的情况。

概述

在本章中,我们通过研究一些 Google Assistant 的实际案例,学习了序列建模和序列记忆。然后,我们学习了序列建模如何与RNN相关,并且RNN与传统前馈网络的不同之处。我们详细了解了梯度消失问题,以及为什么使用LSTM比简单的RNN更好地克服梯度消失问题。我们将所学应用于时间序列问题,通过预测股价趋势来实践。

在这个工作坊中,我们学习了机器学习和 Python 的基础知识,同时深入了解了如何应用 Keras 开发高效的深度学习解决方案。我们探索了机器学习和深度学习之间的差异。我们通过首先使用 scikit-learn,然后使用 Keras 来构建逻辑回归模型,开始了本次工作坊。

然后,我们通过创建各种实际场景的预测模型进一步探索了 Keras 及其不同的模型,例如将在线购物者分类为有购买意图和没有购买意图的群体。我们学习了如何评估、优化和改进模型,以实现最大的信息量,创建在新数据上表现良好的强大模型。

我们还通过构建带有 scikit-learn 包装器的 Keras 模型来实现交叉验证,帮助那些熟悉 scikit-learn 工作流程的人轻松使用 Keras 模型。然后,我们学习了如何应用 L1L2dropout regularization 技术,以提高模型的准确性并防止模型过拟合训练数据。

接下来,我们通过应用诸如空准确率作为基准比较技术,以及评估指标如精确度、AUC-ROC 得分等,进一步探索了模型评估,以理解我们的模型如何对分类任务进行评分。最终,这些高级评估技术帮助我们了解了模型在什么情况下表现良好,哪里有改进的空间。

我们通过使用 Keras 创建一些高级模型来结束了工作坊。我们通过构建带有各种参数的 CNN 模型来探索计算机视觉,以分类图像。然后,我们使用预训练模型对新图像进行分类,并对这些预训练模型进行了微调,以便我们可以将它们应用于自己的应用程序。最后,我们介绍了序列建模,这用于建模诸如股票价格和自然语言处理等序列。我们通过创建带有 LSTM 层的 RNN 网络,测试了使用真实股票数据预测股价的知识,并实验了每层中不同单元数的影响以及 dropout 正则化对模型性能的影响。

总体来说,我们全面了解了如何使用 Keras 解决各种实际问题。我们涵盖了在线购物者的分类任务、丙型肝炎数据和 Scania 卡车的故障数据,还包括回归任务,如在给定各种化学属性时预测各种化学物质的水生毒性。我们还进行了图像分类任务,构建了 CNN 模型来预测图像是花朵还是汽车,并构建了回归任务来预测未来的股价,使用的是 RNNs。通过在这个工作坊中使用真实世界的数据集构建模型,你已准备好将所学知识应用到自己的问题解决中,并创建自己的应用程序。

附录

1. 使用 Keras 进行机器学习简介

活动 1.01:向模型添加正则化

在这个活动中,我们将使用来自 scikit-learn 包的相同逻辑回归模型。然而,这一次,我们将向模型中添加正则化,并搜索最佳正则化参数——这个过程通常称为 超参数调优。在训练模型后,我们将测试预测结果,并将模型评估指标与基准模型和未加正则化的模型的评估指标进行比较。

  1. 练习 1.03数据的适当表示 加载特征数据,从 练习 1.02数据清理 加载目标数据:

    import pandas as pd
    feats = pd.read_csv('../data/OSI_feats_e3.csv')
    target = pd.read_csv('../data/OSI_target_e2.csv')
    
  2. 创建 testtrain 数据集。使用训练数据集训练数据。然而,这一次,请使用部分 training 数据集进行验证,以选择最合适的超参数。

    再次使用 test_size = 0.2,这意味着将 20% 的数据保留用于测试。我们的验证集的大小将由验证折数决定。如果我们进行 10 折交叉验证,则相当于将 10%training 数据集保留用于验证模型。每一折将使用不同的 10% 训练数据集,而所有折的平均误差将用于比较具有不同超参数的模型。为 random_state 变量分配一个随机值:

    from sklearn.model_selection import train_test_split
    test_size = 0.2
    random_state = 13
    X_train, X_test, y_train, y_test = \
    train_test_split(feats, target, test_size=test_size, \
                     random_state=random_state)
    
  3. 检查数据框的维度:

    print(f'Shape of X_train: {X_train.shape}')
    print(f'Shape of y_train: {y_train.shape}')
    print(f'Shape of X_test: {X_test.shape}')
    print(f'Shape of y_test: {y_test.shape}')
    

    上述代码产生以下输出:

    Shape of X_train: (9864, 68)
    Shape of y_train: (9864, 1)
    Shape of X_test: (2466, 68)
    Shape of y_test: (2466, 1)
    
  4. 接下来,实例化模型。尝试两种正则化参数,l1l2,并使用 10 倍交叉验证。将我们的正则化参数从 1x10-2 到 1x106 在对数空间中均匀遍历,以观察这些参数如何影响结果:

    import numpy as np
    from sklearn.linear_model import LogisticRegressionCV
    Cs = np.logspace(-2, 6, 9)
    model_l1 = LogisticRegressionCV(Cs=Cs, penalty='l1', \
                                    cv=10, solver='liblinear', \
                                    random_state=42, max_iter=10000)
    model_l2 = LogisticRegressionCV(Cs=Cs, penalty='l2', cv=10, \
                                    random_state=42, max_iter=10000)
    

    注意

    对于具有 l1 正则化参数的逻辑回归模型,只能使用 liblinear 求解器。

  5. 接下来,将模型拟合到训练数据:

    model_l1.fit(X_train, y_train['Revenue'])
    model_l2.fit(X_train, y_train['Revenue'])
    

    下图显示了上述代码的输出:

    图 1.37:fit 命令的输出,显示所有模型训练参数

    ](tos-cn-i-73owjymdk6/08e3deb7b8d7402cb7fa0358963c1d54)

    图 1.37:fit 命令的输出,显示所有模型训练参数

  6. 在这里,我们可以看到两种不同模型的正则化参数值。正则化参数是根据哪个模型产生了最低误差来选择的:

    print(f'Best hyperparameter for l1 regularization model: \
    {model_l1.C_[0]}')
    print(f'Best hyperparameter for l2 regularization model: \
    {model_l2.C_[0]}')
    

    上述代码产生以下输出:

    Best hyperparameter for l1 regularization model: 1000000.0
    Best hyperparameter for l2 regularization model: 1.0
    

    注意

    C_ 属性只有在模型训练完成后才能使用,因为它是在交叉验证过程确定最佳参数后设置的。

  7. 为了评估模型的性能,请对 test 集合进行预测,并将其与 true 值进行比较:

    y_pred_l1 = model_l1.predict(X_test)
    y_pred_l2 = model_l2.predict(X_test)
    
  8. 为了比较这些模型,计算评估指标。首先,查看模型的准确度:

    from sklearn import metrics
    accuracy_l1 = metrics.accuracy_score(y_pred=y_pred_l1, \
                                         y_true=y_test)
    accuracy_l2 = metrics.accuracy_score(y_pred=y_pred_l2, \
                                         y_true=y_test)
    print(f'Accuracy of the model with l1 regularization is \
    {accuracy_l1*100:.4f}%')
    print(f'Accuracy of the model with l2 regularization is \
    {accuracy_l2*100:.4f}%')
    

    上述代码产生以下输出:

    Accuracy of the model with l1 regularization is 89.2133%
    Accuracy of the model with l2 regularization is 89.2944%
    
  9. 另外,还请查看其他评估指标:

    precision_l1, recall_l1, fscore_l1, _ = \
    metrics.precision_recall_fscore_support(y_pred=y_pred_l1, \
                                            y_true=y_test, \
                                            average='binary')
    precision_l2, recall_l2, fscore_l2, _ = \
    metrics.precision_recall_fscore_support(y_pred=y_pred_l2, \
                                            y_true=y_test, \
                                            average='binary')
    print(f'l1\nPrecision: {precision_l1:.4f}\nRecall: \
    {recall_l1:.4f}\nfscore: {fscore_l1:.4f}\n\n')
    print(f'l2\nPrecision: {precision_l2:.4f}\nRecall: \
    {recall_l2:.4f}\nfscore: {fscore_l2:.4f}')
    

    前面的代码会产生以下输出:

    l1
    Precision: 0.7300
    Recall: 0.4078
    fscore: 0.5233
    l2
    Precision: 0.7350
    Recall: 0.4106
    fscore: 0.5269
    
  10. 观察模型训练完成后系数的值:

    coef_list = [f'{feature}: {coef}' for coef, \
                 feature in sorted(zip(model_l1.coef_[0], \
                                   X_train.columns.values.tolist()))]
    for item in coef_list:
        print(item)
    

    注意

    coef_属性仅在模型训练完成后可用,因为它是在交叉验证过程中确定最佳参数后设置的。

    以下图显示了前面代码的输出:

    图 1.38:特征列名称及其相应系数的值    对于具有 l1 正则化的模型

    图 1.38:具有 l1 正则化的模型的特征列名称及其相应系数的值

  11. 对具有l2正则化参数类型的模型执行相同操作:

    coef_list = [f'{feature}: {coef}' for coef, \
                 feature in sorted(zip(model_l2.coef_[0], \
                                       X_train.columns.values.tolist()))]
    for item in coef_list:
        print(item)
    

    以下图显示了前面代码的输出:

    图 1.39:特征列名称及其相应系数的值    对于具有 l2 正则化的模型

图 1.39:具有 l2 正则化的模型的特征列名称及其相应系数的值

注意

要访问该特定部分的源代码,请参考packt.live/2VIoe5M

本节目前没有在线交互式示例,需要在本地运行。

2. 机器学习与深度学习

活动 2.01:使用 Keras 创建逻辑回归模型

在这个活动中,我们将使用 Keras 库创建一个基本模型。我们将构建的模型将把网站用户分为两类:一类是会从网站购买产品的用户,另一类则不会。为了实现这一目标,我们将使用之前相同的在线购物购买意图数据集,并尝试预测我们在第一章中预测的相同变量,即使用 Keras 进行机器学习入门

执行以下步骤完成此活动:

  1. 打开开始菜单中的 Jupyter 笔记本以实现此活动。加载在线购物购买意图数据集,你可以从 GitHub 仓库下载。我们将使用pandas库进行数据加载,因此请先导入pandas库。确保你已经将 csv 文件保存到本章适当的数据文件夹中,或者可以更改代码中使用的文件路径。

    import pandas as pd
    feats = pd.read_csv('../data/OSI_feats.csv')
    target = pd.read_csv('../data/OSI_target.csv')
    
  2. 对于本次活动,我们不进行进一步的预处理。和上一章一样,我们将数据集拆分为训练集和测试集,并将测试推迟到最后,在评估模型时进行。我们将保留20%的数据用于测试,通过设置test_size=0.2参数,并创建一个random_state参数,以便重现结果:

    from sklearn.model_selection import train_test_split
    test_size = 0.2
    random_state = 42
    X_train, X_test, y_train, y_test = \
    train_test_split(feats, target, test_size=test_size, \
                     random_state=random_state)
    
  3. numpytensorflow中设置随机种子以保证可复现性。通过初始化Sequential类的模型开始创建模型:

    from keras.models import Sequential
    import numpy as np
    from tensorflow import random
    np.random.seed(random_state)
    random.set_seed(random_state)
    model = Sequential()
    
  4. 要向模型中添加一个全连接层,请添加一个Dense类的层。在这里,我们需要包括该层中的节点数。在我们的例子中,由于我们正在执行二分类,且期望输出为zeroone,所以该值将为 1。此外,还需要指定输入维度,这只需要在模型的第一层中指定。它的作用是表示输入数据的格式。传入特征的数量:

    from keras.layers import Dense
    model.add(Dense(1, input_dim=X_train.shape[1]))
    
  5. 在前一层的输出上添加一个 sigmoid 激活函数,以复制logistic regression算法:

    from keras.layers import Activation
    model.add(Activation('sigmoid'))
    
  6. 一旦我们将所有模型组件按正确顺序排列,我们必须编译模型,以便所有的学习过程都能被配置。使用adam优化器,binary_crossentropy作为损失函数,并通过将参数传入metrics参数来跟踪模型的准确率:

    model.compile(optimizer='adam', loss='binary_crossentropy', \
                  metrics=['accuracy'])
    
  7. 打印模型摘要,以验证模型是否符合我们的预期:

    print(model.summary())
    

    下图展示了前面代码的输出:

    图 2.19:模型摘要

    图 2.19:模型摘要

  8. 接下来,使用model类的fit方法拟合模型。提供训练数据,以及训练周期数和每个周期后用于验证的数据量:

    history = model.fit(X_train, y_train['Revenue'], epochs=10, \
                        validation_split=0.2, shuffle=False)
    

    下图展示了前面代码的输出:

    图 2.20:在模型上使用 fit 方法

    图 2.20:在模型上使用 fit 方法

  9. 损失和准确率的值已存储在history变量中。使用每个训练周期后我们跟踪的损失和准确率,绘制每个值的图表:

    import matplotlib.pyplot as plt
    %matplotlib inline
    # Plot training and validation accuracy values
    plt.plot(history.history['accuracy'])
    plt.plot(history.history['val_accuracy'])
    plt.title('Model accuracy')
    plt.ylabel('Accuracy')
    plt.xlabel('Epoch')
    plt.legend(['Train', 'Validation'], loc='upper left')
    plt.show()
    # Plot training and validation loss values
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('Model loss')
    plt.ylabel('Loss')
    plt.xlabel('Epoch')
    plt.legend(['Train', 'Validation'], loc='upper left')
    plt.show()
    

    以下图表展示了前面代码的输出:

    图 2.21:拟合模型时的损失和准确率

    图 2.21:拟合模型时的损失和准确率

  10. 最后,在我们从一开始就保留的测试数据上评估模型,这将为模型的性能提供客观评价:

    test_loss, test_acc = model.evaluate(X_test, y_test['Revenue'])
    print(f'The loss on the test set is {test_loss:.4f} \
    and the accuracy is {test_acc*100:.3f}%')
    

    前面代码的输出如下所示。在这里,模型预测了测试数据集中用户的购买意图,并通过将其与y_test中的真实值进行比较来评估性能。在测试数据集上评估模型将产生损失和准确率值,我们可以打印出来:

    2466/2466 [==============================] - 0s 15us/step
    The loss on the test set is 0.3632 and the accuracy is 86.902%
    

    要访问此特定部分的源代码,请参考

    packt.live/3dVTQLe

    你还可以在packt.live/2ZxEhV4在线运行此示例。

3. 使用 Keras 进行深度学习

活动 3.01:构建一个单层神经网络进行二分类

在本活动中,我们将比较逻辑回归模型和不同节点大小以及不同激活函数的单层神经网络的结果。我们将使用的数据集表示飞机螺旋桨检查的标准化测试结果,而类别表示它们是否通过了人工视觉检查。我们将创建模型来预测给定自动化测试结果时的人工检查结果。请按照以下步骤完成此活动:

  1. 加载所有必要的包:

    # import required packages from Keras
    from keras.models import Sequential 
    from keras.layers import Dense, Activation 
    import numpy as np
    import pandas as pd
    from tensorflow import random
    from sklearn.model_selection import train_test_split
    # import required packages for plotting
    import matplotlib.pyplot as plt 
    import matplotlib
    %matplotlib inline 
    import matplotlib.patches as mpatches
    # import the function for plotting decision boundary
    from utils import plot_decision_boundary
    
  2. 设置一个seed

    """
    define a seed for random number generator so the result will be reproducible
    """
    seed = 1
    
  3. 加载模拟数据集并打印XY的大小以及示例的数量:

    """
    load the dataset, print the shapes of input and output and the number of examples
    """
    feats = pd.read_csv('../data/outlier_feats.csv')
    target = pd.read_csv('../data/outlier_target.csv')
    print("X size = ", feats.shape)
    print("Y size = ", target.shape)
    print("Number of examples = ", feats.shape[0])
    

    预期输出

    X size = (3359, 2)
    Y size = (3359, 1)
    Number of examples = 3359
    
  4. 绘制数据集。每个点的 x 和 y 坐标将是两个输入特征。每条记录的颜色代表通过/失败结果:

    class_1=plt.scatter(feats.loc[target['Class']==0,'feature1'], \
                        feats.loc[target['Class']==0,'feature2'], \
                        c="red", s=40, edgecolor='k')
    class_2=plt.scatter(feats.loc[target['Class']==1,'feature1'], \
                        feats.loc[target['Class']==1,'feature2'], \
                        c="blue", s=40, edgecolor='k')
    plt.legend((class_1, class_2),('Fail','Pass'))
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    

    以下图像显示了前述代码的输出:

    图 3.19:模拟训练数据点

    图 3.19:模拟训练数据点

  5. 构建逻辑回归模型,这是一个没有隐藏层的单节点顺序模型,使用sigmoid 激活函数:

    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    model.add(Dense(1, activation='sigmoid', input_dim=2))
    model.compile(optimizer='sgd', loss='binary_crossentropy')
    
  6. 将模型拟合到训练数据:

    model.fit(feats, target, batch_size=5, epochs=100, verbose=1, \
              validation_split=0.2, shuffle=False)
    

    100轮 = 0.3537

    图 3.20:100 轮中的最后 5 轮的损失详情

    图 3.20:100 轮中的最后 5 轮的损失详情

  7. 在训练数据上绘制决策边界:

    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plot_decision_boundary(lambda x: model.predict(x), feats, target)
    plt.title("Logistic Regression")
    

    以下图像显示了前述代码的输出:

    图 3.21:逻辑回归模型的决策边界

    图 3.21:逻辑回归模型的决策边界

    逻辑回归模型的线性决策边界显然无法捕捉到两类之间的圆形决策边界,并将所有结果预测为通过结果。

  8. 创建一个包含三个节点的单隐藏层神经网络,并使用relu 激活函数,输出层为一个节点,并使用sigmoid 激活函数。最后,编译模型:

    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential() 
    model.add(Dense(3, activation='relu', input_dim=2))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='sgd', loss='binary_crossentropy')
    
  9. 将模型拟合到训练数据:

    model.fit(feats, target, batch_size=5, epochs=200, verbose=1, \
              validation_split=0.2, shuffle=False)
    

    200轮 = 0.0260

    图 3.22:200 轮中的最后 5 轮的损失详情

    图 3.22:200 轮中的最后 5 轮的损失详情

  10. 绘制所创建的决策边界:

    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plot_decision_boundary(lambda x: model.predict(x), feats, target)
    plt.title("Decision Boundary for Neural Network with "\
              "hidden layer size 3")
    

    以下图像显示了前述代码的输出:

    图 3.23:带有隐藏层的神经网络的决策边界    3 个节点的大小和 ReLU 激活函数

    图 3.23:带有 3 个节点的隐藏层和 ReLU 激活函数的神经网络决策边界

    使用三个处理单元代替一个显著提高了模型捕捉两类之间非线性边界的能力。注意,与上一步相比,损失值显著下降。

  11. 创建一个神经网络,包含一个具有六个节点的隐藏层和一个relu 激活函数,输出层有一个节点,并使用sigmoid 激活函数。最后,编译模型:

    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential() 
    model.add(Dense(6, activation='relu', input_dim=2))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='sgd', loss='binary_crossentropy')
    
  12. 将模型拟合到训练数据:

    model.fit(feats, target, batch_size=5, epochs=400, verbose=1, \
              validation_split=0.2, shuffle=False)
    

    400 轮次 = 0.0231

    图 3.24:最后 5 个 epoch(共 400 个)的损失详情

    图 3.24:最后 5 个 epoch(共 400 个)的损失详情

  13. 绘制决策边界:

    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plot_decision_boundary(lambda x: model.predict(x), feats, target)
    plt.title("Decision Boundary for Neural Network with "\
              "hidden layer size 6")
    

    以下图像显示了前述代码的输出:

    图 3.25:具有隐藏层的神经网络的决策边界    隐藏层大小为 6 且使用 ReLU 激活函数

    图 3.25:具有隐藏层大小为 6 且使用 ReLU 激活函数的神经网络的决策边界

    通过将隐藏层中的单元数加倍,模型的决策边界更加接近真实的圆形,而且与前一步相比,损失值进一步减少。

  14. 创建一个神经网络,包含一个具有三个节点的隐藏层和一个tanh 激活函数,输出层有一个节点,并使用sigmoid 激活函数。最后,编译模型:

    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential() 
    model.add(Dense(3, activation='tanh', input_dim=2))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='sgd', loss='binary_crossentropy')
    
  15. 将模型拟合到训练数据:

    model.fit(feats, target, batch_size=5, epochs=200, verbose=1, \
              validation_split=0.2, shuffle=False)
    

    200 轮次 = 0.0426

    图 3.26:最后 5 个 epoch(共 200 个)的损失详情

    图 3.26:最后 5 个 epoch(共 200 个)的损失详情

  16. 绘制决策边界:

    plot_decision_boundary(lambda x: model.predict(x), feats, target) 
    plt.title("Decision Boundary for Neural Network with "\
              "hidden layer size 3")
    

    以下图像显示了前述代码的输出:

    图 3.27:具有隐藏层的神经网络的决策边界    隐藏层大小为 3 且使用 tanh 激活函数

    图 3.27:具有隐藏层大小为 3 且使用 tanh 激活函数的神经网络的决策边界

    使用tanh激活函数消除了决策边界中的尖锐边缘。换句话说,它使得决策边界变得更加平滑。然而,由于我们看到损失值的增加,模型并没有表现得更好。尽管之前提到过tanh的学习速度比relu慢,但在对测试数据集进行评估时,我们得到了相似的损失和准确度评分。

  17. 创建一个神经网络,包含一个具有六个节点的隐藏层和一个tanh 激活函数,输出层有一个节点,并使用sigmoid 激活函数。最后,编译模型:

    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential() 
    model.add(Dense(6, activation='tanh', input_dim=2))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='sgd', loss='binary_crossentropy')
    
  18. 将模型拟合到训练数据:

    model.fit(feats, target, batch_size=5, epochs=400, verbose=1, \
              validation_split=0.2, shuffle=False)
    

    400 轮次 = 0.0215

    图 3.28:最后 5 个 epoch(共 400 个)的损失详情

    图 3.28:最后 5 个 epoch(共 400 个)的损失详情

  19. 绘制决策边界:

    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plot_decision_boundary(lambda x: model.predict(x), feats, target)
    plt.title("Decision Boundary for Neural Network with "\
              "hidden layer size 6")
    

    以下图像显示了前述代码的输出:

    图 3.29:具有隐藏层大小为 6 且使用 ReLU 激活函数的神经网络的决策边界    具有 6 个节点和 tanh 激活函数

图 3.29:具有隐藏层大小为 6 且使用 tanh 激活函数的神经网络的决策边界

再次使用tanh激活函数代替relu,并将更多的节点添加到隐藏层中,使决策边界的曲线更加平滑,训练数据的拟合效果更好,依据训练数据的准确性来判断。我们应当小心,不要向隐藏层中添加过多的节点,否则可能会导致过拟合数据。这可以通过评估测试集来观察,在拥有六个节点的神经网络上,相比于具有三个节点的神经网络,准确度有所下降。

注:

要访问此特定部分的源代码,请参阅packt.live/3iv0wn1

您也可以在packt.live/2BqumZt上在线运行此示例。

活动 3.02:使用神经网络进行高级纤维化诊断

在本活动中,您将使用一个真实数据集来预测患者是否存在高级纤维化,基于的测量指标包括年龄、性别和 BMI。该数据集包含 1,385 名接受过丙型肝炎治疗剂量的患者信息。每个患者都有28个不同的属性,并且有一个类别标签,该标签只能取两个值:1表示高级纤维化,0表示没有高级纤维化迹象。这是一个二分类问题,输入维度为 28。

在本活动中,您将实现不同的深度神经网络架构来执行此分类任务,绘制训练误差率和测试误差率的趋势,并确定最终分类器需要训练多少个 epoch。请按照以下步骤完成此活动:

  1. 导入所有必要的库,并使用 pandas 的read_csv函数加载数据集:

    import pandas as pd
    import numpy as np
    from tensorflow import random
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import StandardScaler
    from keras.models import Sequential
    from keras.layers import Dense
    import matplotlib.pyplot as plt 
    import matplotlib
    %matplotlib inline
    X = pd.read_csv('../data/HCV_feats.csv')
    y = pd.read_csv('../data/HCV_target.csv')
    
  2. 打印recordsfeaturesfeature数据集中的数量,以及target数据集中唯一类别的数量:

    print("Number of Examples in the Dataset = ", X.shape[0])
    print("Number of Features for each example = ", X.shape[1]) 
    print("Possible Output Classes = ", \
          y['AdvancedFibrosis'].unique())
    

    预期输出

    Number of Examples in the Dataset = 1385
    Number of Features for each example = 28
    Possible Output Classes = [0 1]
    
  3. 对数据进行归一化并进行缩放。然后,将数据集拆分为训练集和测试集:

    seed = 1
    np.random.seed(seed)
    sc = StandardScaler()
    X = pd.DataFrame(sc.fit_transform(X), columns=X.columns)
    X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.2, random_state=seed)
    # Print the information regarding dataset sizes
    print(X_train.shape)
    print(y_train.shape)
    print(X_test.shape)
    print(y_test.shape)
    print ("Number of examples in training set = ", X_train.shape[0])
    print ("Number of examples in test set = ", X_test.shape[0])
    

    预期输出

    (1108, 28)
    (1108, 1)
    (277, 28)
    (277, 1)
    Number of examples in training set = 1108
    Number of examples in test set = 277
    
  4. 实现一个具有一个隐藏层,隐藏层大小为3,激活函数为tanh,输出层为一个节点,并使用sigmoid激活函数的深度神经网络。最后,编译模型并打印出模型的摘要:

    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model
    classifier = Sequential()
    classifier.add(Dense(units = 3, activation = 'tanh', \
                         input_dim=X_train.shape[1]))
    classifier.add(Dense(units = 1, activation = 'sigmoid'))
    classifier.compile(optimizer = 'sgd', loss = 'binary_crossentropy', \
                       metrics = ['accuracy'])
    classifier.summary()
    

    以下图像显示了前述代码的输出:

    图 3.30:神经网络的架构

    图 3.30:神经网络的架构

  5. 将模型拟合到训练数据中:

    history=classifier.fit(X_train, y_train, batch_size = 20, \
                           epochs = 100, validation_split=0.1, \
                           shuffle=False)
    
  6. 绘制每个 epoch 的训练误差率测试误差率

    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    

    预期输出

    图 3.31:训练模型时训练误差率和测试误差率的变化图

    图 3.31:训练模型时训练误差率和测试误差率的变化图

  7. 打印出在训练集和测试集上达到的最佳准确率值,以及在test数据集上评估的lossaccuracy值。

    print(f"Best Accuracy on training set = \
    {max(history.history['accuracy'])*100:.3f}%")
    print(f"Best Accuracy on validation set = \
    {max(history.history['val_accuracy'])*100:.3f}%") 
    test_loss, test_acc = \
    classifier.evaluate(X_test, y_test['AdvancedFibrosis'])
    print(f'The loss on the test set is {test_loss:.4f} and \
    the accuracy is {test_acc*100:.3f}%')
    

    下图展示了前面代码的输出结果:

    Best Accuracy on training set = 52.959%
    Best Accuracy on validation set = 58.559%
    277/277 [==============================] - 0s 25us/step
    The loss on the test set is 0.6885 and the accuracy is 55.235%
    
  8. 实现一个具有两个隐藏层的深度神经网络,第一个隐藏层的大小为4,第二个隐藏层的大小为2,使用tanh 激活函数,输出层有一个节点,使用sigmoid 激活函数。最后,编译模型并打印出模型的总结:

    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model
    classifier = Sequential()
    classifier.add(Dense(units = 4, activation = 'tanh', \
                         input_dim = X_train.shape[1]))
    classifier.add(Dense(units = 2, activation = 'tanh'))
    classifier.add(Dense(units = 1, activation = 'sigmoid'))
    classifier.compile(optimizer = 'sgd', loss = 'binary_crossentropy', \
                       metrics = ['accuracy'])
    classifier.summary()
    

    图 3.32:神经网络架构

    图 3.32:神经网络架构

  9. 将模型拟合到训练数据:

    history=classifier.fit(X_train, y_train, batch_size = 20, \
                           epochs = 100, validation_split=0.1, \
                           shuffle=False)
    
  10. 绘制具有两个隐藏层(大小分别为 4 和 2)的训练和测试误差图。打印在训练集和测试集上达到的最佳准确率:

    # plot training error and test error plots 
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    

    预期输出

    图 3.33:训练误差和测试误差率在训练模型时的变化图

    图 3.33:训练误差和测试误差率在训练模型时的变化图

  11. 打印在训练集和测试集上达到的最佳准确率,以及在测试数据集上评估的损失准确率值。

    print(f"Best Accuracy on training set = \
    {max(history.history['accuracy'])*100:.3f}%")
    print(f"Best Accuracy on validation set = \
    {max(history.history['val_accuracy'])*100:.3f}%") 
    test_loss, test_acc = \
    classifier.evaluate(X_test, y_test['AdvancedFibrosis'])
    print(f'The loss on the test set is {test_loss:.4f} and \
    the accuracy is {test_acc*100:.3f}%')
    

    以下展示了前面代码的输出结果:

    Best Accuracy on training set = 57.272%
    Best Accuracy on test set = 54.054%
    277/277 [==============================] - 0s 41us/step
    The loss on the test set is 0.7016 and the accuracy is 49.819%
    

    注意

    若要访问该部分的源代码,请参考 packt.live/2BrIRMF

    你也可以在线运行这个例子,访问 packt.live/2NUl22A

4. 使用 Keras 封装器进行交叉验证评估模型

活动 4.01:使用交叉验证评估高级肝纤维化诊断分类器模型

在这个活动中,我们将使用本主题中学到的内容,使用k 折交叉验证训练和评估深度学习模型。我们将使用前一个活动中得到的最佳测试误差率的模型,目标是将交叉验证的误差率与训练集/测试集方法的误差率进行比较。我们将使用的数据库是丙型肝炎 C 数据集,在该数据集中,我们将构建一个分类模型,预测哪些患者会患上晚期肝纤维化。按照以下步骤完成此活动:

  1. 加载数据集并打印数据集中的记录数和特征数,以及目标数据集中可能的类别数:

    # Load the dataset
    import pandas as pd
    X = pd.read_csv('../data/HCV_feats.csv')
    y = pd.read_csv('../data/HCV_target.csv')
    # Print the sizes of the dataset
    print("Number of Examples in the Dataset = ", X.shape[0])
    print("Number of Features for each example = ", X.shape[1]) 
    print("Possible Output Classes = ", \
          y['AdvancedFibrosis'].unique())
    

    这是预期的输出:

    Number of Examples in the Dataset = 1385
    Number of Features for each example = 28
    Possible Output Classes = [0 1]
    
  2. 定义一个返回 Keras 模型的函数。首先,导入 Keras 所需的库。在函数内部,实例化顺序模型并添加两个全连接层,第一个层的大小为4,第二个层的大小为2,两者均使用tanh 激活函数。添加输出层并使用sigmoid 激活函数。编译模型并返回模型:

    from keras.models import Sequential
    from keras.layers import Dense
    # Create the function that returns the keras model
    def build_model():
        model = Sequential()
        model.add(Dense(4, input_dim=X.shape[1], activation='tanh'))
        model.add(Dense(2, activation='tanh'))
        model.add(Dense(1, activation='sigmoid'))
        model.compile(loss='binary_crossentropy', optimizer='adam', \
                      metrics=['accuracy'])
        return model
    
  3. 使用StandardScaler函数对训练数据进行缩放。设置种子,以便模型可复现。定义超参数n_foldsepochsbatch_size。然后,使用 scikit-learn 构建 Keras 封装器,定义cross-validation迭代器,执行k 折交叉验证并存储得分:

    # import required packages
    import numpy as np
    from tensorflow import random
    from keras.wrappers.scikit_learn import KerasClassifier
    from sklearn.model_selection import StratifiedKFold
    from sklearn.model_selection import cross_val_score
    from sklearn.preprocessing import StandardScaler
    sc = StandardScaler()
    X = pd.DataFrame(sc.fit_transform(X), columns=X.columns)
    """
    define a seed for random number generator so the result will be reproducible
    """
    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    """
    determine the number of folds for k-fold cross-validation, number of epochs and batch size
    """
    n_folds = 5
    epochs = 100
    batch_size = 20
    # build the scikit-learn interface for the keras model
    classifier = KerasClassifier(build_fn=build_model, \
                                 epochs=epochs, \
                                 batch_size=batch_size, \
                                 verbose=1, shuffle=False)
    # define the cross-validation iterator
    kfold = StratifiedKFold(n_splits=n_folds, shuffle=True, \
                            random_state=seed)
    """
    perform the k-fold cross-validation and store the scores in results
    """
    results = cross_val_score(classifier, X, y, cv=kfold)
    
  4. 对于每一折,打印存储在results参数中的准确率:

    # print accuracy for each fold
    for f in range(n_folds):
        print("Test accuracy at fold ", f+1, " = ", results[f])
    print("\n")
    """
    print overall cross-validation accuracy plus the standard deviation of the accuracies
    """
    print("Final Cross-validation Test Accuracy:", results.mean())
    print("Standard Deviation of Final Test Accuracy:", results.std())
    

    以下是预期的输出:

    Test accuracy at fold 1 = 0.5198556184768677
    Test accuracy at fold 2 = 0.4693140685558319
    Test accuracy at fold 3 = 0.512635350227356
    Test accuracy at fold 4 = 0.5740072131156921
    Test accuracy at fold 5 = 0.5523465871810913
    Final Cross-Validation Test Accuracy: 0.5256317675113678
    Standard Deviation of Final Test Accuracy: 0.03584760640500936
    

    注意

    要访问此特定部分的源代码,请参考packt.live/3eWgR2b

    你也可以在线运行这个示例,网址为:packt.live/3iBYtOi

活动 4.02:使用交叉验证为高级纤维化诊断分类器选择模型

在此活动中,我们将通过使用交叉验证来选择模型和超参数,从而改进针对肝炎 C 数据集的分类器。按照以下步骤完成此活动:

  1. 导入所有所需的包并加载数据集。使用StandardScaler函数对数据集进行标准化:

    # import the required packages
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.wrappers.scikit_learn import KerasClassifier
    from sklearn.model_selection import StratifiedKFold
    from sklearn.model_selection import cross_val_score
    import numpy as np
    import pandas as pd
    from sklearn.preprocessing import StandardScaler
    from tensorflow import random
    # Load the dataset
    X = pd.read_csv('../data/HCV_feats.csv')
    y = pd.read_csv('../data/HCV_target.csv')
    sc = StandardScaler()
    X = pd.DataFrame(sc.fit_transform(X), columns=X.columns)
    
  2. 定义三个函数,每个函数返回一个不同的 Keras 模型。第一个模型应具有三个隐藏层,每层大小为 4,第二个模型应具有两个隐藏层,第一个隐藏层大小为 4,第二个隐藏层大小为 2,第三个模型应具有两个隐藏层,大小为 8。使用函数参数来传递激活函数和优化器,以便它们可以传递给模型。目标是找出这三个模型中哪一个导致了最低的交叉验证误差率:

    # Create the function that returns the keras model 1
    def build_model_1(activation='relu', optimizer='adam'):
        # create model 1
        model = Sequential()
        model.add(Dense(4, input_dim=X.shape[1], \
                        activation=activation))
        model.add(Dense(4, activation=activation))
        model.add(Dense(4, activation=activation))
        model.add(Dense(1, activation='sigmoid'))
        # Compile model
        model.compile(loss='binary_crossentropy', \
                      optimizer=optimizer, metrics=['accuracy'])
        return model
    # Create the function that returns the keras model 2
    def build_model_2(activation='relu', optimizer='adam'):
        # create model 2
        model = Sequential()
        model.add(Dense(4, input_dim=X.shape[1], \
                        activation=activation))
        model.add(Dense(2, activation=activation))
        model.add(Dense(1, activation='sigmoid'))
        # Compile model
        model.compile(loss='binary_crossentropy', \
                      optimizer=optimizer, metrics=['accuracy'])
        return model
    # Create the function that returns the keras model 3
    def build_model_3(activation='relu', optimizer='adam'):
        # create model 3
        model = Sequential()
        model.add(Dense(8, input_dim=X.shape[1], \
                        activation=activation))
        model.add(Dense(8, activation=activation))
        model.add(Dense(1, activation='sigmoid'))
        # Compile model
        model.compile(loss='binary_crossentropy', \
                      optimizer=optimizer, metrics=['accuracy'])
        return model
    

    编写代码,循环遍历三个模型并执行5 折交叉验证。设置随机种子以确保模型可重复,并定义n_foldsbatch_sizeepochs超参数。在训练模型时,存储应用cross_val_score函数的结果:

    """
    define a seed for random number generator so the result will be reproducible
    """
    seed = 2
    np.random.seed(seed)
    random.set_seed(seed)
    """
    determine the number of folds for k-fold cross-validation, number of epochs and batch size
    """
    n_folds = 5
    batch_size=20
    epochs=100
    # define the list to store cross-validation scores
    results_1 = []
    # define the possible options for the model
    models = [build_model_1, build_model_2, build_model_3]
    # loop over models
    for m in range(len(models)):
        # build the scikit-learn interface for the keras model
        classifier = KerasClassifier(build_fn=models[m], \
                                     epochs=epochs, \
                                     batch_size=batch_size, \
                                     verbose=0, shuffle=False)
        # define the cross-validation iterator
        kfold = StratifiedKFold(n_splits=n_folds, shuffle=True, \
                                random_state=seed)
        """
        perform the k-fold cross-validation and store the scores 
        in result
        """
        result = cross_val_score(classifier, X, y, cv=kfold)
        # add the scores to the results list 
        results_1.append(result)
    # Print cross-validation score for each model
    for m in range(len(models)):
        print("Model", m+1,"Test Accuracy =", results_1[m].mean())
    

    这是一个示例输出。在此实例中,模型 2具有最佳的交叉验证测试准确率,具体如下所示:

    Model 1 Test Accuracy = 0.4996389865875244
    Model 2 Test Accuracy = 0.5148014307022095
    Model 3 Test Accuracy = 0.5097472846508027
    
  3. 选择具有最高准确率得分的模型,并通过遍历epochs = [100, 200]batches = [10, 20]的值并执行5 折交叉验证来重复步骤 2

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # determine the number of folds for k-fold cross-validation
    n_folds = 5
    # define possible options for epochs and batch_size
    epochs = [100, 200]
    batches = [10, 20]
    # define the list to store cross-validation scores
    results_2 = []
    # loop over all possible pairs of epochs, batch_size
    for e in range(len(epochs)):
        for b in range(len(batches)):
            # build the scikit-learn interface for the keras model
            classifier = KerasClassifier(build_fn=build_model_2, \
                                         epochs=epochs[e], \
                                         batch_size=batches[b], \
                                         verbose=0)
            # define the cross-validation iterator
            kfold = StratifiedKFold(n_splits=n_folds, shuffle=True, \
                                    random_state=seed)
            # perform the k-fold cross-validation. 
            # store the scores in result
            result = cross_val_score(classifier, X, y, cv=kfold)
            # add the scores to the results list 
            results_2.append(result)
    """
    Print cross-validation score for each possible pair of epochs, batch_size
    """
    c = 0
    for e in range(len(epochs)):
        for b in range(len(batches)):
            print("batch_size =", batches[b],", epochs =", epochs[e], \
                  ", Test Accuracy =", results_2[c].mean())
            c += 1
    

    这是一个示例输出:

    batch_size = 10 , epochs = 100 , Test Accuracy = 0.5010830342769623
    batch_size = 20 , epochs = 100 , Test Accuracy = 0.5126353740692139
    batch_size = 10 , epochs = 200 , Test Accuracy = 0.5176895320416497
    batch_size = 20 , epochs = 200 , Test Accuracy = 0.5075812220573426
    

    在此案例中,batch_size= 10epochs=200的组合具有最佳的交叉验证测试准确率。

  4. 选择具有最高准确率得分的批处理大小和训练轮数,并通过遍历optimizers = ['rmsprop', 'adam', 'sgd']activations = ['relu', 'tanh']的值并执行5 折交叉验证来重复步骤 3

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    """
    determine the number of folds for k-fold cross-validation, number of epochs and batch size
    """
    n_folds = 5
    batch_size = 10
    epochs = 200
    # define the list to store cross-validation scores
    results_3 = []
    # define possible options for optimizer and activation
    optimizers = ['rmsprop', 'adam','sgd']
    activations = ['relu', 'tanh']
    # loop over all possible pairs of optimizer, activation
    for o in range(len(optimizers)):
        for a in range(len(activations)):
            optimizer = optimizers[o]
            activation = activations[a]
            # build the scikit-learn interface for the keras model
            classifier = KerasClassifier(build_fn=build_model_2, \
                                         epochs=epochs, \
                                         batch_size=batch_size, \
                                         verbose=0, shuffle=False)
            # define the cross-validation iterator
            kfold = StratifiedKFold(n_splits=n_folds, shuffle=True, \
                                    random_state=seed)
            # perform the k-fold cross-validation. 
            # store the scores in result
            result = cross_val_score(classifier, X, y, cv=kfold)
            # add the scores to the results list 
            results_3.append(result)
    """
    Print cross-validation score for each possible pair of optimizer, activation
    """
    c = 0
    for o in range(len(optimizers)):
        for a in range(len(activations)):
            print("activation = ", activations[a],", optimizer = ", \
                  optimizers[o], ", Test accuracy = ", \
                  results_3[c].mean())
            c += 1
    

    以下是预期的输出:

    activation =  relu , optimizer =  rmsprop , 
    Test accuracy =  0.5234657049179077
    activation =  tanh , optimizer =  rmsprop , 
    Test accuracy =  0.49602887630462644
    activation =  relu , optimizer =  adam , 
    Test accuracy =  0.5039711117744445
    activation =  tanh , optimizer =  adam , 
    Test accuracy =  0.4989169597625732
    activation =  relu , optimizer =  sgd , 
    Test accuracy =  0.48953068256378174
    activation =  tanh , optimizer =  sgd , 
    Test accuracy =  0.5191335678100586
    

    在这里,activation='relu'optimizer='rmsprop'的组合具有最佳的交叉验证测试准确率。此外,activation='tanh'optimizer='sgd'的组合则取得了第二好的性能。

    注意

    要访问此特定部分的源代码,请参考packt.live/2D3AIhD

    你也可以在线运行这个示例,网址为:packt.live/2NUpiiC

活动 4.03:使用交叉验证选择交通量数据集的模型

在这个活动中,你将再次练习使用交叉验证进行模型选择。在这里,我们将使用一个模拟数据集,表示一个目标变量,表示城市桥梁上每小时的交通量,以及与交通数据相关的各种归一化特征,如一天中的时间和前一天的交通量。我们的目标是建立一个模型,根据这些特征预测城市桥梁上的交通量。按照以下步骤完成此活动:

  1. 导入所有必需的包并加载数据集:

    # import the required packages
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.wrappers.scikit_learn import KerasRegressor
    from sklearn.model_selection import KFold
    from sklearn.model_selection import cross_val_score
    from sklearn.preprocessing import StandardScaler
    from sklearn.pipeline import make_pipeline
    import numpy as np
    import pandas as pd
    from tensorflow import random
    
  2. 加载数据集,打印特征数据集的输入和输出大小,并打印目标数据集中的可能类别。同时,打印输出的范围:

    # Load the dataset
    # Load the dataset
    X = pd.read_csv('../data/traffic_volume_feats.csv')
    y = pd.read_csv('../data/traffic_volume_target.csv') 
    # Print the sizes of input data and output data
    print("Input data size = ", X.shape)
    print("Output size = ", y.shape)
    # Print the range for output
    print(f"Output Range = ({y['Volume'].min()}, \
    { y['Volume'].max()})")
    

    这是预期的输出:

    Input data size =  (10000, 10)
    Output size =  (10000, 1)
    Output Range = (0.000000, 584.000000)
    
  3. 定义三个函数,每个函数返回一个不同的 Keras 模型。第一个模型应有一个大小为 10的隐藏层,第二个模型应有两个大小为 10的隐藏层,第三个模型应有三个大小为 10的隐藏层。使用函数参数来传递优化器,以便它们可以传递给模型。目标是找出这三种模型中哪个能带来最低的交叉验证错误率:

    # Create the function that returns the keras model 1
    def build_model_1(optimizer='adam'):
        # create model 1
        model = Sequential()
        model.add(Dense(10, input_dim=X.shape[1], activation='relu'))
        model.add(Dense(1))
        # Compile model
        model.compile(loss='mean_squared_error', optimizer=optimizer)
        return model
    # Create the function that returns the keras model 2
    def build_model_2(optimizer='adam'):
        # create model 2
        model = Sequential()
        model.add(Dense(10, input_dim=X.shape[1], activation='relu'))
        model.add(Dense(10, activation='relu'))
        model.add(Dense(1))
        # Compile model
        model.compile(loss='mean_squared_error', optimizer=optimizer)
        return model
    # Create the function that returns the keras model 3
    def build_model_3(optimizer='adam'):
        # create model 3
        model = Sequential()
        model.add(Dense(10, input_dim=X.shape[1], activation='relu'))
        model.add(Dense(10, activation='relu'))
        model.add(Dense(10, activation='relu'))
        model.add(Dense(1))
        # Compile model
        model.compile(loss='mean_squared_error', optimizer=optimizer)
        return model
    
  4. 编写代码,循环遍历三个模型并执行5 折交叉验证。设置随机种子以确保模型可复现,并定义n_folds超参数。存储在训练模型时应用cross_val_score函数的结果:

    """
    define a seed for random number generator so the result will be reproducible
    """
    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    # determine the number of folds for k-fold cross-validation
    n_folds = 5
    # define the list to store cross-validation scores
    results_1 = []
    # define the possible options for the model
    models = [build_model_1, build_model_2, build_model_3]
    # loop over models
    for i in range(len(models)):
        # build the scikit-learn interface for the keras model
        regressor = KerasRegressor(build_fn=models[i], epochs=100, \
                                   batch_size=50, verbose=0, \
                                   shuffle=False)
        """
        build the pipeline of transformations so for each fold training 
        set will be scaled and test set will be scaled accordingly.
        """
        model = make_pipeline(StandardScaler(), regressor)
        # define the cross-validation iterator
        kfold = KFold(n_splits=n_folds, shuffle=True, \
                      random_state=seed)
        # perform the k-fold cross-validation. 
        # store the scores in result
        result = cross_val_score(model, X, y, cv=kfold)
        # add the scores to the results list 
        results_1.append(result)
    # Print cross-validation score for each model
    for i in range(len(models)):
        print("Model ", i+1," test error rate = ", \
              abs(results_1[i].mean()))
    

    以下是预期的输出:

    Model  1  test error rate =  25.48777518749237
    Model  2  test error rate =  25.30460816860199
    Model  3  test error rate =  25.390239462852474
    

    模型 2(一个两层神经网络)具有最低的测试错误率。

  5. 选择具有最低测试错误率的模型,并在迭代epochs = [80, 100]batches = [50, 25]时重复步骤 4,同时执行5 折交叉验证

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # determine the number of folds for k-fold cross-validation
    n_folds = 5
    # define the list to store cross-validation scores
    results_2 = []
    # define possible options for epochs and batch_size
    epochs = [80, 100]
    batches = [50, 25]
    # loop over all possible pairs of epochs, batch_size
    for i in range(len(epochs)):
        for j in range(len(batches)):
            # build the scikit-learn interface for the keras model
            regressor = KerasRegressor(build_fn=build_model_2, \
                                       epochs=epochs[i], \
                                       batch_size=batches[j], \
                                       verbose=0, shuffle=False)
            """
            build the pipeline of transformations so for each fold 
            training set will be scaled and test set will be scaled 
            accordingly.
            """
            model = make_pipeline(StandardScaler(), regressor)
            # define the cross-validation iterator
            kfold = KFold(n_splits=n_folds, shuffle=True, \
                          random_state=seed)
            # perform the k-fold cross-validation. 
            # store the scores in result
            result = cross_val_score(model, X, y, cv=kfold)
            # add the scores to the results list 
            results_2.append(result)
    """
    Print cross-validation score for each possible pair of epochs, batch_size
    """
    c = 0
    for i in range(len(epochs)):
        for j in range(len(batches)):
            print("batch_size = ", batches[j],\
                  ", epochs = ", epochs[i], \
                  ", Test error rate = ", abs(results_2[c].mean()))
            c += 1
    

    这是预期的输出:

    batch_size = 50 , epochs = 80 , Test error rate = 25.270704221725463
    batch_size = 25 , epochs = 80 , Test error rate = 25.309741401672362
    batch_size = 50 , epochs = 100 , Test error rate = 25.095393986701964
    batch_size = 25 , epochs = 100 , Test error rate = 25.24592453837395
    

    batch_size=5epochs=100的组合具有最低的测试错误率。

  6. 选择具有最高准确度的模型,并重复步骤 2,通过迭代optimizers = ['rmsprop', 'sgd', 'adam']并执行5 折交叉验证

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # determine the number of folds for k-fold cross-validation
    n_folds = 5
    # define the list to store cross-validation scores
    results_3 = []
    # define the possible options for the optimizer
    optimizers = ['adam', 'sgd', 'rmsprop']
    # loop over optimizers
    for i in range(len(optimizers)):
        optimizer=optimizers[i]
        # build the scikit-learn interface for the keras model
        regressor = KerasRegressor(build_fn=build_model_2, \
                                   epochs=100, batch_size=50, \
                                   verbose=0, shuffle=False)
        """
        build the pipeline of transformations so for each fold training 
        set will be scaled and test set will be scaled accordingly.
        """
        model = make_pipeline(StandardScaler(), regressor)
        # define the cross-validation iterator
        kfold = KFold(n_splits=n_folds, shuffle=True, \
                      random_state=seed)
        # perform the k-fold cross-validation. 
        # store the scores in result
        result = cross_val_score(model, X, y, cv=kfold)
        # add the scores to the results list 
        results_3.append(result)
    # Print cross-validation score for each optimizer
    for i in range(len(optimizers)):
        print("optimizer=", optimizers[i]," test error rate = ", \
              abs(results_3[i].mean()))
    

    这是预期的输出:

    optimizer= adam  test error rate =  25.391812739372256
    optimizer= sgd  test error rate =  25.140230269432067
    optimizer= rmsprop  test error rate =  25.217947859764102
    

    optimizer='sgd'具有最低的测试错误率,因此我们应继续使用这个模型。

    注意

    要访问此特定部分的源代码,请参考packt.live/31TcYaD

    你也可以在packt.live/3iq6iqb在线运行这个示例。

5. 改进模型准确性

活动 5.01:在 Avila 模式分类器上应用权重正则化

在此活动中,你将构建一个 Keras 模型,根据给定的网络架构和超参数值对 Avila 模式数据集进行分类。目标是对模型应用不同类型的权重正则化,即L1L2,并观察每种类型如何改变结果。按照以下步骤完成此活动:

  1. 加载数据集,并将数据集拆分为训练集测试集

    # Load the dataset
    import pandas as pd
    X = pd.read_csv('../data/avila-tr_feats.csv')
    y = pd.read_csv('../data/avila-tr_target.csv')
    """
    Split the dataset into training set and test set with a 0.8-0.2 ratio
    """
    from sklearn.model_selection import train_test_split
    seed = 1
    X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.2, random_state=seed)
    
  2. 定义一个包含三个隐藏层的 Keras 顺序模型,第一个隐藏层为 size 10,第二个隐藏层为 size 6,第三个隐藏层为 size 4。最后,编译模型:

    """
    define a seed for random number generator so the result will be reproducible
    """
    import numpy as np
    from tensorflow import random
    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model
    from keras.models import Sequential
    from keras.layers import Dense
    model_1 = Sequential()
    model_1.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu'))
    model_1.add(Dense(6, activation='relu'))
    model_1.add(Dense(4, activation='relu'))
    model_1.add(Dense(1, activation='sigmoid'))
    model_1.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    
  3. 将模型拟合到训练数据上以执行分类,并保存训练过程的结果:

    history=model_1.fit(X_train, y_train, batch_size = 20, epochs = 100, \
                        validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    
  4. 通过导入必要的库来绘制训练误差和测试误差的趋势,绘制损失和验证损失,并将它们保存在模型拟合训练过程时创建的变量中。打印出最大验证准确度:

    import matplotlib.pyplot as plt 
    import matplotlib
    %matplotlib inline 
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0) 
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
          max(history.history['val_accuracy']))
    

    以下是预期输出:

    图 5.13:训练过程中模型训练误差和验证误差的图    对于没有正则化的模型

    图 5.13:训练过程中模型在没有正则化情况下的训练误差和验证误差图

    验证损失随训练损失不断减少。尽管没有使用正则化,这仍然是一个相当不错的训练过程示例,因为偏差和方差都比较低。

  5. 重新定义模型,为每个隐藏层添加 L2 正则化器lambda=0.01。重复 步骤 3步骤 4 来训练模型并绘制 训练误差验证误差

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model with l2 regularization with lambda = 0.01
    from keras.regularizers import l2
    l2_param = 0.01
    model_2 = Sequential()
    model_2.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_2.add(Dense(6, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_2.add(Dense(4, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_2.add(Dense(1, activation='sigmoid'))
    model_2.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_2.fit(X_train, y_train, batch_size = 20, epochs = 100, \
                        validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
          max(history.history['val_accuracy']))
    

    以下是预期输出:

    图 5.14:训练过程中模型训练误差和验证误差的图    对于具有 L2 权重正则化(lambda=0.01)的模型

    图 5.14:训练过程中具有 L2 权重正则化(lambda=0.01)模型的训练误差和验证误差图

    从前面的图中可以看出,测试误差在降到一定程度后几乎趋于平稳。训练过程结束时训练误差和验证误差之间的差距(偏差)稍微缩小,这表明模型对训练样本的过拟合有所减少。

  6. 使用 lambda=0.1L2 参数 重复前一步骤——使用新的 lambda 参数重新定义模型,拟合模型到训练数据,并重复 步骤 4 绘制训练误差和验证误差:

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    from keras.regularizers import l2
    l2_param = 0.1
    model_3 = Sequential()
    model_3.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_3.add(Dense(6, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_3.add(Dense(4, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_3.add(Dense(1, activation='sigmoid'))
    model_3.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_3.fit(X_train, y_train, batch_size = 20, \
                        epochs = 100, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
          max(history.history['val_accuracy']))
    

    以下是预期输出:

    图 5.15:训练过程中模型训练误差和验证误差的图    对于具有 L2 权重正则化(lambda=0.1)的模型

    图 5.15:训练过程中具有 L2 权重正则化(lambda=0.1)模型的训练误差和验证误差图

    训练和验证误差迅速达到平稳状态,且远高于我们使用较低 L2 参数 创建的模型,这表明我们对模型的惩罚过多,导致它没有足够的灵活性去学习训练数据的潜在函数。接下来,我们将减少正则化参数的值,以防止它对模型造成过多惩罚。

  7. 重复前一步骤,这次使用 lambda=0.005。重复 步骤 4 绘制训练误差和验证误差:

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model with l2 regularization with lambda = 0.05
    from keras.regularizers import l2
    l2_param = 0.005
    model_4 = Sequential()
    model_4.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_4.add(Dense(6, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_4.add(Dense(4, activation='relu', \
                      kernel_regularizer=l2(l2_param)))
    model_4.add(Dense(1, activation='sigmoid'))
    model_4.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_4.fit(X_train, y_train, batch_size = 20, \
                        epochs = 100, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
          max(history.history['val_accuracy'])) 
    

    以下是预期的输出:

    图 5.16:带有 L2 权重正则化(lambda=0.005)的模型在训练过程中训练误差和验证误差的图示

    图 5.16:带有 L2 权重正则化(lambda=0.005)的模型在训练过程中训练误差和验证误差的图示

    L2 权重 正则化的值在所有使用 L2 正则化 的模型中,在验证数据上评估时,获得了最高的准确度,但稍微低于没有正则化时的准确度。同样,在被减少到某个值之后,测试误差并没有显著增加,这表明模型没有过拟合训练样本。看起来 lambda=0.005L2 权重正则化 获得了最低的验证误差,同时防止了模型的过拟合。

  8. 向模型的隐藏层添加 L1 正则化器,其中 lambda=0.01。重新定义模型,使用新的 lambda 参数,拟合模型到训练数据,并重复 步骤 4 来绘制训练误差和验证误差:

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model with l1 regularization with lambda = 0.01
    from keras.regularizers import l1
    l1_param = 0.01
    model_5 = Sequential()
    model_5.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_5.add(Dense(6, activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_5.add(Dense(4, activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_5.add(Dense(1, activation='sigmoid'))
    model_5.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_5.fit(X_train, y_train, batch_size = 20, \
                        epochs = 100, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=True)
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
          max(history.history['val_accuracy']))
    

    以下是预期的输出:

    图 5.17:训练过程中训练误差和验证误差的图示    对于带有 L1 权重正则化(lambda=0.01)的模型

    图 5.17:带有 L1 权重正则化(lambda=0.01)的模型在训练过程中训练误差和验证误差的图示

  9. 重复之前的步骤,将 lambda=0.005 应用于 L1 参数—使用新的 lambda 参数重新定义模型,拟合模型到训练数据,并重复 步骤 4 来绘制 训练误差验证误差

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # define the keras model with l1 regularization with lambda = 0.1
    from keras.regularizers import l1
    l1_param = 0.005
    model_6 = Sequential()
    model_6.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_6.add(Dense(6, activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_6.add(Dense(4, activation='relu', \
                      kernel_regularizer=l1(l1_param)))
    model_6.add(Dense(1, activation='sigmoid'))
    model_6.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_6.fit(X_train, y_train, batch_size = 20, \
                        epochs = 100, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0) 
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
           max(history.history['val_accuracy']))
    

    以下是预期的输出:

    图 5.18:带有 L1 权重正则化(lambda=0.005)的模型在训练过程中训练误差和验证误差的图示

    图 5.18:带有 L1 权重正则化(lambda=0.005)的模型在训练过程中训练误差和验证误差的图示

    看起来 lambda=0.005L1 权重正则化 在防止模型过拟合的同时,获得了更好的测试误差,因为 lambda=0.01 的值过于严格,导致模型无法学习训练数据的潜在函数。

  10. 向模型的隐藏层添加 L1L2 正则化器,其中 L1lambda=0.005L2lambda = 0.005。然后,重复 步骤 4 来绘制训练误差和验证误差:

    """
    set up a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    """
    define the keras model with l1_l2 regularization with l1_lambda = 0.005 and l2_lambda = 0.005
    """
    from keras.regularizers import l1_l2
    l1_param = 0.005
    l2_param = 0.005
    model_7 = Sequential()
    model_7.add(Dense(10, input_dim=X_train.shape[1], \
                activation='relu', \
                kernel_regularizer=l1_l2(l1=l1_param, l2=l2_param)))
    model_7.add(Dense(6, activation='relu', \
                      kernel_regularizer=l1_l2(l1=l1_param, \
                                               l2=l2_param)))
    model_7.add(Dense(4, activation='relu', \
                      kernel_regularizer=l1_l2(l1=l1_param, \
                                               l2=l2_param)))
    model_7.add(Dense(1, activation='sigmoid'))
    model_7.compile(loss='binary_crossentropy', optimizer='sgd', \
                    metrics=['accuracy'])
    # train the model using training set while evaluating on test set
    history=model_7.fit(X_train, y_train, batch_size = 20, \
                        epochs = 100, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=True)
    
    # plot training error and test error
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim(0,1)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Best Accuracy on Validation Set =", \
           max(history.history['val_accuracy']))
    

    以下是预期的输出:

    图 5.19:带有 L1 lambda 等于 0.005 和 L2 lambda 等于 0.005 的模型在训练过程中训练误差和验证误差的图示

图 5.19:带有 L1 lambda 等于 0.005 和 L2 lambda 等于 0.005 的模型在训练过程中训练误差和验证误差的图示

尽管L1L2 正则化成功防止了模型的过拟合,但模型的方差非常低。然而,验证数据上的准确率并不像没有正则化训练的模型,或者使用L2 正则化 lambda=0.005L1 正则化 lambda=0.005参数单独训练的模型那样高。

注意

要访问此特定部分的源代码,请参考packt.live/31BUf34

你也可以在packt.live/38n291s上在线运行这个示例。

活动 5.02:在交通量数据集上使用 dropout 正则化

在这个活动中,你将从活动 4.03使用交叉验证对交通量数据集进行模型选择第四章使用 Keras 包装器进行交叉验证评估模型开始。你将使用训练集/测试集方法来训练和评估模型,绘制训练误差和泛化误差的趋势,并观察模型对数据示例的过拟合情况。然后,你将尝试通过使用 dropout 正则化来解决过拟合问题,从而提高模型的性能。特别地,你将尝试找出应该在模型的哪些层添加 dropout 正则化,以及什么rate值能够最大程度地提高该特定模型的性能。按照以下步骤完成这个练习:

  1. 使用 pandas 的read_csv函数加载数据集,使用train_test_split将数据集按80-20比例划分为训练集和测试集,并使用StandardScaler对输入数据进行标准化:

    # Load the dataset
    import pandas as pd
    X = pd.read_csv('../data/traffic_volume_feats.csv')
    y = pd.read_csv('../data/traffic_volume_target.csv')
    """
    Split the dataset into training set and test set with an 80-20 ratio
    """
    from sklearn.model_selection import train_test_split
    seed=1
    X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.2, random_state=seed)
    
  2. 设置一个随机种子,以便模型可以复现。接下来,定义一个包含两个size 10的隐藏层的 Keras 顺序模型,每个隐藏层都使用ReLU 激活函数。添加一个没有激活函数的输出层,并使用给定的超参数编译模型:

    """
    define a seed for random number generator so the result will be reproducible
    """
    import numpy as np
    from tensorflow import random
    np.random.seed(seed)
    random.set_seed(seed)
    from keras.models import Sequential
    from keras.layers import Dense
    # create model
    model_1 = Sequential()
    model_1.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu'))
    model_1.add(Dense(10, activation='relu'))
    model_1.add(Dense(1))
    # Compile model
    model_1.compile(loss='mean_squared_error', optimizer='rmsprop')
    
  3. 使用给定的超参数训练模型:

    # train the model using training set while evaluating on test set
    history=model_1.fit(X_train, y_train, batch_size = 50, \
                        epochs = 200, validation_data=(X_test, y_test), \
                        verbose=0) 
    
  4. 绘制训练误差测试误差的趋势。打印在训练集和验证集上达到的最佳准确率:

    import matplotlib.pyplot as plt 
    import matplotlib
    %matplotlib inline 
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0) 
    # plot training error and test error plots 
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim((0, 25000))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Lowest error on training set = ", \
          min(history.history['loss']))
    print("Lowest error on validation set = ", \
          min(history.history['val_loss']))
    

    以下是预期的输出:

    Lowest error on training set =  24.673954981565476
    Lowest error on validation set =  25.11553382873535
    

    图 5.20:训练过程中训练误差和验证误差的图表    对于没有正则化的模型

    图 5.20:没有正则化的模型在训练过程中训练误差和验证误差的图表

    在训练误差和验证误差值中,训练误差和验证误差之间的差距非常小,这表明模型的方差很低,这是一个好兆头。

  5. 重新定义模型,创建相同的模型架构。然而,这一次,在模型的第一个隐藏层添加rate=0.1的 dropout 正则化。重复步骤 3,使用训练数据训练模型,并重复步骤 4绘制训练误差和验证误差的趋势。然后,打印在验证集上达到的最佳准确率:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    from keras.layers import Dropout
    # create model
    model_2 = Sequential()
    model_2.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu'))
    model_2.add(Dropout(0.1))
    model_2.add(Dense(10, activation='relu'))
    model_2.add(Dense(1))
    # Compile model
    model_2.compile(loss='mean_squared_error', \
                    optimizer='rmsprop')
    # train the model using training set while evaluating on test set
    history=model_2.fit(X_train, y_train, batch_size = 50, \
                        epochs = 200, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim((0, 25000))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Lowest error on training set = ", \
          min(history.history['loss']))
    print("Lowest error on validation set = ", \
          min(history.history['val_loss']))
    

    以下是预期输出:

    Lowest error on training set =  407.8203821182251
    Lowest error on validation set =  54.58488750457764
    

    ![图 5.21:在使用 dropout 正则化(第一层 rate=0.1)训练模型时,训练误差和验证误差的曲线图]

    ](github.com/OpenDocCN/f…)

    图 5.21:在使用 dropout 正则化(第一层 rate=0.1)训练模型时,训练误差和验证误差的曲线图

    训练误差和验证误差之间存在小的差距;然而,验证误差低于训练误差,表明模型没有对训练数据发生过拟合。

  6. 重复上一步,这次为模型的两个隐藏层添加rate=0.1的 dropout 正则化。重复步骤 3,在训练数据上训练模型,并重复步骤 4,绘制训练误差和验证误差的趋势。然后,打印在验证集上达到的最佳准确率:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # create model
    model_3 = Sequential()
    model_3.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu'))
    model_3.add(Dropout(0.1))
    model_3.add(Dense(10, activation='relu'))
    model_3.add(Dropout(0.1))
    model_3.add(Dense(1))
    # Compile model
    model_3.compile(loss='mean_squared_error', \
                    optimizer='rmsprop')
    # train the model using training set while evaluating on test set
    history=model_3.fit(X_train, y_train, batch_size = 50, \
                        epochs = 200, validation_data=(X_test, y_test), \
                        verbose=0, shuffle=False)
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim((0, 25000))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Lowest error on training set = ", \
          min(history.history['loss']))
    print("Lowest error on validation set = ", \
          min(history.history['val_loss']))
    

    以下是预期输出:

    Lowest error on training set =  475.9299939632416
    Lowest error on validation set =  61.646054649353026
    

    ![图 5.22:在使用 dropout 正则化(rate=0.1)训练模型时,训练误差和验证误差的曲线图]

    使用 dropout 正则化(rate=0.1)在两个层上的模型

    ](github.com/OpenDocCN/f…)

    图 5.22:在使用 dropout 正则化(rate=0.1)训练模型时,训练误差和验证误差的曲线图

    这里训练误差和验证误差之间的差距略有增大,主要是由于在模型第二个隐藏层上增加了正则化,导致训练误差的增加。

  7. 重复上一步,这次为模型的第一层添加rate=0.2的 dropout 正则化,为第二层添加rate=0.1的 dropout 正则化。重复步骤 3,在训练数据上训练模型,并重复步骤 4,绘制训练误差和验证误差的趋势。然后,打印在验证集上达到的最佳准确率:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # create model
    model_4 = Sequential()
    model_4.add(Dense(10, input_dim=X_train.shape[1], \
                      activation='relu'))
    model_4.add(Dropout(0.2))
    model_4.add(Dense(10, activation='relu'))
    model_4.add(Dropout(0.1))
    model_4.add(Dense(1))
    # Compile model
    model_4.compile(loss='mean_squared_error', optimizer='rmsprop')
    # train the model using training set while evaluating on test set
    history=model_4.fit(X_train, y_train, batch_size = 50, epochs = 200, \
                        validation_data=(X_test, y_test), verbose=0)
    matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.ylim((0, 25000))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train loss', 'validation loss'], loc='upper right')
    # print the best accuracy reached on the test set
    print("Lowest error on training set = ", \
          min(history.history['loss']))
    print("Lowest error on validation set = ", \
          min(history.history['val_loss']))
    

    以下是预期输出:

    Lowest error on training set =  935.1562484741211
    Lowest error on validation set =  132.39965686798095
    

    ![图 5.23:在使用 dropout 正则化(第一层 rate=0.2,第二层 rate=0.1)训练模型时,训练误差和验证误差的曲线图]

    ](github.com/OpenDocCN/f…)

图 5.23:在使用 dropout 正则化(第一层 rate=0.2,第二层 rate=0.1)训练模型时,训练误差和验证误差的曲线图

由于正则化的增加,训练误差和验证误差之间的差距略有增大。在这种情况下,原始模型没有发生过拟合。因此,正则化增加了训练和验证数据集上的误差率。

注意

若要查看此特定部分的源代码,请参考packt.live/38mtDo7

你也可以在packt.live/31Isdmu上在线运行这个示例。

活动 5.03:Avila 模式分类器的超参数调整

在本次活动中,你将构建一个类似于前几次活动中的 Keras 模型,但这次你将向模型中添加正则化方法。然后,你将使用 scikit-learn 优化器对模型的超参数进行调优,包括正则化器的超参数。按照以下步骤完成本次活动:

  1. 加载数据集并导入库:

    # Load The dataset
    import pandas as pd
    X = pd.read_csv('../data/avila-tr_feats.csv')
    y = pd.read_csv('../data/avila-tr_target.csv')
    
  2. 定义一个函数,返回一个 Keras 模型,该模型具有三层隐藏层,第一层大小为10,第二层大小为6,第三层大小为4,并在每个隐藏层上应用L2 权重正则化ReLU 激活函数。使用给定的参数编译模型并返回模型:

    # Create the function that returns the keras model
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.regularizers import l2
    def build_model(lambda_parameter):
        model = Sequential()
        model.add(Dense(10, input_dim=X.shape[1], \
                        activation='relu', \
                        kernel_regularizer=l2(lambda_parameter)))
        model.add(Dense(6, activation='relu', \
                        kernel_regularizer=l2(lambda_parameter)))
        model.add(Dense(4, activation='relu', \
                        kernel_regularizer=l2(lambda_parameter)))
        model.add(Dense(1, activation='sigmoid'))
        model.compile(loss='binary_crossentropy', \
                      optimizer='sgd', metrics=['accuracy'])
        return model
    
  3. 设置随机种子,使用 scikit-learn 封装器对我们在上一步中创建的模型进行封装,并定义要扫描的超参数。最后,使用超参数网格对模型执行GridSearchCV()并拟合模型:

    from keras.wrappers.scikit_learn import KerasClassifier
    from sklearn.model_selection import GridSearchCV
    """
    define a seed for random number generator so the result will be reproducible
    """
    import numpy as np
    from tensorflow import random
    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    # create the Keras wrapper with scikit learn
    model = KerasClassifier(build_fn=build_model, verbose=0, \
                            shuffle=False)
    # define all the possible values for each hyperparameter
    lambda_parameter = [0.01, 0.5, 1]
    epochs = [50, 100]
    batch_size = [20]
    """
    create the dictionary containing all possible values of hyperparameters
    """
    param_grid = dict(lambda_parameter=lambda_parameter, \
                      epochs=epochs, batch_size=batch_size)
    # perform 5-fold cross-validation for ??????? store the results
    grid_seach = GridSearchCV(estimator=model, \
                              param_grid=param_grid, cv=5)
    results_1 = grid_seach.fit(X, y)
    
  4. 打印在拟合过程中我们创建的变量中存储的最佳交叉验证分数的结果。遍历所有参数并打印各折的准确率均值、准确率标准差以及参数本身:

    print("Best cross-validation score =", results_1.best_score_)
    print("Parameters for Best cross-validation score=", \
          results_1.best_params_)
    # print the results for all evaluated hyperparameter combinations
    accuracy_means = results_1.cv_results_['mean_test_score']
    accuracy_stds = results_1.cv_results_['std_test_score']
    parameters = results_1.cv_results_['params']
    for p in range(len(parameters)):
        print("Accuracy %f (std %f) for params %r" % \
              (accuracy_means[p], accuracy_stds[p], parameters[p]))
    

    以下是预期的输出:

    Best cross-validation score = 0.7673058390617371
    Parameters for Best cross-validation score= {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.01}
    Accuracy 0.764621 (std 0.004330) for params {'batch_size': 20, 
    'epochs': 50, 'lambda_parameter': 0.01}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 50, 'lambda_parameter': 0.5}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 50, 'lambda_parameter': 1}
    Accuracy 0.767306 (std 0.015872) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.01}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.5}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 1}
    
  5. 重复步骤 3,使用GridSearchCV()lambda_parameter = [0.001, 0.01, 0.05, 0.1]batch_size = [20]epochs = [100]。使用5 折交叉验证对模型进行拟合,并打印整个网格的结果:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # create the Keras wrapper with scikit learn
    model = KerasClassifier(build_fn=build_model, verbose=0, shuffle=False)
    # define all the possible values for each hyperparameter
    lambda_parameter = [0.001, 0.01, 0.05, 0.1]
    epochs = [100]
    batch_size = [20]
    """
    create the dictionary containing all possible values of hyperparameters
    """
    param_grid = dict(lambda_parameter=lambda_parameter, \
                      epochs=epochs, batch_size=batch_size)
    """
    search the grid, perform 5-fold cross-validation for each possible combination, store the results
    """
    grid_seach = GridSearchCV(estimator=model, \
                              param_grid=param_grid, cv=5)
    results_2 = grid_seach.fit(X, y)
    # print the results for best cross-validation score
    print("Best cross-validation score =", results_2.best_score_)
    print("Parameters for Best cross-validation score =", \
          results_2.best_params_)
    # print the results for the entire grid
    accuracy_means = results_2.cv_results_['mean_test_score']
    accuracy_stds = results_2.cv_results_['std_test_score']
    parameters = results_2.cv_results_['params']
    for p in range(len(parameters)):
        print("Accuracy %f (std %f) for params %r" % \
              (accuracy_means[p], accuracy_stds[p], parameters[p]))
    

    以下是预期的输出:

    Best cross-validation score = 0.786385428905487
    Parameters for Best cross-validation score = {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.001}
    Accuracy 0.786385 (std 0.010177) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.001}
    Accuracy 0.693960 (std 0.084994) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.01}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.05}
    Accuracy 0.589070 (std 0.008244) for params {'batch_size': 20, 
    'epochs': 100, 'lambda_parameter': 0.1}
    
  6. 重新定义一个函数,返回一个 Keras 模型,该模型具有三层隐藏层,第一层大小为10,第二层大小为6,第三层大小为4,并在每个隐藏层上应用dropout 正则化ReLU 激活函数。使用给定的参数编译模型并从函数中返回:

    # Create the function that returns the keras model
    from keras.layers import Dropout
    def build_model(rate):
        model = Sequential()
        model.add(Dense(10, input_dim=X.shape[1], activation='relu'))
        model.add(Dropout(rate))
        model.add(Dense(6, activation='relu'))
        model.add(Dropout(rate))
        model.add(Dense(4, activation='relu'))
        model.add(Dropout(rate))
        model.add(Dense(1, activation='sigmoid'))
        model.compile(loss='binary_crossentropy', \
                      optimizer='sgd', metrics=['accuracy'])
        return model
    
  7. 使用rate = [0, 0.1, 0.2]epochs = [50, 100],对模型进行GridSearchCV()调优。使用5 折交叉验证对模型进行拟合,并打印整个网格的结果:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # create the Keras wrapper with scikit learn
    model = KerasClassifier(build_fn=build_model, verbose=0,shuffle=False)
    # define all the possible values for each hyperparameter
    rate = [0, 0.1, 0.2]
    epochs = [50, 100]
    batch_size = [20]
    """
    create the dictionary containing all possible values of hyperparameters
    """
    param_grid = dict(rate=rate, epochs=epochs, batch_size=batch_size)
    """
    perform 5-fold cross-validation for 10 randomly selected combinations, store the results
    """
    grid_seach = GridSearchCV(estimator=model, \
                              param_grid=param_grid, cv=5)
    results_3 = grid_seach.fit(X, y)
    # print the results for best cross-validation score
    print("Best cross-validation score =", results_3.best_score_)
    print("Parameters for Best cross-validation score =", \
          results_3.best_params_)
    # print the results for the entire grid
    accuracy_means = results_3.cv_results_['mean_test_score']
    accuracy_stds = results_3.cv_results_['std_test_score']
    parameters = results_3.cv_results_['params']
    for p in range(len(parameters)):
        print("Accuracy %f (std %f) for params %r" % \
              (accuracy_means[p], accuracy_stds[p], parameters[p]))
    

    以下是预期的输出:

    Best cross-validation score= 0.7918504476547241
    Parameters for Best cross-validation score= {'batch_size': 20, 
    'epochs': 100, 'rate': 0}
    Accuracy 0.786769 (std 0.008255) for params {'batch_size': 20, 
    'epochs': 50, 'rate': 0}
    Accuracy 0.764717 (std 0.007691) for params {'batch_size': 20, 
    'epochs': 50, 'rate': 0.1}
    Accuracy 0.752637 (std 0.013546) for params {'batch_size': 20, 
    'epochs': 50, 'rate': 0.2}
    Accuracy 0.791850 (std 0.008519) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0}
    Accuracy 0.779291 (std 0.009504) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0.1}
    Accuracy 0.767306 (std 0.005773) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0.2}
    
  8. 重复步骤 5,使用rate = [0.0, 0.05, 0.1]epochs = [100]。使用5 折交叉验证对模型进行拟合,并打印整个网格的结果:

    """
    define a seed for random number generator so the result will be reproducible
    """
    np.random.seed(seed)
    random.set_seed(seed)
    # create the Keras wrapper with scikit learn
    model = KerasClassifier(build_fn=build_model, verbose=0, shuffle=False)
    # define all the possible values for each hyperparameter
    rate = [0.0, 0.05, 0.1]
    epochs = [100]
    batch_size = [20]
    """
    create the dictionary containing all possible values of hyperparameters
    """
    param_grid = dict(rate=rate, epochs=epochs, batch_size=batch_size)
    """
    perform 5-fold cross-validation for 10 randomly selected combinations, store the results
    """
    grid_seach = GridSearchCV(estimator=model, \
                              param_grid=param_grid, cv=5)
    results_4 = grid_seach.fit(X, y)
    # print the results for best cross-validation score
    print("Best cross-validation score =", results_4.best_score_)
    print("Parameters for Best cross-validation score =", \
          results_4.best_params_)
    # print the results for the entire grid
    accuracy_means = results_4.cv_results_['mean_test_score']
    accuracy_stds = results_4.cv_results_['std_test_score']
    parameters = results_4.cv_results_['params']
    for p in range(len(parameters)):
        print("Accuracy %f (std %f) for params %r" % \
              (accuracy_means[p], accuracy_stds[p], parameters[p]))
    

    以下是预期的输出:

    Best cross-validation score= 0.7862895488739013
    Parameters for Best cross-validation score= {'batch_size': 20, 
    'epochs': 100, 'rate': 0.0}
    Accuracy 0.786290 (std 0.013557) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0.0}
    Accuracy 0.786098 (std 0.005184) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0.05}
    Accuracy 0.772004 (std 0.013733) for params {'batch_size': 20, 
    'epochs': 100, 'rate': 0.1}
    

    注意

    要访问此特定部分的源代码,请参考packt.live/2D7HN0L

    本节目前没有在线互动示例,需要在本地运行。

6. 模型评估

活动 6.01:当我们改变训练/测试数据集的拆分时,计算神经网络的准确率和空准确率

在这个活动中,我们将看到null accuracyaccuracy会受到train/test划分的影响。为实现这一点,需要修改定义训练/测试划分的代码部分。我们将使用在练习 6.02中使用的相同数据集,即使用 Scania 卡车数据计算准确率和零准确率。按照以下步骤完成此活动:

  1. 导入所需的库。使用 pandas 的read_csv函数加载数据集,并查看数据集的前行:

    # Import the libraries
    import numpy as np
    import pandas as pd
    # Load the Data
    X = pd.read_csv("../data/aps_failure_training_feats.csv")
    y = pd.read_csv("../data/aps_failure_training_target.csv")
    # Use the head function to get a glimpse data
    X.head()
    

    下表显示了前面代码的输出:

    图 6.13:数据集的初始五行

    图 6.13:数据集的初始五行

  2. test_sizerandom_state0.2042分别更改为0.313

    # Split the data into training and testing sets
    from sklearn.model_selection import train_test_split
    seed = 13
    X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.3, random_state=seed)
    

    注意

    如果使用不同的random_state,可能会得到不同的train/test划分,这可能会导致稍微不同的最终结果。

  3. 使用StandardScaler函数对数据进行缩放,并使用缩放器对测试数据进行缩放。将两者转换为 pandas DataFrame:

    # Initialize StandardScaler
    from sklearn.preprocessing import StandardScaler
    sc = StandardScaler()
    # Transform the training data
    X_train = sc.fit_transform(X_train)
    X_train = pd.DataFrame(X_train, columns=X_test.columns)
    # Transform the testing data
    X_test = sc.transform(X_test)
    X_test = pd.DataFrame(X_test, columns = X_train.columns)
    

    注意

    sc.fit_transform()函数转换数据,同时数据也被转换为NumPy数组。我们可能稍后需要将数据作为 DataFrame 对象进行分析,因此使用pd.DataFrame()函数将数据重新转换为 DataFrame。

  4. 导入构建神经网络架构所需的库:

    # Import the relevant Keras libraries
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Dropout
    from tensorflow import random
    
  5. 启动Sequential类:

    # Initiate the Model with Sequential Class
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  6. 向网络中添加五个Dense层,并使用Dropout。设置第一个隐藏层的大小为64,丢弃率为0.5;第二个隐藏层的大小为32,丢弃率为0.4;第三个隐藏层的大小为16,丢弃率为0.3;第四个隐藏层的大小为8,丢弃率为0.2;最后一个隐藏层的大小为4,丢弃率为0.1。将所有激活函数设置为ReLU

    # Add the hidden dense layers and with dropout Layer
    model.add(Dense(units=64, activation='relu', \
                    kernel_initializer='uniform', \
                    input_dim=X_train.shape[1]))
    model.add(Dropout(rate=0.5))
    model.add(Dense(units=32, activation='relu', \
                    kernel_initializer='uniform', \
                    input_dim=X_train.shape[1]))
    model.add(Dropout(rate=0.4))
    model.add(Dense(units=16, activation='relu', \
                    kernel_initializer='uniform', \
                    input_dim=X_train.shape[1]))
    model.add(Dropout(rate=0.3))
    model.add(Dense(units=8, activation='relu', \
                    kernel_initializer='uniform', \
                    input_dim=X_train.shape[1]))
    model.add(Dropout(rate=0.2))
    model.add(Dense(units=4, activation='relu', \
                    kernel_initializer='uniform'))
    model.add(Dropout(rate=0.1))
    
  7. 添加一个输出Dense层,并使用sigmoid激活函数:

    # Add Output Dense Layer
    model.add(Dense(units=1, activation='sigmoid', \
                    kernel_initializer='uniform'))
    

    注意

    由于输出是二分类的,我们使用sigmoid函数。如果输出是多类的(即超过两个类别),则应使用softmax函数。

  8. 编译网络并拟合模型。这里使用的度量标准是accuracy

    # Compile the Model
    model.compile(optimizer='adam', loss='binary_crossentropy', \
                  metrics=['accuracy'])
    

    注意

    在我们这例中,度量标准是accuracy,已在前面的代码中定义。

  9. 使用100轮训练、批量大小为20、验证集划分比例为0.2来训练模型:

    # Fit the Model
    model.fit(X_train, y_train, epochs=100, batch_size=20, \
              verbose=1, validation_split=0.2, shuffle=False)
    
  10. 在测试数据集上评估模型,并打印出lossaccuracy的值:

    test_loss, test_acc = model.evaluate(X_test, y_test)
    print(f'The loss on the test set is {test_loss:.4f} and \
    the accuracy is {test_acc*100:.4f}%')
    

    前面的代码产生以下输出:

    18000/18000 [==============================] - 0s 19us/step
    The loss on the test set is 0.0766 and the accuracy is 98.9833%
    

    模型返回的准确率为98.9833%。但这足够好吗?我们只能通过与零准确率进行比较来回答这个问题。

  11. 现在,计算空准确率。空准确率可以使用pandas库的value_count函数计算,之前在本章的练习 6.01中,计算太平洋飓风数据集上的空准确率时,我们已经用过这个函数:

    # Use the value_count function to calculate distinct class values
    y_test['class'].value_counts()
    

    上述代码将产生以下输出:

    0    17700
    1      300
    Name: class, dtype: int64
    
  12. 计算空准确率

    # Calculate the null accuracy
    y_test['class'].value_counts(normalize=True).loc[0]
    

    上述代码将产生以下输出:

    0.9833333333333333
    

    注意

    要访问此特定部分的源代码,请参阅packt.live/3eY7y1E

    你也可以在packt.live/2BzBO4n在线运行此示例。

活动 6.02:计算 ROC 曲线和 AUC 得分

ROC 曲线AUC 得分是一种有效的方式,能够轻松评估二分类器的性能。在这个活动中,我们将绘制ROC 曲线并计算模型的AUC 得分。我们将使用相同的数据集并训练与练习 6.03中相同的模型,基于混淆矩阵推导和计算指标。继续使用相同的 APS 故障数据,绘制ROC 曲线并计算模型的AUC 得分。按照以下步骤完成这个活动:

  1. 导入必要的库,并使用 pandas 的read_csv函数加载数据:

    # Import the libraries
    import numpy as np
    import pandas as pd
    # Load the Data
    X = pd.read_csv("../data/aps_failure_training_feats.csv")
    y = pd.read_csv("../data/aps_failure_training_target.csv")
    
  2. 使用train_test_split函数将数据集分割为训练集和测试集:

    from sklearn.model_selection import train_test_split
    seed = 42
    X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.20, random_state=seed)
    
  3. 使用StandardScaler函数对特征数据进行缩放,使其具有0均值1标准差。对训练数据进行拟合,并将其应用于测试数据

    from sklearn.preprocessing import StandardScaler
    sc = StandardScaler()
    # Transform the training data
    X_train = sc.fit_transform(X_train)
    X_train = pd.DataFrame(X_train,columns=X_test.columns)
    # Transform the testing data
    X_test = sc.transform(X_test)
    X_test = pd.DataFrame(X_test,columns=X_train.columns)
    
  4. 导入创建模型所需的 Keras 库。实例化一个Sequential类的 Keras 模型,并向模型中添加五个隐藏层,包括每层的丢弃层。第一个隐藏层应具有64的大小和0.5的丢弃率。第二个隐藏层应具有32的大小和0.4的丢弃率。第三个隐藏层应具有16的大小和0.3的丢弃率。第四个隐藏层应具有8的大小和0.2的丢弃率。最后一个隐藏层应具有4的大小和0.1的丢弃率。所有隐藏层应具有ReLU 激活函数,并将kernel_initializer = 'uniform'。在模型中添加一个最终的输出层,并使用 sigmoid 激活函数。通过计算训练过程中准确率指标来编译模型:

    # Import the relevant Keras libraries
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Dropout
    from tensorflow import random
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    # Add the hidden dense layers with dropout Layer
    model.add(Dense(units=64, activation='relu', \
                    kernel_initializer='uniform', \
                    input_dim=X_train.shape[1]))
    model.add(Dropout(rate=0.5))
    model.add(Dense(units=32, activation='relu', \
                    kernel_initializer='uniform'))
    model.add(Dropout(rate=0.4))
    model.add(Dense(units=16, activation='relu', \
                    kernel_initializer='uniform'))
    model.add(Dropout(rate=0.3))
    model.add(Dense(units=8, activation='relu', \
              kernel_initializer='uniform'))
    model.add(Dropout(rate=0.2))
    model.add(Dense(units=4, activation='relu', \
                    kernel_initializer='uniform'))
    model.add(Dropout(rate=0.1))
    # Add Output Dense Layer
    model.add(Dense(units=1, activation='sigmoid', \
                    kernel_initializer='uniform'))
    # Compile the Model
    model.compile(optimizer='adam', loss='binary_crossentropy', \
                  metrics=['accuracy'])
    
  5. 使用100个训练周期、batch_size=20validation_split=0.2将模型拟合到训练数据:

    model.fit(X_train, y_train, epochs=100, batch_size=20, \
              verbose=1, validation_split=0.2, shuffle=False)
    
  6. 一旦模型完成了对训练数据的拟合,创建一个变量,该变量是模型对测试数据的预测结果,使用模型的predict_proba方法:

    y_pred_prob = model.predict_proba(X_test)
    
  7. 从 scikit-learn 导入roc_curve并运行以下代码:

    from sklearn.metrics import roc_curve
    fpr, tpr, thresholds = roc_curve(y_test, y_pred_prob)
    

    fpr = 假阳性率(1 - 特异性)

    tpr = 真阳性率(灵敏度)

    thresholds = y_pred_prob的阈值

  8. 运行以下代码使用matplotlib.pyplot绘制ROC 曲线

    import matplotlib.pyplot as plt
    plt.plot(fpr, tpr)
    plt.title("ROC Curve for APS Failure")
    plt.xlabel("False Positive rate (1-Specificity)")
    plt.ylabel("True Positive rate (Sensitivity)")
    plt.grid(True)
    plt.show()
    

    以下图表显示了前述代码的输出:

    图 6.14:APS 失败数据集的 ROC 曲线

    ](tos-cn-i-73owjymdk6/dd022a541b284c1880d88fd5f099c857)

    图 6.14:APS 失败数据集的 ROC 曲线

  9. 使用roc_auc_score函数计算 AUC 分数:

    from sklearn.metrics import roc_auc_score
    roc_auc_score(y_test,y_pred_prob)
    

    以下是前述代码的输出:

    0.944787151628455
    

    94.4479%的 AUC 分数表明我们的模型表现优秀,符合上面列出的普遍可接受的AUC 分数标准。

    注意

    若要访问此特定部分的源代码,请参考packt.live/2NUOgyh

    你也可以在packt.live/2As33NH在线运行此示例。

7. 卷积神经网络的计算机视觉

活动 7.01:通过多个层和使用 softmax 来修改我们的模型

让我们尝试提高图像分类算法的性能。有很多方法可以提升性能,其中最直接的一种方式就是向模型中添加多个 ANN 层,这将在本活动中讲解。我们还将把激活函数从 sigmoid 更改为 softmax。然后,我们可以将结果与之前练习的结果进行比较。按照以下步骤完成此活动:

  1. 导入numpy库以及必要的 Keras 库和类:

    # Import the Libraries 
    from keras.models import Sequential
    from keras.layers import Conv2D, MaxPool2D, Flatten, Dense
    import numpy as np
    from tensorflow import random
    
  2. 现在,使用Sequential类初始化模型:

    # Initiate the classifier
    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    classifier=Sequential()
    
  3. 添加 CNN 的第一层,设置输入形状为(64, 64, 3),即每个图像的维度,并设置激活函数为 ReLU。然后,添加32个大小为(3, 3)的特征检测器。再添加两层卷积层,每层有32个大小为(3, 3)的特征检测器,且都使用ReLU 激活函数:

    classifier.add(Conv2D(32,(3,3),input_shape=(64,64,3),\
                   activation='relu'))
    classifier.add(Conv2D(32,(3,3),activation = 'relu'))
    classifier.add(Conv2D(32,(3,3),activation = 'relu'))
    

    32, (3, 3)表示有32个大小为3x3的特征检测器。作为良好的实践,始终从32开始;你可以稍后添加64128

  4. 现在,添加池化层,图像大小为2x2

    classifier.add(MaxPool2D(pool_size=(2,2)))
    
  5. 通过向CNN 模型添加 flatten 层,来展平池化层的输出:

    classifier.add(Flatten())
    
  6. 添加 ANN 的第一层密集层。此处,128是节点数量的输出。作为一个良好的实践,128是一个不错的起点。activationrelu。作为一个良好的实践,建议使用 2 的幂:

    classifier.add(Dense(units=128,activation='relu')) 
    
  7. 向 ANN 中添加三层相同大小为128的层,并配以ReLU 激活函数:

    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    classifier.add(Dense(128,activation='relu'))
    
  8. 添加 ANN 的输出层。将 sigmoid 函数替换为softmax

    classifier.add(Dense(units=1,activation='softmax')) 
    
  9. 使用Adam 优化器编译网络,并在训练过程中计算准确率:

    # Compile The network
    classifier.compile(optimizer='adam', loss='binary_crossentropy', \
                       metrics=['accuracy'])
    
  10. 创建训练和测试数据生成器。通过1/255重新缩放训练和测试图像,使所有值都介于01之间。仅为训练数据生成器设置以下参数:shear_range=0.2zoom_range=0.2horizontal_flip=True

    from keras.preprocessing.image import ImageDataGenerator
    train_datagen = ImageDataGenerator(rescale = 1./255, \
                                       shear_range = 0.2, \
                                       zoom_range = 0.2, \
                                       horizontal_flip = True)
    test_datagen = ImageDataGenerator(rescale = 1./255)
    
  11. training set文件夹创建训练集。'../dataset/training_set'是我们存放数据的文件夹。我们的 CNN 模型的图像大小为64x64,因此这里也应该传递相同的大小。batch_size是每个批次中的图像数量,设置为32class_mode设置为binary,因为我们正在处理二分类器:

    training_set = \
    train_datagen.flow_from_directory('../dataset/training_set', \
                                      target_size = (64, 64), \
                                      batch_size = 32, \
                                      class_mode = 'binary')
    
  12. 对测试数据重复步骤 6,将文件夹设置为测试图像的所在位置,即'../dataset/test_set'

    test_set = \
    test_datagen.flow_from_directory('../dataset/test_set', \
                                     target_size = (64, 64), \
                                     batch_size = 32, \
                                     class_mode = 'binary')
    
  13. 最后,拟合数据。将steps_per_epoch设置为10000,将validation_steps设置为2500。以下步骤可能需要一些时间来执行:

    classifier.fit_generator(training_set, steps_per_epoch = 10000, \
                             epochs = 2, validation_data = test_set, \
                             validation_steps = 2500, shuffle=False)
    

    上述代码生成以下输出:

    Epoch 1/2
    10000/10000 [==============================] - 2452s 245ms/step - loss: 8.1783 - accuracy: 0.4667 - val_loss: 11.4999 - val_accuracy: 0.4695
    Epoch 2/2
    10000/10000 [==============================] - 2496s 250ms/step - loss: 8.1726 - accuracy: 0.4671 - val_loss: 10.5416 - val_accuracy: 0.4691
    

    请注意,由于新的 softmax 激活函数,准确率已降至46.91%

    注意

    要访问此特定部分的源代码,请参考packt.live/3gj0TiA

    您也可以在线运行此示例:packt.live/2VIDj7e

活动 7.02:分类新图像

在本次活动中,您将尝试分类另一张新图像,就像我们在前面的练习中所做的那样。该图像尚未暴露于算法,因此我们将使用此活动来测试我们的算法。您可以运行本章中的任何算法(虽然推荐使用获得最高准确率的算法),然后使用该模型对您的图像进行分类。按照以下步骤完成此活动:

  1. 运行本章中的一个算法。

  2. 加载图像并处理它。'test_image_2.jpg'是测试图像的路径。请在代码中更改为您保存数据集的路径:

    from keras.preprocessing import image
    new_image = \
    image.load_img('../test_image_2.jpg', target_size = (64, 64))
    new_image
    
  3. 您可以使用以下代码查看类标签:

    training_set.class_indices
    
  4. 通过使用img_to_array函数将图像转换为numpy数组来处理图像。然后,使用numpyexpand_dims函数沿第 0 轴添加一个额外的维度:

    new_image = image.img_to_array(new_image)
    new_image = np.expand_dims(new_image, axis = 0)
    
  5. 通过调用分类器的predict方法来预测新图像:

    result = classifier.predict(new_image)
    
  6. 使用class_indices方法结合if…else语句,将预测的 0 或 1 输出映射到一个类标签:

    if result[0][0] == 1:
        prediction = 'It is a flower'
    else:
        prediction = 'It is a car'
    print(prediction)
    

    上述代码生成以下输出:

    It is a flower
    

    test_image_2是一张花卉图像,预测结果为花卉。

    注意

    要访问此特定部分的源代码,请参考packt.live/38ny95E

    您也可以在线运行此示例:packt.live/2VIM4Ow

8. 转移学习与预训练模型

活动 8.01:使用 VGG16 网络训练深度学习网络以识别图像

使用VGG16网络预测给定的图像(test_image_1)。在开始之前,请确保您已将图像(test_image_1)下载到工作目录中。按照以下步骤完成此活动:

  1. 导入numpy库和必要的Keras库:

    import numpy as np
    from keras.applications.vgg16 import VGG16, preprocess_input
    from keras.preprocessing import image 
    
  2. 初始化模型(注意,此时您还可以查看网络架构,如以下代码所示):

    classifier = VGG16()
    classifier.summary()
    

    classifier.summary() 显示了网络的架构。需要注意以下几点:它具有四维输入形状(None, 224, 224, 3),并且有三层卷积层。

    输出的最后四层如下:

    图 8.16:网络的架构

    图 8.16:网络的架构

  3. 加载图像。 '../Data/Prediction/test_image_1.jpg' 是我们系统中图像的路径,在您的系统上会有所不同:

    new_image = \
    image.load_img('../Data/Prediction/test_image_1.jpg', \
                   target_size=(224, 224))
    new_image
    

    以下图显示了前面代码的输出:

    图 8.17:示例摩托车图像

    图 8.17:示例摩托车图像

    目标大小应该是 224x 224,因为 VGG16 仅接受(224, 224)。

  4. 使用 img_to_array 函数将图像转换为数组:

    transformed_image = image.img_to_array(new_image)
    transformed_image.shape
    

    前面的代码提供了以下输出:

    (224, 224, 3)
    
  5. 图像应该是四维形式的,以便 VGG16 允许进一步处理。按如下方式扩展图像的维度:

    transformed_image = np.expand_dims(transformed_image, axis=0)
    transformed_image.shape
    

    前面的代码提供了以下输出:

    (1, 224, 224, 3)
    
  6. 预处理图像:

    transformed_image = preprocess_input(transformed_image)
    transformed_image
    

    以下图显示了前面代码的输出:

    图 8.18:图像预处理

    图 8.18:图像预处理

  7. 创建 predictor 变量:

    y_pred = classifier.predict(transformed_image)
    y_pred
    

    以下图显示了前面代码的输出:

    图 8.19:创建预测变量

    图 8.19:创建预测变量

  8. 检查图像的形状。它应该是(1,1000)。之所以是 1000,是因为如前所述,ImageNet 数据库有 1000 个图像类别。预测变量显示了我们图像属于这些图像类别之一的概率:

    y_pred.shape
    

    前面的代码提供了以下输出:

    (1, 1000)
    
  9. 使用 decode_predictions 函数打印我们图像的前五个概率,并传递预测变量 y_pred 的函数,以及预测数量和对应标签以输出:

    from keras.applications.vgg16 import decode_predictions
    decode_predictions(y_pred, top=5)
    

    前面的代码提供了以下输出:

    [[('n03785016', 'moped', 0.8433369),
      ('n03791053', 'motor_scooter', 0.14188054),
      ('n03127747', 'crash_helmet', 0.007004856),
      ('n03208938', 'disk_brake', 0.0022349996),
      ('n04482393', 'tricycle', 0.0007717237)]]
    

    数组的第一列是内部编码号,第二列是标签,第三列是图像属于该标签的概率。

  10. 将预测转换为人类可读的格式。我们需要从输出中提取最可能的标签,如下所示:

    label = decode_predictions(y_pred)
    """
    Most likely result is retrieved, for example, the highest probability
    """
    decoded_label = label[0][0]
    # The classification is printed
    print('%s (%.2f%%)' % (decoded_label[1], decoded_label[2]*100 ))
    

    前面的代码提供了以下输出:

    moped (84.33%)
    

    在这里,我们可以看到该图片有 84.33% 的概率是摩托车,这与摩托车足够接近,可能表示在 ImageNet 数据集中摩托车被标记为踏板车。

    注意

    若要访问此特定部分的源代码,请参考 packt.live/2C4nqRo

    您还可以在 packt.live/31JMPL4 在线运行此示例。

活动 8.02:使用 ResNet 进行图像分类

在本活动中,我们将使用另一个预训练网络,称为 ResNet。我们有一张位于 ../Data/Prediction/test_image_4 的电视图像。我们将使用 ResNet50 网络来预测这张图像。请按照以下步骤完成此活动:

  1. 导入 numpy 库和必要的 Keras 库:

    import numpy as np
    from keras.applications.resnet50 import ResNet50, preprocess_input
    from keras.preprocessing import image 
    
  2. 初始化 ResNet50 模型并打印模型的总结:

    classifier = ResNet50()
    classifier.summary()
    

    classifier.summary() 显示了网络的架构,以下几点需要注意:

    图 8.20:输出的最后四层

    图 8.20:输出的最后四层

    注意

    最后一层预测(Dense)有 1000 个值。这意味着 VGG16 总共有 1000 个标签,我们的图像将属于这 1000 个标签中的一个。

  3. 加载图像。'../Data/Prediction/test_image_4.jpg' 是我们系统上图像的路径,在你的系统中会有所不同:

    new_image = \
    image.load_img('../Data/Prediction/test_image_4.jpg', \
                   target_size=(224, 224))
    new_image
    

    以下是上述代码的输出:

    图 8.21:一张电视的示例图像

    图 8.21:一张电视的示例图像

    目标大小应该是 224x224,因为 ResNet50 只接受 (224,224)。

  4. 使用 img_to_array 函数将图像转换为数组:

    transformed_image = image.img_to_array(new_image)
    transformed_image.shape
    
  5. 为了使 ResNet50 允许进一步处理,图像必须为四维形式。使用 expand_dims 函数沿第 0 维扩展图像的维度:

    transformed_image = np.expand_dims(transformed_image, axis=0)
    transformed_image.shape
    
  6. 使用 preprocess_input 函数对图像进行预处理:

    transformed_image = preprocess_input(transformed_image)
    transformed_image
    
  7. 使用分类器的 predict 方法,通过创建预测变量来预测图像:

    y_pred = classifier.predict(transformed_image)
    y_pred
    
  8. 检查图像的形状。它应该是(1,1000):

    y_pred.shape
    

    上述代码提供了以下输出:

    (1, 1000)
    
  9. 使用 decode_predictions 函数,传递预测变量 y_pred 作为参数,选择最顶端的五个概率及其对应的标签:

    from keras.applications.resnet50 import decode_predictions
    decode_predictions(y_pred, top=5)
    

    上述代码提供了以下输出:

    [[('n04404412', 'television', 0.99673873),
      ('n04372370', 'switch', 0.0009829825),
      ('n04152593', 'screen', 0.00095111143),
      ('n03782006', 'monitor', 0.0006477369),
      ('n04069434', 'reflex_camera', 8.5398955e-05)]]
    

    数组的第一列是内部代码编号,第二列是标签,第三列是图像与标签匹配的概率。

  10. 将预测结果转化为人类可读的格式。从 decode_predictions 函数的输出中打印最可能的标签:

    label = decode_predictions(y_pred)
    """
    Most likely result is retrieved, for example, 
    the highest probability
    """
    decoded_label = label[0][0]
    # The classification is printed 
    print('%s (%.2f%%)' % (decoded_label[1], decoded_label[2]*100 ))
    

    上述代码产生了以下输出:

    television (99.67%)
    

    注意

    要访问此特定部分的源代码,请参考 packt.live/38rEe0M

    你也可以在网上运行这个示例,网址是 packt.live/2YV5xxo

9. 使用递归神经网络进行序列建模

活动 9.01:使用 50 个单元(神经元)的 LSTM 预测亚马逊股价趋势

在这个活动中,我们将研究亚马逊过去 5 年的股票价格——从 2014 年 1 月 1 日到 2018 年 12 月 31 日。通过这一过程,我们将尝试使用RNNLSTM预测 2019 年 1 月该公司的未来趋势。我们拥有 2019 年 1 月的实际数据,因此可以稍后将预测结果与实际值进行比较。按照以下步骤完成这个活动:

  1. 导入所需的库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    from tensorflow import random
    
  2. 使用 pandas 的read_csv函数导入数据集,并使用head方法查看数据集的前五行:

    dataset_training = pd.read_csv('../AMZN_train.csv')
    dataset_training.head()
    

    以下图示显示了上述代码的输出:

    图 9.24:数据集的前五行

    ](tos-cn-i-73owjymdk6/9a8394f72ce140c1bfc5d25fb8bde667)

    图 9.24:数据集的前五行

  3. 我们将使用Open股票价格进行预测;因此,从数据集中选择Open股票价格列并打印其值:

    training_data = dataset_training[['Open']].values 
    training_data
    

    上述代码生成了以下输出:

    array([[ 398.799988],
           [ 398.290009],
           [ 395.850006],
           ...,
           [1454.199951],
           [1473.349976],
           [1510.800049]])
    
  4. 然后,通过使用MinMaxScaler进行特征缩放来规范化数据,设定特征的范围,使它们的最小值为 0,最大值为 1。使用缩放器的fit_transform方法对训练数据进行处理:

    from sklearn.preprocessing import MinMaxScaler
    sc = MinMaxScaler(feature_range = (0, 1))
    training_data_scaled = sc.fit_transform(training_data)
    training_data_scaled
    

    上述代码生成了以下输出:

    array([[0.06523313],
           [0.06494233],
           [0.06355099],
           ...,
           [0.66704299],
           [0.67796271],
           [0.69931748]])
    
  5. 创建数据以从当前实例获取60个时间戳。我们选择60是因为它能为我们提供足够的前置实例来理解趋势;从技术上讲,这个数字可以是任何值,但60是最优值。此外,这里的上界值是1258,它是训练集中的行数(或记录数)索引:

    X_train = []
    y_train = []
    for i in range(60, 1258):
        X_train.append(training_data_scaled[i-60:i, 0])
        y_train.append(training_data_scaled[i, 0])
    X_train, y_train = np.array(X_train), np.array(y_train)
    
  6. 使用 NumPy 的reshape函数重塑数据,为X_train的末尾添加一个额外的维度:

    X_train = np.reshape(X_train, (X_train.shape[0], \
                         X_train.shape[1], 1))
    
  7. 导入以下库来构建 RNN:

    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Dropout
    
  8. 设置随机种子并初始化顺序模型,如下所示:

    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  9. 向网络中添加一个LSTM层,设定50个单元,将return_sequences参数设置为True,并将input_shape参数设置为(X_train.shape[1], 1)。添加三个额外的LSTM层,每个层有50个单元,并为前两个层将return_sequences参数设置为True。最后,添加一个大小为 1 的输出层:

    model.add(LSTM(units = 50, return_sequences = True, \
              input_shape = (X_train.shape[1], 1)))
    # Adding a second LSTM layer
    model.add(LSTM(units = 50, return_sequences = True))
    # Adding a third LSTM layer
    model.add(LSTM(units = 50, return_sequences = True))
    # Adding a fourth LSTM layer
    model.add(LSTM(units = 50))
    # Adding the output layer
    model.add(Dense(units = 1))
    
  10. 使用adam优化器并使用均方误差作为损失函数编译网络。将模型拟合到训练数据,进行100个周期的训练,批量大小为32

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')
    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 100, batch_size = 32)
    
  11. 加载并处理测试数据(在这里视为实际数据),并选择表示Open股票数据的列:

    dataset_testing = pd.read_csv('../AMZN_test.csv')
    actual_stock_price = dataset_testing[['Open']].values
    actual_stock_price
    
  12. 连接数据,因为我们需要60个前一个实例来获得每天的股票价格。因此,我们将需要训练数据和测试数据:

    total_data = pd.concat((dataset_training['Open'], \
                            dataset_testing['Open']), axis = 0)
    
  13. 重塑并缩放输入数据以准备测试数据。请注意,我们正在预测 1 月的月度趋势,这一月份有21个金融工作日,因此为了准备测试集,我们将下界值设为60,上界值设为81。这样可以确保21的差值得以保持:

    inputs = total_data[len(total_data) \
             - len(dataset_testing) - 60:].values
    inputs = inputs.reshape(-1,1)
    inputs = sc.transform(inputs)
    X_test = []
    for i in range(60, 81):
        X_test.append(inputs[i-60:i, 0])
    X_test = np.array(X_test)
    X_test = np.reshape(X_test, (X_test.shape[0], \
                                 X_test.shape[1], 1))
    predicted_stock_price = model.predict(X_test)
    predicted_stock_price = \
    sc.inverse_transform(predicted_stock_price)
    
  14. 通过绘制实际股价和预测股价来可视化结果:

    # Visualizing the results
    plt.plot(actual_stock_price, color = 'green', \
             label = 'Real Amazon Stock Price',ls='--')
    plt.plot(predicted_stock_price, color = 'red', \
             label = 'Predicted Amazon Stock Price',ls='-')
    plt.title('Predicted Stock Price')
    plt.xlabel('Time in days')
    plt.ylabel('Real Stock Price')
    plt.legend()
    plt.show()
    

    请注意,您的结果可能会与亚马逊的实际股价略有不同。

    预期输出

    图 9.25:实际股价与预测股价

图 9.25:实际股价与预测股价

如前面的图所示,预测股价和实际股价的趋势几乎相同;两条线的波峰和波谷一致。这是因为 LSTM 能够记住序列数据。传统的前馈神经网络无法预测出这一结果。这正是LSTMRNNs的真正强大之处。

注意

要访问此特定部分的源代码,请参阅packt.live/3goQO3I

你也可以在packt.live/2VIMq7O上在线运行此示例。

活动 9.02:使用正则化预测亚马逊的股价

在这个活动中,我们将研究亚马逊过去 5 年的股价,从 2014 年 1 月 1 日到 2018 年 12 月 31 日。在此过程中,我们将尝试使用 RNN 和 LSTM 预测并预测 2019 年 1 月亚马逊股价的未来趋势。我们已拥有 2019 年 1 月的实际值,因此稍后我们将能够将我们的预测与实际值进行比较。最初,我们使用 50 个单元(或神经元)的 LSTM 预测了亚马逊股价的趋势。在本次活动中,我们还将添加 Dropout 正则化,并将结果与活动 9.01使用 50 个单元(神经元)的 LSTM 预测亚马逊股价趋势进行比较。按照以下步骤完成此活动:

  1. 导入所需的库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    from tensorflow import random
    
  2. 使用 pandas 的read_csv函数导入数据集,并使用head方法查看数据集的前五行:

    dataset_training = pd.read_csv('../AMZN_train.csv')
    dataset_training.head()
    
  3. 我们将使用Open股票价格来进行预测;因此,从数据集中选择Open股票价格列并打印其值:

    training_data = dataset_training[['Open']].values
    training_data
    

    上述代码产生了以下输出:

    array([[ 398.799988],
           [ 398.290009],
           [ 395.850006],
           ...,
           [1454.199951],
           [1473.349976],
           [1510.800049]])
    
  4. 然后,通过使用MinMaxScaler对数据进行特征缩放,并设置特征的范围,使其最小值为0,最大值为 1。使用缩放器的fit_transform方法对训练数据进行处理:

    from sklearn.preprocessing import MinMaxScaler
    sc = MinMaxScaler(feature_range = (0, 1))
    training_data_scaled = sc.fit_transform(training_data)
    training_data_scaled
    

    上述代码产生了以下输出:

    array([[0.06523313],
           [0.06494233],
           [0.06355099],
           ...,
           [0.66704299],
           [0.67796271],
           [0.69931748]])
    
  5. 创建数据以获取来自当前实例的60个时间戳。我们选择60,因为它将为我们提供足够的先前实例,以便理解趋势;技术上讲,这可以是任何数字,但60是最优值。此外,这里的上限值是1258,这是训练集中的索引或行数(或记录数):

    X_train = []
    y_train = []
    for i in range(60, 1258):
        X_train.append(training_data_scaled[i-60:i, 0])
        y_train.append(training_data_scaled[i, 0])
    X_train, y_train = np.array(X_train), np.array(y_train)
    
  6. 使用 NumPy 的reshape函数将数据重塑,以便在X_train的末尾添加一个额外的维度:

    X_train = np.reshape(X_train, (X_train.shape[0], \
                                   X_train.shape[1], 1))
    
  7. 导入以下 Keras 库以构建 RNN:

    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Dropout
    
  8. 设置种子并初始化顺序模型,如下所示:

    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  9. 在网络中添加一个 LSTM 层,设置 50 个单元,将return_sequences参数设置为True,并将input_shape参数设置为(X_train.shape[1], 1)。为模型添加丢弃层,rate=0.2。再添加三个 LSTM 层,每个 LSTM 层有50个单元,前两个 LSTM 层的return_sequences参数设置为True。在每个LSTM层后,添加丢弃层,rate=0.2。最后添加一个大小为1的输出层:

    model.add(LSTM(units = 50, return_sequences = True, \
                   input_shape = (X_train.shape[1], 1)))
    model.add(Dropout(0.2))
    # Adding a second LSTM layer and some Dropout regularization
    model.add(LSTM(units = 50, return_sequences = True))
    model.add(Dropout(0.2))
    # Adding a third LSTM layer and some Dropout regularization
    model.add(LSTM(units = 50, return_sequences = True))
    model.add(Dropout(0.2))
    # Adding a fourth LSTM layer and some Dropout regularization
    model.add(LSTM(units = 50))
    model.add(Dropout(0.2))
    # Adding the output layer
    model.add(Dense(units = 1))
    
  10. 使用adam优化器编译网络,并使用均方误差作为损失函数。将模型拟合到训练数据,训练100个周期,批量大小为32

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')
    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 100, batch_size = 32)
    
  11. 加载并处理测试数据(这里将其视为实际数据),并选择表示Open股票数据的列:

    dataset_testing = pd.read_csv('../AMZN_test.csv')
    actual_stock_price = dataset_testing[['Open']].values
    actual_stock_price 
    
  12. 将数据连接起来,因为我们需要60个前序实例来获取每天的股票价格。因此,我们将需要训练数据和测试数据:

    total_data = pd.concat((dataset_training['Open'], \
                            dataset_testing['Open']), axis = 0)
    
  13. 对输入数据进行重塑和缩放,以准备测试数据。请注意,我们正在预测 1 月的月度趋势,1 月有21个交易日,因此为了准备测试集,我们将下界值设为60,上界值设为81。这确保了21的差异得以保持:

    inputs = total_data[len(total_data) \
             - len(dataset_testing) - 60:].values
    inputs = inputs.reshape(-1,1)
    inputs = sc.transform(inputs)
    X_test = []
    for i in range(60, 81):
        X_test.append(inputs[i-60:i, 0])
    X_test = np.array(X_test)
    X_test = np.reshape(X_test, (X_test.shape[0], \
                                 X_test.shape[1], 1))
    predicted_stock_price = model.predict(X_test)
    predicted_stock_price = \
    sc.inverse_transform(predicted_stock_price)
    
  14. 通过绘制实际股票价格与预测股票价格的图表来可视化结果:

    # Visualizing the results
    plt.plot(actual_stock_price, color = 'green', \
             label = 'Real Amazon Stock Price',ls='--')
    plt.plot(predicted_stock_price, color = 'red', \
             label = 'Predicted Amazon Stock Price',ls='-')
    plt.title('Predicted Stock Price')
    plt.xlabel('Time in days')
    plt.ylabel('Real Stock Price')
    plt.legend()
    plt.show()
    

请注意,您的结果可能与实际股票价格略有不同。

预期输出

图 9.26:实际股票价格与预测股票价格的对比

](tos-cn-i-73owjymdk6/cb7e00dae0344d55b21f5112989031c8)

图 9.26:实际股票价格与预测股票价格的对比

在下图中,第一个图展示了来自活动 9.02 的带正则化的模型预测输出,第二个图展示了来自活动 9.01 的没有正则化的模型预测输出。正如你所见,加入丢弃正则化并没有更精确地拟合数据。因此,在这种情况下,最好不要使用正则化,或者使用丢弃正则化并设置较低的丢弃率:

图 9.27:比较活动 9.01 与活动 9.02 的结果

](tos-cn-i-73owjymdk6/fd71797b987c48e090c2793a4488d055)

图 9.27:比较活动 9.01 与活动 9.02 的结果

注意

要访问该特定部分的源代码,请参考packt.live/2YTpxR7

你也可以在线运行这个示例,网址是packt.live/3dY5Bku

活动 9.03:使用增加数量的 LSTM 神经元(100 个单元)预测亚马逊股票价格的趋势

在此活动中,我们将分析亚马逊过去 5 年(2014 年 1 月 1 日到 2018 年 12 月 31 日)的股价。我们将使用四个 LSTM 层的 RNN,每个层有 100 个单元,尝试预测并预测 2019 年 1 月的公司未来趋势。我们已知 2019 年 1 月的实际股价,因此之后可以将我们的预测与实际值进行对比。你也可以将输出差异与 活动 9.01使用 50 个单元(神经元)的 LSTM 预测亚马逊股价趋势 进行比较。按照以下步骤完成此活动:

  1. 导入所需的库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    from tensorflow import random
    
  2. 使用 pandas 的 read_csv 函数导入数据集,并使用 head 方法查看数据集的前五行:

    dataset_training = pd.read_csv('../AMZN_train.csv')
    dataset_training.head()
    
  3. 我们将使用 Open 股票价格进行预测;因此,从数据集中选择 Open 股票价格列并打印值:

    training_data = dataset_training[['Open']].values
    training_data
    
  4. 然后,使用 MinMaxScaler 对数据进行特征缩放,并设置特征的范围,使其最小值为零,最大值为一。对训练数据使用 fit_transform 方法:

    from sklearn.preprocessing import MinMaxScaler
    sc = MinMaxScaler(feature_range = (0, 1))
    training_data_scaled = sc.fit_transform(training_data)
    training_data_scaled
    
  5. 创建数据,从当前实例获取 60 个时间戳。我们选择 60,因为它能为我们提供足够的前期数据,以帮助理解趋势;技术上讲,这个数字可以是任何值,但 60 是最佳值。此外,这里的上界值为 1258,即训练集中行(或记录)的索引或数量:

    X_train = []
    y_train = []
    for i in range(60, 1258):
        X_train.append(training_data_scaled[i-60:i, 0])
        y_train.append(training_data_scaled[i, 0])
    X_train, y_train = np.array(X_train), np.array(y_train)
    
  6. 重塑数据,通过使用 NumPy 的 reshape 函数,在 X_train 末尾添加一个额外的维度:

    X_train = np.reshape(X_train, (X_train.shape[0], \
                                   X_train.shape[1], 1))
    
  7. 导入以下 Keras 库以构建 RNN:

    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Dropout
    
  8. 设置种子并初始化顺序模型:

    seed = 1
    np.random.seed(seed)
    random.set_seed(seed)
    model = Sequential()
    
  9. 向网络中添加一个 100 单元的 LSTM 层,将 return_sequences 参数设置为 True,并将 input_shape 参数设置为 (X_train.shape[1], 1)。再添加三个 LSTM 层,每个层有 100 个单元,前两个层的 return_sequences 参数设置为 True。最后添加一个大小为 1 的输出层:

    model.add(LSTM(units = 100, return_sequences = True, \
                   input_shape = (X_train.shape[1], 1)))
    # Adding a second LSTM layer
    model.add(LSTM(units = 100, return_sequences = True))
    # Adding a third LSTM layer
    model.add(LSTM(units = 100, return_sequences = True))
    # Adding a fourth LSTM layer
    model.add(LSTM(units = 100))
    # Adding the output layer
    model.add(Dense(units = 1))
    
  10. 使用 adam 优化器编译网络,并使用 均方误差(Mean Squared Error) 作为损失函数。将模型拟合到训练数据上,训练 100 个周期,批量大小为 32

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')
    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 100, batch_size = 32)
    
  11. 加载并处理测试数据(这里视为实际数据),并选择表示开盘股价数据的列:

    dataset_testing = pd.read_csv('../AMZN_test.csv')
    actual_stock_price = dataset_testing[['Open']].values
    actual_stock_price
    
  12. 合并数据,因为我们需要 60 个前期数据来获取每天的股价。因此,我们将需要训练数据和测试数据:

    total_data = pd.concat((dataset_training['Open'], \
                            dataset_testing['Open']), axis = 0)
    
  13. 重塑并缩放输入数据以准备测试数据。请注意,我们预测的是一月的月度趋势,21 个交易日,因此,为了准备测试集,我们将下界值设置为 60,上界值设置为 81。这确保了 21 的差值得以保持:

    inputs = total_data[len(total_data) \
             - len(dataset_testing) - 60:].values
    inputs = inputs.reshape(-1,1)
    inputs = sc.transform(inputs)
    X_test = []
    for i in range(60, 81):
        X_test.append(inputs[i-60:i, 0])
    X_test = np.array(X_test)
    X_test = np.reshape(X_test, (X_test.shape[0], \
                                 X_test.shape[1], 1))
    predicted_stock_price = model.predict(X_test)
    predicted_stock_price = \
    sc.inverse_transform(predicted_stock_price)
    
  14. 通过绘制实际股价和预测股价来可视化结果:

    plt.plot(actual_stock_price, color = 'green', \
             label = 'Actual Amazon Stock Price',ls='--')
    plt.plot(predicted_stock_price, color = 'red', \
             label = 'Predicted Amazon Stock Price',ls='-')
    plt.title('Predicted Stock Price')
    plt.xlabel('Time in days')
    plt.ylabel('Real Stock Price')
    plt.legend()
    plt.show()
    

    请注意,你的结果可能会与实际股价略有不同。

    预期输出

    图 9.28:实际股票价格与预测股票价格

    ](tos-cn-i-73owjymdk6/233a2d23c5ca4343abe5b7e49b296c0e)

图 9.28:实际股票价格与预测股票价格

因此,如果我们将本节中的LSTM(50 个单元,来自活动 9.01使用 50 个单元(神经元)的 LSTM 预测亚马逊股票价格趋势)与LSTM(100 个单元)进行比较,我们会得到 100 个单元的趋势。另外,请注意,当我们运行LSTM(100 个单元)时,它比运行LSTM(50 个单元)需要更多的计算时间。在这种情况下需要考虑权衡:

图 9.29:比较 50 个和 100 个单元的实际股票价格与预测股票价格

](tos-cn-i-73owjymdk6/836e789c6eca440c8bb0b51d2d79a477)

图 9.29:比较 50 个和 100 个单元的实际股票价格与预测股票价格

注意

要访问此特定部分的源代码,请参阅 packt.live/31NQkQy

您还可以在网上运行此示例,访问 packt.live/2ZCZ4GR