Python-数学建模手册-全-
Python 数学建模手册(全)
原文:
annas-archive.org/md5/245fb5b1e6a45a04dfcd1296bbb38482译者:飞龙
前言
数学建模是将商业问题转化为清晰数学公式的艺术。对于给定问题或使用场景,模型的算法实现有助于优化参数,并生成更好的洞见与理解,从而支持决策过程。数学模型可以补充机器学习模型,并在如医学等敏感领域做出重大决策时发挥作用。
有三个核心主题帮助你理解数学建模:
-
数学模型有用的领域——例如,控制工程和信号处理
-
已验证的基于 Python 的数学工具——例如,图论和 MCMC
-
数学优化的基础算法
本书将通过数学建模的概念和各种建模方法向你介绍。根据两大信息来源,我将指导你选择最优技术和最适合的算法,使用 Python 来解决商业问题:
-
我过去五年作为数据科学家和企业应用开发者的经验
-
我在不同科学学科中的学术研究(处于不同成熟阶段)已有十年之久
作为一名数据专业人士,我认为具有目标和约束条件的数学模型(基于方程的)与(基于数据的)机器学习模型一样重要。在某些情况下,恰当的两者结合能提供最佳解决方案。
本书适合人群
数据科学家、研究与开发专业人员以及商业科学家通常可以从本书中获得有关使用 Python 进行数学建模的实际洞见。
假定你已掌握以下知识:
-
微分方程
-
线性代数
-
统计学基础
-
数据类型与数据结构
-
数值算法
你将学习到数学模型的相关性,如何在解决商业问题时考虑模型的可解释性,以及数学优化和调优机器学习模型在获得最佳解决方案时的重要性。你还将学会如何选择合适的模型,考虑到根据商业案例来评估基础算法的成本效益和效率。
本书涵盖内容
第一章,数学建模简介,介绍了数学建模的理论与概念,以及数学模型在不同领域的主导地位和应用。
第二章,机器学习与数学建模的关系,通过示例描述了机器学习模型如何作为预测工具,而经典数学模型则作为规定性工具。
第三章,主成分分析,提供了一种减少高维数据维度的方法,以及在需要降维的情况下的应用示例。
第四章,梯度下降,介绍了一种为机器学习模型奠定基础的算法。梯度下降方法的变种用于训练机器学习以及深度学习模型。
第五章,支持向量机,提供了一个主要用于数据分类的算法的数学细节。
第六章,图论,提供了一种定量或建模网络中相互连接实体关系的理论。
第七章,卡尔曼滤波器,是一个在动态系统的测量不精确和不确定的情况下进行状态估计和预测的算法。
第八章,马尔可夫链,提供了建模随机过程的理论。马尔可夫链是一类概率模型,它根据当前状态来确定下一个未来状态。
第九章,探索优化技术,介绍了机器学习模型中使用的优化算法以及运筹学中使用的优化算法。它还通过示例介绍了进化算法。
第十章,机器学习的优化技术,提供了确定选择哪个算法来优化适应数据集的机器学习模型的方法。
如何最大化利用本书
您需要 Python 3.0 或更高版本来运行各章节中的代码。执行特定方法所需的 Python 库已在代码中导入(与 Python 3.0 兼容的版本),并且可以在您的系统的笔记本或 Python 环境中轻松安装。
| 本书涵盖的软硬件 | 操作系统要求 |
|---|---|
| Python 3.0 或更高版本 | Windows、macOS 或 Linux |
| Python 库 | Windows、macOS 或 Linux |
如果您正在使用本书的数字版,建议您自己输入代码或访问本书的 GitHub 仓库中的代码(下一个章节中会提供链接)。这样做可以帮助您避免任何因复制粘贴代码而可能出现的错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件,地址是github.com/PacktPublishing/A-Handbook-of-Mathematical-Models-with-Python。如果代码有更新,GitHub 仓库中的代码将会同步更新。
我们还提供了来自我们丰富的书籍和视频目录中的其他代码包,您可以在github.com/PacktPublishing/找到。快去查看吧!
使用的约定
本书中使用了多种文本约定。
文本中的代码:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。举个例子:“scikit-learn Python 库中的超参数优化方法假设较好的性能得分是接近零的负值。”
代码块设置如下:
import pandas as pd, numpy as np
from collections import Counter
import matplotlib.pyplot as plt
当我们希望你注意到代码块中的特定部分时,相关的行或项会以粗体显示:
prediction = one_class_svm.predict(X_test)
prediction = [1 if i == -1 else 0 for i in prediction] #outliers denoted by 1, inliers by 0
print(classification_report(y_test, prediction))
提示或重要说明
如此显示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何内容有疑问,请发送电子邮件至 customercare@packtpub.com,并在邮件主题中提到书名。
勘误:尽管我们已尽力确保内容的准确性,但难免会出现错误。如果你在本书中发现错误,恳请告知我们。请访问www.packtpub.com/support/errata并填写表格。
盗版:如果你在互联网上遇到任何我们作品的非法复制品,恳请提供该地址或网站名称。请通过 mailto:copyright@packtpub.com 与我们联系,并附上相关资料链接。
如果你有兴趣成为作者:如果你在某个领域有专业知识,并且有意写书或参与书籍创作,请访问authors.packtpub.com。
分享你的想法
阅读完《使用 Python 的数学模型手册》后,我们很想听听你的想法!请点击此处直接前往 Amazon 书评页面并分享你的反馈。
你的评价对我们和技术社区至关重要,能帮助我们确保提供优质的内容。
下载本书的免费 PDF 副本
感谢你购买这本书!
你喜欢随时随地阅读,但又不能随身携带印刷书籍吗?
你的电子书购买是否无法兼容你选择的设备?
不用担心,现在每本 Packt 书籍都会免费附赠一份无 DRM 保护的 PDF 版本。
在任何地方、任何设备上阅读。可以直接将你最喜爱的技术书籍中的代码搜索、复制并粘贴到你的应用程序中。
优惠不仅仅如此,你还可以独家获得折扣、新闻通讯以及每天发送到你邮箱的精彩免费内容。
按照以下简单步骤获取福利:
- 扫描二维码或访问以下链接

https://packt.link/free-ebook/9781804616703
-
提交你的购买证明
-
就这些!我们将直接通过电子邮件将免费的 PDF 及其他福利发送给你。
第一部分:数学建模
在这一部分,你将了解数学建模背后的理论。你将学习数学模型的概念,以及它们在解决商业问题中的应用。数学模型在很大程度上依赖于领域知识、将商业案例转化为数学问题的目标,以及上下文中的约束条件,而机器学习(统计)模型则依赖于数据。数学建模与机器学习是互补的;对于一些应用场景,单独使用其中之一就足够了,而其他一些场景则需要将二者结合使用。
本部分包括以下章节:
-
第一章,数学建模简介
-
第二章,机器学习与数学建模
第一章:数学建模简介
在数据科学领域,特别是在机器学习(ML)和深度学习(DL)的领域,正在进行大量有趣的工作,并且这些方法之所以受到广泛关注是有原因的。然而,经过验证的“老派”数学建模方法却鲜少被提及。数学建模方法同样具有重要意义,并且与机器学习互为补充。为了创造能够解决实际商业问题的成功数据产品,我们往往需要部署一整套可用的数学工具,远远超出机器学习的范畴。
模型是现实系统的简化表示,旨在捕捉系统的本质。数学模型使用变量、运算符、函数、方程和等式。在数学模型的背后,有基于物理定律的第一性原理模型,基于分布、平均值的随机模型,以及基于模式或历史数据的经验模型。根据建模的具体类型,可以为所考虑的系统提供定性或定量的建议。数学模型有助于设计和原型开发,并为决策提供依据。为了建立数学模型,需要输入和输出、常数和变量、领域和边界或初始条件和约束。解决方案可以是解析的,也可以是数值的;无论是哪种情况,都能确定系统的典型行为和关键参数、趋势、依赖关系以及操作模式。系统可以是确定性的,我们知道因果关系,或者是随机的,涉及概率分布。
数学建模中的一些成熟工具涵盖以下领域:
-
数学优化
-
信号处理
-
控制理论
我们将在接下来的章节中探讨这些数学建模方法。仅专注于机器学习(ML)会忽略许多纯数学优化在许多应用场景中的相关特征。跨多个领域的成功解决方案将机器学习的现代方法与经典的数学建模技术相结合。例如,可以将状态空间建模方法与机器学习结合,推断参数估计问题中系统的未观测参数。
数学优化
应用数学的一个分支是数学优化,通常被称为数学编程。它广泛应用于制造、库存控制、调度、网络、经济学、工程和金融组合配置等领域。几乎任何分类、回归或聚类问题都可以转化为优化问题。一些问题是静态的,而另一些则是动态的,其中系统变量的值随时间变化。
理解问题
数学优化基本上是从一组允许的选项中选择输入,以便在给定的问题中获得优化或最佳可能的输出。变量本质上是我们必须做出的决策;约束是我们必须遵守的业务规则;目标是通过将现实世界的业务问题表示为优化问题来实现的业务目标。例如,医院的业务问题是设备和设施容量规划。包括床位和检测工具在内的医疗设备构成了此案例中的决策变量;约束是常规和危机容量水平及规定;最终,目标是最大化资源利用率和服务表现,并同时最小化运营成本。
最基本的优化问题由一个目标函数或成本函数组成,它是我们尝试优化的输出值,换句话说,最大化或最小化。输入是可以控制的变量,变量可以是离散的或连续的。问题的规模通常由其维度决定,即搜索所涉及的标量变量数量(也称为决策变量)。约束或方程对某些变量的最大值或最小值进行限制。一些问题有约束,这些约束可以是等式或不等式约束,而有些问题完全没有约束,这意味着函数的优化没有边界。
线性规划问题是一个优化问题,其中目标函数和所有约束都是线性的,即变量只有一阶项。正是线性规划推动了 20 世纪 40 年代优化学科的发展。如果函数或一个或多个约束是非线性的,那么我们就有了非线性规划问题。例如,优化平滑(良好定义的梯度,连续)函数相对较容易。了解问题的类型有助于选择正确的工具来解决它。
问题的表述
数学问题的一般表述,具有目标函数f(x),通过变量和约束来表达问题。一个典型的形式如下:
最小化 f (
,使得
,其中 i = 1, 2, ...., m
变量和约束的性质可能非常多样。变量可以是离散的、连续的或集合(组);约束可以是确定性的或随机的。目标函数也可能包含动态方面。
有时我们感兴趣的是在没有任何约束或限制的情况下找到全局最优点。这类问题是无约束优化问题。而在其他情况下,我们需要解决有特定约束的问题,比如对控制变量的限制。例如,在上述情况下,我们可能需要最小化目标函数,同时满足(
)。这些是有约束的优化问题。
示例 1:
假设我们有多个(不等式)约束,涉及两个变量,x 和 y,如下所示:
2x + 3y ≤ 34
3x + 5y ≤ 54
0 ≤ x, y
图形优化将是图形的重叠(暗区),如 图 1.1 所示。这里的约束是线性的,因此最大值和最小值必须位于边界上。而最优解很可能出现在三个指定的点之一。对于非线性约束,最优解出现在边界上或边界之间。在无约束优化中,要么函数没有边界,要么如果有边界,它们是软约束。

图 1.1:线性约束优化的图形表示
商业问题中的典型约束包括时间、金钱和资源,同时试图最大化目标函数。约束更加针对特定的用例,同时最小化目标函数。假设在上述问题中,目标函数是线性的,如 f(x, y) = 20x + 35y,并且最优解是通过函数的斜率得出的。如果 f(x, y) 取得一个值,这个值就成为边界,约束和边界结合形成线性约束。
对于线性约束,重叠区域被认为是可行的。非线性约束则非常难以可视化,因为扭曲的 x-y 平面几乎使得绘制可行区域变得不可能。
示例 2:
在非线性约束优化中,第一步是从可行区域的边界开始。为了最小化目标函数,应该选择一个向量方向,使得它能够降低函数值并保持在可行区域内。如果目标函数的梯度(斜率)与该向量在边界上的点的点积为负,则该向量被认为是朝着下降方向移动的。另外,一个不违反约束的向量被认为是朝着可行方向移动的。

图 1.2:非线性约束优化中的可行方向
边界上的约束方程是 g(x) = 0,如 图 1.2 所示。一个可行向量不能使 g(x) 的值增加。它必须保持为零或减小。如果约束的梯度与该向量本身的点积在该点为负或零,则可以认为该向量沿着可行方向移动。例如,假设我们有以下目标函数:
最小化 
以及初始点(4, 2
在单一约束条件下:

其中
和
通常是代表矩阵或数组的变量。向量 <-1, 0> 既是下降方向也是可行方向。由于初始点是随机选择的,因此所有可行向量集合和所有下降向量集合的重叠可能较大。然而,随着我们接近最小值,重叠会变小,并且在最小值或最优点时,二者完全没有重叠。在最优点处,如果不违反约束,目标函数就无法进一步最小化。当两个梯度的点积为负,并且这两个向量的矩阵行列式等于零时,我们就知道达到了最优解。
另一种可能性是最优解发生在可行区域的内部,而不是边界上。在这种情况下,目标函数的梯度在该点将为零。该点的凹性(非凸性)由函数的 Hessian 矩阵(第二阶导数)的特征值决定。
在目标函数存在噪声或其梯度是通过数值计算得出的情况下(例如,复杂的边值问题),会引入误差。即使目标函数本身没有噪声,基于梯度的优化方法也可能变得有噪声。对于这类优化问题,Scientific Python 库中有不同的优化器,简称scipy,我们将在接下来的章节中了解其中的一些。现在我们已经了解了数学优化的概念,接下来将探讨数学建模中的另一个概念——信号处理。
信号处理
应用数学的另一个分支是信号处理,它在工程领域中得到了应用,专注于分析和处理信号,如声音、图像、科学测量,并去除噪声。信号处理涉及将信号从时间序列转换为超光谱图像,这些图像是通过不同的电磁测量得到的。经典的信号转换,如频谱图和小波,常与机器学习技术一起使用。这些表示也可以作为深度神经网络的输入。卡尔曼滤波器是一个经典的信号处理滤波器,通过一系列时间测量来产生未知变量的估计。
理解问题
信号是一个连续变量的函数,例如时间或空间。通过在规定的时间间隔(称为采样周期)对模拟信号进行采样,可以将模拟信号转换为数字信号,采样周期的倒数就是采样率(每秒或赫兹)。采样率必须至少是模拟信号最大频率的两倍。这建立了一个充分的条件,允许通过离散的采样序列将所有连续时间信号的信息封装到离散时间信号中。

图 1.3:在信号的时域和频域中的 60 kHz 正弦波(汉宁窗)脉冲
信号的频域表示通过使用 scipy 计算每一帧的频率来实现。通常在每一帧上应用一种叫做短时傅里叶变换(STFT)的傅里叶变换。
问题的表述
很明显,离散时间信号处理(DSP)是用于采样信号的,并为 DSP 建立了数学基础,本质上是分析和修改信号,以提高(或优化)其效率或性能。通过使用离散傅里叶变换(DFT),离散序列可以表示为其等效的频域‘
”。傅里叶变换的线性性质产生了两个信号,
和
:

其中
和
分别是
和
的傅里叶变换,这是信号过滤中常用的概念,指的是将时间
域转化为频率
域。傅里叶变换的对偶性质非常有用,因为它能够解决一些复杂的问题,这些问题如果直接计算会很困难。它的结果是,如果 x
有傅里叶变换
,那么可以形成一个新的时间函数
,其形式与该变换的功能形式相同,例如:

时间的偏移会影响频率,而频率的偏移则会影响函数的时间。让我们以一个频谱图为例来理解数字信号处理(DSP)。
频谱图显示了波形随时间变化的频率谱,并广泛应用于音乐和语音处理以及雷达领域。它是通过光学光谱仪、傅里叶变换或小波变换生成的,通常以热图的形式呈现,其中信号的强度或强度随着颜色(亮度)的变化而变化。为了生成频谱图,时域信号被分割成等长且通常有重叠的块,并对每个块应用快速傅里叶变换(FFT)来计算频率范围。频谱图是每个段或 FFT 框架上频谱的图表或图像,表现为频率与时间的关系(或 3D 表面),如图 1.4所示,第三维(由颜色条表示)指示特定时间点特定频率的幅度。此过程对应于信号的短时傅里叶变换(STFT)的平方幅度计算。

图 1.4: 频谱图
频谱图可以用来识别非平稳或非线性信号的特征,作为一系列时间频率分析。频谱图中的参数通常包括帧数(构成它的 FFT 数量)、频率范围(最小值和最大值)、FFT 间距和 FFT 宽度(每个 FFT 表示的时间宽度)。
频谱图与循环神经网络(RNNs)一起应用于语音识别,这是一个主要的例子。我们在这一小节中了解了数字信号在噪声上的免疫性(好吧,几乎)以及更少的失真,接下来我们将探索控制理论,这是一种广泛应用于工业过程中的数学建模技术。控制理论通常在反馈发生的情况下有用,无论是在调节器还是伺服机制中,例如导航系统和工业生产过程。
控制理论
控制理论是数学和工程学的一个分支,也在社会科学中得到了应用,例如经济学和心理学。它处理动态系统的行为或演化。尤其在系统的动态不是任意的,即我们理解系统的物理特性时,它非常有用。控制的目标是从测量数据中开发模型。这个模型是应用于驱动系统达到期望状态的输入的数学描述,同时最小化任何延迟或误差,并确保一定的稳定性。
动态系统的行为受反馈回路的影响——控制器通过操控系统输入来获得期望的输出效果。误差控制调节通常通过比例-积分-微分(PID)控制器来实现,顾名思义,信号是通过误差信号的加权和、积分和微分来得出的。误差是实际输出与期望输出之间的差异,作为反馈作用于输入。系统的标准术语是过程,控制变量的术语是过程变量(PV),其目标是减少偏差误差。通过负反馈回路,PV 的测量值(图 1.5 中的 E)从期望值 S(设定点或 SP)中减去,估算误差(SP 减去 PV),该误差被调节器 R(图 1.5)用来缩小测量值与期望值之间的差距。误差可能会作为干扰 D 引入系统 T,如控制器的闭环(图 1.5)所示。

图 1.5:负反馈控制器
控制理论可以是线性的,也可以是非线性的。线性控制理论适用于遵循叠加原理的设备,意味着输出大致与输入成比例。这类(接近理想的)系统可以通过频域数学方法进行处理,如拉普拉斯变换、傅里叶变换和奈奎斯特稳定性准则。另一方面,非线性控制理论适用于不遵循叠加原理的真实世界系统。这些系统通常由非线性微分方程控制,并通过数值方法进行分析。非线性系统通常通过仿真操作进行数值研究,使用的仿真语言模拟系统过程。然而,如果仅对稳定点或平衡点附近的解感兴趣,可以通过扰动技术将非线性控制系统线性化为近似值。
理解问题
数学技术用于频域或时域中分析控制系统。在频域中,表示系统输入、输出和反馈的状态变量是频率的函数。传递函数、系统函数或网络函数是描述输入与输出之间关系的数学模型,基于描述或支配系统的微分方程。输入和传递函数通过数学变换从时间函数转化为频率函数。在该领域,微分方程被代数方程替代,后者更容易求解。时域中的状态变量是时间的函数,系统通过一个或多个微分方程来描述。
时域技术用于探索和分析实际的非线性系统,因为频域技术仅能用于研究(理想的)线性系统。尽管非线性系统的方程难以求解,但计算机仿真方法使其分析变得常见。控制回路的一个关键应用是在工业过程控制系统设计中,如图 1.6 所示。

图 1.6:工业控制显示持续调节的过程流
工业过程的基础是控制回路,它由所有元素组成,用于在扰动存在的情况下测量并控制过程值,以达到期望的设定点(SP)。控制器可以是一个独立的硬件单元,或者在大型分布式控制系统中,是一个可编程逻辑控制器(PLC)系统,设定点输入可以手动设置或从其他来源级联。图 1.6 中的绿色文本是描述功能并标识组件的标签,这些标签在工厂中是唯一的(字符串),表示设备组件或元素。相关的传感器本质上捕捉这些标签的数据。
问题的公式化
现代控制理论利用状态空间方法(时域表示),不同于经典控制理论使用变换方法(频域表示),如拉普拉斯变换,它编码了所有的系统信息。在状态空间方法中,数学模型是一组一阶微分方程,支配着系统的相关输入、输出和状态变量。这些变量以向量的形式表示,微分方程采用矩阵格式,更便于解决。相反,表示线性动态系统行为的代数方程是以矩阵形式书写的。
状态空间方法不限于线性系统,提供了一种方便且紧凑的方式来建模和分析大多数具有多个输入和输出的非线性系统。状态空间是指其坐标轴为状态变量的空间,系统状态以向量形式表示在该空间内。
工厂或过程是被控制的系统部分,控制器(或简单的过滤器)构成其余部分。输入到过程的信号会影响输出,效果通过传感器测量并由控制器处理。控制信号反馈到输入,从而闭合回路。这样的典型架构是 PID 控制器,通常是最常用的工业设计,如图 1.7所示。它持续计算误差值e(t),误差是期望的 SP 和测量的 PV 之间的差异,并基于比例、积分和微分项进行修正。

图 1.7:u(t)是发送到系统的控制信号,e(t) = r(t) – y(t)是误差
当多个控制器监控一个过程时,它就成为一个具有分散控制回路的分布式控制系统。分散化是有益的,因为它有助于控制系统在广泛的区域内操作,同时通过通信渠道进行交互。
工业中广泛使用的一些主要控制技术包括自适应控制、层次控制、最优控制、鲁棒控制和随机控制。除此之外,智能控制利用人工智能(AI)和机器学习(ML)方法,如模糊逻辑、神经网络等,来控制动态系统。工业 4.0 正在彻底改变制造商将 AI 整合到其运营和生产设施中的方式。
总结
本章介绍了通过其广泛应用的主要领域,如优化、信号处理、控制系统和控制工程,引入数学建模的概念。数学建模或数学规划是将问题转化为清晰的数学公式的艺术。随后的算法实现生成可操作的洞察力,并帮助构建更多关于该领域的知识。
本章帮助我们学习了数学优化问题的公式化,以便得出最优解,公式化取决于我们打算研究的领域。数学优化模型就像现实世界商业场景的数字化双胞胎。它在严格的数学和编程设置中反映了商业景观,这种环境对于支持高风险决策的业务过程可解释性尤为重要。
在下一章中,我们将了解数学模型如何强调数据和领域知识的重要性。此外,我们还将学习如何将机器学习模型视为优化问题。
第二章:机器学习与数学建模
在上一章学习了数学优化的主要组成部分:决策变量、目标函数和约束条件后,现在是时候探讨机器学习(ML)模型了,其中大多数可以被看作是数学模型。人类通过大量的历史数据让机器学习。机器学习模型增强了人类和机器的决策能力,充分利用数据和算法的力量。几乎所有这些模型背后都在运行某种优化算法。
机器学习(ML)这一术语首次由计算机科学和游戏领域的先驱 Arthur L. Samuel 在 20 世纪 50 年代普及。自那时以来,数据量飞速增长,尤其是在过去几十年中,处理大量数据已经超出了人类大脑的能力范围。因此,机器学习介入并找到了在几乎所有领域的应用,帮助人类做出决策。
数据科学中的学习问题可以根据业务问题或用例大致分为回归、分类和聚类。回归和分类使用监督学习算法预测目标变量,通常称为因变量,而独立变量则称为预测变量。聚类则使用无监督学习算法,其中目标是未知的。值得一提的是,并非所有机器学习算法中的学习都涉及优化,例如k-最近邻(kNN)就是一个监督学习的例子。机器学习主要是一个预测工具,帮助企业为未来做好规划,从而有利于其利润。企业还利用机器学习进行异常(或离群点)检测和推荐系统。严格的数学建模则帮助企业在电力分配、员工调度和库存管理等领域做出决策。
一些在机器学习模型中使用的约束优化算法如下:
-
主成分分析(PCA)
-
使用期望最大化算法进行聚类(例如高斯混合模型)
-
使用拉格朗日乘子法的支持向量机
其他使用无约束优化的机器学习算法包括神经网络中的随机梯度下降(SGD)和深度学习中的批量梯度下降(神经网络具有大量的隐藏层,位于输入和输出之间)。除了这些,还有进化学习中的遗传算法,这些算法涵盖了约束和无约束优化问题。
机器学习的主要组成部分包括表示、评估和优化。表示是指通过统计地呈现知识和历史数据,以寻找模式,换句话说,就是将一个业务问题表述出来,从而得出或估算解决方案。接下来是对这一表述的评估,我们称之为模型,并将数据与已知的示例或数据样本进行拟合和比较。最后,模型背后的算法优化其权重和偏差,以更好地拟合数据,优化过程会迭代进行,直到达到所需的精度为止。在接下来的章节中,我们将学习主成分分析(PCA)和梯度下降法。
本章涵盖以下主题:
-
机器学习作为数学优化问题
-
机器学习作为一种预测工具
-
数学建模作为一种规范性工具
机器学习作为数学优化
机器学习可以描述为寻找将输入示例映射到输出示例的未知潜在(近似)函数。在这一过程中,机器学习算法定义了一个参数化的映射函数,并通过优化或最小化函数中的误差来求解其参数的值。机器学习就是函数近似与函数优化的结合。函数参数也称为模型系数。每次我们将模型拟合到训练数据集时,都在解决一个优化问题。
每种机器学习算法对映射函数的形式做出不同的假设,这进而影响要执行的优化类型。机器学习是一种函数近似方法,旨在最优地拟合输入数据。当数据(样本大小或样本数量)有限时,特别具有挑战性。必须选择一个机器学习算法,以最有效的方式解决优化问题;例如,神经网络使用随机梯度下降(SGD),而普通最小二乘法和梯度下降用于线性回归。当我们偏离默认算法时,必须有充分的理由这么做。在数学优化中,有时可能会使用启发式方法来确定近似最优解。这发生在经典算法过于缓慢,无法找到近似解,或者无法找到优化问题的精确解时。启发式算法的例子包括遗传算法和模拟退火算法。
示例 1 – 回归
一个机器学习问题可以表述为:给定输入数据(X)和输出数据(Y),学习一个映射函数(f),使得 Y = f(X)。给定新的输入数据,我们应该能够通过已学习的函数 f 将每个数据映射到(或预测)输出。通常,由于观察数据中的噪声和选择的学习算法在逼近映射函数时的误差,预测误差是不可避免的。寻找能够最小化误差、代价或损失的输入集合,本质上就是在解决优化问题。映射函数的选择决定了优化的难度。选择越偏向或约束越强,优化越容易。
例如,线性回归是一个受约束的模型。通过线性代数,可以解析求解它。在这种情况下,映射函数的输入是模型系数。优化算法,如迭代局部搜索,可以用数值方法求解,但几乎总是比解析解法效率低。逻辑回归(用于分类任务)是一个约束较少的模型,在这种情况下需要使用优化算法。这里的损失或误差也称为逻辑损失或交叉熵。虽然在这两种回归模型中都可以使用全局搜索优化算法,但它通常不如使用解析方法或局部搜索方法高效。当搜索空间或地形是多模态和非线性时,适合使用迭代全局搜索(如梯度下降),正如图 2.1所示。

图 2.1:无约束优化空间的 3D 地形图,其中 A 是当前状态
示例 2 – 神经网络
神经网络是一个灵活的模型,约束非常少。网络通常包含一个输入层、一个隐藏层(可以有多个)和一个输出层节点,映射函数的输入通过加权连接到输入层,如图 2.2所示。正是这个映射函数,监督学习算法试图最优地逼近。

图 2.2:网络中的三个基本最小层
预测输出与期望输出的偏差就是误差值,而这个误差或代价,如图 2.3所示,在模型训练过程中被最小化,同时逼近该函数。神经网络需要一个迭代的全局搜索算法。梯度下降是优化神经网络的首选方法,神经网络有多个变种,即批量梯度下降、迷你批量梯度下降和随机梯度下降(SGD)。其中最受欢迎的 SGD 算法之一是自适应矩估计(Adam),它为每个参数计算自适应学习率。

图 2.3:通过梯度下降最小化成本函数 J(w),其中 w 是输入(来自《Python 机器学习》Sebastian Raschka 的贡献)
梯度是函数对输入变量值的偏导数(斜率/曲率)所组成的向量。顾名思义,梯度下降算法需要计算该梯度。每个输入的梯度的负值沿着梯度指向上坡的方向向下走,从而得出新的输入值。步长用于缩放梯度,并控制输入相对于梯度的变化。这个步长或增量就是学习率,是算法的超参数,也是更新网络权重的比例。这个过程会一直重复,直到找到函数的最小值。梯度下降算法被用于最小化预测模型的损失函数,如回归或分类。这个适应过程产生了 SGD,如图 2.4所示。

图 2.4:梯度下降扩展
SGD 是梯度下降优化算法的扩展,其中目标函数被视为损失或误差,例如回归中的均方误差和分类中的交叉熵。由于目标函数相对于输入的梯度是噪声性的,且仅能通过概率近似确定,算法因此被称为“随机”。由于训练数据中的稀疏性和噪声,评估出的梯度带有统计噪声。一般而言,SGD 及其变种仍然是机器学习和深度学习(人工神经网络)模型训练中最常用的优化算法。神经网络的输入是权重(模型参数),目标函数是针对一个批次的预测误差的平均值,而这个批次是训练数据集的一个子集。
为了提高过程效率(例如在更少的迭代中找到相同或更好的损失),SGD 的一个流行扩展是 Adam。Adam 优化方法在计算上高效,内存占用少,非常适合处理规模大、特征多的问题。Adam 的配置参数包括学习率(步长)、用于均值(第一矩)估计的指数衰减率(表示为 beta 1)、用于方差(第二矩)估计的指数衰减率(表示为 beta 2),以及防止除零错误的非常小的数值 epsilon。较大的学习率(表示为 alpha)会导致在更新之前更快的初始学习,而较小的学习率则意味着在整个训练过程中较慢的学习。这些参数通常需要很少的调节,因为它们有直观的解释。
使用 SGD 训练多层神经网络的一个主要挑战是计算网络隐藏层节点的梯度。这个问题可以通过利用微积分中的链式法则来解决,一个实现该法则的高效算法叫做反向传播,它计算损失函数对模型变量的梯度。一个特定输入变量值的函数的一级导数是该函数随着该变量的变化率,当有多个输入变量时,(偏)导数形成一个向量。对于网络中的每个权重,反向传播计算出梯度,然后 SGD 优化算法使用这些梯度来更新权重。反向传播从网络的输出向输入方向传播,如图 2.5所示。它将预测输出中的误差传播回去,以计算每个输入变量的梯度,基本上是信息从代价函数通过网络向后流动。反向传播涉及链式法则的递归应用,即在已知父函数导数的情况下,计算子函数的导数。

图 2.5:神经网络中的反向传播
遗传算法不利用模型的结构,这意味着它不需要梯度。对于使用神经网络模型的问题,我们需要使用反向传播计算的梯度来优化模型。可以公平地说,反向传播是优化过程的一部分,而优化算法是 SGD。
现在我们已经探讨了将回归、分类和神经网络等机器学习任务视为数学优化问题的方式,接下来我们将学习机器学习作为一种预测建模工具,并了解它在一些重要领域中的应用。
机器学习——一种预测工具
通过预测模型的工作过程涉及到多个步骤的优化,除去将学习算法最佳拟合数据之外,还包括将原始数据转换为最适合在学习算法中使用的形式。机器学习模型具有可以配置的超参数,以便根据特定数据集进行调整。对于所选择的机器学习算法,测试一组超参数是标准做法,这被称为超参数调优或优化。网格搜索或随机搜索算法通常用于这种调优过程。图 2.6展示了这两种搜索算法类型。网格搜索更适合快速搜索超参数,并且通常表现良好。你也可以在某些问题中使用贝叶斯优化进行超参数调优。我们将在本书的最后部分详细学习这些优化技术。

图 2.6:网格搜索(左)与随机搜索(右)
机器学习实践者通常会执行一个手动过程来进行预测模型选择,这涉及到数据准备、模型评估、调整模型参数,最后为给定数据集选择最佳模型。这可以被看作是一个优化问题,可以通过自动化机器学习(AutoML)来解决,几乎不需要用户干预。自动化优化方法在机器学习中的应用,也被像谷歌和微软这样的公司提供为云产品服务。
无论输入数据集中是否包含目标变量,机器学习算法分别会成为监督学习或无监督学习。在强化学习中,某些行为会受到鼓励,而其他行为则会被抑制。通过从环境中获得的经验,奖励强化了期望的行为。这三种机器学习类型如图 2.7所示。

图 2.7:三种机器学习类型——监督学习、无监督学习与强化学习
接下来,我们将讨论机器学习模型在一些主要领域中如何安全地作为预测工具占据一席之地。
电子商务
机器学习模型帮助零售商了解顾客的购买行为及其偏好。通过分析顾客的历史购买模式和产品的点击率,电子商务公司能够有效地推荐产品并提供优惠,以最大化销售额。个性化推荐帮助零售商保持顾客基础,从而创造忠诚度。以下链接概述了机器学习在电子商务行业中的具体应用:
blog.shift4shop.com/machine-learning-ecommerce-industry
销售与市场营销
ML 模型也被用于 B2B 营销。通过客户细分,识别并获取与现有企业特征相似的潜在客户是一个应用案例。通过潜在客户评分算法,优先考虑已知的潜在客户,并根据客户采取行动的可能性生成新的线索。公司可以通过数据驱动和算法驱动来简化销售和营销活动。以下是一些机器学习驱动的销售和营销改进方式:
scinapse.ai/blog/11-ways-machine-learning-can-improve-marketing-and
网络安全
网络攻击可能随时袭击组织并造成严重损害;然而,借助机器学习(ML)算法,它们可以被预测和防止。通过在短时间内处理结构化和非结构化数据,可以实时分析流量,追踪异常或不寻常的模式。公司通过分析数据中的这些离群点来抵御攻击。这也减少了来自手动处理大量数据的人为错误的范围,使人类能够集中精力制定保护系统免受网络攻击的策略。以下由卡巴斯基指出的数据驱动方法值得研究:
www.kaspersky.com/enterprise-security/wiki-section/products/machine-learning-in-cybersecurity
在探讨了机器学习如何作为预测建模工具在行业中应用后,我们将在下一节中学习数学建模如何作为规定性工具在不同领域的应用。
数学建模——一种规定性工具
企业通常通过数学建模或启发式方法做出关于行动方针的复杂决策,以实现目标。从这个意义上说,数学模型是一种规定性分析工具。回答“在哪里”和“何时”与回答过去发生了什么(描述性分析)和未来可能发生什么(预测性分析)同样重要。如果企业希望从数据中推动决策,并且除了洞察和未来预测之外,还需要使用预测性和规定性工具进行综合应用。

图 2.8:数学优化或数学建模
我们将查看一些行业垂直领域的例子,在这些领域,机器学习和其他方法协同工作,带来了更高的生产力和盈利能力。
金融
包括银行在内的金融服务行业依赖机器学习模型以及数学模型来确定投资组合的正确分配。以时间序列预测形式呈现的机器学习模型帮助预测资产表现,进而将预测结果输入到应用程序中,应用数学模型。根据市场变动和预测,数学优化应用决定最优的分配方式。最佳的投资组合分配还会考虑个人的投资目标和偏好。这些有助于降低风险并最大化风险调整后的投资回报。
零售
领先的零售商利用机器学习模型预测产品的需求,特别是在特定地点和时间的畅销产品。他们将这些预测输入数学模型,以最大化利润和客户满意度。在这种情况下,数学优化应用将预测结果作为输入,生成最优的生产、定价、库存和分销计划、物流和仓储,从而在最小化运营成本的同时做出最佳商业决策。供应链管理是数学优化的经典示例。
能源
政府和行业参与者在网络基础设施和资源的战略投资决策上扮演着关键角色,尤其是随着电力从依赖化石燃料转向太阳能和风能等可再生能源。各组织利用机器学习模型预测未来的电力需求和容量需求。这些预测被输入到数学模型或数学优化应用中,生成最优的长期投资规划,并帮助做出战略投资决策。
数字广告
像 Google 这样的搜索引擎巨头利用机器学习(ML)和深度学习模型,根据用户的历史搜索记录以及其他一些因素,预测个人可能感兴趣的产品和服务。此外,他们还利用数学模型来确定在特定时间可以展示给单个用户的在线广告。搜索引擎巨头使用这种优化模型来向广告商收费并最大化收入。
这些领域将数学建模加入了他们的数据科学工具箱,以处理复杂、重要且可扩展的商业问题,从而提供更大的价值。其他行业,如电信和云计算,也使用这些模型来精确评估长期需求和容量需求,以做出最佳商业决策。
总结
在本章中,我们将机器学习模型介绍为数学优化或数学规划的问题。我们发现,端到端的机器学习项目实际上是多个小的优化问题的总和。我们还了解到,企业如何通过利用数学模型(主要由数学方程驱动)与机器学习(由数据驱动)模型的结合,解锁数据的真正价值。我们学习到,机器学习模型主要是预测工具,而数学模型则是指导性工具。
在下一章(即本书下一个部分的开始),我们将详细探讨一个著名算法,叫做 PCA,它用于无监督机器学习模型中,以适应高维数据。PCA 是一种降维技术,是一种经过验证的数学工具,采用约束优化方法。
第二部分:数学工具
在这一部分,你将学习一些经过验证的数学工具和算法。一方面,涉及数据降维、机器学习模型优化和数据分类的算法,这些内容通过 Python 代码进行探索。另一方面,涉及建模对象(数据点)之间关系的算法,以及估计动态系统中变量(未知和无法测量的)当前和未来状态的算法。还有一些算法从当前状态的知识出发,概率性地预测下一状态,且通过简单的例子和 Python 代码进行解释。
本部分包含以下章节:
-
第三章,主成分分析
-
第四章,梯度下降法
-
第五章,支持向量机
-
第六章,图论
-
第七章,卡尔曼滤波器
-
第八章,马尔可夫链
第三章:主成分分析
一个广为人知的算法,用于从高维数据中提取特征,供机器学习(ML)模型使用,便是主成分分析(PCA)。用数学术语来说,维度是指指定空间中一个向量所需的最小坐标数。要在高维空间中找到两个向量之间的距离,需要大量的计算能力,在这种情况下,维度被视为一种诅咒。维度的增加会在一定程度上提升算法的性能,但超过这个范围后,性能将下降。这就是维度诅咒,正如图 3.1所示,它妨碍了大多数 ML 算法效率的提升。数据中的变量列或特征代表空间的维度,而行代表该空间中的坐标。随着数据维度的增加,稀疏性增加,并且计算距离和密度所需的计算量呈指数增长。从理论上讲,维度的增加实际上会增加大数据集中的噪声和冗余。可以说,PCA 是应对高维问题中维度复杂性最流行的技术。

图 3.1:维度诅咒
PCA 源自线性代数领域,本质上是一种数据准备方法,在将 ML 模型拟合到新创建的低维数据集之前,先将数据投影到一个子空间。PCA 是一种数据投影技术,能有效地可视化高维数据并改善数据分类。它是在 1900 年代根据主轴定理发明的。PCA 的主要目标是为数据找到一个正交归一基,按重要性或方差的顺序排序维度,丢弃不重要的维度,并只关注不相关的高斯成分。
本章涵盖以下内容:
-
PCA 的线性代数
-
线性判别分析 – 与 PCA 的区别
-
PCA 的应用
接下来的部分将讨论线性代数,这是 PCA 所基于的数学学科。
PCA 的线性代数
PCA 是一种无监督方法,用于减少高维数据集的特征数量。通过矩阵因式分解(或分解),然后根据方差对这些部分进行排序,将一个未标记的数据集缩减为其组成部分。表示原始数据的投影数据将成为训练 ML 模型的输入。
PCA 被定义为数据向下投影到一个称为主子空间的低维线性空间,这一过程是通过找到新的坐标轴(或基向量)来实现的,这些新坐标轴最大程度地保留了投影数据的方差;这些新坐标轴或向量被称为主成分。PCA 通过考虑投影向量的方差来保持信息:最大方差位于第一坐标轴,第二大方差位于第二坐标轴,依此类推。线性变换的工作原理,即 PCA,如图 3.2 所示。它通过识别一个子空间,捕捉完整特征矩阵的本质,从而压缩了特征空间。

图 3.2:PCA 工作原理
除了 PCA,还有其他减少数据维度的方法,如特征选择方法(包装法和过滤法)、非线性方法如流形学习(t-SNE),以及深度学习(自编码器)网络;然而,最广泛且最流行的探索性方法是 PCA。通常,线性代数方法假设所有输入具有相同的分布,因此,如果输入特征具有不同的单位,使用 PCA 之前最好对数据进行归一化或标准化处理。
协方差矩阵 – 特征值和特征向量
PCA 的约束是所有主轴应互相正交。数据的协方差是衡量数据中任意一对特征之间变化程度的度量。协方差矩阵检查数据中特征之间的相关性,这些关系的方向取决于协方差是小于、等于还是大于零。图 3.3 显示了协方差矩阵的公式。矩阵中的每个元素表示数据中两个特征之间的相关性,其中j 和 k 遍历p个变量,N 是观察值的数量(行数),公式中的
条和
条表示期望值(均值)。

图 3.3:协方差矩阵
PCA 的目标是通过比原始数据集中的变量或特征少得多的变量来解释大部分的数据变化性。每个N个观察值或记录都位于p维空间中。并非所有维度都是同等重要的。PCA 寻求少数几个重要的维度,其中重要性通过每个维度上观察值的变化量来量化。PCA 确定的维度是p个特征的线性组合。我们可以利用这些线性组合来减少用于可视化分析特征空间的图表数量,同时保留原始数据的本质。
由特征分解计算出的协方差矩阵的特征值和特征向量分别决定了新子空间的大小和方向。在线性代数中,特征向量(与一组线性方程相关联)是一个非零向量,当对其进行线性变换时,该向量会按某个大小(标量)发生变化。对应的特征值是特征向量被缩放的大小或因子,特征分解是将矩阵分解为其特征向量和特征值。主成分就是特征向量。对协方差矩阵按降序排序后,最顶端的特征值对应数据集的主成分。
数据的第一个主成分(PC)是具有最大方差的特征的线性组合。第二个主成分是所有与第一个主成分不相关的线性组合中具有最大方差的特征的线性组合。前两个主成分在图 3.4中显示,这一计算过程会一直进行,直到找到数据集的所有主成分。这些主成分本质上就是特征向量,线性代数方法表明,协方差矩阵中与最大特征值对应的特征向量解释了数据变异性的最大比例。每个主成分向量在特征空间中定义了一个方向,而且它们彼此之间没有相关性——即相互正交。主成分构成了新空间的基础。

图 3.4:特征空间中的主成分(x =
,y =
)
主成分数量 – 如何为数据集选择合适的数量
问题是如何确定一个数据集被某个数量的主成分(PC)解释的程度。答案在于由主成分数量保留的方差百分比。我们理想情况下希望能够用最少数量的主成分来解释大部分的变异性。没有一个健壮的方法来确定可用的成分数量。随着数据集中观测值和变量的变化,所需的准确度和降维量也不同。
主成分方差解释比例(PVE)是通过考虑第m个特征值来计算的。PVE 是由第m个特征值表示的比例,即
(来自图 3.5中的j个变异量),与所有主成分或特征向量的特征值之和的比率。简单来说,PVE 是每个主成分解释的方差与数据集中所有主成分总方差之比。

图 3.5:PVE 计算
通常,我们会寻找 PVE 显著下降的“拐点”,以确定可用的主成分数量。

图 3.6a:PVE 与主成分的关系
在 图 3.6a 中显示的第一个主成分解释了 62.5% 的数据变异性,第二个主成分解释了 25%。以累积的方式,前两个主成分解释了 87.5% 的变异性,如 图 3.6b 所示。

图 3.6b:累积 PVE(纵轴)与主成分(PC)
另一种广泛使用的矩阵分解方法用于确定主成分(PC)的数量是 奇异值分解 (SVD)。它是一种降秩逼近方法,提供了一种简便的方式来实现相同的目标——即计算与奇异值对应的主成分。SVD 只是另一种矩阵分解方法,它让我们解开与特征分解类似的信息。通过 SVD 得到的数据矩阵的奇异值,本质上是 PCA 中协方差矩阵的特征值的平方根。SVD 就是对原始数据矩阵(均值中心化的矩阵)进行 PCA。
SVD 的数学演示可以在 Wolfram 页面上找到:mathworld.wolfram.com/SingularValueDecomposition.xhtml。
SVD 是一种迭代的数值方法。每个矩阵都有 SVD,尽管对于一些复杂问题,它可能无法整齐地分解某些矩阵。你可以使用 Python 库中的线性代数类 scipy 来执行 SVD。scikit-learn 库提供了 SVD 的函数:scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.xhtml。
高维数据可以被简化为一组与所解决问题最相关的维度(列)。数据矩阵(行与列)会得到一个秩较低的矩阵,该矩阵近似原始矩阵,并且最佳地捕捉其显著特征。
特征提取方法
降维可以通过两种方式进行:一种是特征选择,另一种是特征提取。在特征选择方法中,通过根据某些符合特定使用案例和相应数据的标准进行过滤,选出原始特征的一个子集。而在特征提取方法中,找到一组新的特征。
特征提取是通过从原始特征进行线性映射来完成的,实施该方法后原始特征将不再存在。从本质上讲,通过可用数据构建的新特征没有像原始数据那样的列名。有两种特征提取方法:PCA 和 LDA。根据数据的不同,也可以使用非线性映射,但该方法就不再是 PCA 或 LDA。
现在我们已经探索了用于特征降维的 PCA(因此,也降维了高维空间),接下来我们将学习一种监督式的线性特征提取方法,称为 线性判别分析 (LDA)。
LDA – 与 PCA 的区别
LDA 和 PCA 都是线性变换方法;后者通过求解最大化数据方差的方向或主成分(PCs),前者则通过求解最大化数据类别间分离的方向。PCA 算法的工作方式忽略了类别标签。
LDA 是一种监督方法,用于将数据投影到一个子空间,以最大化(组)类别之间的可分性;因此,它用于模式分类问题。LDA 对于具有多个类别的数据效果较好;然而,它假设类别是正态分布的且类别协方差相等。PCA 通常在每个类别的样本数相对较少时表现较好。然而,无论如何,观测值应该相对于维度要高得多,以便获得有意义的结果。
LDA 寻找一种最能区分数据的投影,而 PCA 则寻找一种能够保持数据中最大信息量的投影。当引入协方差估计的正则化,以调节不同变量对 LDA 的影响时,这被称为正则化 判别分析。

图 3.7:线性判别
LDA 涉及为每个类别基于每个输入变量的分布开发一个概率模型(图 3.7)。它可以视为应用贝叶斯定理进行分类,并假设输入变量是无关的;如果它们是相关的,PCA 变换可以帮助去除线性依赖。scikit-learn 库提供了 LDA 的函数。这里给出了一个使用合成数据集的示例代码:
from sklearn.datasets import make_classification
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
X, y = make_classification(n_samples = 1000, n_features = 8, n_informative = 8,
n_redundant = 0, random_state = 1) #train examples and labels
model = LinearDiscriminantAnalysis()
cv = RepeatedStratifiedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
grid = dict()
grid['solver'] = ['svd', 'lsqr', 'eigen'] #grid configuration
search = GridSearchCV(model, grid, scoring = 'accuracy', cv = cv, n_jobs = -1)
results = search.fit(X, y)
print('Mean Accuracy: %.4f' % results.best_score_) #model accuracy check
row = [0.1277, -3.6440, -2.2326, 1.8211, 1.7546, 0.1243, 1.0339, 2.3582] #new example
yhat = search.predict([row]) #predict on test data
print('Predicted Class: %d' % yhat) #class probability of new example
在上述示例中,网格搜索中的超参数(solver)设置为'svd'(默认值),但也可以使用其他solver值。此示例只是介绍了如何在 scikit-learn 中使用 LDA;根据解决的问题,实际上可以进行大量定制。
我们已经探讨了用于降维的线性代数方法;接下来我们将学习 PCA 的最重要应用。
PCA 的应用
PCA 是一个基础算法,是机器学习的基石。它广泛应用于图像噪声减少、数据分类、异常检测以及医学数据相关性等领域。在本节中,我们将探讨 PCA 的几个广泛应用。
Python 中的 scikit-learn 库提供了 PCA 的函数。以下示例代码展示了如何在开发预测模型时,利用 PCA 进行降维,并将 PCA 投影作为输入。我们将在一个合成数据集上使用 PCA,同时拟合一个用于分类的逻辑回归模型:
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from numpy import mean
from numpy import std
import matplotlib.pyplot as plt
def get_models():
models = dict()
for i in range(1, 11):
steps = [('pca', PCA(n_components = i)), ('m', LogisticRegression())]
models[i] = Pipeline(steps = steps)
return models
def evaluate_model(model, X, y):
cv = RepeatedStratifiedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
scores = cross_val_score(model, X, y, scoring = 'accuracy', cv = cv, n_jobs = -1, error_score = 'raise')
return scores
X, y = make_classification(n_samples = 1000, n_features = 10, n_informative = 8, n_redundant = 2, random_state = 7)
models = get_models()
results, names = list(), list()
for name, model in models.items():
scores = evaluate_model(model, X, y)
results.append(scores)
names.append(name)
print('Mean Accuracy: %.4f (%.4f)' % (mean(results), std(results))) red_square = dict(markerfacecolor = 'r', marker = 's')
我们将使用以下代码绘制主成分:
plt.boxplot(results, labels = names, showmeans = True, showfliers = True, flierprops = red_square)
plt.grid()
plt.xlabel('Principal Components')
plt.xticks(rotation = 45)
plt.show()
row = [0.1277, -3.6440, -2.2326, 1.8211, 1.7546, 0.1243, 1.0339, 2.3582, -2.8264,0.4491] #new example
steps = [('pca', PCA(n_components = 8)), ('m', LogisticRegression())]
model = Pipeline(steps = steps)
model.fit(X, y)
yhat = model.predict([row]) #predict on test data
print('Predicted Class: %d' % yhat) #predicted class of new example
在前面的例子中,我们没有看到模型准确率在超过八个主成分后有所提升(图 3.8)。显然,前八个主成分包含了关于类别的最大信息,其余的则是冗余的。

图 3.8:合成数据集上分类准确率与主成分数量的关系
在 PCA 转换特征后的主成分数量,是选择能够提供最佳平均模型性能的数量,然后将其输入到机器学习模型中进行预测。在接下来的小节中,我们将学习如何使用 PCA 进行去噪和异常点检测。
噪声减少
PCA 用于减少数据中的噪声,尤其是在图像处理中。PCA 通过去噪对图像进行重建可以通过使用 scikit-learn Python 库的分解方法实现。该库函数的详细信息和示例可以在此找到:scikit-learn.org/stable/auto_examples/applications/plot_digits_denoising.xhtml
一个好的练习是重建从视频序列中获得的图像,探索线性 PCA 和核 PCA,并检查哪种方法提供更平滑的图像。
图像压缩(图 3.9)是 PCA 的另一个重要应用。

图 3.9:PCA 图像压缩
主成分所表示的方差百分比决定了应该有多少特征作为输入传递到深度学习模型(神经网络)中进行图像分类,以便在处理庞大和高维数据集时,不影响计算性能。
异常检测
异常检测在欺诈检测、故障检测和传感器网络中的系统健康监控中非常常见。PCA 利用聚类方法来检测异常点,通常是集体性的和无序的异常点。基于聚类的异常检测假设,正常数据点属于大而密集的簇,而异常数据点则属于小的或稀疏的簇,或者根本不属于任何簇。带有示例遥测数据的代码可以在以下仓库找到:github.com/ranja-sarkar/mm。
主成分应用距离度量来识别异常。在这种情况下,PCA 确定什么构成正常类别。作为一个练习,你可以使用无监督学习方法,如 K 均值和隔离森林,在相同数据集上检测异常点,并比较结果,获取更多有意义的洞察。
总结
在本章,我们学习了两种用于减少数据维度的线性代数方法:即主成分分析(PCA)和线性判别分析(LDA)。重点是 PCA,它是一种无监督方法,用于减少高维数据的特征空间,并了解为什么这种降维对于解决业务问题至关重要。我们详细研究了该算法背后的数学原理及其在机器学习模型中的工作原理。我们还了解了 PCA 的一些重要应用,并附带了 Python 代码。
在下一章,我们将学习一种优化方法——梯度下降法,它可以说是优化神经网络中最常见(也是最流行)的算法。这是一种通过最小化给定的成本函数来工作的学习算法。顾名思义,它通过迭代使用梯度(导数)来最小化该函数。
第四章:梯度下降
一种为机器学习模型奠定基础的优化算法是梯度下降(GD)。GD 是一个简单且有效的工具,适用于训练此类模型。顾名思义,梯度下降涉及“下坡”。我们选择一个方向穿越地形,并采取每一步让我们下坡的方式。步长取决于坡度(梯度)。在机器学习(ML)模型中,梯度下降估计误差梯度,帮助最小化成本函数。很少有优化方法能像梯度下降那样在计算效率上表现得如此出色。GD 还为深度学习模型的优化奠定了基础。
在那些无法通过线性代数解析计算参数,且必须通过优化搜索的难题中,GD 找到了它的最佳应用。该算法通过迭代的方式沿着最陡的下降方向移动。在每次迭代中,模型参数(如线性回归中的系数和神经网络中的权重)都会被更新。模型会不断更新其参数,直到成本函数收敛或达到最小值(即图 4.1a中的斜坡底部)。

图 4.1a:梯度下降
每次迭代中所采取的步长称为学习率(每次迭代时,函数的导数都会按学习率缩放)。如果学习率过低,模型可能会在达到底部之前就达到最大允许迭代次数,而如果学习率过高,则可能无法收敛或完全发散(即所谓的爆炸梯度问题)。选择最合适的学习率对于获得最佳精度的模型至关重要,如图 4.1b所示。

图 4.1b:梯度下降中的学习率
为了使梯度下降(GD)有效,目标或成本函数必须是可微分的(意味着在单变量函数的定义域中的每个点都有一阶导数)且是凸的(即函数上的两点可以通过一条不相交的线段连接)。凸函数的二阶导数总是为正。凸函数和非凸函数的例子如图 4.2所示。GD 是一种一阶优化算法。

图 4.2:凸函数(L)和非凸函数(R)的示例
在多变量函数中,梯度是该函数在定义域中每个方向上的导数向量。此类函数可能具有鞍点(准凸或半凸),在这些点上,算法可能会陷入困境,无法保证找到最小值。为了突破鞍点并达到全局最小值,引入了二阶优化算法。除了机器学习(ML)和深度学习(DL)之外,梯度下降算法也在控制工程和机械工程中有应用。以下各节将梯度下降算法与其他用于 ML 和深度学习(DL)模型的优化算法进行比较,并专门探讨一些常用的梯度下降优化器。
本章涵盖以下主题:
-
梯度下降法的变种
-
梯度下降优化器
梯度下降法的变种
本节通过 Python 代码详细说明了梯度下降算法在优化简单线性回归模型(y = mx + c)中的工作原理。
梯度下降法的应用
在保持迭代次数相同的情况下,算法会分别在三种不同的学习率下运行,产生三种模型,因此得到三种MSE(均方误差)值。MSE 是线性回归中计算的损失或代价函数:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
#gradient descent method
class GDLinearRegression:
def __init__(self, learning_rate, epoch):
self.learning_rate, self.iterations = learning_rate, epoch
#epoch is number of iterations
def fit(self, X, y):
c = 0
m = 5
n = X.shape[0]
for _ in range(self.iterations):
b_gradient = -2 * np.sum(y - m*X + c) / n
m_gradient = -2 * np.sum(X*(y - (m*X + c))) / n
c = c + (self.learning_rate * b_gradient)
m = m - (self.learning_rate * m_gradient)
self.m, self.c = m, c
def predict(self, X):
return self.m*X + self.c
#dataset
np.random.seed(42)
X = np.array(sorted(list(range(5))*20)) + np.random.normal(size = 100, scale = 0.5)
y = np.array(sorted(list(range(5))*20)) + np.random.normal(size = 100, scale = 0.3)
#model 1
Clf_1 = GDLinearRegression(learning_rate = 0.05, epoch = 1000)
Clf_1.fit(X, y)
y_pred = Clf_1.predict(X)
mse_1 = mean_squared_error(y, y_pred)
plt.style.use('fivethirtyeight')
plt.scatter(X, y, color='black')
plt.plot(X, y_pred)
plt.gca().set_title("Linear Regression Model 1")
print('Slope = ', round(Clf_1.m, 4))
print('Intercept = ', round(Clf_1.c, 4))
print('MSE = ', round(mse_1, 2))
另外两个模型分别使用两种不同的学习率进行训练,一个比模型 1 的学习率高,另一个低,如下所示:
#model 2
Clf_2 = GDLinearRegression(learning_rate = 0.2, epoch = 1000)
Clf_2.fit(X, y)
y_pred = Clf_2.predict(X)
mse_2 = mean_squared_error(y, y_pred)
plt.style.use('fivethirtyeight')
plt.scatter(X, y, color='black')
plt.plot(X, y_pred)
plt.gca().set_title("Linear Regression Model 2")
print('MSE = ', round(mse_2, 2))
#model 3
Clf_3 = GDLinearRegression(learning_rate = 0.0001, epoch = 1000)
Clf_3.fit(X, y)
y_pred = Clf_3.predict(X)
mse_3 = mean_squared_error(y, y_pred)
plt.style.use('fivethirtyeight')
plt.scatter(X, y, color='black')
plt.plot(X, y_pred)
plt.gca().set_title("Linear Regression Model 3")
print('MSE = ', round(mse_3, 2))
执行代码后,得到的线性回归模型(图 4.3)展示了如何谨慎选择参数(学习率),以达到最佳性能或最优的 ML 模型准确度。

图 4.3:线性回归模型的梯度下降
存在不同的梯度下降法变种(图 4.4),它们在计算目标函数的梯度时使用的数据量不同。根据数据量的多少,在参数(系数或权重)的准确性与计算所需时间之间需要做出权衡。梯度下降法的变种包括批量梯度下降(BGD)、小批量梯度下降和随机梯度下降(SGD),我们将在以下小节中讨论它们。
小批量梯度下降与随机梯度下降
BGD,也称为原始梯度下降法,仅仅是梯度下降,它对整个训练数据计算梯度,进行一次更新(一步更新),因此可能非常慢。使用 BGD 优化的常见 ML 模型包括用于较小数据集的线性回归和逻辑回归。
对于更大的数据集,我们通常使用小批量梯度下降(mini-batch GD),它允许将训练数据分割成可以单独处理的小批量。处理完每个小批量后,更新参数,直到整个数据集被迭代处理完毕。通过这种方式,一次完整的数据集循环称为一个周期。为了达到全局最小值,需要执行若干步骤,这在优化过程中引入了一些方差。这种 GD 的变体通常用于效率与精度同等重要的建模问题。
SGD 对每个训练样例执行频繁的参数更新,具有较高的方差,导致成本函数波动较大。这使得它能够跳到一个新的、潜在更好的局部最小值。通过缓慢减小学习率,SGD 表现出类似于 BGD 的收敛行为。SGD 的计算速度比 BGD 快,因为它一次考虑一个样本。

图 4.4: 梯度下降变种
SGD 通常是训练神经网络的选择算法。在最小化神经网络中常见的高度非凸误差函数时,SGD 的一个关键挑战是避免陷入其众多次优局部最小值中。这些鞍点使得 SGD 很难逃脱,因为梯度在所有维度上接近零。在下一节中,我们将概述一些处理此类挑战的梯度下降优化器。
梯度下降优化器
这里讨论的优化器广泛用于训练依赖于误差或成本函数非凸性程度的深度学习模型。
动量
动量法使用移动平均梯度而不是每个时间步的梯度,并减少由 SGD 引起的前后振荡(成本函数波动)。此过程侧重于最陡下降路径。图 4**.5a 显示了没有动量时 SGD 中的振荡,而 图 4**.5b 显示了在相关方向上累积速度并减少阻尼振荡,接近最优解。

图 4.5a: 没有动量的 SGD

图 4.5b: 具有动量的 SGD
动量项减少了梯度方向变化的维度更新,从而实现更快的收敛。
Adagrad
adagrad 优化器在处理稀疏数据时使用,因为该算法基于频繁出现的特征对参数进行小的更新。在 adagrad 中,每次更新的学习率是不同的或“自适应”的(图 4**.6)。该算法为不常见的特征使用较大的学习率,为更常见的特征使用较小的学习率。使用此优化器的主要优势是学习率不需要手动设置。当学习率缩小到接近零时,模型将不再获得新的知识。

图 4.6:Adagrad 优化器
RMSprop
RMSprop 优化器类似于adagrad优化器,因此也被称为泄漏的 adagrad,只是它使用不同的参数更新方法。RMSprop 算法限制了垂直方向的振荡,使其可以在水平方向上迈出更大的步伐(图 4**.7)。该算法通过使用梯度的指数加权平均值来自适应地缩放每个维度的学习率,从而使其能够聚焦于最近的梯度。

图 4.7:RMSprop 优化器
Adam
自适应矩估计(adam)优化器继承了动量和 RMSprop 优化算法的优点(图 4**.8)。它结合了移动平均梯度和自适应学习率的思想。这两者分别表示代价函数梯度的一阶矩(均值)和二阶矩(方差)的估计,因此得名。

图 4.8:Adam 优化器
经实验证明,Adam 优化器在实践中比 SGD 更有效,表现更好。它已经成为训练深度学习模型的默认优化器。欲了解更多,请阅读以下《机器学习精粹》文章:machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/。
总结
在本章中,我们学习了一个基础的优化算法及其在训练机器学习和深度学习模型中的变体。还详细讲解了如何在 Python 中将优化技术应用于线性回归问题。无论是代价函数及其梯度,还是如何更新梯度以收敛到最优点,都是每个数据科学家必须彻底理解的数学概念;优化代价函数是为问题或预测实现最优模型的基础。根据代价函数的行为,可以使用不同的方法来估算梯度。
在接下来的章节中,我们将探讨另一种基础算法,称为支持向量机(SVMs)。尽管 SVM 可以用于回归问题,但它更广泛地用于分类任务。
第五章:支持向量机
本章探讨了一种经典算法,它是每个机器学习工具库中都必须掌握的算法——支持向量机(SVM),主要用于分类问题而非回归问题。自 1990 年代问世以来,它通常用于识别数据中的模式和异常值。在 极限梯度提升(XGB)等提升算法出现后,它的受欢迎程度有所下降。然而,它仍然是最常用的监督学习算法之一。
在 1990 年代,基于计算学习的高效学习算法被开发出来,用于处理非线性函数。诸如线性学习算法等算法具有明确定义的理论特性。随着这一发展的出现,使用核函数的非线性区域的高效可分性(决策面)被建立起来。非线性 SVM 被广泛应用于实际(非线性)数据的分类中。
SVM 最初被称为一种二分类器,可以用于处理类别分布不平衡或倾斜的一类分类问题。这个无监督算法可以有效地从数据集中的大多数或正常类别中学习,将新的数据点分类为 正常 或 异常值。识别少数类或稀有类(通常被称为异常值)的过程被称为 异常检测,因为异常值是异常的,其他数据点则是正常的。分类过程包括在正常数据(训练样本)上拟合模型,并预测传入的新数据是正常的(内点)还是异常的。单类 SVM 最适合于一种特定的问题,即少数类没有一致的模式或是噪声实例,导致其他分类算法难以学习决策边界。异常值通常被视为与正常数据的偏差。
一般来说,SVM 在变量数量大于记录数的问题中有效,即在高维空间中。该算法在决策函数中使用训练样本的子集,因此具有内存效率。事实证明,该算法非常灵活,因为可以为决策函数指定不同的核函数。
本章涵盖以下主题:
-
SVM 中的支持向量
-
SVM 的核函数
-
SVM 的实现
我们将在接下来的章节中学习支持向量和核函数。
SVM 中的支持向量
SVM 是一种可以以较少计算资源产生显著准确结果的算法,广泛应用于数据分类任务。如果一个数据集有 n 个特征,SVM 会在 n 维空间中找到一个超平面,也称为 决策边界,以分类数据点。最优的决策边界最大化边界与两类实例之间的距离。类别中数据点之间的距离(如 图 5.1a 所示)被称为 间隔:

图 5.1a:最优超平面
SVM 算法在二维空间中找到最优直线,或在多维空间中找到最优超平面,从而将空间分割成不同类别。最优超平面或最优直线最大化边距(即两个类别的数据点之间的距离)。在三维空间(或更高维度)中,数据点变成了向量,而那些最靠近或在超平面上的数据点(位于最大边距外的极小子集的训练样本)被称为支持向量(见 图 5.1b):

图 5.1b:支持向量
如果所有支持向量距离最优超平面相同,则表示边距很好。图中的边距(图 5.1b)较差,因为类别 +1 的支持向量非常接近最优超平面,而类别 -1 的支持向量距离最优超平面较远。移动一个支持向量会移动决策边界或超平面,而移动其他数据点则不会产生影响。
如果输入数据集的特征数为 2,则超平面仅为一条直线。如果特征数为 3,则超平面(见 图 5.2)是一个二维平面。决策边界的维度取决于特征的数量,而其两侧(超平面两侧)属于不同类别的数据点:

图 5.2:三维特征空间中的超平面
支持向量影响超平面的位置和方向。使用支持向量可以最大化分类器的边距。如果数据是线性可分的,则边距为硬边距。对于大多数实际问题,数据是非线性可分的,因此在这种情况下,边距为软边距。这允许数据点位于两个类别分隔符之间的边距距离内(见 图 5.3):

图 5.3:软边距
较大的边距更好,它可能会允许一些边距违规的发生。边距越大,分类器的误差越低。最大化边距等同于在机器学习算法中最小化损失。帮助最大化边距的函数是铰链损失。如果数据正确分类,则铰链损失(错误)为零,意味着我们有一个硬边距,因为数据点距离超平面较远。若大多数数据点被错误分类,则铰链损失为一。通常,当问题无法线性分割时,支持向量位于边距边界内(软边距)。
在下一节中,将介绍核技巧。核函数是 SVM 中用来分类非线性可分数据点的一种技术。核函数使得我们可以在高维特征空间中进行操作,而无需计算数据在该空间中的坐标,因此该操作在计算上并不昂贵。
SVM 的核函数
通过核技巧,二维空间通过映射函数转换为三维空间,从而使得非线性数据能够在更高维度上进行分类或分离(见图 5**.4)。原始数据的转换是通过核来完成的。核函数定义了在变换空间中的内积(相似性度量)。
随着训练样本数的增加,SVM 的计算和存储需求也随之增加。该算法的核心是一个二次规划问题,将支持向量从训练数据集中分离出来。线性核是 SVM 最快速的实现方式,它仅是一个点积。图 5**.5a中展示了线性和非线性核的一些示例。最常见的非线性 SVM 核函数有径向基函数(RBF)、Sigmoid和多项式。

图 5.4:(a)非线性分隔符示例(L),和(b)数据在更高维度中有效分类
SVM 对于那些非线性可分的小型数据集非常有效。所谓的小型数据,是指特征数大于训练样本数,这会导致 SVM 在某些情况下出现过拟合。在这些情况下,合适的核函数和正则化(惩罚函数)可以帮助解决问题。每个核函数有不同的数学形式,因此它们的参数集合各不相同。

图 5.5a:使用线性核(L)、RBF 核(M)和多项式核(R)函数进行数据分类
多项式核的参数指数(度数)设为 1 时成为线性核,设为 3 时成为三次核,其中一个示例如图 5**.5a(最右侧)所示。Sigmoid 核的累积分布函数从 0 到 1 分类数据,通常作为神经网络中的激活函数或感知机使用。使用 Sigmoid 核函数进行 SVM 数据分类的示例见图 5**.5b:

图 5.5b:Sigmoid 核(L),使用 sigmoid 分类的数据(R)
所有的 SVM 核函数都有一个参数,用于在数据集的误分类与分隔器的简洁性之间进行权衡。在使用 RBF 核(它是一个指数型函数(
))训练 SVM 时,参数a大于零,并定义了一个训练样本对分隔器的影响。各个核函数参数的选择对于 SVM 的性能至关重要。
在不平衡数据集中,用于为类和样本提供权重的参数变得至关重要,因为在这种情况下,可能需要赋予某个样本或类更多的权重。样本加权对类边界的影响如图 5.6所示,其中数据点的大小与样本权重成比例:

图 5.6:使用恒定样本权重(左图)与修改后权重(右图)的分类
虽然各种方法和算法可以检测数据集中的离群点,但本章演示了由一类支持向量机(SVM)算法使用的核方法。其他示例包括孤立森林算法中的决策树集成方法、局部离群因子算法中的距离或密度方法等。异常类型可以是点异常或集体异常,选择检测算法时需要根据数据集中的异常类型进行选择。图 5.7a 和 5.7b 展示了这些异常类型的示例。点异常是全局行为,而集体异常是局部异常(非正常)行为。还可能存在一些数据集,其中异常是完全上下文相关的,这在时间序列数据中大多能看到。

图 5.7a:点异常的示例

图 5.7b:集体(非点)异常的示例
在接下来的部分中,我们将使用 Python 实现一类 SVM 解决方案,因为该解决方案通常在解决那些少数类离群点没有结构并且主要是噪声样本(即与正常点的偏差严重)的情况下被证明非常有用。
SVM 实现
一类支持向量机(SVM)算法在训练过程中不使用(忽略)远离或偏离观察值的示例。只有那些最集中或密集的观察值用于(无监督)学习,这种方法在那些期望只有极少数偏离正常的特定问题中非常有效。
为了实现 SVM,创建了一个合成数据集。该数据集约有 2% 的少数类(离群点),用 1 表示,98% 的多数类(正常点)用 0 表示,并利用 RBF 核将数据映射到高维空间。以下是使用 Python(配合 scikit-learn 库)运行的代码:
import pandas as pd, numpy as np
from collections import Counter
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.svm import OneClassSVM
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
X, y = make_classification(n_samples = 10000, n_features = 2, n_informative = 2,
n_redundant = 0, n_classes = 2,
n_clusters_per_class = 1,
weights = [0.98, 0.02], class_sep = 0.5, random_state = 0)
#Dataset as pandas dataframe
df = pd.DataFrame({'feature1': X[:, 0], 'feature2': X[:, 1], 'target': y})
#Split dataset into train and test subsets in the ratio 4:1
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
#Train SVM model with RBF
one_class_svm = OneClassSVM(nu = 0.01, kernel = 'rbf', gamma = 'auto').fit(X_train)
#nu (specifies number of outliers) = 1% , gamma is a parameter for nonlinear kernels
prediction = one_class_svm.predict(X_test)
prediction = [1 if i == -1 else 0 for i in prediction] #outliers denoted by 1, inliers by 0
print(classification_report(y_test, prediction))
分类器的报告(图 5.8)清楚地显示,一类 SVM 模型的召回率为 23%,这意味着该模型捕捉到了 23% 的离群点。F1 分数是精确度和召回率的调和平均数:

图 5.8:一类 SVM 的分类报告
我们将使用以下代码来可视化异常值:
#Visualization of outliers
df_test = pd.DataFrame(X_test, columns = ['feature1', 'feature2'])
df_test['y_test'] = y_test
df_test['svm_predictions'] = prediction
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (16, 8))
ax1.set_title('Original Data')
ax1.scatter(df_test['feature1'], df_test['feature2'], c = df_test['y_test'])
ax2.set_title('One-Class SVM Prediction')
ax2.scatter(df_test['feature1'], df_test['feature2'], c = df_test['svm_predictions'])
用于识别这 2%异常值的算法默认阈值也可以定制,以便根据不同的使用场景,标记更多或更少的数据点为异常值。从图 5.9中可以明显看出,大多数异常值(黄色)已经被分类器正确检测出来:

图 5.9:一类 SVM 分类
一类 SVM 特别适合作为异常检测器,并在制造业中通过从机器捕获的传感器数据得到了广泛应用。
总结
在这一章中,我们探讨了支持向量机(SVM)作为分类器的应用。除了线性数据外,SVM 还可以通过使用核函数高效地分类非线性数据。SVM 算法所使用的方法也可以扩展用于解决回归问题。SVM 同样可以用于新颖性检测,其中训练数据集不会被异常值污染,算法被用来将新的观测值检测为异常值,这时异常值被称为新颖性。
下一章将介绍图论,它提供了必要的数学工具来量化和简化复杂系统。图论研究的是在动态系统中,节点或个体实体之间的关系(连接或边)。它是机器学习(ML)和深度学习(DL)的重要组成部分,因为图提供了一种将商业问题表示为数学编程任务的方式,形式为节点和边。
第六章:图论
图是用于建模成对关系的数学结构。图论提供了量化这些关系的工具,特别是在动态系统中。换句话说,图是表示网络或一组互联对象的方式。图论主要应用于运筹学和社会科学。其历史可以追溯到 18 世纪,当时瑞士数学家莱昂哈德·欧拉解决了“哥尼斯堡七桥问题”(图 6.1),这个问题也被认为是图论的前身。俄罗斯哥尼斯堡市位于普雷格尔河的两岸,包含两个大岛——克奈普霍夫和洛姆泽,这两个岛通过七座桥连接。问题是如何设计一条路径,使得每座桥都只跨越一次。欧拉绘制了城市的第一个现代图形的可视化表示。它通过一组称为顶点或节点的点表示(抽象表示),并通过一组称为边的线连接。边表示节点之间的关系。

图 6.1:哥尼斯堡七桥问题
图论问题通常使用数学优化框架,该框架包括三个组成部分:目标函数、决策变量和约束条件。目标函数被最小化,以获取图中节点之间的最优路径,因为两个节点之间可能存在多条路径(多重图)。使用图解决问题的目标是多方面的。一个目标可能是可视化边,找出紧密连接的节点,并识别直接影响目标函数的节点。可能的决策变量之一是是否在两个节点之间添加一条边。一个典型的约束条件可能是每个节点能够达到的度数,即每个节点能与其他节点的最大连接数。一个使用图论解决的著名问题是旅行商问题(TSP),其中最短路径从同一个顶点/节点开始并结束,并且每条边都经过一次。这样的例子(路由)与线性规划领域有关。

图 6.2a:简单图(左)与多重图(右)
图有多种类型,其中最常见的是简单图和多重图。它们在图 6.2a中展示,还有一种图形或有向图(边有方向),并且每条边都有一个正实数的函数赋值,这种图被称为网络。网络实际上是一个有向边标记图,如图 6.2b所示。

图 6.2b:图作为网络(模型)
有一种限制类型的图叫做树。树数据结构不同于图数据结构(图 6.3)。树作为一种层次模型,永远不会像某些图那样具有循环链接(边)。树是有向无环图(DAGs),并且是单向的。图没有根节点(源节点),而树具有根节点,以及子节点,每个子节点都有一个父节点。

图 6.3:树(左)具有 n-1 条边(n = 节点数量)和图(右)没有关于其边数的规则
图还可以包含循环、回路和自循环。有些数据库使用图结构进行语义查询,具有节点和边。查询关系(带标签的、有方向的边)通常是快速的,因为它们始终存储在数据库中。图数据库通常被称为 NoSQL。图也被用于分析社交网络。
本章涵盖以下主题:
-
图的类型
-
优化使用案例
-
图神经网络
下一节将讨论根据标签、方向和图中边的权重来区分图的类型。
图的类型
主要的图类型有无向图、有向图和带权图,如图 6.4所示。社交网络可以是无向图或有向图。在前者中,边是无序的对,例如 Facebook。在后者中,边是有序的对,例如 Twitter,其中一个节点是起点,另一个是终点。

图 6.4:三种标准图类型
我们将在接下来的小节中探索这些图的每一种。
无向图
在使用图论解决问题时,第一步是确定我们所处理的图的类型。在无向图中,节点之间的边没有特定方向,换句话说,边是双向的。连接节点 1 和节点 2 的边(图 6.5)与连接节点 2 和节点 1 的边是相同的。

图 6.5:有向图和无向图
有向图
在有向图(或称为有向图)中,节点之间有一个指定的方向。从节点 1 到节点 2 的边是从 1 指向 2(图 6.5),而从 2 指向 1 的链接是不允许的。换句话说,节点之间的边是单向的。
带权图
如果两个节点或顶点之间的边有一个相关的权重,用来表示如距离或成本等含义,那么这个图就称为加权图。加权图可以是有向的也可以是无向的(图 6.6)。加权图适用于许多现实世界的场景,例如搜索引擎比较航班时间和成本或路径规划。

图 6.6:无向加权图 (L) 和有向加权图 (R)
任何图都可以通过邻接矩阵进行数学表示,邻接矩阵描述了图中节点之间所有允许的路径或路线。有向图的邻接矩阵见图 6.7a,而无向图的邻接矩阵见图 6.7b。

图 6.7a:有向图的邻接矩阵
到目前为止,我们知道图能够使网络更具可解释性并更易于可视化。对于一个更大的图(更多节点),邻接矩阵消耗更多计算机内存,像图中显示的加权图就是一个例子(图 6.7c)。

图 6.7b:无向图的邻接矩阵

图 6.7c:有向加权图的邻接矩阵
大多数邻接矩阵是稀疏的;也就是说,图的连接不是非常密集,这使得计算变得更加困难。
现在我们已经探索了不同的图,我们将研究一个使用图论解决的优化问题的应用场景。
优化应用场景
图可以用于建模物理、生物和信息系统中的关系和过程。它们有广泛的应用,如搜索引擎中超链接的排名、生物分子研究、计算机网络安全、地图中的 GPS 寻找最短路线以及社交网络分析。还有用于信息挖掘的知识图谱。接下来的子节中,我们选择一个数据集并以一种利用图论解决的问题的方式来表达它。
优化问题
起点和终点机场之间可能有多条路径。航空公司寻求机场之间的最短路径,其中最短路径可以通过距离或飞行时间来定义。如果将城市机场表示为节点,航班路线表示为边,我们就将问题转化为一个图(图 6.8a)。数据集可以在 GitHub 仓库中找到:github.com/ranja-sarkar/graphs。

图 6.8a:起点(城市)机场与终点(城市)机场之间的网络(航班路线)
我们可以从图中识别出任何两个城市机场之间可能的最短路径(最小空中时间或最小距离)。实现此解决方案的 Python 示例代码将在下一个小节中进行讲解。
优化解决方案
数据集,其中一部分显示在图 6**.8b中,包含 2017 年 1 月从美国起飞的航班记录,从源城市(Origin)到目的城市(Dest)。源和目的地之间的距离以及航班的空中时间是寻找优化解决方案所需的最相关变量。

图 6.8b:案例研究的数据集
您可以查看执行代码后生成的相应图的节点和边。航班的图(网络)如图 6.8a 所示:
import pandas as pd, numpy as np
import networkx as nx
import matplotlib.pyplot as plt
#dataset
data = pd.read_csv('FlightsUSA.csv')
df = nx.from_pandas_edgelist(data, source = 'Origin', target = 'Dest', edge_attr = True)
#df.nodes()
#df.edges()
plt.figure(figsize = (18,12))
nx.draw_networkx(df, with_labels = True)
希望根据距离和空中时间指标从LAS(拉斯维加斯)到PBI(佛罗里达州的棕榈滩)走最短路线的乘客,可以运行这段代码(使用 NetworkX Python 库中的 Dijkstra 最短路径算法)来自助服务或决定最佳路线:
shortest_airtime = nx.dijkstra_path(df, source = 'LAS', target = 'PBI', weight = 'AirTime')
shortest_dist = nx.dijkstra_path(df, source = 'LAS', target = 'PBI', weight = 'Distance')
print(shortest_dist,shortest_airtime)
LAS和PBI之间基于距离的最短路径如图 6**.9a所示。

图 6.9a:当模型参数为距离时的输出
基于空中时间的最短路径如图 6**.9b所示。

图 6.9b:当模型参数为空中时间时的输出
该算法首先通过选择距离源顶点最近的顶点来创建最短路径集合,然后从剩余的顶点中选择一个离源顶点最近的顶点。直到集合中包含所有顶点/节点为止。因此,图论可以应用于旅行规划和寻找最佳邮递路线等方面。图的数学公式直观且全面。在接下来的部分中,我们将介绍图神经网络(GNNs),并深入探讨深度学习(DL)。
图神经网络
深度学习算法利用图来进行节点、边或整个图的预测。在节点分类中,通过查看邻居的标签来确定样本(节点)的标签。在图分类中,整个图被分类到不同的类别中,一个例子是使用自然语言处理对文档进行分类。节点或实体之间的关系(边)被推荐系统利用。图像和文本是结构化数据的类型,分别可以描述为像素网格和单词序列。这些都显示在图 6**.10a中。相比之下,图是非结构化数据。图可以包含任何类型的数据,包括图像和文本。

图 6.10a:结构化数据(左)与图/网络(右)
GNN 通过一种叫做消息传递的过程来组织图,这样深度学习算法可以利用每个节点邻居的信息来寻找模式并做出预测。通常,GNN 管道的输入(图 6.10b)是一个定义好的图结构,带有其类型和规模。

图 6.10b:GNN 以图作为输入
在图论中,节点嵌入的概念被实现,即将节点映射到比实际维度更低的空间,以使图中相似的节点靠得更近。图 6.11说明了输入图中的信息如何传播到神经网络的外部(灰色框)。因此,信息的聚合需要多层(这里是三层)。一个模型可以通过有监督和无监督的方法进行训练。在后者中,仅使用图结构,相似的节点具有相似的嵌入。前者用于诸如节点分类等有监督任务。

图 6.11:信息传播
GNN 架构用于图像和文本分类问题。它们还用于关系(语义)提取。在近年来,它们已成为可以通过图模型化的任何问题的强大工具。
总结
在本章中,我们学习了一种有助于简化和量化复杂连接系统的理论,这些系统被称为网络。图论研究的是动态实体之间的关系(在图中表示为边),并有助于更好地解释网络模型。我们进一步阐述了(通过 Python 代码)如何利用这个概念将优化问题进行数学建模并求解。许多问题可以通过使用图框架来处理,其中涉及数学优化的组成部分,这一点在本章的某一部分中进行了讨论。
本章还介绍了 GNN,它们在图的结构和属性上进行操作。对于图级任务,预测整个图的单一属性;对于节点级任务,预测每个节点的属性;对于边级任务,抽象地预测图中每个现有边的属性。GNN 在图复杂且深时得到应用。
在下一章中,我们将学习卡尔曼滤波器,这是最有效的估计算法之一。它提供了一种递归计算方法,通过使用一系列通常带有噪声的测量数据,估计离散数据控制过程的(未知)状态,并计算测量的不确定性。卡尔曼滤波是应用于信号处理等领域的一个概念,在这些领域中,无法直接测量的感兴趣变量通过间接方式被测量。
第七章:卡尔曼滤波器
在动态系统中,存在不确定信息。为了捕捉这些不确定性,另一个数学工具——卡尔曼滤波器应运而生。我们可以利用卡尔曼滤波器来最优地估算系统的下一个状态,它特别适用于不断变化的系统。它对于处理噪声传感器数据尤其有用,通过对传感器数据的整合来最佳估算感兴趣的参数。换句话说,卡尔曼滤波器是在不精确和不确定的测量条件下,对系统状态进行估算的工具。它主要用于实时估算未观察到的变量。
卡尔曼滤波算法广泛应用于信号处理、目标跟踪、导航和控制等领域。在跟踪和控制系统中,位置和速度的准确精确估算是一个挑战,因为它们是隐藏(未知)状态。隐藏状态测量中的不确定性通常归因于外部因素,如大气效应和热噪声。卡尔曼滤波器是一种估算动态系统隐藏状态并根据过去的估算预测系统未来状态的算法。它以 Rudolf E. Kalman 的名字命名,他在 1960 年发表了关于离散数据线性滤波问题的递归解决方案的著名论文。
一个系统由一组方程控制,这组方程称为动态模型或状态空间模型。如果已知系统的当前状态和动态模型,就可以估算出后续状态。动态模型中的不确定性受外部因素的影响,称为过程噪声。这是运动方程与系统实际运动之间的误差或偏差。测量中的随机误差或不确定性被称为测量噪声。为了提高对系统未来状态的估算,必须考虑过程噪声和测量噪声。卡尔曼滤波器会同时考虑这两种不确定性。
任何测量(或计算)的参数都是一个估算值,通过使用多个传感器,可以显著提高估算精度。在这方面,有两个术语需要明确理解,它们不能互换使用,分别是准确度和精度。准确度表示测量值与真实值之间的接近程度,而精度表示相同参数测量值的变异性。准确度和精度构成了对系统隐藏状态估算的基础。图 7.1 显示了高精度和高准确度的估算:

图 7.1:高精度和高准确度的测量
无偏系统没有或只有极低的内建系统误差(偏差),因此是高精度系统。实际系统是有偏的,并且有过程噪声。高精度系统具有低方差(或低不确定性)。通过对测量值进行平均(平滑),可以减小方差的影响。测量次数越多,估算值越接近真实值。概率分布函数(PDF)将测量值描述为一个随机变量。分布的离散度(图 7.2)显示了测量噪声。在低精度和低准确度系统中,估计值既不彼此接近,也不接近真实值,且将分布在空间坐标的四个象限中。

图 7.2:测量分布
现在我们已经清楚了测量值的概念及其精度和准确度,我们将讨论卡尔曼滤波器是如何工作的。本章涵盖了这一估算算法的基本步骤,最后会给出一个示范的 Python 代码:
-
测量值的计算
-
测量值的滤波
-
卡尔曼滤波器的实现
卡尔曼滤波器将在最后一节中通过一个示例进行测试,用 Python 来估算一个移动物体的位置(位移)和速度。
测量值的计算
我们将从卡尔曼滤波器算法的流程图开始,如图 7.3a所示。卡尔曼滤波器需要一个初始猜测来开始,这个输入可以是一个非常粗略的估计。因此,步骤 0 是初始猜测,步骤 1 是状态变量的测量。

图 7.3a:卡尔曼滤波器的流程图
当输入是测量值时,输出是使用步骤 2 中的状态更新方程估算的当前状态,该状态是根据当前状态的预测值和由一个叫做卡尔曼增益的因子缩放(更新)的残差计算出来的。卡尔曼增益考虑了输入测量的不确定性,残差是测量值和预测值之间的差异。这一更新和估算构成了算法中的第二步。
步骤 2 的输出被用来预测系统的下一状态。下一次迭代的状态通过动态模型来预测。步骤 3 中的预测基本上是利用动态方程组对当前状态的外推。这一过程会持续多个迭代,每次迭代都会计算卡尔曼增益。图 7.3b 中展示了一个示例,追踪了一个常速飞机在一维空间中的值(真实值、测量值、估计值和预测值),这也举例了一个单变量卡尔曼滤波器:

图 7.3b:恒速飞行器的估计值和预测值(位置)
从图中可以看出,估计算法(卡尔曼滤波器)对测量值具有平滑作用,并且随着迭代步数的增加,估计值逐渐趋近于真实值。下一节描述了如何以测量形式对随机变量进行滤波,以优化估计中的不确定性。
测量值的滤波
卡尔曼滤波器具有与任何滤波器相似的输入和输出。输入是噪声大且不准确的测量值,而输出则是噪声较小、估计更准确的值。从数学角度来看,滤波器的输入是测量值和测量协方差矩阵。动态系统模型是状态转移矩阵(表示方程)和过程噪声协方差矩阵,卡尔曼增益是内部的,并且依赖于系统,滤波器的输出是状态变量和状态协方差矩阵。如图 7.4a所示:

图 7.4a:卡尔曼滤波器的输入和输出
当估计值在时间上传播时,未来状态本质上是不可确定的,因此,误差协方差矩阵会随着时间的推移而增长。动态模型(运动方程)是近似的,过程噪声(不确定性)会增加已有的噪声,这由过程噪声协方差矩阵表示。估计值需要从状态空间转换到测量空间,这一转换是通过另一个矩阵(状态到测量矩阵)完成的。对于本章讨论的应用于线性系统的卡尔曼滤波器,这一变换矩阵较为简单,而对于非线性(或扩展)卡尔曼滤波器,变换可能会更为复杂。如果系统是非线性的,则使用非线性状态估计器或滤波器。例如,扩展卡尔曼滤波器会对当前估计的均值附近的分布进行线性化,并将其应用于算法的预测和更新状态。
卡尔曼增益在每次迭代中计算,并决定输入测量(新信息)对估计的影响。如果输入测量非常嘈杂,卡尔曼增益会更多依赖当前的状态估计而非输入。卡尔曼滤波器能够在每个时间步长上识别如何适当地对当前估计和新的输入测量赋予权重,从而生成最优估计。

图 7.4b:以分布函数形式表示的卡尔曼滤波
总结来说,卡尔曼滤波器是一种最优滤波器,它通过最小化不确定性来处理两个随机变量(先验估计和测量),以估计当前状态变量,如图 7.4b所示。如果我们有一个高方差的先验分布和一个低方差的测量分布,卡尔曼滤波器将两者结合起来,估计出一个比先验分布峰值更高、方差更窄的分布。
在接下来的章节中,将实现基于卡尔曼滤波估计算法的测量计算与滤波过程,并使用 Python 代码。
卡尔曼滤波器的实现
在这个示例中,使用时间序列数据作为输入,卡尔曼滤波器在每个时间步骤提供估计值。该示例展示了一辆移动的车辆,并进行了速度、位移和加速度的初始化。不同时间步的加速度值也被包含在内。描述车辆位移、速度和加速度之间关系的运动学方程式提供了真实值:
import numpy as np
import math, random
import matplotlib.pyplot as plt
current_vel, current_disp, current_accel = 2, 0, 0
total_time = 100
accel_dict = {0:0,5:2,10:8, 20: -2,40:5,45: 9, 60: -3,85:0}
true_values = []
for t in range (1, total_time+1):
current_disp = current_disp + current_vel + (1/2) * current_accel
try:
current_accel = accel_dict[t]
except KeyError:
pass
current_vel = current_vel + current_accel
true_values.append((current_disp, current_vel, current_accel))
随机噪声(误差)以小扰动的形式添加到真实值中,并确定了测量值:
err_range = [700, 30, 15] #noise
measurements = []
for item in true_values:
d,v,a = item
random_err = [random.randint(-1*err_range[0], err_range[0]), random.randint(-1*err_range[1], err_range[1]), random.randint(-1*err_range[2], err_range[2])]
new_disp = d + random_err[0] if d+random_err[0] > 0 else 0
new_vel = v + random_err[1]
new_accel = a + random_err[2]
measurements.append((new_disp, new_vel, new_accel))
我们可以将真实值与位移的测量值进行比较。运行以下代码后,我们得到一个视觉对比(图 7.5)。类似地,我们也可以对真实值和速度的测量值进行视觉对比:
plt.plot([i for i in range(total_time)], [y[0] for y in true_values], 'r--', label = 'True Values')
plt.plot([i for i in range(total_time)], [y[0] for y in measurements], 'b--', label = 'Measurements')
plt.ylabel("Displacement")
plt.xlabel("Time (s)")
plt.legend()
plt.show()

图 7.5:位移的真实值与测量值
因此,我们已经完成了测量值的计算。接下来是测量值的滤波过程,其中噪声数据被输入到卡尔曼滤波器中。初始化了位移和速度,并估计了误差协方差(Q)。同时使用了转移矩阵(A),假设车辆的加速度是未知的。由于数据中的噪声,测量误差(R)高于估计误差。以下代码中的H提供了状态,P是误差矩阵:
x_k = np.asarray([30,20])
Q = np.asarray([[0.004,0.002],[0.002,0.001]])
A = np.asarray([[1,1],[0,1]])
R = np.asarray([[0.4,0.01],[0.04,0.01]])
H = np.asarray([[1,0],[0,1]])
P = np.asarray([[0,0],[0,0]])
estimation = []
for k_loop in range(total_time):
z_k = np.asarray([measurements[k_loop][0], measurements[k_loop][1]])
x_k = A.dot(x_k)
P = (A.dot(P)).dot(A.T) + Q
K = (P.dot(H.T)).dot(np.linalg.inv((H.dot(P).dot(H.T)) + R))
x_k = x_k + K.dot((z_k - H.dot(x_k)))
P = (np.identity(2) - K.dot(H)).dot(P)
estimation.append((x_k[0], x_k[1]))
我们现在可以将真实值与位移和速度的估计值进行比较。运行以下代码后,我们得到一个仅关于位移的视觉对比(图 7.6):
plt.plot([i for i in range(total_time)], [y[0] for y in true_values], 'r--', label = 'True Values')
plt.plot([i for i in range(total_time)], [y[0] for y in measurements], 'b--', label = 'Measurements')
plt.plot([i for i in range(total_time)], [y[0] for y in estimation], 'g--', label = 'Estimated Values')
plt.title('Estimation of displacement')
plt.ylabel("Displacement")
plt.xlabel("Time (s)")
plt.legend()
plt.show()

图 7.6:位移的真实值与估计值
显而易见,当使用卡尔曼滤波器处理带噪数据时,它能够提供与真实值非常接近的位移估计。算法实现中使用的移动车辆示例如图 7.7所示:

图 7.7:移动车辆的最优状态估计
尽管在 Python 代码中使用了合成数据,但该算法的使用和应用是通用的,只要系统动态的矩阵设置得当,该算法就能有效作用于动态系统。
总结
在本章中,我们探讨了卡尔曼滤波器——一种用于解决信号处理、导航和控制系统问题的估计与预测算法。卡尔曼滤波器包括线性和单变量(即一维)滤波器,其中假设系统动态是线性的。然而,许多动态过程具有多维度,在这种情况下,我们使用多变量且通常是非线性的(或扩展的)卡尔曼滤波器。例如,描述一个运动物体的位置和速度的状态向量是六维的,而非线性卡尔曼滤波器被用来确定此类物体在空间中的位移(和速度)。此外,由于卡尔曼滤波器在操作中使用占用较少计算机内存的矩阵,它消耗的计算资源较少(从而减少运行时间)。卡尔曼滤波器被认为是在噪声数据下最好的估计算法,因为它通过结合我们已有的信息,提供了一个更具信心的分布,从而减轻了不确定性。
下一章是本书这一部分(数学工具)的最后一章,内容是关于马尔可夫链的,它是一种从具有复杂概率分布的总体中采样状态的算法。它是一个用于遍历状态系统的概率工具。换句话说,它在图上随机行走,并帮助根据当前知识预测下一个状态。
第八章:马尔可夫链
马尔可夫链是最重要的随机过程之一,能够通过概率解决现实世界中的问题。马尔可夫链是一个离散位置集合中的随机运动模型,换句话说,它是一个从一个位置(状态)到另一个位置(状态)过渡的模型,并且具有一定的概率。它以俄罗斯数学家安德雷·马尔可夫(Andrey Markov)的名字命名,他以在随机过程上的工作而著名。它是一个数学系统,用于描述一系列事件,其中每个事件的概率仅依赖于前一个事件。
“未来仅依赖于当前,而非过去。”
事件或状态可以表示为 {
,其中
是时刻 t 的状态。该过程 {} 具有一个性质,即
,它仅依赖于
,并且不依赖于 {
}。这样的过程称为马尔可夫过程或马尔可夫链。它是遍历一组状态的随机游走。二状态马尔可夫链是一种状态可以转换为自身(即保持在同一状态)。如 图 8.1(状态图)所示。马尔可夫链的一个例子是 PageRank 算法,它被谷歌用来确定搜索结果的排序。

图 8.1:二状态(A 和 E)马尔可夫链
当涉及将现实世界的现象融入计算机仿真时,马尔可夫链非常强大。它是一类表示动态过程的概率图模型,限制在于它只能采用有限数量的状态。马尔可夫链没有长期记忆(简而言之是无记忆的),因此无法知道过去的状态。因此,在马尔可夫链中,唯一决定未来状态的状态是当前状态,这就是所谓的马尔可夫性质。
本章涵盖以下主题:
-
离散时间马尔可夫链
-
马尔可夫链蒙特卡洛 (MCMC)
以下部分讨论了马尔可夫链的基本原理,马尔可夫链是一个离散时间的随机过程。
离散时间马尔可夫链
对于离散时间马尔可夫过程,
在连续时间中,
被
替代,其中
会持续到无穷大。在马尔可夫链中,给定当前状态,过去和未来的状态是独立的,这也意味着未来仅依赖于当前状态。在以下小节中,我们将学习转移矩阵以及马尔可夫链在时间序列数据中的应用,特别是在短期预测中的应用。
转移概率
马尔科夫状态之间的转移概率被捕捉在状态转移矩阵中。转移矩阵的维度由状态空间中的状态数量决定。每个状态都作为一行和一列包含在内,矩阵中的每个单元格给出了从其行状态到其列状态的转移概率,如 图 8.2 所示。为了预测一步后的状态,必须知道转移矩阵和当前状态。转移概率(矩阵元素)通常通过历史序列数据来建立。

图 8.2:两个状态的转移矩阵
马尔科夫链的应用
马尔科夫链用于建模随机过程的行为。它们可以用于文本预测,例如自动完成句子,或者建模时间序列数据的演变,例如建模金融市场的行为。
以下 Python 代码展示了如何使用马尔科夫链建模股票价格。为股票价格的时间演变定义了一组状态(按顺序为 increase、decrease 和 stable),并给出了这些状态之间的转移概率。转移矩阵用于预测未来的可能(下一个状态)价格:
import numpy as np
states = ["increase", "decrease", "stable"] #Markov states
transition_probs = np.array([[0.6, 0.3, 0.1], [0.4, 0.4, 0.2], [0.5, 0.3, 0.2]])
num_steps = 10 #time-steps for simulation
def MC_states(current_state):
future_states = []
for i in range(num_steps):
probs = transition_probs[states.index(current_state)]
new_state = np.random.choice(states, p = probs)
future_states.append(new_state)
current_state = new_state #Update current state
return future_states
#output
MC_states("increase")
输出是一系列未来的状态,如 图 8.3 所示,基于当前状态。如果在执行代码中的函数时,当前状态设置为 decrease 或 stable(初始状态),则会得到不同的输出。状态序列展示了股票价格随时间的变化。需要注意的是,当系统未表现出平稳行为时(即状态之间的转移概率随时间变化),应当小心处理。在这种情况下,可以使用更复杂的马尔科夫模型或完全不同的模型来捕捉系统的行为。

图 8.3:Python 示例代码的输出
如果
是序列处于状态
(观察到状态)的次数,而
是从状态 i 到状态 j 之间发生转移的次数,那么转移概率定义如下:

在下一节中,我们将学习一种用于高维概率分布的采样方法 MCMC,其中下一个样本依赖于当前从一个人群中随机抽取的样本。简而言之,从分布中抽取的样本在概率上是相互依赖的。样本空间的体积随着参数或维度的增加而呈指数增长,使用蒙特卡罗采样等直接方法建模这样的空间可能会不准确。MCMC 方法尝试利用随机问题的属性,并高效地构建相应的马尔可夫过程。
马尔可夫链蒙特卡罗
MCMC 是一种从高维概率定义所定义的目标人群/分布中进行随机采样的方法。这是一种大规模统计方法,从复杂的概率空间中随机抽取样本,以近似未来状态范围内属性的分布。它有助于评估未来结果的分布,样本均值帮助近似期望值。马尔可夫链是一个状态图,采样算法在其上执行随机游走。
最著名的 MCMC 算法可能是吉布斯采样。这些算法只是构建马尔可夫链的不同方法论。最通用的 MCMC 算法是 Metropolis-Hastings 算法,具有多种灵活性。这两个算法将在接下来的小节中讨论。
吉布斯采样算法
在吉布斯采样中,马尔可夫链中下一个样本的概率被计算为前一个样本的条件概率。马尔可夫链中的样本是通过每次改变一个随机变量来构建的(以分布中的其他变量为条件),这意味着后续样本在搜索空间中会更接近。吉布斯采样最适用于离散(而非连续)分布,这种分布具有允许采样并计算条件概率的参数形式。吉布斯采样的一个示例显示在图 8.4中,它再现了期望的分布。

图 8.4:吉布斯采样器再现期望的高斯混合
吉布斯采样器比 Metropolis-Hastings 算法(将在下一个小节中讨论)更高效。它从一个提议分布开始,并且提议总是被接受;也就是说,接受概率始终为 1。我们将在最后一个小节中使用二维高斯分布的例子,结合 Python 代码来演示吉布斯采样器。
Metropolis-Hastings 算法
Metropolis-Hastings 算法用于吉布斯抽样无法使用的概率模型。它不假设下一个样本的状态可以从目标分布生成,这是吉布斯抽样中的主要假设。该算法使用了一个代理概率分布,也叫做核函数,并通过接受准则帮助决定是否将新样本接受到马尔科夫链中,或者是否必须拒绝它。提议分布(代理)是对下一个样本的任意建议,接受准则确保在接近真实或期望的下一个样本状态时,能沿着适当的方向收敛。算法的起始点非常重要,可以探索不同的提议分布。
这个算法是如何工作的?
-
我们从一个随机状态开始。
-
基于提议的概率,我们随机选择一个新的状态。
-
我们计算提议新状态的接受概率。
例如,假设投币正面朝上的概率就是接受概率。如果投币正面朝上,我们接受该样本;否则,拒绝它。
- 我们重复这个过程很长时间。
我们丢弃最初的几个样本,因为链条尚未达到其平稳状态。链条达到平稳状态之前的阶段称为烧入期(见图 8.5)。经过一段时间后,接受的抽样将会收敛到平稳分布。

图 8.5:马尔科夫链
平稳分布展示了在任意给定时间处于任意状态 X 的概率,如果生成大量样本,始终可以达到该分布。这个分布就是我们要寻找的后验分布。后验分布与似然函数和先验分布的乘积成正比。Metropolis-Hastings 算法类似于扩散过程,其中所有状态都在相互交流(设计使然),因此系统最终会趋于一个平衡状态,这与收敛到平稳状态是一样的。这一性质称为遍历性。
在接下来的小节中,我们通过 Python 代码使用双变量分布的例子来说明 Metropolis-Hastings 抽样算法。
MCMC 算法的示意图
吉布斯抽样算法的工作原理通过以下代码展示了一个简单的双变量高斯分布。我们传递两个参数(mu 和 sigma)用于条件概率分布,并丢弃一部分最初采样的值,以便算法收敛,即使起始(猜测)值偏差很大。这部分样本称为烧入期:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(42)
def gibbs_sampler(mus, sigmas, n_iter = 10000):
samples = []
y = mus[1]
for _ in range(n_iter):
x = p_x_y(y, mus, sigmas)
y = p_y_x(x, mus, sigmas)
samples.append([x, y])
return samples
def p_x_y(y, mus, sigmas):
mu = mus[0] + sigmas[1, 0]/sigmas[0, 0] * (y - mus[1])
sigma = sigmas[0, 0]-sigmas[1, 0]/sigmas[1, 1]*sigmas[1, 0]
return np.random.normal(mu, sigma)
def p_y_x(x, mus, sigmas):
mu = mus[1] + sigmas[0, 1] / sigmas[1, 1]*(x - mus[0])
sigma = sigmas[1, 1] - sigmas[0, 1]/sigmas[0, 0]*sigmas[0, 1]
return np.random.normal(mu, sigma)
mus = np.asarray([5, 5])
sigmas = np.asarray([[1, 0.9], [0.9, 1]])
samples = gibbs_sampler(mus, sigmas)
burnin = 200
x = list(zip(*samples[burnin:]))[0]
y = list(zip(*samples[burnin:]))[1]
sns.jointplot(samples[burnin:], x = x, y = y, kind = 'kde')
sns.jointplot(samples[burnin:], x = x, y = y, kind = 'reg')
plt.show()
我们运行代码,Gibbs 采样器生成了一个输出,如图 8.6a所示,输出有两种形式,即核密度估计图和线性回归拟合图。输出是基于使用 Gibbs 采样算法的采样值所得到的结果(目标)分布。

图 8.6a:Gibbs 采样算法的目标分布
我们为 Metropolis-Hastings 采样器运行了一个类似的设置(二元分布)。Python 代码和输出如下所示。首先,我们绘制真实分布,然后使用多元正态分布作为提议分布。图 8.6b 是基于使用该算法采样的输出(目标分布):
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm as tqdm
def density(z):
z = np.reshape(z, [z.shape[0], 2])
z1, z2 = z[:, 0], z[:, 1]
norm = np.sqrt(z1 ** 2 + z2 ** 2)
exp1 = np.exp(-0.5 * ((z1 - 2) / 0.6) ** 2)
exp2 = np.exp(-0.5 * ((z1 + 2) / 0.6) ** 2)
v = 0.5 * ((norm - 2) / 0.4) ** 2 – np.log(exp1 + exp2)
return np.exp(-v)
r = np.linspace(-5, 5, 1000)
z = np.array(np.meshgrid(r, r)).transpose(1, 2, 0)
z = np.reshape(z, [z.shape[0] * z.shape[1], -1])
def metropolis_sampler(target_density, size = 100000):
burnin = 5000
size += burnin
x0 = np.array([[0, 0]])
xt = x0
samples = []
for i in tqdm(range(size)):
xt_candidate = np.array([np.random.multivariate_normal(xt[0], np.eye(2))])
accept_prob = (target_density(xt_candidate))/(target_density(xt))
if np.random.uniform(0, 1) < accept_prob:
xt = xt_candidate
samples.append(xt)
samples = np.array(samples[burnin:])
samples = np.reshape(samples, [samples.shape[0], 2])
return samples
q = density(z) #true density
plt.hexbin(z[:,0], z[:,1], C = q.squeeze())
plt.gca().set_aspect('equal', adjustable ='box')
plt.xlim([-3, 3])
plt.ylim([-3, 3])
plt.show()
samples = metropolis_sampler(density)
plt.hexbin(samples[:,0], samples[:,1])
plt.gca().set_aspect('equal', adjustable = 'box')
plt.xlim([-3, 3])
plt.ylim([-3, 3])
plt.show()

图 8.6b:Metropolis-Hastings 采样算法的真实分布(L)和目标分布(R)
对于有限(离散的以及连续的)状态空间,保证存在一个唯一的平稳状态。我们从一个先验概率分布开始,最终得到一个平稳分布,即基于采样值得到的后验或目标分布。
摘要
在本章中,我们学习了马尔科夫链,它被用来建模特殊类型的随机过程,比如那些假设整个过去已经编码在当前状态中的问题,这反过来可以用于确定下一个(未来)状态。我们还展示了马尔科夫链在建模时间序列数据中的应用。我们还介绍了最常见的 MCMC 算法(Metropolis-Hastings),并用代码进行了说明。如果一个系统表现出非平稳行为(转移概率随时间变化),那么马尔科夫链就不是合适的模型,可能需要更复杂的模型来捕捉动态系统的行为。
本章结束后,我们完成了本书的第二部分。在下一章中,我们将探讨基本的优化技术,其中一些技术在机器学习中得到了应用。我们将讨论进化优化、运筹学中的优化,以及在神经网络训练中应用的优化方法。
第三部分:数学优化
在本部分,你将接触到为机器学习、深度学习以及运筹学中使用的其他模型奠定基础的优化技术。优化技术在预测性和规范性分析中具有极大的威力,并应用于重工业中的许多复杂问题。此外,将经典数学建模与机器学习相结合,通常可以为特定的敏感商业问题提取出更有意义的洞察。
本部分包括以下章节:
-
第九章,探索优化技术
-
第十章,机器学习的优化技术
第九章:探索优化技术
本章主要旨在回答“为什么在解决问题时优化是必要的?”这个问题。数学优化,或称数学规划,是一种强大的决策工具,在第一部分的各章中已进行了深入讨论。重要的是要回忆一个简单的事实:优化通过减少误差(本质上是预测数据与实际数据之间的差距)来为问题提供最佳结果。优化是有代价的;几乎所有的优化问题都通过成本来描述,例如金钱、时间和资源。这个成本函数就是误差函数。如果一个商业问题具有明确的目标和约束条件,例如航空和物流行业,那么数学优化就可以用于高效的决策制定。
在机器学习(ML)问题中,成本通常称为损失函数。机器学习模型对趋势进行预测或对数据进行分类,而训练一个模型的过程本质上就是优化过程,因为该过程的每次迭代都旨在提高模型的准确性并降低误差范围。选择超参数的最佳值是确保模型准确高效运行的关键。超参数是机器学习模型中的元素(例如,学习率、簇的数量等),它们经过调优以使特定数据集适应模型。简而言之,它们是控制学习过程的参数值。优化是一个迭代过程,意味着在大多数情况下,机器学习模型在每次迭代后变得更加准确,并且在预测结果或分类数据方面变得更好。
机器学习与数学优化的正确结合对于某些商业问题是有用的。例如,机器学习模型的输出可以决定优化模型的范围,特别是在路径规划问题中,其中使用机器学习进行预测性维护和聚类,其结果被输入到数学模型中,以创建最佳路径。同样,机器学习模型也可以从数学模型中学习。通过数学模型获得的决策变量的初始值可以用于机器学习模型,不仅预测决策变量的最佳值,还可以帮助加速优化算法的性能。
ML 优化是通过使用一系列技术的算法来优化机器学习模型。优化过程寻找最有效的配置或超参数集,以使模型适应特定的使用案例(数据集)或商业问题。
总结来说,机器学习(ML)是数据驱动的,而优化是算法驱动的。每个机器学习模型都遵循最小化成本函数的原则;因此,优化在其核心上是一个超集。
本章包含以下主题:
-
优化机器学习模型
-
运筹学中的优化
-
演化优化
下一节将探讨用于优化机器学习模型的各种方法和技术,以便找到最佳的超参数集。
优化机器学习模型
优化的概念是机器学习模型的核心。机器学习有助于进行聚类、检测异常、从历史数据预测未来等。然而,当涉及到最小化业务成本、寻找业务设施的最佳位置等问题时,我们所需要的是一个数学优化模型。
本节将讨论机器学习中的优化问题。优化确保了机器学习模型的结构和配置尽可能有效,以实现其建立的目标。优化技术可以自动化测试不同的模型配置。最佳配置(超参数集)具有最小的误差范围,从而为给定的数据集提供最准确的模型。对于机器学习模型,正确的超参数优化可能是一个繁琐的过程,因为无论是未优化(欠拟合)还是过度优化(过拟合)的模型都会失败。过拟合是指模型过于贴合训练数据,导致在新数据上的预测不准确。欠拟合是指模型训练不足,使其在训练数据和新数据上都表现不佳。超参数可以通过手动搜索获得,这是一种通过反复试验进行的繁琐方法。欠拟合、最佳拟合和过拟合的模型在图 9.1中进行了说明,如下所示:

图 9.1:未优化(L)和过度优化(R)模型拟合
优化的主要技术包括随机搜索、网格搜索超参数和贝叶斯优化,这些都将在接下来的小节中讨论。
随机搜索
随机抽样搜索空间并确定最有效的超参数配置集的过程叫做随机搜索。随机搜索技术可以发现新的超参数组合,从而优化机器学习模型。搜索过程中的迭代次数需要设置,这限制了这些新组合的数量,否则过程会变得更加耗时。它是一个高效的过程,因为它用随机性替代了穷举搜索。搜索空间可以被看作是一个空间中的体积,每个维度代表一个超参数,体积中的每个点或向量代表一个模型配置。优化过程包括定义搜索空间。
搜索空间是 Python 代码中的一个字典,scikit-learn 库提供了调节模型超参数的函数。这里提供一个分类模型的随机搜索代码示例:
import pandas as pd
from scipy.stats import loguniform
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.model_selection import RandomizedSearchCV
dataframe = pd.read_csv('sonar.csv')
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
#Model
model = LogisticRegression()
cv = RepeatedStratifiedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
#Define search space
space = dict()
space['solver'] = ['newton-cg', 'lbfgs', 'liblinear']
space['penalty'] = ['none', 'l1', 'l2', 'elasticnet']
space['C'] = loguniform(1e-5, 100)
search = RandomizedSearchCV(model, space, n_iter = 500, scoring = 'accuracy',
n_jobs = -1, cv = cv, random_state = 1)
result = search.fit(X, y)
print('Best Score: %s' % result.best_score_)
print('Best Hyperparameters: %s' % result.best_params_)
使用的数据集是一组由在各种条件下将声纳信号反射到金属圆柱体上获得的 60 个模式。每个模式是一组介于 0.0 和 1.0 之间的数字,每个数字代表在一段时间内集成的频率带内的能量。每个记录关联的标签要么是R(表示物体为岩石),要么是M(表示物体为金属圆柱体或地雷)。数据可以在 GitHub 仓库中找到:github.com/ranja-sarkar/dataset。
还提供了一个用于线性回归模型的随机搜索示例代码。保险数据集包含两个变量,即索赔数量和所有索赔的总支付金额(以瑞典克朗计),可以在 GitHub 仓库中找到:github.com/ranja-sarkar/dataset。
回归任务和分类任务的区别在于选择模型的性能评分协议。scikit-learn Python 库中的超参数优化方法假设好的性能评分为接近零的负值(对于回归任务),其中零表示完美的回归模型:
import pandas as pd
from scipy.stats import loguniform
from sklearn.linear_model import Ridge
from sklearn.model_selection import RepeatedKFold
from sklearn.model_selection import RandomizedSearchCV
df = pd.read_csv('auto-insurance.csv')
data = df.values
X, y = data[:, :-1], data[:, -1]
#Model
model = Ridge()
cv = RepeatedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
#Define search space
space = dict()
space['solver'] = ['svd', 'cholesky', 'lsqr', 'sag']
space['alpha'] = loguniform(1e-5, 100)
space['fit_intercept'] = [True, False]
space['normalize'] = [True, False]
search = RandomizedSearchCV(model, space, n_iter = 500, scoring = 'neg_mean_absolute_error', n_jobs = -1, cv = cv, random_state = 1)
result = search.fit(X, y)
print('Best Score: %s' % result.best_score_)
print('Best Hyperparameters: %s' % result.best_params_)
代码的运行时间取决于搜索空间的大小和系统处理器的速度。代码中的result类提供了结果,其中最重要的值是最佳得分,用于表示模型和超参数的最佳表现。知道最佳的超参数集后,可以定义一个新模型,将超参数设置为已知值,并在可用数据上拟合该模型。然后,这个模型可以用于对新数据进行预测。参数空间中随机配置的数量类似于图 9.2,该图表明随机搜索在低维数据中表现最佳:

图 9.2:随机搜索
在下一小节中,我们将详细阐述网格搜索在分类和回归模型优化中的应用。
网格搜索
评估机器学习模型已知超参数值有效性的过程是网格搜索。每个超参数在搜索空间的网格上表示为一个维度,网格中的每个点都要进行搜索和评估。网格搜索非常适合检查直观的猜测和已知在一般情况下表现良好的超参数组合。如前所述,优化过程涉及定义搜索空间(在 Python 中为字典),可以将其视为一个体积,其中每个维度表示一个超参数,每个点(向量)表示一个模型配置。这里必须定义一个离散网格。换句话说,网格搜索空间取离散值(可以是对数尺度),而不是随机搜索空间中使用的对数均匀分布。
以下给出一个使用相同数据集(sonar.csv)进行分类模型网格搜索的示例代码,该数据集也用于随机搜索算法:
import pandas as pd
from scipy.stats import loguniform
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.model_selection import GridSearchCV
dataframe = pd.read_csv('sonar.csv')
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
#Model
model = LogisticRegression()
cv = RepeatedStratifiedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
#Define search space
space = dict()
space['solver'] = ['newton-cg', 'lbfgs', 'liblinear']
space['penalty'] = ['none', 'l1', 'l2', 'elasticnet']
space['C'] = [1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 10, 100]
search = GridSearchCV(model, space, scoring = 'accuracy', n_jobs = -1, cv = cv)
result = search.fit(X, y)
print('Best Score: %s' % result.best_score_)
print('Best Hyperparameters: %s' % result.best_params_)
以下提供了一个使用相同数据集(auto-insurance.csv)进行线性回归模型网格搜索的示例代码,该数据集也用于随机搜索算法。可以比较使用随机搜索和网格搜索算法为该数据集获得的最佳超参数,以估算哪种算法对该数据集表现更好:
import pandas as pd
from sklearn.linear_model import Ridge
from sklearn.model_selection import RepeatedKFold
from sklearn.model_selection import GridSearchCV
df = pd.read_csv('auto-insurance.csv')
data = df.values
X, y = data[:, :-1], data[:, -1]
#Model
model = Ridge()
cv = RepeatedKFold(n_splits = 10, n_repeats = 3, random_state = 1)
#Define search space
space = dict()
space['solver'] = ['svd', 'cholesky', 'lsqr', 'sag']
space['alpha'] = [1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 10, 100]
space['fit_intercept'] = [True, False]
space['normalize'] = [True, False]
search = GridSearchCV(model, space, scoring = 'neg_mean_absolute_error', n_jobs = -1, cv = cv)
result = search.fit(X, y)
print('Best Score: %s' % result.best_score_)
print('Best Hyperparameters: %s' % result.best_params_)
使用随机搜索和网格搜索获得的分类和回归模型数据集的分数几乎相同。针对给定数据集选择优化技术取决于具体的应用场景。虽然在某些情况下,随机搜索可能会导致更好的性能,但它需要更多的时间,而网格搜索则适用于快速搜索那些通常表现良好的超参数。超参数的值像矩阵一样排列,如图 9**.3所示,类似于一个网格:

图 9.3:网格搜索
另一种方法,称为贝叶斯优化,其搜索过程不同于前述两种方法,将在以下小节中讨论。
贝叶斯优化
使用概率进行全局优化的有向迭代方法是贝叶斯优化。这是一个高斯过程,对于连续超参数的优化收敛速度较快,即在连续搜索空间中(图 9**.4)。在贝叶斯优化中,会构建一个函数的概率模型,并将超参数映射到在验证数据集上评估的目标。该过程基于当前模型评估一个超参数配置,然后更新模型,直到达到最优点,并尽量在最少的步骤中找到全局最优解。在大多数情况下,它比随机搜索更高效、有效。优化景观(多个局部极小值)与一个全局极小值的示例如下:

图 9.4:优化景观(响应面)
贝叶斯优化结合了关于目标函数的先验信念(边际概率),并通过从该函数中抽取样本来更新先验信念,从而获得一个后验信念(条件概率),该后验信念能更好地逼近该函数,如图 9**.5所示。该过程会不断重复,直到找到目标函数的极值点或资源耗尽:

图 9.5:贝叶斯统计
贝叶斯搜索通常在数据量大、学习速度慢且需要最小化调优时间时非常有益。scikit-optimize库提供了用于机器学习模型贝叶斯优化的函数。以下是一个在分类问题中使用贝叶斯方法进行超参数调优的示例代码:
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from skopt.space import Integer
from skopt.utils import use_named_args
from skopt import gp_minimize
#Generate classification dataset
X, y = make_blobs(n_samples = 500, centers = 3, n_features = 2) ##3 class labels in data
#Model kNN
model = KNeighborsClassifier()
#Define search space
search_space = [Integer(1, 5, name = 'n_neighbors'), Integer(1, 2, name = 'p')]
@use_named_args(search_space)
def evaluate_model(**params):
model.set_params(**params)
result = cross_val_score(model, X, y, cv = 5, n_jobs = -1, scoring = 'accuracy')
estimate = np.mean(result)
return 1.0 – estimate
#Optimize
result = gp_minimize(evaluate_model, search_space)
print('Accuracy: %.3f' % (1.0 - result.fun))
print('Best Parameters: n_neighbors = %d, p = %d' % (result.x[0], result.x[1]))
用于逼近目标函数的模型称为代理模型,而后验概率则是一个代理目标函数,可用于估算候选样本的成本。后验概率用于从搜索空间中选择下一个样本,执行此操作的技术称为获取函数。贝叶斯优化在函数评估成本较高或目标函数形式复杂(非线性、非凸、高维或噪声很大)时效果最佳——例如,在深度神经网络中。
优化过程通过减少机器学习模型预测中的错误或损失,从而提高模型的准确性。机器学习的基本前提依赖于某种形式的函数优化,以便将输入几乎精确地映射到预期的输出。
在接下来的部分,我们将学习运筹学中的数学优化。
运筹学中的优化
运筹学这一术语源于第一次世界大战,当时英国军方将一群科学家聚集在一起,旨在最有效地分配不足的资源(如食物、药品等)到不同的军事行动中。因此,这一术语暗示了优化,即在约束条件下最大化或最小化目标函数,通常用于复杂问题和高维度问题。运筹问题通常包括规划工作班次或为大组织创建时间表、为大商店设计客户设施、选择可用资金的投资、供应链管理和库存管理,这些都可以通过一系列变量及其关系来表述或形式化为数学问题。
在运筹学中,商业问题被映射到一个更低层次的通用问题,该问题足够简洁,可以用数学符号来描述。这些通用问题反过来可以使用更高层次的语言来表达;例如,资源和活动用于描述调度问题。更高层次的语言是问题特定的,因此通用问题可以使用建模范式来描述。建模范式是一组规则和实践,允许使用更低层次的数据结构(如矩阵)来表示更高层次的问题。这些数据结构或矩阵会传递到最后一步的抽象层次,即算法。最突出的建模范式包括线性规划、整数规划和混合整数规划,所有这些方法都使用线性等式约束。有一系列算法用于解决这些线性规划问题。搜索算法(如分支限界法)用于解决整数规划问题,而单纯形算法则用于线性规划建模范式。
以下数据演示了如何通过优化来解决背包问题(图 9.6a 和 9.6b):

图 9.6a:背包问题
假设约束条件是背包只能携带最大 2.9 公斤的物品,而所有物品的总重量为 3.09 公斤。物品的价值有助于选择最优的物品数量。随着物品数量的增加,问题变得更加复杂,通过尝试所有可能的物品组合来求解,所需时间会显著增加:

图 9.6b:具有另一个变量的背包问题
目标函数是价值,必须最大化。必须选择最佳物品,以满足总重量不超过 2.9 公斤的约束条件。使用求解器(在此情况下为 pulp)来解决此线性规划问题,代码如下所示。决策变量(待确定)由
给出。若选择该物品,变量为 1;若未选择该物品,变量为 0:
from pulp import *
#value per weight
v = {'Sleeping bag': 4.17, 'Pillow': 5.13, 'Torch': 10.0, 'First Aid Kit': 8.0, 'Hand sanitiser': 2.0}
#weight
w = {'Sleeping bag': 1.2, 'Pillow': 0.39, 'Torch': 0.5, 'First Aid Kit': 0.5, 'Hand sanitiser': 0.5}
limit = 2.9
items = list(sorted(v.keys()))
# Model
m = LpProblem("Knapsack Problem", LpMaximize)
# Variables
x = LpVariable.dicts('x', items, lowBound = 0, upBound = 1, cat = LpInteger)
#Objective
m += sum(v[i]*x[i] for i in items)
#Constraint
m += sum(w[i]*x[i] for i in items) <= limit
#Optimize
m.solve()
#decision variables
for i in items:
print("%s = %f" % (x[i].name, x[i].varValue))
执行此代码时,将产生以下输出:
x_First_Aid_Kit = 1.0
x_Hand_sanitizer = 0.0
x_Pillow = 1.0
x_Sleeping_bag = 1.0
x_Torch = 1.0
根据结果(最优解),背包中不应该携带手部消毒液。这是一个简单的整数规划问题,因为决策变量被限制为整数。以类似的方式,其他实际的商业问题,如生产规划,都是通过数学优化来解决的,在这些问题中,选择合适的资源来最大化利润等。当运筹学与机器学习预测相结合时,数据科学实际上转变为决策科学,帮助组织做出可行的决策。
在下一节中,我们将学习关于进化优化的内容,这受到自然界中观察到的优化过程的启发,例如物种迁移、鸟群和蚂蚁群。
进化优化
进化优化利用模仿自然界内选择过程的算法。例如通过自然选择优化的遗传算法。每次超参数值的迭代就像是遗传学中的突变,进行评估和改变。这个过程通过重新组合的选择一直持续,直到达到最有效的配置。因此,每一代在优化中都会随着每次迭代而改进。遗传算法经常用于训练神经网络。
进化算法通常包括三个步骤:初始化、选择和终止。更适应的生成会像自然选择那样生存和繁殖。一般来说,在问题的约束条件下随机创建一个广泛范围的解初始种群。种群包含了解决问题的任意数量的可能解,或者这些解大致集中在被认为是理想解的周围。然后根据适应度(或目标)函数对这些解进行评估。一个好的适应度函数能够代表数据并计算一个解对特定问题可行性的数值。一旦计算出所有解的适应度,就会选择得分最高的解。可能存在多个适应度函数导致多个最优解,这时会使用决策者根据一些关键指标缩小到一个单一问题特定解。图 9**.7 描述了这些算法的步骤如下:

图 9.7: 进化算法的步骤
最优解会成为算法的下一代。这些解通常具有上一代解的特征混合。新的遗传材料被引入到这个新的代中,从数学角度来说,这意味着引入新的概率分布。这一步骤是突变,没有它很难达到最优结果。最后一步是终止,当算法达到性能阈值或最大迭代次数(运行时间)时。然后选择并返回最终解。
进化算法是一种基于启发式方法的问题解决方法,使用确定性方法耗时过长。它是一种随机搜索技术,通常应用于组合问题或与其他方法一起快速找到最优解。
摘要
在这一章中,我们了解了优化技术,尤其是那些用于机器学习的技术,旨在找到适合数据集的机器学习模型的最有效超参数配置。优化后的机器学习模型具有最小的错误,从而提高预测的准确性。没有优化,就没有模型的学习或发展。
我们讨论了在运筹学中使用的优化算法,以及在深度学习模型优化和更复杂问题的网络建模中应用的进化算法。
在本书的最后一章,我们将学习如何选择标准技术来优化机器学习模型。对于给定的问题,可能存在多个最优解,并且可能有多种优化技术可以达到这些解。因此,在构建模型并解决相关业务问题时,选择合适的技术至关重要。
第十章:机器学习的优化技术
我们在上一章中讨论了数学优化技术及其在商业问题中的必要性,这些问题需要最小化成本(误差)函数,以及在预测建模中,机器通过历史数据学习来预测未来。在机器学习(ML)中,成本是需要最小化的损失函数或能量函数。在大多数情况下,知道应该为给定的机器学习模型选择哪种优化算法是具有挑战性的。优化是一个迭代过程,旨在最大化或最小化目标函数,在每一步之间,总是存在一个折衷,即所采取的迭代步骤数量与达到下一步所需的计算难度之间的权衡。本章提供了如何根据问题(因此也就是目标)选择优化算法的提示。优化算法的选择取决于多个因素,包括要解决的具体问题、相关数据集的大小和复杂性,以及可用的资源,如计算能力和内存。
直接搜索和随机搜索算法是为目标函数的导数不可用时设计的。严格来说,优化算法可以分为使用导数的和不使用导数的两类。依赖于梯度下降的优化算法快速且高效;然而,它们需要目标函数具有良好的行为才能有效工作。如果函数具有复杂的特性,我们可以采用穷举搜索,但这需要极长的时间(图 10.1)。有些比梯度下降更复杂的优化方法,如遗传算法(GAs)和模拟退火,这些方法需要比梯度下降更多的计算时间和步骤,但即使在非常难以找到的情况下,它们仍然能够发现最优点。

图 10.1:优化算法的性能
优化算法可以是无导数的,也可以是基于梯度的。用于机器学习模型的优化算法一般可以分为两类:一类是使用目标函数的一阶导数(称为梯度),另一类是使用目标函数的二阶导数(称为 Hessian 矩阵)来进行搜索。
本章涵盖以下主题:
-
常规优化算法
-
复杂优化算法
复杂的优化算法包括可微分和不可微分的函数。接下来的两个部分将介绍常规优化算法和复杂优化算法的示例。
常规优化算法
在机器学习中,最常见的优化问题是连续函数优化,其中函数的输入参数是(实数)数值。在训练机器学习模型时,优化的目标是最小化损失函数,直到它达到或收敛到一个局部最小值(值)。
全局优化使用整个搜索域,而局部优化仅探索邻域,这需要知道初步的近似值,如图 10.2a所示。如果目标函数具有局部最小值,那么局部搜索算法(例如梯度法)也可能会困在某个局部最小值中。如果算法达到了局部最小值,那么几乎不可能在搜索空间中找到全局最小值。在离散搜索空间中,邻域是一个有限集合,可以被完全探索,而在连续搜索空间中,目标函数是可微分的(例如梯度方法、类似牛顿法的算法)。

图 10.2a:局部最小值与全局最小值
函数可能是离散性质的,取离散变量,且出现在组合优化问题中(例如旅行商问题(TSP),如图 10.2b所示),其中可行解也是离散的。一般而言,在连续空间中搜索最优解比在离散空间中搜索更高效。

图 10.2b:TSP 是一个组合优化问题
括号法算法是一种优化算法,具有一个输入变量,且已知最优解存在于特定范围内。它们假设在已知的搜索空间范围内存在一个单一的最优解(单峰目标函数)。当导数信息不可用时,这些算法有时甚至可以被使用。二分法优化就是其中一个例子。
具有多个输入变量的优化算法是局部下降算法。局部下降过程涉及在搜索空间中选择一个方向,然后沿所选方向在一条线或超平面上执行括号搜索。局部下降也称为线搜索算法;然而,在搜索空间中优化每次方向移动的计算开销较大。梯度下降是线搜索算法的经典例子。
按照是否使用梯度(一级导数)或梯度的梯度(二级导数)信息在搜索空间中移动以找到最优点的方式对算法进行分组,接下来的子章节将对此进行讨论。
一阶算法
目标函数的一阶导数(梯度或斜率)用于一阶优化算法。一阶算法通常称为梯度下降(或最速下降法)。与梯度下降不同,正则化算法使用预定义的目标函数。机器学习模型通过最小化目标(损失函数)进行学习,当模型发生过拟合时,会在此基础上使用正则化方法。
在搜索空间中,梯度是通过步长(称为学习率)计算的,学习率是一个超参数,控制着在空间中移动的距离(图 10.3)。如果步长过小,则会导致寻找最优点的时间过长;如果步长过大,则可能完全错过最优点。优化算法有许多超参数,如学习率,这些超参数会对机器学习模型的性能产生重大影响。

图 10.3: 梯度下降中的学习率
梯度下降的变体包括批量梯度下降、迷你批量梯度下降和随机梯度下降(SGD)。批量梯度下降是针对整个训练数据集(所有训练样本)计算梯度,而 SGD 则是针对每个训练样本计算梯度。迷你批量则是针对每个(迷你)子集的训练样本进行更新,因此能够兼顾两者的优点。批量梯度下降可能非常慢,而迷你批量梯度下降则效率很高。迷你批量梯度下降非常适合处理大规模数据的问题。SGD 会进行频繁更新,因此目标函数波动较大,但它能够更好地收敛到最优解。SGD 常用于训练人工神经网络。

图 10.4: 一阶算法(梯度下降)示例
对梯度下降优化过程的小幅扩展产生了几种算法,例如动量法、自适应梯度(AdaGrad)和自适应矩估计(Adam)。例如,动量法是一种帮助加速 SGD 沿相关方向收敛的技术(图 10.4),从而实现更快的收敛。像 AdaGrad 和 Adam 这样的算法为每个参数计算自适应学习率,帮助函数快速收敛。然而,Adam 可能是处理稀疏数据时的最佳选择。Adam 同时使用梯度和梯度的二阶矩。AdaGrad 适用于数据噪声非常大以及目标函数条件不良的问题;即,目标函数的不同维度具有不同的尺度。
二阶算法
目标函数的二阶导数(海森矩阵)用于二阶优化算法,前提是可以计算或近似计算海森矩阵(曲率)。这些算法用于具有单一实变量的一元目标函数,其中少数优化时显示出最小值或最大值,但在其定义域(搜索空间)中为鞍点。牛顿法是二阶优化算法的一个例子。在优化中,梯度下降(一阶)与牛顿法(二阶)的比较如图10**.5所示。

图10**.5:梯度下降(绿色)和牛顿法(红色),在极小学习率条件下从
到
寻找路径。
这些算法在神经网络中表现更好;然而,当维度或参数数量巨大时,计算和存储变得具有挑战性。为了成功使用二阶算法,必须简化矩阵,通常是通过简化海森矩阵的形式来完成。
接下来的章节详细阐述了目标函数的可微性,这决定了在给定问题中选择一般(在本节讨论的)或复杂的优化算法。
复杂的优化算法
目标函数的性质有助于选择用于优化给定业务问题的算法。关于函数的信息越多,优化函数就越容易。最重要的是,目标函数可以在搜索空间中的任何点处被微分。
目标函数的可微性
可微的目标函数是指在输入空间中的任意点都可以计算出导数的函数。导数(斜率)是该点函数变化的速率。海森矩阵是函数导数变化的速率。微积分帮助分析优化简单可微函数。对于可微的目标函数,使用基于梯度的优化算法。然而,有些目标函数的导数无法计算,通常是非常复杂(噪声多、多模态等)的函数,这些被称为非可微目标函数。也可能有不连续的目标函数,其导数只能在搜索空间的某些区域计算。随机和群体算法处理此类函数,因此有时被称为黑盒算法。
当目标函数的解析形式不可得时,一般使用基于仿真的优化方法。下一小节简要介绍了在使用经典方法找到可行解具有挑战性时,考虑的算法,这些算法要么计算,要么基于关于目标函数导数的假设进行构建。
直接法与随机算法
直接法与随机优化算法用于目标函数的导数未知或无法计算的情况,即搜索空间是离散的。前者是确定性的,假设目标函数是单峰的(即只有一个全局最优解)。直接搜索通常被称为模式搜索,因为它通过几何形状有效地在搜索空间中导航。梯度信息通过目标函数进行近似,并用于启动搜索空间中的线性搜索,最终(通过重复的线性搜索)确定最优解区域。Powell 方法是直接搜索算法的一个例子。它是一种无梯度方法,因为它优化的函数是不可微分的。
另一方面,随机算法利用全局搜索中的随机性,这也就是“随机”名称的来源。它们通常涉及对目标函数的抽样,可以处理具有误导性局部极值的问题。模拟退火(图 10.6)是一个随机搜索算法的例子,即全局优化,它偶尔会接受较差的初始配置。模拟退火是一种概率技术,用于解决无约束和有约束的优化问题。它是一种元启发式方法,用于在物理过程的大搜索空间中近似全局优化,其中系统能量最小化。

图 10.6:模拟退火是一种随机优化算法
像遗传算法(GAs)这样的群体优化算法也是随机的,通常用于具有多个全局极值和不平滑(高度噪声)的多峰目标函数。这些算法维持一个候选解的群体,从而增强了搜索的鲁棒性,提高了克服局部极值的可能性。它们的效率对描述问题时所使用的变量非常敏感。与其他启发式算法一样,进化算法有许多自由度,因此很难调整以获得良好的模型性能。
遗传算法(GA)借鉴了进化的类比,通过保持群体中个体的数量均衡来进行进化。这些个体构成一代人,并通过随机选择一对个体来生成新的一代,其中较适应的个体更可能被选中。GA 被用于通过初始化(种群)、适应度分配(给个体分配适应度)和选择最佳(重组)解决方案来解决复杂的优化问题。一个庞大的研究社区正在致力于遗传算法的研究,以便在大多数实际问题中加以利用。
总结
在本章中,我们了解了在优化机器学习模型时,应该考虑哪种优化算法来最小化(连续)目标函数,这些函数通常出现在机器学习模型中。此类模型具有对输入变量的实值评估,并涉及局部搜索。目标函数的可微性可能是考虑优化算法类型时最重要的因素。
本章并没有列出完整的优化机器学习模型的算法清单,但捕捉了主要算法的精髓以及它们的基本行为,并通过示例加以说明。还涉及了确定性优化和随机优化的概念,后者包括遗传算法(GA),其在现实世界问题中的应用正在不断发展。
结语
本书主要面向职业生涯初期的数据科学家。假设本书的读者已经具备线性代数、基础统计学、微分方程、基本数值算法、数据类型和数据结构的知识。话虽如此,我们必须意识到,将业务问题转化为数学公式是一门艺术。
在探索数据科学的世界时,理解经典数学模型的相关性非常重要,并了解如何将其与机器学习模型结合使用,以解决通常复杂的业务问题。混合(综合)模型可以促进更好的决策,尤其在敏感领域的高风险决策中至关重要。数学优化通常能够提升机器学习模型,帮助最佳地解释决策变量与相关数据、商业目标之间的关系,以及找到业务问题的最佳解决方案。然而,简单的(纯粹的或未混合的)模型往往更具可解释性,而在构建复杂模型时,我们需要考虑效率和成本等因素。
我想通过向以下领域专家表示感谢并衷心致谢来结束本书:
-
Brandon Rohrer (
github.com/brohrer) -
Sebastian Raschka (
github.com/rasbt) -
Jason Brownlee (
www.linkedin.com/in/jasonbrownlee/)
他们的在线文章、书籍、课程和教程/博客激励我去学习、重新学习,并深入探索数据科学和数学优化的世界。我的学习和工作经验最终在这本书中得到了体现。


浙公网安备 33010602011771号