【DL with Pytorch】第 2 章 : 神经网络的构建块

    🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

介绍

神经网络简介

什么是神经网络?

练习 2.01:执行感知器的计算

多层感知器

神经网络的学习过程

前向传播

损失函数的计算

反向传播

梯度下降

的优点和缺点

优点

缺点

人工神经网络简介

卷积神经网络简介

循环神经网络简介

数据准备

处理混乱的数据

练习 2.02:处理混乱的数据

数据缩放

练习 2.03:重新缩放数据

拆分数据

练习 2.04:拆分数据集

未能准备好数据的缺点

活动 2.01:执行数据准备

构建深度神经网络

练习 2.05:使用 PyTorch 构建深度神经网络

活动 2.02:开发深度学习回归问题的解决方法

概括


本章介绍了神经网络的主要构建块,并解释了当今三种主要的神经网络架构。此外,它解释了在训练任何人工智能模型之前进行数据准备的重要性,最后解释了解决回归数据问题的过程。到本章结束时,您将牢牢掌握不同网络体系结构及其不同应用的学习过程。

介绍

在上一章中,解释了为什么深度学习现在变得如此流行,并介绍了 PyTorch 作为开发深度学习解决方案的最受欢迎的库之一。虽然已经解释了使用 PyTorch 构建神经网络的主要语法,但在本章中,我们将进一步探讨神经网络的概念。

尽管神经网络理论是几十年前发展起来的,但由于这个概念是从感知器的概念演变而来的,近来已经创建了不同的体系结构来解决不同的数据问题。这在一定程度上是由于在现实生活中的数据问题中可以找到不同的数据格式,例如文本、音频和图像。

本章的目的是深入探讨神经网络及其主要优缺点,以便您了解何时以及如何使用它们。然后,我们将解释最流行的神经网络架构的构建块:人工神经网络ANN )、卷积神经网络CNN ) 和递归神经网络RNN )。

在此之后,将通过解决现实生活中的回归问题来解释构建有效模型的过程。这包括准备要馈送到神经网络的数据(也称为数据预处理)、定义要使用的神经网络架构以及评估模型的性能,目的是确定如何改进它以实现最优解。

上述过程将使用本章将讨论的一种神经网络架构来完成,同时考虑到每个数据问题的解决方案都应该使用最适合所讨论数据类型的架构来执行。其他架构将在后续章节中用于解决更复杂的数据问题,这些问题涉及使用图像和文本序列作为输入数据。

神经网络简介

神经网络从训练数据中学习,而不是被编程为通过遵循一组规则来解决特定任务。此学习过程可以遵循以下方法之一:

  • 监督学习:这是最简单的学习形式,因为它由标记的数据集组成,神经网络在其中找到解释特征与目标之间关系的模式。学习过程中的迭代旨在最小化预测值与真实值之间的差异。这方面的一个例子是根据叶子的属性对植物进行分类。
  • 无监督学习:与前面的方法相反,无监督学习包括使用未标记数据(意味着没有目标值)训练模型。这样做的目的是为了更好地理解输入数据。一般来说,网络获取输入数据,对其进行编码,然后从编码版本重建内容,理想情况下保留相关信息。例如,给定一段,神经网络可以映射单词,然后建议哪些是最重要的或描述该段落的。这些可以用作标签。
  • 强化学习:这种方法包括从输入数据中学习,其主要目标是从长远来看最大化奖励函数。这是通过从数据中学习来实现的,而不是通过静态数据对其进行训练(如在监督学习中)。因此,决策不是基于即时奖励,而是基于整个学习过程中奖励的积累。这方面的一个例子是将资源分配给不同任务的模型,目的是最大限度地减少降低总体性能的瓶颈。

    笔记

    从我们这里提到的学习方法来看,最常用的是监督学习,这也是后面章节主要用到的一种。这意味着本章中的所有练习、活动和示例都将使用带标签的数据集作为输入数据。

什么是神经网络?

正如我们之前讨论的那样,神经网络是一种机器学习算法,它以人脑的解剖结构为模型,并使用数学方程从训练数据的观察中学习模式。

然而,要真正理解神经网络通常遵循的训练过程背后的逻辑,理解感知器的概念很重要。

感知器由 Frank Rosenblatt 在 1950 年代开发,是一种人工神经元,它接受多个输入并产生二进制输出,类似于人脑中的神经元。这然后成为后续感知器(神经元)的输入。感知器是神经网络的基本组成部分(就像神经元是人脑的组成部分一样):

图 2.1:感知器图

这里,1、2、3和4代表感知器的不同输入,可以有任意数量的输入。圆圈是感知器,它是处理输入以达到输出的地方。

Rosenblatt 还引入了权重的概念(1 , 2 , …, n),这些数字表示每个输入的重要性。输出可以是 0 或 1,它取决于输入的加权和是高于还是低于给定阈值(开发人员设置的数值限制或数据问题的约束),可以设置为感知器的一个参数,如下所示:

图 2.2:感知器输出方程

练习 2.01:执行感知器的计算

以下练习不需要任何类型的编程;相反,它由简单的计算组成,可帮助您理解感知器的概念。要执行这些计算,请考虑以下场景。

下周五在你的镇上有一个音乐节,但你生病了并试图决定是否去(其中 0 表示你不去,1 表示你去)。您的决定取决于三个因素:

  • 会有好天气吗?( 1 )
  • 你有人陪吗?( × 2 )
  • 音乐是你喜欢的吗?( 3 )

对于上述因素,如果问题的答案为是,我们将使用 1,如果答案为否,则使用 0。此外,由于您病得很重,与天气相关的因素高度相关,因此您决定赋予该因素两倍于其他两个因素的权重。因此,您决定因子的权重为 4 ( 1 )、2 ( 2 ) 和 2 ( 3 )。现在,考虑阈值 5:

  1. 根据提供的信息,考虑到下周五天气不好,但你有人陪你,你喜欢音乐节上的音乐,计算感知器的输出:

图 2.3:感知器的输出

考虑到输出小于阈值,最终结果将等于 0,这意味着你不应该去节日,以免患上更严重的病。

您已经成功地执行了感知器的计算,这是理解神经网络内部发生的学习过程的起点。

多层感知器

考虑到我们在上一节中学到的知识,多层网络的概念由堆叠在一起的多个感知器网络(也称为节点或神经元)组成,如下所示:

图 2.4:多层感知器示意图

笔记

在神经网络中引用层的常规方式如下:

第一层是输入层,最后一层是输出层,中间的所有层都是隐藏层。

在这里,再次使用一组输入来训练模型,但不是将它们馈送到单个感知器,而是将它们馈送到第一层中的所有感知器(神经元)。接下来,从该层获得的输出将用作后续层中感知器的输入,依此类推,直到到达最后一层,该层负责输出结果。

请注意,感知器的第一层通过对输入进行加权来处理简单的决策过程,而后续层可以根据前一层的输出处理更复杂和抽象的决策,因此具有最先进的性能用于复杂数据问题的深度神经网络(使用多层的网络)。

与传统的感知器不同,神经网络已经进化到在输出层中有一个或多个节点,因此它们能够将结果呈现为二进制或多类。

神经网络的学习过程

一般而言,神经网络由多个神经元组成,其中每个神经元计算一个线性函数以及一个激活函数,以根据某些输入得出输出(激活函数旨在打破线性 - 这将是本章稍后会详细解释)。该输出与一个权重相关联,该权重代表其重要性级别,并将用于下一层的计算。

此外,这些计算在整个网络架构中进行,直到达到最终输出。此输出用于确定网络与地面实况相比的性能,然后用于调整网络的不同参数以重新开始计算过程。

考虑到这一点,神经网络的训练过程可以看作是一个迭代过程,通过网络的层向前和向后到达最优结果,如下图所示(损失函数将在后面介绍)在这一章当中):

图 2.5:神经网络学习过程图

前向传播

这是从左到右遍历网络架构的过程,同时使用输入数据执行计算以得出可以与基本事实进行比较的预测。这意味着网络中的每个神经元都会根据与其关联的权重和偏差来转换输入数据(初始数据或从上一层接收到的数据),并将输出发送到后续层,直到最后一层达到并做出预测。

笔记

在神经网络中,偏差是有助于移动每个神经元的激活函数以避免可能影响训练过程的零值的数值。它们在神经网络训练中的作用将在本章后面解释。

在每个神经元中执行的计算包括一个线性函数,该函数将输入数据乘以某个权重加上偏差,然后通过激活函数传递。激活函数的主要目的是打破模型的线性,考虑到使用神经网络解决的大多数现实生活中的数据问题不是由一条线定义的,而是由一个复杂的函数定义的,这一点至关重要。这些公式如下:

图 2.6:每个神经元执行的计算

在这里,正如我们之前提到的,X指的是输入数据,W是确定输入数据重要性级别的权重,b是偏置值,sigma ( ) 表示应用于线性函数的激活函数。

 激活函数用于向模型引入非线性。有不同的激活函数可供选择,现在最常用的激活函数列表如下:

  • Sigmoid:这是 S 形的,它基本上将值转换为 0 和 1 之间的简单概率,其中 sigmoid 函数获得的大部分输出将接近 0 和 1 的极值:

图 2.7:S 形激活函数

下图显示了 sigmoid 激活函数的图形表示:

图 2.8:S 形激活函数的图形表示

  • Softmax:类似于 sigmoid 函数,它计算一个事件在n 个事件上的概率分布,这意味着它的输出不是二进制的。简单来说,此函数计算与其他类相比输出是目标类之一的概率:

图 2.9:Softmax 激活函数

考虑到它的输出是一个概率,这个激活函数经常出现在分类网络的输出层。

  • Tanh:这个函数表示双曲正弦和双曲余弦之间的关系,结果在-1和1之间。这个激活函数的主要优点是可以更容易地处理负值:

图 2.10:Tanh 激活函数

下图显示了tanh激活函数的图形表示:

图 2.11:tanh 激活函数的图形表示

  • 整流线性函数 (ReLU):这基本上激活了一个节点,因为线性函数的输出大于 0;否则,其输出将为 0。如果线性函数的输出大于 0,则此激活函数的结果将是它作为输入收到的原始数字:

图 2.12:ReLU 激活函数

按照惯例,此激活函数用于所有隐藏层。我们将在本章接下来的部分学习更多关于隐藏层的知识。下图显示了 ReLU 激活函数的图形表示:

图 2.13:ReLU 激活函数的图形表示

损失函数的计算

前向传播完成后,训练过程的下一步是计算损失函数,通过比较预测相对于真实值的好坏来估计模型的误差。考虑到这一点,要达到的理想值为 0,这意味着两个值之间没有差异。

这意味着训练过程的每次迭代的目标是通过更改用于在前向传递期间执行计算的参数(权重和偏差)来最小化损失函数。

同样,有多种损失函数可供选择。然而,回归和分类任务最常用的损失函数如下:

  • 均方误差(MSE):广泛用于衡量回归模型的性能,MSE函数计算ground truth与预测值之间的距离之和:

图 2.14:MSE 损失函数

这里,n是指样本数,是ground truth值,是预测值。

  • 交叉熵/多类交叉熵:该函数通常用于二元或多类分类模型。它衡量两个概率分布之间的差异;一个大的损失函数将代表一个大的分歧。因此,这里的目标也是最小化损失函数:

图 2.15:交叉熵损失函数

同样,n指的是样本数。分别是真实值和预测值。

反向传播

训练过程的最后一步包括在网络架构中从右到左计算损失函数相对于每一层中的权重和偏差的偏导数(也称为梯度),以便更新这些参数(权重和偏差),以便在下一个迭代步骤中,损失函数较低。

优化算法的最终目标是找到损失函数达到最小可能值的全局最小值,如下图所示:

笔记

局部最小值是指函数域的一部分内的最小值。另一方面,全局最小值是指函数整个域的最小值。

图 2.16:通过二维空间中的迭代步骤优化损失函数

这里,最左边的点A是任何优化之前损失函数的初始值。曲线底部最右边的点B是几个迭代步骤后的损失函数,其值已最小化。从一个点到另一个点的过程称为一步

然而,重要的是要提到损失函数并不总是像前面那样平滑,这可能会在优化过程中引入达到局部最小值的风险。

此过程也称为优化,并且有不同的算法在实现相同目标的方法上有所不同。接下来将解释最常用的优化算法。

梯度下降

梯度下降是数据科学家中使用最广泛的优化算法,它是许多其他优化算法的基础。在计算出每个神经元的梯度后,权重和偏差在梯度的相反方向上更新,应该乘以学习率(用于控制每次优化所采取的步长),如图所示以下等式。

学习率在训练过程中至关重要,因为它可以防止权重和偏差的更新过高/过低,这可能会分别阻止模型达到收敛或延迟训练过程。

梯度下降算法中权值和偏置的优化如下:

图 2.17:梯度下降算法中参数的优化

这里,α指的是学习率,dw/db表示给定神经元中权重或偏差的梯度。从权重或偏差的原始值中减去这两个值的乘积,以惩罚有助于计算较大损失函数的较高值。

梯度下降算法的改进版本称为随机梯度下降,它基本上遵循相同的过程,不同之处在于它以随机批次而不是一个块的形式获取输入数据,这在提高训练时间的同时达到了出色的性能。此外,这种方法允许使用更大的数据集,因为通过使用小批量数据集作为输入,我们不再受计算资源的限制。

的优点和缺点

以下是对神经网络优缺点的解释。

优点

神经网络在过去几年变得越来越流行,主要原因有四个:

  • 数据:神经网络以其利用大量数据的能力而广为人知,并且由于硬件和软件的进步,现在可以收集和存储海量数据库。随着越来越多的数据被输入,这使得神经网络能够显示出它们的真正潜力。
  • 复杂数据问题:正如我们之前解释的那样,神经网络非常适合解决其他机器学习算法无法解决的复杂数据问题。这主要是由于它们能够处理大型数据集并发现复杂的模式。
  • 计算能力:技术的进步也增加了当今可用的计算能力,这对于训练使用数百万条数据的神经网络模型至关重要。
  • 学术研究:由于前三点,互联网上提供了大量关于该主题的学术研究,这不仅有助于每天沉浸在新研究中,而且有助于使算法和硬件/软件要求保持最新.

缺点

仅仅因为使用神经网络有很多优势并不意味着每个数据问题都应该以这种方式解决。这是一个经常犯的错误。没有一种算法可以很好地解决所有数据问题,选择要使用的算法应取决于可用资源以及数据问题。

尽管神经网络被认为优于几乎所有机器学习算法,但也必须考虑它们的缺点,这样您才能权衡对数据问题最重要的因素。现在让我们来看看它们:

  • 黑匣子:这是神经网络最常见的缺点之一。它基本上意味着神经网络如何以及为什么达到某个输出是未知的。例如,当神经网络将猫的图片错误地预测为狗时,就不可能知道错误的原因是什么。
  • 数据要求:他们需要大量数据才能获得最佳结果,这既是优势也是劣势。神经网络比传统的机器学习算法需要更多的数据,这可能是在某些数据问题上选择它们和其他算法的主要原因。当手头的任务受到监督时,这将成为一个更大的问题,这意味着需要标记数据。
  • 训练时间:与上述缺点相关,对大量数据的需求也使得训练过程比传统机器学习算法持续时间更长,在某些情况下,这不是一种选择。通过使用加速计算的 GPU 可以减少训练时间。
  • 计算成本高:同样,神经网络的训练过程在计算上是昂贵的。虽然一个神经网络可能需要数周时间才能收敛,但其他机器学习算法可能需要数小时或数分钟才能完成训练。所需的计算资源量取决于手头的数据量以及网络的复杂性;更深的神经网络需要更长的时间来训练。

    笔记

    有一个广泛的各种神经网络架构。本章将介绍其中三个最常用的方法,并在后续章节中介绍它们的实际应用。但是,如果您想了解其他架构,请访问The Neural Network Zoo - The Asimov Institute

人工神经网络简介

人工神经网络ANN ),也称为多层感知器,是多个感知器的集合。感知器之间的连接通过层发生。一层可以有任意数量的感知器,它们都连接到前一层和后一层中的所有其他感知器。

网络可以有一层或多层。超过四层的网络被认为是深度神经网络,通常用于解决复杂和抽象的数据问题。

人工神经网络通常由三个主要元素组成,这在前面已经解释过,也可以在下图中看到:

  1. 输入层:这是网络的第一层,通常位于网络图形表示的最左侧。它在执行任何计算之前接收输入数据并完成第一组计算。这是发现最通用模式的地方。

    对于监督学习问题,输入数据由一对特征和目标组成。网络的工作是揭示特征和目标之间的相关性或依赖性。

  2. 隐藏层:接下来,可以找到隐藏层。神经网络可以有许多隐藏层,这意味着输入层和输出层之间可以有任意数量的层。它拥有的层数越多,它可以解决的数据问题就越复杂,但训练时间也会更长。也有完全不包含隐藏层的神经网络架构,单层网络就是这种情况。

    在每一层中,根据从上一层接收到的输入信息执行计算,然后使用该信息输出一个值,该值将成为后续层的输入。

  3. 输出层:这是网络的最后一层,位于网络图形表示的最右侧。它在数据经过网络中的所有神经元处理后接收数据,以做出最终预测。

    输出层可以有一个或多个神经元。前者指的是解决方案是二进制的模型,形式为 0 或 1。另一方面,后一种情况由输出实例属于每个可能类别标签(目标变量具有的可能值)的概率的模型组成,这意味着该层将具有与类别一样多的神经元标签:

图 2.18:具有两个隐藏层的神经网络架构

卷积神经网络简介

卷积神经网络CNN ) 主要用于计算机视觉领域,近几十年来,机器已经达到了超越人类能力的准确度水平。

CNN 创建的模型使用神经元子群来识别图像的不同方面。这些组应该能够相互通信,以便它们可以共同形成完整的图像。

考虑到这一点,CNN 架构中的层划分了它们的识别任务。第一层专注于琐碎的模式,而网络末端的层使用该信息来发现更复杂的模式。

例如,在识别图片中的人脸时,前几层专注于寻找将一个特征与另一个特征分开的边缘。接下来,后续图层强调面部的某些特征,例如鼻子。最后,最后几层使用此信息将人的整张脸放在一起。

这种在遇到某​​些特征时激活一组神经元的想法是通过使用过滤器(内核)实现的,过滤器是 CNN 架构的主要构建块之一。然而,它们并不是架构中存在的唯一元素,这就是为什么将在此处提供对 CNN 的所有组件的简要说明:

笔记

您在使用 CNN 时可能听说过的 padding 和 stride 的概念将在本书的后续章节中进行解释。

  1. 卷积层:在这些层中,卷积计算发生在图像(表示为像素矩阵)和滤波器之间。该计算产生一个特征图作为输出,最终作为下一层的输入。

    该计算采用与过滤器形状相同的图像矩阵的一部分,并执行值的乘法运算。然后,将乘积之和设置为该部分图像的输出,如下图所示:

    图 2.19:图像与滤波器的卷积运算

    这里,左边的矩阵是输入数据,中间的矩阵是过滤器,右边的矩阵是计算的输出。可以在这里看到用方框突出显示的值发生的计算:

    图 2.20:图像第一部分的卷积

    这种卷积乘法是针对图像的所有子部分完成的。下图显示了同一示例的另一个卷积步骤:

    图 2.21:进一步的卷积运算

    卷积层的一个重要概念是它们以这样一种方式保持不变,即每个过滤器都将具有特定的功能,该功能在训练过程中不会发生变化。例如,负责检测耳朵的过滤器在整个训练过程中只会专注于该功能。

    此外,CNN 通常会有多个卷积层,考虑到每个卷积层都将专注于识别图像的特定特征或特征集,具体取决于所使用的过滤器。通常,两个卷积层之间有一个池化层。

  2. 池化层:虽然卷积层能够从图像中提取相关特征,但在分析复杂的几何形状时,它们的结果会变得巨大,这将使训练过程在计算能力方面变得不可能,因此池化层的发明。

    这些层不仅实现了减少卷积层输出的目标,而且还实现了去除已提取特征中存在的任何噪声的目的,这最终有助于提高模型的准确性。

    有两种主要类型的池化层可以应用,它们背后的想法是检测图像中表现出更强影响力的区域,从而忽略其他区域。

    最大池化:此操作包括取给定大小的矩阵的一个子部分,并将该子部分中的最大数作为最大池化操作的输出:

    图 2.22:最大池操作

    在上图中,通过使用 3 x 3 最大池化过滤器,可以实现右侧的结果。此处,黄色部分(左上角)的最大数量为 4,而橙色部分(右上角)的最大数量为 5。

    平均池化:类似地,平均池化操作取矩阵的子部分并将满足规则的数字作为输出,在这种情况下,它是相关子部分中所有数字的平均值:

    图 2.23:平均池化操作

    在这里,使用 3 x 3 过滤器,我们得到 2.9,这是黄色部分(左上角)中所有数字的平均值,而 3.2 是橙色部分(右上角)中所有数字的平均值.

  3. 全连接层:最后,考虑到如果网络只能检测一组特征而不能将它们分类到类别标签中,那么网络将毫无用处,因此在 CNN 的末尾使用全连接层来采取前一层检测到的特征(称为特征图)并输出该组特征属于类别标签的概率,用于进行最终预测。

    与 ANN 一样,全连接层使用感知器根据给定输入计算输出。此外,值得一提的是,CNN 通常在架构末端有不止一个全连接层。

通过结合所有这些概念,获得了 CNN 的传统架构。每种类型可以有任意多的层,每个卷积层可以有任意多的过滤器(每个用于特定任务)。此外,池化层应具有与前一个卷积层相同数量的过滤器,如下图所示:

图 2.24:CNN 架构图

循环神经网络简介

上述神经网络(ANN 和 CNN)的主要局限性在于它们仅通过考虑当前事件(正在处理的输入)来学习,而不考虑之前或之后的事件,考虑到我们人类不思考,这是不方便的那样。例如,在阅读一本书时,您可以通过考虑上一段或更多内容的上下文来更好地理解每个句子。

因此,并考虑到神经网络旨在优化传统上由人类完成的几个过程这一事实,考虑一个能够考虑一系列输入和输出的网络,从而创建递归神经网络是至关重要的网络RNN)。它们是一种强大的神经网络,可以通过使用内部存储器为复杂的数据问题找到解决方案。

简而言之,这些网络中包含循环,允许信息在其记忆中保留更长时间,即使正在处理后续信息集。这意味着 RNN 中的感知器不仅将输出传递给下一个感知器,而且还为自身保留了一些信息,这对于分析下一位信息很有用。这种记忆保持能力使他们能够非常准确地预测接下来会发生什么。

与其他网络类似,RNN 的学习过程试图映射输入 (x) 和输出 (y) 之间的关系,不同之处在于这些模型还考虑了先前输入的全部或部分历史记录。

RNN 允许以输入序列、输出序列或同时两者的形式处理数据序列,如下图所示:

图 2.25:RNN 处理的数据序列

在这里,每个方框都是一个矩阵,箭头代表发生的函数。底部的框是输入,顶部的框是输出,中间的框代表 RNN 在该点的状态,保存着网络的记忆。

从左到右,上述图表可以解释如下:

  1. 不需要 RNN 求解的典型模型。它有一个固定的输入和一个固定的输出。例如,这可以指图像分类。
  2. 该模型接受输入并产生一系列输出。以接收图像作为输入的模型为例;输出应该是图像标题。
  3. 与前面的模型相反,该模型采用一系列输入并产生单一结果。这种类型的架构可以在情感分析问题上看到,其中输入是要分析的句子,输出是句子背后的预测情感。
  4. 最后两个模型接受一系列输入并返回一系列输出,不同之处在于第一个模型同时分析输入并生成输出;例如,当视频的每一帧都被单独标记时。另一方面,第二个多对多模型分析整个输入集以生成输出集。这方面的一个例子是语言翻译,在进行实际翻译之前,需要理解一种语言的整个句子。

数据准备

开发任何深度学习模型的第一步——当然是在收集数据之后——应该是准备数据。如果我们希望了解手头的数据以正确概述项目范围,这一点至关重要。

许多数据科学家未能做到这一点,这导致模型性能不佳,甚至是无用的模型,因为它们无法从一开始就回答数据问题。

准备数据的过程可以分为三个主要任务:

  1. 了解数据并处理任何潜在问题
  2. 重新缩放特征以确保没有错误引入偏差
  3. 拆分数据以便能够准确地衡量性能

所有这三个任务将在下一节中进一步解释。

笔记

我们之前解释的所有任务在应用任何机器学习算法时都几乎相同,考虑到它们指的是预先准备数据所需的技术。

处理混乱的数据

该任务主要包括执行探索性数据分析EDA ) 以了解可用数据,以及检测可能影响模型开发的潜在问题。

EDA 过程很有用,因为它可以帮助开发人员发现对定义行动过程至关重要的信息。此信息在此处解释:

  1. 数据量:这既指实例的数量,也指特征的数量。前者对于确定是否有必要甚至可能使用神经网络甚至深度神经网络来解决数据问题至关重要,因为此类模型需要大量数据才能达到高精度。另一方面,后者有助于确定预先开发一些特征选择方法以减少特征数量、简化模型和消除任何冗余信息是否是一种好的做法。
  2. 目标特征:对于监督模型,需要标记数据。考虑到这一点,选择目标特征(我们希望通过构建模型实现的目标)以评估该特征是否有许多缺失值或离群值非常重要。此外,这有助于确定开发目标,该目标应与可用数据保持一致。
  3. 噪声数据/异常值:噪声数据是指明显不正确的值,例如,一个人 200 岁。另一方面,离群值指的是尽管可能正确但与平均值相差甚远的值,例如,一名 10 岁的大学生。

    没有检测异常值的精确科学,但有一些普遍接受的方法。假设一个正态分布的数据集,最流行的数据集之一是将与平均值相差大约 3-6 个标准差的任何值确定为异常值。

    识别离群值同样有效的方法是选择第 99个和第 1个百分位数的值。

    当这些值代表特征数据的 5% 以上时,处理这些值非常重要,因为不这样做可能会给模型带来偏差。与任何其他机器学习算法一样,处理这些值的方法是删除异常值或使用均值或回归插补技术分配新值。

  4. 缺失值:与上述类似,考虑到不同的模型会对这些值做出不同的假设,具有许多缺失值的数据集会给模型带来偏差。同样,当缺失值占特征值的 5% 以上时,应通过消除或替换它们来处理它们,再次使用均值或回归插补技术。
  5. 定性特征:最后,检查数据集是否包含定性数据也是一个关键步骤,考虑到去除或编码数据可能会产生更准确的模型。

    此外,在许多研究发展中,对相同数据测试了几种算法以确定哪种算法表现更好,并且其中一些算法不允许使用定性数据,例如神经网络。这证明了对它们进行转换或编码以便能够为所有算法提供相同数据的重要性。

练习 2.02:处理混乱的数据

笔记

本章中的所有练习将使用来自 UC Irvine 机器学习存储库的Appliances 能源预测数据集完成,该存储库可从UCI Machine Learning Repository: Appliances energy prediction Data Set下载. 它也可以在本书的 GitHub 存储库中找到:https ://packt.live/34MBoSw

Appliances 能源预测数据集包含 4.5 个月的与低能耗建筑中不同房间的温度和湿度测量相关的数据,目的是预测某些电器使用的能源。

在本练习中,我们将使用流行的 Python 包pandas来探索手头的数据,并学习如何检测缺失值、异常值和定性值。执行以下步骤来完成本练习:

笔记

对于本章中的练习和活动,您需要在本地计算机上安装 Python 3.7、Jupyter 6.0、NumPy 1.17 和 Pandas 0.25。

  1. 打开一个 Jupyter notebook 来实施这个练习。
  2. 导入熊猫库:
    import pandas as pd
  3. 使用 pandas 读取包含我们从 UC Irvine Machine Learning Repository 站点下载的数据集的 CSV 文件。

    接下来,删除名为date的列,因为我们不想在以下练习中考虑它:

    data = pd.read_csv("energydata_complete.csv")
    data = data.drop(columns=["date"])

    最后,打印DataFrame的头部:

    data.head()

    输出应如下所示:

    图 2.26:Appliances 能源预测数据集的顶级实例

  4. 检查数据集中的分类特征:
    cols = data.columns
    num_cols = data._get_numeric_data().columns
    list(set(cols) - set(num_cols))

    第一行生成数据集中所有列的列表。接下来,包含数值的列也存储在变量中。最后,通过从整个列列表中减去数字列,可以获得非数字列。

    结果列表为空,这表明没有要处理的分类特征。

  5. 使用 Python 的isnull()sum()函数找出数据集的每一列中是否有缺失值:
    data.isnull().sum()

    此命令计算每列中空值的数量。对于正在使用的数据集,不应有任何缺失值,如下所示:

    图 2.27:缺失值计数

  6. 使用三个标准偏差作为检测数据集中所有特征的任何异常值的度量:
    outliers = {}
    
    for i in range(data.shape[1]):
        min_t = data[data.columns[i]].mean() \
                - (3 * data[data.columns[i]].std())
        max_t = data[data.columns[i]].mean() \
                + (3 * data[data.columns[i]].std())
        count = 0
        for j in data[data.columns[i]]:
            if j < min_t or j > max_t:
                count += 1
        percentage = count / data.shape[0]
        outliers[data.columns[i]] = "%.3f" % percentage
    
    outliers

    前面的代码片段对数据集中的列执行for循环,以评估每个列中是否存在异常值。它会继续计算最小和最大阈值,以便计算落在阈值范围之外的实例数。

    最后,它计算离群值的百分比(即离群值的数量除以实例总数),以便输出一个字典来显示每列的百分比。

    通过打印生成的字典(异常值),可以显示数据集中所有特征(列)的列表,以及异常值的百分比。根据结果​​,可以得出不需要处理离群值的结论,考虑到它们占数据的比例不到 5%,如下面的屏幕截图所示:

    笔记

    请注意,只要变量位于笔记本中单元格的末尾,Jupyter Notebooks 就可以打印变量的值而无需 print 函数。在任何其他编程平台或任何其他场景中,请确保使用打印功能。

    例如,打印包含离群值的结果字典的等效方法(也是最佳实践)是使用 print 语句,如下所示:print(outliers)。这样,代码在不同的编程平台上运行时将具有相同的输出。

图 2.28:异常值参与每个特征

您已成功探索数据集并处理潜在问题。

数据缩放

尽管不需要重新缩放数据以提供给算法进行训练,但如果您希望提高模型的准确性,这是一个重要的步骤。这基本上是因为每个特征具有不同的尺度可能会导致模型假设给定特征比其他特征更重要,因为它具有更高的数值。

以两个特征为例,一个衡量一个人拥有的孩子数量,另一个衡量这个人的年龄。尽管年龄特征可能具有更高的数值,但在推荐学校的研究中,孩子数量特征可能更重要。

考虑到这一点,如果所有特征的缩放比例相同,那么模型实际上可以为那些对目标特征最重要的特征赋予更高的权重,而不是它们具有的数值。此外,它还可以通过消除模型从数据的不变性中学习的需要来帮助加速训练过程。

有两种主要的重新缩放方法在数据科学家中很流行,虽然没有选择其中一种的规则,但重要的是要强调它们是单独使用的(一个或另一个)。

可以在此处找到对这两种方法的简要说明:

  • 归一化:这包括重新缩放值,使所有特征的所有值都在零和一之间。这是使用以下等式完成的:

图 2.29:数据规范化

  • 标准化:相比之下,这种重新缩放方法转换所有值,使其平均值为 0,标准差等于 1。这是使用以下等式完成的:

图 2.30:数据标准化

练习 2.03:重新缩放数据

在本练习中,我们将重新缩放上一个练习中的数据。为此,请执行以下步骤:

笔记

使用您在上一个练习中使用的同一 Jupyter 笔记本。

  1. 将特征与目标分开。我们这样做只是为了重新缩放特征数据:
    X = data.iloc[:, 1:]
    Y = data.iloc[:, 0]

    前面的代码片段获取数据并使用切片将特征与目标分开。

  2. 使用规范化方法重新缩放特征数据。显示结果DataFrame的头部(即前五个实例)以验证结果:
    X = (X - X.min()) / (X.max() - X.min())
    
    X.head()

    输出应如下所示:

图 2.31:规范化电器能源预测数据集的顶级实例

您已成功重新缩放数据集。

拆分数据

将数据集拆分为三个子集的目的是使模型可以在不引入偏差的情况下进行适当的训练、微调和测量。以下是对每组的解释:

  • 训练集:顾名思义,该集被馈送到要训练的神经网络。对于监督学习,它由特征和目标值组成。正如我们之前提到的,考虑到神经网络需要大量数据进行训练,这通常是三者中最大的一组。
  • 验证集(dev set):该集主要用于衡量模型的性能,以便对超参数进行调整以提高性能。完成此微调过程,以便我们可以配置实现最佳结果的超参数。

    尽管模型没有根据这些数据进行训练,但它会间接影响它,这就是为什么不应该对它进行性能的最终测量,因为它可能是一种有偏见的测量。

  • 测试集:这个集对模型没有影响,这就是为什么它被用来对模型对看不见的数据进行最终评估,这成为模型在未来数据集上表现如何的指南。

考虑到每个数据问题都是不同的,并且开发深度学习解决方案通常需要反复试验的方法,因此没有关于将数据分成上述三组的完美比例的实际科学。然而,众所周知,更大的数据集(数十万和数百万个实例)对于每个集合应该有 98:1:1 的分割比例,考虑到为训练集使用尽可能多的数据是至关重要的。对于较小的数据集,常规拆分比例为 60:20:20。

练习 2.04:拆分数据集

在本练习中,我们会将上一个练习中的数据集拆分为三个子集。为了学习的目的,我们将探索两种不同的方法。首先,数据集将使用索引进行拆分。接下来,scikit-learn 的train_test_split()函数将用于相同的目的,从而使用两种方法获得相同的结果。执行以下步骤来完成本练习:

笔记

使用您在上一个练习中使用的同一 Jupyter 笔记本。

  1. 打印数据集的形状以确定要使用的拆分比率:
    X.shape

    此操作的输出应为(19735, 27)。这意味着可以对训练集、验证集和测试集使用 60:20:20 的分割比例。

  2. 获取您将用作训练集和验证集上限的值。这将用于使用索引拆分数据集:
    train_end = int(len(X) * 0.6)
    dev_end = int(len(X) * 0.8)

    前面的代码确定了将用于通过切片来划分数据集的实例的索引。

  3. 打乱数据集:
    X_shuffle = X.sample(frac=1, random_state=0)
    Y_shuffle = Y.sample(frac=1, random_state=0)

    使用 pandas样本函数,可以打乱特征矩阵和目标矩阵中的元素。通过将frac设置为 1,我们确保所有实例都被打乱并在函数的输出中返回。使用random_state参数,我们确保两个数据集被平等地洗牌。

  4. 使用索引将打乱的数据集拆分为特征和目标数据的三组:
    x_train = X_shuffle.iloc[:train_end,:]
    y_train = Y_shuffle.iloc[:train_end]
    x_dev = X_shuffle.iloc[train_end:dev_end,:]
    y_dev = Y_shuffle.iloc[train_end:dev_end]
    x_test = X_shuffle.iloc[dev_end:,:]
    y_test = Y_shuffle.iloc[dev_end:]
  5. 打印所有三组的形状:
    print(x_train.shape, y_train.shape)
    print(x_dev.shape, y_dev.shape)
    print(x_test.shape, y_test.shape)

    前面操作的结果应该是这样的:

    (11841, 27) (11841,)
    (3947, 27) (3947,)
    (3947, 27) (3947,)
  6. 从 scikit-learn 的model_selection模块导入train_test_split()函数:
    from sklearn.model_selection import train_test_split

    笔记

    虽然导入不同的包和库是为了实际学习目的,但最好在代码开头导入它们。

  7. 拆分打乱的数据集:
    x_new, x_test_2, y_new, y_test_2 = train_test_split(X_shuffle, Y_shuffle, \
                                       test_size=0.2, \
                                       random_state=0)
    
    dev_per = x_test_2.shape[0]/x_new.shape[0]
    
    x_train_2, x_dev_2, y_train_2, y_dev_2 = train_test_split(x_new, y_new, \
                                          test_size=dev_per, \
                                          random_state=0)

    第一行代码执行初始拆分。该函数将以下内容作为参数:

    X_shuffle , Y_shuffle : 待拆分的数据集,即特征数据集,以及目标数据集(也称为X和Y)

    test_size:要包含在测试集中的实例的百分比

    random_state : 用于保证结果的可重复性

    这行代码的结果是将每个数据集(X 和 Y)划分为两个子集。

    要创建附加集(验证集),我们将执行第二次拆分。上述代码的第二行负责确定用于第二次拆分的test_size,以便测试集和验证集具有相同的形状。

    最后,最后一行代码使用先前计算的值作为test_size执行第二次拆分。

  8. 打印所有三组的形状:
    print(x_train_2.shape, y_train_2.shape)
    print(x_dev_2.shape, y_dev_2.shape)
    print(x_test_2.shape, y_test_2.shape)

    上述操作的结果应如下所示:

    (11841, 27) (11841,)
    (3947, 27) (3947,)
    (3947, 27) (3947,)

    正如我们所见,两种方法的结果集具有相同的形状。使用一种方法或另一种方法是偏好问题。

您已成功将数据集拆分为三个子集。

未能准备好数据的缺点

虽然准备数据集的过程很耗时,在处理大型数据集时可能会很累,但不这样做的坏处就更不方便了:

  • 更长的训练时间:包含噪声、缺失值以及冗余或不相关列的数据需要相当长的训练时间,并且在大多数情况下,这种时间延迟甚至比准备数据所需的时间还要长。例如,在数据准备过程中,可能会确定有五列与研究目的无关,这可能会大大减少数据集,从而大大减少训练时间。
  • 偏差的引入:未清理的数据通常包含错误或缺失值,这些错误或缺失值会使模型偏离事实。例如,缺失值可能导致模型做出不正确的推断,这反过来又会创建一个不代表数据的模型。
  • 避免泛化:异常值和噪声值会阻止模型对数据进行泛化,这对于构建表示当前训练数据以及未来未见数据的模型至关重要。例如,包含年龄变量的数据集包含超过 100 岁的人的条目可能会生成一个模型,该模型考虑了那些实际上代表人口中很小一部分的用户。

活动 2.01:执行数据准备

在此活动中,我们将准备一个包含歌曲列表的数据集,每首歌曲都有几个有助于确定它们发行年份的属性。此数据准备步骤对于本章的下一个活动至关重要。让我们看看下面的场景。

你在一家音乐唱片公司工作,你的老板想要揭示不同时期唱片的特征细节,这就是为什么他们将包含 515,345 条唱片数据的数据集放在一起,发行年份从 1922 年到 2011 年不等。他们有要求您准备数据集,以便将其输入神经网络。执行以下步骤以完成此活动:

笔记

要下载此活动的数据集,请访问以下 UC Irvine 机器学习存储库 URL:https ://archive.ics.uci.edu/ml/datasets/YearPredictionMSD 。

引文:Dua, D. 和 Graff, C.(2019 年)。UCI 机器学习库 [ http://archive.ics.uci.edu/ml ]。加州欧文市:加州大学信息与计算机科学学院。

它也可以在本书的 GitHub 存储库中找到:https ://packt.live/38kZzZR

  1. 导入所需的库。
  2. 使用 pandas,加载.csv文件。
  3. 验证数据集中是否存在任何定性数据。
  4. 检查缺失值。

    您还可以添加一个额外的sum()函数来获取整个数据集中缺失值的总和,而无需按列进行区分。

  5. 检查异常值。
  6. 将特征与目标数据分开。
  7. 使用标准化方法重新缩放数据。
  8. 将数据分成三组:训练、验证和测试。使用您喜欢的任何方法。

    笔记

    可以通过此链接找到此活动的解决方案。

构建深度神经网络

一般而言,构建神经网络可以在非常简单的层面上使用诸如 scikit-learn(不适合深度学习)之类的库来实现,它可以为您执行所有数学运算而没有太大的灵活性,或者在非常复杂的层面上实现通过从头开始对训练过程的每一步进行编码,或者使用更强大的框架,从而提供更大的灵活性。

PyTorch 的构建考虑了该领域许多开发人员的输入,并且具有允许在同一位置进行两种近似的优势。正如我们之前提到的,它有一个神经网络模块,该模块旨在允许使用顺序容器轻松预定义简单架构的实现,同时允许创建自定义模块,从而为构建非常复杂的架构的过程引入灵活性.

在本节中,我们将讨论使用顺序容器开发深度神经网络以揭开其复杂性的神秘面纱。尽管如此,在本书后面的章节中,我们将继续探索更复杂和抽象的应用程序,这些应用程序也可以不费吹灰之力就可以实现。

正如我们之前提到的,顺序容器是一个模块,它被构建为包含遵循一个顺序的模块序列。它包含的每个模块都会对给定的输入应用一些计算以得出结果。

此处解释了可在顺序容器内用于开发常规分类模型的一些最流行的模块(层):

笔记

用于其他类型架构(例如 CNN 和 RNN)的模块将在后续章节中进行说明。

  • 线性层:这对输入数据应用线性变换,同时保持内部张量来保持权重和偏差。它接收输入样本的大小(数据集的特征个数或上一层的输出个数),输出样本的大小(当前层的单元个数,即输出的个数) ),以及是否在训练过程中使用偏差张量(默认设置为True)作为参数。
  • 激活函数:它们接收线性层的输出作为输入,以打破线性。如前所述,有几个激活函数可以添加到顺序容器中。最常用的解释如下:

    ReLU:这将修正后的线性单位函数应用于包含输入数据的张量。它接受的唯一参数是操作是否应该就地完成,默认情况下设置为False

    Tanh:这将按元素的 tanh 函数应用于包含输入数据的张量。它不需要任何参数。

    Sigmoid:这将前面解释的 sigmoid 函数应用于包含输入数据的张量。它不需要任何参数。

    Softmax:这将 softmax 函数应用于包含输入数据的 n 维张量。重新缩放输出,使张量的元素位于零和一之间的范围内,并且总和为一。它将计算 softmax 函数的维度作为参数。

  • Dropout layer:该模块根据设定的概率随机将输入张量的一些元素置零。它以用于随机选择的概率,以及是否应原位进行操作(默认情况下设置为False)作为参数。这种技术通常用于处理过度拟合的模型,稍后将对此进行更详细的解释。
  • Normalization layer:有不同的方法可用于在顺序容器中添加规范化层。其中一些包括BatchNorm1dBatchNorm2dBatchNorm3d。这背后的想法是规范化前一层的输出,最终在较低的训练时间达到相似的准确度水平。

练习 2.05:使用 PyTorch 构建深度神经网络

在本练习中,我们将使用 PyTorch 库定义四层深度神经网络的架构,然后使用我们在之前练习中准备的数据集对其进行训练。为此,请执行以下步骤:

笔记

使用您在上一个练习中使用的同一 Jupyter 笔记本。

  1. 导入名为torch的 PyTorch 库,以及来自 PyTorch 的nn模块:
    import torch
    import torch.nn as nn
  2. 将我们在上一个练习中创建的每个集合的特征列与目标分开。此外,将最终的 DataFrame 转换为张量:
    x_train = torch.tensor(x_train.values).float()
    y_train = torch.tensor(y_train.values).float()
    x_dev = torch.tensor(x_dev.values).float()
    y_dev = torch.tensor(y_dev.values).float()
    x_test = torch.tensor(x_test.values).float()
    y_test = torch.tensor(y_test.values).float()
  3. 使用sequential()容器定义网络架构。确保创建一个四层网络。考虑到我们处理的是回归问题,前三层使用 ReLU 激活函数,最后一层不使用激活函数。

    每层的单元数应为 100、50、25 和 1:

    model = nn.Sequential(nn.Linear(x_train.shape[1], 100), \
                          nn.ReLU(), \
                          nn.Linear(100, 50), \
                          nn.ReLU(), \
                          nn.Linear(50, 25), \
                          nn.ReLU(), \
                          nn.Linear(25, 1))
  4. 定义损失函数为 MSE:
    loss_function = torch.nn.MSELoss()
  5. 将优化器算法定义为 Adam 优化器:
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
  6. 使用for循环在训练数据上训练网络 1,000 次迭代步骤:
    for i in range(1000):
        y_pred = model(x_train).squeeze()
        loss = loss_function(y_pred, y_train)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if i%100 == 0:
            print(i, loss.item())

    笔记

    squeeze()函数用于去除y_pred的额外维度,它从大小 [3000,1] 转换为 [3000]。

    考虑到y_train是一维的,并且两个张量需要具有相同的维度才能提供给损失函数,这一点至关重要。

    运行前面的代码片段将产生类似于以下内容的输出:

    图 2.32:不同迭代步骤的损失值

    可以看出,损失值随时间不断减小。

  7. 要测试模型,请对测试集的第一个实例执行预测并将其与基本事实(目标值)进行比较:
    pred = model(x_test[0])
    print("Ground truth:", y_test[0].item(), \
          "Prediction:",pred.item())

    输出应类似于以下内容:

    Ground truth: 60.0 Prediction: 69.5818099975586

    如您所见,真实值 ( 60 ) 非常接近预测值 ( 69.58 )。

您已成功创建并训练了一个深度神经网络来解决回归问题。

活动 2.02:开发深度学习回归问题的解决方法

在本活动中,我们将创建并训练一个神经网络来解决我们在上一个活动中提到的回归问题。让我们看看这个场景。

你继续在音乐唱片公司工作,在看到你准备数据集的出色工作后,你的老板信任你定义网络架构的任务,并使用准备好的数据集对其进行训练。执行以下步骤以完成此活动:

笔记

使用您在上一个活动中使用的同一 Jupyter 笔记本。

  1. 导入所需的库。
  2. 将特征与我们在上一个活动中创建的所有三组数据的目标分开。将数据帧转换为张量。
  3. 定义网络的架构。随意尝试层数和每层单元数的不同组合。
  4. 定义损失函数和优化器算法。
  5. 使用for循环训练网络 3,000 个迭代步骤。
  6. 通过对测试集的第一个实例执行预测并将其与基本事实进行比较来测试您的模型。

您的输出应与此类似:

Ground truth: 1995.0 Prediction: 1998.0279541015625

笔记

可以通过此链接找到此活动的解决方案。

概括

几十年前,弗兰克·罗森布拉特 (Frank Rosenblatt) 提出了催生神经网络的理论。它从感知器的定义开始,感知器是一个受人类神经元启发的单元,它将数据作为输入来对其执行转换。感知器背后的理论包括为输入数据分配权重以执行计算,以便最终结果要么是一件事,要么是另一件事,这取决于结果。

最广为人知的神经网络形式是由一系列感知器创建而成的神经网络,这些感知器分层堆叠在一起,其中一列感知器(层)的输出是下一个感知器的输入。

解释了神经网络的典型学习过程。这里,要考虑三个主要过程:前向传播、损失函数的计算和反向传播。

此过程的最终目标是通过更新网络每个神经元中每个输入值的权重和偏差来最小化损失函数。这是通过一个迭代过程实现的,这个过程可能需要几分钟、几小时甚至几周,具体取决于数据问题的性质。

还讨论了三种主要类型的神经网络的主要架构:人工神经网络、卷积神经网络和循环神经网络。第一个用于解决传统的分类或回归问题,第二个因其解决计算机视觉问题(例如图像分类)的能力而广受欢迎,第三个能够按顺序处理数据,这对语言翻译等任务。

在下一章中,将讨论解决回归和分类数据问题之间的主要区别。您还将学习如何解决分类数据问题,以及如何提高其性能和如何部署模型。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sonhhxg_柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值