R-深度学习入门指南-全-
R 深度学习入门指南(全)
一、深度学习简介
随着硬件的进步和大数据的出现,更先进的计算方法变得越来越受欢迎。消费者对更好产品的需求不断增加,企业寻求更有效地利用资源,这也是推动这一趋势的主要原因。为了应对这些市场力量,我们最近看到了对机器学习领域的新的广泛关注。在统计学、数学和计算机科学的交叉领域,机器学习是指创建和研究算法的科学,这些算法通过设计以迭代的方式改善自己的行为。最初,该领域致力于开发人工智能,但由于当时存在的理论和技术的限制,将这些算法集中在特定任务上变得更符合逻辑。现有的大多数机器学习算法都专注于函数优化,产生的解决方案并不总是解释数据中的潜在趋势,也没有提供人工智能试图接近的推理能力。因此,使用机器学习算法经常成为一个重复的试错过程,其中跨问题的算法选择会产生不同的性能结果。这在某些情况下没问题,但是在语言建模和计算机视觉的情况下,这就成问题了。
为了应对机器学习的一些缺点,以及我们今天可支配的理论和技术能力的重大进步,深度学习已经出现,并正在迅速扩展为最令人兴奋的科学领域之一。它正被用于自动驾驶汽车、社交媒体平台上的图像识别以及从一种语言到另一种语言的文本翻译等技术。深度学习是机器学习的子领域,致力于构建算法,解释和学习传统机器学习算法通常无法实现的高层次和低层次的数据抽象。深度学习中的模型通常受到许多知识来源的启发,如博弈论和神经科学,许多模型通常模仿人类神经系统的基本结构。随着该领域的发展,许多研究人员设想了一个世界,在这个世界中,软件不像今天经常需要的那样被硬编码,从而允许更健壮、更通用的解决问题的解决方案。
虽然它最初是在类似于机器学习的空间中开始的,其中主要关注的是对不同复杂程度的约束满足,但深度学习现在已经发展到包含更广泛的算法定义,这些算法能够理解对应于不同复杂层次的数据的多级表示。换句话说,算法不仅具有预测和分类能力,而且能够学习不同程度的复杂性。这方面的一个例子是在图像识别中发现的,在图像识别中,神经网络建立在识别睫毛、脸、人等等的基础上。这其中的力量是显而易见的:我们可以达到创建智能软件所需的复杂程度。我们目前在自动更正等功能中看到这一点,该功能针对每个人的词汇,对观察到的语音模式的建议更正进行建模。
深度学习模型的结构通常是这样的,它们具有处理数据的非线性单元或神经元的层,并且这些模型中的多个层处理数据的不同抽象级别。图 1-1 显示了神经网络层的可视化。
图 1-1。
Deep neural network
深度神经网络的区别在于具有许多隐藏层,这些隐藏层被称为“隐藏的”,因为除了知道它们是前一层的输出之外,我们不一定看到这些神经元的输入和输出是什么。层的增加,以及这些层的神经元内部的功能,是将一个单独的架构与另一个架构区分开来,并建立给定模型的不同用例的原因。
更具体地说,这些模型的较低层解释“如何”,而较高层的神经网络处理“为什么”。这些层中使用的函数取决于用例,但通常可由用户定制,这使得它们比通常用于分类和回归的一般机器学习模型更加健壮。深度学习模型的基本假设是,被解释的数据是由分层组织的不同因素的相互作用产生的。因此,拥有多个层允许模型处理数据,以便从简单的方面到更大的结构建立理解。这些模型的目标是在没有许多机器学习算法需要的相同程度的显式指令的情况下执行任务。关于如何使用这些模型,一个主要的好处是当应用于无监督学习问题时,或者在执行实验之前我们不知道响应变量 y 应该给定一组解释变量 x 的问题时,它们表现出的前景。一个例子是图像识别,特别是在模型已经根据给定的数据集训练之后。假设我们在测试阶段输入一张狗的图片,这意味着我们没有告诉模型这张图片是什么。神经网络将首先识别鼻子之前的睫毛,然后识别狗的头部形状,等等,直到它将图像分类为狗的图像。
深度学习模型
既然我们已经建立了深度学习的简要概述,那么讨论一下你在本书中将会学到什么,以及描述我们将在这里讨论的模型将会很有用。
本文假设你对数学和统计学有一定的了解。尽管如此,我们将简要回顾理解线性代数、优化和机器学习所需的所有概念,以便我们将形成掌握深度学习所需的坚实知识基础。虽然它确实有助于准确理解所有这些技术信息,但那些对更高级的数学感到不舒服的人不必担心。如果需要的话,这份正文以这样的方式被写,给读者进一步研究它所必需的所有背景信息。然而,本文的主要目标是向读者展示如何应用机器学习和深度学习模型,而不是对所有讨论的理论概念进行冗长的学术论述。
在我们充分回顾了所有必要的数学和机器学习概念之后,我们将进一步详细讨论机器学习模型。本节描述并举例说明深度学习模型。
单层感知器模型(SLP)
单层感知器(SLP)模型是最简单的神经网络形式,也是深度学习中开发的更高级模型的基础。通常,我们在分类问题中使用 SLP,在分类问题中,我们需要基于输入给出数据观察标签(二元或多项式)。输入图层中的值在乘以权重并在累积和中添加偏差后,直接发送到输出图层。然后,这个累积和被放入一个激活函数中,这个函数就是定义输出的函数。当该输出高于或低于用户确定的阈值时,确定最终输出。研究人员麦卡洛克-皮茨神经元在 20 世纪 40 年代描述了一个类似的模型(见图 1-2 )。
图 1-2。
Single layer perceptron network
多层感知器模型(MLP)
与 SLP 非常相似,多层感知器(MLP)模型具有多个层,这些层以这样一种方式相互连接,从而形成一个前馈神经网络。一层中的每个神经元都与另一层的神经元有直接连接。该模型和单层感知器模型的关键区别因素之一是反向传播算法,这是训练神经网络的一种常用方法。反向传播将从输出层计算的误差传递到输入层,这样我们可以看到每一层对误差的影响,并相应地改变网络。这里,我们使用梯度下降算法来确定每次迭代时权重应该改变的程度。梯度下降(Gradient descent)是另一种流行的机器学习/优化算法,它只是一个函数的导数,这样我们就可以找到一个指向最大动量方向的标量值(一个以大小为唯一属性的数字)。通过减去梯度,我们会得到一个比当前更优的解,直到达到全局最优(见图 1-3 )。
图 1-3。
MultiLayer perceptron network
卷积神经网络
卷积神经网络(CNN)是最常用于图像处理和计算机视觉的模型。它们被设计成模仿动物视觉皮层的结构。具体来说,CNN 具有三维排列的神经元:宽度、高度和深度。给定层中的神经元仅连接到前一层的一小部分区域。CNN 模型最常用于图像处理和计算机视觉(见图 1-4 )。
图 1-4。
Convolutional neural network
循环神经网络
循环神经网络(RNNs)是人工神经网络(ann)的模型,其中单元之间的连接形成有向循环。具体来说,有向循环是一个序列,其中沿着顶点和边的行走完全由所使用的边集决定,因此具有某种特定顺序的外观。rnn 通常专门用于语音和手写识别(参见图 1-5 )。
图 1-5。
Recurrent neural network
受限玻尔兹曼机
受限玻尔兹曼机是一种具有独特架构的二进制马尔可夫模型,使得存在多层隐藏随机变量和对称耦合的随机二进制单元的网络。DBMs 由一组可见单元和一系列隐藏单元层组成。然而,同一层的单元之间没有连接。DMBs 可以在对象或语音识别等任务中学习复杂和抽象的内部表征(见图 1-6 )。
图 1-6。
Restricted Boltzmann machine
深度信念网络
深度信任网络类似于 RBM,除了每个子网的隐藏层实际上是下一个子网的可见层。dbn 是广义的生成图形模型,由多层潜在变量组成,各层之间有联系,但各层单元之间没有联系(见图 1-7 )。
图 1-7。
Deep belief networks
讨论的其他主题
在涵盖了所有关于模型的信息之后,我们将转向理解数据科学的实践。为了有助于这项工作,本节涵盖了其他感兴趣的主题。
试验设计
这篇文章的重点最终是让读者对深度学习模型有一个理论上的理解,这样他们就能舒服地应用它们。因此,重要的是讨论实验设计的特征,以帮助读者理解构建其研究的适当方法,从而得出可操作的见解,而不是浪费时间和/或精力。在很大程度上,鉴于深度学习经常遇到的问题,除了定义最佳实践之外,我还将借鉴 Fisher 的原则。
特征选择
作为实验设计的一部分,但最终完全是研究的一个子课题,我将介绍变量选择的概念和数据科学家处理高维数据集经常使用的多种方法。具体来说,我将深入谈论主成分分析以及遗传算法。所有讨论的算法都可以在开源包的 R 统计语言中找到。对于那些想进一步研究这个领域的人,我会参考与这个主题相关的论文。从深度学习的角度来看,我们将深入讨论每个模型如何通过层架构的设计来执行其自己的特定特征选择方法,以及解决该领域的最新发现。
应用机器学习和深度学习
对于文本的最后一部分,我将带领读者使用 R 语言的包来进行机器学习和深度学习模型,以解决专业和学术设置中常见的问题。希望从这些例子中,读者将被激励在他们的专业和/或学术追求中应用机器学习和深度学习。所有例子、实验和研究的代码都使用 R 编程语言,并将通过 GitHub 提供给所有读者(更多信息请参见附录)。讨论的主题包括使用深度学习模型的回归、分类和图像识别。
深度学习的历史
现在,我们已经涵盖了文本的大致轮廓,除了读者在此期间预计要学习的内容,我们将看到该领域如何发展到这一阶段,并了解它今天寻求的方向。虽然深度学习是一个相对较新的领域,但它有着丰富而充满活力的历史,充满了今天仍在进行的发现。至于这个领域最清晰的起源在哪里,讨论把我们带到了 20 世纪 60 年代。
第一个经常与深度学习模型相关联的工作学习算法是由 Ivakhenenko 和帕拉开发的。1965 年,他们在一篇名为“数据处理小组方法(GMDH)训练的网络”的论文中发表了他们的发现。这些是第一批前馈多层感知器类型的深度学习系统。前馈网络描述了单元之间的连接不形成循环的模型,就像在循环神经网络中一样。该模型以多项式激活函数为特征,并且通过回归分析对层进行增量增长和训练。它们随后在单独的验证集的帮助下被修剪,其中正则化被用来剔除多余的单元。
20 世纪 80 年代,福岛国浩推出了新克隆体。它是一种多层人工神经网络,主要用于手写字符识别和需要模式识别的类似任务。它的模式识别能力给了卷积神经网络灵感。不管怎样,新认知神经的灵感来自神经生理学家 Hubel 和 Wiesel 提出的一个模型。也是在这十年间,Yann LeCun 等人将反向传播算法应用于深度神经网络。这样做的最初目的是让美国电话电报公司识别邮件上手写的邮政编码。这项技术的优势是显著的,尤其是在互联网及其商业化于 20 世纪 90 年代末和 21 世纪初出现之前。
在 20 世纪 90 年代,深度学习领域见证了循环神经网络的发展,该网络需要在 RNN 中及时展开超过 1000 层,并且发现可以使用所谓的唤醒-睡眠算法来训练包含六个完全连接的层和数百个隐藏单元的网络。启发式算法,或我们应用于另一个单个或一组算法的算法,唤醒-睡眠算法是一种无监督的方法,它允许算法以输出最佳密度估计值的方式调整参数。“唤醒”阶段描述了神经元从输入到输出的放电过程。来自输入和输出的连接被修改,以增加它们在当前层之下的层中复制正确活动的可能性。“睡眠”阶段与觉醒阶段相反,因此神经元被连接激活,同时识别被修改。
正如该领域在 2000 年代初和 2010 年代取得的进展一样,当前向前发展的时期被描述为深度学习的分水岭时刻。现在,我们看到了深度学习在众多行业和领域的应用,以及用于这些模型的硬件的非常投入的改进。在未来,预计深度学习所涵盖的进步将有助于让技术在人类今天经常做的事情和传统机器学习算法表现不佳的情况下做出行动。虽然肯定还有进步,但许多公司和大学为加速进步所做的投资是显而易见的,并对世界产生了重大影响。
摘要
对于读者来说,重要的是最终要理解,无论我们在这里描述的任何模型是多么复杂,无论它可能提供多么有趣和强大的用途,在使用这些模型的领域中,没有什么可以替代足够的领域知识。对于高级和初级从业者来说,很容易陷入这样的陷阱:完全相信深度学习模型的输出,而不认真评估它们使用的环境。虽然看起来不言自明,但强调仔细检查结果的重要性是很重要的,更重要的是,在错误风险最有限的情况下做出可操作的推断。我希望给读者留下深刻印象的不仅仅是他们可以在哪里应用这些模型的知识,还有当今存在的技术和研究的合理限制。
这在机器学习和深度学习中尤其重要,因为尽管这些模型中的许多都很强大,并且能够达到手工几乎不可能达到的适当解决方案,但我们还没有确定为什么总是如此。例如,我们知道反向传播算法是如何工作的,但我们看不到它是如何工作的,我们也不知道到底发生了什么才能得出这样的结论。这种情况产生的主要问题是,当一个流程中断时,我们不一定总是知道为什么会中断。尽管已经创建了尝试和跟踪神经元及其激活顺序的方法,但神经网络的决策过程并不总是一致的,尤其是在不同的问题上。我希望读者在前进时记住这一点,并在必要时适当地评估这一点。
二、数学回顾
在讨论机器学习之前,有必要简要概述一下统计学。广义而言,统计是对定量数据的分析和收集,最终目标是对这些数据进行可操作的洞察。也就是说,虽然机器学习和统计学不是同一个领域,但它们密切相关。本章简要概述了与本书后面的讨论相关的术语。
统计概念
如果不先讨论概率的概念,任何关于统计学或机器学习的讨论都是不合适的。
可能性
概率是对事件发生的可能性的度量。虽然许多机器学习模型倾向于确定性的(基于算法规则)而不是概率性的,但是概率的概念除了在更复杂的深度学习架构(如循环神经网络和卷积神经网络)中引用之外,还特别在诸如期望最大化算法的算法中引用。数学上,这个算法定义如下:
这种计算概率的方法代表了频率主义者对概率的观点,在这种观点中,概率基本上是由下面的公式推导出来的。然而,另一个概率学派,贝叶斯,采取了不同的方法。贝叶斯概率理论是基于概率是有条件的假设。换句话说,事件发生的可能性受到当前存在的条件或之前发生的事件的影响。我们在下面的等式中定义条件概率。假设事件 B 已经发生,事件 A 的概率等于:
在这个等式中,我们将读作“给定 B 的概率”,
读作“A 和 B 的概率。”
也就是说,计算概率并不像看起来那么简单,因为必须经常评估依赖性和独立性。举个简单的例子,假设我们正在评估两个事件 A 和 B 的概率,我们还假设事件 B 发生的概率依赖于 A 的发生。因此,如果 A 不发生,B 发生的概率为 0。数学上,我们将两个事件 A 和 B 的依赖性与独立性定义如下:
在图 2-1 中,我们可以把事件 A 和 B 想象成两个集合,A 和 B 的并集是两个圆的交点:
图 2-1。
Representation of two events (A,B)
如果这个等式在给定的环境中不成立,事件 A 和事件 B 被称为是相关的。
And vs. Or
通常,当谈到概率时,例如,当评估两个事件 A 和 B 时,通常在“A 和 B 的概率”或“A 或 B 的概率”的上下文中讨论概率。直观地说,我们将这些概率定义为两个不同的事件,因此它们的数学推导是不同的。简单地说,或表示事件概率的相加,而和意味着事件概率的相乘。以下是所需的方程式:
和(概率的乘法定律)是两个事件 A 和 B 相交的概率:
如果事件是独立的,那么
或者(p 可加性法则)是两个事件 A 和 B 并的概率:
符号的意思是“A 或 b 的概率”
图 2-2 说明了这一点。
图 2-2。
Representation of events A,B and set S
A 和 B 的概率仅仅是它们各自球面上不相交的部分,而 A 或 B 的概率是这两个部分加上交点的和。我们将 S 定义为我们在给定问题中考虑的所有集合加上这些集合之外的空间的总和。因此,S 的概率总是 1。
话虽如此,A 和 B 之外的空间代表了这些事件的反面。例如,假设 A 和 B 分别代表母亲下午 5 点回家和父亲下午 5 点回家的概率。空白代表他们俩都不会在下午 5 点回家的概率
贝叶斯定理
如上所述,贝叶斯统计在机器学习和深度学习领域不断获得赞赏。尽管这些技术通常需要大量的硬编码,但它们的强大之处在于相对简单的理论基础,同时功能强大,适用于各种环境。贝叶斯定理建立在条件概率的概念上,是一个事件 A 的概率与其他类似事件的概率相关的概念:
在后面的章节中提到,贝叶斯分类器是建立在这个公式以及期望最大化算法的基础上的。
随机变量
通常,当分析事件的概率时,我们在一组随机变量内进行。我们将随机变量定义为一个量,其值取决于一组可能的随机事件,每个事件都有相关的概率。它的值在被绘制之前是已知的,但是它也可以被定义为从概率空间映射的函数。通常,我们通过一种叫做随机抽样的方法来抽取这些随机变量。从总体中随机抽样,当每个观察值都以这样的方式被选择,即它和总体中的其他观察值一样有可能被选择时,就说是随机的。
广义地说,读者可能会遇到两种类型的随机变量:离散随机变量和连续随机变量。前者指的是只能取有限个不同值的变量,而后者指的是有无限个可能变量的变量。一个例子是车库里的汽车数量与股票价格百分比变化的理论变化。当分析这些随机变量时,我们通常依赖于读者可能会经常看到的各种统计数据。但是这些统计数据通常直接用于算法中,或者在各个步骤中,或者在评估给定的机器学习或深度学习模型的过程中。
例如,算术平均值直接用于 K-means 聚类等算法,同时也是均方差等模型评估统计的理论基础(本章后面会提到)。直观地说,我们将算术平均值定义为一组离散数字的集中趋势,具体来说,就是这些值的总和除以这些值的个数。数学上,这个等式由下面给出:
广义地说,算术平均值代表随机变量中一组值的最可能值。然而,这不是我们可以用来理解随机变量的唯一一种均值。几何平均值也是一种描述数字序列集中趋势的统计量,但它是通过使用值的乘积而不是总和来获得的。这通常在比较一个序列中的不同项目时使用,尤其是当它们分别具有多个属性时。几何平均值的方程式如下:
对于那些经常使用时间序列的领域来说,几何平均对于获取特定时间间隔(小时、月、年等)内的变化度量是有用的。也就是说,随机变量的集中趋势并不是描述数据的唯一有用的统计数据。通常,我们希望分析数据围绕最可能值的分散程度。从逻辑上讲,这将我们引向方差和标准差的讨论。这两种统计数据高度相关,但它们有一些关键的区别:方差是标准差的平方值,在各个领域中,标准差通常比方差更有参考价值。在处理后一个区别时,这是因为方差很难直观地描述,而且方差的单位是模糊的。标准偏差以被分析的随机变量为单位,易于可视化。
例如,当评估给定机器学习算法的效率时,我们可以从几个时期得出均方误差。收集这些变量的样本统计数据可能会有所帮助,这样我们就可以了解这些统计数据的离差。数学上,我们将方差和标准差定义如下
变化
![$$ = E\Big{X}²-2 X E\left[ X\right]+{\left( E\left[ X\right]\right)}² $$
标准偏差
此外,协方差可用于测量一个特征的变化对另一个特征的影响程度。数学上,我们定义协方差如下:
尽管深度学习在建模具有非线性相关性的变量之间的关系方面取得了重大进展,但一些用于更简单任务的估计器需要这一点作为初步假设。例如,线性回归要求这是一个假设,尽管许多机器学习算法可以对复杂数据进行建模,但有些算法比其他算法更擅长。因此,建议在选择估计量特征之前,使用这些先验统计量检查它们之间的关系。因此,这就引出了对相关系数的讨论,相关系数用来衡量变量之间线性相关的程度。数学上,我们这样定义:
相关系数的值可以低至–1,高至 1,下限代表相反的相关性,上限代表完全的相关性。从统计学上讲,相关系数为 0 表示完全没有相关性。在评估机器学习模型时,特别是那些执行回归的模型,我们通常会参考决定系数(R 平方)和均方误差(MSE)。我们认为 R 平方是模型的估计回归线与数据分布拟合程度的度量。因此,我们可以说,这个统计量最广为人知的是给定模型的适合度。MSE 测量从模型预测到观察数据的偏差的平方误差的平均值。我们将两者分别定义如下:
决定系数(R 的平方)
均方误差
关于这些值应该是什么,我将在本文后面详细讨论。简而言之,我们通常寻求比其他估计量具有更高的 R 平方值和更低的 MSE 值的模型。
线性代数
线性代数的概念在机器学习、数据科学和计算机科学中被大量使用。虽然这不是一个详尽的综述,但所有读者至少应该熟悉以下概念。
标量和向量
标量是只有一个属性的值:量值。标量的集合,称为向量,可以有大小和方向。如果在一个给定的向量中有一个以上的标量,我们称之为向量空间的元素。向量空间的区别在于,它是标量序列,可以相加和相乘,并且可以对其执行其他数值运算。向量被定义为 n 个数字的列向量。当我们提到向量的索引时,我们将 I 描述为索引值。比如我们有一个向量 x,那么 x 1 指的是向量 x 中的第一个值,直观的说,把向量想象成一个类似于文件柜内文件的物体。这个向量中的值是单张纸,向量本身是保存所有这些值的文件夹。
向量是本文中讨论的许多概念的主要构件之一(见图 2-3 )。例如,在深度学习模型(如 Doc2Vec 和 Word2Vec)中,我们通常将单词和文本文档表示为向量。这种表示允许我们将大量数据压缩成一种易于输入神经网络进行计算的格式。从这种大规模降维中,我们可以确定一个文档与另一个文档的相似度或相异度,或者我们可以获得比简单贝叶斯推理更好的同义词理解。对于已经是数字的数据,向量提供了一种简单的方法来“存储”这些数据,并将其输入到用于相同目的的算法中。向量(和矩阵)的属性,特别是关于数学运算的属性,允许对大量数据进行相对快速的计算,也提供了对数据集中的每个单独值进行手动运算的计算优势。
图 2-3。
Representation of a vector
向量的性质
向量维数通常由ℝ n 或ℝ m 表示,其中 n 和 m 是给定向量中值的数量。例如,表示具有实分量的 5 个向量的集合。虽然我到目前为止只讨论了一个列向量,但是我们也可以有一个行向量。也可以执行将列向量转换为行向量的转换,称为转置。转置是矩阵/向量 X 的变换,使得 X 的行被写成 X T 的列,X 的列被写成 X T 的行。
添加
让我们定义两个向量和
其中
因此,矢量之和如下:
减法
假设前一个例子中的假设没有改变,向量 d 和 e 之间的差异如下:
逐元素乘法
假设前一个例子中的假设没有改变,向量 d 和 e 的乘积如下:
公理
设 A、b 和 x 是集合 A 中的一组向量,e 和 d 是 b 中的标量。如果某物是向量空间,则下列公理必须成立:
关联属性
关联属性是指给定表达式中括号的重新排列不会改变最终值:
交换性质
交换性是指改变给定表达式中操作数的顺序不会改变最终值:
加法的单位元
哪里。在这种情况下,0 是零向量,或零向量。
加法的逆元素
在这种情况下,对于每个 a := A,存在一个元素–A:= A,我们将其标记为 a:
的加法逆
标量乘法的单位元
标量乘法关于向量加法的分配性
标量乘法关于场加法的分配性
子空间
向量空间的子空间是满足向量空间要求的非空子集,特别是线性组合留在子空间中。这个子集在加法和标量乘法下是“封闭的”。最值得注意的是,零向量将属于每个子空间。例如,由支持向量回归产生的超平面之间的空间就是子空间的一个例子,支持向量回归是一种机器学习算法,我将在后面介绍。在这个子空间中是响应变量的可接受值。
矩阵
矩阵是我们数学复习中线性代数的另一个基本概念。简单地说,矩阵是以行和列排列的数字、符号或表达式的矩形阵列。矩阵有多种用途,但特别是经常用于存储数字数据。例如,当使用卷积神经网络执行图像识别时,我们将照片中的像素表示为三维矩阵中的数字,该矩阵表示由彩色照片组成的红色、绿色和蓝色照片的矩阵。通常,我们将一个单独的像素取为 256 个单独的值,通过这种数学解释,一种难以理解的数据表示成为可能。关于向量和标量,矩阵包含每个单独值的标量,由行和列向量组成。当我们索引一个给定的矩阵 A 时,我们将使用符号 A ij 。我们也称之为。
矩阵属性
根据矩阵是向量的组合的定义,矩阵本身具有许多与向量相同的基本性质。但是,有一些重要的关键差异,特别是关于矩阵乘法。例如,矩阵乘法是理解普通最小二乘回归如何工作的关键特征,也是从根本上理解为什么我们在执行线性回归时会对使用梯度下降感兴趣。也就是说,矩阵的性质将在本节的其余部分讨论。
添加
假设 A 和 B 都是 m×n 维矩阵:
纯量乘法
让我们假设 A 和 B 都是 m×n 维矩阵
调换
矩阵的类型
矩阵有多种形式,通常用它们呈现的形状来表示。虽然矩阵可以有多个维度,但有许多维度通常会被引用。其中最简单的是方阵,它的区别在于它有相等数量的行和列:
一般来说,读者不太可能碰到正方形矩阵,但是矩阵性质的含义使得讨论它是必要的。也就是说,这使我们讨论不同类型的矩阵,如对角矩阵和单位矩阵。对角矩阵是这样一种矩阵,其中不沿着矩阵的主对角线(从左上角到右下角)的所有条目都是零,由下面给出:
类似于对角矩阵,单位矩阵也具有沿着除矩阵对角线之外的所有条目的值的零。然而,这里的关键区别在于对角矩阵中的所有元素都是 1。这个矩阵由下图给出:
另一个你可能看不到,但从理论角度来看很重要的矩阵是对称矩阵,它的转置等于非变换矩阵。我将在本章中描述转置,但它可以简单地理解为将行转换成列,反之亦然。
我将定义的矩阵的最终类型,特别是牛顿法(第三章描述的一种优化方法)中提到的,是正定和半正定矩阵。如果所有元素都大于零,则称对称矩阵为正定矩阵。但是如果所有的值都是非负的,这个矩阵叫做正半定。尽管在下一章中会有更详细的描述,但这对于理解一个问题是否有全局最优解(以及牛顿法是否可以用来寻找这个全局最优解)是很重要的。
矩阵乘法
与向量不同,矩阵乘法包含独特的规则,这将有助于计划应用这些知识的读者,尤其是那些使用编程语言的读者。例如,假设我们有两个矩阵,A 和 B,我们想把它们相乘。这些矩阵只有在 A 中的列数与 b 中的行数相同的条件下才能相乘。我们称这个矩阵乘积为矩阵 A 和 b 的点积。下面几节讨论矩阵乘法及其乘积的例子。
纯量乘法
假设我们有一个矩阵 A,我们想用它乘以标量值σ。该操作的结果如下图所示:
矩阵中的每个值乘以随后产生的新矩阵中的标量。具体来说,我们可以看到这种关系显示在以下与本征分解相关的方程中。
矩阵乘矩阵乘法
矩阵乘法用于几种回归方法,特别是 OLS、岭回归和 LASSO。这是一种高效而简单的方式来表示独立数据集上的数学运算。在下面的例子中,设 D 是一个 n×m 矩阵,E 是一个 m×p 矩阵,这样当我们将它们相乘时,我们得到如下:
假设维数相等,一个矩阵中的每个元素乘以另一个元素的相应元素,得到一个新矩阵。尽管浏览这些例子似乎毫无意义,但它实际上比看起来更重要——特别是因为所有的操作都将由计算机执行。如果只是为了调试代码中的错误,读者应该熟悉矩阵乘法的乘积。我们将看到不同的矩阵运算,它们也将在以后不同的上下文中出现。
行列向量乘法
对于那些想知道矩阵乘法如何精确地产生单个标量值的人,下一节将对此进行进一步的阐述。如果
那么它们的矩阵乘积由下面给出:
对比:
列向量和方阵
在某些情况下,我们需要用整个矩阵乘以一个列向量。在这个实例中,以下成立:
B 和 C 的矩阵乘积由下式给出:
正方形矩阵
其中最简单的矩阵运算是当我们处理两个方阵时,如下:
按此逻辑:
行向量、方阵和列向量
在其他情况下,我们将对每个具有不同形状的矩阵/向量执行操作:
矩形矩阵
我们最后的例子处理矩形矩阵。对于这个例子,我们有两个矩阵 Z 和 Y,例如:
矩阵乘法属性(两个矩阵)
不可交换
一般情况下,给定两个矩阵 A 和 B,AB ≠ BA,AB 和 BA 可能不同时定义,即使同时定义,也仍然可能不相等。这与普通的数字乘法相反。例如,要口头指定矩阵乘法的顺序,A 与 B 前乘意味着 BA,而 A 与 C 后乘意味着 AC。只要矩阵的元素来自一个有单位元且 n > 1 的环,则该环上存在一对 n×n 非交换矩阵。一个显著的例外是单位矩阵,因为它与每个方阵互换。
分配超矩阵加法
矩阵中的分配性遵循与向量中相同的逻辑。因此,以下公理成立:
左分配性:
右分配性:
这些操作的指标符号分别如下:
标量乘法与矩阵乘法兼容
根据我们前面关于矩阵的标量乘法的讨论,我们在这里看到矩阵的标量乘法的分配性也成立。例如,我们有下面的等式,它是这样证明的:
λ是标量。如果矩阵的元素是实数或复数,那么所有四个量都相等。更一般地,如果λ属于矩阵的元素环的中心,则所有四个都相等,因为在这种情况下。
这是如下的索引符号:
移项
如前所述,矩阵的转置是对矩阵的操作,其中该变换的乘积是新矩阵,其中新矩阵的行是原始矩阵的列,新矩阵的列是原始矩阵的行。给定两个矩阵 A 和 B
,下面的等式显示了我们如何表示这种变换
其中 T 表示转置,即矩阵中第 I 行与第 I 列的互换。这个恒等式适用于交换环上的任何矩阵,但不适用于一般的所有环。注意 A 和 B 是颠倒的。
指数表示法:
微量
乘积 AB 的迹与 A 和 b 的阶无关,迹也可以认为是一个矩阵的对角线:
指数表示法:
规范
范数是为向量空间中的每个向量分配严格正的长度或大小的函数。在机器学习中,你会遇到许多不同的规范,除了增加分类模型的准确性之外,它们在减少回归模型的 MSE 方面也起着至关重要的作用。例如,岭回归使用 L2 范数在高度多重共线性期间缩小回归系数,而 LASSO 使用 L1 范数将某些回归系数缩小为零。我将在第三章详细回顾这两个回归模型。
在深度学习的背景下,在深度神经网络中添加不同层的实验(其中规范用于对数据执行维度缩减)在一些任务中被证明是成功的。例如,在卷积神经网络中使用 L2 范数层。但是这也可以用作多层感知器中的相异/损失度量,而不是传统的梯度函数。
欧几里得范数
这描述了在ℝ n 的欧几里得空间中一个向量的距离。让我们假设。
L2 常模
这给出了向量内原点到 x 内最后一点的距离,通常称为 L2 范数:
L1 常模
这和 L2 范数是一样的,除了标量不是平方的:
L1 和 L2 标准的形状如图 2-4 所示。
图 2-4。
L1 and L2 norm shapes
注意,在 L1 范数下,我们观察到的是正方形(或立方体),而在 L2 范数下,我们观察到的是圆形(或球形)。在某些情况下,最好在执行回归分析的同时使用 L1 范数来执行变量选择,但是这个问题并不总是存在,我将在第八章中进一步详细讨论。
使用 L1 范数的优势是显而易见的,因为您可以在执行回归的同时执行特征选择。然而,应该注意,在数据集的缩减已经发生之后执行特征选择会鼓励过拟合。第八章更广泛地回顾了构建稳健模型的策略和一般实践,但通常建议读者在将数据拟合到模型之前,在很少或没有进行特征选择的情况下使用 L1 规范。
对于那些对车辆路线问题感兴趣的人来说,出租车(曼哈顿)规范与那些想要关注与运输和/或递送或包裹/人员相关的领域的人相关。出租车定额描述了出租车沿给定城市街区行驶的距离:
绝对值范数是由实数或复数形成的一维向量空间上的范数。绝对值范数已被用来代替其他损失函数或相异函数:
p-范数
设 p ≥ 1 为实数:
该定额的形状如图 2-5 所示。
图 2-5。
P-norm
对于,我们得到出租车范数,对于
,我们得到欧几里德范数,对于
,我们得到无穷范数或最大范数。p-范数与广义均值或幂均值相关。然而当
时,我们没有得到离散定义的范数,因为它违反了三角形不等式。三角形不等式表明,三角形的任何一条边都必须小于或等于其他两条边之和。
矩阵范数
矩阵范数是来自的函数,它满足给定数量的属性,用||A||表示,给定矩阵 A。
这些属性如下:
内部产品
机器学习文献中经常提到的一种重要的向量空间是内积。向量空间的这个元素允许某人知道向量的长度或者两个向量之间的角度。此外,还可以从内积中确定赋范向量空间。具体地,内积是在支持向量机的内核中使用的函数,用于计算支持向量机从输入空间放入特征空间的数据的图像。的内积空间是一个函数〈.,.〉定义如下,其中 u 和 v 是矢量, :
对于要成为内积的函数,它必须满足三个公理:
共轭对称:
第一个自变量中的线性:
正定性:
内积空间上的范数
内积空间自然具有定义的范数,该范数基于空间本身的范数,由以下给出:
直接从公理出发,我们可以证明如下:柯西-施瓦茨不等式陈述对于一个内积空间的所有向量 u 和 v,以下为真:
只有当且仅当 u 和 v 线性相关时,两边才被视为相等,这意味着它们必须平行,其中一个向量的大小为零,或者一个向量是另一个向量的标量乘数。
证明
第一个证明:展开括号并收集相同的项得到下面的等式:
因为等式的左边是实数的平方和,所以它大于或等于零。照此,以下必然成立:
第二个证明:考虑以下二次多项式方程:
因为接下来 f(x)的判别式是负的,因此下面的情况一定是:
第三个证明:考虑下面两个欧几里德范数 A 和 B:
由算术-几何平均不等式,我们得到
如
于是,就产生了下面的:
正交性
正交性被描述为不相关性的度量或程度。例如,向量的正交变换产生一个向量,使得它与我们变换的向量无关。内积在角度和长度方面的几何解释激发了我们在这些空间中使用的许多术语。事实上,柯西-施瓦茨不等式的一个直接结果是,它证明了定义两个非零向量之间的角度是正确的:
外部产品
两个向量的张量积与前面定义的内积略有关系。张量积是一种创建类似于整数乘法的新向量空间的方式:
特征值和特征向量
特征值是从方阵中导出的数,对应于特定的特征向量,也与方阵相关联。它们一起“提供了矩阵的特征分解”简单地说,矩阵的特征分解仅仅以特征向量及其相应的特征值的形式提供矩阵。特征分解很重要,因为它是一种“我们可以找到包含矩阵的函数的最大值(或最小值)的方法。”
特征分解:
其中 A =方阵,u =矩阵 A 的特征向量(如果向量乘以 A 后长度改变):
假设以下也成立:
因此:
对于大多数应用,特征向量被归一化为单位向量,如下:
此外,a 的特征向量放在矩阵 u 中。u 的每一列是 a 的特征向量。特征值存储在对角矩阵^中,其中矩阵的迹或对角线给出特征值。由此我们相应地改写第一个方程:
图 2-6 给出了特征向量的图示。
图 2-6。
Visulaization of eigenvectors
本征向量和本征值成为理解一种技术不可或缺的一部分,这种技术将在我们后面关于称为主成分分析(PCA)的变量选择技术的讨论中讨论。对称半正定矩阵的特征分解产生特征向量的正交基,每个特征向量具有非负特征值。PCA 研究变量之间的线性关系,并对输入数据集的协方差矩阵或相关矩阵执行。对于协方差或相关矩阵,特征向量对应于主分量,特征值对应于由主分量解释的方差。相关矩阵的主分量分析为观测数据的空间提供了标准正交本征基:在该基中,最大的本征值对应于与包含观测数据集的最大协变性相关联的主分量。
线性变换
线性变换是两个模块之间的映射,它保留加法和标量乘法的操作。当 V = W 时,我们称之为 V 的线性算子,或自同态,线性变换总是将线性子空间映射到线性子空间上,有时这可以在一个较低的维数上。这些线性映射可以表示为矩阵,例如旋转和反射。使用线性变换的一个例子是 PCA。稍后将详细讨论,PCA 是数据集中的特征到不相关主分量的正交线性变换,因此对于 K 个特征,我们有 K 个主分量。我将在下面的小节中详细讨论正交性,但是现在我将重点放在 PCA 的更广泛的方面。每个主成分保留了原始数据集的方差,但给出了它的表示,这样我们可以根据方差对数据集的贡献来推断给定主成分的重要性。当将其转换为原始数据集时,我们可以从数据集中删除我们认为没有显著差异的特征。
如果以下条件成立,函数称为线性变换:
当我们固定ℝ n 和ℝ m 的基时,线性变换ℒ可以用矩阵 a 来表示。具体地,存在使得下面的表示成立。假设
是给定的向量,x’是 x 相对于ℝ m 的给定基的代表。如果
和 y’是 y 相对于ℝ m 给定基的代表,则
我们称 a 为ℒ关于ℝ n 和ℝ m 的给定基的矩阵表示。
二次型
二次型是多个变量的二次齐次多项式,在机器学习中有应用。具体来说,我们寻求优化的两次可微的函数可以使用牛顿法进行优化。这其中的力量在于,如果一个函数是两次可微的,我们知道我们可以达到一个客观的最小值。
二次型是一个函数,使得以下成立:
其中 Q 是一个 n×n 实矩阵。假设 Q 是对称的——即,不失一般性
矩阵 Q 的子矩阵是通过连续地从 Q 中移除行和列而获得的矩阵的行列式。主要子矩阵是 detQ 本身以及通过移除第 I 行和第 I 列而获得的矩阵的行列式。
西尔威斯特标准
Sylvester 准则是判定矩阵是否半正定的充要条件。简单地说,它说明了一个矩阵是半正定的,所有的主要子矩阵都必须是正的。
证明:如果实对称矩阵 A 有正的非负特征值,称为正定。当特征值恰好非负时,称 A 是正半定的。
一个实对称矩阵 A 有非负的特征值当且仅当 A 可以分解为并且所有的特征值都是正的当且仅当 B 是非奇异的。
前向含义:如果是对称的,那么有一个正交矩阵 P 使得
其中
是一个实对角矩阵,其元素使得它的列是 a 的特征向量。如果
对于每个 I,
存在。
反向蕴涵:如果 a 可以因式分解为 A = B^TB,那么 a 的所有特征值都是非负的,因为对于任何特征对(x,λ)
正交投影
投影是从向量空间到其自身的线性变换 P,使得直观地,这意味着每当 P 被应用于任何值两次时,它给出与它被应用一次时相同的结果。它的图像是不变的,而且这个定义推广了图形投影的概念。
是
的子空间,这个子空间的维数也等于
中线性无关向量的最大数目如果
是ℝ n 的子空间,则
降级的
的正交补由与
中的每个向量正交的所有向量组成,因此,以下为真:
的正交补也是子空间。总之,
跨越ℝ n ,在这个意义上,每个向量
都可以表示为
其中我们称上面的表示为 x 关于
的正交分解,我们说 x 1 和 x 2 分别是 x 到子空间
和
的正交投影。我们写
并且说ℝ n 是
和
的直和,我们说 p 的线性变换是到
的正交投影,对于所有的
我们有
矩阵的值域
矩阵的值域定义了它包含的列向量的数量。
设 A 的值域或图像写为:
矩阵的零空间
两个向量空间之间的线性映射的零空间是
的所有元素的集合,其中
中的零表示
中的零向量
A 的零空间,或者说内核,写作如下:
超平面
前面我提到了支持向量机和超平面的重要性。在回归问题的上下文中,超平面内的观察值作为响应变量解是可接受的。在分类问题的背景下,超平面形成了不同观察类之间的边界(如图 2-7 所示)。
图 2-7。
Visualization of hyperplane
我们将超平面定义为比其周围空间小一个维度的子空间,或者称为围绕对象的特征空间。
设,其中 u i 中至少有一个是非零的。满足线性方程
的所有点的集合
称为空间ℝ n 的超平面。我们可以用下面的等式来描述超平面:
超平面不一定是ℝ n 的子空间,因为一般来说,它不包含原点。对于 n = 2,超平面的方程具有形式,它是一条直线的方程。因此,直线是ℝ 2 中的超平面。在ℝ 3 中,超平面就是普通平面。超平面 h 将ℝ n 分成两个半空间,表示如下:
这里是正半空间,
是负半空间。超平面 H 本身由
的点组成,其中
是超平面的任意点。简单地说,超平面 H 是向量 u 和 x–a 彼此正交的所有点 x。
顺序
实数序列是一个函数,其定义域是自然数 1,2,…,k 的集合,值域包含在ℝ.内因此,一个实数序列可以看作一组数{x 1 ,x 2 ,…,x k },通常也表示为{x k }。
序列的性质
序列的长度被定义为其中元素的数量。有限长度 n 的序列也称为 n 元组。有限序列也包括空序列或没有元素的序列。无限序列是指在一个方向上无限的序列。因此,它被描述为具有第一个元素,但没有最后一个元素。既没有第一个元素也没有最后一个元素的序列称为双向无限序列或双无限序列。
此外,如果每一项都大于或等于前一项,则称一个序列是单调递增的。例如,序列是单调递增的,如果且仅如果对于所有的
,术语非递减和非递增经常用于代替递增和递减,以避免分别与严格递增和严格递减的任何可能的混淆。
如果实数序列的所有项都小于某个实数,则称该序列从上有界。这意味着存在 M 使得对于所有 n,a n ≤ M 。任何这样的 M 称为一个上界。同样,如果,对于某个实数 m,
对于所有大于某个 N 的 N,那么序列从下有界,任何这样的 m 称为下界。
限制
极限是当输入或索引接近某个值时,函数或序列接近的值。一个数称为数列的极限,如果对于任何正ϵ都有一个数 k,使得对于所有的
:
有极限的序列称为收敛序列。非正式地说,一个单无限序列有一个极限,当 n 变得很大时,如果它接近某个值 L,称为极限。如果它向某个极限收敛,那么它就是收敛的。否则就是发散。图 2-8 显示了一个收敛于极限的序列。
图 2-8。
A function converging upon 0 as x increases
我们通常在机器学习和深度学习的背景下谈论收敛,以达到最佳解决方案。这是我们所有算法的最终目标,但随着读者遇到更困难的用例,这变得更加模糊。不是每个解决方案都有单一的全局最优解,相反,它可能有局部最优解。避免这些局部最优的方法将在后面的章节中详细讨论。通常这需要机器学习和深度学习算法的参数调整,这是算法训练过程中最困难的部分。
导数和可微性
可微性成为机器学习和深度学习的重要组成部分,特别是为了参数更新。这可以通过用于训练多层感知器的反向传播算法以及卷积神经网络和循环神经网络的参数更新来看出。函数的导数衡量一个量相对于另一个量的变化程度。衍生产品最常见的例子之一是斜率(y 随 x 的变化),或股票回报(价格百分比随时间的变化)。这是微积分的基本工具,但也是我们将在本书后半部分学习的许多模型的基础。
如果存在一个线性函数和一个向量
,使得
,则认为该函数是仿射的
对于每一个,考虑一个函数
和一个点
,我们想要找到一个仿射函数 A,它在点 x0 附近逼近 f。第一,很自然的强加这个条件:
我们通过ℒ、
的线性度得到
我们还要求 A(x)接近 f(x)的速度要比 x 接近 x 0 的速度快。
偏导数和梯度
偏导数也在各种机器学习推导中大量使用。它类似于导数,只是我们只对函数中的一个变量求导,而其他变量保持不变,而在全微分中,所有变量都要计算。梯度下降算法在第三章中讨论,但是我们现在可以讨论梯度本身的更广泛的概念。当应用于几个变量的函数时,梯度是导数概念的概括。梯度代表函数中增长率最大的点,其大小是图形在该方向的斜率。这是一个向量场,它在一个坐标系中的分量会在从一个坐标系到另一个坐标系时发生变换:
海森矩阵
函数可以不止一次可微,这就引出了海森矩阵的概念。Hessian 是一个标量值函数的二阶偏导数的方阵,或者标量场:
如果函数的梯度在某点 x 为零,那么 f 在 x 处有一个临界点,那么在 x 处的 Hessian 行列式称为判别式。如果这个行列式为零,则称 x 为 f 的退化临界点,或者 f 的非 Morse 临界点,否则为非退化临界点。
雅可比矩阵是向量值函数的一阶偏导数的矩阵。当这是一个方阵时,这个矩阵及其行列式都称为雅可比矩阵:
摘要
这使我们得出了基本统计学和数学概念的结论,这些将在后面的章节中引用。当读者对后面章节中的任何内容不确定时,应该鼓励他们回头再看这一章。接下来,我们将讨论为机器学习算法提供动力的更高级的优化技术,以及那些形成了我们随后将处理的深度学习方法的灵感的相同机器学习算法。
三、最优化和机器学习综述
在我们深入研究深度学习的模型和组件之前,重要的是要解决它所适合的更广泛的领域,即机器学习。但在此之前,我想简单地讨论一下优化。优化是指从一组可用的备选方案中选择最佳元素。大多数机器学习算法的目标是在给定一个函数和一些输入集的情况下找到最优解。正如已经提到的,这通常属于监督学习问题或无监督学习问题的概念,尽管过程大致相同。
无约束最优化
无约束最优化指的是一个我们很难找到最优解的问题。与约束优化相反,我们选择的 x 值是有约束的,这允许我们从更多的途径接近解决方案。无约束最优化问题的一个例子是下面的玩具问题:
图 3-1 显示了该功能。
图 3-1。
Visualization of f(x)
在这个问题中,因为没有约束,所以我们可以在定义的范围内选择 x 的任意值。给定我们寻求最小化的等式,x 的答案是 100。我们可以看到,当我们选择 x 时,我们全局最小化 f(x)的值。因此,我们声明 x = 100 = x*,这是 f(x)的全局极小值。相比之下,这里有一个约束优化问题:
我们想要最小化的函数是一个实值函数,称为目标/成本函数。向量 x 是由独立变量组成的长度为 n 的向量,其中
。这个向量中的变量通常被称为决策变量。集合ω是ℝ的子集,称为约束/可行集。我们说,前面的优化问题是一个决策问题,其中我们必须找到 x 的最佳向量,以满足目标主体的约束。这里,x 的最佳向量将导致目标函数的最小化。在这个函数中,因为我们有一个约束,我们称之为约束优化问题。
被称为集合约束。通常,这采取
的形式
其中 h 和 g 是一些给定的函数。h 和 g 被称为功能约束。
假设我们仍然在查看图 3-1 中显示的同一个函数,只是我们的可行集是ω。为简单起见,我们假设 h(x)和 g (x)等于如下:
因此,约束优化问题的答案将是 x = 10,因为这最接近 f(x)的全局极小值,x = 100,同时还满足ω中列出的函数约束。正如我们所看到的,约束集限制了我们选择解决方案的能力,因此必须做出妥协。我们每天都会遇到实际意义上的约束优化。例如,假设一个企业主正试图将他们工厂的生产成本降到最低。这将是一个受约束的优化问题,因为如果企业所有者不希望对他们的业务产生负面影响(并且仍然继续生产),他们可能会受到产量约束,从而限制了他们可能的选择。
读者将会遇到的大多数机器学习问题都在约束优化问题的范围内,并且该约束通常是被分析的数据集的函数。其原因通常是因为在深度学习模型开发之前,这是我们可以接近人工智能的最接近的方法。概括地说,大多数专注于回归的机器学习算法都是约束优化问题,其目标是在给定的模型内最小化精度损失。正如我们在前面的玩具问题中简要讨论的,有两种极小值:局部极小值和全局极小值。
局部最小值
假设是定义在某个集合
上的实值函数。对于所有的
,点 x*是ω
上 f 的局部极小值。
全局最小化器
假设相同的函数 f 及其三级性质,点 x*是 f 在ω上的全局极小点,如果对于所有
一般来说,在一个给定的问题中可以有多个局部极小值,但是如果有全局极小值,也只能有一个。在图 3-2 中,我们可以看到这与一个函数的映射有关。
图 3-2。
Local versus global minima
根据我们在给定时刻评估的函数的多少,我们可以选择大量的局部最小值。但是如果我们评估这个函数的整个范围,我们可以看到只有一个全局最小值。现在讨论一下我们如何确切地知道我们得出的解决方案,从数学上来说,是最优的,这是很有用的。
局部最小值的条件
在这一节中,我们导出了点 x*成为局部极小点的条件。我们用函数的导数回忆一下,f 的一阶导数,记为 Df 是
f 的梯度只是 Df 的转置。二阶导数,或 f 的黑森,是
一阶导数/梯度给出了函数 f 在特定点的近似值的方向。二阶导数,或 Hessian,给了我们 f 在一点的二次近似。Hessian 和梯度都可以用来寻找最优化问题的局部解。如前所述,梯度用于参数更新,例如在通过梯度下降的线性回归中。然而,Hessian 也可以用于深度学习环境中的参数更新。我将在后面详细讨论这一点,但循环神经网络通常用于对时序或文本段等序列中出现的数据进行建模。具体而言,循环神经网络通常难以通过具有长期数据依赖性的某些数据序列的乘积来训练。在训练其他深度学习架构时,由于权重数量非常大,我们会遇到训练问题。这产生了一个大的海森矩阵,实际上使牛顿的方法失效。
Hessian-free 优化专注于最小化目标函数,其中我们计算矩阵向量乘积,而不是计算 Hessian。假设 Hessian 矩阵是正定的,我们收敛到一个解。通过解下面的方程,我们可以有效地用牛顿法对权重矩阵训练一个网络
其中 H p 是矩阵矢量积,θ是某个参数(在本例中是权重),d 是用户确定的值。
在 Hessian 矩阵不是正定的情况下,不能保证收敛于一个解,并导致完全不同的结果。然而,我们可以使用黑森矩阵的高斯-牛顿近似来近似黑森矩阵,于是黑森矩阵等于
其中 J 是参数的雅可比矩阵。
这产生了一个有保证的正定矩阵,因此验证了保证收敛所必需的假设。从回归向前,我想讨论分类算法的数学基础之一:邻域。
附近
邻域是分类算法范例中的一个重要概念。例如,使用这个概念的优秀算法是 K-最近邻。用户定义的 K 参数是最简单的算法之一,它决定了用于最终将对象分类为一类点的相邻数据点的数量。我们将一个点的邻域定义为包含上述点而不离开该集合的点集。考虑一点。这个集合的一个邻域将是方程
其中ϵ是在给定上下文中定义的某个正数。ϵ表示定义给定邻域大小的界限。视觉上,我们可以把一个邻域看作一个球体,或者两个半空间之间的空间,以 x 为中心,以ϵ为半径,如图 3-3 所示。
图 3-3。
Visualization of a neighborhood of a point x
这对于理解任何使用ε密集损失来定义观测分离的算法是很重要的。ε密集损失尤其用于支持向量机的情况,但是 K-最近邻广泛地利用邻域的概念来定义给定类内的观察值。
内部和边界点
如果集合 S 包含 x 的某个邻域,则称点为集合 S 的内点。如果 x 的某个邻域内的所有点都在 S 中,则称 S 的所有内点的集合为 S 的内部。如果 x 的每个邻域都包含 S 中的一个点和不在 S 中的一个点,则称点 x 为集合 S 的边界点。类似地,S 的所有边界点都命名为边界。如果一个集合包含它的每个点的邻域,或者没有边界点,那么这个集合就是开的。如果一个集合包含它的边界,那么它就是闭的。如果一个集合既闭又有界,那么它就是紧的。
我们现在已经完成了对优化的回顾。现在,我们已经解决了必要的先决信息,我们可以深入讨论机器学习,并掌握这种范式中算法的更广泛的含义。
机器学习方法:监督学习
机器学习可以分为两大类:监督学习和非监督学习。监督学习的区别在于,在拟合模型之前,我们知道标签/响应变量 Y 是什么。因此,我们可以有效地评估模型的功效。在无监督学习中,我们没有这些信息,这不允许我们确定我们正确的程度。在讨论这两种范式的挑战之前,讨论一下这个领域的发展是合理的
机器学习的历史
机器学习是在 20 世纪 50 年代中期开发出来创造人工智能的。它的重点转移到创建程序,这些程序在迭代的基础上得到改进,但是专门用来完成一个任务,并且通常可以被看作是一种函数优化的方法。人工智能最终开始成为自己的领域,随着 20 世纪末的到来,机器学习开始成为一门更加发达和成熟的科学。机器学习从许多领域获得贡献和灵感,如统计学和计算机科学,重叠是如此之多,以至于许多统计程序经常包括并鼓励他们的学生精通这些技术。接下来的章节将讨论一些最常见的机器学习算法,包括一些为以下章节中描述的深度学习模型提供灵感的算法。
什么是算法?
在这之前,我偶尔会提到算法。简单地说,算法是我们为了完成某项任务而创建的过程。在接下来的章节中,在处理深度学习模型之前,我们将回顾除了在数据科学的一般实践中有用的算法之外,将在深度学习模型中使用的重要机器学习算法。
回归模型
回归指的是我们试图预测特定值的一系列问题。这些可能是房价、雇员的工资或者花瓣的长度。更重要的是,回归也可以用来衡量解释变量 x 影响响应变量 y 的程度。
线性回归
假设我们试图预测一个给定节目的电视收视率。根据之前的研究,我们知道这个节目最受欢迎的人群是 25-50 岁的人。我们还看到这两个变量之间有很强的线性相关性。因此,我们决定将此视为我们的解释变量或 x 变量,将评级视为我们的响应变量或 Y 变量。我们到底该如何进行?简单的线性回归是最合理的方法。简单线性回归利用相对基本的概念将解释变量 x 建模为响应变量 y
,
其中β 0 为 y 截距,β 1 至β k 为每个解释变量 x 1 至 x k 对应的偏斜率,其中 k = 1,2,…,m,m =解释变量的个数。这就是所谓的线性概率模型,因为我们对 Y 的期望值建模是基于这样的假设,即 Y 位于普通最小二乘预测的可能点分布中的某个位置。
普通最小二乘法(OLS)
普通最小二乘法是线性回归的最基本形式。我们选择特定点 x 处的特定 E(Y)值的直觉是,我们希望找到一个 E(Y)值,使实际值和预测值之间的平方差最小。当在给定的实验中满足上述假设时,我们发现 OLS 方法会产生 Y 的最小方差和无偏估计值,同时也是 Y 的最大似然估计值
该模型的基本假设如下:
- 误差项呈正态分布。
- 当观察误差项时,存在恒定的方差。
- 数据的观测值是独立同分布的。
- 解释变量之间没有多重共线性。
我们选择特定点 x 处的特定 E(Y)值的直觉是,我们希望找到一个 E(Y)值,使实际 Y 值和预测 Y 值之间的平方差最小。在给定的实验中,当满足上述假设时,我们发现 OLS 方法会产生 Y 值的最小方差和无偏估计值,同时也是 Y 值的最大似然估计值。假设我们有一个 xy 图,类似于图 3-4 所示的图。
图 3-4。
Plotting of the response variable x
理论上,我们可以绘制无限多的 E(Y)线图。然而,只有一个解产生最佳解,使 E(Y)和 Y 之间的误差最小。假设只有一个解释变量,我们得出回归系数如下:
或者,回归系数方程可以写成
给予】
我们在这里所做的目的是最小化回归系数的大小,以便当我们将它乘以 x 时,简单地说,我们试图在数据和回归线之间找到一条最佳拟合线,以便最小化预测和实际数据点之间的平均误差。在我们推导出回归系数后,我们可以找到 y 截距,或 x = 0 时 y 的值,如下:
由此,我们有了 E(Y)方程的所有组成部分,现在可以对数据进行建模。
也就是说,使用 OLS 来寻找解决方案并不总是最佳的方法。对于相对小而简单的数据,利用 OLS 并不是一个特别的问题。当数据复杂且庞大,并且我们不满足 OLS 回归的假设时,利用梯度下降法可能更有效。
梯度下降算法
如前所述,函数的梯度代表函数中增长率最大的点,其大小是图中该方向的斜率。考虑到这一点,我们如何将梯度的概念应用到算法中,以便迭代地改进它呢?梯度下降是一种迭代算法,在这种算法中,根据您定义的某个阈值或一定次数的迭代,通过梯度的负值来更新参数。梯度通常乘以学习率,学习率决定了函数向最优解收敛的速度。
在线性回归的情况下,我们的目标是最小化 y^和 y 之间的残差值,称为误差函数,由
给出
其中 h θ (x i 是预测的 y 值。
如果我们的目标是尽可能快地最小化成本函数,并且梯度是指向最陡方向的向量,则我们想要获取成本函数的梯度。梯度由以下给出:
为了更新参数,包括 y 轴截距和回归系数,我们计算如下,直到算法收敛于最优解:
通过梯度下降的多元线性回归
通过梯度下降的多元线性回归背后的直觉与简单线性回归相同——只有一个修改来适应在每次迭代时被调整的多个部分斜率:
学习率
要讨论的最后一个方面是学习率,表示为α,它实际上是梯度下降算法最重要的方面之一。学习率决定了梯度下降算法收敛到最优解的速度。通常,学习率被初始化为一个相对较小的值,通常为 0.01 或更小。也就是说,选择一个最佳的学习率并不总是显而易见的,不这样做可能会影响“解决方案”的产生。通常,梯度下降算法有两个停止条件:1)找到了最优解,2)达到了允许的最大迭代次数。以下与较差算法性能相关的问题是由以下情况引起的:
- 学习率太小:在我们选择一个太小的学习率的例子中,算法给出的解实际上不是最优解,我们达到最优解是由于第二个停止条件。有些人可能会说,避免这种情况的方法是通过选择学习率来增加迭代次数,但这很可能会破坏这种方法的目的,即其计算效率。
- 学习率太大:如果我们选择一个比需要的学习率大得多的学习率,我们也可能永远达不到一个最优解,尽管这是由于一个不同的原因。当学习率太大时,我们发现每次迭代的成本函数可能会过校正,并给出太小或太大的系数的更新值。因此,我们达成解决方案是靠运气,而且在大多数情况下,我们最终会达成最大的解决方案。
选择合适的学习速度
既然我们已经了解了与选择不正确的学习率相关的问题是什么,我们需要找出如何选择一个学习率。一个可能的解决方案是硬编码各种梯度,并观察算法在每次迭代中的表现。在下面的方法中,我们在梯度下降算法的每次迭代中更新步长。
粗体驱动方法将最近的梯度值与根据先前迭代导出的梯度值进行比较。如果误差已经减小,则适度增加学习速率。如果误差增加,将学习率降低 50%。
在下面的代码示例中,我们正在修改虹膜数据集。这个数据集可以追溯到罗纳德·费雪;他用它做了一系列初步实验。当显示各种统计和机器学习算法的基本方面时,它很受欢迎。这里,我们采用 iris 数据集的第一列,并针对 X 变量(仅仅是长度)进行建模,这样显示的数据就形成了一个线性模式。这只是一个展示 OLS 线性回归机制的例子。在下面的代码中,我们通过lm()
函数将数据拟合到 OLS 回归中。然后,我们计算残差平方和,在输出中表示为Cost
。然后,我们从lm()
函数中提取该模型的回归系数,然后在数据框中输出这两个属性:
#Modifying Data From Iris Data Set
data(iris)
Y <- matrix(iris[,1])
X <- matrix(seq(0,149, 1))
olsExample <- function(y = Y, x = X){
y_h <- lm(y ∼ x)(1)
y_hf <- y_h$fitted.values
error <- sum((y_hf - y)²) (2)
coefs <- y_h$coefficients (3)
output <- list("Cost" = error, "Coefficients" = coefs)
return(output)
}
当我们运行代码时,我们观察到如图 3-5 所示的结果。
图 3-5。
Output of OLS regression function
Cost
是平方和,因此Coefficients
被列为 y 截距,后跟 x 变量的部分斜率。我们将以此为基准,通过梯度下降来比较线性回归的性能。同样,本说明的目的是展示梯度下降算法复制简单 OLS 回归结果的效率和能力:
#Gradient Descent Without Adaptive Step
gradientDescent <- function(y = Y, x = X, alpha = .0001, epsilon = .000001, maxiter = 300000){
#Intializing Parameters
theta0 <- 0
theta1 <- 0
cost <- sum(((theta0 + theta1*x) - y)²)
converged <- FALSE
iterations <- 1
接下来,我们定义一个通过梯度下降实现线性回归的函数。这种梯度下降算法具有恒定的学习速率,但是如果您选择在其他数据集上使用这种实现,您可以改变该参数以及损失容限。我们已经将最大迭代次数定义为 300,000 次,如果在此之前没有达到最优解,将会强制算法在该解处停止。当具体分析代码时,我们首先将参数theta0
和theta1
初始化为 0。用户可以随意修改代码,用从正态分布中随机抽取的值初始化参数,但是应该将这些值除以 10,以确保它们不会过大。我们将cost
函数初始化为 0 减去所有 y 值的 SSR,从这里我们将开始改变参数:
#Gradient Descent Algorithm
while (converged == FALSE){
gradient0 <- as.numeric((1/length(y))*sum((theta0 + theta1*x) - y))
gradient1 <- as.numeric((1/length(y))*sum((((theta0 + theta1*x) - y)*x)))
t0 <- as.numeric(theta0 - (alpha*gradient0))
t1 <- as.numeric(theta1 - (alpha*gradient1))
theta0 <- t0
theta1 <- t1
error <- as.numeric(sum(((theta0 + theta1*x) - y)²))
if (as.numeric(abs(cost - error)) <= epsilon){
converged <- TRUE
}
cost <- error
iterations <- iterations + 1
if (iterations == maxiter){
converged <- TRUE
}
}
尽管我们还没有收敛到一个解决方案,或者我们还没有达到执行函数时允许的最大迭代次数,但是我们创建了gradient0
和gradient1
变量,它们分别对应于参数theta0
和theta1
。然后,我们使用包含在gradient0
和gradient1
变量中的梯度更新theta0
和theta1
参数。此后,我们计算误差,并从while
( converged == FALSE
)继续循环,直到达到停止条件:
output <- list("theta0" = theta0, "theta1" = theta1, "Cost" = cost, "Iterations" = iterations)
return(output)
}
这里,我们运行一个简单的线性回归,其中y
和x
变量被随机初始化。当我们按照说明运行代码时,我们会得到如图 3-6 所示的结果。
图 3-6。
Output of gradient descent without adaptive step function
theta0
是 y 轴截距,theta1
是x
变量的偏斜率,cost
是平方和,iterations
是执行的迭代次数。在这里,我们观察到较低的回归系数,大致相同的基线平方和误差。然而,如果我们使用一个太大的学习率,我们经常会得到一个错误,因为回归系数变得无限大。当学习率太小时,我们会注意到图 3-7 中显示的内容。
图 3-7。
Output of gradient descent with small learning rate
我们看到,该算法没有收敛到最小值,而是达到了一个可行的解决方案,并被我们设置的损失容限切断。顺便说一下,我们也接近了允许的最大迭代次数。错误选择算法的后果与应用给定算法的环境有关。但所有用户都应该谨慎评估他们在任何机器学习或深度学习算法上找到的结果。因此,在选择解决方案时,我们要尽可能地自信,这一点很重要。
在下一个示例中,我们使用自适应步长运行相同的算法来比较性能:
#Gradient Descent with Adaptive Step
adaptiveGradient <- function(y = Y, x = X, alpha = .0001, epsilon = .000001, maxiter = 300000){
#Intializing Parameters
theta0 <- 0
theta1 <- 0
cost <- sum(((theta0 + theta1*x) - y)²)
converged <- FALSE
iterations <- 1
#Gradient Descent Algorithm
while (converged == FALSE){
gradient0 <- as.numeric((1/length(y))*sum((theta0 + theta1*x) - y))
gradient1 <- as.numeric((1/length(y))*sum((((theta0 + theta1*x) - y)*x)))
t0 <- as.numeric(theta0 - (alpha*gradient0))
t1 <- as.numeric(theta1 - (alpha*gradient1))
delta_0 <- t0 - theta0
delta_1 <- t1 - theta1
if (delta_0 < theta0){
alpha <- alpha*1.10
} else {
alpha <- alpha*.50
}
这里,我们应用相同的梯度下降函数,除了现在我们应用大胆的驱动方法,以便我们有一个自适应的学习率。bold driver 方法基于先前的结果,从一个单独的迭代到下一个迭代改变学习速率。简单来说,如果梯度从一次迭代到下一次迭代增加,学习率增加 10%。如果梯度降低,我们将学习率降低 50%。读者可以随意更改这些参数,对收到的结果进行实验:
theta0 <- t0
theta1 <- t1
error <- as.numeric(sum(((theta0 + theta1*x) - y)²))
if (as.numeric(abs(cost - error)) <= epsilon){
converged <- TRUE
}
cost <- error
iterations <- iterations + 1
if (iterations == maxiter){
converged <- TRUE
}
}
output <- list("theta0" = theta0, "theta1" = theta1, "Cost" = cost, "Iterations" = iterations, "Learning.Rate" = alpha)
return(output)
}
执行此功能后,我们会收到如图 3-8 所示的结果。
图 3-8。
Output of gradient descent with adaptive learning rate functions
在我们测试的算法中,考虑到我们的目标是最小化成本和回归系数大小,通过梯度下降和自适应步长的线性回归或 OLS 方法是可以接受的。作为一个有趣的观察,该算法收敛到这个解决方案明显快于梯度下降法与静态学习率。
牛顿方法
对于我们寻求最小化二次函数的例子,牛顿的方法经常被证明是有用的。牛顿法是一种求函数根的方法,或者 f(x)等于 0 的地方。它是由艾萨克·牛顿和约瑟夫·拉弗森发明的。为了计算一个最佳点,我们推导出方程式
其中 f’是给定函数的一阶导数,f”是给定函数的二阶导数。这就是所谓的割线法。如果 f’(x)> 0,牛顿方法工作得特别好,但是如果 f”(x)< 0,它可能不会收敛到全局最小值。我们知道,如果函数的 Hessian 是半正定的,牛顿函数将总是收敛到全局最优。牛顿方法的另一个缺点是,如果起始点离全局最小值相当远,就不能保证收敛。在牛顿法不收敛于全局最小值的情况下,有一种启发式方法可以用来克服这一点,这将在下一章中介绍。
勒文伯格-马夸特启发式
Levenberg-Marquardt (LM)算法最适用于函数不是二次可微或其 Hessian 矩阵不是正定的情况。这个方程由下面给出:
考虑一个非正定的方阵 F。这个矩阵的特征值可能不是正的,但都是实数。考虑一个矩阵
其中G 的特征向量为
。因此,以下必然成立:
通过这种修改,G 的所有特征值都是正的,那么 G 必须是正定的。如果μ也足够大,我们可以确定牛顿算法选择的方向将总是朝着最陡下降的方向。对算法的最后修改将是增加一个步长:
什么是多重共线性?
多重共线性是许多数据科学家在解决问题时都会遇到的问题。在这种情况下,解释变量与其他变量几乎完全相关。在这种情况下,通过 OLS 或梯度下降使用线性回归变得很困难,因为该技术不能精确地估计回归系数,经常导致这些参数的值被夸大。这是因为很难区分一个解释变量对另一个解释变量的影响,以及随后每个解释变量对响应变量的影响。作为多重共线性的产物,我们观察到每次初始化线性回归算法时,回归系数的值都会发生变化,有时变化幅度很大。最终,这使得传统的线性回归成为处理显示这些类型模式的数据的不太可取的方法。
多重共线性测试
非常高的正回归系数是多重共线性的第一个迹象。除此之外,我们还应该计算所有解释变量之间的相关性。相关系数也应该引起数据科学家的警惕。不过,具体来说,我们可以使用一个统计数据来确定我们的数据集中是否绝对存在多重共线性,该统计数据称为方差膨胀因子。
差异通货膨胀系数(VIF)
VIF 统计是在从开始的范围内计算的。通常,经验法则是任何 VIF 分数为> 5 表示多重共线性,任何超过 10 的分数表示严重多重共线性。统计数据的计算方法是将一个给定的解释变量与其他变量进行回归,然后使用结果来计算决定系数,得出以下结果:
里脊回归
为了解决多重共线性问题,岭回归被开发出来,并且是一种有用的技术。与我们之前对规范的讨论(L1 对 L2)相关,岭回归使用 L2 规范来获得最优解。岭回归方程如下:
岭回归的关键区别之一是调整参数λ,它决定了回归系数收缩的程度。由于 L2 范数形成了一个球形或圆形区域,其中回归系数的最优解沿着该形状的“脊”选择,因此该技术被命名为脊。从视觉上看,这通常类似于图 3-9 。
图 3-9。
Ridge regression OLS estimates
最小绝对收缩和选择算子(LASSO)
Lasso 与岭回归非常相似,只是 LASSO 在回归解释变量和响应变量时执行变量选择。LASSO 和岭回归之间的主要区别在于,LASSO 使用 L1 范数而不是 L2 范数,根据数据的维度为选择区域提供正方形或立方体形状。在图 3-10 的中,我们可以看到拉索 OLS 估计:
图 3-10。
LASSO regression
比较岭回归和 LASSO
这两种方法在数据存在多重共线性的情况下都非常有用,但在试图像简单线性回归那样拟合数据的情况下,应避免使用这两种方法,而应使用之前给出的梯度法和 OLS 法。如果你没有一个以上的解释变量,这些方法将不会对你有用。尽管这在大多数情况下不太可能发生,但是记住这一点还是很重要的。
评估回归模型
除了构建回归模型之外,我们还需要找到一种方法来确定模型得出的结果有多准确,并最终根据具体情况选择最佳模型。在回归的情况下,评估机器学习模型的有用方法是通过自举。通常,bootstrapping 涉及使用比原始数据集更小的数据集和随机排列的原始观察值在多次迭代中运行不同的回归模型,然后对几个统计数据进行采样,并将它们的值与其他模型的值进行比较。流程如下:
- 建立几个模型。
- 收集样本统计数据,我们在 N 次实验迭代中使用这些数据作为每个模型的评估者。
- 对每个评估者进行采样,并在每次迭代时收集统计数据,例如:
- 平均
- 标准偏差
- 最大
- 福建话
- 评估结果,并根据您的目标和情况选择最有效的模型。
本节的其余部分将介绍在引导过程中应该选择的赋值器。
决定系数(R 2
如第二章所述,决定系数是我们用来评估模型通过 x 的可变性解释 y 的可变性的准确程度。R 2 值越高越好。也就是说,一般来说,“好”的 R 2 值应该在以下范围内:. 70 ≤ R 2 ≤ .95。任何低于 0.70 的都应被视为一般不可接受,任何高于 0.95 的都应被检查,以查看模型中是否存在过度拟合。虽然这在给定的迭代中不会有很大的变化,但是我们仍然应该在模型中客观地评估这一点。
均方误差
MSE 测量 y 的给定预测值与实际响应变量的平均值之间的距离。我们对任何回归模型的目标都是尽可能地最小化这个统计量,所以我们希望选择相对于其他模型具有最低 MSE 的模型。这将是显示跨模型的最大差异的评估器,并且应该是在我们应该选择哪个模型方面给我们最大推理能力的评估器。
标准误差
在回归模型的情况下,我们可能会测量给定模型的标准误差。我们的目标应该是标准误差尽可能接近 0。高度负的或高度正的标准误差值通常是不期望的。
分类
超越预测特定值的情况,我们的数据观察通常属于我们想要给它们贴上标签的某个类别。我们将这种问题范式称为分类问题。为了向读者介绍这些类型的问题,我们从解决这些算法中最基本的问题开始:逻辑回归。
逻辑回归
除了回归,机器学习的一个重要任务是对观察值进行分类。尽管有多项分类算法,我们将从检查二元分类器开始,这种方法通常用作剩余分类器的基线。逻辑回归的名字来源于为其提供动力的函数,即逻辑函数,如图 3-11 所示。
图 3-11。
Visualization of logistic function
函数本身是这样写的:
我们如何对观测值进行分类的直觉很简单:我们为给定的 f(x)值设置一个阈值,如果它达到或超过该阈值,则将其分类为 1,否则分类为 0。在许多情况下,x 变量将被线性回归公式所取代,在该公式中,我们对数据进行建模。因此,f(x)的方程式,或对数几率,将是
其中,π =对数概率,π*是给定阈值。至于我们建立的阈值,这取决于我们想要最大化什么:准确性、敏感性或特异性。
-
Sensitivity/recall: The ability of a binary classifier to detect true positives:
-
Specificity: The ability of a binary classifier to detect true negatives:
-
Accuracy: The ability of a binary classifier to accurately classify both positives and negatives:
在某些情况下,放大这些统计数据可能更有利,但这都是相对于这些算法的应用场合而言的。例如,如果您正在测试手机电池燃烧的可能性,您可能希望确保尽可能减少误报。但是,如果你试图检测某人在约会网站上找到匹配的概率,你可能会想最大化真正的积极因素。使用 ROC 曲线最容易说明这些预测能力之间的权衡关系,该曲线显示了改变π*的值如何影响模型的分类统计。
受试者工作特性曲线
ROC 曲线最初在第二次世界大战期间用于雷达探测的目的,但它的用途很快被考虑到其他领域,统计学就是其中之一。ROC 曲线显示了二元分类器准确检测真阳性并同时通过显示其假阳性率来检查其不准确程度的能力。如图 3-12 所示。
图 3-12。
Example ROC curve plot
在逻辑回归的情况下,给定π的特定阈值,任何特定模型的评估最终由曲线下面积或 AUC 决定。该图的顶点是. 50 AUC 分数,这表明该模型,如果它的分数是这样的话,在分类上并不比随机猜测好。理想情况下,这个 AUC 分数应该接近 1,但是我们通常接受任何≥ .70 的分数。
混淆矩阵
评估分类模型的另一种方法是混淆矩阵,它是分类器预测与给定观察值的实际标签的图形表示。从这个可视化中,我们得到了前面列出的统计值,这些值最终帮助我们准确地评估分类模型的性能。图 3-13 显示了混淆矩阵的可视化示例。
图 3-13。
Confusion matrix
解释混淆矩阵中的值通常是一项由读者决定的主观任务。在某些情况下,误报,比如确定用户是否应该购买某个产品,对于解决手头的问题并没有那么有害。在其他情况下,例如确定汽车发动机是否有故障,假阳性实际上可能是有害的。读者应该意识到他们正在执行的任务,并相应地调整模型以限制假阳性和/或假阴性。
逻辑回归的局限性
逻辑回归只能预测离散的结果。它需要普通线性回归所必需的许多假设,并且数据的过度拟合会变得非常普遍。除此之外,当我们有明显可分的数据时,用逻辑回归分类效果最好。出于这些原因,除了有更复杂的技术可用这一事实之外,一种常见的建模实践是将逻辑回归模型视为基线,通过它我们可以并列其他分类方法并观察其细微差别。
接下来,我们将看一个使用逻辑回归的简单例子。对于那些对该模型生产过程感兴趣的人来说,该数据集将在后面的章节以及第十章中详细引用。:
#Code Redacted, please check github!
#Logistic Regression Model
lr1 <- glm(data[,1] ∼ data[,2] + data[,3] + data[,4] + data[,5] + data[,6] + data[,7],
family = binomial(link = "logit"), data = data)
#Building Random Threshold
y_h <- ifelse(lr1$fitted.values >= .40, 1, 0)
#Construct ROC Curve
roc(response = data[,1], predictor = y_h, plot=TRUE, las=TRUE, legacy.axes=TRUE, lwd=5, main="ROC for Speed Dating Analysis", cex.main=1.6, cex.axis=1.3, cex.lab=1.3)
我们模型的 ROC 曲线如图 3-14 所示。
图 3-14。
ROC curve for logistic regression example
使用前面的代码,我们得到了. 7353 曲线下的面积。考虑到我们之前设置的阈值,该模型的性能可以接受,但可能需要进行更多的调整。
支持向量机(SVM)
在可用的更复杂的机器学习模型中,支持向量机是一种二进制分类方法,它比逻辑回归模型更灵活,因为它们可以执行非线性分类。这是通过其核函数来执行的,核函数是将数据正交投影到新特征空间的方程,对象的分类是作为由范数构造的两个超平面的产物来执行的(参见第二章)。
在线性支持向量机的情况下,我们将响应变量 Y 和解释变量 x 作为输入。我们将这些数据正交投影到特征空间,从而形成分离数据点的超平面。这些超平面的大小由权重的欧几里德范数或 w 确定,向量除了上界和下界之外,分别表示为:
我们不断重复这个过程,直到我们达到一个范数 w,使两个类之间的分离最大化。通过最小化||w||,类的分离被最大化,因为超平面的大小由下面给出:
以下约束也阻止我们允许观察值落在两个超平面之间:
最终落在超平面边界上的观察是最重要的,因为它们是定义分类之间的分离的“支持向量”。这种转变如图 3-15 所示。
图 3-15。
Orthogonal transformation of data via kernel function
除了 SVM 的目标之外,由这些约束形成的优化问题由下面给出:
内核的类型
为了扩展支持向量机的灵活性,已经开发了不同的内核。其中包括以下内容:
- 多项式
- 高斯径向基函数
- 双曲正切
次梯度法在支持向量机中的应用
函数的次梯度被定义为导数对不可微函数的推广。简单来说,就是经过函数导数,但落在导数以下的直线的斜率。当处理不仅仅具有 10⁵ 特征和 10⁵ 观测值的数据时,对 SVM 算法的现代修改已经产生了性能更好的分类模型。我们将优化问题定义为
其中 f 是 w 和 b 的凸函数。此外,这允许我们使用梯度下降方法,因为它们在凸集上特别有效。给定一个成本函数 C(w),定义为实际分类减去预测分类,我们使用梯度下降公式因此如下:
次梯度步长选择方法类似于本章前面描述的 bold driver 方法。一如既往,算法应用的环境应该最终决定使用哪种方法,而不仅仅是哪种方法执行得更好。
支持向量机的扩展
Vladimir Vapnik,Harris Druck,Christopher Burges,Linda Kaugman 和 Alexander Smola 在 1996 年提出的回归方法是支持向量机的更流行的扩展。不同的是,在 SVR 中,我们不关心落在超平面内的观测值。相反,我们只修改超平面的形状来响应落在损失容差区域之外的点,目标是最小化落在该区域之外的这些点的数量。所执行的回归类型可以通过前面列出的相同内核函数再次改变。除此之外,K-均值聚类的一种替代方法是使用高斯核作为正交投影的激活函数。这里,该算法搜索生成超平面,使得包围数据图像的最小球体定义给定的聚类。聚类算法将在本章后面介绍。
与支持向量机相关的限制
支持向量机的主要问题来自于它们为什么如此强大的关键:内核函数。确定使用合适的内核通常被认为是这种技术的最大缺点。当深入研究算法训练的这个方面时,具体地说,损失参数和高斯核的宽度参数的选择并不明显,并且高度受制于使用算法的环境。第二,尽管支持向量机在大型数据集上表现良好,但它们是一种计算昂贵的方法,并且在应用于行业设置时需要足够好的硬件。因此,在研究之外的环境中,或者在任何需要分析实时数据的环境中,使用支持向量机并不总是有意义的。
以下是在 iris 数据集上使用的支持向量机的快速示例:
#Code Redacted, please check github!
require(LiblineaR)
require(e1071)
#SVM Classification
output <- LiblineaR(data=s, target=y_train, type = 3, cost = heuristicC(s))
#Predicted Y Values
y_h <- predict(output, s, decisionValues = TRUE)$predictions
#Confusion Matrix
confusion_matrix <- table(y_h, y_train)
print(confusion_matrix)
当执行我们的代码时,它会产生如图 3-16 所示的混淆矩阵。
图 3-16。
Confusion matrix for support vector machine
机器学习方法:无监督学习
超越我们知道我们试图预测的答案的范式,是深度学习中更模糊的部分,我们试图根据我们的算法进行推理。这个特定的问题子集被称为无监督学习的一部分,或者我们事先不知道答案应该是什么的问题。
k 均值聚类
到目前为止,我们主要谈论了监督学习,但机器学习的另一个重要方面是在无监督学习情况下算法的使用。通常,无监督学习可以作为探索性的研究方法来进行,或者作为实验的主要部分之前的预备步骤来进行。无监督学习的最好例子之一是 K-means 聚类算法。该算法背后的动机是基于它们远离聚类中心的距离来寻找相似的观察值。
分配步骤
这里,我们取数据的观察值,通过计算数据中三个随机观察值的平均值,给出一组初始的 k 平均值。从这一点,我们将每个观察值分配给聚类中心,基于哪一个分配产生最小的聚类平方和,由观察值的平均值和聚类中心平均值
之间的欧几里德范数确定
其中 S =聚类中心,x p 是观察平均值。
更新步骤
然后,我们通过取中心内观察值的平均值来重新计算聚类平均值,然后重复这两步,直到重新分配停止:
K 均值聚类的局限性
K-means 聚类的主要问题是所得到的解通常依赖于均值的初始化位置,因此不能保证收敛到全局最小值。此外,根据所选 K 均值的变化,收敛所需的时间也可能不是特别快。
下面是 K 均值聚类的一个简单示例:
#Upload data
data <- read.table("http://statweb.stanford.edu/∼tibs/ElemStatLearn/datasets/nci.data", sep ="", header = FALSE)
data <- t(data)
k_means <- c()
k <- seq(2, 10, 1)
for (i in k){
k_means[i] <- kmeans(data, i, iter.max = 100, nstart = i)$tot.withinss
}
clus <- kmeans(data, 10)$cluster
summ <- table(clus)
#Removing NA Values
k_means <- k_means[!is.na(k_means)]
#Plotting Sum of Squares over K
plot(k, k_means, main ="Sum of Squares Over K-Clusters", xlab = "K Clusters", ylab= "Sum of Squares",
type = "b", col = "red")
通常,当执行 K-means 聚类时,最困难的部分是确定我们应该选择 K 的哪个值。通常,一个人拥有的聚类越多,其观察值和聚类中心之间的平方和就越低。然而,存在的聚类越多,这些聚类的信息量就越少。因此,挑战变成了 K 个聚类的平方和与尽可能少的聚类之间的权衡,以使观察值合理地可区分。图 3-17 显示了一个帮助我们努力的图。
图 3-17。
Within cluster sum of squares over K clusters
在图 3-17 的曲线图中,我们注意到我们的平方和在开始时急剧下降,但是我们看到值在接近结束时逐渐变小。因此,我们选择一个介于 6 和 8 之间的值是合理的,如果不是,最好接近 6。这遵循上一段中列出的目标,并将为我们提供可操作的见解,或者为包含分类或回归算法要检测的显著差异的数据视图创建一个特征。
期望最大化算法
EM 算法在无监督学习的范例中很流行,可以用于多种目的,例如分类或回归。对用户来说,最具体的用途是,它可以用来估算数据集中缺失的值。我们将在第十一章中展示这种能力。无论如何,EM 算法是一个概率模型,这使它区别于许多机器学习模型,后者往往是确定性的。该算法使用对数似然函数来估计参数,然后最大化找到的期望对数似然。
期望步骤
考虑一组未知值 Z,它是数据集 x 的子集。我们根据给定 x 的 Z 的条件分布来计算参数的对数似然。下面的等式得出参数的最大似然估计的期望值:
最大化步骤
在这一步,我们寻求最大化我们正在分析的给定参数的概率。这个方程由下面给出:
期望值最大化算法的局限性
EM 算法也趋向于非常缓慢地收敛,并且不产生 MLE 的渐近方差-协方差矩阵。除此之外——类似于朴素贝叶斯分类器的相同限制,因为 MLE 估计器假设特征独立——如果被分析的特征实际上不独立,则不建议使用这种方法。以下是用于通过聚类进行分类的 EM 算法的示例:
#Expectation-Maximization Algorithm for Clustering
require(MASS)
require(mclust)
y_h <- Mclust(x_train, G = 3)$classification
print(table(y_h, y_train))
plot(Mclust(x_train, G = 3), what = c("classification"), dimens=c(1,3))
当执行我们的代码时,它会产生如图 3-18 所示的图。
图 3-18。
Iris data clusters from EM algorithm clustering
决策树学习
决策树通常用于各种领域的数据挖掘,提供了一种相对简单的方法来揭示隐藏在数据表面下的洞察力。广义上有两种类型的决策树,它们通常用于回归和分类。决策树是通过创建确定决策流向的规则来构建的。想法是你使用一个漏斗方法,其中第一个规则是最广泛的,你把问题分解成子集,直到最后的“叶子”是确定的最细粒度的方面。
通常与决策树相关的好处是,总的来说,它们相对容易理解,并且通常非常有效。此外,决策树可以比一些机器算法更好地处理缺失数据,而无需替换或更改数据(我们可以只对值或分类进行平均),并且相对于其他建模技术,它们可以快速计算最终值。最重要的是,有各种各样的方法可以用来帮助树有效地学习,当传统的回归方法不能时,它们可以很好地模拟数据。
分类树
分类树类似于回归树。分割通常由二进制变量决定,但它们可以是数字的,也可以是分类的。除此之外,分类树可以进行两种类型的预测:1)点预测,简单地表示类别,以及 2)分布预测,给出每个类别的概率。对于概率预测,树中的每个终端节点产生类别分布。如果叶子对应于答案的序列,由 A = a,B = b,… Q = q 给出,那么下面的等式产生概率:
为了评估分类树,使用前面描述的评估不同分类模型的相同方法。但是我们也引入了平均损失的概念。简单地说,一些错误可能会对准确达到正确的分类造成更大的“损害”。平均损失公式如下:
除此之外,我们可以使用归一化负对数似然来确定模型在不确定或不确定的情况下是否做出了错误的分类。它的公式由
给出
其中 Q(Y = y|X = x)是模型预测的条件概率。在这种情况下,L 也被称为交叉熵。如果完美的分类是可能的,我会是 0。如果分类中存在某种不可约的不确定性,则最佳可能分类器将给出 L = H[Y|X],即给定 X 时 Y 的条件熵。下面是一个分类树的示例:
require(rpart)
#Classification Tree
classification_tree <- rpart(y_train ∼ x_train[,1] + x_train[,2] + x_train[,3] + x_train[,4]
+x_train[,5] + x_train[,6], method = "class")
pruned_tree <- prune(classification_tree, cp = .01)
#Data Plot
plot(pruned_tree, uniform = TRUE, branch = .7, margin = .1, cex = .08)
text(pruned_tree, all = TRUE, use.n = TRUE)
当执行我们的代码时,我们产生了如图 3-19 所示的图。
图 3-19。
Classification tree splits based on Classification tree model fitted above
伴随该模型的混淆矩阵如图 3-20 所示。
图 3-20。
Confusion matrix for classification tree
回归树
该模型的主要目标是最大化作为被分析变量的产物落在给定叶子上的概率。我们寻求最大化我们得到的关于响应变量的信息。这是仿照
其中 I 是信息,C 是决定我们走向的叶子的变量,Y 是响应变量
哪里,
其中
不管我们看的是连续变量还是离散变量,我们计算平方和的方式都是一样的
其中为叶 c 的预测。
使用回归树进行预测的不确定性,类似于在分类树中看到的不确定性,是使用这些模型时值得考虑的问题。首先,这些不确定性是对条件概率的不精确估计。该树也随着响应值的变化而主动变化。如果我们从同一个分布中抽取不同的样本,我们会很理想地想要一个树会有多不同的度量。这可以使用非参数自举来估计。假设数据(x 1 ,y 1 ),(x 2 ,y 2 ),…,(x n ,y n ),我们从数字 1:n 中独立均匀地抽取一组随机整数 J 1 ,J 2 ,…,J n ,并进行替换。然后我们设定
在这里,我们像对待原始数据一样对待这个自举样本,并为其拟合一棵树。经过多次迭代,我们得到了树的 bootstrap 抽样分布。这接近回归树的实际抽样分布。我们的自举树的预测在原始树周围的分布表明了这种分布。
决策树的局限性
通常,构建决策树最困难的部分是选择创建最佳决策树的规则,并选择不太复杂的树大小,这将导致训练集中的过度拟合,或者根本不会产生任何可操作的见解。更糟糕的是,仅仅从训练错误中很难判断出过度拟合的确切时间。为了减轻这些问题,通常鼓励决策树具有足够的训练样本大小。理想情况下,模型与数据吻合得相当好,用于确定方向分裂的规则不应过于复杂。停止标准最终决定了我们何时到达一片叶子。经常使用的规则的例子是当产生的信息减少到低于某个用户确定的阈值时,或者当“父”节点的“子”产生足够小的一组数据点时停止。然而,从这一点向前发展,决策树是相对简单的模型,对于回归问题,它并不总是在复杂数据上表现得很好,对于每个类别有多个级别的分类数据,它也表现得不好。
集成方法和其他启发式方法
对于标准机器学习算法失败的情况,可以通过实际上是多种算法组合的算法来实现精确度的显著提高。我们称之为系综方法。
梯度推进
梯度推进最初由 Leo Breiman 开发,是一种用于回归和分类问题的技术,目的是从较弱的模型中产生较好的模型。它迭代建立模型,优化问题是最小化这个函数的梯度。让我们以模型 F 为例,我们期望它预测值 y h ,目标是最小化平方误差。设 M 是我们想要进行的提升迭代的次数,其中 1 ≤ m ≤ M。我们假设在我们的实验开始时,我们将有一个模型 F m ,我们试图改进它。因此:
梯度提升旨在使比之前的模型更加正确。已经提出的其他损失函数是由
给出的平方误差损失函数
其中
其中 n =数据集 x 内的观察次数。
梯度推进算法
-
Define the optimization problem as
![
where L(y, F(x)) is some differentiation loss function, such as the gradient of the squared loss as shown earlier.
-
Calculate the residuals as given by the following equation for m = 1, …, M:
-
使用具有训练集的初始模型来迭代地提高性能。
-
Calculate γ m via the following equation:
-
重复 2–4 直到收敛。
随机森林
我将在这一章中讲述的最后一个系综方法是随机森林。简而言之,随机森林是几个决策树的组合,使得每个决策树在给定分支评估的特征方面可以被认为是与其他决策树不同的。尽管这些树的长度是同质的,但是每棵树的决策都是相互独立的。我们为给定观测值选择的值通常是回归情况下所有树的平均值,或者是分类中所有树的平均(或最普遍)观测值。
随机森林的限制
随机森林的主要局限性在于,与组成它们的树木相似,它们有过度适应的倾向。我推荐在决策树上使用的相同技术,例如修剪和抢先限制增长,应该在这里使用,以限制树过度拟合的可能性。
贝叶斯学习
贝叶斯学习建立在贝叶斯定理的基础上,并最终在许多机器学习和自然语言处理模型中使用,它通过有向图使用随机变量及其条件依赖的表示。贝叶斯学习用于这样的情况,例如在给定单词所处的上下文的情况下确定该单词的情感,以及基于通常在测试集中规定的性别找到名字是女性还是男性的概率。
朴素贝叶斯分类器
贝叶斯定理的一个简单应用是分类。朴素贝叶斯分类器使用条件概率来确定事件的可能性。假设我们有一个向量,我们想要确定事件 a 的概率。我们将这个方程建模为
其中 P(A|Z)定义为后验概率,P(z|A)是先验概率,P(A)是似然性,P(z)是实例发生的概率(这个几乎总是可以忽略)。现在我们想用这个公式来正确地对观察结果进行分类。对此,我们把这变成一个优化问题,由下面的等式给出:
我们根据使某个事件 a 的概率最大化的值为 y 赋值。虽然这不是使用朴素贝叶斯分类器的唯一方法,但它是将贝叶斯定理应用于分类的一种更常见方法的示例。
贝叶斯分类器的局限性
贝叶斯分类器最大的局限性在于它假设了特征的独立性,这在我们分析数据的许多环境中并不总是如此。一旦确定特征独立性不存在,我们根本不能使用这个分类器:
#Bayesian Classifier
require(e1071)
#Fitting Model
bayes_classifier <- naiveBayes(y = y_train, x = x_train , data = x_train)
y_h <- predict(bayes_classifier, x_train, type = c("class"))
#Evaluating Model
confusion_matrix <- table(y_h, y_train)
print(confusion_matrix)
执行前面的代码时,会产生如图 3-21 所示的混淆矩阵。
图 3-21。
Confusion matrix for Bayesian classifier example
关于调整机器学习算法的最后评论
实践机器学习算法的更困难的部分之一是参数调整的概念,我还没有解决这个问题。可以调整的参数数量取决于所采用的算法,但尽管如此,这仍然是整个学科都面临的挑战。我们已经讨论了为什么确保不发生过度拟合非常重要,这样我们才能获得尽可能稳健的解决方案。一般来说,稳健性通过从一个数据集到下一个数据集的预测能力的稳定性来反映,而过度拟合通过从一个数据集到下一个数据集的预测能力的明显下降来反映。现在,我将在接下来的小节中讨论如何通过方法实现这种健壮性。
50/25/25 交叉验证
用户应该使用一个验证集来进行参数调整,验证集应该是总数据集大小的 50%。然后,用户应该创建两个训练集:一个将用于训练他们调整的算法,另一个用于测试鲁棒性程度/检查过度拟合。可以检查其他百分比分割,以了解性能差异。
一次调整一个参数
如果读者使用的是软件包,而不是算法的自定义实现,可能会有一些参数被设置为默认值。试图一次改变一个以上的参数是困难的,这不仅是为了及时产生算法的结果,而且是因为很难将特定参数的贡献与输出中的变化程度分开。例如,随机森林从单棵树的巨大以及给定模型中允许的树的数量中获得大量的能量。同时增加这两个因素会扭曲我们可以从整体上适当调整算法的能力,最终会导致欠拟合或过拟合。
使用搜索算法调整机器学习参数
希望采用更高级方法的读者被建议密切关注第八章,其中我们深入讨论了可用于选择机器学习算法的搜索算法。尽管仍是一个发展中的研究领域,但通过降低回归算法的误差统计或增加分类算法产生的 AUC 分数,使用 GridSearch 和其他局部搜索算法来选择更好的算法已经取得了显著的成功。
强化学习
强化学习与监督学习的不同之处在于,在监督学习问题中,我们所针对的标签是永远不会给出的。相反,我们关注于在利用模型中的现有知识和我们希望模型从未知环境中找到的知识之间找到适当的平衡。强化学习领域不可或缺的是概率论的这个副主题。在这些类型的问题中,我们假设在一组吃角子老丨虎丨机附近有一个游戏者,他必须决定玩哪些机器,每台机器玩多少次,以及以什么顺序玩机器。当玩时,每台机器从特定于给定机器的概率分布中提供随机奖励。目标是最大化游戏者在这个游戏周期中获得的钱数。向前看,我们通常可以把强化学习问题描述为一个需要对环境进行智能探索的问题,参考多臂赌徒方法中描述的相同目标。
区分强化学习与监督和非监督方法的事实是,我们采取的行动会显著影响我们获得的后续信息,因此强调在给定算法的每次迭代中做出最佳决策。基本算法描述如下:
- 代理人
- 执行给定的操作
- 观察某种结果
- 获得奖励,通常以标量的形式建模
- 环境
- 接收代理执行的操作
- 输出一个观察和一个标量奖励
我们将历史定义为相对于代理人和给定环境发生的观察、奖励和行动的序列,记为。所有随后的观察、奖励和行动都受到历史的影响,因为它存在于给定的实验中。这就是我们定义的状态,表示为
。环境的状态对代理来说是不可见的,所以它不允许代理可能选择的动作有偏差。相反,代理的状态是内部的。信息也有一个状态,它被描述为马尔可夫过程。需要注意的是,由于这是一本深度学习的入门书籍,所以强化学习的应用不会像更高级的书籍那样深入。也就是说,我希望通过阅读这本书,那些目前发现强化学习问题难以解决的人将能够在对本文过程中提出的概念有了坚实的理解后解决这些问题。
摘要
我们现在已经结束了对优化和机器学习的必要组成部分的回顾。这一章,以及前一章,应该作为理解一些更复杂的算法的参考点,我们将在后面的章节中讨论。现在,我们将继续讨论深度学习范式中最简单的模型:单层感知器。
四、单层和多层感知器模型
有了足够的背景知识,是时候开始讨论神经网络了。我们将从两个最常见和最简单的神经网络开始,它们的用例围绕着分类和回归。
单层感知器(SLP)模型
最简单的神经网络模型 SLP 是由研究人员麦卡洛克和皮茨设计的。在许多机器学习科学家的眼中,SLP 被视为人工智能的开端,并为开发其他神经网络模型和机器学习模型提供了灵感。SLP 的架构是这样的,单个神经元由许多突触连接,每个突触都包含一个权重(如图 4-1 所示)。
图 4-1。
Visualization of single perceptron model
权重影响神经元的输出,这在示例模型中将是分类问题。然后,乘以输入的权重的合计值在神经元内被求和,然后被馈入激活函数,标准函数是逻辑函数:
设输入向量和权重向量
。
函数的输出由
给出
当使用逻辑函数时,激活函数如下:
训练感知器模型
我们通过用从正态分布中随机采样的值初始化所有权重来开始训练过程。我们可以使用梯度下降方法来训练模型,目标是最小化误差函数。我们将感知器模型描述为
其中
其中π* =对数概率的阈值,如第三章中逻辑回归所述。
WH 算法
该算法由 Bernard Widrow 和 Macron Hoff 在 20 世纪 50 年代末开发,用于训练 SLP 模型。虽然类似于用于训练神经网络的梯度方法(如前所述),但 WH 算法使用所谓的瞬时算法,由
给出
其中
因此,我们可以将前面的方程式总结如下:
以这种方式,我们有同样的优化问题,我们会在任何传统的梯度方法。我们的目标是通过梯度下降调整应用于数据输入的权重来最小化模型的误差。考虑到分类问题,让我们使用逻辑回归作为我们的基线指标,同时使用 WH 算法将其与固定速率感知器指标和 bold 驾驶员自适应梯度进行比较。
单一感知器模型的局限性
导致后来神经网络模型发展的 SLP 模型的主要限制是,感知器模型只有在处理明显线性可分的数据时才是准确的。这显然在数据更加密集和复杂的情况下变得困难,并且有效地消除了这种技术在实际环境中遇到的分类问题中的有用性。这方面的一个例子是异或问题。假设我们有两个输入,x 1 和 x 2 ,对于这两个输入,给定了响应 y,使得以下为真:
| | x 1 | x2y | | --- | --- | --- | | Zero | Zero | Zero | | one | Zero | one | | Zero | one | one | | one | one | Zero |从下面的例子中,我们可以看到,当任一解释变量等于 1 时,响应变量等于 1,但当两个解释变量彼此相等时,响应变量等于 0。这种情况如图 4-2 所示。
图 4-2。
XOR problem
现在,让我们来看一个使用 SLP 的例子,其中的数据不是严格线性可分的,以了解该模型的表现。对于这个例子,我已经创建了一个简单的单层感知器模型的示例函数。对于误差函数,我使用 1 减去 AUC 分数,因为这将给我们一个数字量,这样我们可以通过使用梯度下降的反向传播来训练权重矩阵。读者可以随意使用下一个函数以及更改参数。
我们首先设置一些与通过梯度下降执行的线性回归算法相同的参数。(如果您需要复习梯度下降的细节以及如何将其应用于参数更新,请复习第三章。)这里唯一的区别是,我们使用的误差函数不同于回归中使用的均方误差:
singleLayerPerceptron <- function(x = x_train, y = y_train, max_iter = 1000, tol = .001){
#Initializing weights and other parameters
weights <- matrix(rnorm(ncol(x_train)))
x <- as.matrix(x_train)
cost <- 0
iter <- 1
converged <- FALSE
这里,我们为单层感知器定义一个函数,通过第三章中定义的梯度下降算法,设置类似于线性回归的参数。像往常一样,我们在每次迭代时交叉验证(这部分代码被编辑,请查看 GitHub)我们的数据,以防止权重过度拟合。在下面的代码中,我们为上一节中描述的 SLP 定义了算法:
while(converged == FALSE){
#Our Log Odds Threshold here is the Average Log Odds
weighted_sum <- 1/(1 + exp(-(x%*%weights)))
y_h <- ifelse(weighted_sum <= mean(weighted_sum), 1, 0)
error <- 1 - roc(as.factor(y_h), y_train)$auc
}
最后,我们使用梯度下降训练我们的算法,误差定义为 1–AUC。在下面的代码中,我们定义了重复的过程,直到我们收敛到最优解或允许的最大迭代次数:
#Weight Updates using Gradient Descent
#Error Statistic := 1 - AUC
if (abs(cost - error) > tol | iter < max_iter){
cost <- error
iter <- iter + 1
gradient <- matrix(ncol = ncol(weights), nrow = nrow(weights))
for(i in 1:nrow(gradient)){
gradient[i,1] <- (1/length(y_h))*(0.01*error)*(weights[i,1])
}
(Next section redacted, please check github!)
和往常一样,读者评估他们的实验结果是有用的。图 4-3 显示了除最后一次 AUC 评分之外的 AUC 评分汇总统计数据,并绘制了各自的 ROC 曲线:
图 4-3。
ROC curve
#Performance Statistics
cat("The AUC of the Trained Model is ", roc(as.factor(y_h), y_train)$auc)
cat("\nTotal number of iterations: ", iter)
curve <- roc(as.factor(y_h), y_train)
plot(curve, main = "ROC Curve for Single Layer Perceptron")
}
汇总统计数据
Mean Std.Dev Min Max Range
1 0.4994949 0.03061466 0.3973214 0.6205357 0.2232143
请注意,AUC 分数相当差,平均评级比猜测好不了多少。有时,这里的算法会达到稍微好一点的结果,但是这对于部署来说仍然是不够的。这可能是由于类的线性可分性不是很明显,导致了错误分类,每次迭代时都会更新权重矩阵。
现在我们已经看到了 SLP 的局限性,让我们继续这个模型的继任者,多层感知器,或 MLP。
多层感知器(MLP)模型
MLP 与 SLP 的区别在于存在影响模型输出的隐藏层。这个显著的因素也恰好是它们的优势,因为它允许它们更好地处理 XOR 问题。这个模型中的每一个神经元都接收来自一个神经元的输入,或者在输入神经元的情况下接收来自环境的输入。每个神经元由一个突触连接,突触上附着一个类似 SLP 的重物。在引入一个隐藏层后,我们可以让模型表示一个布尔函数,而引入两个层则允许网络表示一个任意的决策空间。
一旦我们超越了 SLP 模型,一个更困难且不太明显的问题就变成了 MLP 的实际架构应该是什么,以及这如何影响模型性能。本节讨论了读者应该记住的一些问题。
收敛于全局最优
根据模型的设计,MLP 模型不是线性的,因此寻找最优解远不像 OLS 回归那么简单。在 MLP 模型中,用于训练的标准算法是反向传播算法,这是早先描述的 Widrow-Hoff 算法的扩展。它最初是由鲁梅尔哈特和麦克莱兰在 20 世纪 80 年代提出的,被认为是训练 MLP 网络的第一个实用方法。这是使用梯度下降训练 MLP 模型的原始方法之一。设 E 为多层网络的误差函数,其中
我们用下面的公式表示输入到隐藏层的单个神经元的加权和值:
同样,我们把从隐藏层到输出层的输出表示如下:
用下面的权重表示:
MLP 模型的反向传播算法;
- 通过从正态分布中取样来初始化所有权重。
- 输入数据,然后通过隐藏层将数据传递到输出层。
- 计算梯度并相应地更新权重。
- 重复步骤 2 和 3,直到算法收敛于可容忍的丢失阈值或达到最大迭代次数。
在概念上回顾了这个模型之后,让我们看一个玩具例子。对多层感知器在实际问题中的应用感兴趣的读者应该特别注意第十章。在下面的代码段中,我们生成新的数据并显示在下面的图中(如图 4-4 ):
图 4-4。
Plotting generated data sequence
#Generating New Data
x <- as.matrix(seq(-10, 10, length = 100))
y <- logistic(x) + rnorm(100, sd = 0.2)
#Plotting Data
plot(x, y)
lines(x, logistic(x), lwd = 10, col = "gray")
本质上,我们有一个逻辑函数,数据围绕它分布,因此围绕这个逻辑函数存在方差。然后,我们定义包含 MLP 模型权重的变量。我使用的是打包的 monmlp,但用户也可以自由地尝试其他包中的实现,如 RSNSS 和 h2o。第十章在从框架访问深度学习模型的背景下简要介绍了 h2o:
#Loading Required Packages
require(ggplot2)
require(lattice)
require(nnet)
require(pROC)
require(ROCR)
require(monmlp)
#Fitting Model
mlpModel <- monmlp.fit(x = x, y = y, hidden1 = 3, monotone = 1,
n.ensemble = 15, bag = TRUE)
mlpModel <- monmlp.predict(x = x, weights = mlpModel)
#Plotting predicted value over actual values
for(i in 1:15){
lines(x, attr(mlpModel, "ensemble")[[i]], col = "red")
}
当绘制 MLP 模型的预测图时,我们看到的结果如图 4-5 所示。
图 4-5。
Predicted lines laying over function representing data
如您所见,在某些情况下,模型会捕捉到一些噪声,这可以从与逻辑函数形状的任何偏差中得到证明。但是所有生成的行总体上都是数据模式下逻辑函数的良好概括。这是 MLP 模型处理非线性函数能力的一个简单展示。虽然是一个玩具例子,但这个概念在实际例子中也是成立的。
MLP 模型的局限性和注意事项
当使用误差是权重的函数的反向传播算法时,收敛到全局最优可能难以实现,这通常是一个问题。如前所述,当我们试图优化非线性函数时,许多局部最小值掩盖了全局最小值。因此,我们可能会被欺骗,认为我们已经找到了一个可以有效解决问题的模型,而实际上我们选择了一个不能有效地达到全局最小值的解决方案(见图 4-6 )。
图 4-6。
Error over weight plot
为了减轻这种情况,应用共轭梯度算法。共轭梯度算法不同于传统的梯度下降法,它的学习速率是在每次迭代时调整的。已经发展了许多类型的共轭梯度法,但是它们都有相同的动机。在 MLP 网络的背景下,我们试图找到最小化误差函数的权重。为了做到这一点,我们朝着最陡下降的方向移动,但是我们以这样的方式改变步长,使得在搜索全局最优时最小化任何可能的“失误”。让我们举一个简单的例子,在这里我们试图解决
其中 x 是未知向量,或 MLP 网络中的权重向量,A 是解释变量的矩阵,b 是响应变量。现在看二次函数
其中 c 是常数标量。当考虑一个例子,其中 A 是正定的,最小化 f(x)的最优解是计算梯度时的的解,我们发现
意味着最陡下降的方向将等于
因此,我们想用下面的等式调整权重向量 x:
该方法的操作部分是学习速率η的变换。根据定义,当函数相对于学习率的方向导数等于零时,η最小化该函数。根据链式法则:
最后,我们确定学习率如下:
要用多少隐藏层,里面有多少神经元
我们通常只在数据不可线性分离的情况下选择使用隐藏层。每当使用阶跃函数、重侧函数或阈值激活函数时,通常建议使用两个隐藏层。至于使用一个以上的隐藏层,这在很大程度上是不必要的,因为在大多数情况下,使用两个或两个以上的隐藏层所带来的性能提升可以忽略不计。在可能不是这种情况的情况下,通过观察隐藏层数的 RMSE 或另一个统计指标的实验应该被用作决定的方法。通常,当向神经网络模型添加一个层时,这将是简单的编辑函数中的参数,或者在 mxnet(在后面的章节中介绍)等一些深度学习框架的情况下,通过一个全新的函数传递前一层的值。关于在给定的隐藏层中应该有多少神经元,这必须以最小化训练误差为目标进行测试。有人建议它必须在输入和输出层大小之间,永远不要超过输入数量的两倍,捕捉初始数据集的. 70-.90 方差-或者使用下面的公式:。
简而言之,让我们用下面的代码来看看共轭梯度训练方法和使用 R 中的 RNSS 包的传统梯度下降之间的区别:
#Conjugate Gradient Trained NN
conGradMLP <- mlp(x = x, y = y,
size = (2/3)*nrow(x)*2,
maxit = 200,
learnFunc = "SCG")
#Predicted Values
y_h <- predict(conGradMLP, x)
我们首先使用mlp()
函数定义神经网络,其中我们特别将learnFunc
参数表示为 SCG(比例共轭梯度)。我们还使用前面提到的 2/3 规则来选择size
参数(神经网络中神经元的数量)。
现在,让我们比较之前显示的 MLP 模型和我们刚刚构建的这个模型的 MSE:
- 共轭梯度下降训练模型的 MSE:0.03533956
- 梯度下降训练模型的 MSE:0.03356279
虽然在这种情况下只有微小的差别,但我们可以看到,在这种情况下,共轭梯度法产生的 MSE 值比传统的梯度下降法稍差。因此,考虑到保持一致性的趋势,选择梯度下降训练方法将是明智的。
摘要
这一章是对神经网络世界的介绍。接下来,我们将讨论为任务开发的模型,这些任务通常超出了 SLP 和 MLP 模型的适用范围。具体来说,在第五章中,我们将研究用于图像识别的卷积神经网络以及用于时间序列预测的循环神经网络。建议对本章讨论的概念还不太熟悉的读者在继续阅读第五章之前,再次回顾第 2 到第四章,因为第五章中提到的许多概念在这些章节中都有详细介绍。
五、卷积神经网络
类似于第四章中关于多层感知器问题的概念,卷积神经网络(CNN)也有多层,用于计算给定数据集的输出。这个模型的发展可以追溯到 20 世纪 50 年代,当时研究人员 Hubel 和 Wiesel 对动物视觉皮层进行了建模。在 1968 年的一篇论文中,他们详细讨论了他们的发现,这些发现在他们研究的猴子和猫的大脑中识别出了简单细胞和复杂细胞。他们观察到,相对于观察到的直边,简单细胞具有最大化的输出。相反,观察到复杂细胞中的感受野相当大,并且它们的输出相对不受上述感受野内边缘位置的影响。除了图像识别之外,CNN 最初获得并仍然保持其恶名,CNN 还有相当多的其他应用,例如在自然语言处理和强化学习领域。
CNN 的结构和性质
广义地说,CNN 是多层神经网络模型。与 Hubel 和 Weisel 描述的动物视觉皮层的结构一致,该模型可以被视觉地解释,如图 5-1 所示。
图 5-1。
Broad visual display of a CNN
每个区块代表 CNN 的一个不同层,我将在本章后面详细解释。从左到右是输入层、隐藏层(卷积层、汇集层和分离层)和全连接层。在最后一层之后,模型输出一个分类。现在考虑图 5-2 。
图 5-2。
CNN architecture diagram
完全连接的层加强了神经元和相邻层之间的局部连接,如图 5-2 所示。因此,隐藏层的输入是来自该隐藏层之前的层的神经元的子集。这确保了学习的子集神经元产生最佳可能的响应。此外,单元在特征/激活图中共享相同的权重和偏差,使得给定层中的所有神经元都在分析/检测完全相同的特征。
至于 CNN 上下文中的特征,我指的是图像中不同的部分。这就是我们的过滤器与它正在分析的图像部分进行比较的内容,因此它可以确定正在扫描的图像部分与正在分析的特征的相似程度。假设我们有足够的训练数据和足够多的图像类别,这些特征足够明显,有助于区分不同的类别。
想象我们正在看两幅图像,具体来说是 X 和 O,如图 5-3 所示。
图 5-3。
O and X example photo
如果我们把 X 和 O 都想象成不同的图像,那么对于人眼来说,我们可以确定它们是明显不同的字母。它们的区别因素包括 O 的中心是空的,而 X 的中心有两条相交的线。图 5-4 和 5-5 显示了这些数值的特征图示例。
图 5-5。
Feature map of “O”
图 5-4。
Feature map of “X”
这些值通常表示为矩阵中的一个条目,黑色和白色分别为–1 和 1。当处理彩色图像时,每个像素通常被表示为矩阵中的一个条目,对于黑色和白色分别具有值 1 或 256。但是,根据所使用的语言,零索引可能会影响 RGB 值的表示,从而使边界向后移动 1。
CNN 架构的组件
本节涵盖 CNN 架构的组件。
卷积层
这一层是任何给定 CNN 中大多数计算发生的地方,因此是图像通过输入后的第一层。在卷积层中,我们用滤波器扫描图像的一部分。就高度和宽度而言,每个过滤器都不是特别大,但是它们都延伸通过该层的整个长度。
例如,假设我们试图将一幅图像分类为 1 或 0,而实际上这幅图像是 1。想象一下,图像有一个黑色背景,但数字是用白色像素勾勒出来的。图 5-6 显示了该图像的一个示例。
图 5-6。
Example image of “1”
计算机会将值为 1 的白色像素和值为-1 的黑色像素区分开来。当我们通过卷积层输入该图像时,该模型提取图像的独特特征,这些特征通常是最终定义特定图像的颜色、形状和边缘。一旦我们有了给定训练图像的特征,我们就对输入的图像进行所谓的过滤。过滤是获取图像特征的过程,在这种情况下,我们可以将其想象为一个 3 x 3 像素的正方形,并将其与输入图像的一部分匹配,该部分也是一个 3 x 3 像素的正方形。在图 5-7 中,我们可以看到过滤的过程是什么样子。
图 5-7。
Example of a filter
然后,我们将特征的像素数量乘以图像块的相应像素数量。在示例中,我们应该为每个操作获得 1 或–1 的输出。直观上,当像素完全匹配时,它们应该输出为 1,当不匹配时,它们应该输出为–1。最后,我们取像素乘积的平均值。如果图像完全匹配,则平均值应为 1。如果没有,那就比 1 低了相当大的度数。在这种情况下,想象图像补丁和选择的特征根本不匹配。因此,当我们取平均值时,它应该输出到-1。我们将该产品放在我们正在分析的图像片位置的中心,在所谓的特征图上具有给定的特征。这最终将是卷积层的输出,并将用于下一层。卷积层将通过不同的迭代产生多个特征图。在每个可能的位置将特征与给定的图像块进行匹配的过程称为图像卷积。我们将给定 CNN 的特征/激活图表示为
其中 w k 是权重,b k 是偏差,x 是特定像素的值,tanh 是数据中的非线性。下标 I、j 指的是代表特征/激活图的矩阵的条目。权重 w k 最终将特征图中的像素连接到前一层。最终,卷积图层是由前面描述的操作生成的特征地图的堆叠。然后,我们将特征地图放入池层。我们计算输出体积的空间大小为
其中 W =输入音量大小,F =卷积层中感受野的大小,P =零填充量,S =步幅。
汇集层
在连续的卷积层之间,通常放置一个所谓的池层。简而言之,汇集层采用卷积层中生成的特征地图,并将它们“汇集”到一个图像中。汇集层有效地执行维度缩减,因此优先强调空间表示,从而降低模型的复杂性。这可以与决策树中的修剪过程相比较,同样有助于防止给定模型的过度拟合。在原型 CNN 模型中,池层有一个 2 x 2 的过滤器,步幅为 2,输入中的每个深度切片都被向下采样,因此我们相对于高度和宽度移动了 2 个像素。池层中的这些操作有助于丢弃 75%的功能/激活映射。这一层使用最大值运算,在前面提到的例子中,最大值超过 4 个数字,或任何给定特征/激活图中的 4 个像素。
与之前描述的示例保持一致,假设使用 2 x 2 过滤器,我们正在查看 9 x 9 特征地图,其中我们使用以下分数分析左上角:
| .88 | Zero | | Zero | .95 |使用最大值运算时,我们会选择 0.95,因为这是 2 x 2 窗口内的最大值。因为我们的步幅为 2,所以我们向右移动了 2 个像素,这意味着我们看到的是一个 2 x 2 的图像切片,其中该切片的左上角应该是特征图的第三列,直到我们得到一个最大合并图像,这大大减少了模型的不必要的复杂性。作为该层中使用的最大值运算的直接结果,在分析图像时,我们不需要像前一层那样精确,因此这有助于创建一个更健壮的模型,可以更容易地对输入进行分类。我这样说的具体意思是,连接每一层的权重值可以更一般化到它们所接触到的所有训练数据,而不是以 CNN 在样本外表现不佳的方式过度拟合。
决定输出空间大小的函数由
给出
其中
校正线性单位(ReLU)图层
整流器是激活功能的另一个术语。通常,我们将以下函数应用于该层的输入
其中 x =神经元的输入。
当应用于特征图时,我们可以想象特征图中的任何负值现在都为零。具体来说,这有助于勾勒出更接近最相关图像的特征地图。我们对所有的特征地图都这样做,然后得到图像的“堆栈”。
全连接(FC)层
这一层中的任何神经元都连接到前一层中的所有激活图。这一层通常位于用户确定数量的卷积层、池层和 ReLU 层之后。由于先前操作中指定的图像缩小,输入到该层的图像将明显小于原始输入。在这一层,我们扫描简化的图像,这些图像应该对应于每个特征图,并将这里给出的每个值转换为值列表。这个列表对应于我们放入的 k 个图像中的一个。根据本章开头的例子,我们最初输入 1。在移动通过所有层之后,我们取对应于这个图像的分数的平均值,然后这是图像为 1 或 0 的概率。应当注意,该层与卷积层之间的唯一区别在于,卷积层仅连接到输入中的局部区域,并且卷积层体积中的许多神经元共享参数。考虑到这一点,在构建给定架构时,我们还可以在 FC 层和卷积层之间进行转换。
损失层
在这一层,我们将预测的标签与图像的实际标签进行比较。当试图从 k 个可能的特征级别中对对象进行分类时,我们将使用 softmax 损失分类器。为了对特定图像的标签进行回归,使用欧几里德函数也是常见的。它们的功能如下:
-
Softmax loss function :
-
Euclidean loss function :
-
Softmax normalization :
当使用反向传播算法时,我们制作一个混淆矩阵来比较 1 或 0,其中我们将答案的标签减去分配的概率。按照我们一直使用的例子,假设 1 = 1,0 = 0,但当我们输入 1 时,我们只收到 0 的概率为 0 . 85,1 的概率为 0 . 45。因此,我们会有–. 60 的累积误差。然后,我们通过在完全连接的层中显示的权重,使用如前所述的梯度方法,以指定的学习速率来调整每个特征映射像素。我们将权重初始化为 0,并在达到损失容限或达到最大迭代阈值的点停止 CNN。必须考虑前面章节中描述的关于收敛到最优解的相同考虑。
调谐参数
发送到输入层的图像应该多次被 2 整除。常见的图像尺寸为 32 x 32、64 x 62 等。卷积层的过滤器尺寸应该最多为 3 x 3 或 5 x 5,并且应该以不改变输入的空间尺寸的方式执行零填充。对于池层,它们的尺寸应该是 2 x 2,通常步长为 2。使用这些参数,75%的激活将被丢弃。大于 3 的池层会导致分类过程中的过多损失。当描述神经元及其排列时,超参数与这一对话最相关。具体来说,我将提到步幅、深度和零填充。在 CNN 最重要的参数中,步幅是一个固定参数,它决定了滑过过滤器的像素数量。例如,如果步幅为 2,则每次有 2 个像素滑过过滤器。通常,步幅不大于 2,也不小于 1。零填充是输入体积边界周围零的大小。通过控制零填充,我们可以从一层到另一层更仔细地控制激活图和其他输出的大小。最后,深度是指我们为给定实验选择的滤波器数量,每个滤波器都是最终在卷积层中搜索每个图像的内容。
著名的 CNN 架构
-
LeNet:由著名的深度学习研究人员 Yann LeCun 在 20 世纪 90 年代开发,LeNet 是一个相对简单的架构,从各方面考虑。该模型最初的目的是对数字进行分类,读取邮政编码,并执行一般的简单图像分类。这被认为类似于任何开发者首先用给定语言编写的“Hello World”程序,因为它被认为是第一个成功应用于实际任务的 CNN 程序。如图 5-8 所示,涉及的层次如下:
-
input, conv layer, ReLU, pooling layer, conv layer, ReLU, pooling layer, fully connected, ReLu, fully connected, and softmax classifier.
图 5-8。
Visualization of LeNet
-
-
GoogLeNet (Inception):这种架构在 2014 年赢得了 ImageNet 大规模视觉识别挑战(ILSVRC)比赛,以此向 Yann LeCun 的 LeNet 致敬。它是由克里斯蒂安·塞格迪、、杨青·贾、皮埃尔·塞尔马内、斯科特·里德、德拉戈米尔·安盖洛夫、杜米特鲁·埃赫兰、钦文·万霍克和安德鲁·拉宾诺维奇开发的。GoogLeNet 的名字来源于这样一个事实,即该架构的相当多的开发人员在 Google Inc .工作。在他们的论文“用卷积加深”中,他们描述了一种允许“在保持计算预算约束的同时增加网络的深度和宽度”的架构。如图 5-9 所示,建议的结构如下:初始架构的重点是通过前面描述的层中的方向,CNN 模型允许“增加每个阶段的单元数量”,而不会使模型变得太复杂。总的来说,该模型旨在处理各种规模的视觉信息,然后将计算汇总到下一阶段,以便同时分析更高层次的抽象。
-
Input, conv. layer, max pool, conv layer, pooling layer (with max function), inception (2 layers), max pool, inception, inception (5 layers), max pool, inception (2 layers), average pooling layer, dropout, ReLU, softmax classifier.
图 5-9。
GoogLeNet architectureThe focus of the inception architecture is that through the orientation in the layers as described earlier, the CNN model allows for “increasing the number of units at each stage” without doing so to the point where the model becomes too complex. Overall, the model seeks to process visual information at various scales and then aggregate the calculations to the next stage so higher levels of abstraction are analyzed simultaneously.
-
-
AlexNet: Developed by Alex Krizhevsky, Ilya Sutskever, and Geoffrey Hinton , this won the ILSVRC in 2012. Similar in architecture to LeNet, AlexNet uses “non-saturating neurons” and efficiently implements the GPU for the convolution layers. The neurons in fully connected layers are connected to all neurons in the previous layer, response-normalization layers follow the first and second convolutional layers, and the kernel of layers two, four, and five are connected only to the kernel maps in the previous layer, which would be on the same GPU. The architecture is as follows (and shown in Figure 5-10):
图 5-10。
AlexNet architecture
- 卷积(5 层),全连接(3 层),[输出为 1000 路 softmax 分类器]
-
VGGNet:它在 ILSVRC 2014 竞赛中获得了 AlexNet 的第二名。VGGNet 是由牛津大学的卡伦·西蒙扬和安德鲁·齐泽曼开发的。感受野是 3×3,有 1×1 个过滤器,步幅是 2,最大集合大小是 2×2。该架构是这样的,输入通过几个卷积层馈送到三个完全连接的层(第一层和第二层具有 4096 个通道,最后一层是执行 1000 路分类的 softmax 层)。
-
ResNet:2015 年 ILSVRC 的第一名得主,ResNet 拥有 152 层——远远超过了前面提到的网络的数量。它是由微软研究院的何开民、、任和开发的。这种架构的目的是形成一个学习参考层输入的剩余函数的网络,而不是学习未参考函数的网络。最终结果是,网络更容易学习,更容易优化,并通过增加深度来提高精度,而不是通过增加深度来降低精度。
正规化
当多层感知器有不止一层时,它们被认为具有逼近给定目标的能力,这将导致过度拟合。为了防止过度拟合,通常建议对输入数据进行正则化,然而,在 CNN 的情况下,这是一个稍微不同的过程,我们可以使用:1) DropOut,这是从人脑中观察到的现象的灵感中获得的。这是一个给定的隐藏层不被通过的概率,这个概率我们设置为一个超参数。2)随机池,其中随机选择激活。可以说,随机池不需要超参数,可以作为一种启发式方法,与其他正则化技术一起使用。3) DropConnect,它是 dropout 的推广,其中每个连接都可以以 1–p 的概率被丢弃。该层中的每个单元都从前一层中的随机单元输入数据,这些数据在每次迭代时都会发生变化。这有助于确保重量不会过重。4)权重衰减,其功能类似于 L1/L2 正则化,其中我们严重惩罚大的权重向量。
在这些方法中,CNN 对使用 DropOut 有相当大的热情,因为它被证明是一种有效和强大的技术。除了防止过度拟合之外,已经观察到丢弃提高了具有大量参数的网络的计算效率,因为这种形式的正则化实际上导致网络在给定迭代期间变得更小。在所有这些迭代之后,较小网络的性能可以被平均为完整网络的性能的一般预测。第二,可以观察到,丢弃层在网络中引入了随机化的性能,这使得数据内的噪声被平均,使得其对数据内的信号的屏蔽被减小。
使用 L1 正则化也并不少见,但要注意的是,这种情况下的权重向量通常会缩小到 0,有时缩小到足够小,这样我们就可以得到稀疏填充的权重矩阵。这种类型的正则化的负面影响是,由于层之间的“死”连接,包含重要信息的某些层的输入可能变得完全不被注意。相比之下,当你特别想要非常明确的特征选择时,L1 正则化可能会产生明显更好的性能。
传统上,L2 正则化被视为在 CNN 中执行正则化的标准方法,因为它倾向于惩罚异常大的权重,而支持那些相对于整个矩阵而言比例一般较低的权重。与 L1 正则化相比,您会得到一个更加丰富的权重矩阵,这将导致网络从一个给定的层向下一层提供更多的数据。因此,特征选择将比使用 L1 正则化时更不明显。
你应该知道的最后一种正则化是对 L1 或 L2 正则化的补充,通过对给定权重的标准大小施加限制。因此,这将允许参数更新具有硬限制,并因此限制给定网络能够产生的可能解决方案的数量。这将有助于通过限制可能的解决方案来更快地训练网络,并且在最佳解决方案中防止参数在不正确的方向上更新太多。
摘要
我们已经充分讨论了 CNN 的概念,并浏览了目前最新的所有架构。有关 CNN 的应用示例,请参见第十一章,特别是关于图像数据的预处理,这是构建图像识别软件的一个非常重要的步骤。接下来,我们将讨论循环神经网络(RNs)以及在基于时间序列的数据中检测模式的复杂性。
六、循环神经网络
循环神经网络(RNNs)是被创建来处理模式识别范围内的问题的模型,并且基本上建立在关于前馈 MLPs 的相同概念上。不同之处在于,虽然 MLP 根据定义具有多层,但 rnn 没有,而是具有将输入转换为输出的定向循环。本章开始时,我将介绍几个 RNN 模型,并以 RNNs 的实际应用结束。
完全递归网络
假设我们有一个输入 x,我们将其输入到 RNN 模型中,其中我们将状态定义为 h,输入乘以权重矩阵 w。到目前为止,一切都与之前描述的神经网络模型相同,但如前所述,rnn 随着时间的推移对输入执行相同的任务。正因为如此,为了计算一个神经网络的当前状态,我们推导出如下等式:
这里的关键特征是,当神经网络执行这些操作时,它会“展开”成多个新状态,每个新状态都依赖于先前的状态。因为这些网络对每个输入都执行相同的任务,除了模型的功能依赖性之外,rnn 通常被称为具有记忆。图 6-1 显示了一个 RNN。
图 6-1。
Architecture of recurrent neural network
这种形式的 RNN 是在 20 世纪 80 年代开发的。与其他神经网络类似,多层神经元通过权重连接,每个权重通过反向传播方法改变。我们基于评估统计来改变我们的权重,在这种情况下,评估统计是给定时间步长激活单元的加权和。总误差是所有时间步长上所有这些单独加权和的总和。在某些时间步骤中,某些输出单元可能会有教师驱动的目标激活。例如,如果输入序列是对应于说出的数字的语音信号,则在序列末尾的最终目标输出可以是对该数字进行分类的标签。对于每个序列,其误差是由网络计算的所有目标信号与相应激活的偏差之和。对于大量序列的训练集,总误差是所有单个序列的误差之和。
通过时间反向传播训练 RNNs(BPPT)
Sepp Hochreiter 和 Jurgen Schmidhuber 等人被认为是开发深度学习训练方法的最伟大的先驱之一。标准方法被称为时间反向传播(BPTT)。BPTT 与常规反向传播大致相同,只是它是为了处理 rnn 的一个特定问题而创建的,即我们正在通过不同的时间步骤进化一个模型。对于每个训练时期,我们首先在相当小的序列上进行训练,然后逐渐增加上述训练序列的长度。直观上,这通常被设想为对长度为 1,2 到 N 的序列进行训练,其中 N 是该序列的最大可能长度。这里有一个方程更简洁地描述了这种现象
其中 t =时间步长,h = t 处隐藏节点的索引,j = t = 1 步处的隐藏节点,δ =误差。
具体来说,我们可以这样看待这些现象:我们用等式
将 W 定义为输出层的权重矩阵
其中 e o =来自输出层的错误:
我们现在有了 k 个序列,通过它们我们将网络展开成一个常规的前馈网络,我们一直观察到现在。然而,RNN 模型中的循环层同时接受前一层和后一层的输入。为了抵消反向传播误差时由于同时输入而发生的权重变化,我们对每一层接收到的更新进行平均。
Elman 神经网络
RNN 建筑公司还收到了杰弗里·埃尔曼的额外捐款,他创建了以他的名字命名的埃尔曼网络模型。Elman 构建的体系结构主要是用于语言处理算法,但它们也可以用于输入数据是顺序的或基于时间序列的任何问题。图 6-2 显示了 Elman 网络的基本结构。
图 6-2。
Illustration of Elman network
Elman 在该体系结构中包括一层上下文单元,其区别在于它们的功能高度关注先前的内部状态。Elman 网络的关键区别之一是隐藏层的输出也馈入前一层中的上下文单元,但是连接上下文单元和隐藏层的权重具有恒定值 1,使得关系是线性的。此后,输入层和上下文层同时激活隐藏层,于是隐藏层在执行更新步骤的同时也输出一个值。在下一个时期期间,先前描述的训练序列以相同的方式发生,除了这里我们观察到具有上下文单元的层现在采用来自前一时期的隐藏层的值。上下文单元的这一特征通俗地被描述为具有记忆的网络。训练这个神经网络需要许多步骤,步骤的数量最终取决于所选字符串的长度。
神经历史压缩器
消失梯度特指网络早期层中的梯度变得极小。这是由于我们使用的激活函数,通常是 tanh 或 sigmoid。因为这些激活函数将输入“挤压”到相对较小的范围内,使得插值结果更容易,这使得导出梯度变得非常困难。在多个堆叠层之后重复这个挤压输入的过程,当我们反向传播到第一层时,我们的梯度已经“消失”了消失梯度的问题通过神经历史压缩器的创建得到了部分解决,这是一种早期的生成模型,作为循环神经网络的无监督“堆栈”来实现。输入级学习从以前的输入历史预测它的下一个输入。在下一个更高级别的 RNN 中,输入仅包括堆栈中 rnn 子集的不可预测输入,这确保了内部状态很少被重新计算。因此,每个高级 RNN 学习下面 RNN 中信息的压缩表示。通过设计,我们可以从最高级别的序列表示中精确地重构输入序列。当我们使用具有相当可预测性的序列数据时,监督学习可以用于通过最高级别的 RNN 对更深层次的序列进行分类。
长短期记忆(LSTM)
LSTM 是一个越来越受欢迎的模型,它的优势在于处理数据噪音中信号之间未知大小的间隙。LSTMs 是由 Sepp Hochreiter 和 Jurgen Schmidhuber 在 20 世纪 90 年代末开发的,它是通用的,当存在足够多的网络单元时,计算机可以计算的任何东西都可以用 LSTMs 复制,假设我们有一个正确校准的权重矩阵。图 6-3 所示。
图 6-3。
Visualization of long short-term memory network
LSTMs 的应用范围部分解释了它们受欢迎的原因,因为它们经常用于机器人控制、时间序列预测、语音识别和其他任务的领域。与我们在其他 RNN 体系结构中常见的单元不同,LSTM 网络包含块。LSTMs 的另一个关键区别因素是能够长时间“记住”一个给定值,并且模型中的门决定输入序列的几个属性。门的考虑因素包括输入重要性、何时应该保留内存或进行“垃圾收集”并删除数据,以及输出值时间。LSTM 模块的典型实现如图 6-3 所示。标准 LSTM 中的 sigmoid 单位包含等式
其中 s 是挤压函数(在许多情况下,通常是逻辑函数或任何激活函数,如先前模型中所述)。查看图 6-3 ,最左边的 sigmoid 单元将输入馈送到 LSTM 块的“存储器”从这一点开始,图中的其他单元就像门一样,允许或拒绝对 LSTM 存储器的访问。标题为 I 的单元,我们将其表示为图中的输入门,将阻止所有非常小(接近于零)的值进入存储器。图底部的单元“遗忘”门“遗忘”它所记忆的任何值,并将其从内存中丢弃。该图右上角的单元是“输出门”,它决定是否应该输出存储在 LSTM 存储器中的值。有时,我们观察到用以下符号表示的单位:P 或σ。具有求和符号的单元被反馈到 LSTM 块中,以便于在没有值衰减的许多时间步长上记忆相同的值。典型地,该值也被输入到三个门单元,以改善它们各自的决策过程。在 LSTMs 中使用的矩阵的 Haramard 乘积或 entrywise 乘积由以下索引符号给出:
传统 LSTM
上面,我们有一个 LSTM 的层,我们的数据通过它传递
其中 x =输入向量,h t =输出向量,c t =细胞状态,(W,U,b) =参数矩阵和向量,(f t ,i t ,ot=记忆信息,获取信息,输出,分别为,sg = sigmoid 函数,sc =原始双曲正切,sh =原始双曲正切。
培训 LSTMs
BPPT 用于 LSTM,但是由于 LSTM 的特殊特征,我们也可以像传统一样通过 BP 使用梯度下降。LSTMs 中的消失梯度由误差转盘专门处理。LSTMs“记住”它们的反向传播误差,然后反馈给每个权重。因此,规则的反向传播对于训练 LSTM 块在很长的持续时间内记住值是有效的。
RNNs 内的结构阻尼
如果我们使用共轭梯度法,它偏离原始 x 太远,曲率估计变得不准确,我们可能会观察到无法收敛到全局最优。根据 Martens 和 Sutskever 的建议,当使用共轭梯度法时,建议使用结构阻尼。用这种方法,我们惩罚与 x 的大偏差,其中公式由
给出
其中| |δx | |2是偏差的大小。λ,类似于岭回归,用作调整参数。
调谐参数是自适应的,通过类似于第三章所述的 Levenburg-Marq 算法的过程进行选择。建议我们找到一个减速比,由下面的等式给出:
调整参数更新算法
权重在每个时间步长更新,因此增加矩阵中的值会导致输出的剧烈变化:
RNN 的实际例子:模式检测
让我们以尝试预测基于时序的序列数据为例。在这种情况下,我们将尝试预测一年中不同时间的牛奶产量(图 6-4 和 6-5 )。让我们从检查我们的数据开始,以获得对它的理解:
图 6-5。
Visualization of milk data via histogram
图 6-4。
Visualization of sequence
#Clear the workspace
rm(list = ls())
#Load the necessary packages
require(rnn)
#Function to be used later
#Creating Training and Test Data Set
dataset <- function(data){
x <- y <- c()
for (i in 1:(nrow(data)-2)){
x <- append(x, data[i, 2])
y <- append(y, data[i+1, 2])
}
#Creating New DataFrame
output <- cbind(x,y)
return(output[1:nrow(output)-1,])
}
当处理时间序列数据时,我们将不得不执行大量的数据转换。特别是,我们必须创建与给定数据略有不同的 X 和 Y 变量。从dataset()
函数中,我们创建了一个新的 X 变量,它是从原来的 Y 变量开始的时间步长 t。我们创建一个新的 Y 变量,它是从原来的 Y 变量 t + 1 而来的。然后,我们将数据截断一行,这样就可以删除缺失的观察值。接下来,让我们加载并可视化数据(如图 6-4 和 6-5 ):
#Monthly Milk Production: Pounds Per Cow
data <- read.table("/Users/tawehbeysolow/Downloads/monthly-milk-production-pounds-p.csv", header = TRUE, sep = ",")
#Plotting Sequence
plot(data[,2], main = "Monthly Milk Production in Pounds", xlab = "Month", ylab = "Pounds",
lwd = 1.5, col = "cadetblue", type = "l")
#Ploting Histogram
hist(data[,2], main = "Histogram of Monthly Milk Production in Pounds", xlab = "Pounds", col = "red")
如您所见,尽管值的范围看起来很广,但我们的数据在值的频率方面有严重的右偏。
现在您已经直观地理解了我们的数据,让我们继续准备要输入到 RNN 中的数据:
#Creating Test and Training Sets
newData <- dataset(data = data)
#Creating Test and Train Data
rows <- sample(1:120, 120)
trainingData <- scale(newData[rows, ])
testData <- scale(newData[-rows, ])
我建议所有用户在将数据输入到 RNN 之前使用最大-最小缩放,因为这有助于减少给定神经网络的误差。与标准归一化类似,最大-最小缩放会显著缩小输入数据集的范围,但它是通过对 0 到 1 之间的观察值进行分类来实现的,而不是通过返回数据偏离平均值的标准偏差来实现的。完成这一步后,我们可以输入数据。用户可以随意试验这些参数,但是我已经训练了网络以获得良好的性能。
现在让我们评估我们的培训和测试结果(如图 6-6 和 6-7 ):
图 6-7。
Test set performance
图 6-6。
Training data performance
#Max-Min Scaling
x <- trainingData[,1]
y <- trainingData[,2]
train_x <- (x - min(x))/(max(x)-min(x))
train_y <- (y - min(y))/(max(y)-min(y))
#RNN Model
RNN <- trainr(Y = as.matrix(train_x),X = as.matrix(train_y),
learningrate = 0.04, momentum = 0.1,
network_type = "rnn", numepochs = 700, hidden_dim = 3)
y_h <- predictr(RNN, as.matrix(train_x))
#Comparing Plots of Predicted Curve vs Actual Curve: Training Data
plot(train_y, col = "blue", type = "l", main = "Actual vs Predicted Curve", lwd = 2)
lines(y_h, type = "l", col = "red", lwd = 1)
cat("Train MSE: ", mse(y_h, train_y))
#Test Data
testData <- scale(newData[-rows, ])
x <- testData[,1]
y <- testData[,2]
test_x <- (x - min(x))/(max(x)-min(x))
test_y <- (y - min(y))/(max(y)-min(y))
y_h2 <- predictr(RNN, as.matrix(x))
#Comparing Plots of Predicted Curve vs Actual Curve: Test Data
plot(test_y, col = "blue", type = "l", main = "Actual vs Predicted Curve", lwd = 2)
lines(y_h3, type = "l", col = "red", lwd = 1)
cat("Test MSE: ", mse(y_h2, test_y))
训练集和测试集的 MSEs 分别为 0.01268307 和 0.06666131。虽然训练集的 MSE 较低,但这可能只是因为训练集明显大于测试集。通过直观地比较各个图中的曲线,我们可以看到测试性能不如训练集准确。如您所见,训练集和测试集中的实际曲线显示出比 RNN 完全捕获的更高的方差。如果你在读电子书,实际曲线是蓝色的,预测曲线是红色的。
摘要
本章有效地涵盖了最常提到的 RNN 例子。它还引导读者解决时间序列数据问题。第七章讲述了深度学习的一些最新发展,并探索了我们如何利用这些见解来解决更困难的问题。
七、自编码器、受限玻尔兹曼机和深度信念网络
本章涵盖了一些更新、更先进的深度学习模型,这些模型在该领域越来越受欢迎。它旨在帮助您了解数据科学领域的一些最新发展。要了解这些模型在实际环境中是如何应用的,请参见第 10 和 11 章,我们将在实际例子中使用这些模型。
自编码器
在讨论受限玻尔兹曼机(RBM)之前,我想解决一组相关的算法。自编码器被称为特征提取器,因为它们能够学习数据的编码/表示。输入到 RBM 的数据将与我们输入到任何机器学习算法的数据相同,但为了简单起见,我们可以将其想象为一个 M x N 矩阵,其中每一列是一个唯一的特征,每一行是 N 个特征的唯一观察。它是一种无监督的学习方法,使用反向传播找到一种方法来重建自己的输入。由 Geoffrey Hinton 和其他研究人员开发的自编码器解决了如何执行反向传播的问题,而无需明确告诉自编码器要学习什么。
自编码器由两部分组成:编码器和解码器。让我们看一个简单的例子,我们称之为 n/p/n 自编码器架构。该架构由表示,其中以下内容为真:
正集。
- n 和 p 是正整数,其中
- 设
是一个函数,其中
- 设
是一个函数,其中
当目标出现时,
- δ是 L p 范数或一些其他损失/相异函数。
对于任意的和
,自编码器将输入 x 转换为输出向量:
概括地说,我们试图通过使用自编码器来解决的问题最终是一个优化问题——在这种情况下,它是最小化损失/相异度函数。我们把这个问题定义为:
当目标出现时:
线性自编码器与主成分分析(PCA)
对于这个例子,让我们看看主成分分析(PCA)和线性自编码器之间的相似之处。主成分分析的主要目的是找到原始数据集的线性变换,这些变换包含了。当将这种分析转换到原始数据集时,我们使用它来实现降维。第八章更详细地讨论了 PCA,但是我将在这里解释它与线性自编码器的关系。简单地说,PCA 是一种正交线性变换,其中我们寻求最大化每个主成分内的方差,以满足每个主成分彼此不相关的约束。让我们把 y 定义为:
其中和是数据集,
和是正交协方差矩阵。与 PCA 的情况一样,每个主成分应该按照方差递减的顺序列出。我们定义最大方差的方向如下:
根据定义,这是一个约束优化问题,可通过使用拉格朗日乘数来解决。因此,我们可以把这个问题改造成
其中
单层自编码器将产生与 PCA 几乎完全相同的特征向量。也就是说,PCA 在推导过程中假设了一个线性系统,而自编码器则不然。在我们在自编码器中强制线性的情况下,将得到类似的答案。
要查看自编码器的应用,请参见第十一章,其中我们专门使用这些模型进行异常检测,并提高标准机器学习模型的模型性能。
受限玻尔兹曼机
在 20 世纪 80 年代,Geoffrey Hinton、David Ackley 和 Terrence Sejnowski 开发了这种算法,它可以被描述为一种随机神经网络。当时,它代表了深度学习科学的突破,因为它是第一批能够学习数据的内部表示并有能力解决困难的组合学问题的模型之一。标准的受限玻尔兹曼机具有二进制值的隐藏和可见单元,由权重矩阵 W 和偏置权重组成,权重矩阵 W 与给定的一组隐藏单元和可见单元之间的连接相关联。隐藏、可见和偏置单元可以被认为是类似于出现在多层感知器模型中的那些相同的单元。给定这些,一个组态的能量表述如下:
这个能量函数类似于 Hopfield 网络的输出神经元(见图 7-1 ),是一种特殊类型的 RNN。由约翰·霍普菲尔德在 20 世纪 80 年代创建,与其他 RNN 模型一样,输入通常是我们怀疑具有某种潜在模式的数据(例如时间序列)。计算所有输入的加权和,然后将其输入线性分类器,如逻辑函数。我们将输出定义为:
图 7-1。
Visualization of a Hopfield network
将数据输入模型后,网络中的所有节点都会收到特定的值。然后,使用异步或同步更新对网络进行多次迭代。达到停止标准后,将显示神经元内的值。Hopfield 网络的主要动机是发现存储在权重矩阵中的模式。
当回过头来参考 RBM 模型时,构成数据基础的概率分布被定义为
其中 =指数函数,上标为前述能量函数的负值。
RBMs 和二部图具有相似的性质。这样,给定来自可见单元的激活,来自隐藏单元的激活是相互独立的,使得
而个体激活概率分别是
其中 a =激活单位。
RBM 的可见单元的值可以从多项式分布中导出,而隐藏单元的值可以从伯努利分布中导出。在我们为可见单元使用 softmax 函数的实例中,我们有以下函数:
传统上,通过反向传播使用梯度下降来优化 RBM 内部的权重,直到我们收敛到最优解。RBM 最流行的用例之一是填充数据集中缺失的值,特别是在协同过滤的情况下。第十一章看一个执行协同过滤的简单例子。如果你有兴趣阅读关于使用 RBMs 执行这一操作的文章,可以搜索 Salakhutdinov 等人关于使用 RBMs 进行协同过滤的论文( http://www.machinelearning.org/proceedings/icml2007/papers/407.pdf
)。
关于 RBMs 的实现,有几个包您可以随意探索,比如 deepnet、darch 和其他在线实现。如果您觉得足够先进,您也可以寻求创建自己的实现。与此同时,你应该检查深度学习框架的更新,看看他们是否/何时添加 RBM 实现。
对比发散学习
由 Hinton 开发的对比发散(CD)学习是训练受限玻尔兹曼机的标准方法。它基于使用 Gibbs 采样的思想,运行 k 个步骤,用训练集的训练样本初始化,并在 k 个步骤后产生样本。作为无向图模型的训练方法,它有更广泛的应用,但它最流行的用例是 RBM 的训练。我将从定义对数似然的梯度开始讨论:
直观上,我们将对数似然定义为某个参数具有某个值的概率。上面,我们将 sig()函数定义为 signum 函数,它返回输入的符号。
我们用下面的等式定义训练模式 v 的对数似然的梯度:
训练集上的梯度的平均值被给定为
其中
现在,回到我们最初的讨论,我们近似训练模式 v 的对数似然的梯度如下:
每一个参数的导数都是根据 p(v)上期望值的近似值计算出来的。在批量学习中,我们计算整个训练集的梯度。然而,在某些情况下,对训练数据集的子集运行这种近似在计算上更有效,我们称之为小批量。如果我们在执行这种近似时评估训练集的单个元素,这就是所谓的在线学习。在 RBMs 中,我们将重建误差称为实际输入和预测输入之间的差异,该差异从训练开始向前移动时急剧下降。建议使用这一指标,但要谨慎行事。CD 学习近似地优化了训练数据和由 RBM 和吉布斯链的混合率产生的数据之间的 KL 散度。也就是说,如果混合率也很小,重建误差通常可能看起来很小。随着 RBM 内权重的增加,我们通常会观察到混合速率反向移动。但是,较低的混合率并不总是意味着一个模型优于混合率较高的模型。
与其他深度学习模型类似,RBM 权重通常使用从正态分布随机采样的值或其他无穷小的值来初始化。关于学习率,必须考虑梯度方法的相同因素,特别是注意不要选择太大或太小的学习率。也就是说,自适应学习速率可能会导致问题,因为它会给出由于较低的重建误差而导致模型正在改善的外观,然而,如前所述,情况可能并不总是如此。建议每次权重更新一般约为当前权重的倍。初始隐藏偏差和权重通常通过从正态分布中随机选择它们来初始化,这是其他神经网络模型的标准操作程序。
成果管理制内的势头
为了提高 RBM 的学习速度,动量法是一种推荐的方法。想象一个梯度图,如图 7-2 所示。如果我们可以想象一个圆上的一个点所代表的误差,那么当这个点越来越接近最小值时,它会获得“动量”,但如果它试图越过这个点,沿着对面的球体向上移动,它就会失去动量。
图 7-2。
Gradient plot
与传统的梯度下降公式不同,动量法逐渐影响参数更新的速度。我们把动量定义为一个给定时期后仍然存在的速度的百分比;我们假设一个参数的速度随时间衰减。实际上,动量方法使得参数的更新朝着不是最陡下降的方向移动,除了不太复杂之外,与典型的梯度方法一样。使用动量法时,建议将动量参数α设置为. 5。当进一步减小重建误差变得更加困难时,动量应该增加到 0.9。如果我们注意到重建误差的不稳定性——通常表现为偶尔的增量增加——我们会将学习率降低 2 倍,直到这种现象消失。我们将更新参数的动量法定义如下:
重量衰减
权重衰减可视为一种正则化形式,类似于岭回归和/或 LASSO 中的参数正则化。在 RMBs 中,我们通常使用欧几里德范数,我们将其表示为权重的成本。通常,从业者取罚项的导数,并乘以学习率。这防止了学习率改变我们试图优化的目标函数。权重衰减有助于减少过度拟合,以这种方式实现的解决方案不会有异常大的权重或权重总是打开或关闭的单元。它还提高了混合率,参考我们执行的吉布斯采样,使 CD 学习更准确。Geoffrey Hinton 建议最初使用 0.0001 的权重成本。
稀少
一般来说,一个好的模型是具有隐藏单元的模型,这些隐藏单元只在部分时间是活动的。原因是,与活动单元密集的模型相比,活动单元稀疏的模型更容易解释。我们可以通过使用正则化指定一个单元活动的概率来实现稀疏性。这个概率用 q 表示,用
估计
其中 q current =隐藏单元的平均激活概率
要使用的自然惩罚度量是期望分布和实际分布之间的交叉熵:
正如 Hinton 所建议的,我们寻求低至 0.1 9 和高至 0.01 的稀疏目标。我们将衰减率表示为λ,它指的是估计的稀疏度值。这应该不高于 0.99,但高于 0.9。如果我们计算的概率是围绕目标值聚集的,我们应该减少稀疏性成本,对这种建模的一般建议是在收集随机样本时收集平均活动的直方图。
隐藏单元的数量和类型
通常,主要的考虑是我们寻求避免过度拟合。因此,我们通常会尝试使用更少的隐藏单元,而不是更多。特别是,如果观察到的数据非常相似,我们也应该尝试使用更少而不是更多的隐藏单元。然而,如果我们试图实现的稀疏目标恰好落在一个非常小的范围内(或者本身非常小),那么使用比正常情况更多的隐藏单元是合理的。至于单位的类型,我们可以使用高斯可见(和/或隐藏)的,除了 sigmoid 和 softmax 之外的单位分别用下面表示:
深度信念网络
我要介绍的最后一个模型是深度信念网络(DBN),如图 7-3 所示,这是 Geoffrey Hinton 的另一项创新。为了制造 DBN,我们将受限的玻尔兹曼机堆叠在一起,一次训练一层。通常,我们将 DBNs 用于无监督学习问题。
图 7-3。
Visualization of a deep belief network
在 2006 年的一篇论文中,多伦多大学的研究员 Geoffrey Hinton 和 Simon Osindero 描述了一种用于快速学习的算法。具有许多隐藏层的训练网络所带来的困难启发了混合模型的创建。与训练问题相关,该模型的主要吸引力在于,通过设计,存在互补的先验,这允许我们容易地从条件概率分布中提取。这是通过从网络深层的随机配置开始实现的。然后,我们遍历网络的每一层,其中给定层的状态由伯努利试验确定。伯努利函数的参数是从初始“自上而下”传递中的前一层接收的输入中导出的。
快速学习算法(Hinton 和 Osindero 2006)
通过在给定的层中取一个随机状态并对其执行 Gibbs 抽样,从 RBM 中生成数据。简而言之,Gibbs 抽样是一种蒙特卡罗方法,在这种方法中,我们试图根据用户指定的概率分布获得一个序列,但该算法试图近似这个序列。通常,分布是多元的。所选层内的所有单元以并行方式更新,并且重复这一过程,直到我们决定从平衡分布中进行采样。在图 7-4 中,我们可以看到 RBM 的可见层和隐藏层。
图 7-4。
Visualization of restricted Boltzmann machine
每个权重使用一个可见单元 I 和一个隐藏单元 j。当数据向量被“钳制”在可见单元上时,隐藏单元从它们的条件分布中被采样,这是阶乘。对数概率的梯度由下面给出:
当我们最小化 KL 散度时,我们实际上最大化了对数概率。如果你想学习复杂的模型,把单一的模型分解成更小、更简单的模型。过了这一点,就可以依次学习这些模型了。如第三章所述,这种顺序学习的一个例子是梯度推进。基于更高层导出 W 0 的互补先验的假设,学习 W 0 的合理近似。在实践中,我们可以通过假设所有权重矩阵必须彼此相等来实现这个结果。当解决这个约束优化问题时,学习变得比以前容易得多,并且问题本身被简化为学习一个 RBM,于是通过最小化对比差异来获得好的近似解。
算法步骤
- 假设所有权重矩阵都是并列的,学习 W 0 。
- 使用 W 0 T 来推断第一个隐藏层中变量状态的阶乘近似后验分布。
- 学习一个关于 W 0 T 生成的数据的高层抽象的 RBM 模型。
- 重复直到收敛到最优解。
如果模型中较高层次的权重矩阵发生变化,我们肯定会看到模型的改进。如果是数据的真实后验值,则给定的界限变成等式。Hinton 特别提出了一种贪婪的学习方法,如 Neal 和 Hinton (1998)所述。给定构型 v 0 ,h 0 的能量定义为
同一个界
其中 h 0 =初始隐藏层单元的二进制配置,p(h 0 ) =当前模型的先验 h 0 ,以及 =初始隐藏层的二进制配置的概率分布。
摘要
这就结束了对自编码器、RBM 和 dbn 的讨论。这也结束了所有关于深度学习模型的章节。既然我们已经讨论了这些模型,现在是时候将我们的注意力转向实验设计和特征选择技术,以帮助您提高机器学习模型的准确性。
八、实验设计和启发式
在回顾了所有与你将遇到的问题解决相关的机器学习和深度学习模型之后,终于到了谈论构建你的研究的有用方法的时候了,包括正式和非正式的方法。
除了知道如何正确评估开发的解决方案,你还应该熟悉与实验设计领域相关的概念。罗纳德·费雪是 20 世纪杰出的英国统计学家,也是统计学领域最有影响力的人物之一。他的技术在进行实验时经常被引用,即使你没有明确地使用它们,回顾一下也是有用的。
方差分析(ANOVA)
方差分析是一组用于研究数据中各组观察值之间差异的方法。z 和 t 检验的扩展,类似于回归,我们观察响应和解释变量之间的相互作用。我们假设数据中的观察值是独立且同分布的(IID)正态随机变量,残差是正态分布的,方差是齐次的。在多个方差分析模型中,下面是本节其余部分讨论的模型。
单向方差分析
用于比较三个或更多样本空间的平均值。具体来说,它用于由具有两个或更多级别的一个变量/因子执行分类的情况。
双向(多向)方差分析
这类似于单因素方差分析,除了这种模型可用于有两个或更多解释变量的情况。
混合设计方差分析
与之前描述的模型相比,混合设计方差分析的区别在于其中一个因素变量是跨受试者分析的,而另一个因素是受试者内的变量。
多元方差分析
这种方法类似于单因素方差分析和双因素方差分析,只是它特别用于分析多变量样本均值,或者在给定数据集中有两个或更多解释变量时。
讨论了各种方差分析模型后,下一节将讨论评估结果的方法:F 统计量。
f 统计量和 f 分布
以罗纳德·费雪命名的 F 统计量是两个统计方差的比值。f 统计基于 f 分布,一种连续的概率分布(见图 8-1 )。我们把这种分布称为 f 检验的给定检验统计量的零分布。让我们假设我们有变量 A 和 B,使得它们都具有分别具有 n 和 d 自由度的卡方分布,使得
图 8-1。
PDF for F-distribution
比方说,我们正在考虑单向方差分析,并假设一组人群的平均值相等且呈正态分布。我们将 F 统计量定义为
其中 k 是自由度,n 是 n 个响应变量的数量。零假设表示仅使用 x 截距创建的模型和由用户创建的模型产生不可区分的结果(在给定的置信区间内)。另一个假设是,读者创建的模型明显优于仅具有 x 截距的模型。就像测试任何其他统计显著性的度量一样,这是基于我们想要设置的阈值来确定的。(90%的置信度,95%的置信度,以此类推)。
现在,让我们用一个玩具例子来应用和解释我们刚刚提到的概念。对于本例,我们将使用虹膜数据集:
#Loading Data
data("iris")
#Simple ANOVA
#Toy Example Using Iris Data as Y
y <- iris[, 1]
x <- seq(1, length(y), 1)
plot(y)
该数据集将用于在以下实验中创建响应和/或解释变量。在第一个玩具示例中,我们采用 iris 数据集的第一列(代表每次观察的萼片长度)并将其作为解释变量。然而,在我们进行单因素方差分析之前,让我们验证将数据拟合到线性模型所需的假设。我们将从目视检查我们的数据开始,如图 8-2 所示。
图 8-2。
Visualization of data
我们立即注意到,数据在方向上是相当线性的,具有正斜率。这是一个很好的第一个指标,但我们应该更深入地挖掘,以确保我们的其他假设得到满足。在这种情况下,我们将专注于绘制拟合模型的残差。所谓残差,我指的是实际值减去模型预测值的余数。在处理线性模型时,您应该大量使用残差分析,但一般情况下也是如此,因为残差分析提供了对特定模型工作情况以及数据方向的直观了解。在图 8-3 中,我们看到通过拟合 x 和 y 的线性模型创建的以下曲线图:
图 8-3。
Residual plot
plot(glm(y∼x))
注意图 8-3 中显示的四个中右上角的图形。这是一个分位数图,它有效地显示了残差分布的正常程度。当仔细检查该图时,我们可以看到相当数量的数据位于虚线 45 度角线上,这是数据中正常的标记。然而——通常情况下——我们注意到尾部倾向于稍微高于这条线。值得注意的是,我们认为是正态分布的数据几乎总是表现出相似的模式。在现实世界中,当我们拥有足够多的数据时,大多数数据往往接近正态分布,但它不太可能完全呈正态分布。因此,我们在这里接受数据是正态分布的,并继续验证其余的假设。当数据呈正态分布时,它可以符合线性模型,因此我们可以合理地估计 x 变量范围内的值。
因为我们还要求误差呈现恒定方差,所以让我们将注意力转向左上角的图。请注意,此图中的 x 轴表示回归输出的值,而 y 轴详细说明了残差的值。穿过图中心的水平线表示拟合值等于实际值的区域,或者观测值的残差为零的区域。具体参考我们的数据时,我们可以看到,一般来说,从图的左侧到右侧,所绘制的残差的形状似乎是一致的。因此,我们可以说残差实际上表现出恒定的方差。如果不是,我们会注意到散点图的形状中会有明显的图案,从图的左侧到右侧会变得更夸张或不那么夸张。
最重要的是,注意图右下方的情节。它提出了一个重要的概念,有助于理解某些数据点如何改变回归模型的拟合线。杠杆被描述为特定观察值与数据集其余部分之间的相对差异。通过将索引放置在数据点附近,在 R 中表示特别具有高杠杆作用的观察值。我们用
来定义杠杆
其中 n =观察次数,XI= X 的第 I 次观察,= X 内所有观察的平均值,i = 1,2,…,n
与杠杆高度相关的是库克距离(Cook's distance)的概念,它直接估计一个特定的观察对这个回归模型的影响。我们把库克的距离定义为
其中 e i 2 =给定观测值的残差平方,s 2 =模型的均方误差,p =模型中参数的个数,HI= H 矩阵的第 I 条对角线其中 i = 1,2,…,n,n =观测值个数。
通常,如果观察值的 Cook 距离值大于 1 或距离值大于 4/n,我们认为该观察值特别有影响。使用哪个阈值最终取决于您,但很明显,这将取决于具体情况,值得在实验的基础上检查是否提供了更多或更少异常值的数据集,以及这将如何影响您的最终目标。例如,如果实验的目的是异常检测,那么降低阈值使得数据集中更多的噪声被限定为信号可能是愚蠢的。当回头参考我们的特定图时,我们可以看到相当数量的数据点被标记为有影响。在我们选择型号时,我们将牢记这一点。
当评估数据集中的所有图时,我们可以自信地说,尽管存在异常值,并且我们的假设不完全符合,但 OLS 回归的稳健性允许克服这些轻微的偏差。因此,选择 OLS 回归作为这项任务的模型是合理的,因此方差分析将产生具有统计意义的结果。执行代码时,我们观察到以下情况:
simpleAOV <- aov(y ∼ x)
summary(simpleAOV)
Df Sum Sq Mean Sq F value Pr(>F)
x 1 52.48 52.48 156.3 <2e-16 ***
Residuals 148 49.69 0.34
正如当我们在 glm 对象上使用summary()
函数时,我们得到了它的统计显著性的度量。然而,我们得到的不是 Z 分数,而是本例之前提出的概念的 F 分数及其相对 p 分数。在这种情况下,我们可以以大于 99%的显著性说,我们拒绝了零假设的结果。因此,该模型比仅截距模型更适合,因此我们对其结果更有信心。然而,假设我们想要比较多个合适的模型。因此,让我们来看看当我们包含不止一个变量时会发生什么,但是也要研究这两个变量之间的相互作用。
正如我们在下面的代码中看到的,我们在这个模型中使用第二列和第三列作为解释变量。在拟合我们的模型时,我们将两个解释变量相乘。执行代码时,我们会观察到以下结果:
#Mixed Design Anova
x1 <- iris[,2]
x2 <- iris[,3]
mixedAOV <- aov(y ∼ x1*x2)
summary(mixedAOV)
Df Sum Sq Mean Sq F value Pr(>F)
x1 1 1.41 1.41 12.9 0.000447 ***
x2 1 84.43 84.43 771.4 < 2e-16 ***
x1:x2 1 0.35 0.35 3.2 0.075712 .
Residuals 146 15.98 0.11
我们的残差明显更小,并且所有变量在至少 90%的置信区间内具有统计显著性。让我们执行下面的代码,直观地比较图 8-4 中的两个模型:
图 8-4。
Mixed design ANOVA plot
par(mfrow = c(2,2))
plot(glm(y ∼ x1*x2))
dev.off()
我们可以看到,我们需要满足的所有假设都做得非常好。实际上,所有的残差都是正态分布的,如正态 Q-Q 图所示,残差呈现恒定的方差,相当小的一部分具有杠杆作用。因此,当在我们定义的两个模型之间进行选择时,与第一个相比,我们选择第二个是合理的。
这是一个简单的例子,说明我们如何在模型选择过程中使用方差分析。在第 10 和 11 章中,你将学会有效地执行这些关于比较深度学习和机器学习算法的相同分析。
现在让我们在费希尔原理的指导下,更详细地讨论如何组织我们的实验。
费希尔原则
有史以来最杰出的统计学家之一罗纳德·费雪解释了实验设计的原则。以下是对他的原则的描述,以及关于如何实现这些原则的一般建议:
- 实验陈述:你应该明确陈述激发实验的场景,非常明确地给出实验中将要发生的步骤的概要。一般认为,导言应该包括对主题的高层次概述,每一部分都应该更详细地描述不同的组成部分,从实验开始到结束有逻辑地进行。
- 解释及其推理基础:从一开始,给出你可能期望的合理结果是合理的。你应该陈述你认为必须考虑的结果,但要意识到为你的下属提供一个无止境的结果列表可能不会很有帮助。此外,当讨论所有可能的结果时,要为阅读你的研究的人提供可操作的见解。不言而喻,确实能给出可操作见解的研究,会给误用留下更多空间。
- 显著性测试:在评估机器学习和深度学习解决方案的背景下,一个简单的建议是引导用于评估给定模型的测试统计。有理由假设,如果你在足够长的时间内得到足够多的测试统计数据,那么数据将呈正态分布。从这一点出发,可以进行 Z 检验,以确定模型的合理统计置信度。
- 零假设:这种假设应该声明显示的结果没有显著性,并且测试人群之间的任何偏差都是由于一些无关的误差,例如不适当的采样或与适当的实验实践的偏差。这必须是所有统计测试的一个组成部分。
- 随机化:测试有效性的物理基础:当进行测试时,所得到的结果应该以一种结果没有偏差的方式进行。在某些情况下,这可能需要对数据进行随机观察,以消除实验建模中可能导致某些结果的任何固有偏差。
- 统计复制:从测试中得出的结果应该而且必须是可复制的。鉴于数据集和我们预期观察这种情况的环境的固有限制,得出的不合理的结果不如可复制的结果有价值。
- 分组:将不同的实验组划分开来,从而减少或完全防止不同的变异和偏差影响实验结果的过程。
普拉克特-伯尔曼设计公司
Plackett-Burman 设计是由 Robin Plackett 和 J. P. Burman 在 20 世纪 40 年代创建的,是一种寻找解释变量的可量化相关性的方法,在这种情况下我们称之为因子,其中每个因子有 L 个水平。总体目标是使用有限数量的实验来最小化相关性估计的方差。为了实现这个目标,选择一个实验设计,使得任何给定因素对的每个组合在每个实验“运行”中出现相同的次数
Plackett-Burman 设计需要少量实验,特别是 4 到 36 的倍数,并且该设计有 N 个样本,可以研究多达 k 个参数,其中 k = N–1。在 L = 2 的情况下,使用每个元素为–1 或 1 的正交矩阵。这个矩阵也称为哈达玛矩阵。这种方法对于识别不同因素对响应变量的主要影响是有用的,这样我们就可以消除那些似乎影响很小或没有影响的因素。Plackett 和 Burman 自己给出了 L 等于 3、4、5 和 7 的具体设计。
请看图 8-5 中的矩阵,它直观地描述了 Plackett-Burman 设计。当执行实验设计(DOE)时,您必须将适当的行作为设计表的第一行。在这种情况下,我们从+、-、+、-、+、+开始。这是每行中出现的序列的排列,代表一种治疗组合。您可以将治疗组合视为一个功能集的独特组合。然后,通过将前一行中的序列向右移动一列来创建第二行。对剩余的每一行重复这一过程。最后一行显示所有负元素。不过,重要的是要认识到,Plackett-Burman 设计不能描述对一个给定因素的影响是否会导致另一个因素的影响,同样,它也不能知道给定足够小的设计的影响本身。这种设计被认为是数据分析的准备步骤,除了随后采取的其他步骤之外,还建议将替代的准备步骤与其并列。
图 8-5。
Plackett-Burman matrix
填空白
这些方法不需要离散的参数,样本大小的选择与参数总数无关。对于读者想要创建响应面的情况,推荐使用这些方法,但是应该注意,很难分别确定给定或一组参数的主要影响和相互作用。
全因子
全析因是实验设计中最流行的方法之一,其中 N = 2^K,k 等于因子的个数。举个例子,让我们有 k 个因子,其中 L = 2。在这个模型中,在实验发生之前,我们不区分干扰因素和主要因素。假设 L = 2,我们将它们表示为高电平“h”或低电平“L”。高级因子的值为 1,低级因子的值为–1。我们将变量的交互作用确定为单个因素的乘积。从任何给定因子约束的可能实验来看,一次改变一个因子的样本仍然是样本空间的一部分。这考虑了每个因素对响应变量的影响。现在,让我们将 M 定义为变量 x 的主要交集。这是高水平样本的平均响应变量与低水平样本的平均响应之间的差异。如果我们有三个因子,每个因子有两个级别,那么 X_1 的 M 将被定义如下:
如果我们想了解两个或更多因素之间的相互作用,等式将是相同的,除了变量的相互作用将由变量的乘积来表示,而不是一个因素在给定状态下拥有的单个值。主效应和交叉效应统计提供了一种有效的方法来确定单个因素或因素组合对响应变量的影响程度。全因子设计不会以任何这样的方式使数据复杂化,并且提供了一种检查可变效应的透明方法。如果有两个以上的级别,则必须进行调整,以获得所有级别对给定响应变量的平均影响,其中分母为 N,因此
Halton、Faure 和 Sobol 序列
在空间填充技术的保护伞下,其中许多是由伪随机数发生器激发的。伪随机数是通过随机性测试的序列生成集。我们将伪随机数发生器表示为以下函数:
![$$ \phi :\left0,1\right)\to \left[0,1\right),\kern0.5em {\upgamma}{\mathrm{k}}=\phi \left({\gamma}\right),\kern0.75em k=1,2,\dots $$我们必须选择一个能给出γ k 均匀分布的ϕ值。实现这一点的一种流行方法是范德科尔普序列,其中我们有一个基数 b,≥ 2 和连续的整数 n 以它们的 b-adic 展开形式表示,使得下面是真实的
![
其中 a 表示展开系数。
Halton 序列分别在第一、第二和第三维中使用基数为 2、基数为 3 和基数为 5 的 Van der Corput 序列。这种模式继续下去,在每一个连续的维度中,质数被用作基数。也就是说,多维聚类导致维度之间的高度相关性,实际上违背了实验设计本身的目的。为了解决这个问题,Faure 和 Sobol 序列对所有维度只使用一个基,对每个维度使用不同的向量元素排列。
A/B 测试
在设计应用程序、网站和/或仪表板应用程序时,确定某些功能的变化对产品的影响是很有用的。例如,我们可以想象,一个工程师正试图用某种统计确定性来确定一个新特性的实现是否对获得新用户有影响。在这种情况下,建议人们使用 A/B 测试。广义的 A/B 检验是指用来比较两个数据集的统计假设检验方法,一个是对照组,一个是测试组,分别是 A 和 B。我们也可以修改测试,这样我们可以测试一个和多个额外的控制测试。
A/B 测试的动机很简单,因为不同产品的开发,不管它们是否具有机器学习或深度学习能力,都允许我们用统计上的信心来确定我们是否从最初的迭代到下一次迭代做出了改进。也就是说,我们可以使用这些过程作为一系列实验,从一代软件迭代到下一代软件,以观察效率的提高。通常,beta-二项式层次模型是最流行的方法之一,通过它我们可以 A/B 测试一个控制组而不是多个测试组。因此,我们将回顾这一模式。然而,首先让我们回顾一个简单的双样本 A/B 测试。
简单双样本 A/B 检验
假设我们在这里将一个控制组与一个测试组进行比较,并且我们试图了解我们的新网站是否会因为功能变化而产生更多的点击。我们将坚定地表明,虽然这个测试对两个例子是稳定的,但您应该避免对两个以上的样本使用这个测试。假设我们有两个数据集代表不同网站的不同属性,我们希望在 95%的置信度下进行测试。为此,我们将使用 t 检验。现在,我们还假设在执行 t 检验后,我们观察到均值的差异显著不同,并且 x2 与之前的模型相比显著提高。现在让我们假设我们一直在制作不同版本的网页,并不断尝试使用这种模式。经过九次不同的测试,x2 仍然被证明是最优秀的车型。但是当我们运行 x2 时,我们实际上看不到 x2 对其他网站的点击有任何改善。双样本 A/B 测试的常见问题是由于假阳性。
接下来,我将通过二项式分布展示 10 个个体假设检验显示正确结果的概率。设事件 A = x2 假设在 90%置信区间优于其他 9 个同行,B = x2 在 95%置信区间优于其他 9 个同行,C = x2 在 99%置信区间优于其他 9 个同行:
简单地说,在事件 A、B 和 C 下,我们可以预期我们的实验产生 6.5、4.013 和 0.95 的假阳性。虽然 99%的置信区间在这个例子中表现最好,但是我们可以看到在其他置信区间下为什么这个方法会成为一个问题。因此,为了测试多个组,建议我们使用 beta-二项式分布。
用于 A/B 测试的 beta-二项式分层模型
贝叶斯统计是关于概率概念的一个学派。这里,它成为该模型的理论基础,并且还可以用于提供关于分布的改进的分级模型。在贝叶斯统计中,我们经常提到先验分布和后验分布。先验分布是指某个参数(我们已经获得的数据)的概率分布,而后验分布是指某个参数(我们想要获得的数据)的概率分布。先验分布和后验分布形成共轭分布。为了便于分析,我们通常寻求使用同一家族内的分布来表示先验分布和后验分布,这就是为什么在这个层次模型中我们使用贝塔分布和二项式分布。
贝塔分布是区间[0,1]内的概率分布,参数α和β最终控制分布的形状。通常,我们使用贝塔分布来统计建模随机变量。如前所述,与贝塔分布属于同一家族的是二项分布。这通常用于模拟以独立二元结果为特征的概率分布,例如掷硬币。我们将贝塔分布和二项式分布(见图 8-6 和 8-7 )的概率密度函数分别定义为
其中 n =成功的次数,k =试验的总次数,p =成功的概率。
图 8-7。
Binomial distribution
图 8-6。
Beta distribution
然后,我们将 beta 分布和先验分布的后验期望值建模为二项式,然后我们比较先验分布和后验分布之间的均值差异,以比较网站性能。
特征/变量选择技术
现在我们已经讨论了几个实验设计模型,让我们来谈谈在你对给定数据集中的因素有了更多的了解之后,你想要采取的步骤。变量选择似乎与实验设计直接相关,但本节将讨论用于降低维度的更具体的算法,以及用于分析变量及其与响应变量的相互作用的探索性较少的方法。这一点很重要,原因有很多,但在部署机器学习算法时,这往往是优化它们的一个主要因素。特征选择是一个比参数调整简单得多的过程,尤其是在深度学习模型中。因此,这是一种快速创建模型的方法,可以更快地训练并产生更准确的输出。与前面描述的许多技术一样,必须小心谨慎,因为过多的特征选择会导致创建过度拟合的模型。
向后和向前选择
向后选择是最简单的变量选择方法之一,在使用简单或多元线性回归时尤其常见。首先,你应该获取包含所有解释变量的数据集,并根据响应变量对它们进行回归。在这一步之后,选择适合于给定情况的统计显著性水平(85%、90%、95%等等)。一次一个变量,我们从数据集中删除具有最低统计显著性的变量(例如在使用 glm()模型时由 summary()函数产生的统计显著性)。我们回归原始数据集的新子集,并继续下去,直到数据集中的所有变量都具有统计显著性。在正向选择中,过程与先前的方法相同,除了区别在于您从没有变量的模型开始,添加变量,并检查它们的统计显著性。如果它们处于或高于阈值,则应该添加它们。如果没有,就应该删除。使用这些方法时要记住的注意事项是显著减少统计噪声,但要避免模型过度拟合测试数据,特别是当样本外预测是所构建模型的最终目标时。您还应该注意不要删除过多的变量,以免降低性能。
对于深度学习,一些模型中嵌入了特征选择。具体来说,CNN 中的某些层可以说是为了消除噪声而存在的,以便留下的数据富含信息。具体来说,可以认为池层就是这样做的。通过减少输入大小,我们减轻了从输入到输出的计算负荷,同时还帮助算法更准确地调整这些层之间的权重,并最终对图像进行分类。
除了使用 P 值,您还可以选择其他统计标准来确定保留/删除哪些变量。其中最常见的是赤池信息准则(AIC)和贝叶斯信息准则(BIC):
其中 L 是模型的最大似然函数,是参数,k 是参数的数量。
AIC 和 BIC 关系非常密切。AIC 基于信息论领域,目标是选择一个具有最小 AIC 值的模型。根据函数的定义,对数似然值越大,AIC 值越小。从今以后,更接近数据拟合的模型将最终具有较低的 AIC 值。BIC 最终是由贝叶斯统计推动的,与 AIC 相似。BIC 分数专门用于评估模型在训练集上的性能,其中我们选择产生最小 BIC 的模型。特别是,BIC 惩罚有更多而不是更少参数的模型。正因为如此,BIC 天生喜欢不会过度适应数据集的模型,因此制定了一个标准,鼓励你选择一个能概括你正在分析的数据的模型。请注意,BIC 无法处理复杂的模型集合,仅在 n 远大于 k 的情况下才有效。考虑到 AIC,计算的 AIC 值必须跨相同的数据。具体来说,它不是一个客观的衡量标准,如决定系数。
主成分分析
主成分分析(PCA)是最常用的变量选择技术之一,可以专门用于数值数据。前面在几个例子中提到,PCA 是一种用于降低数据集维数的统计方法。简而言之,我们将数据转换为新的变量,称为主成分,并消除主成分,这些主成分解释了数据集中可忽略的方差。这种技术的好处是,我们保留了数据集的方差,同时能够比转换之前更容易地执行可视化和探索性分析。
我们的目标是从 x 向量中找到随机变量的线性函数,从α向量中找到方差最大的常数向量。这个线性函数产生我们的主分量。尽管如此,每个主成分必须按照方差递减的顺序排列,并且每个主成分必须彼此不相关。我们的目标如下:
我们寻求使用约束优化,因为如果没有约束,a k 的值可能无限大。因此,我们将选择以下规范化约束,其中
拉格朗日乘子法是一种对可微函数进行约束优化的工具。特别是,它有助于在给定的约束条件下找到相应函数的局部最大值和最小值。在实验范围内,拉格朗日乘数应用如下
方程的最后一步产生特征向量αk及其相应的特征值λk。我们的目标是最大化λ k ,并且特征向量以降序定义。如果λ 1 是最大的特征向量,那么第一主成分定义为一般来说,我们定义给定的特征向量为 x 的第 k 个主成分,并且给定特征向量的方差由其对应的特征值表示。我现在将演示当 k = 1 和 k > 2 时的这个过程。第二主成分在与第一主成分不相关的情况下最大化方差,非相关约束如下:
这个过程可以重复到 k = p,产生 p 个随机变量中每一个的主分量。但是,与 PCA 相关的限制是多方面的,对于问题类型必须加以考虑。首先,PCA 假设特征之间存在线性相关性。显然,在实际环境中不一定总是如此,因此使得 PCA 得出的结果值得怀疑。其次,PCA 只能用于数字数据集,数字编码分类数据的下降(在本章后面讨论)会增加隐含偏差,使这种技术的结果变得无用。此外,PCA 明确假设方差是分析数据集时最重要的统计量。尽管方差通常是一个重要的统计量,但在一些问题案例中,它不一定是。
PCA 如何应用于深度学习的一个例子是通过 PCA 白化的过程。当我们提到白化时,我们指的是使输入数据不那么同质的过程,以努力使数据从一个观察到另一个观察时不那么同质。在 CNN 的例子中,这对于图像分类非常有用。具体而言,在图像数据中,彼此相邻的许多像素在大区域内通常具有相似的值,如果不是相同的话。
这方面的一个例子是查看 MNIST 数据集,看看图像的哪些部分是黑色的,哪些是白色的。相反,PCA 白化产生矩阵的特征分解,从而消除了这种同质性。因此,每个个体的特征与其原始形式相比明显不太相似,但是数据内的方差被保留,这是在矩阵上执行特征分解时的好处。
特征分析
因素是不可观察的变量,彼此高度相关,并影响给定的解释变量。与 PCA 的最终目的降维不同,因子分析寻求定位自变量。此外,我们想确定这些因素对表面属性有什么影响。它建立在这样的假设上,即观察到的变量可以减少到一个表现出相似方差的子集。在因子分析中,我们要求数据必须是正态分布的,并且数据集中实际上没有异常值。我们还应该寻求分析大量的观察数据,虽然相关性不是近似线性的以避免多重共线性,但在整个数据集上必须是中等至高的。典型的因子分析模型由
给出
其中 e j =给定解释变量的唯一和特定因素,j =因素负荷,X j =解释变量,m =潜在因素
因素负荷可以被认为是权重,它们表示相对于单个变量,它们对给定因素的影响程度。表面属性被表示为单独的解释变量。典型地,因子分析模型将产生因子,使得各个变量之间没有相关性,因此我们有独立变量,类似于主成分。应该注意的是,因素不是被创建的,而是基于表面属性之间的相关性而被揭示的。看不见的因素可以是无形的,但却是可以想象的。例如,我们可以想象在一个给定的实验中,与一个人相比,一个人的阅读或写作能力。就我们如何衡量它们而言,这些属性是不客观的,但当评估一个标准化的考试时,例如,阅读和写作部分,显然会影响一个人的分数。
因子分析的局限性
因子分析可以找到一种方法来获得从随机数生成的数据中的模式。因此,人们应该记住,如果可以在随机数据中找到结构,那么他们在结构化数据中观察到的模式也可能是错误的。此外,在数据中发现的结构最终是输入到因子分析中的变量/数据集的衍生物。简而言之,数据集中没有显而易见的客观模式,最终数据集/变量的重组会导致因子分析产生的结果出现显著差异。因此,一个人如何解释因素分析的结果最终比它看起来更主观。也就是说,建议将因子分析与统计方法一起使用,并且/或者对数据进行结构化,使其符合所处理问题领域内已知为真的假设。
处理分类数据
在您可能遇到的所有困难中,最大的挑战之一是处理和分析分类数据,或数值数据。通常,我们经常遇到分类数据作为一个因素变量具有不同的水平。本节讨论将会遇到的一些常见问题以及可能的解决方案,同时要记住一些注意事项。
编码因子级别
例如,假设我们有一个数据集,其中我们正在分析一个变量,即给定街区的所有街道。这是一个特别有趣的例子,因为街道可能都是名称(如“枫树街”、“云杉街”、“红杉街”等等),也可能都是数字(第一街、第二街、第三街等等)。如果街道是名字,我们可以采取这种方法,用数字对街道进行编码。这是给每个变量一个唯一标识符的简单方法,但是它有局限性。机器学习算法将把级别解释为值的指示,而不是唯一的标识符,这实质上没有给出关于观察的“质量”的描述性数据。具体来说,如果我们将“枫树街”标记为 1,将“云杉街”标记为 2,当没有证据确定这一点时,许多算法可能会将云杉街解释为比枫树街更重要。当考虑数字的情况时,同样的问题也存在,但它只是隐含的,而不是由标签编码引起的。这种技术的另一个局限性是,如果编码变量与其他变量高度相关,多重共线性可能会被引入数据集,否则它将不会存在。
分类标签问题:级别太多
为了与使用街道名称的示例保持一致,我们可以想象在许多城市中,这将导致我们拥有数百甚至数千条单独的街道。尽管有变化的变量比完全没有变化的变量产生更好的结果,但这也会在执行模型评估时造成困难。因此,在这些情况下,对变量进行编码并使用分类/回归树或随机森林模型可能是一个好主意。此外,一个建议的方法是对变量进行编码,并使用 K-means 聚类来获得聚类数,然后我们用该变量替换级别。尽管在许多方面,这仍然会使我们之前讨论的编码变量的偏差融入到聚类观察中,但这仍然是一种有效降低级别的方法,应该在必要时进行探索。
典型相关分析
与 PCA 密切相关的是典型相关分析(CCA ),这是一种寻找两个变量的线性组合的方法,使得它们彼此具有最大可能的协方差。通常,这是一种数据预处理技术,适用于使用多元线性回归的相同情况,但特别是当有两组多元数据集时,我们希望检查以下各项之间的关系:
给定两个矢量和方向
包装器、过滤器和嵌入式(WFE)算法
当评估一些更先进的变量选择技术时,我们采用 WFE 算法。通过在数据上运行每个可能的特征子集并评估模型性能来区分包装器算法,从而选择对于给定模型表现最佳的子集。嵌入式算法被明确地写入模型的过程中(使用 LASSO 的 L1 正则化)。过滤方法试图通过查看数据本身来评估特征的优点,而不是仅通过方法来评估其性能。
救济算法
由 Aha、Kibler 和 Albert 在 1991 年设计的 relief 算法是一种基于特征的权重算法,其灵感来自基于实例的学习。每个特征被分配一个表示其与目标的相关性的权重。该算法是随机化的,并且相关性值的更新取决于所选实例和两个最近实例之间的差异。
算法
- 给定
T =迭代次数,s =核宽度,q =停止准则。
- 对于 t = 1 : T
- 计算成对距离 w.r.t.
- 计算 P m ,P h ,P o 。
- 更新权重。
- 如果
。
- 计算成对距离 w.r.t.
其他本地搜索方法
如果不是直接相关的话,本文后面部分提到的许多算法将从优化的这个子领域中得到启发,这个子领域通常用于计算密集型优化问题。我们认为所有可能的解决方案都在我们称为特征空间或搜索空间的集合中。目标是满足我们寻求解决的优化问题的全局最优。局部搜索算法从特征空间中的随机元素开始,并在每次迭代中基于从当前邻域产生的信息选择新的解决方案。在这个阶段之后,算法将移动到最近的邻域中的给定邻域,但是根据问题,搜索算法可以选择不止一个邻域。
爬山搜索方法
在 20 世纪 80 年代和 90 年代机器学习发展之前,爬山往往是更受欢迎的搜索方法之一。爬山形成了本章中描述的许多新的搜索方法的动机,并且对于参数调整仍然是一种有用的技术。与其他搜索方法一样,爬山法寻求在当前点的局部范围内优化目标函数。爬山法最适用于有一个最大值或一个最小值的函数,这样算法就可以相对容易地找到问题的解。然而,对于具有大量局部极小值的函数,它面临许多问题。为了解决这个问题,许多不同的启发式算法和方法,如随机重启以避免局部极小值和搜索轨迹的随机邻域选择,都被添加到基本的爬山算法中。
遗传算法
遗传算法被认为是人工智能领域的直接产物,因为它们直接模拟了进化过程。在该算法中,总特征空间的几个子集“进化”,使得下一个子集在统计上优于上一次迭代。当一个更好的子集不能被创建时,进化过程停止,并且最好的子集被选择作为答案。与其他算法相比,该算法的优势在于,遗传算法可以在多次迭代中积累关于给定特征空间的信息,该过程本质上是并行的,因此陷入局部最小值的可能性较小,并且该算法本身相对容易理解。遗传算法的局限性之一是,如果存在大量的局部最优解,遗传算法并不总是收敛于全局最优解。此外,该算法可能不是部署的最佳选择,因为它难以扩展,因为特征空间大小随着可能子集的数量呈指数增长。
算法
- 选择一组初始随机解决方案供选择。
- 基于一些统计标准评估解决方案,例如 MSE。
- 选择最佳人选。
- 通过“变异”先前选择的解决方案来产生新的个体。
- 评估新解决方案的适用性。
- 当达到某个标准时停止,如损失容限。
模拟退火
在我们将要讨论的启发式技术中,SA 是少数被评估的概率模型之一。SA 的设计灵感来自于冶金学中的退火,它模拟了缓慢冷却的效果,即缓慢降低接受更差解决方案的可能性。我们将每个解视为一个状态,算法可以搜索的邻域逐渐变小。在特征空间已经被完全搜索之后,或者已经达到另一个停止标准之后,该算法收敛于一个解。一
算法
- T =温度=热,冻结=停止标准。
- 而(温度!=冻结),移动到特征空间中的随机点并计算能量。
- 当系统在电流 t 下处于热平衡时,If 能量< 0 or loss tolerance, accept new state with probability
。
- If (E 在最后几次迭代中递减),
,否则 T =冻结。
SA 的最大困难是所需的参数调整量,随着特征量(和相应的特征空间)的增加,这变得很耗时。此外,对于这些参数中的任何一个都没有通用的基线或经验法则,这进一步增加了这种技术处理大量变化的数据集的难度。它应该更像是一种研究技术,而不是在算法中使用的技术。
蚁群优化算法
蚁群算法是 20 世纪 90 年代首次提出的一套优化算法。对组合数学问题最有用,ACO 已经被用于诸如车辆路径、计算机视觉、特征子集选择、定量金融和其他领域的任务。直觉是基于成群蚂蚁的活动,最终目标通常是从特征空间中找到给定随机选项集的最佳选项。在这种情况下,我们可以将一个蚁群想象成一个由边连接起来的图,其中每个节点代表数据集中 k 个特征中的一个。蚂蚁沿着边缘行进,“释放信息素”以吸引更多的蚂蚁进行后续迭代。根据设计,信息素会随着时间的推移而衰减,但是从 x 点到 y 点沿着尽可能短的边行进的蚂蚁会沿着给定的路径储存更多的信息素。因为蚂蚁被吸引到有更多信息素的路径上,这是寻找最优解的方法。每个“蚂蚁”从一个给定的状态移动,其概率由
给出
其中𝓇 =沉积在给定路径上的信息素,η =从 x : y 到所有路径距离之和的比例,β =调谐参数,jIk=未被访问的邻居节点
用信息素更新为
其中τ xy =沉积的环己烯酮,ρ =环己烯酮的蒸发速率。
我们将δτxy表示为由
给出的单个蚂蚁在给定路径上投放的信息素数量
其中 Q =某个常数,L k =用户定义的损失函数。
虽然 ACO 问题在没有大量特征的情况下是成功的,并且它通常比模拟退火和遗传算法执行得更好,但是随着更多节点的增加,问题变得更加难以解决。除此之外,虽然收敛是有保证的,但是不确定收敛实际何时发生。
算法
- 通过创建完整的解决方案空间进行初始化。
- 当没有达到停止标准时,将每个蚂蚁定位在给定的开始节点。
- 对于每个蚂蚁,通过状态转换规则选择下一个节点。
- 应用信息素更新,直到每只蚂蚁都达到给定的解决方案。
- 根据选择标准评估每个解决方案。
- 更新最佳解决方案,并在此路径上应用信息素更新。
- 重复直到收敛到全局最优。
可变邻域搜索(VNS)
VNS 是一个特征子集选择算法家族,旨在处理组合学的挑战,并因此提供有保证的收敛性。发展于 20 世纪 90 年代末,VNS 的灵感来自于寻找离散和连续优化问题的解决方案的愿望(线性和非线性规划问题就是一个例子)。VNS 的假设是,关于给定邻域的局部最小值理论上可能不是另一个邻域中的局部最小值,局部最小值在一个或多个邻域之间彼此相对接近,全局最小值是解空间内所有邻域的局部最小值。在关于局部搜索方法的可用于 VNS 的算法中,有一些相关的扩展对于给定的任务更加具体。对于基于特征的选择,我们将研究基于过滤器的 VNS 算法。
算法
- 找到一个初步的解决方案。
- 为 k = 1,…,j 选择邻域集合 N k ,其中 j = #个邻域和一个停止准则。
- 设 k = 1,从
的第 k 个邻域生成一个随机点 S’。
- 如果基于目标函数,应用搜索方法使停止标准更接近达到。
- 如果此解决方案优于以前的解决方案,请将解决方案更新为当前解决方案。否则,设置 k = k + 1,保留当前解。
- 继续进行,直到收敛到全局最优或达到停止标准。
通常,我们选择信息商或线性相关性作为这些算法中的评估函数,但这最终是一个可以改变的参数。如果你觉得更先进,请随意实现自己的深度学习和/或机器学习算法,而不是传统的梯度下降,你可以使用上述搜索方法之一进行参数优化。尽管这可能很困难,但它将为您提供一个熟悉特定算法的绝佳练习,同时还能帮助您理解给定算法中的特定操作如何影响性能。这就把我们带到了一个关于改进现有机器学习算法的类似主题:反应式搜索优化。
反应式搜索优化
RSO 是优化领域中相对较新的创新。它产生了有趣的影响,值得更高级的读者一提。RSO 的目的是为那些打算创建机器学习平台和工具的人提供特别的帮助,这些平台和工具是为那些不像典型的机器学习工程师那样技术娴熟的用户设计的。智能优化指的是 RSO 内部更具体的研究领域,但仍然是相关的。在这个范例中,我们评估不同学习方案的有效性。大致有三种,我们称之为线上、线下以及两者不同比例的结合。这是在不同环境中实现算法的思想,使得它们具有不同的搜索历史,这最终影响当前会话中的时期的动作。
被动禁令
基于禁止的技术和智能方案,与基本的启发式搜索如局部搜索相反,是禁忌搜索的智力动机。禁忌搜索方法主要是在 20 世纪 80 年代获得了最初的牵引力,鉴于它所占据的肥沃土壤,它已被证明是一个大的研究领域。与局部搜索方法相比,禁忌搜索(TS)特别值得注意,因为它使用了从数据集中收集的先验信息,以及它如何影响新迭代的结果。假设我们有一个由长度为的二进制字符串组成的可行搜索空间,X 是当前配置,N(X)是前一个邻域。以下等式与禁忌搜索相关,即基于禁止的
其中允许功能选择的子集,使得它依赖于整个搜索轨迹
。
禁忌搜索算法有多种分类方式,但我将阐述的最初区别因素是 TS 算法中确定性系统与随机系统。禁忌搜索的最基本形式被称为严格禁忌搜索。在该算法中,我们观察到 N(X)具有以下值:
当引入一个禁止参数 T 时,它决定了一个移动在执行其逆移动后将保持禁止多长时间,我们可以得到两个不同于严格禁忌搜索的算法。当且仅当通过将方向应用于搜索而从当前点获得邻居,使得其逆在最后 T 次迭代期间没有被使用,例如
时,才允许邻居
其中 LastUsed()是 move μ的最后一次使用时间。如果 T 随着迭代计数器而变化,生成搜索轨迹的一般动态系统包括 T 的附加演化方程,使得
对于作用于二进制字符串的基本移动,。
对于随机模型,我们可以用概率生成-接受规则代替禁止规则,大概率表示允许的移动,小概率表示禁止的移动。随机性可以增加 TS 算法的鲁棒性。随机性会限制或消除记忆诱发活动的益处,这是禁忌搜索的主要吸引力。鲁棒禁忌搜索的特点是禁止参数在搜索过程中在上限和下限之间随机变化。在固定禁忌搜索中,可以通过随机打破束缚来增加随机性,或者通过一个以上的最佳邻居()函数的候选来获得成本函数的降低。当在反应式禁忌搜索中实现随机性时,观察到相同的效果。
固定禁忌搜索
让我们假设我们有一个搜索空间 X,使得具有一个成本函数
,其中 b 是一个 3 比特的字符串。可行点将是如图 8-8 所示的三维立方体的边缘。给定点的邻域是与边相连的点集。点 X⁰ = [0,0,0]与 f(X⁰) = 0 是一个局部极小值,因为其他移动产生了更高的成本。
图 8-8。
A feature space with error function, E, and f value = [x,y,z], using tabu search
我们将定义两个参数,用于测试给定禁忌搜索时期的效率,表示为汉明距离和最小重复间隔。汉明距离描述了沿着搜索轨迹的起始点和最成功点之间的距离,而最小重复间隔描述了沿着给定搜索轨迹访问类似移动的次数。这些参数的方程式如下:
向前看,我们应该注意避免搜索轨迹的吸引子,这里我们把吸引子定义为由确定性局部搜索产生的局部极小值。如果代价函数是下界的,并且从任意点开始,它将终止于局部极小点。我们还定义了所谓的吸引盆地。吸引盆由所有点组成,使得从它们开始的确定性局部搜索轨迹终止于特定的局部极小点。确定性搜索轨迹经常遭受偏向于吸引盆地的痛苦,并且因此可能产生不是全局极小值的结果。为了解决这个问题,给定的搜索点保持接近在搜索轨迹开始时发现的局部极小值。在这之后,搜索轨迹可以搜索关于降低成本函数的更好的吸引流域。一如既往,我们必须意识到存在一些局限性。使用禁忌搜索时,最常遇到的困难是确定适当的禁止参数,并使该技术足够健壮,从而不需要从一个上下文到另一个上下文进行繁琐的调整。这就把我们带到了反应式禁忌搜索,它已经被提出作为解决这些问题的一种方法。
反应式禁忌搜索
反应式禁忌搜索(RTS)具有一个禁止参数,该参数通过搜索轨迹内的反应机制来确定。我们一开始用值 1 初始化它,但是我们给它的变化增加了一个确定性的方面。如果有证据表明搜索轨迹需要多样化,T 增加。一旦这个证据不明显,T 就会降低。当我们沿着搜索轨迹重复访问先前的点时,就获得了搜索路径多样化的充分证据,因为它们存储在算法的“存储器”中。此外,为了避免算法非常严格地陷入吸引域的情况,RTS 有一个逃逸机制。这是在给定周期内重复了太多搜索轨迹配置时启动的,其特征是当前搜索路径的随机重新配置。
目标函数 f 最终是搜索轨迹方向的信息来源。因此,下面的算法直接属于这个范例。
WalkSAT 算法
WalkSAT 算法可以理解为 GSAT 算法的一个更一般化的版本,它是一种局部搜索算法。在该算法中,对于给定的迭代次数,允许有固定数量的机会来找到解决方案。在给定的迭代过程中,算法在两个标准之间选择一个变量。此后,变量被放入翻转函数。WalkSAT 通过比 GSAT 更少的计算获得能量,因为它在给定时间考虑的参数更少。除此之外,通过确定变量选择的子句的乘积,它因此有机会解决可能阻止收敛到全局最优的问题变量。子句加权也可以合并到 WalkSAT 算法中,这为参数调整和产生的反馈循环提供了新的可能性。下面的算法建议将权重作为一种鼓励优先解决较难子句的方法。几个结构之后,困难的分句被认为是这样的。
k-最近邻(KNN)
KNN 被认为是基于实例的学习,其特征在于函数的局部近似和分类后发生的所有计算。它也可以用于回归,但通常被描述为一种搜索方法。它的主要优点是相对容易理解,并且在数据模式不规则的情况下是有效的。在分类的情况下,这些模型被认为是基于记忆的,其中我们定义了我们想要考虑的 k 个相邻点。我们对标准化数据使用欧几里德范数来确定给定点与其 k 个邻居之间的距离。这个方程给出为
其中 I = 1,2,…,N,N =观察总数,x i =第 I 次观察,y =我们要分类的特定点。
随着 K 的增加,通常我们会注意到类之间的定义变得不那么严格,导致通常更健壮的模型。就涉及特征选择而言,KNN 可以用作数据预处理技术,通常与其他搜索技术一起用于更精细的特征选择。Tahir、Bouridane 和 Kurugollu 在 2007 年的一篇论文中给出了一个例子,他们使用禁忌搜索和 KNNs 的变体创建了一个混合算法。该算法执行特征加权和选择,产生更准确的分类结果。流水线发生使得特征通过禁忌搜索被选择和加权,并通过 KNN 被分类。如果我们不使用禁忌搜索来执行特征选择,或者根本不执行特征选择,那么更多的噪声将被结合到 KNN 算法的决策过程中。通常情况下,在这里执行特征选择有助于算法在对每个观察值进行分类时做出更精确的选择。
摘要
到目前为止,这一章是对所讨论的所有粒度细节的一种元启发。首先,实验设计、特性选择和 A/B 测试对任何数据科学家的职业都至关重要。正确构建实验的能力至关重要,通过这些实验,您可以进行建模,通过修改输入来提高它们的性能,然后定量验证模型的结果。第九章讨论了为那些对创建个人或专业使用的构建感兴趣的人提供的硬件解决方案。
九、硬件和软件建议
要在专业环境中应用本书中探索的技术,硬件升级可能会成为一个考虑因素。在某些情况下,甚至有必要从头开始建造一台计算机。很少有现成的版本,即使有也要花费惊人的金钱。考虑到这一点,本章旨在为读者提供他们最应该注意的硬件组件的基本概述,并提供关于购买硬件的一般建议。
用标准硬件处理数据
在相对“普通”的机器上操作时,您可能会面临许多困难。当使用大型数据集处理机器学习和深度学习问题时,通常建议您对数据子集运行大多数操作,并以迭代次数乘以子集大小等于原始数据集大小的方式进行训练。虽然这只是提供了一个近似的性能,但是它可能能够运行您的解决方案,而不会由于 RAM 不足而导致解释器崩溃。
也强烈建议资金充足的个人使用亚马逊网络服务(AWS)。从专业角度来说,亚马逊是云服务的首选解决方案,甚至可能让你获得许多雇主渴望拥有的宝贵技能。简而言之,你可以付费在云环境中运行你需要的所有硬件的实例。尽管出于部署目的,这样做可能成本极高,但对于概念证明或研究,使用像 Amazon AWS 这样的云服务可能是解决深度学习问题的一种经济高效且简单的解决方案。但是,如果你需要实现解决方案,作为为业务或服务部署算法的一部分,请继续阅读——本章给出的建议是一个很好的起点。
固态硬盘和硬盘(HDD)
硬盘(HDD)是一种用于保存信息的存储设备,即使机器不在线。硬盘的主要特征是它可以存储的数据量和它提供的性能。正如我在本书前面提到的,自 2000 年代中期以来,存储的价格大幅下降,促进了对机器学习和深度学习科学的兴趣复苏。这一发展使得存储和收集大量的训练数据和/或训练模型成为可能,您可以在以后更新这些数据和/或模型或将其用于相关任务。用户应该熟悉他们最想处理的案例。
图形处理单元
在区分能够提供高性能深度学习的机器和不是专门用于深度学习的机器方面,GPU 是最常被引用的硬件之一)。对于深度学习,GPU 加速了计算的处理,是深度学习构建的一个组成部分。与中央处理器(CPU)计算相比,GPU 的性能明显优于 CPU,并且是大部分计算发生的地方。您可以构建一个具有多个 GPU 的单元,但在这样做时要意识到有效利用计算能力的挑战。如果您不熟悉并行计算,学习和正确实现这样的构建可能会非常耗时,并且会花费未知的时间,更不用说在有效部署算法/解决方案之前设计和调试软件所需的时间了。
有不同语言的软件包可以并行化您的代码并提高性能。在 R 中,我建议您考虑并行包,特别是对于在大量数据上执行相同的任务。不是将整个数据集输入到算法中,而是将其分解,使得相同的任务与数据集的块并行执行,从而使其更高效。在适用的情况下,您还应该实现lapply
函数。这个函数接受一个参数并将其传递给一个函数,使得执行复杂的操作比使用嵌套循环在计算上更有效。
我对 GPU 的建议(截至 2017 年初)集中在以下 Nvidia 型号上:
- 泰坦 x 号
- GTX 680
- GTX 980
截至 2017 年初,英伟达是少数几家专注于开发专门用于深度学习的 GPU 的公司之一。(注意,AMD 正在与谷歌合作创建深度学习硬件,将于 2017 年某个时候发布。)虽然这对普通从业者来说可能不太划算,但对于专业人士或有足够预算的人来说,我建议您在 AMD 的 FirePro S9300 x2 GPU 发布时查看其规格和性能评论。
选择 GPU 取决于您想要解决的问题类型以及您预计在此过程中消耗的内存量。使用 CNN 的人应该会消耗大量内存,尤其是在训练给定模型的过程中。深度学习的图像和其他数据的物理存储是另一个需要记住的考虑因素。尽管固态存储和虚拟存储的价格都大幅下降,但您应该留出时间来正确估计所需的存储。
中央处理器
除了执行非常基本的算术、逻辑和输入/输出功能之外,CPU 还指示计算机应该进行什么操作以及这些操作应该在哪里进行。CPU 还与 GPU 密切合作,以启动函数调用并启动向 GPU 的计算传输。对于深度学习特定的工作,CPU 核心的数量以及 CPU 缓存大小都很重要。大多数深度学习库依赖于使用单个 CPU 线程,通常每个 GPU 使用一个线程就可以很好地执行。然而,每个 GPU 使用更多线程可能会带来更好的性能——将这一事实与您打算执行的任务联系起来。对于图像分类任务,比如经典的 MNIST 数字分类任务,我发现如果我在使用本地机器时遇到困难,使用 AWS 的 g2.2xlarge 实例就足够了——它提供了 1 个 GPU,15 GB 的 RAM 和 60 GB 的 SSD 存储。
关于 CPU 缓存大小,有几种不同速度的缓存类型。L1 和 L2 往往很快,L3 和 L4 很慢。CPU 缓存的目的是通过匹配密钥对值来帮助加速计算。在实际环境中遇到的大多数数据集都太大,无法放入 CPU 缓存中,因此对于每个小批量,将从给定计算机上的 RAM 中读入新数据。在深度学习的情况下,大部分计算发生在 GPU 中,所以你不必担心购买能够处理这种负载的 CPU。但是,由于 CPU 缓存未命中,您可能会经常看到机器性能不佳,并且您有延迟问题。这导致了缓存未命中的核心考虑因素:RAM 以及在机器学习和深度学习中经常需要更多 RAM。
随机存取存储器
ram 存储经常使用的程序指令,使得程序的速度增加,因为它存储将被读取或写入的数据,而不管其在 RAM 中的位置。至于你需要的内存大小,它应该与你正在使用的 GPU 的大小相当。使用小于 GPU 大小的 RAM 可能会导致延迟问题,这可能会导致问题,特别是在训练不同的网络(如 CNN)时。使用更多而不是更少的 RAM 可以让您更容易地执行预处理和特征工程。说“买尽可能多的内存”很容易,但当然这并不总是可能的。然而,你应该考虑在这方面投入大量的可用资金。
母板
主板是主要的电路板,除了个人电脑之外,在各种产品中都可以找到。它的主要目的是促进计算机内各种组件之间的通信,并保持这些组件之间的连接器。确保主板有足够的 PCIe 端口来支持将安装在给定计算机中的 GPU 数量,并支持所有其他选择的硬件组件。
电源装置(PSU)
电源单元将交流电转换为稳定的直流电,以便计算机内的组件可以使用。关于用于深度学习的 PSU,请注意,如果您使用多个 GPU,请购买一个可以服务于您使用的 GPU 数量的 PSU。深度学习通常需要密集的训练,运行这些实例的成本应该最小化。给定深度学习机器所需的瓦特数可以通过将 GPU 和 CPU 的瓦特数相加,同时为计算机内的其他组件和功耗差异添加大约 200 瓦特来估算。
优化机器学习软件
本章的主要目的是让读者找到他们在改进机器方面的关注点。最终目标是提高被测试和部署的软件的性能,但是其中一部分涉及到直接优化软件。为此,在所有其他步骤之前,我建议您尝试改进您正在使用的算法,或者在实现给定解决方案时找到一个更好的算法。算法的最佳选择和找到所述算法的最佳实现可能相当耗时。这可能需要通读大量的文档,深入查看各种函数的代码,还可能需要做实验。虽然这本书是为那些在 R 方面相对有经验的人和刚接触深度学习/机器学习的人准备的,但在阅读完这篇文章后,你应该有足够的信心开始创建自己的各种机器学习算法的实现。虽然很费时间,但这样做可以让你学到很多关于不同算法及其实现的效率。
目前一个常见的争论围绕着使用哪种语言。r 是一种非常容易理解的语言,非常适合用于概念验证,特别是因为它的语法允许快速编写和测试代码。然而,当试图为任何需要实时应用的东西部署算法时,尤其是当试图将软件嵌入其他应用时,往往会被证明是麻烦的。如果你打算在一个专业的环境中工作,在设计任何事情的最终解决方案时,请记住这一点。通常,那些希望提高速度的人经常用 C++来写。当然,这本书没有涵盖 C++,也没有涉及 C++中的任何包,但是读者应该探索 C++中用于机器学习和深度学习的无数库。
摘要
本章应该让读者对他们在为机器学习进行专门构建时,或者在试图修改他们现有的硬件以更好地服务于他们的深度学习需求时,应该关注的一些最常见的问题有一个基本的了解。
第十章深入探讨了更多使用机器学习和深度学习解决方案的实际例子。
十、机器学习示例问题
在这一章中,我们将开始把目前讨论的技术应用到你可能面临的实际问题中。所提供的数据集将从随机数据中生成,或将来自 https://github.com/TawehBeysolowII/AnIntroductionToDeepLearning
。请注意,您还可以参考前面章节中给出的示例中提供的所有代码和数据集的 URL。
在这一章中,我们将专门研究机器学习问题。虽然我不能涵盖所有可能的领域和问题类型,但是这里的例子将重点解决用户可能遇到的常见场景。
我鼓励您将这些最后的示例章节视为如何从数据集(原始或处理过的)到解决方案的教程。虽然这些例子是可行的解决方案,但最重要的方面是应用我们已经讨论过的实验设计、特性选择和模型评估方法来有效地解决问题。
问题 1:资产价格预测
量化金融是一个不断将数据科学和机器学习技术融入其方法的领域,特别是在自动化交易和市场研究的过程中。虽然数量金融学本身是一个具有丰富多样性和自身技术的领域,但我们可以应用许多宽泛的分析和数学概念。对于这个例子,我们将使用 quantmod 包来下载金融数据,我将带您了解如何预测资产价格。我还将简要解释如何创建交易策略——特别是统计套利策略。和往常一样,强烈建议在应用这些技术之前对这些结果进行回溯测试。本章的目的是提供对机器学习的学术理解——它不打算作为量化投资组合管理的教程!
让我们假设你是一家资产管理公司的定量分析师,你的任务是合理预测一项标准普尔 500 资产的回报。你的董事总经理认为,还有十只股票有助于模拟这种特殊资产的表现,你应该在分析中使用它们。除了建议使用机器学习方法来解决这个问题之外,导演没有给出具体的使用方法。
让我们从定义问题开始。
问题类型:监督学习—回归
我们试图预测离散或连续值的任何问题都被称为回归问题。因为我们已经有了答案,并且我们正在尝试将我们提出的答案与实际答案进行比较,这是一个监督学习问题。具体来说,我们将尝试根据其他资产 x 的回报来预测一项资产 y 的回报。让我们开始构建管道来解决这个问题。
通常,使用雅虎!或者建议使用 Google Finance API 来完成这些任务。对于那些特别关注机器学习在定量金融中的应用的人,请注意雅虎!《金融》杂志的数据有生存偏见,也就是说,任何已经倒闭的公司都不能再访问他们的数据。因此,因任何原因被除名的公司不再存储在数据库中。这就产生了一个问题,因为使用这些数据的所有策略都不会反映出最糟糕的情况,例如,有人在金融危机期间交易了雷曼兄弟(Lehman Brothers)的贝尔斯登(Bear Sterns)等证券。但是,可以找到保存破产或不再上市的公司数据的数据库(Norgate Data 就是一个例子)。
我们将从使用 Google Finance API 加载数据开始,但是将使用 quantmod 包来完成。对于任何需要访问股票数据的工作,都推荐使用这个软件包,比如获取各种金融工具的每日、每月或每季度的价格,以及从上市公司获取财务报表数据。
让我们开始浏览代码:
#Clear the workspace (1)
rm(list = ls())
#Upload the necessary packages (2)
require(quantmod)
require(MASS)
require(LiblineaR)
require(rpart)
require(mlbench)
require(caret)
require(lmridge)
require(e1071)
require(Metrics)
require(h2o)
require(class)
#Please access github to see the rest of the required packages!
#Summary Statistics Function
#We will use this later to evaluate our model performance (3)
summaryStatistics <- function(array){
Mean <- mean(array)
Std <- sd(array)
Min <- min(array)
Max <- max(array)
Range <- Max - Min
output <- data.frame("Mean" = Mean, "Std Dev" = Std, "Min" = Min,"Max" = Max, "Range" = Range)
return(output)
}
在前面的代码中,和使用 R 时一样,在进行新的实验时,清空工作空间(1)很重要。然后我们加载所需的包(2)。定义的下一个函数给出了我们正在分析的数组的汇总统计数据(3)。在本例中,我们将只关注 MSE。这是为了提供一个如何评估机器学习模型的简单示例。
我经常采用两种方法:
- 在默认模式下评估几个模型,然后对最佳模型执行参数调整。
- 一次调整一个参数,然后对调整后的模型进行评估。
在这里,我将执行后者,尽管出于简单和解释的目的,没有那么深入。
实验描述
我们将创建一个通用管道来解决这个问题,描述如下:
数据摄取→特征选择→模型训练和评估→模型选择
具体来说,在这个问题中,我们将尝试根据我们怀疑准确描述这些回报的股票(市场指数和其他股票的混合)的回报来预测福特公司(股票代码为 F)的回报。我们的股票投资组合的选择本身就是一项研究,但在这种情况下,我们选择了与汽车市场相关的股票(宏观指标和与能源行业相关的指标)。这里的假设是,跟踪福特表现的股票很可能是同一行业的公司,或者是以某种方式服务于汽车市场的相关行业的公司,或者是对整体经济有更大影响的公司。
请注意,除了正确理解如何创建机器学习模型所需的数学知识之外,还有必要为这些模型提供有用的数据。如果我们使用与正在解决的问题完全不相关的特征,我们将很难从拟合的模型中得到任何有用的结果。因此,在我们对算法进行任何微调之前,我们为创建数据集而做出的这些假设将有助于产生更好的结果:
#Loading Data From Yahoo Finance (4)
stocks <- c("F", "SPY", "DJIA", "HAL", "MSFT", "SWN", "SJM", "SLG", "STJ")
stockData <- list()
for(i in stocks){
stockData[[i]] <- getSymbols(i, src = 'google', auto.assign = FALSE, from = "2013-01-01", to = "2017-01-01")
}
#Creating Matrix of close prices
df <- matrix(nrow = nrow(stockData[[1]]), ncol = length(stockData))
for (i in 1:length(stockData)){
df[,i] <- stockData[[i]][,4]
}
#Calculating Returns
return_df <- matrix(nrow = nrow(df), ncol = ncol(df))
for (j in 1:ncol(return_df)){
for(i in 1:nrow(return_df) - 1){
return_df[i,j] <- (df[i+1, j]/df[i,j]) - 1
}
}
在前面的代码中,我们从 Yahoo!金融(4)。除非在初次下载后保存了这些数据,否则您应该有一个活动的互联网连接,否则这部分代码将无法正常执行。在计算给定股票的回报时,您可以将回报视为衍生产品,但基于回报的价格的更简单公式如下:
(一)
其中 x =股票 x,y =股票 y,t =时间段(1,2,… n),
n =观察次数,= t 期间股票 x 的价格
为了这个实验的目的,同样在量化金融的许多这样的情况下,我们根据调整后的收盘价计算回报(等式 A)。我们将这些调整后的收盘价称为收盘价,因为它们反映了由于股息、股票分割或其他与股票表现或市场条件无关的财务调整而导致的基础股票价格随时间的任何变化。在这里,我们将查看每日收益。时间频率的选择完全由用户决定,并取决于被评估的策略。一般来说,高频交易在一天内发生多次,低频交易发生的增量明显长于一天。
我们组织数据,使得每一列代表给定股票的收益,每一行代表给定一天的收益。图 10-1 显示了数据集的头部。
图 10-1。
Head of stock return data set
股票回报通常与机器学习算法配合得很好,因为它们都以类似的方式进行缩放,并表示与给定股票内的所有观察结果以及可用于分析的股票范围相关的度量。
特征选择
在处理时间序列数据时,我们经常会遇到多重共线性。因此,PCA 是用于特征选择的公平方法。我们这样做是因为除了变量之间的线性相关性很高这一事实之外,可能还有不需要评估的特征,因此不需要评估噪声。因此,根据特征贡献的方差来评价特征是合理的。下面显示了执行 PCA 的代码:
#Feature Selection
#Removing last row since it is an NA VALUE
return_df <- return_df[-nrow(return_df), ]
#Making DataFrame with all values except label IE all columns except for Ford since we are trying to predict this
#Determing Which Variables Are Unnecessary
pca_df <- return_df[, -1]
pca <- prcomp(scale(pca_df))
cor(return_df[, -1])
summary(pca)
当执行前面的代码时,我们会收到如图 10-2 和 10-3 所示的结果。
图 10-3。
Summary of principal components analysis (PCA) on data set
图 10-2。
Correlation matrix for entire data set
在图 10-3 的第 2 行,你可以看到每个主成分对数据集贡献的方差的比例。为了清楚起见,必须声明主成分不代表数据集中的特征。也就是说,我们可以认为主成分 1 是特征 1 到 8 的组合,PC 2 是特征 2 到 8 的组合,等等。一般的经验法则是将对总方差贡献 1%或更少的主成分视为无关紧要的主成分。当将其转化为数据集时,我们将删除数据集中的特征 8。这种相同的分析模式应该进行外推,但只有当观察到特征之间的线性相关性时。回到图 10-1 ,你可以看到这些特征之间通常存在中度到强烈的线性相关性,这表明主成分分析确实是特征的一个合适选择。
模型评估
既然我们已经预处理了数据,让我们考虑我们的算法选择。在本例中,我们将评估几个不同的选择,并评估所有选择的 MSE。选择的型号数量完全由您决定,但是对于这个实际的例子,我将选择两个。此外,如果您选择评估 MSE 之外的统计数据,例如 R 的平方,则评估这些与实验目标相关的度量是合理的。也就是说,MSE 应该是回归模型中最小化的主要目标,也应该是所有其他评估方法中的主要关注点。
里脊回归
让我们选择第一个模型:岭回归。这里,我们将根据调整参数的值来评估 MSE。在下面的代码中,我们从从正态分布(5)中随机采样值开始。这些值将用于选择调整参数的大小,我们用 k 表示。这背后的直觉是,我们将从最低到最高对这些值进行排序,然后随着调整参数的增加,通过可视化误差来比较岭回归模型的 MSEs 性能:
#Ridge Regression
k <- sort(rnorm(100))(5)
在下面的代码中,我们开始交叉验证我们的结果,以便我们评估模型性能的一般性,而不是在完全相同的数据集上测试我们的算法(6)。我们选择使用大小相等的训练和测试集,将数据分成两半:
mse_ridge <- c()
for (j in 1:length(k)){ (6)
valid_rows <- sample(1:(nrow(return_df)/2))
valid_set <- new_returns[valid_rows, -1]
valid_y <- new_returns[valid_rows, 1]
#Ridge Regression (7)
ridgeReg <- lmridge(valid_y ∼ valid_set[,1] + valid_set[,2] + valid_set[,3] + valid_set[,4]
+ valid_set[,5] + valid_set[,6], data = as.data.frame(valid_set), type = type, K = k[j])
mse_ridge <- append(rstats1.lmridge(ridgeReg)$mse, mse_ridge)
}
然后,我们使用lmridge()
函数将数据拟合到岭回归模型,然后将 MSE 附加到名为mse_ridge
(7)的向量。
当执行以下代码时,我们看到如图 10-4 所示的结果:
图 10-4。
MSE over tuning parameter size
#Plots of MSE and R2 as Tuning Parameter Grows
plot(k, mse_ridge, main = "MSE over Tuning Parameter Size", xlab = "K", ylab = "MSE", type = "l",
col = "cadetblue")
当查看该图时,我们看到当我们的调整参数 K 最接近所显示范围的上限和下限时,该模型表现最佳。具体来说,我们将选择创建一个调整参数值为 1 的拟合模型,因为这个 K 值会产生较低的 MSE。评估模型时,在访谈、实验和个人评估中,使用图来查看模型在某些参数值变化时的性能是很重要的。这对你和其他使用/评估你的代码的人都很有用。这将有助于引导人们了解你的思维过程,而情节往往比看终端代码的数字输出更有吸引力。
在我们对验证集之外的数据测试我们的拟合模型之前,让我们看看如何调整另一个算法:支持向量回归(SVR)。
支持向量回归机
这里要调整的主要参数是kernel
函数,它决定了超平面的形状,因此也决定了回归线的形状。当我们执行下面的代码时,我们得到如图 10-5 所示的图:
图 10-5。
SVR MSE with respect to kernel selection
#Kernel Selection
svr_mse <- c()
k <- c("linear", "polynomial", "sigmoid")
for (i in 1:length(k)){
valid_rows <- sample(1:(nrow(return_df)/2))
valid_set <- new_returns[valid_rows, -1]
valid_y <- new_returns[valid_rows, 1]
SVR <- svm(valid_y ∼ valid_set[,1] + valid_set[,2] + valid_set[,3] + valid_set[,4]
+ valid_set[,5] + valid_set[,6], kernel = k[i])
svr_y <- predict(SVR, data = valid_set)
svr_mse <- append(mse(valid_y, svr_y), svr_mse)
}
#Plots of MSE and R2 as Tuning Parameter Grows
plot(svr_mse, main = "MSE over Tuning Parameter Size", xlab = "K", ylab = "MSE", type = "l",
col = "cadetblue")
在评估输出时,我们注意到图 10-5 中的以下 MSE 值。多项式核产生最小的 MSE,因此是我们的选择。现在,我们已经训练了两个模型,我们将使用调整后的模型对样本外进行预测。在实际设置中,您可能需要安装两个以上的模型并评估性能。因为这个过程是详尽的,为了便于解释,我将这个例子浓缩为比较两个模型。无论如何,让我们来看看我们调整后的模型的性能:
#Predicting out of Sample with Tuned Models
#Tuned Ridge Regression
ridgeReg <- lmridge(valid_y ∼ valid_set[,1] + valid_set[,2] + valid_set[,3] + valid_set[,4]
+ valid_set[,5] + valid_set[,6], data = as.data.frame(valid_set), type = type, K = 1)
y_h <- predict(ridgeReg, as.data.frame(new_returns[-valid_rows, -1]))
mse_ridge <- mse(new_returns[-valid_rows, 1], y_h)
#Tuned Support Vector Regression
svr <- SVR <- svm(valid_y ∼ valid_set[,1] + valid_set[,2] + valid_set[,3] + valid_set[,4]
+ valid_set[,5] + valid_set[,6], kernel = "polynomial")
svr_y <- predict(svr, data = new_returns[-valid_rows, -1])
svr_mse <- mse(new_returns[-valid_rows, 1], svr_y)
#Tail of Predicted Value DataFrames
svr_pred <- cbind(new_returns[-valid_rows, 1], svr_y)
colnames(svr_pred) <- c("Actual", "Predicted")
tail(svr_pred)
ridge_pred <- cbind(new_returns[-valid_rows, 1], y_h)
colnames(ridge_pred) <- c("Actual", "Predicted")
tail(ridge_pred)
前面的代码使用了我们训练的回归模型,只是我们根据产生最低 MSE 的值来设置参数值。尽管我们将模型与训练数据相匹配,但我们是在测试数据上进行预测。这表现在我们使用所有我们没有训练模型的观察值从返回数据帧中进行索引。当对测试数据集进行预测时,图 10-6 和 10-7 显示了每个算法的实际股票值与预测股票值的对比。
图 10-7。
Tail of actual versus predicted data frame (ridge regression)
图 10-6。
Tail of actual versus predicted data frame (SVR)
当评估这些算法的 MSE 时,我们得到以下结果:
- 支持向量回归的 MSE:0.0002967161
- 岭回归的 MSE:0.002632815
基于这些结果,有理由说我们应该选择岭回归而不是基于更好的 MSE 的 SVR。在评估一个解决方案时,除了完全不同的算法之外,您应该可以自由地研究给出的示例,并使用不同的特性选择算法。这一节的目的,同样是提供我通常如何处理这些问题的见解,以便您可以开始开发自己的方法。尽管模型选择和调优有一些通用的指导原则,但是每个人都可以按照自己的方式自由地执行。
现在让我们来看一个分类问题。
问题 2:速配
在快速约会中,参与者会见许多人,每个人几分钟,然后决定他们希望再次见到谁。我们将使用的数据集包含了对研究生和专业学生进行的快速约会实验的信息。实验中的每个人与 10-20 个随机选择的异性(只有异性配对)见面,每个人四分钟。每次快速约会后,每个参与者都要填写一份关于对方的问卷。我们的目标是建立一个模型来预测哪对约会者希望再次见面(即有第二次约会)。
问题类型:分类
我们试图确定二元或有限多项式结果的任何问题都可以被认为是分类问题。在这种情况下,这将是一个监督问题,因为我们事先知道数据的标签,但我们需要通过特定于该数据集的确定性规则来计算它们。第二次约会只有在特定配对中的两个人都决定他们想再次见到对方的情况下才会计划。因此,我们将在数据集的预处理阶段创建该列:
#Upload Necessary Packages
require(ggplot2)
require(lattice)
require(nnet)
require(pROC)
require(ROCR)
#Clear the workspace
rm(list = ls())
#Upload the necessary data
data <- read.csv("/Users/tawehbeysolow/Desktop/projectportfolio/SpeedDating.csv", header = TRUE, stringsAsFactors = TRUE)
#Creating response label
second_date <- matrix(nrow = nrow(data), ncol = 1)
for (i in 1:nrow(data)){
if (data[i,1] + data[i,2] == 2){
second_date[i] <- 1
} else {
second_date[i] <- 0
}
}
和往常一样,我们通过加载必要的包和清理工作区来开始实验。然后,我们加载数据并创建一个名为 second_date 的响应标签。
现在我们已经完成了一些初步的预处理,让我们来描述和探索我们的数据集。该数据集中的特征如下,从第一列到最后一列:
- Second_Date:二进制数据集的响应变量 y。1 =是(您想再次看到日期),0 =否(您不想再次看到日期)。
- 决定:由性别隔离的个人做出的决定,关于他们是否愿意进行第二次约会。1 =是(您想再次看到日期),0 =否(您不想再次看到日期)。
- 喜欢:总的来说,你有多喜欢这个人?(1 =完全不喜欢,10 =喜欢很多)。
- PartnerYes:你认为这个人会答应你的可能性有多大?(1 =不太可能,10 =极有可能)。
- 年龄:年龄。
- 种族:白种人、亚洲人、黑人、拉丁美洲人或其他人种。
- 有吸引力:用 1-10 的标准给伴侣的吸引力打分(1 =糟糕,10 =很棒)。
- 真诚:给合作伙伴的销售诚意评分,1-10(1 =糟糕,10 =很好)。
- 有趣:用 1-10 分的标准评价伴侣的有趣程度(1 =糟糕,10 =很棒)。
- 雄心勃勃:用 1-10 的标准给伴侣的雄心壮志打分(1 =糟糕,10 =伟大)。
- 共同兴趣:用 1-10 分(1 =很糟糕,10 =很棒)来评价你和伴侣共同兴趣/爱好的程度。
预处理:数据清理和插补
注意,在这个数据集中有 NA 观察值。如前所述,我们有多种工具来处理这个问题,但重要的是我们要从算法上找到处理这个问题的方法。我们将在执行任何特性转换之前解决这个问题。以下代码显示了我们处理 NA 数据的过程:
#Cleaning Data
#Finding NA Observations
lappend <- function (List, ...){
List <- c(List, list(...))
return(List)
}
na_index <- list()
for (i in 1:ncol(data)){
na_index <- lappend(na_index, which(is.na(data[,i])))
}
首先,我们创建一个函数,让我们将向量附加到一个列表中,这样,对于每一列,我们都有一个指示 NA 观察位置的行向量。给定数据集的性质,在给定该列/特征中的数据的情况下,使用最合理的方法估算值是合乎逻辑的。请注意,Second_Date、DecisionM、DecisionF、RaceM 和 RaceF 列没有任何缺失数据。我们将处理确实存在缺失数据的特征。
我们将使用第三章中描述的期望最大化(EM)算法进行数据插补。这在amelia
包中给出,可以从 R 端子安装。不过,在此之前,我们必须稍微准备一下我们的数据:
#Imputing NA Values where they are missing using EM Algorithm
#Step 1: Label Encoding Factor Variables to prepare for input to EM Algorithm
data$RaceM <- as.numeric(data$RaceM)
data$RaceF <- as.numeric(data$RaceF)
#Step 2: Inputting data to EM Algorithm
data <- amelia(x = data, m = 1, boot.type = "none")$imputations$imp1
#Proof of EM Imputation
na_index <- list()
for (i in 1:ncol(data)){
na_index <- lappend(na_index, which(is.na(data[,i])))
}
na_index <- matrix(na_index, ncol = length(na_index), nrow = 1)
print(na_index)
#Scaling Age Features using Gaussian Normalization
data$AgeM <- scale(data$AgeM)
data$AgeF <- scale(data$AgeF)
EM 算法不能处理因子(分类变量)。这意味着我们必须在将这些因素输入算法之前对它们进行数字编码。在这之后,我们执行amelia
函数,它执行我们想要的。接下来,我们通过索引任何 NA 值,然后打印此输出,提供该数据集中不再有 NA 数据的证据,产生如图 10-8 所示的结果。
图 10-8。
Displaying counts of NA values in cleaned data set
我们已经成功地移除了所有 NA 观测值,并将在继续进行特性选择之前执行最后一点预处理。让我们看看男性和女性的年龄分布。我们对此进行如下编码,并接收后续结果:
#Scaling Age Features using Gaussian Normalization
summaryStatistics(data$AgeM)
Mean Std.Dev Min Max Range
1 26.60727 3.509664 18 42 24
summaryStatistics(data$AgeF)
Mean Std.Dev Min Max Range
1 26.24317 3.977411 19 55 36
#Making Histograms of Data
hist(data$AgeM, main = "Distribution of Age in Males", xlab = "Age", ylab = "Frequency", col = "darkorange3")
hist(data$AgeF, main = "Distribution of Age in Females", xlab = "Age", ylab = "Frequency", col = "firebrick1")
data$AgeM <- scale(data$AgeM)
data$AgeF <- scale(data$AgeF)
当使用hist()
函数可视化数据的分布时,代码产生如图 10-9 和 10-10 所示的结果。
图 10-10。
Histogram of female ages
图 10-9。
Histogram of male ages
女性和男性年龄的分布呈正偏态,这意味着平均值小于中位数。然而,请注意,与男性年龄相比,女性年龄的差异明显较小。虽然这也可能是我们想要保留的观点,但是在探索数据集和解释信息显示的内容时,您应该了解显示图表的重要性。对于不太懂技术的人来说,这可能是展示信息的最有吸引力的方式之一。对于那些经常做报告的人来说,有效地利用情节是必须的。最后,我们通过对年龄变量执行高斯归一化来结束我们的数据清理和预处理,以便它们的输入不会影响我们的分类模型的准确性,因为它们与不是数字标签的每个其他变量处于不同的范围。
既然已经完成了所有必要的预处理,我们就可以着手特征选择的任务了。
特征选择
这个数据集没有异常大量的观察值,但 27 个单独的特征可能会造成过度杀伤,并将不必要地削弱我们的机器学习算法的预测能力。因此,我们消除不必要的特征是合理的,尽管我们应该注意这个过程不一定像看起来那样简单。
当查看相关矩阵时(该矩阵太大,无法在此显示),我们注意到通常存在弱到中等的线性相关性。我们可能无法从任何严重依赖线性假设的模型中获得有效的结果。当把它与特征选择联系起来时,我们同样不可能从使用 PCA 中得到好的结果。因此,我选择使用随机森林来表示特征的重要性,基于它们对观察分类的影响程度:
#Feature Selection
corr <- cor(data)
#Converting all Columns to Numeric prior to Input
for (i in 1:ncol(data)){
data[,i] <- as.integer(data[,i])
}
#Random Forest Feature Selection Based on Importance of Classification
data$second_date <- as.factor(data$second_date)
featImport <- random.forest.importance(second_date ∼., data = data, importance.type = 1)
columns <- cutoff.k.percent(featImport, 0.4)
print(columns)
执行上述代码时,以下各列高于为重要性设置的阈值 0.4:
[1] "DecisionF" "DecisionM" "AttractiveM" "FunF" "LikeM"
[6] "LikeF" "SharedInterestsF" "AttractiveF" "PartnerYesM"
这些将是我们训练集中使用的特性,现在我们可以继续进行模型训练和评估。
模型训练和评估
既然我们已经有了一个充分简化和转换的数据集,那么是时候开始模型选择的过程了。因为决定分类的函数不是线性的,所以我们应该考虑可以处理这种类型数据的函数。在下一个问题中,我们将使用以下算法组合:
- 逻辑回归
- 贝叶斯分类器
- k-最近邻
我们将单独调整每个算法的参数,评估训练集的性能,然后预测样本外。一旦我们对所有的算法都这样做了,我们将并排评估结果,然后选择最佳的算法。
方法 1:逻辑回归
有人建议,在评估投资组合分类算法时,你应该总是从逻辑回归开始。原因不在于期望这是最好的算法,而更多的是从这样一个观点来看,这形成了一个基线评估,从这个评估中你可以比较不同的分类算法。在本实验中,我们将评估模型在 AUC 分数方面的表现,AUC 分数是(ROC)曲线下的面积:
#Method 1: Logistic Regression
lambda <- seq(.01, 1, .01)
AUC <- c()
for (i in 1:length(lambda)){
rows <- sample(1:nrow(processedData), nrow(processedData)/2)
logReg <- glm(as.factor(second_date[rows]) ∼., data = processedData[rows, ], family = binomial(link = "logit"), method = "glm.fit")
y_h <- ifelse(logReg$fitted.values >= lambda[i], 1, 0)
AUC <- append(roc(y_h, as.numeric(second_date[-rows]))$auc, AUC)
}
我们从改变阈值开始,该阈值决定了我们是基于 lambda 参数将观察分类为 1 还是 0。我们迭代该算法,并将基于该参数的 AUC 分数附加到 AUC 向量。在这个迭代循环之后,我们应该使用一个图来直观地评估性能。当绘制λ值的 AUC 得分向量时,我们编写以下代码,并观察图 10-11 中所示的输出:
图 10-11。
AUC over lambda value
#Summary Statistics and Various Plots
plot(lambda[-1], AUC, main = "AUC over Lambda Value \n(Logistic Regression)",
xlab = "Lambda", ylab = "AUC", type = "l", col = "cadetblue")
我们看到,当λ值为 0.15 时,AUC 得分最高,因此我们将使用该λ值。这是我建议你如何调整机器学习算法参数的一个例子。每个参数都应该单独调整,以便实现给定的目标,无论是最小化 MSE 还是最大化 AUC。在逻辑回归中,对数优势阈值实际上是我们需要调整的唯一参数。我们可以在测试集上通过多次迭代来查看优化模型的性能:
#Tuned Model
AUC <- c()
for (i in 1:length(lambda)){
rows <- sample(1:nrow(processedData), nrow(processedData)/2)
logReg <- glm(as.factor(second_date[rows]) ∼., data = processedData[rows, ], family = binomial(link = "logit"), method = "glm.fit")
y_h <- ifelse(logReg$fitted.values >= lambda[which(AUC == max(AUC))], 1, 0)
AUC <- append(roc(y_h, as.numeric(second_date[-rows]))$auc, AUC)
}
#Summary Statistics and Various Plots
plot(AUC, main = "AUC over 100 Iterations \n(Naive Bayes Classifier)",
xlab = "Iterations", ylab = "AUC", type = "l", col = "cadetblue")
hist(AUC, main = "Histogram for AUC \n(Naive Bayes Classifier)",
xlab = "AUC Value", ylab = "Frequency", col = "firebrick3")
通过收集 AUC,我们遵循与调整机器学习算法时相同的直觉。逻辑回归的本质是在每次迭代时拟合模型,而不是像某些算法那样选择最佳回归解。当相对于一段时间内的迭代绘制 AUC 向量并绘制 AUC 向量的直方图时,我们观察到图 10-12 和 10-13 中所示的结果。
图 10-13。
Logistic regression AUC histogram over 100 iterations
图 10-12。
Logistic regression AUC over 100 iterations
在数值上,我们可以用下面的函数来总结这个向量:
summaryStatistics(AUC)
Mean Std.Dev Min Max Range
1 0.5063276 0.04964798 0.3920711 0.6297832 0.2377121
我们将在前进的道路上牢记这些价值观。当按原样分析它们时,逻辑回归是一个不充分的分类器。通常,我们希望 AUC 分数至少为 0.70,因为 0.50 的分数表明模型只有 50%的正确率。小于 0 . 50 不是最佳的,这意味着我们应该认为这个分类器是不够的。
方法 3:K-最近邻(KNN)
这是一个相当简单的分类算法,在第三章中有详细描述。选择这种算法相对于另一种概率算法的目的是创建一个多样化的算法组合,以便我们可以推断出哪种类型的算法最适合这项任务。作为读者的注意事项,class 包中的 K-NN 算法从测试数据中产生分类。若要仅根据训练数据训练算法,请使用分配给“train”参数的相同数据:
#Method 3: K-Nearest Neighbor
#Tuning K Parameter (Number of Neighbors)
K <- seq(1, 40, 1)
AUC <- c()
for (i in 1:length(K)){
rows <- sample(1:nrow(processedData), nrow(processedData)/2)
y_h <- knn(train = processedData[rows, ], test = processedData[rows,], cl = second_date[rows], k = K[i], use.all = TRUE)
AUC <- append(roc(y_h, as.numeric(second_date[rows]))$auc, AUC)
}
#Summary Statistics and Various Plots
plot(AUC, main = "AUC over K Value \n(K Nearest Neighbor)", xlab = "K", ylab = "AUC", type = "l", col = "cadetblue")
当查看 AUC 与 K 值的关系图时,我们可以看到图 10-14 所示的结果。
图 10-14。
KNN classifier AUC over 100 iterations
对于所有值,训练阶段的 AUC 分数通常令人印象深刻,但是选择比大 K 值低的 K 值以防止过度拟合是合理的。因此,我们将选择 K 为 3。让我们用我们调优的模型在测试集上观察 AUC 分数,如图 10-15 和 10-16 所示。
图 10-16。
KNN AUC over 100 iterations on test set histogram
图 10-15。
KNN AUC over 100 iterations on test set
在数字上,我们评估 AUC 向量如下:
summaryStatistics(AUC)
Mean Std.Dev Min Max Range
1 0.445006 0.01126862 0.4257075 0.4663915 0.04068396
最后,我们预测出样本并观察到以下结果:
#Predicting out of Sample
y_h <- knn(train = processedData[rows, ], test = processedData[-rows, ], cl = second_date[-rows])
roc(y_h, as.numeric(second_date[-rows]))$auc
曲线下面积:0.4638
除了测试集的性能客观上很差之外,我们还看到了从训练集到测试集的明显下降。
方法 2:贝叶斯分类器
我怀疑第二次约会的发生可以用贝叶斯估计器来建模,所以我们将开始的第一个模型是贝叶斯分类器。在下面的代码中,我们首先对数据集执行双重交叉验证,以便评估训练集的性能。在这个特定的模型中,只需要进行很少的调整,所以我们只需观察模型在 100 次迭代后的性能:
#Method 1: Bayesian Classifier
AUC <- c()
for (i in 1:100){
rows <- sample(1:nrow(processedData), 92)
bayesClass <- naiveBayes(y = as.factor(second_date[rows]), x = processedData[rows, ], data = processedData)
y_h <- predict(bayesClass, processedData[rows, ], type = c("class"))
AUC <- append(roc(y_h, as.numeric(second_date[rows]))$auc, AUC)
}
#Summary Statistics and Various Plots
plot(AUC, main = "AUC over 100 Iterations \n(Naive Bayes Classifier)",
xlab = "Iterations", ylab = "AUC", type = "l", col = "cadetblue")
hist(AUC, main = "Histogram for AUC \n(Naive Bayes Classifier)",
xlab = "AUC Value", ylab = "Frequency", col = "cadetblue")
summaryStatistics(AUC)
当执行代码时,我们将 AUC 分数附加到向量 AUC,如前面循环 100 次迭代的代码所示。该矢量的线图和直方图如图 10-17 和 10-18 所示。
图 10-18。
Bayes classifier AUC histogram over 100 iterations
图 10-17。
Bayes classifier AUC performance over 100 iterations
我们观察到 AUC 分数在其分布中具有轻微的右偏,并且大多数 AUC 分数分布在彼此相对紧密的范围内。当查看原始数值数据时,我们观察到以下情况:
Mean Std.Dev Min Max Range
1 0.8251087 0.03142345 0.7567568 0.9027778 0.146021
对于我们选择的模型而言,这些 AUC 分数超出了一般可接受的范围,尽管我们仍应评估样本外模型的性能,以确定该过程的稳定性:
#Predicting out of Sample
y_h <- predict(bayesClass, processedData[-rows, ], type = c("class"))
roc(y_h, as.numeric(second_date[-rows]))$auc
执行以下代码后,我们观察到以下 AUC 得分:曲线下面积:0.8219。这在从训练集产生的数据的分布中是可接受的,该 AUC 分数趋向于数据的平均值。
在评估所选的解决方案时,我强烈建议选择贝叶斯分类器,因为它从训练到测试集都很稳定,并且 AUC 得分高于所有其他方法。在实际设置中,我们会使用样本数据的预测来帮助影响我们的决策过程。在专业背景下,这可能包括根据不同用户的交友档案向他们进行有针对性的营销或推荐。
摘要
现在你已经对我如何推荐应用我在前面章节中解释的概念有了一个简要而全面的了解。你还应该注意到,虽然我已经成功地使用这种通用过程/方法实现了机器学习算法,但这并不是训练/调整机器学习模型的唯一方式。尽管如此,我还是非常强调度量标准的使用,以及在调优不同参数时根据这些度量标准绘制模型的性能。第十一章将看如何实现和使用各种深度学习模型的使用示例。
十一、深度学习和其他示例问题
既然我已经充分地介绍了如何使用和应用机器学习概念,我们应该最终使用 r 来应用和编码深度学习模型。这似乎是一项艰巨的任务,但不要被吓倒。如果您已经能够成功地编写本书中的所有代码,那么就只需要适应新的软件包了。我们将讨论各种深度学习示例,但将从处理更简单的模型开始,然后最终转向更复杂的模型。这些练习有两个目的:
- 展示如何构建这些模型或者从不同的包中访问它们
- 举例说明它们在实际概念中的应用
自编码器
该书深度学习章节中描述的许多其他模型在谈到如何使用它们时都相对简单,但我发现自编码器的使用不会自动变得清晰。因此,我想探索一个用例,在这个用例中,自编码器的使用在实际环境中变得非常清晰。让我们考虑这样一种情况,我们希望使用自编码器来提高第十章中分类算法的性能。具体来说,我指的是我们走过的分类问题,其中我们试图根据几个特征来确定一对个体是否会进行第二次约会。让我们从贝叶斯分类器开始:
#Bayes Classifier
#Bayes Classifier
AUC <- c()
for (i in 1:100){
rows <- sample(1:nrow(processedData), 92)
bayesClass <- naiveBayes(y = as.factor(second_date[rows]), x = processedData[rows, ], data = processedData)
y_h <- predict(bayesClass, processedData[rows, ], type = c("class"))
AUC <- append(roc(y_h, as.numeric(second_date[rows]))$auc, AUC)
}
summaryStatistics(AUC)
curve <- roc(y_h, as.numeric(second_date[rows]))
plot(curve, main = "Bayesian Classifier ROC")
当执行前面的代码时,会产生如图 11-1 所示的结果。
图 11-1。
ROC plot for Bayesian classifier
当收集样本统计数据时,我们观察该模型的 AUC 分数:
Mean Std.Dev Min Max Range
0.8210827 0.02375922 0.7571429 0.875 0.1178571
这些都是客观上的好成绩。然而,出于这个例子的目的,我们将使用一个自编码器来帮助进一步提高这个模型的性能。这就是我介绍 h2o 的地方。h2o 为 R(以及其他语言)提供了一个深度学习框架,您会发现它对实现许多模型非常有用。我鼓励你搜索文档,因为深度学习模型的一些实现很难找到(更不用说找到健壮的实现了)。让我们初始化 h2o 并使用自编码器:
#Autoencoder
h2o.init()
training_data <- as.h2o(processedData, destination_frame = "train_data")
autoencoder <- h2o.deeplearning(x = colnames(processedData),
training_frame = training_data, autoencoder = TRUE, activation = "Tanh",
hidden = c(6,5,6), epochs = 10)
autoencoder
h2o 类似于 TensorFlow,每个会话都必须初始化。初始化之后,无论什么数据通过所用的模型,都必须转换成 h2o 友好的格式。我们对训练数据进行转换。我们的 autoencoder 有三个隐藏层,每个层在给定的层中分别有六个、五个和六个神经元(由h2o.deeplearning()
函数中的“hidden”参数表示)。我们用 tanh 作为我们的激活函数。执行以下代码后,我们会看到如图 11-2 所示的内容。
图 11-2。
Summary of autoencoder function
注意 MSE 值。因为我们试图重新创建一个函数的输入,这就变成了一个回归任务。因此,我们使用传统的回归统计(MSE 和 RSME)来评估该算法的有效性。让我们仔细看看此处得出的 MSE,并根据保存训练数据的数据帧的索引来查看 MSE:
#Reconstruct Original Data Set
syntheticData <- h2o.anomaly(autoencoder, training_data, per_feature = FALSE)
errorRate <- as.data.frame(syntheticData)
#Plotting Error Rate of Feature Reconstruction
plot(sort(errorRate$Reconstruction.MSE), main = "Reconstruction Error Rate")
h2o.anomaly()
函数使用自编码器来检测异常,我们在统计上将异常定义为在重建过程中 MSE 明显高于其他观测值的观测值。当执行前面的代码时,我们得到图 11-3 。
图 11-3。
Plot of reconstruction error
我们可以看到,从指数水平 225 到训练数据结束,MSE 稳定增加,但也急剧增加。我们可以合理地说,离群值通常是这些最后的输入。考虑到这一点,我们将使用由 MSE 确定的阈值来将离群值从非离群值中分离到它们各自的子集。我们试图通过将我们的模型拟合到这些子集来训练我们的贝叶斯分类器,并观察模型的性能相对于 AUC 分数如何提高(或不提高):
#Removing Anomolies from Data
train_data <- processedData[errorRate$Reconstruction.MSE < 0.01, ]
#Bayes Classifier
AUC <- c()
for (i in 1:100){
rows <- sample(1:nrow(processedData), 92)
bayesClass1 <- naiveBayes(y = as.factor(second_date[rows]), x = processedData[rows, ], data = processedData)
y_h <- predict(bayesClass1, processedData[rows, ], type = c("class"))
AUC <- append(roc(y_h, as.numeric(second_date[rows]))$auc, AUC)
}
#Summary Statistics
summaryStatistics(AUC)
我们遵循第十章中关于模型训练的相同一般步骤,收集 100 次试验的 AUC 统计样本。这里唯一的区别是,我们使用了低于 MSE 阈值的指数值的数据子集。查看汇总统计数据时,我们观察到以下情况:
Mean Std.Dev Min Max Range
0.8274664 0.03076285 0.75 0.9117647 0.1617647
当将我们的结果分布与原始模型进行比较时,我们观察到一个稍高的平均值,一个较高的最大值。然而,我们也观察到一个较低的最小值。因此,我们的结果的范围和标准偏差增加。让我们评估一下我们只看异常情况时的结果:
##########################################################################
#Using only Anomalies in Data Set
train_data <- processedData[errorRate$Reconstruction.MSE >= 0.01, ]
#Bayes Classifier
AUC <- c()
for (i in 1:100){
rows <- sample(1:nrow(processedData), 92)
bayesClass2 <- naiveBayes(y = as.factor(second_date[rows]), x = processedData[rows, ], data = processedData)
y_h <- predict(bayesClass2, processedData[rows, ], type = c("class"))
AUC <- append(roc(y_h, as.numeric(second_date[rows]))$auc, AUC)
}
#Summary Statistics
summaryStatistics(AUC)
执行上述代码时,我们会看到以下结果:
Mean Std.Dev Min Max Range
0.8323727 0.03168166 0.7692308 0.9107143 0.1414835
在这里,我们观察到,这种分布包含最高的平均值和最小值,与范围和标准偏差的中等结果。当在两个数据集之间进行选择时,我会主张在这种情况下使用第二个子集,因为平均而言 AUC 得分表现更好,并且考虑到至少我们仍然可以期待更高的得分。
这种技术的重要性在于,它是一种有效的方法,通过这种方法,您可以在数据子集上拟合高级模型。如果您发现您的数据集比您想要的要小,这将非常方便。尽管使用了适当的交叉验证技术、数据预处理技术和参数调整技术,但有时您会发现自己在尝试调整一个性能稍微不令人满意的模型时遇到困难。在由于缺乏数据而导致这种情况的情况下,在试图获取更多数据之前,我会首先尝试使用这种技术。至于我们实验的最后一步,让我们使用拟合的模型,看看它们在样本外的表现如何:
#Fitted Models and Out of Sample Performance
AUC1 <- AUC2 <- c()
for (i in 1:100){
rows <- sample(1:nrow(processedData), 92)
y_h1 <- predict(bayesClass1, processedData[-rows,], type = c("class"))
y_h2 <- predict(bayesClass2, processedData[-rows,], type = c("class"))
AUC1 <- append(roc(y_h1, as.numeric(second_date[-rows]))$auc, AUC1)
AUC2 <- append(roc(y_h2, as.numeric(second_date[-rows]))$auc, AUC2)
}
summaryStatistics(AUC1)
summaryStatistics(AUC2)
当执行前面的代码时,我们看到模型与没有异常和只有异常的子集相匹配的结果,分别如图 11-4 和 11-5 所示:
图 11-5。
ROC curve for Bayes model w/o anomalies (AUC: 0.8188)
图 11-4。
ROC curve for Bayes model without anomalies (AUC : 0.7821)
Mean Std.Dev Min Max Range
0.7890102 0.01468805 0.75 0.8194444 0.06944444
Mean Std.Dev Min Max Range
0.8303613 0.01506222 0.7957983 0.8688836 0.07308532
当回顾我们实验的结果时,已经变得非常清楚的是,仅拟合异常的第二个模型比拟合没有异常的观察的模型产生明显更好的模型。但是,在我们完全确信应该使用第二个模型之前,让我们使用来自这两个模型的数据快速执行一个双边假设检验。
因为我们对结果进行了 100 次采样,所以我们可以安全地使用 Z 检验。因此,我们设置 Z 测试参数,如以下代码所示:
#Two Sided Hypothesis Test
require(BSDA)
z.test(x = AUC1, y = AUC2, alternative = "two.sided", mu = mean(AUC2) - mean(AUC1),
conf.level = 0.99, sigma.x = sd(AUC1), sigma.y = sd(AUC2))
当执行前面的函数时,它产生如图 11-6 所示的输出。
图 11-6。
Two-sided hypothesis test results
从统计学上看,在 99%的置信区间内,我们已经确定两个模型的结果在统计学上彼此不同,因此我们可以放心地选择拟合的第二个贝叶斯模型,因为我们知道它是最优模型。
卷积神经网络
当我在第五章中讨论 CNN 时,我通过讨论 MNIST 数字识别用例展示了这个模型的威力。尽管这一度是 CNN 的主要用途,但现在它们正被用于越来越困难和复杂的任务。现在我想探索一个用例,在这个用例中,我们试图区分比手写数字复杂得多的不同对象。在本教程中,我们将使用加州理工学院 101 数据集,它包含 101 个对象类别,每个类别中有 60 到 800 张图像。我们将从每个类别中选取不同的图片,这样我们就可以得到不同的图片,而不会选取完全不同的图片。我们将在吉他和笔记本电脑的图像中进行选择。这些照片的样本如图 11-7 和 11-8 所示。
图 11-8。
Photo of laptop
图 11-7。
Photo of guitar
这些图像是技术的产物,但它们彼此之间有着明显的不同,我们希望人类能够区分它们。现在让我们讨论我们应该如何为 CNN 准备数据。
预处理
处理图像文件需要一种特殊类型的预处理,我们还没有详细讨论过,主要是因为图像识别和计算机视觉是计算机科学的一个非常特殊的子领域。明智的做法是寻找其他文本来建立你对计算机视觉的理解,但是这篇文章将会给你一个基本的概述。我们正在处理彩色图像,每个图像都有 x,y,z 维度,其中 x 和 y 是每张照片特有的,但是 z 总是 3。就计算机理解的图像文件而言,它们是相互堆叠的三层矩阵,每个像素是矩阵中的一个单独的条目。对于这个任务,我推荐你使用 EBImage 包,这样你就可以灰度化和调整图像大小。为了帮助神经网络的训练时间,我们将调整图像的大小,使它们更小,因此神经网络接受的数据更少。但是让我们一步一步地完成我们的预处理:
#Loading required packages
require(mxnet)
require(EBImage)
require(jpeg)
require(pROC)
#Downloading the strings of the image files in each directory
guitar_photos <- list.files("/file/path/to/image")
laptop_photos <- list.files("/file/path/to/image")
加州理工学院图书馆被组织成多个层次的目录,所以当试图以自动化的方式访问这些图像时要小心。每个类别的所有目录都具有相同的文件名格式:图像文件表示为 image_000,X,其中 X 是目录中图像的编号。但是每个目录都有不同数量的文件,所以我们应该使用list.files()
函数来收集目录中所有图像文件的名称。我们在下面的代码中使用它们。使用list.files()
功能时吉他照片目录的内容以截断的形式显示在图 11-9 中。
图 11-9。
List of files from image directory
现在我们有了各个文件的名称,我们可以使用以下过程将它们加载到img_data
数据框中:
#Creating Empty Data Frame
img_data <- data.frame()
#Turning Photos into Bitmaps
#Guitar Bitmaps
for (i in 1:length(bass_photos)){
img <- readJPEG(paste("/path/to/image/directory/", guitar_photos[i], sep = ""))
我们在这里使用paste
函数将目录和带有字符串的图像结合起来,这样它就可以引导我们找到数据。使用 jpeg 包中的readJPEG()
函数,我们可以将图像读入位图,就像前面描述的矩阵堆栈一样。每个维度代表构成每张彩色照片的三种颜色(红色、蓝色和绿色)。但是为了降低我们正在处理的图像的复杂性,我们将把这些图像转换成灰度(黑白)。当处理黑白图像时,我们给像素值分配一个 0 到 1 之间的数字,0 代表黑色,1 代表白色。中间的颜色决定了特定颜色向光谱任一侧的强度:
#Reshape to 64x64 pixel size and grayscale image
img <- Image(img, dim = c(64, 64), color = "grayscale")
#Resizing Image to 28x28 Pixel Size
img <- resize(img, w = 28, h = 28)
img <- img@.Data
我们使用 EBImage 中提供的resize()
函数对各种图像进行整形和调整大小。如果你对查看图像灰度化后的样子感兴趣,可以随意使用display()
和Image()
函数。调整图像大小后,我们将位图转换成矢量,以便更好地存储。最后,在创建和训练模型时,我们必须向数据向量添加一个标签。这在计算我们模型的准确性时会很有用。具体来说,吉他将被标记为 1,笔记本电脑将被标记为 2:
#Transforming to vector
img <- as.vector(t(img))
#Adding Label
label <- 1
img <- c(label, img)
#Appending to List
img_data <- rbind(img_data, img)
}
我们对笔记本电脑图像重复这一过程。如果您想使用这种预处理和模型评估的结构,请随意,或者尝试其他预处理方法。在创建 CNN 模型之前,我们必须确保模型的输入格式是正确的。MXNet 和许多神经网络模型都有您应该熟悉的特定格式。第一步是创建一个训练和测试集。在本例中,我们将拆分数据集,这样我们可以针对 75%的数据进行训练,针对剩余的 25%进行测试。我们现在将转换数据,使其成为一个矩阵,其中每一行都是不同的图像观察,标签作为第一列条目,位图值作为连续的列条目。然后,我们将从 X 矩阵中剥离标签,并将其用作 y 向量相应观察顺序中的值。然后,我们使用sample()
函数执行交叉验证:
#Transforming data into matrix for input into CNN
training_set <- data.matrix(img_data)
#Cross Validating Results
rows <- sample(1:nrow(training_set), nrow(training_set)*.75)
#Training Set
x_train <- t(training_set[rows, -1])
y_train <- training_set[rows, 1]
dim(x_train) <- c(28,28, 1, ncol(x_train))
在前面的代码中,指出一个明显的细节是很重要的,如果忽略这个细节,您将无法执行代码。MXNet CNN 模型只取一个 4 维的 X 矩阵。请务必记住这一点,否则您将浪费时间来调试这个问题!我们还相应地改变了测试集的维度:
#Test Set
x_test <- t(training_set[-rows, -1])
y_test <- training_set[-rows, 1];
dim(x_test) <- c(28,28, 1, ncol(x_test))
既然我们已经完成了数据的预处理,我们终于可以开始构建和训练我们的模型了。
模型构建和培训
CNN 模型的构建方式是数据通过每一层,但实际输入到FeedForward()
函数的唯一一层是最后一层。因此,我们在这里激活模型之前构建它。有些包可能更专有,需要更少的架构,但 MXNet 允许很大程度的定制,如果你想构建不同的 ConvNet 结构,这将是有用的,如第五章中详述的那些。如果你想提高这里的结果,这可能是一个很好的利用你的时间。
让我们来看看建筑。这里我们将使用通用的 LeNet 架构,这是图像识别任务的标准。因此,我们以同样的方式组织这些层:
data <- mx.symbol.Variable('data')
#Layer 1
convolution_l1 <- mx.symbol.Convolution(data = data, kernel = c(5,5), num_filter = 20)
tanh_l1 <- mx.symbol.Activation(data = convolution_l1, act_type = "tanh")
pooling_l1 <- mx.symbol.Pooling(data = tanh_l1, pool_type = "max", kernel = c(2,2), stride = c(2,2))
#Layer 2
convolution_l2 <- mx.symbol.Convolution(data = pooling_l1, kernel = c(5,5), num_filter = 20)
tanh_l2 <- mx.symbol.Activation(data = convolution_l2, act_type = "tanh")
pooling_l2 <- mx.symbol.Pooling(data = tanh_l2, pool_type = "max", kernel = c(2,2), stride = c(2,2))
我们首先创建一个虚拟的data
变量,该变量将用于以对 ConvNet 友好的文件格式传递 x 矩阵值。data
经过每一层,如第五章所述,模型从数据的较低抽象到较高抽象进行构建,以做出决定。在这里,我们将使用一般建议的步幅 2,20 个过滤器在第一 Conv 层,50 个过滤器在第二 Conv 层。作为激活函数,我们使用 tanh。该激活函数将在整个模型中保持不变,但输出函数除外:
#Fully Connected 1
fl <- mx.symbol.Flatten(data = pooling_l2)
full_conn1 <- mx.symbol.FullyConnected(data = fl, num_hidden = 500)
tanh_l3 <- mx.symbol.Activation(data = full_conn1, act_type = "tanh")
#Fully Connected 2
full_conn2 <- mx.symbol.FullyConnected(data = tanh_l3, num_hidden = 40)
#Softmax Classification Layer
CNN <- mx.symbol.SoftmaxOutput(data = full_conn2)
数据继续传递到完全连接的层。在完全连接的层中分别有 500 和 40 个隐藏神经元。最后,数据到达最后一层,在这里我们有一个 softmax 分类器来确定观察值的类别。
但是,在我们做出任何预测之前,我们必须使用上一节中建议的方法来训练我们的参数。如果可能,特别是在神经网络的情况下,强烈建议对支持这些功能的包使用本地搜索方法。具体来说,h2o 支持网格搜索功能来调整参数。虽然我们在这里使用 MXNet,但是让读者了解提供这些功能的包是很有用的。
让我们从训练参数开始:
#Learning Rate Parameter
AUC <- c()
learn_rate <- c(0.01, 0.02, 0.03, 0.04)
CPU <- mx.cpu()
for (i in 1:length(learn_rate)){
cnn_model <- mx.model.FeedForward.create(CNN, X = x_train, y = y_train, ctx = CPU, num.round = 50, array.batch.size = 40,
learning.rate = learn_rate[i],
momentum = 0.9, eval.metric = mx.metric.accuracy,
epoch.end.callback = mx.callback.log.train.metric(100),
optimizer = "sgd")
#Code redated partially, please check github!
与其他神经网络模型类似,学习率参数决定了更新连接各层的权重时梯度的大小。我们给出了一个数组,并根据图 11-10 中的调整参数绘制了 AUC。
图 11-10。
AUC score over learning rate
我们可以清楚地看到,这里 0.04 的学习率是最优的,因为它产生最高的 AUC 分数。
现在让我们训练动量参数:
AUC1 <- c()
mom <- c(0.5, 0.9, 1.5)
for (i in 1:length(mom)){
cnn_model <- mx.model.FeedForward.create(CNN, X = x_train, y = y_train, ctx = CPU, num.round = 50, array.batch.size = 40, learning.rate = 0.04,
momentum = mom[i], eval.metric = mx.metric.accuracy,
epoch.end.callback = mx.callback.log.train.metric(100), optimizer = "sgd")
#Code redacted partially, please check github!
当我们执行前面的代码时,我们会收到如图 11-11 所示的结果。
图 11-11。
AUC over momentum value
当评估不同参数的结果时,我们将动量值设置为 0.9。现在我们已经调优了这两个参数,我们可以在最后一部分开始训练调优的模型,并在测试和训练集上评估它的性能:
#Fitted Model Training
cnn_model <- mx.model.FeedForward.create(CNN, X = x_train, y = y_train, ctx = CPU, num.round = 150, array.batch.size = 40,
learning.rate = 0.04, momentum = 0.9, eval.metric = mx.metric.accuracy,
initializer = mx.init.normal(0.01) , optimizer = "sgd")
#Calculating Training Set Accuracy
y_h <- predict(cnn_model, x_train)
Labels <- max.col(t(y_h)) - 1
roc(as.factor(y_train), as.numeric(Labels))
curve <- roc(as.factor(y_train), as.numeric(Labels))
#Code partially redacted, please check github!
在执行代码之前,我想指出一个细节。这里,我们没有启用 GPU 训练。如果您想减少训练时间并提高计算性能,请查看 MXNet 文档中启用此功能的必要步骤。在这个例子中,我们将使用 CPU 培训。您还应该意识到,增加num.round
参数的诱惑通常会很强烈,因为这将直接影响模型对训练集数据的准确性。请注意,将该参数设置得太高会导致过度拟合,特别是对于我们在本例中使用的数据集。当执行上述代码时,用户应该看到终端以如下格式打印出训练精度:
[184] Train-accuracy=0.708333333333333
[185] Train-accuracy=0.708333333333333
[186] Train-accuracy=0.708333333333333
[187] Train-accuracy=0.708333333333333
[188] Train-accuracy=0.708333333333333
单词Train-accuracy
左侧的数字代表当前迭代,该迭代将运行到num.round
参数中指示的数字。这里使用的accuracy
参数相当于 AUC 分数,由mx.metric.accuracy
对象给出。像往常一样,学习率很难近似,但我们可以通过使用随机梯度下降优化器调整神经网络中的权重来减轻准确性的损失。执行代码时,我们得到图 11-12 。
图 11-12。
ROC plot for CNN over training data
该 ROC 图的 AUC 为 0.7706。当评估测试数据的性能时,产生图 11-13 和结果。
图 11-13。
ROC plot for CNN over test data
当对照测试数据进行预测时,模型的 AUC 为 0.7063。粗略地说,这里的性能相当相似,尽管正如我们所料,我们确实注意到从训练集到测试集的性能有所下降。也就是说,即使在这种情况下,也不太可能有任何过度拟合的迹象。然而,如果您想将这样的东西投入生产,您可能仍然倾向于改进这些模型的性能。理想情况下,在对图像进行分类时,我们希望模型的准确率至少达到 90%。虽然这里的图像分类情况是相当良性的,但是存在不正确的分类会在每次观察中损失大量金钱或者导致不正确的诊断从而导致患者接受不适当的护理的情况。考虑到这一点,你将如何从这一点着手?
最合理的下一步是为这个模型的训练阶段获取更多的数据。这通常是我们认为建立足够的卷积神经网络的最大挑战:获得足够的训练数据。对于许多不同的商业产品,合法地获取这些数据可能是一项非常艰巨的任务,在最坏的情况下,需要团队自己在现实世界中获取数据。在为特定任务创建 CNN 时,读者应该注意这一点,因为有时任务的可行性纯粹是数据可访问性的问题。在这种情况下,我们使用的数据集大约只有总共 170 张照片,其中 75%以上是我们训练的。
另一个你可能想注意的建议是使用另一个网络架构,或者如果你有足够的野心,尝试创建自己的网络架构。然而,创建自己的网络架构可能是一项极其艰巨的任务。另一个可能的探索途径是创建几个卷积神经网络。从这些模型中,我们可以创建一个数据集,其中每个特征都是给定 CNN 的输出。这个数据集然后可以被输入到传统的机器学习模型中。但是,您应该意识到,这些方法本身可能需要对前面概述的方法进行重大调整。
协同过滤
对于我们的最后一个例子,我们将简要地处理推荐系统的问题,就像在前面的章节中简要地处理的那样。推荐系统是不断发展的,但是由于数据科学在其中的应用,提出这个概念是有用的。在这里,您将了解插补的实际应用,以及数据科学的一些软技能,如数据转换,这些都已简要介绍过,但从未介绍过。
推荐系统是 Amazon.com 等电子商务网站特有的,但也存在于网飞等基于内容的网站。动机相当简单,因为向客户推荐他们合理喜欢的产品是合理的。然而,这样做的任务比看起来更困难。大多数用户不会使用某个公司提供的所有产品。即使他们这样做了,也不意味着他们会对他们使用的每一种产品进行评级。这就给我们留下了一个矩阵中数值稀少的问题。然而,我们已经回顾了处理这个问题的技术,并将继续检查我们的数据集。
对于这个实验,我们将使用第三个 Jester 数据集( http://goldberg.berkeley.edu/jester-data/
)。特征都代表个人笑话,行代表用户。矩阵中的每个条目都是一个笑话的等级,其中下限是–10,上限是 10。然而,每当没有一个笑话的条目时,就用 99 来表示。当检查数据集的头部时,我们看到如图 11-14 所示的矩阵。
图 11-14。
Snapshot of the Jester data set
这里的目标是根据笑话本身的相似性来衡量不同用户口味的相似性。为此,我们将计算列向量之间的余弦相似度。简而言之,在讨论结合矩阵分解和 RBMs 来估算缺失值之前,让我们先讨论余弦相似性的概念。当处理试图比较向量的问题时,余弦相似性是一个经常被引用的概念。直观上,我们将余弦相似度定义为两个非零向量不同的程度。数学上,我们用下面的等式
定义余弦相似度,其中 A,B =两个不同的向量。
与相关系数类似,余弦相似值的范围从–1 到 1。余弦相似度为 1 表示值完全相同,而–1 表示值完全相反。零值表示向量之间完全没有关系。考虑到这一点,我们将比较某些音乐的消费模式,这样我们就可以比较哪些项目彼此最相似,因此应该推荐给其他人。
然而,对于那些密切关注的人来说,余弦相似性与两个非零向量一起使用,这意味着我们必须为我们的数据集生成缺失值。已经讨论了许多插补技术,但 Geoffrey Hinton 认为在这种情况下有用的一种技术是矩阵分解。具体来说,我建议你用奇异值分解(SVD)。
本书其他地方讨论的 SVD 和 PCA 是高度相关的技术。它们都是执行矩阵特征分解,但是 SVDs 应用不同于 PCA 的应用。特别地,奇异值分解可以用来逼近缺失值。因此,让我们使用impute.svd()
函数估算我们的值:
require(lsa)
require(bcv)
require(gdata)
require(Matrix)
#Upload the data set
#Please be patient this may take a handful of seconds to load.
data <- read.xls("/path/to/data/.xls", sheet = 1)
colnames(data) <- seq(1, ncol(data), 1)
#Converting 99s to NA Values (1)
data[data == 99] <- NA
#Converting 99s to Mean Column Values (2)
for (i in 1:ncol(data)){
data[is.na(data[,i]), i] <- mean(data[,i], na.rm = TRUE)
}
我们首先将 99(1)转换为 NA 值,然后将 NA 值更改为列 means (2)。在这一点上,我们可以前进,估算的价值:
#Imputing Data via SVD
newData <- impute.svd(data, k = qr(data)$rank, tol = 1e-4, maxiter = 200)
print(newData$rss)
head(data[, 2:10])
请注意,impute.svd()
函数要求您估算缺失值的任一列平均值,或者如果一整列的观察值缺失,则使其为 0。如果你不遵循这些指示,你会收到不正确的结果。当执行前面的代码时,我们产生如图 11-15 所示的输出。
图 11-15。
Head of imputed data set
当执行 SVD 时,我们还计算了关于非缺失值和这些非缺失值的预测的平方和为 4.398197e-20。想要挑战自我的读者可以不使用 SVD 估算值,而是使用 RBM。但是,请注意,这项任务的计算量非常大,并且针对这项任务修改 RBM 并不容易。寻找 Geoffrey Hinton 给出的关于这个主题的高层次概述( http://www.machinelearning.org/proceedings/icml2007/papers/407.pdf
)。
我们现在可以计算列之间的余弦距离:
itemData <- matrix(NA, nrow = ncol(data), ncol = 11,
dimnames=list(colnames(data)))
#Getting Cosine Distances
for (i in 1:nrow(itemData)){
for (j in 1:ncol(itemData)){
itemData[i,j] <- cosine(data[,i], data[,j])
}
}
当执行前面的代码时,我们产生如图 11-16 所示的数据集。
图 11-16。
Head of the cosine distance data set
从这个数据集,我们现在可以执行最终的数据转换,这样每行代表一个特定的笑话,每列代表从左到右降序排列的最相似的笑话。我们首先通过实例化一个具有适当维度(1)的空矩阵来做到这一点。实例化该矩阵后,我们可以通过对余弦值进行排序并获取包含前 11 个值的索引来填充数据——我们获取前 11 个值是因为数字 1 值本身总是相同的项目:
#Creating Matrix for ranking similarities (1)
similarMat <- matrix(NA, nrow = ncol(itemData), ncol = 11)
#Sorting Data Within Item Data Matrix (2)
for(i in 1:ncol(itemData)) {
rows <- order(itemData[,i], decreasing = TRUE)
similarMat[i,] <- (t(head(n=11, rownames(data[rows ,][i]))))
}
#Printing Result
similarMat
当执行前面的代码时,我们得到我们的最终答案,如图 11-17 所示。
图 11-17。
Top 10 recommendations for 11 separate jokes
我们将结果解释为对 11 个不同的笑话产生了前 10 个推荐。您可以在一个平台中实现这一点,这样,在一个网页上,用户可以收到不同页面、产品或类似实体的推荐。
摘要
我们现在已经到了本章的结尾,以及我们对深度学习和机器学习技术的全面回顾。第十二章提供了所有数据科学家在他们的研究或专业努力中前进时应该知道的简要建议。
十二、总结
我们已经读完了这本书。到现在为止,你应该感到很舒服,因为你已经获得了数据科学、机器学习和深度学习的一般概述。如果没有,你至少应该充分意识到在复习和进一步研究中你需要集中精力的地方。这本书的目的不是让任何人成为专家。相反,它应该被用来突出这些技术在给定领域中各自的能力。最后,我想就使用这些模型的最佳方式和机器学习的一般方法向所有读者提供一些建议。
在每个领域,都有长期被研究的特质。这就是我通常所说的 X 科学,其中 X 是我们正在讨论的一个给定的领域。有时,在更广泛的领域中已经开发了特定的定量子领域来处理这些目标。鉴于这个世界的复杂性,在你寻求实现机器学习方法来解决问题之前,全面研究更广泛的领域和你感兴趣的特定子领域是一项要求,这一点怎么强调都不为过。我从许多同事和朋友那里听到的抱怨之一是,许多数据科学家经常有一个压倒性的缺陷:领域知识。机器学习和深度学习算法已经非常擅长在各种环境中执行,并且越来越能够产生鲁棒的解决方案。然而,在给定的环境中不恰当地使用一个好的工具会产生与在给定的环境中不恰当地使用错误的工具一样糟糕的结果。
在大规模实现算法之前,你应该确保你已经深刻理解了你所选择的算法。很少有比提供一个解决方案更糟糕的事情了,眼睁睁地看着过程失败,却无法提供如何修复的建议。除了一些不好的事情发生之外,通常你会被期望和那些没有多少技术背景的人讨论这些算法。虽然我在前几章已经强调了这一点,但我必须再次说明良好的可视化和简洁解释的力量。虽然你可能会发现错综复杂的细节引人注目,但一般人没有你花在这个主题上的自学时间。所以,只把事情弄得尽可能复杂。
最后,我敦促你在制定自己的解决方案时,尽可能发挥创造力。机器学习算法的激增令人兴奋,因为它彻底改变了我们的世界,但如果这些算法不以独特的方式使用,它也会导致产品之间的高度同质化。解决问题的过程,虽然有时令人沮丧,但也应该是富有挑战性和令人兴奋的。这应该是一个运用你的聪明才智创造独特解决方案的机会——而不是使用陈旧和陈旧的解决方案。重用大部分代码,虽然在开发阶段节省时间很诱人,而且经常是必要的,但也应该尽可能避免。总是强迫自己从零开始解决问题,因为这将激发新的更好的解决方案。
我祝愿所有读者在各自的学习、事业和生活中取得最大的成功。机器学习是我遇到过的最令人沮丧的概念之一,但通过学习它,我学到了大量关于计算机科学和我自己的知识,同时也认识了大量非常聪明的人。我希望通过研究这一领域给我的生活带来的快乐也能同样带给你好运。
真诚地
塔维·贝索罗二世
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2020-10-02 《线性代数》(同济版)——教科书中的耻辱柱