机器学习的数学指南-全-

机器学习的数学指南(全)

原文:annas-archive.org/md5/8e7afdad3bbf3a40e1422439a336f979

译者:飞龙

协议:CC BY-NC-SA 4.0

第一章:介绍

为什么我要学习数学?——这是我每天都会被问到的问题。

其实,你不必学习。但你应该学!

表面上看,先进的数学对于生产环境中的软件工程和机器学习没有什么影响。你不需要手动计算梯度、解线性方程或找特征值。基本和高级算法已经被抽象成库和 API,它们为你完成了所有的繁重工作。

现在,实现一个最先进的深度神经网络几乎等同于在 PyTorch 中实例化一个对象,加载预训练的权重,然后让数据通过模型快速流动。就像所有技术进步一样,这也是一把双刃剑。一方面,加速原型设计和开发的框架让机器学习得以在实践中应用。没有这些框架,我们就无法看到过去十年中深度学习的爆发。

另一方面,高级抽象是我们与底层技术之间的障碍。用户层面的知识仅在我们走熟悉的道路时才足够。(或者直到某些东西出问题了。)

如果你还不相信,那我们来做一个思想实验吧!想象一下,搬到一个新国家,没有语言能力,也不了解当地的生活方式。不过,你有一部智能手机和一个可靠的互联网连接。

如何开始探索呢?

有了 Google Maps 和一张信用卡,你可以在那儿做很多了不起的事情:探索城市,去优秀的餐馆吃饭,享受美好时光。你可以每天购物,而无需说一个字:只需将东西放进篮子里,在收银台刷卡支付。

几个月后,你也会开始学一些语言——比如简单的问候语或自我介绍。你已经有了一个良好的开端!

有一些内置的解决方案可以处理日常任务,它们工作得非常顺利——比如食品订购服务、公共交通等。然而,到了某个时刻,它们会出现故障。例如,你需要联系那个把包裹送到错门的快递员。或者,如果你的租车坏了,你需要寻求帮助。

你可能还想做更多的事情。找一份工作,或者甚至开创自己的事业。为此,你需要有效地与他人沟通。

当你打算在某个地方待几个月时,学习语言是没必要的。然而,如果你打算在那里度过余生,学习语言就是你能做出的最佳投资之一。

现在,把国家换成机器学习,把语言换成数学。

事实上,算法是用数学语言编写的。要精通算法,你必须懂得这门语言。

这本书是关于什么的?

“知道如何在一个城市中找到路和精通一门学科之间有相似之处;从任何一个点出发,都应该能够到达任何其他的地方。如果能立即找到从一个地方到另一个地方最方便、最迅捷的路径,那就更好了。”

— 乔治·波利亚与加博尔·谢格,在传奇书籍《分析中的问题与定理》的序言中

上述引用是我最喜欢的之一。对我来说,它意味着知识建立在许多支柱之上。就像一把椅子有四条腿,一个全面的机器学习工程师也有广泛的技能组合,使他们能够在工作中发挥有效作用。我们每个人都专注于一个平衡的技能星座,而数学对许多人来说是一个很好的补充。你可以在没有高级数学的情况下开始机器学习,但在你职业生涯的某个阶段,了解机器学习的数学背景将帮助你将技能提升到一个新的水平。

深度学习的精通之路有两条。一条从实际部分开始,另一条从理论开始。两者都是完全可行的,最终它们会交织在一起。这本书是为那些已经开始走实际应用路线的人准备的,比如数据科学家、机器学习工程师,甚至对这个话题感兴趣的软件开发者。

这本书不是一部 100%纯粹的数学论文。在某些地方,我会做一些简化,以平衡清晰性与数学准确性。我的目标是让你获得“ Eureka!”的瞬间,并帮助你理解更宏大的图景,而不是为你准备数学博士学位。

我读过的大多数机器学习书籍都属于以下两类之一。

  1. 专注于实际应用,但在数学概念上不清晰且不精确。

  2. 专注于理论,涉及大量的数学,但几乎没有实际应用。

我希望这本书能够兼顾两种方法的优点:既有基本和高级数学概念的扎实介绍,又始终紧扣机器学习的应用。

我的目标不仅仅是涵盖基础知识,而是提供广泛的知识。在我的经验中,要精通一门学科,既需要深入,也需要广泛。仅仅覆盖数学的最基本内容就像走钢丝一样。与其每次遇到数学课题时都像在表演杂技,我希望你能够打下稳固的基础。这样的自信能带你走得更远,并让你在人群中脱颖而出。

在我们的旅程中,我们将遵循一条带领我们穿越的路线图

  1. 线性代数,

  2. 微积分,

  3. 多变量微积分,

  4. 概率论。

我们将从线性代数开始我们的旅程。在机器学习中,数据通过向量表示。训练学习算法就等于通过一系列变换来找到数据的更具描述性的表示。

线性代数是研究向量空间及其变换的学科。

简单来说,神经网络就是一个将数据映射到高级表示的函数。线性变换是这些变换的基本构建块。深入理解它们将大有裨益,因为它们在机器学习中无处不在。

虽然线性代数展示了如何描述预测模型,微积分则提供了将这些模型拟合到数据中的工具。当你训练神经网络时,几乎可以肯定是在使用梯度下降法,这是一种源于微积分和微分研究的技术。

除了微分,其“逆运算”也是微积分的核心部分:积分。积分表示一些重要量,如期望值、熵、均方误差等。它们为概率论和统计学奠定了基础。

然而,在进行机器学习时,我们处理的是包含数百万个变量的函数。在更高维度中,情况有所不同。这就是多变量微积分派上用场的地方,其中微分和积分被调整以适应这些空间。

在掌握了线性代数和微积分之后,我们已经准备好描述和训练神经网络。然而,我们仍然缺乏从数据中提取模式的理解。我们如何从实验和观察中得出结论?我们如何描述和发现其中的模式?这些问题通过概率论和统计学得以解答,它们是科学思维的逻辑。在最后一章中,我们扩展了经典的二元逻辑,学习如何处理预测中的不确定性。

如何阅读本书

数学遵循定义-定理-证明的结构,这可能一开始很难理解。如果你不熟悉这种思维流程,不用担心,我会在接下来做一个温和的介绍。

本质上,数学是通过数学对象的基本性质来研究抽象对象(例如函数)。与经验观察不同,数学是基于逻辑的,因此具有普遍性。如果我们想要使用逻辑这一强大工具,数学对象需要被精确定义。定义通常会像下面这样以框框的形式呈现。

定义 1.(一个示例定义)

定义通常以这种方式呈现。

给定一个定义,结果通常以“如果 A,则 B”的形式表述,其中 A 为前提,B 为结论。这样的结果称为定理。例如,如果一个函数是可微的,那么它也是连续的;如果一个函数是凸的,那么它有全局最小值;如果我们有一个函数,那么我们可以通过单层神经网络以任意精度近似它。你已经掌握了这种模式。定理是数学的核心。

我们必须提供合理的逻辑推理来接受命题的有效性,这种推理将结论从前提中推导出来。这被称为证明,是数学学习曲线陡峭的原因之一。与其他科学学科不同,数学中的证明是不可争议的陈述,永远铭刻在石上。实际操作中,注意这些框框。

定理 1.(一个示例定理)

设 x 为一个复杂的数学对象。以下两个陈述成立。

(a 如果 A,则 B。

(b)如果 C 和 D,则 E。

证明。证明过程如下。

为了增强学习体验,我会经常把一些有用但不是绝对必要的信息放入备注中。

备注 1。(一个令人兴奋的备注)

数学太棒了。你将因此成为一个更好的工程师。

最有效的学习方式是通过构建事物并将理论付诸实践。在数学中,这就是唯一的学习方式。这意味着你需要仔细阅读文本。不要因为某些东西被写下来就认为它是理所当然的。逐句思考每个句子。把每个论点和计算拆解开来。在阅读证明之前,尽量自己证明定理。

记住这一点,让我们开始吧!系好安全带,路途漫长且充满了曲折与转折。

使用的约定

本书中使用了若干文本约定。CodeInText 表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名或 URL。例如:“切片通过指定第一个和最后一个元素以及一个可选的步长来实现,语法为 object[first:last:step]。”

代码块设置如下:

from sklearn.datasets import load_iris 
data = load_iris() 
X, y = data["/span>data, data["/span>target 
X[:10]

任何命令行输入或输出如下所示:

(3.5, -2.71, 'a string')

斜体表示新概念或强调。例如,菜单或对话框中的词汇在文本中通常以这种方式出现。例如:“这是我们第一个非可微函数的例子。”

本书内容

第一章,向量和向量空间涵盖了向量是什么以及如何使用它们。我们将从具体的例子出发,通过精确的数学定义到实现,理解向量空间和用于高效表示向量的 NumPy 数组。除了基础知识,我们还将学习

第二章,向量空间的几何结构通过研究范数、距离、内积、角度和正交性等概念向前推进,为向量空间的代数定义增添了急需的几何结构。这些不仅是可视化的工具,它们在机器学习中也扮演着至关重要的角色。我们还将遇到我们的第一个算法——Gram-Schmidt 正交化方法,它可以将任何一组向量转换为正交标准基。

在第三章,实践中的线性代数中,我们再次使用 NumPy,实施迄今为止所学的一切。在这里,我们学习如何在实践中使用高性能的 NumPy 数组:操作、广播、函数,最终实现从头开始的 Gram-Schmidt 算法。这也是我们第一次接触到矩阵,线性代数中的工作马。

第四章《线性变换》讲述了矩阵的真正性质;即在向量空间之间的结构保持变换。通过这种方式,原本看似深奥的事物——比如矩阵乘法的定义——突然变得有意义。我们再次从代数结构跃升到几何结构,让我们能够将矩阵视为改变其底层空间的变换。我们还将研究矩阵的一个最重要的描述量:行列式,描述了底层线性变换如何影响空间的体积。

第五章《矩阵与方程》展示了矩阵作为线性方程组的第三种(也是我们最终的)面貌。在这一章中,我们首先学习了如何使用高斯消元法手动求解线性方程组,然后通过我们新学的线性代数知识,对其进行超级强化,得到了强大的 LU 分解。在 LU 分解的帮助下,我们迎难而上,成功实现了大约 70000 倍的行列式计算加速。

第六章介绍了矩阵的两个最重要的描述量:特征值和特征向量。我们为什么需要它们?

因为在第七章《矩阵分解》中,我们能够借助它们达到线性代数的巅峰。首先,我们展示了通过构造基于特征向量的基,实对称矩阵可以被写成对角形式,这就是著名的谱分解定理。反过来,谱分解的巧妙应用导出了奇异值分解,这是线性代数中最重要的结果。

第八章《矩阵与图论》通过研究线性代数与图论之间的丰硕联系,结束了本书的线性代数部分。通过将矩阵表示为图,我们能够展示深刻的结果,比如 Frobenius 标准形,甚至可以讨论图的特征值和特征向量。

在第九章《函数》中,我们将详细研究函数,这是我们迄今为止直觉上使用的一个概念。这一次,我们将直觉用数学的方式精确定义,学习到函数本质上是点与点之间的箭头。

第十章《数字、序列与级数》继续深入探讨数字的概念。从自然数到实数的每一步都代表着一个概念上的飞跃,最终在序列与级数的研究中达到顶峰。

在第十一章《拓扑学、极限与连续性》中,我们几乎达到了最有趣的部分。然而,在微积分中,许多对象、概念和工具通常是通过极限和连续函数来描述的。因此,我们将详细研究这些概念。

第十二章讲述了微积分中最重要的概念:微分。在这一章中,我们学习了函数的导数描述了 1)切线的斜率,2)函数的最佳局部线性逼近。从实际角度来看,我们还研究了导数在各种运算中的行为,最重要的是函数组合,从而得出了反向传播的基础链式法则。

在所有准备工作完成之后,第十三章《优化》介绍了用于训练几乎所有神经网络的算法:梯度下降。为此,我们学习了导数如何描述函数的单调性,以及如何通过一阶和二阶导数来表征局部极值。

第十四章《积分》总结了我们对单变量函数的学习。从直观上讲,积分描述的是函数图形下的(有符号)面积,但经过仔细观察,我们还会发现它实际上是微分的逆运算。在机器学习中(实际上在整个数学领域中),积分描述了各种概率、期望值以及其他基本量。

现在我们已经理解了如何在单变量中进行微积分,第十五章引领我们进入多变量函数的世界,这也是机器学习的领域。在这里,我们有各种各样的函数:标量-向量、向量-标量和向量-向量函数。

在第十六章《导数和梯度》中,我们继续我们的旅程,克服了将微分推广到多变量函数的困难。在这里,我们有三种类型的导数:偏导数、全导数和方向导数;从而得到梯度向量和雅可比矩阵与海森矩阵。

如预期的那样,在多变量中,优化也稍显复杂。这个问题在第十七章《多变量优化》中得到了澄清,在这里我们学习了单变量二阶导数检验的类似方法,并实现了万能的梯度下降的最终形式,完成了我们的微积分学习。

现在我们已经对机器学习有了机械性的理解,第十八章《什么是概率?》向我们展示了如何在不确定性下进行推理和建模。在数学术语中,概率空间是由科尔莫哥洛夫公理定义的,我们还将学习那些让我们能够与概率模型打交道的工具。

第十九章介绍了随机变量和分布,不仅让我们能够将微积分工具引入概率论,还将概率模型压缩为序列或函数。

最后,在第二十章中,我们学习了期望值的概念,通过平均值、方差、协方差和熵来量化概率模型和分布。

为了充分利用本书

本书的代码以 Jupyter notebooks 形式提供,托管在 GitHub 上,链接为 github.com/cosmic-cortex/mathematics-of-machine-learning-book。要运行这些 notebooks,您需要安装所需的包。

安装这些包的最简单方法是使用 Conda。Conda 是一个很棒的 Python 包管理工具。如果您的系统上尚未安装 Conda,可以在此找到安装说明:bit.ly/InstallConda

请注意,Conda 的许可证可能对商业使用有一些限制。安装 Conda 后,请按照本书仓库 README.md 中的环境安装说明进行操作。

下载示例代码文件

本书的代码包托管在 GitHub 上,链接为 github.com/cosmic-cortex/mathematics-of-machine-learning-book。我们还提供了来自我们丰富书籍和视频目录的其他代码包,链接为 github.com/PacktPublishing/。快去看看吧!

下载彩色图片

我们还提供了一个 PDF 文件,包含本书中使用的带有彩色图片的截图/图表。您可以在此下载:packt.link/gbp/9781837027873

联系我们

我们始终欢迎读者的反馈。一般反馈请发送至 [email protected],并在邮件主题中注明书名。如果您有任何关于本书的疑问,请通过 [email protected] 与我们联系。勘误:尽管我们已尽力确保内容的准确性,但错误总会发生。如果您发现书中的错误,我们将非常感激您向我们报告。请访问 www.packtpub.com/submit-errata,点击提交勘误并填写表单。盗版:如果您在互联网上发现我们作品的任何非法复制品,我们将非常感激您提供该位置地址或网站名称。请通过 [email protected] 联系我们,并附上该资料的链接。如果您有兴趣成为作者:如果您在某个领域有专长,并且对写书或参与书籍贡献感兴趣,请访问 authors.packtpub.com

分享您的想法

阅读完《机器学习的数学》后,我们很希望听到您的想法!点击这里直接访问亚马逊评论页面,并分享您的反馈。

您的评价对我们和技术社区都非常重要,它将帮助我们确保提供优质的内容。

下载本书的免费 PDF 副本

感谢购买本书!

喜欢随时随地阅读,却无法携带纸质书籍?或者您的电子书购买与您选择的设备不兼容?

不用担心;现在购买每本 Packt 书籍,您都可以免费获得该书的无 DRM 版 PDF。

随时随地阅读,在任何设备上都能使用。直接将您喜爱的技术书籍中的代码复制、粘贴到您的应用程序中。

福利不仅仅如此!您还可以获得独家的折扣、新闻通讯和每天发送到您邮箱的精彩免费内容。

按照以下简单步骤享受这些福利:

  1. 扫描二维码或访问以下链接:

    PIC

    packt.link/free-ebook/9781837027873

  2. 提交您的购买凭证。

  3. 就这么简单!我们会直接将您的免费 PDF 和其他福利发送到您的电子邮件地址。

第二章:线性代数

本部分包含以下章节:

  • 第一章,向量与向量空间

  • 第二章,向量空间的几何结构

  • 第三章,线性代数在实践中的应用

  • 第四章,线性变换

  • 第五章,矩阵与方程

  • 第六章,特征值与特征向量

  • 第七章,矩阵分解

  • 第八章,矩阵与图形

向量和向量空间

“我想指出的是,抽象线性空间的类并不比元素为数组的空间类大。那么抽象带来了什么好处呢?首先,我们可以使用单一符号表示数组;这样我们可以把向量看作是基本构建块,不受分量的束缚。抽象视角导致结果简单、透明的证明。”

— 彼得·D·拉克斯,在他的书《线性代数及其应用》第一章中

机器学习的数学基础建立在三大支柱之上:线性代数、微积分和概率论。线性代数描述了如何表示和操作数据;微积分帮助我们拟合模型;而概率论则帮助解释这些模型。

这些内容相互依赖,我们将从最基础开始:表示和操作数据。

在本节中,我们将以著名的鸢尾花数据集为指导(en.wikipedia.org/wiki/Iris_flower_data_set)。这包含了三种鸢尾花的测量数据:萼片和花瓣的长度和宽度。每个数据点包括这四个测量值,以及相应的鸢尾花种类:山鸢尾、维吉尼亚鸢尾或变色鸢尾。(萼片是花朵基部的通常是绿色的、类似叶片的结构,在花开放之前保护发育中的芽。花瓣是花朵的色彩鲜艳、柔软的部分,吸引传粉者如昆虫或鸟类。)

数据集可以直接从 scikit-learn(scikit-learn.org/)加载,让我们来看一下!

from sklearn.datasets import load_iris 
data = load_iris() 
X, y = data["/span>data, data["/span>target 
X[:10]
array([[5.1, 3.5, 1.4, 0.2], 
      [4.9, 3\. , 1.4, 0.2], 
      [4.7, 3.2, 1.3, 0.2], 
      [4.6, 3.1, 1.5, 0.2], 
      [5\. , 3.6, 1.4, 0.2], 
      [5.4, 3.9, 1.7, 0.4], 
      [4.6, 3.4, 1.4, 0.3], 
      [5\. , 3.4, 1.5, 0.2], 
      [4.4, 2.9, 1.4, 0.2], 
      [4.9, 3.1, 1.5, 0.1]])

在进入数学定义之前,让我们先建立一个共同的词汇表。测量数据本身以表格格式存储。行代表样本,列代表测量值。特定的测量类型通常称为特征。正如 X.shape 所示,鸢尾花数据集有 150 个数据点和四个特征:

X.shape
(150, 4)

(如果你对 NumPy 不熟悉也不要担心。我们会逐步学习细节。现在,理解数组的形状描述其维度就足够了。)

对于给定的样本,相应的鸢尾花种类称为标签。在我们的情况下,这可以是山鸢尾、维吉尼亚鸢尾或变色鸢尾。这里,标签用数字 0、1 和 2 编码:

y
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
      0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

从数学角度来看,鸢尾花数据集形成一个矩阵,数据点形成向量。简而言之,矩阵是表格,而向量是元组。(元组只是对象的有限和有序序列,例如 (1.297,−2.35,32.3,29.874)。)然而,这种简单化的观点并没有展示出整体图景。向量和矩阵具有美丽的几何和代数结构,探索它们的数学理论使我们能够看到数据背后的模式。

怎么回事呢?假设除了将数据点以紧凑的形式表示外,我们还想对它们进行操作,如加法和标量乘法。为什么我们需要将数据点相加?举个简单的例子,如果特征都在同一尺度上,通常会更有利。如果某个特征的分布尺度比其他特征小,它对预测的影响会较小。

想一想这个场景:如果有人在隔壁房间低声对你说话,而扬声器就在你耳边放着大声的音乐,那么你根本听不到那个人说的任何话。大规模的特征就像那震耳欲聋的音乐,而较小的特征就像那低语。你可能从低语中获得更多信息,但首先你需要把音乐调小。

为了看到这个现象的实际效果,让我们看看数据集特征的分布情况!

import pandas as pd 
import seaborn as sns 
import matplotlib.pyplot as plt 
import numpy as np 

sns.set_theme(style="/span>white rc={"/span>axes.facecolor (0, 0, 0, 0)}) 

# 第三章:Create the data 
x = X.ravel() 
labels = ["/span>sepal length /span>sepal width /span>petal length /span>petal width 
g = np.tile(labels, len(X)) 
df = pd.DataFrame(dict(x=x, g=g)) 

# Initialize the FacetGrid object 
pal = sns.cubehelix_palette(10, rot=-.25, light=.7) 
g = sns.FacetGrid(df, row="/span>g hue="/span>g aspect=10, height=1.5, palette=pal) 

# Draw the densities 
g.map(sns.kdeplot, /span>x bw_adjust=.5, clip_on=False, fill=True, alpha=1, linewidth=1.5) 
g.map(sns.kdeplot, /span>x clip_on=False, color="/span>w lw=2, bw_adjust=.5) 

# Add reference line 
g.refline(y=0, linewidth=2, linestyle="" color=None, clip_on=False) 

# Label each plot 
g.map(lambda x, color, label: plt.gca().text(0, .2, label, fontweight="/span>bold color=color,ha="/span>left va="/span>center transform=plt.gca().transAxes), /span>x 

# Adjust subplots and aesthetics 
g.figure.subplots_adjust(hspace=-.25) 
g.set_titles("#x0022;) 
g.set(yticks=[], ylabel="#x0022;) 
g.despine(bottom=True, left=True) 

plt.show()

PIC

图 1.1:鸢尾花数据集的原始特征

你可以从上图中看到,有些特征的分布较为扩展(如花萼长度),而有些则较为紧凑(如花萼宽度)。在实际应用中,这可能会影响我们算法的预测性能。

为了解决这个问题,我们可以去除数据集的均值和标准差。如果数据集由向量 x[1], x[2], …, x[150] 组成,我们可以通过以下方式计算它们的均值:

 150 μ = -1--∑ x ∈ ℝ4 150 i=1 i

以及它们的标准差

 ┌ ---------------- ││ 1 1∑50 σ = ∘ ---- (xi − μ )2 ∈ ℝ4, 150 i=1

其中在 (x[i] −μ)² 中的减法和平方操作是逐元素进行的。

μ = (μ[1], μ[2], μ[3], μ[4]) 和 σ = (σ[1], σ[2], σ[3], σ[4]) 这些分量是各个特征的均值和方差。(回想一下,鸢尾花数据集包含 150 个样本,每个样本有 4 个特征。)

换句话说,均值描述了样本的平均值,而标准差则代表了样本与均值的平均距离。标准差越大,样本的分布就越广。

利用这些量,尺度化的数据集可以描述为

x1-−-μ- x2-−-μ- x150-−-μ σ , σ ,..., σ ,

其中减法和除法都是逐元素进行的。

如果你熟悉 Python 和 NumPy,操作是这样的。(如果你不熟悉也没关系——关于它们的所有内容将在下一章中解释,并附有示例代码。)

 X_scaled = (X - X.mean(axis=0))/X.std(axis=0)
X_scaled[:10] 
array([[-0.90068117,  1.01900435, -1.34022653, -1.3154443 ], 
      [-1.14301691, -0.13197948, -1.34022653, -1.3154443 ], 
      [-1.38535265,  0.32841405, -1.39706395, -1.3154443 ], 
      [-1.50652052,  0.09821729, -1.2833891 , -1.3154443 ], 
      [-1.02184904,  1.24920112, -1.34022653, -1.3154443 ], 
      [-0.53717756,  1.93979142, -1.16971425, -1.05217993], 
      [-1.50652052,  0.78880759, -1.34022653, -1.18381211], 
      [-1.02184904,  0.78880759, -1.2833891 , -1.3154443 ], 
      [-1.74885626, -0.36217625, -1.34022653, -1.3154443 ], 
      [-1.14301691,  0.09821729, -1.2833891 , -1.44707648]])
# Create the data 
x = X_scaled.ravel() 
labels = ["/span>sepal length /span>sepal width /span>petal length /span>petal width 
g = np.tile(labels, X_scaled.shape[0]) 
df = pd.DataFrame(dict(x=x, g=g)) 

# Initialize the FacetGrid object 
pal = sns.cubehelix_palette(10, rot=-.25, light=.7) 
grid = sns.FacetGrid(df, row="/span>g hue="/span>g aspect=10, height=1.5, palette=pal) 

# Draw the densities 
grid.map(sns.kdeplot, /span>x bw_adjust=.5, clip_on=False, fill=True, alpha=1, linewidth=1.5) 
grid.map(sns.kdeplot, /span>x clip_on=False, color="/span>w lw=2, bw_adjust=.5) 

# Add reference line 
grid.refline(y=0, linewidth=2, linestyle="" color=None, clip_on=False) 

# Add labels to each plot 
grid.map(lambda x, color, label: plt.gca().text(0, .2, label, fontweight="/span>bold color=color,ha="/span>left va="/span>center transform=plt.gca().transAxes), /span>x 

# Adjust subplots and aesthetics 
grid.figure.subplots_adjust(hspace=-.25) 
grid.set_titles("#x0022;) 
grid.set(yticks=[], ylabel="#x0022;) 
grid.despine(bottom=True, left=True) 

plt.show()

PIC

图 1.2:鸢尾花数据集的尺度化特征

如果你将修改后的版本与原始数据进行比较,你会发现它的特征都在相同的尺度上。换句话说,我们将数据集转换成了一个更具表现力的数据集。从一个(非常)抽象的角度来看,机器学习其实就是一系列学到的数据转换,将原始数据转换成一种预测简单的形式。

在数学环境中,数据操作和标签之间关系的建模源于向量空间及其之间的变换概念。让我们通过精确定义向量空间来迈出第一步!

1.1 什么是向量空间?

将多个测量值表示为一个元组(x[1], x[2], …, x[n])是一个自然的想法,具有很多优点。元组的形式暗示了各个分量按精确的顺序排列,提供了一种清晰简洁的方式来存储信息。

然而,这也有代价:现在我们不得不处理更复杂的对象。尽管我们处理的是像 (x[1], …, x[n]) 这样的元组而不是数字,但它们之间仍有相似性。例如,任何两个元组 x = (x[1], …, x[n]) 和 y = (y[1], …, y[n]),

  • 可以通过 x + y = (x[1] + y[1], …, x[n] + y[n]) 来相加,

  • 并且可以与标量相乘:如果 c ∈ ℝ,则 cx = (cx[1], …, cx[n])。

这几乎就像使用一个数字一样。

这些运算也有明确的几何解释。加法与平移相同,而与标量的乘法则是简单的拉伸。(如果 |c| < 1,则是压缩。)

PIC

图 1.3:加法和标量乘法的几何解释

另一方面,如果我们想要遵循几何直觉(我们肯定是这么做的),那么如何定义向量乘法就不太清楚了。尽管有定义,

xy = (x1y1,...,xnyn)

从代数角度来看是有意义的,但我们无法从几何角度理解它的含义。

当我们思考向量和向量空间时,我们是在思考一种数学结构,这种结构符合我们的直觉观点和预期。让我们将这些转化为定义!

定义 2.(向量空间)

向量空间是一个数学结构(V, F, +, ⋅),其中

(a) V 是向量的集合,

(b) F 是标量域(最常见的是实数 ℝ 或复数 ℂ),

(c) + : V × V → V 是加法运算,满足以下性质:

  • x + y = y + x(交换律),

  • x + (y + z) = (x + y) + z(结合律),

  • 存在一个元素 0 ∈ V,使得 x + 0 = x(零向量的存在性),

  • 对于每个 x ∈ V,都存在一个逆元素 −x ∈ V,使得 x + (−x) = 0(加法逆元的存在性)

对于所有的向量 x, y, z ∈ V,

(d) 以及 ⋅ : F × V → V 是标量乘法运算,满足

  • a(bx) = (ab)x(结合律),

  • a(x + y) = ax + ay(分配律),

  • 且 1x = x

对于所有的标量 a, b ∈ F 和向量 x, y ∈ V。

这个定义包含了很多新的概念,我们来逐步拆解它。

首先,将加法和标量乘法等运算视为函数对你来说可能不太习惯,但这是一种完全自然的表示方式。(我们稍后会详细学习函数,但现在可以直观地理解它们。)在写作中,我们使用符号 x + y,但当将 + 视为两个变量的函数时,我们也可以写作 +(x, y)。形式 x + y 称为中缀表示法,而 +(x, y) 则称为前缀表示法。

在向量空间中,加法的输入是两个向量,结果是一个单一的向量,因此 + 是一个将笛卡尔积 V × V 映射到 V 的函数。

类似地,标量乘法接收一个标量和一个向量,结果是一个向量;也就是说,它是一个将 F × V 映射到 V 的函数。

(笛卡尔积 V × V 只是一个有序对的集合:

V × V = {(u,v) : u,v ∈ V }.

随时可以查看集合论附录(附录 C)了解更多细节,但目前,直观理解已经足够了。

这里也需要指出,数学定义总是在事后形式化的,即在对象本身有一定的具体化并且对用户变得熟悉之后。数学通常是先定义,再定理。这并不是实际操作中的做法。示例激发定义,而不是相反。

一般来说,标量域可以是实数或复数以外的其他东西。域这个术语指的是一个定义明确的数学结构,它使自然概念在数学上变得精确。我们不深入技术细节,只需把域理解为“一个数字集合,其中加法和乘法的运算方式与实数相同”。

由于我们不关心最一般的情况,我们将使用ℝ或ℂ以避免不必要的困难。如果你不熟悉域的精确数学定义,不必担心——每次看到“域”这个词时,直接理解为ℝ就好。

当上下文中的所有内容都很清晰时,(V,ℝ,+,⋅)通常会简化为 V 以方便表示。所以,如果没有特别指定域 F,则隐式假设 F 为ℝ。如果我们想要强调这一点,我们会称这些为实向量空间。

初看起来,定义 2 确实太复杂,难以理解。它看起来像是一堆集合、运算和属性的堆砌。然而,为了帮助我们建立一个心理模型,我们可以将向量想象成一支箭,从零向量开始。(回想一下,零向量 0 是那个对于所有 x 都有 x + 0 = x 成立的特殊向量。因此,它可以被视为一支零长度的箭;原点。)

为了更好地熟悉这一概念,让我们看一些向量空间的示例!

1.1.1 向量空间的示例

示例是构建对看似复杂的概念(如向量空间)理解的最佳方式之一。我们人类通常以模型而非抽象的形式进行思考。(是的,这也包括纯粹的数学家,尽管他们可能会否认这一点。)

示例 1. 向量空间最常见的实例是 (ℝ^n, ℝ, +, ⋅),这就是我们用来激发定义本身的那个实例。(ℝ^n 是实数集的 n 重笛卡尔积。如果你不熟悉这一概念,可以查看附录 C 中的集合论教程。)

(ℝ^n,ℝ,+,⋅) 是标准模型,我们用它来指导我们整个学习过程。如果 n = 2,那么我们讨论的就是熟悉的欧几里得平面。

PIC

图 1.4:欧几里得平面作为一个向量空间

使用 ℝ² 或 ℝ³ 进行可视化会有很大帮助。这里有效的做法通常在一般情况下也有效,尽管有时这可能是危险的。数学依赖于直觉和逻辑。我们通过直觉发展思想,但用逻辑来验证它们。

示例 2. 向量空间不仅仅是有限元组的集合。一个例子是具有实系数的多项式函数空间,定义为

 ∑n ℝ [x ] = { pixi : pi ∈ ℝ, n = 0,1,...}. i=0

两个多项式 p(x) 和 q(x) 可以通过以下方式相加

 ∑n i p(x)+ q(x) := (pi + qi)x , k=1

并且可以通过与实数标量相乘来运算

 ∑n cp(x) = cpixi. k=1

通过这些运算,(ℝ[x],ℝ,+,⋅) 是一个向量空间。虽然我们大多数时候把多项式看作是函数,但它们也可以表示为系数的元组:

∑n pixi ← → (p0,...,pn). i=0

请注意,n——多项式的次数——是没有上限的。因此,这个向量空间的结构比 ℝ^n 更加丰富。

示例 3. 之前的例子可以进一步推广。设 C([0,1]) 表示所有连续实值函数 f : [0,1] →ℝ 的集合。那么 (C(ℝ),ℝ,+,⋅) 是一个向量空间,其中加法和标量乘法是按元素定义的:

(f + g )(x) := f(x)+ g(x), (cf)(x) = cf (x )

对于所有 f,g ∈C(ℝ) 和 c ∈ℝ。(尽管连续性是我们还未定义的概念,但可以把连续函数看作是图像可以在不抬起笔的情况下绘制的函数。)

是的,没错:函数也可以看作是向量。函数空间在数学中扮演着重要角色,它们有多种不同的形式。我们经常将空间限制为连续函数、可微分函数,或者基本上是任何在给定运算下封闭的子集。

(事实上,ℝ^n 也可以看作是一个函数空间。从抽象的角度来看,每个向量 x = (x[1],…,x[n]) 是从 {1,2,…,n} 到 ℝ 的映射。)

函数空间出现在更高级的主题中,例如 反转 ResNet 架构,我们在本书中不会讨论这些内容。然而,看到一些不同的(而且不那么直观的)例子是值得的,像 ℝ^n 这样的例子就不算是直观的。

1.2 基底

尽管我们的向量空间包含无限多个向量,但我们可以通过找到特殊的子集来简化复杂度,这些子集能够表示任何其他向量。

为了使这个概念更加明确,让我们考虑我们反复提到的例子 ℝ^n。在这里,我们有一个特殊的向量集合

e[1] = (1,0,…,0)
e[2] = (0,1,…,0)
.. .
e[n] = (0,0,…,1)

这可以用来将每个向量 x = (x[1],…,x[n]) 表示为

 n x = ∑ xe , x ∈ ℝ, e ∈ ℝn i i i i i=1

例如,e[1] = (1,0) 和 e[2] = (0,1) 在ℝ²中。

我们刚刚看到的内容似乎极其简单,而且看起来只是让事情变得更加复杂。为什么我们需要将向量写成 x = ∑ [i=1]^nx[i]e[i]的形式,而不是简单地使用坐标(x[1],…,x[n])?因为,实际上,坐标表示法依赖于用来表示其他向量的基础向量集(在我们这里是{e[1],…,e[n]})。

向量不同于它的坐标!同一个向量在不同的坐标系统中可以有多个不同的坐标,转换这些坐标是一个有用的工具。

因此,集合 E = {e[1],…,e[n]}⊆ℝ^n 非常特殊,因为它显著简化了向量的表示。通过向量加法和标量乘法操作,它完全生成了我们的向量空间。E 是向量空间基的一个例子,作为ℝ^n 的骨架。

在本节中,我们将详细介绍并研究向量空间基的概念。

1.2.1 线性组合与独立性

让我们从特殊情况ℝ^n 缩小视野,开始讨论一般的向量空间。通过我们关于基的启发式例子,我们已经看到像这样的和式

∑n xivi, i=1

其中 v[i]是向量,x[i]系数是标量,这些在线性组合中起着至关重要的作用。当所有系数为零时,线性组合被称为平凡的。

给定一组向量,同一个向量可能以多种方式表示为线性组合。例如,如果 v[1] = (1,0),v[2] = (0,1),v[3] = (1,1),那么

(2,1) = 2v[1] + v[2]
= v[1] + v[3].

这表明集合 S = {v[1],v[2],v[3]}是多余的,因为它包含了重复的信息。线性相关性和独立性的概念使得这一点更加明确。

定义 3. (线性相关性与独立性)

设 V 是一个向量空间,S = {v[1],…,v[n]}是其向量的一个子集。如果 S 只包含零向量,或者存在一个非零向量 v[k],它可以表示为其他向量 v[1],…,v[k−1],v[k+1],…,v[n]的线性组合,则称 S 是线性相关的。

S 被称为线性独立的,如果它不是线性相关的。

线性相关性和独立性可以从不同角度来看。如果

 k∑−1 ∑n vk = xivi + xivi, i=1 i=k+1

对于某些非零 v[k],通过减去 v[k],我们得到零向量可以通过非平凡的线性组合得到。

 ∑n 0 = xivi i=1

对于某些标量 x[i],其中 x[k] = −1。这是线性相关的等价定义。通过这一点,我们证明了以下定理。

定理 2.

设 V 是一个向量空间,S = {v[1],…,v[n]}是其向量的一个子集。

(a) S 是线性相关的,当且仅当零向量 0 可以通过非平凡的线性组合得到。

(b) 当且仅当 0 = ∑ [i=1]^nx[i]v[i] 时,所有系数 x[i] 都为零,S 是线性无关的。

1.2.2 向量集的张成

线性组合提供了一种方法,通过一个小的向量集合生成更多的向量。对于一个向量集合 S,取其所有可能的线性组合叫做张成,生成的集合叫做张成集合。形式化地,它定义为

 ∑n span(S ) = { xivi : n ∈ ℕ, vi ∈ S,xi 是标量}. i=1

注意,向量集合 S 不一定是有限的。为了帮助说明张成的概念,我们可以在三维空间中可视化这个过程。两个线性无关向量的张成是一个平面。

PIC

图 1.5:两个线性无关向量 u,v ∈ ℝ³ 的张成

当我们讨论有限集合 {v[1],…,v[n]} 的张成时,我们表示其张成为

span (v ,...,v ). 1 n

这样可以避免通过给每个集合命名来过度复杂化符号。

命题 1. 设 V 为一个向量空间,S, S[1], S[2] ⊆ V 为其向量的子集。

(a) 如果 S[1] ⊆ S[2],则 span(S[1]) ⊆ span(S[2])。

(b) span(span(S)) = span(S)。

这是我们第一次证明!读一读,如果太难,跳过,稍后再回来看。只要确保你理解了命题的内容。

证明。性质 (a) 直接由定义得出。为了证明 (b),我们需要证明 span(S) ⊆ span(span(S)) 和 span(span(S)) ⊆ span(S)。

(这是一个学习曲线较陡的时刻,但想一想:两个集合 A 和 B 相等的充要条件是 A ⊆ B 且 B ⊆ A。)

前者由定义得出。对于后者,设 x ∈ span(span(S))。那么

 ∑n x = xivi i=1

对于某些 v[i] ∈ span(S)。由于 v[i] 在 S 的张成中,我们得到

 ∑m vi = vi,juj j=1

对于某些 u[j] ∈ S。因此,

 ∑n ∑n ∑m ∑m ∑n x = xivi = xi vi,juj = ( xivi,j)uj, i=1 i=1 j=1 j=1 i=1

这意味着 x ∈ span(S)。

因为 span(span(S)) = span(S),如果 S 是线性相关的,我们可以去除冗余的向量,仍然保持张成不变。

想一想:如果 S = {v[1],…,v[n]},假设 v[n] = ∑ [i=1]^(n−1)x[i]v[i],那么 v[n] ∈ span(S ∖{v[n]})。因此,

span(S ∖{vn }) = span (span(S ∖ {vn})) = span (S ).

(操作 A ∖B 是集合差,包含所有属于 A 但不属于 B 的元素。详情请参见附录 C。)

在向量集合中,那些能生成整个向量空间的集合是特别的。经过这些铺垫,我们已经准备好做一个正式的定义。任何满足 span(S) = V 的向量集合 S 称为 V 的生成集。

S 可以被看作是 V 的“无损压缩”,因为它包含了重建 V 中任何元素所需的所有信息,同时它比整个空间要小。因此,我们希望尽可能减少生成集的大小。这引出了线性代数中的一个最重要的概念:最小生成集,或者我们更愿意称之为基。

1.2.3 基与最小生成集

结合我们迄今为止建立的所有直觉,让我们立即进入定义吧!

定义 4.(基)

设 V 为一个向量空间,S 为其向量的子集。如果 S 是 V 的基,则:

(a) S 是线性无关的,

(b) 且 span(S) = V。

基集的元素称为基向量。

可以证明这些定义性质意味着每个向量 x 都可以唯一地表示为 S 的线性组合。(这部分留给读者练习。)

让我们来看一些例子!在 ℝ³ 中,集合 {(1,0,0),(0,1,0),(0,0,1)} 是一个基,但 {(1,1,1),(1,1,0),(0,1,1)} 也是一个基。所以,同一个向量空间可以有多个基。

对于 ℝ^n,最常用的基是 {e[1],…,e[n]},其中 e[i] 是一个向量,其所有坐标为 0,除了第 i 个坐标是 1。这个基称为标准基。

就“信息”而言,基向量集正好处于一个最佳的点上。向基集添加任何新向量会引入冗余;移除其中任何元素会导致集合不完整。

这些概念在下面的两个定理中得到了形式化。

定理 3。

设 V 为一个向量空间,S = {v[1],…,v[n]} 为其向量的子集。以下是等价的:

(a) S 是一个基。

(b) S 是线性无关的,且对于任何 x ∈V ∖S,向量集 S ∪{x} 是线性相关的。换句话说,S 是一个最大线性无关集。

证明。为了证明两个命题的等价性,我们需要证明两件事:一是 (a) 推导出 (b);二是 (b) 推导出 (a)。让我们从第一个开始!

(a) =⇒ (b) 如果 S 是基,那么任何 x ∈V 都可以表示为

 n ∑ x = xivi i=1

对于某些 x[i] ∈ℝ。因此,根据定义,S ∪{x} 是线性相关的。

(b) =⇒ (a) 我们的目标是证明任何 x 都可以表示为 S 中向量的线性组合。根据我们的假设,S ∪{x} 是线性相关的,因此 0 可以表示为一个非平凡的线性组合:

 ∑n 0 = αx + xivi, i=1

其中并非所有系数都为零。由于 S 是线性无关的,α 不能为零(否则意味着 S 是线性相关的,这与我们的假设相悖)。因此,

 ∑n x = − xivi, i=1 α

证明 S 是一个基。

接下来,我们要证明基的每个向量都是必不可少的。

定理 4。

设 V 为一个向量空间,S = {v[1],…,v[n]} 是一个基。那么,对于任何 v[i] ∈S,

span(S ∖ {vi}) ⊂ V,

即,S ∖{v[i]} 的跨度是 V 的一个真子集。

证明。我们将通过反证法来证明这一点。无损一般性,我们可以假设 i = 1。如果

span(S ∖{v1 }) = V,

然后

 n ∑ v1 = xivi. i=2

这意味着 S = {v[1],…,v[n]} 不是线性无关的,这与我们的假设相矛盾。

换句话说,上述结果意味着基既是最大线性无关集,又是最小生成集。

给定一个基 S = {v[1],…,v[n]},我们隐式地将向量 x = ∑ [i=1]^nx[i]v[i] 写作 x = (x[1],…,x[n])。由于这种分解是唯一的,我们可以毫无问题地这样做。系数 x[i] 也称为坐标。(请注意,坐标强烈依赖于基。给定两个不同的基,相同向量的坐标可能不同。)

1.2.4 有限维向量空间

正如我们之前所见,一个向量空间可以有许多不同的基,所以基不是唯一的。在这个背景下,出现了一个非常自然的问题。若 S[1] 和 S[2] 是 V 的两个基,那么是否有 jS[1]j = jS[2]j 这一关系?(其中 jSj 表示集合 S 的基数,即其“大小”。)

换句话说,如果我们更聪明地选择基,能不能得到更好的结果呢?事实证明,我们不能,任何两个基集合的大小都是相等的。我们不打算证明这一点,但这里是该定理的完整内容。

定理 5。

V 为一个向量空间,且 S1 S2 V 的两个基。则有 |S1| = |S2|

这为我们提供了一种定义向量空间维度的方法,维度就是其基的基数。我们将 V 的维度表示为 dim(V)。例如,ℝ^n 是 n 维的,如标准基 {(1,0,…,0),…,(0,0,…,1)} 所示。

如果你回顾之前的定理,我们假设基是有限的。你可能会问:这总是成立吗?答案是否定的。例子 2 和 3 表明情况并非如此。例如,可数无限集 {1,x,x²,x³,…} 是 ℝ[x] 的一个基。因此,根据上述定理,那里不可能存在有限基。

这标志着向量空间之间的一个重要区别:具有有限基的向量空间被称为有限维的。我有一个好消息:所有有限维的实向量空间本质上是 ℝ^n。(回想一下,如果一个向量空间的标量是实数,我们就称这个向量空间为实向量空间。)

为了理解这一点,假设 V 是一个 n 维实向量空间,其基为 {v[1],…,v[n]},并定义映射 φ : V →ℝ^n,如下所示:

 ∑n φ : xivi → (x1,...,xn). i=1

φ是可逆的并且保持 V 的结构,也就是加法和标量乘法操作。事实上,如果 u,v ∈V 且α,β ∈ℝ,则有φ(αu + βv) = αφ(x) + βφ(y)。这样的映射称为同构。这个词本身来源于古希腊语,isos 意味着相同,morphe 意味着形状。尽管这听起来很抽象,但两个向量空间之间存在同构,意味着它们具有相同的结构。因此,ℝ^n 不仅仅是有限维实数向量空间的一个例子,它是这些空间的普遍模型。注意,如果标量不是实数,那么到ℝ^n 的同构就不成立。(我们将在后面的章节中讨论类似的变换。)

考虑到我们几乎完全处理的是有限维实数向量空间,这是个好消息。使用ℝ^n 不仅仅是启发式的,它是一个良好的思维模型。

1.2.5 为什么基底如此重要?

如果每个有限维实数向量空间本质上与ℝ^n 相同,那么我们从抽象中得到什么呢?当然,我们可以不谈基底,直接使用ℝ^n,但为了深入理解机器学习中的核心数学概念,我们需要这种抽象。

让我们简要展望一下并看看一个例子。如果你对神经网络有一些经验,你会知道矩阵在其中扮演着至关重要的角色。没有任何背景,矩阵只不过是一个数字表格,似乎有任意的计算规则。你是否曾经想过,为什么矩阵乘法是按这种方式定义的?

尽管我们还没有精确定义矩阵,你可能之前已经遇到过它们。我们将在第三章和第四章全面学习它们,但对于这两个矩阵

 ⌊ ⌋ ⌊ ⌋ |a1,1 a1,2 ... a1,n| | b1,1 b1,2 ... b1,n| ||a2,1 a2,2 ... a2,n|| || b2,1 b2,2 ... b2,n|| | . . . . | | . . . . | A = || .. .. .. .. || , B = || .. .. .. .. || , ||a a ... a || ||b b ... b || ⌈ n,1 n,2 n,n⌉ ⌈ n,1 n,2 n,n⌉

它们的积 AB 定义为

 ⌊∑ ∑ ∑ ⌋ nk=1 a1,kbk,1 nk=1 a1,kbk,2 ... nk=1a1,kbk,n ||∑n ∑n ∑n || || k=1 a2,kbk,1 k=1 a2,kbk,2 ... k=1a2,kbk,n|| AB = || ... ... ... ... || , |∑n ∑n ∑n | |⌈ k=1 an,kbk,1 k=1 an,kbk,2 ... k=1an,kbk,n|⌉

即,AB 的(i,j)-th 元素定义为

 n ∑ a b . i,kk,j k=1

这个定义感觉有些随意。为什么不直接取元素逐一相乘(a[i,j]b[i,j])[i,j=1]^n 呢?一旦我们将矩阵视为描述向量空间之间线性变换的工具,这个定义就变得非常清晰,因为矩阵的元素描述了基向量的像。在这个背景下,矩阵的乘法仅仅是线性变换的组合。

我不想仅仅给出定义并告诉你如何使用它,而是希望你理解为什么它是这样定义的。在接下来的章节中,我们将学习矩阵乘法的每一个细节。

1.2.6 基的存在性

此时,你可能会问:对于给定的向量空间,我们是否能保证找到一个基?如果没有这样的保证,前面的设置可能会变得没有意义。(因为可能没有可以使用的基。)

幸运的是,情况并非如此。由于证明极其困难,我们不会展示这个过程,但这个问题非常重要,我们至少应该陈述定理。如果你对如何做到这一点感兴趣,我附上了一个证明的概述。可以跳过它,因为它对我们的目的并不至关重要。

定理 6。

每个向量空间都有一个基。

证明。(概述。)这个证明使用了一种叫做超限归纳的高级技术,远超我们的范围。(可以参考 Paul Halmos 的《Naive Set Theory》了解更多。)我们不精确地展示,而是专注于建立直觉,了解如何为任何向量空间构造基。

对于我们的向量空间 V,我们将逐个构造基。给定任意非零向量 v[1],如果 span(S[1])≠V,集合 S[1] = {v[1]}还不是基。因此,我们可以找到一个向量 v[2] ∈ V ∖ span(S[1]),使得 S[2] := S[1] ∪{v[2]}依然是线性无关的。

S[2]是基吗?如果不是,我们可以继续这个过程。如果过程在有限的步骤中停止,我们就完成了。然而,这并不能得到保证。想一想ℝ[x],即多项式的向量空间,它是无限维的,正如我们在第 1.2.4 节中所看到的。

这时,我们需要使用一些集合论的重型工具(我们并不具备)。

如果过程没有停止,我们需要找到一个包含所有 S[i]作为子集的集合 S[ℵ[0]]。(找到这个 S[ℵ[0]]集合是最棘手的部分。)S[ℵ[0]]是基吗?如果不是,我们继续这个过程。

这个过程很难展示,但最终会停止,我们无法再将任何向量添加到我们线性无关的向量集合中而不破坏无关性。当这一点发生时,我们已经找到了一个最大线性无关集——也就是基。

对于有限维向量空间,上述过程很容易描述。事实上,线性代数的支柱之一就是所谓的 Gram-Schmidt 过程,用于显式构造向量空间的特殊基。由于几个经典的结果依赖于此,我们将在接下来的章节中详细研究它。

1.2.7 子空间

在我们动手处理 Python 中的向量之前,还有一个主题我们需要讨论,它在讨论线性变换时会非常有用。(但再次强调,线性变换是机器学习的核心。我们学习的一切都是为了更好地理解它们。)对于给定的向量空间 V,我们通常对它的一个子集感兴趣,且这个子集本身也是一个向量空间。这就是子空间的概念。

定义 5。(子空间)

设 V 是一个向量空间。如果集合 U ⊆V 对加法和标量乘法是封闭的,那么 U 是 V 的一个子空间。

U 是一个真子空间,当且仅当它是一个子空间并且 U ⊂V。

根据定义,子空间本身就是向量空间,因此我们也可以定义它们的维度。每个向量空间至少有两个子空间:它本身和 {0}。这些被称为平凡子空间。除此之外,一组向量的跨度总是一个子空间。一个这样的例子在图 1.5 中说明。

子空间的一个最重要的方面是,我们可以利用它们来创建更多的子空间。这个概念将在下文中具体说明。

定义 6. (子空间的直和)

设 V 是一个向量空间,U[1] 和 U[2] 是它的两个子空间。U[1] 和 U[2] 的直和定义为

U1 + U2 = {u1 + u2 : u1 ∈ U1, u2 ∈ U2}.

你可以很容易地验证 U[1] + U[2] 确实是一个子空间,此外 U[1] + U[2] = span(U[1] ∪U[2])。子空间及其直和在多个主题中起着至关重要的作用,例如矩阵分解。例如,稍后我们将看到,其中许多是将一个线性空间分解为多个向量空间之和的等价问题。

选择一个基,其子集能够跨越某些给定子空间的能力通常非常有用。这个结果在下文中得到了形式化。

定理 7.

设 V 是一个向量空间,U[1] 和 U[2] 是它的两个子空间,且 U[1] + U[2] = V。同时,设 {p[1],…,p[k]} ⊆ U[1] 是 U[1] 的一个基,{q[1],…,q[l]} ⊆ U[2] 是 U[2] 的一个基。那么,联合

{p1,...,pk} ∪ {q1,...,ql}

是 V 中的一个基。

证明:这一点直接从直和的定义中得出。如果 V = U[1] + U[2],那么任何 x ∈V 都可以表示为 x = a + b,其中 a ∈U[1] 且 b ∈U[2]。

反过来,由于 p[1],…,p[k] 在 U[1] 中形成一个基,q[1],…,q[l] 在 U[2] 中形成一个基,向量 a 和 b 可以写成

 k l a = ∑ a p , b = ∑ bq . i=1 i i i=1 i i

因此,任何 x 都可以表示为

 ∑k ∑ l x = aipi + biqi, i=1 i=1

这是基的定义。

我们仅仅是触及了表面。基是至关重要的,但它们仅提供了实践中遇到的向量空间的骨架。为了正确地表示和操作数据,我们需要在这个骨架上构建几何结构。我们如何衡量两个测量值之间的“距离”?它们的相似度又如何呢?

除此之外,还有一个更加关键的问题:我们到底如何在计算机中表示向量?在下一节中,我们将看看 Python 中的数据结构,为我们之后进行的数据操作和转换打下基础。

1.3 向量在实践中的应用

到目前为止,我们主要讨论了向量和向量空间的理论。然而,我们的最终目标是构建计算模型,用于发现和分析数据中的模式。为了将理论付诸实践,我们将看看向量在计算中的表示方式。

在计算机科学中,我们对于数学结构的理解与如何在计算机内部表示它们之间有着显著的差异。到目前为止,我们的目标是开发一个数学框架,使我们能够推理数据的结构及其转换过程。我们希望有一种语言,它

  • 表达力强,

  • 易于使用,

  • 尽可能紧凑。

然而,当我们旨在进行计算而非纯粹的逻辑推理时,我们的目标发生了变化。我们需要的是那些能够实现计算的工具。

  • 易于使用,

  • 内存高效,

  • 快速访问、操作和转换。

这些要求常常是相互矛盾的,具体情况可能会更倾向于某一方。例如,如果我们有大量内存,但需要进行大量计算,我们可以牺牲空间来换取速度。由于所有潜在的使用场景,表示相同数学概念的格式有很多种。这些都被称为数据结构。

不同的编程语言以不同的方式实现向量。因为 Python 在数据科学和机器学习中无处不在,所以它将是我们的首选语言。在本章中,我们将研究 Python 中的所有可能的数据结构,看看哪种最适合表示高性能计算中的向量。

1.3.1 元组

在标准的 Python 中,至少有两种内置数据结构可以用来表示向量:元组和列表。我们先从元组开始!它们可以通过将元素列在两个括号之间,并用逗号分隔来定义。

v_tuple = (1, 3.5, -2.71, /span>a string 42) 
v_tuple
(1, 3.5, -2.71, ’a string’, 42)
type(v_tuple)
tuple

单个元组可以包含各种类型的元素。尽管在计算线性代数中我们将专门处理浮点数,但这个特性对于通用编程非常有用。

我们可以通过索引访问元组的元素。就像在其他一些编程语言中一样,索引从零开始。这与数学中的做法大相径庭,数学中我们通常从一开始索引。因此,在大多数为科学计算设计的语言中,如 Fortran、Matlab 或 Julia,索引是从一开始的。

(不要告诉别人,但从零开始索引曾经让我疯狂。我是受过数学训练的。)

v_tuple[0]
1

元组的大小可以通过调用内置的 len 函数来访问。

len(v_tuple)
5

除了索引,我们还可以通过切片访问多个元素。

v_tuple[1:4]
(3.5, -2.71, ’a string’)

切片的工作方式是通过指定第一个和最后一个元素,以及可选的步长,使用语法 object[first:last:step]。

元组相当不灵活,因为你不能改变它们的组件。尝试这么做会导致 TypeError,这是 Python 告诉你对象不支持你尝试调用的方法的标准方式。(在我们的例子中,是项赋值。)

v_tuple[0] = 2
--------------------------------------------------------------------------- 
TypeError                               Traceback (most recent call last) 
Cell In[22], line 1 
---->/span> 1 v_tuple[0] = 2 

TypeError: ’tuple’ object does not support item assignment

此外,扩展元组以添加额外元素也不受支持。由于我们无法在实例化后以任何方式改变元组对象的状态,它们是不可变的。根据用例,不可变性既可能是优点也可能是缺点。不可变对象消除了意外更改,但每个操作都需要创建一个新对象,导致计算开销。因此,在复杂计算中表示大量数据时,元组并不是最佳选择。

列表解决了这个问题。让我们来看看它们以及它们引入的新问题!

1.3.2 列表

列表是 Python 的工作马。与元组相比,列表非常灵活且易于使用,尽管这会牺牲运行时的性能。类似于元组,列表对象可以通过在方括号内枚举其对象并用逗号分隔来创建。

v_list = [1, 3.5, -2.71, /span>qwerty 
type(v_list)
list

与元组一样,通过索引或切片访问列表的元素。我们可以对列表执行各种操作:覆盖其元素,追加项目,甚至移除其他项目。

v_list[0] = /span>this is a string/span> 
v_list
[’this is a string’, 3.5, -2.71, ’qwerty’]

此示例说明列表也可以容纳各种类型的元素。添加和删除元素可以通过诸如 append、push、pop 和 remove 之类的方法完成。

在尝试之前,让我们快速记录一下我们示例列表的内存地址,通过调用 id 函数访问。

v_list_addr = id(v_list) 
v_list_addr
126433407319488

这个数字简单地指代了我的计算机内存中的一个地址,那里是 v_list 对象的位置。确实,因为这本书是在我的个人电脑上编译的。

现在,我们将对我们的列表执行几个简单操作,并展示内存地址不会改变。因此,不会创建新对象。

v_list.append([42])    # adding the list [42] to the end of our list 
v_list
[’this is a string’, 3.5, -2.71, ’qwerty’, [42]]
id(v_list) == v_list_addr    # adding elements doesn’t create any new objects
True
v_list.pop(1)    # removing the element at the index /span> 
v_list
[’this is a string’, -2.71, ’qwerty’, [42]]
id(v_list) == v_list_addr    # removing elements still doesn’t create any new objects
True

不幸的是,将列表相加得到的结果与我们的期望完全不同。

[1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]

不像我们希望向量行为一样将相应元素相加,而是将列表连接起来。这个特性在编写通用应用程序时非常方便。然而,这对科学计算并不适用。“标量乘法”也会产生奇怪的结果。

3*[1, 2, 3]
[1, 2, 3, 1, 2, 3, 1, 2, 3]

用整数乘以列表会重复列表指定次数。鉴于+运算符在列表上的行为,这似乎是合乎逻辑的,因为整数乘法是重复加法:

a⋅b = b◟+--⋅◝⋅⋅◜+--b◞。a 次

总体而言,列表可以做比我们需要表示向量更多的事情。虽然我们可能想要更改向量的元素,但我们不需要添加或删除它们的元素,也不需要存储除浮点数之外的对象。我们能够牺牲这些额外的功能并获得适合我们目的但具有闪电般快速计算性能的实现吗?是的。进入 NumPy 数组。

1.3.3 NumPy 数组

尽管 Python 的内建数据结构非常出色,但它们是为易用性优化的,而不是为了科学计算。这一问题在语言开发初期就被意识到,并通过NumPy库得到了解决。

Python 的主要卖点之一就是它写代码的速度快且直接,即使是复杂任务也能轻松完成。这种便利性是以速度为代价的。然而,在机器学习中,速度对我们来说至关重要。在训练神经网络时,一小部分操作需要重复数百万次。即使是性能的微小改进,也能节省大量的时间,尤其是在处理极大型模型时,可能节省数小时、数天甚至数周的时间。

C 语言位于技术谱系的另一端。虽然 C 代码编写起来较为困难,但当正确编写时,它的执行速度极快。由于 Python 是用 C 语言编写的,获取快速性能的一个行之有效的方法是从 Python 调用用 C 语言编写的函数。简而言之,这就是 NumPy 提供的功能:C 数组和操作,全部在 Python 中实现。

为了深入了解 Python 内建数据结构的底层问题,我们应该将数字和数组放在显微镜下观察。在计算机的内存中,对象被表示为固定长度的 0-1 序列。每个组成部分称为比特(bit)。比特通常按 8 位、16 位、32 位、64 位或甚至 128 位的块进行分组。根据我们希望表示的内容,相同的序列可以表示不同的意义。例如,8 位序列 00100110 可以表示整数 38 或 ASCII 字符“&”。

PIC

图 1.6:内存中的 8 位对象

通过指定数据类型,我们可以解码二进制对象。32 位整数称为 int32 类型,64 位浮点数称为 float64,以此类推。

由于单个比特包含的信息非常有限,因此内存是通过将其划分为 32 位或 64 位大小的块并依次编号来进行寻址的。这个地址是一个十六进制数,从 0 开始。(为了简化,假设内存是通过 64 位进行寻址的,这是现代计算机中的常见做法。)

存储一系列相关对象(具有相同数据类型)的一种自然方式是将它们放置在内存中的相邻位置。这种数据结构被称为数组。

PIC

图 1.7:一个 int64 类型对象的数组

通过存储第一个对象的内存地址,例如 0x23A0,我们可以通过访问内存位置 0x23A0 + k,瞬间获取第 k 个元素。

我们称这种数据结构为静态数组,或者通常称之为 C 数组,因为它在强大的 C 语言中是如此实现的。虽然这种数组实现速度极快,但它相对不灵活。首先,您只能存储单一类型的对象。其次,您必须预先知道数组的大小,因为不能使用超出预分配部分的内存地址。因此,在开始使用数组之前,您必须为其分配内存。(即预留空间,以防止其他程序覆盖它。)

然而,在 Python 中,你可以在同一个列表中存储任意大和不同的对象,并且可以随时移除或添加元素。

l = [2**142 + 1, /span>a string 
l.append(lambda x: x) 
l
[5575186299632655785383929568162090376495105, 
 ’a string’, 
 /span>function __main__.</span>lambdax)>

在上面的例子中,l[0] 是一个非常大的整数,甚至无法容纳在 128 位中。此外,我们的列表中包含各种各样的对象,包括一个函数。这是怎么回事?

Python 的列表提供了一个灵活的数据结构,

  1. 内存的过度分配,以及

  2. 将列表中对象的内存地址保存,而不是对象本身。

(至少在最广泛使用的 CPython 实现中(docs.python.org/3/faq/design.html\#how-are-lists-implemented-in-cpython)。)

PIC

图 1.8:CPython 实现的列表

通过检查我们列表 l 中每个对象的内存地址,我们可以看到它们分布在内存的各个位置。

[id(x) for x in l]
[126433412959232, 126433407528240, 126433410174944]

由于过度分配,删除或插入操作始终可以通过简单地移动剩余元素来完成。由于列表存储的是元素的内存地址,因此所有类型的对象都可以存储在单一结构中。

然而,这也有其代价。因为对象在内存中不是连续的,所以我们失去了引用局部性(en.wikipedia.org/wiki/Locality_of_reference),意味着由于我们频繁地访问内存中遥远的位置,读取速度会变得更慢。因此,循环遍历 Python 列表的效率不高。

因此,NumPy 数组本质上就是 Python 中的老式 C 数组,并且具备 Python 列表的用户友好界面。(如果你曾经使用过 C,你会知道这有多么令人感激。)让我们看看如何使用它们!

首先,我们导入 numpy 库。(为了节省字符,通常将其导入为 np。)

import numpy as np

主要的数据结构是 np.ndarray,表示 n 维数组。我们可以使用 np.array 函数从标准 Python 容器创建 NumPy 数组或从头开始初始化。(是的,我知道这有点混乱,但你很快就会习惯的。请记住,np.ndarray 是类,而 np.array 是你用来从 Python 对象创建 NumPy 数组的函数。)

X = np.array([87.7, 4.5, -4.1, 42.1414, -3.14, 2.001])    # creating a NumPy array from a Python list 
X
array([87.7   ,  4.5   , -4.1   , 42.1414, -3.14  ,  2.001 ])
np.ones(shape=7) # initializing a NumPy array from scratch using ones
array([1., 1., 1., 1., 1., 1., 1.])
np.zeros(shape=5)    # initializing a NumPy array from scratch using zeros
array([0., 0., 0., 0., 0.])

我们甚至可以使用随机数初始化 NumPy 数组。

np.random.rand(10)
array([0.92428404, 0.37719596, 0.92071695, 0.56905245, 0.12024811, 
      0.02868856, 0.53215047, 0.51749348, 0.21022765, 0.96749756])

最重要的是,当我们有一个给定的数组时,我们可以使用 np.zeros_like、np.ones_like 和 np.empty_like 函数初始化具有相同维度的另一个数组。

np.zeros_like(X)
array([0., 0., 0., 0., 0., 0.])

就像 Python 列表一样,NumPy 数组支持项赋值和切片操作。

X[0] = 1545.215 
X
array([1545.215 ,    4.5   ,   -4.1   ,   42.1414,   -3.14  ,    2.001 ])
X[1:4]
array([ 4.5   , -4.1   , 42.1414])

然而,正如预期的那样,你只能在每个 ndarray 中存储单一的数据类型。当试图将一个字符串作为第一个元素赋值时,我们会得到一条错误信息。

X[0] = /span>str/span>
--------------------------------------------------------------------------- 
ValueError                              Traceback (most recent call last) 
Cell In[48], line 1 
---->/span> 1 X[0] = /span>str/span> 

ValueError: could not convert string to float: ’str’

如你所料,每个 ndarray 都有一个数据类型属性,可以通过 ndarray.dtype 访问。如果可以在要分配的值与数据类型之间进行转换,它将自动执行,从而使项的赋值成功。

X.dtype
dtype(’float64’)
val = 23 
type(val)
int
X[0] = val 
X
array([23\.    ,  4.5   , -4.1   , 42.1414, -3.14  ,  2.001 ])

NumPy 数组是可迭代的,就像 Python 中的其他容器类型一样。

for x in X: 
    print(x)
23.0 
4.5 
-4.1 
42.1414 
-3.14 
2.001

这些适合用来表示向量吗?是的,我们稍后会看到原因!

1.3.4 NumPy 数组作为向量

让我们再谈谈向量。从现在开始,我们将使用 NumPy 的 ndarray 来表示向量。

v_1 = np.array([-4.0, 1.0, 2.3]) 
v_2 = np.array([-8.3, -9.6, -7.7])

加法和标量乘法操作默认支持,并且表现如预期。

v_1 + v_2    # adding v_1 and v_2 together as vectors
array([-12.3,  -8.6,  -5.4])
10.0*v_1    # multiplying v_1 with a scalar
array([-40.,  10.,  23.])
v_1 * v_2    # the elementwise product of v_1 and v_2
array([ 33.2 ,  -9.6 , -17.71])
np.zeros(shape=3) + 1
array([1., 1., 1.])

由于 Python 的动态类型,我们通常可以将 NumPy 数组传入设计用于标量的函数中。

def f(x): 
    return 3*x**2 - x**4 
f(v_1)
array([-208\.    ,    2\.    ,  -12.1141])

到目前为止,NumPy 数组几乎满足我们表示向量所需的所有条件。还有一个条件需要检查:性能。为了调查这一点,我们将使用 Python 内置的 timeit 工具来测量执行时间。

在它的第一个参数中,timeit (docs.python.org/3/library/timeit.html) 接受一个要执行并计时的函数。除了传递函数对象,它也接受作为字符串的可执行语句。由于在 Python 中函数调用有显著的计算开销,我们传递的是代码而非函数对象,以便更精确地进行时间测量。

在下面的对比中,我们将比较两个 NumPy 数组与包含千个零的 Python 列表相加的速度。

from timeit import timeit 

n_runs = 100000 
size = 1000 

t_add_builtin = timeit( 
    x + y for x, y in zip(v_1, v_2)]" 
    setup=f/span>size={size}; v_1 = [0 for _ in range(size)]; v_2 = [0 for _ in range(size)]" 
    number=n_runs 
) 

t_add_numpy = timeit( 
    /span>v_1 + v_2" 
    setup=f/span>import numpy as np; size={size}; v_1 = np.zeros(shape=size); 
    v_2 = np.zeros(shape=size) 
    number=n_runs 
) 

print(f/span>Built-in addition:       \t{t_add_builtin} s 
print(f/span>NumPy addition:          \t{t_add_numpy} s 
print(f/span>Performance improvement: \t{t_add_builtin/t_add_numpy:.3f} times faster
Built-in addition:             3.3522969299992837 s 
NumPy addition:               0.09616518099937821 s 
Performance improvement:       34.860 times faster

NumPy 数组要快得多。这是因为它们的操作是

  • 在内存中是连续的,

  • 类型一致,

  • 在 C 语言中实现的。

这只是冰山一角。我们仅仅看到了其中的一小部分,但 NumPy 提供的功能远不止一个快速的数据结构。随着书中的内容逐渐深入,我们会慢慢探索其更广泛的功能,最终揭示它所提供的丰富功能。

1.3.5 NumPy 真的比 Python 更快吗?

NumPy 的设计目标是比原生 Python 更快。这真的是这样吗?并不总是。如果使用不当,它甚至可能会降低性能!为了了解何时使用 NumPy 最有利,我们将探讨它在实践中为什么更快。

为了简化调查,我们的玩具问题将是随机数生成。假设我们只需要一个随机数。我们应该使用 NumPy 吗?让我们测试一下!我们将其与内置的随机数生成器进行比较,运行十百万次并测量执行时间。

from numpy.random import random as random_np 
from random import random as random_py 

n_runs = 10000000 
t_builtin = timeit(random_py, number=n_runs) 
t_numpy = timeit(random_np, number=n_runs) 

print(f/span>Built-in random:\t{t_builtin} s 
print(f/span>NumPy random:   \t{t_numpy} s
Built-in random:      0.47474874800172984 s 
NumPy random:         5.1664929229991685 s

在生成单个随机数时,NumPy 明显较慢。为什么会这样?如果我们需要一个数组而不是一个数字呢?这样也会更慢吗?

这次,让我们生成一个包含千个元素的列表/数组。

size = 1000
n_runs = 10000

t_builtin_list = timeit(
    "[random_py() for _ in range(size)]",
    setup=f"from random import random as random_py; size={size}",
    number=n_runs
)

t_numpy_array = timeit(
    "random_np(size)",
    setup=f"from numpy.random import random as random_np; size={size}",
    number=n_runs
)

print(f"Built-in random with lists:\t{t_builtin_list}s")
print(f"NumPy random with arrays:  \t{t_numpy_array}s")
Built-in random with lists:    0.5773125300001993s 
NumPy random with arrays:       0.08449692800058983s

(再次说明,我不想把计时表达式包装在 lambda 中,因为在 Python 中函数调用有开销。我希望尽可能精确地计时,所以我将它们作为字符串传递给 timeit 函数。)

现在情况看起来完全不同了。在生成一个随机数数组时,NumPy 毫无疑问是最优选择。

这个结果也有一些有趣的地方。首先,我们生成了一个随机数,重复了 10000000 次。其次,我们生成了一个包含 1000 个随机数的数组,重复了 10000 次。在这两种情况下,最终我们都得到了 10000000 个随机数。使用内置方法时,当我们将它们放入列表中时,耗时大约是两倍。然而,在使用 NumPy 时,相比于自身处理数组时,我们看到的加速效果约为 30 倍!(实际的数字可能会因计算机不同而有所差异。)

为了了解背后的情况,我们将使用 cProfiler 来分析代码(docs.python.org/3/library/profile.html)。通过这个工具,我们可以准确看到每个函数被调用的次数以及花费的时间。

让我们先来看一下内置函数。在以下函数中,我们会像之前一样生成 10000000 个随机数。

def builtin_random_single(n_runs): 
    for _ in range(n_runs): 
        random_py()

在本书的写作环境 Jupyter Notebook 中,可以使用魔法命令 %prun 调用 cProfiler。

n_runs = 10000000 

%prun builtin_random_single(n_runs)
 10000558 function calls (10000539 primitive calls) in 2.082 seconds 

   Ordered by: internal time 

   ncalls  tottime  percall  cumtime  percall filename:lineno(function) 
       1    0.937    0.937    1.671    1.671 2471337341.py:1(builtin_random_single) 
 10000000    0.911    0.000    0.911    0.000 {method ’random’ of ’_random.Random’ objects} 
     4/0    0.213    0.053    0.000         {method ’poll’ of ’select.epoll’ objects} 
      10    0.009    0.001    0.016    0.002 socket.py:626(send) 
       2    0.009    0.004    0.015    0.008 {method ’__exit__’ of ’sqlite3.Connection’ objects}

这里有两个重要的列。ncalls 显示函数被调用的次数,而 tottime 是函数内花费的总时间,不包括在子函数中花费的时间。

如预期的那样,内置函数 random.random() 被调用了 10000000 次。注意函数内花费的总时间。(我不能给你一个准确的数字,因为这取决于本书编写时的机器。)

那么 NumPy 版本呢?结果令人惊讶。

def numpy_random_single(n_runs):
    for _ in range(n_runs):
        random_np()

%prun numpy_random_single(n_runs)
448 function calls (444 primitive calls) in 7.203 seconds 

   Ordered by: internal time 

   ncalls  tottime  percall  cumtime  percall filename:lineno(function) 
       1    7.029    7.029    7.029    7.029 2015715881.py:1(numpy_random_single) 
       2    0.136    0.068    0.136    0.068 {method ’poll’ of ’select.epoll’ objects} 
       2    0.015    0.007    0.015    0.007 {method ’__exit__’ of ’sqlite3.Connection’ objects} 
       1    0.011    0.011    0.011    0.011 {method ’execute’ of ’sqlite3.Connection’ objects} 
       3    0.010    0.003    7.339    2.446 base_events.py:1910(_run_once) 
       7    0.000    0.000    0.000    0.000 socket.py:626(send) 
       1    0.000    0.000    0.000    0.000 {method ’disable’ of ’_lsprof.Profiler’ objects} 
       1    0.000    0.000    0.026    0.026 history.py:833(_writeout_input_cache) 
       1    0.000    0.000    0.000    0.000 inspect.py:3102(_bind) 
   88/84    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}

同样地,正如之前所说,numpy.random.random() 函数确实被调用了 10000000 次,正如预期的那样。然而,脚本在这个函数中花费的时间比之前的 Python 内置 random 函数要长得多。因此,每次调用的成本更高。

当我们开始处理大型数组和列表时,情况发生了显著变化。接下来,我们将生成一个包含 1000 个随机数的列表/数组,并测量执行时间。

def numpy_random_single(n_runs): 
    for _ in range(n_runs): 
        random_np() 
%prun numpy_random_single(n_runs)
448 function calls (444 primitive calls) in 7.203 seconds 

   Ordered by: internal time 

   ncalls  tottime  percall  cumtime  percall filename:lineno(function) 
       1    7.029    7.029    7.029    7.029 2015715881.py:1(numpy_random_single) 
       2    0.136    0.068    0.136    0.068 {method ’poll’ of ’select.epoll’ objects} 
       2    0.015    0.007    0.015    0.007 {method ’__exit__’ of ’sqlite3.Connection’ objects} 
       1    0.011    0.011    0.011    0.011 {method ’execute’ of ’sqlite3.Connection’ objects} 
       3    0.010    0.003    7.339    2.446 base_events.py:1910(_run_once) 
       7    0.000    0.000    0.000    0.000 socket.py:626(send) 
       1    0.000    0.000    0.000    0.000 {method ’disable’ of ’_lsprof.Profiler’ objects} 
       1    0.000    0.000    0.026    0.026 history.py:833(_writeout_input_cache) 
       1    0.000    0.000    0.000    0.000 inspect.py:3102(_bind) 
   88/84    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}

如我们所见,大约 60% 的时间花费在列表推导式上。(请注意,tottime 不包括像 random.random() 这样的子函数调用。)

现在,我们准备好了解为什么在正确使用的情况下 NumPy 更快了。

def numpy_random_array(size, n_runs): 
    for _ in range(n_runs): 
        random_np(size) 
%prun numpy_random_array(size, n_runs)
149 function calls (148 primitive calls) in 0.132 seconds 

   Ordered by: internal time 

   ncalls  tottime  percall  cumtime  percall filename:lineno(function) 
       1    0.122    0.122    0.122    0.122 1681905588.py:1(numpy_random_array) 
       2    0.009    0.004    0.009    0.004 {method ’__exit__’ of ’sqlite3.Connection’ objects} 
     2/1    0.000    0.000    0.122    0.122 {built-in method builtins.exec}

对于每一个 10000 次函数调用,我们都会得到一个包含 1000 个随机数的 numpy.ndarray。NumPy 快速的原因在于,它的数组在操作时非常高效。它们像 C 语言中的数组,而不是 Python 列表。

如我们所见,它们之间有两个显著的差异。

  • Python 列表是动态的,例如,你可以添加或移除元素。NumPy 数组具有固定的长度,因此无法在不创建新数组的情况下添加或删除元素。

  • Python 列表可以同时容纳多种数据类型,而 NumPy 数组只能包含一种数据类型。

所以,NumPy 数组虽然不如 Python 列表灵活,但在性能上显著优于后者。当不需要额外的灵活性时,NumPy 超越了 Python。

为了精确地看到 NumPy 在什么大小时超过 Python 的随机数生成速度,我们可以通过测量多个大小的执行时间来进行比较。

sizes = list(range(1, 100)) 

runtime_builtin = [ 
    timeit( 
        random_py() for _ in range(size)]" 
        setup=f/span>from random import random as random_py; size={size}" 
        number=100000 
    ) 
    for size in sizes 
] 

runtime_numpy =  
    timeit( 
        /span>random_np(size) 
        setup=f/span>from numpy.random import random as random_np; size={size}" 
        number=100000 
    ) 
    for size in sizes
sizes = import matplotlib.pyplot as plt

with plt.style.context("seaborn-v0_8"):
    plt.figure(figsize=(10, 5))
    plt.plot(sizes, runtime_builtin, label="built-in")
    plt.plot(sizes, runtime_numpy, label="NumPy")
    plt.xlabel("array size")
    plt.ylabel("time (seconds)")
    plt.title("Runtime of random array generation")
    plt.legend()
    plt.show()

![PIC 图 1.9:随机数组生成的运行时间在大约 20 时,NumPy 的性能开始超过 Python。当然,这个数字对于其他操作(例如计算正弦或加法)可能不同,但趋势是相同的。对于小输入规模,Python 会稍微超过 NumPy,但随着规模的增加,NumPy 会大幅领先。## 1.4 小结在本章中,我们学习了什么是向量,以及为什么在数据科学和机器学习中必须使用它们。向量不仅仅是一堆捆绑在一起的数字,它是一种数学结构,允许我们更有效地推理数据,无论在理论上还是实践中。与常见观点相反,向量之所以是向量,不是因为它们有方向和大小,而是因为你可以将它们相加。这一点通过向量空间的概念得到了形式化,为我们的研究提供了数学框架。向量空间最好通过基来描述,也就是最小且线性独立的生成集。理解向量空间及其基在我们研究线性变换时将带来巨大的回报,线性变换是预测模型中最重要的构建模块。除了向量所提供的抽象跃迁外,通过向量化我们的代码,我们在实践中获得了显著的收益,将复杂的逻辑压缩成如数据缩放等一行代码:pyX_scaled = (X - X.mean(axis=0)) / X.std(axis=0)除了从标量到向量和矩阵的概念跃迁,NumPy(数值 Python 的简称)使得高效的数据处理成为可能,它是机器学习工具包中的首选库。如果一个张量库没有使用 NumPy,那么它一定是受到 NumPy 启发的。我们已经理解了它的基本概念,并且知道何时以及为什么要使用它。在下一章中,我们继续探索向量空间。基是很酷的,但除了基,向量空间还有一个美丽而丰富的几何结构。让我们来看看!## 1.5 问题问题 1:并非所有的向量空间都是无限的。我们将在下一个问题中看到一些只包含有限个向量的向量空间。定义集合ℤ2 := {0,1},

运算符 + 和 ⋅ 由以下规则定义

0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0

0 ⋅ 0 = 0
0 ⋅ 1 = 0
1 ⋅ 0 = 0
1 ⋅ 1 = 1.

这称为二进制(或模 2)算术。

(a) 证明 (ℤ[2],ℤ[2],+,⋅) 是一个向量空间。

(b) 证明 (ℤ[2]^n, ℤ[2], +, ⋅) 也是一个向量空间,其中 ℤ[2]^n 是 n 次笛卡尔积

 n ℤ 2 = ℤ◟2-×-⋅⋅◝◜⋅×-ℤ2◞, n 次

加法和标量乘法是逐元素定义的:

x + y = (x[1] + y[1],…,x[n] + y[n]), x,y ∈ ℤ[2]^n,
cx = (cx[1],…,cx[n]), c ∈ ℤ[2].

问题 2:以下向量集是线性无关的吗?

(a) S[1] = {(1,0,0),(1,1,0),(1,1,1)}⊆ℝ³

(b) S[2] = {(1,1,1),(1,2,4),(1,3,9)}⊆ℝ³

(c) S[3] = {(1,1,1),(1,1,−1),(1,−1,−1)}⊆ℝ³

(d) S[4] = {(π,e),(−42,13∕6),(π³,−2)}⊆ℝ²

问题 3. 设 V 是一个有限的 n 维向量空间,S = {v[1],…,v[m]} 是一组线性无关的向量,且 m/span>n。证明存在一个基集 B,使得 S ⊂ B。

问题 4. 设 V 是一个向量空间,S = {v[1],…,v[n]} 是它的基。证明每个向量 x ∈ V 都可以唯一地表示为 S 中向量的线性组合。(即,如果 x = ∑ [i=1]^n α[i]v[i] = ∑ [i=1]^n β[i]v[i],则对于所有 i = 1,…,n,α[i] = β[i]。)

问题 5. 设 V 是一个任意的向量空间,U[1] 和 U[2] ⊆ V 是其两个子空间。证明 U[1] + U[2] = span(U[1] ∪ U[2])。

提示:要证明这两个集合的相等性,你需要证明两件事:1)如果 x ∈ U[1] + U[2],则 x ∈ span(U[1] ∪ U[2]),2)如果 x ∈ span(U[1] ∪ U[2]),则 x ∈ U[1] + U[2]。

问题 6. 考虑由实系数多项式构成的向量空间,定义为:

 n ℝ [x] = {p(x) = ∑ pxi : p ∈ ℝ, n = 0,1,...}. i i i=0

(a)证明:

 ∑n i xℝ[x] := {p(x) = pix : pi ∈ ℝ, n = 1,2,...} i=1

是 ℝ[x] 的一个适当子空间。

(b)证明:

f : ℝ[x] → xℝ [x ], p(x) ↦→ xp(x)

是双射且线性的。(如果函数 f : X → Y 是双射的,那么对每个 y ∈ Y,都有唯一的 x ∈ X 使得 f(x) = y。如果你不熟悉这个概念,可以在 第九章之后再回顾这个问题。)

一般来说,向量空间之间的线性且双射的函数 f : U → V 被称为同构映射。给定这样的函数的存在,我们称向量空间 U 和 V 是同构的,意味着它们具有相同的代数结构。

结合(a)和(b),我们得到 ℝ[X] 与其适当子空间 xℝ[X] 同构。这是一个非常有趣的现象:一个代数上与其适当子空间相同的向量空间。(注意,这在有限维空间中是无法发生的,比如 ℝ^n。)

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,提供解决方案给其他读者,参与作者的问我任何问题(AMA)环节,还有更多内容。扫描二维码或访问链接加入社区。packt.link/math

PIC

向量空间的几何结构

让我们重新审视上一章中介绍的鸢尾花数据集!我想考察一下你的直觉。我将花瓣宽度与花瓣长度绘制在一起,并在图 2.1 中隐藏了类别标签:

import matplotlib.pyplot as plt 
from sklearn.datasets import load_iris 

# 第四章:Load the iris dataset 
iris = load_iris() 
data = iris.data 

# Extract petal length (3rd column) and petal width (4th column) 
petal_length = data[:, 2] 
petal_width = data[:, 3] 

with plt.style.context("/span>seaborn-v0_8": 
    # Create the scatter plot 
    plt.figure(figsize=(7, 7)) 
    plt.scatter(petal_length, petal_width, color=’indigo’, alpha=0.8, edgecolor=’none’, s=70, marker=’o’) 
    plt.xlabel(’petal length (cm)’) 
    plt.ylabel(’petal width (cm)’) 
    plt.show()

PIC

图 2.1:鸢尾花数据集中的“花瓣宽度”和“花瓣长度”特征

即使没有知道任何标签,我们也可以凭直觉指出,可能至少有两个类别。你能用一句话总结你的推理吗?

有许多有效的论点,但最常见的观点是,两个簇之间相距很远。正如这个例子所示,距离的概念在机器学习中起着至关重要的作用。在本章中,我们将把距离的概念翻译成数学语言,并将其置于向量空间的背景下。

2.1 范数与距离

之前,我们看到向量本质上是从零向量开始的箭头。除了方向,向量还有大小。例如,正如我们在高中数学中学到的,欧几里得平面中的大小定义为

 ∘ ------- 2 2 ∥x ∥ = x1 + x 2, x = (x1,x2),

同时我们可以计算 x 和 y 之间的距离为

 ∘ --------2-----------2 d(x,y) = (x1 − y1) + (x2 − y2) .

(函数 ∥⋅∥ 仅表示向量的大小。)

PIC

图 2.2:欧几里得平面中的大小

大小公式 ∘ ------- x21 + x22 可以简单地推广到更高维度,方法是

 ∘ ------------ 2 2 n ∥x ∥ = x1 + ⋅⋅⋅+ xn, x = (x1,...,xn) ∈ ℝ .

然而,仅从这个公式看,并不清楚为什么它是这样定义的。平方和的平方根与距离和大小有什么关系呢?实际上,它背后只是勾股定理。

记住,勾股定理指出,在直角三角形中,斜边的平方等于其他两边的平方和,如图 2.3 所示。

PIC

图 2.3:勾股定理

将其转换为代数形式时,它表明 a² + b² = c²,当 c 是直角三角形的斜边,a 和 b 是它的两个其他边。如果我们将此应用于二维向量 x = (x[1],x[2]),我们可以看到,勾股定理给出了其大小 ∥x∥ = ∘ -2----2 x1 + x2

这可以推广到更高维度。为了看看发生了什么,我们将检查三维情况,如图 1.4 所示。在这里,我们可以将勾股定理应用两次来得到大小!

PIC

图 2.4:三维空间中的勾股定理

对于每个向量 x = (x[1],x[2],x[3]),我们可以首先查看由(0,0,0)、(x[1],0,0)和(x[1],x[2],0)确定的三角形。斜边的长度可以通过∘ ------- x21 + x22来计算。然而,点(0,0,0)、(x[1],x[2],0)和(x[1],x[2],x[3])形成一个直角三角形。再次应用毕达哥拉斯定理,我们得到

 ∘ ------------ ∥x∥ = x2 + x2+ x2, 1 2 3

这被称为欧几里得范数。这正是一般 n 维情况下发生的事情。

在机器学习中,大小和距离的概念至关重要,因为我们可以利用它们来确定数据点之间的相似度,衡量并控制神经网络的复杂度,等等。

毕达哥拉斯定理是唯一可行的度量大小和距离的方法吗?当然不是。

由于曼哈顿的街道布局本质上是一个矩形网格,其居民以按街区测量距离而闻名。如果某个地方位于北方两街区和东方三街区,那么就意味着你需要先向北走两个交叉口,再向东走三个交叉口才能到达。由此产生了一种数学上完全有效的度量概念,称为曼哈顿距离,定义为

d(x,y) = |x1 − y1| + |x2 − y2|.

使用曼哈顿距离时,两个点之间的最短路径不是唯一的。

PIC

图 2.5:对于曼哈顿距离,两个点之间的最短路径不是唯一的

除了欧几里得距离和曼哈顿距离外,还有其他几种度量。我们将再次从具体实例中抽离,采用抽象的视角来讨论。

如果我们谈论一般的测量和度量,那么我们期望它们具备哪些属性呢?什么构成了测量距离?本质上,有三个这样的特征:

  • 距离应该是非负的,

  • 它应该保持缩放性(即,d(cx,cy) = cd(x,y) 对所有标量c成立),

  • 从点xy的直线距离始终等于或小于触及任何其他点z的距离。

这些都通过范数的概念来形式化。

定义 7.(范数)

V是一个向量空间。若函数![∥⋅∥ : V → 0,∞ )满足以下条件,则称其为范数:对于所有x, y ∈ V,以下性质成立:

  1. 正定性:∥x∥ ≥ 0∥x∥ = 0 当且仅当x = 0

  2. 正齐次性:∥cx∥ = |c|∥x∥ 对所有c ∈ ℝ成立。

  3. 三角不等式:∥x + y∥ ≤ ∥x∥ + ∥y∥ 对所有x,y ∈ V成立。

配备范数的向量空间称为范数空间。

让我们看一些例子吧!

示例 1. 设 p ∈ 1,∞),并定义

![ ∑n p 1∕p ∥x∥p = ( |xi|) , x = (x1,...,xn ) i=1

在 ℝ^n 上,函数 ∥⋅∥[p] 被称为 p-范数。证明 ∥⋅∥[p] 确实是一个范数有点技术性。因此,我们不会进入详细的讨论。(三角不等式需要一些工作,但其他两个性质很容易看出。)

我们已经看到了两个特殊情况:欧几里得范数(p = 2)和曼哈顿范数(p = 1)。它们在机器学习中经常出现。例如,常见的均方误差就是预测值和真实值之间的缩放欧几里得距离:

 n MSE (y,yˆ) =-1∥y − ˆy∥2 = 1-∑ (y − ˆy )2 n 2 n i i i=1

如前所述,2-范数与 1-范数通常一起用于训练过程中控制模型的复杂度。举个具体的例子,假设我们正在拟合一个多项式 f(x) = ∑ [i=0]mq[i]xi 到数据 {(x[1],y[1]),…,(x[n],y[n])}。为了得到一个对新数据有良好泛化能力的模型,我们希望模型尽可能简单。因此,我们可能考虑最小化损失:

![Loss(y,ˆy,q) = MSE (y, ˆy)+ λ∥q ∥p, q = (q0,q1,...,qm), λ ∈ 0,∞ )

其中项 ∥q∥[p] 负责保持多项式 f(x) 的系数较小,而 λ 控制正则化的强度。通常,p 的值为 1 或 2,但 1,∞) 区间内的其他值也是有效的。

示例 2. 让我们再在 ℝ^n 上停留一会儿!所谓的 ∞-范数定义为

![∥x ∥ = max {|x |,...,|x |}. ∞ 1 n

证明 ∥⋅∥[∞] 确实是一个范数是一个简单的任务,留给你自己练习。(这或许是数学教材中最臭名昭著的一句话,但相信我,这真的很简单。试试看!如果你看不懂,可以尝试特殊情况 ℝ²。)

这就是所谓的 ∞-范数,并且与我们刚才看到的 p-范数有着密切的关系。事实上,如果我们让 p 的值无限增大,∥x∥[p] 将非常接近 ∥x∥[∞],最终在极限时达成一致。

备注 2. (∞-范数作为 p-范数的极限)

如果你已经熟悉收敛序列和极限,你会看到这是所谓的 ∞-范数,因为

lim ∥x∥ = ∥x∥ . p→ ∞ p ∞

为了证明这一点,考虑到

 ∑n p 1∕p ∑n -|xi|-p 1∕p pli→m∞ ∥x ∥p = pl→im∞ ( |xi|) = pli→m∞ ∥x∥∞ ( (∥x ∥∞ )) . i=1 i=1

由于根据定义!-|xi|- ∥x∥∞ ≤ 1,

 ∑n |xi| 1 ≤ ( (------)p)1∕p ≤ n1 ∕p i=1 ∥x ∥∞

成立。因为

lim n1∕p = 1, p→∞

我们可以得出结论:

lim ∥x∥p = ∥x∥∞. p→ ∞

这就是为什么 ∞-范数被视为一个 p-范数,其中 p = ∞。

如果你不熟悉序列极限的概念,不用担心。我们将在学习单变量微积分时详细讲解这一点。

示例 3. ∞-范数可以推广到函数空间。还记得 C([0,1])吗?这是在第 1.1.1 节中介绍的连续函数的向量空间示例。在那里,∥⋅∥[∞]可以定义为

∥f∥∞ = sup |f (x )|. x∈[0,1]

这种范数可以在其他函数空间中定义,例如 C(ℝ),即连续实函数的空间。由于最大值不能保证存在(例如在 C(ℝ)中的 S 形函数),最大值被替换为上确界。因此,∞-范数通常被称为上确界范数。

如果将函数想象为一种景观,则上确界范数是最高峰的高度或最深槽的深度(无论绝对值较大者)。

图片

图 2.6:上确界范数

当第一次遇到这种范数时,可能会觉得难以理解它与任何大小概念有什么关系。然而,∥f −g∥[∞]是衡量两个函数 f 和 g 之间距离的一种自然方式,一般来说,大小只是与 0 的距离。

图片

图 2.7:由上确界范数给出的两个函数之间的距离

2.1.1 定义从范数到距离的距离

除了测量向量的大小外,我们还对测量它们之间的距离感兴趣。如果您在某个诱导范数空间中的位置为 x,则 y 有多远?在诱导范数向量空间中,我们可以通过以下方式定义任何 x 和 y 之间的距离

d(x,y) = ∥x− y∥.

这被称为诱导范数的度量。因此,范数衡量了与零向量的距离,而度量 d 衡量了差异的范数。

通常,我们说函数 d:V ×V → 0,∞)是一种度量,如果以下条件对于所有 x,y,z ∈ V 都成立。

定义 8. (度量)

让 ![V 是一个向量空间,d:V ×V → 0,∞) 是一个函数。如果以下条件对于所有的 x,y,z ∈ V 都成立,d 就是一个度量:

  1. 当 d(x,y ) = 0 时,我们有 x = y(正定性)。

  2. d (x, y) = d (y, x)(对称性)。

  3. d (x, z) ≤ d(x,y )+ d(y,z)(三角不等式)。

定义的一个直接结果是,如果 x≠y,则 d(x,y)/span>0。 (由于正定性给出了 d(x,y) = 1 意味着 x = y。)

由于向量空间的线性结构,我们可以快速检查 d(x,y) = ∥x−y∥确实是一种度量。这是因为范数生成的度量对平移是不变的。也就是说,对于任意的 x,y,z ∈V ,我们有

![d(x,y) = d(x + z,y + z).

换句话说,你从哪里出发并不重要:距离只取决于你的位移。这对于任何度量来说都不成立。因此,诱导范数度量是特殊的。在我们的研究中,我们只处理这些特殊情况。因此,我们甚至不会讨论度量,只讨论范数。

向量空间本身只是一个骨架,它提供了一种表示数据的方式。在此基础上,范数定义了一种几何结构,揭示了大小和距离等属性。这两者在机器学习中都是至关重要的。例如,一些无监督学习算法根据数据点之间的相互距离将数据点分成不同的簇。

还有一种方法可以增强向量空间的几何结构:内积,也称为点积。我们将在下一节中对这个概念进行详细探讨。

2.2 内积、角度及其重要性

在上一节中,我们给向量空间赋予了范数,用来衡量向量的大小和点之间的距离。在机器学习中,这些概念可以用于,例如,识别无标签数据集中的簇。然而,在没有上下文的情况下,距离往往是不够的。根据我们的几何直觉,我们可以希望测量数据点之间的相似性。通过内积(也称为点积)可以实现这一点。

你可以回想起内积是我们用来测量两向量之间角度的量,就像在高中几何课上学到的那样。给定平面上的两个向量 x = (x[1],x[2]) 和 y = (y[1],y[2]),我们通过以下方式定义它们的内积:

⟨x, y⟩ = x y + x y , 1 1 2 2

可以证明

⟨x,y⟩ = ∥x∥∥y∥ cosα (2.1)

其成立,其中 α 是 x 和 y 之间的角度。(事实上,存在两个这样的角度,但它们的余弦值相等。)因此,角度本身可以通过以下方式提取:

 -⟨x,y-⟩ α = arccos∥x ∥∥y∥,

其中 arccosx 是余弦函数的反函数。我们可以使用内积来确定两个向量是否正交,因为只有当 ⟨x,y⟩ = 0 时,才成立正交性。在我们早期接触到的数学中,几何直觉(比如正交性)是首先出现的,然后我们才构建了像内积这样的工具。然而,如果我们放远来看,并采取一种抽象的观点,事情恰恰相反。正如我们很快会看到的,内积的出现非常自然,推动了正交性的普遍概念的形成。

一般来说,这是内积的正式定义。

定义 9. (内积与内积空间)

设 V 为实向量空间。函数 ⟨⋅,⋅⟩ : V × V → ℝ 称为内积,如果对所有 x, y, z ∈ V 和 a ∈ ℝ,以下条件成立:

  1. ⟨ax + y,z⟩ = a⟨x,z⟩ + ⟨y, z⟩ (第一个变量的线性性)。

  2. ⟨x,y ⟩ = ⟨y,x ⟩ (对称性)。

  3. ⟨x,x ⟩ > 0 对所有 x ⁄= 0 (正定性)。

带有内积的向量空间称为内积空间。

立刻,我们可以立即推导出两个性质。首先,

⟨0,x ⟩ = ⟨0x,x ⟩ = 0⟨x,x⟩ = 0. (2.2)

作为特例,⟨0,0⟩ = 0。就像我们之前看到的范数一样,有更多的性质成立:如果 ⟨x,x⟩ = 0,那么 x = 0。这是由正定性和(2.2)推导出来的。

此外,由于第一个变量的对称性和线性,内积在第二个变量上也是线性的。因此,它们被称为双线性的。

为了熟悉这个概念,让我们看一些例子!

示例 1. 像往常一样,内积空间的典型且最普遍的例子是 ℝ^n,其中内积 ⟨⋅,⋅⟩ 被定义为

 ∑n ⟨x,y⟩ = xiyi, x = (x1,...,xn ), y = (y1,...,yn). i=1

这个双线性函数通常被称为点积。配备了这个,ℝ^n 被称为 n 维欧几里得空间。这是机器学习中的一个核心概念,因为数据通常表示为欧几里得空间中的向量。因此,我们将在本书中详细探索这个空间的结构。

示例 2. 除了欧几里得空间,还有其他在数学和机器学习中扮演重要角色的内积空间。如果你熟悉积分,在某些函数空间中,双线性函数

 ∫ ∞ ⟨f,g⟩ = f(x)g(x)dx −∞

定义了一个具有非常丰富和美丽结构的内积空间。

⟨f,g⟩ 的对称性和线性是显而易见的。只有正定性似乎是一个问题。

例如,如果 f 被定义为

 ( |{1 if x = 0, f(x) = |(0 otherwise,

那么 f ≠ 0,但 ⟨f,f⟩ = 0. 这个问题可以通过“重载”相等操作符并让 f = g 当且仅当 ∫ [−∞]^∞|f(x) −g(x)|² dx = 0 来避免。尽管像这样的函数空间在数学和机器学习中扮演着重要角色,但它们的研究超出了我们讨论的范围。

2.2.1 生成的范数

回想一下,ℝ^n 中的 2-范数定义为 ∥x∥[2] = (∑ [i=1]nx[i]²)(1∕2),根据我们在此处对内积的定义,它等于 ∘ ------ ⟨x,x⟩。这不是巧合。内积可以用来定义向量空间上的范数。

为了确切地展示这一点,我们需要一个简单的工具:柯西-施瓦茨不等式。

定理 8.(柯西-施瓦茨不等式)

设 V 为一个内积空间。那么,对于任何 x,y ∈ V,不等式

|⟨x, y⟩|2 ≤ ⟨x,x⟩⟨y,y⟩

成立。

证明。此时,我们对内积了解得还不多,只知道它的核心定义特性。因此,我们将使用一个小技巧。对于任何 λ ∈ ℝ,正定性意味着

⟨x + λy,x + λy ⟩ ≥ 0.

另一方面,由于双线性(即,两个变量的线性)和对称性,我们有

2 ⟨x + λy, x+ λy ⟩ = ⟨x,x ⟩+ 2λ⟨x,y ⟩+ λ ⟨y,y⟩, (2.3)

这是一个关于λ的二次多项式。一般来说,我们知道,对于任何形如 ax² + bx + c 的二次多项式,其根由公式给出:

 √ -------- − b-±--b2 −-4ac- x1,2 = 2a .

由于

⟨x + λy,x + λy ⟩ ≥ 0,

由(2.3)定义的多项式最多只有一个实根。因此,判别式 b² − 4ac 非正。将多项式(2.3)的系数代入判别式公式,我们得到

|⟨x, y⟩|² − ⟨x, x⟩⟨y, y⟩ ≤ 0,

这就完成了证明。

柯西-施瓦茨不等式可能是研究内积空间中最有用的工具之一。接下来我们将看到的一个应用是展示内积如何定义范数。

定理 9.(由内积生成的范数)

设 V 为内积空间。那么,定义函数 ∥⋅∥ : V → 0, ∞) 由内积诱导。则,

![ ∘ ------ ∥x∥ = ⟨x, x⟩ 是 V 上的一个范数。证明。根据范数的定义,我们必须证明三个性质成立:正定性、齐次性和三角不等式。前两个性质可以通过内积的性质轻松推导出来。三角不等式则来自柯西-施瓦茨不等式:∥x + y∥² = ⟨x + y, x + y⟩= ∥x∥² + ∥y∥² + 2⟨x, y⟩≤ ∥x∥² + ∥y∥² + 2∥x∥∥y∥= (∥x∥ + ∥y∥)²,从中得出三角不等式。因此,内积空间也是带有范数的空间。它们具有适当的代数和几何结构,我们需要这些结构来表示、操作和转换数据。最重要的是,定理 9 是可以反向的!也就是说,给定一个范数 ∥⋅∥,我们可以定义一个匹配的内积。定理 10.(极化恒等式)设 V 为内积空间,∥⋅∥ 为由内积诱导的范数。那么,1 ⟨x, y⟩ = -(∥x + y∥² − ∥x∥² − ∥y∥²). 2 (2.4)

换句话说,可以从范数生成内积,而不仅仅是反过来。

证明。由于内积是双线性的,我们有

⟨x + y, x + y⟩ = ⟨x, x⟩ + 2⟨x, y⟩ + ⟨y, y⟩,

从中得出极化恒等式(2.4)。

2.2.2 正交性

在除 ℝ² 外的向量空间中,包含角度的概念完全不明确。例如,在向量是函数的空间中,没有直观的方法定义两个函数之间的角度。然而,正如(2.1)所示,在特例 ℝ² 中,这些可以被推广。

定义 10.(向量的正交性)

设 V 为内积空间,x, y ∈ V 。我们说 x 和 y 互为正交,当且仅当

⟨x, y⟩ = 0.

正交性用 x ⊥ y 表示。

为了说明内积和正交性如何定义向量空间上的几何,我们来看看经典的毕达哥拉斯定理在这个新形式中的表现。回想一下,原版的定理表示,在直角三角形中,a² + b² = c²,其中 c 是斜边的长度,而 a 和 b 是其他两边的长度。

在内积空间中,这个概念以以下方式进行推广。

定理 11.(毕达哥拉斯定理)

设 V 为内积空间,x, y ∈ V 。那么,x 和 y 互为正交,当且仅当

⟨x + y, x + y⟩ = ⟨x, x⟩ + ⟨y, y⟩。 (2.5)

证明。根据内积和正交性的定义,证明过程很直接。由于双线性,我们有

⟨x + y, x + y⟩ = ⟨x, x + y⟩ + ⟨y, x + y⟩ = ⟨x, x⟩ + 2⟨x, y⟩ + ⟨y, y⟩.

由于 x 和 y 正交,我们有⟨x, y⟩ = 0。因此,方程简化为:

⟨x + y, x + y⟩ = ⟨x, x⟩ + ⟨y, y⟩.

这完成了证明。

为什么这是毕达哥拉斯定理的另一种形式?因为范数和内积通过⟨x, x⟩ = ∥x∥²相联系,(11)是等价的

∥x + y∥² = ∥x∥² + ∥y∥²,

这正是著名的“ 2 2 2 a + b = c ”。

2.2.3 内积的几何解释

从一般定义来看,很难对内积有直观的理解。然而,通过使用正交性这一概念,我们可以直观地了解⟨x, y⟩对任何 x 和 y 的含义。

直观地说,任何 x 都可以分解为两个向量 x[o] + x[p]的和,其中 x[o]与 y 正交,而 x[p]与 y 平行。

PIC

图 2.8:x 分解为与 y 平行和正交的分量

让我们把直觉弄得更精确一些。我们如何找到 x[p]和 x[o]呢?由于 x[p]与 y 的方向相同,因此可以写成 x[p] = cy,其中 c 是某个标量 c ∈ℝ。因为 x[p]和 x[o]的和等于 x,所以我们也有 x[o] = x − x[p] = x − cy。

由于 x[o]与 y 正交,因此常数 c 可以通过解方程来确定。

⟨x − cy, y⟩ = 0.

通过利用内积的双线性性质,我们可以从这个方程中求解出 c。因此,我们得到

c = ⟨x, y⟩. ⟨y, y⟩

所以,

xp = ⟨x, y⟩-y, ⟨y, y⟩ ⟨x, y⟩- xo = x − ⟨y, y⟩y. (2.6)

我们称 x[p]为 x 在 y 上的正交投影。这是一个常见的变换,因此我们将引入符号

projy = ⟨x, y⟩ ⟨y, y⟩ y. (2.7)

从中,我们可以看出 y 与 projy 之间的比例关系可以通过内积来描述。

到目前为止,我们已经看到可以使用内积来定义两个向量之间的正交关系。那么,我们能否用它来测量(并且在某些情况下,甚至定义)角度呢?答案是肯定的!接下来,我们将看到如何做到这一点,最终得出从基本几何学中已经熟悉的公式(2.1)。

为了构建我们的直觉,让我们选择两个任意的 n 维向量 x,y ∈ℝ^n。x + y 的内积可以通过双线性性质来计算。

PIC

图 2.9:x 与 y 的和

这样,我们得到

⟨x + y, x + y⟩ = ∥x + y∥² = ∥x∥² + ∥y∥² + 2⟨x, y⟩. (2.8)

另一方面,考虑到 x、y 和 x + y 形成一个三角形,我们可以使用余弦定律(en.wikipedia.org/wiki/Law_of_cosines)将⟨x + y, x + y⟩ = ∥x + y∥²以另一种形式表示。

在这里,余弦定律意味着

∥x + y∥² = ∥x∥² + ∥y∥² − 2∥x∥∥y∥ c◟os(π◝ −◜-α-)◞ [=−cos α] 。(2.9)

通过结合 (2.8) 和 (2.9),我们得到

⟨x,y⟩ = ∥x∥∥y ∥cosα.

也就是说,在 ℝ^n 中,x 和 y 之间的夹角可以通过以下方式提取:

α = arccos  ⟨x,y⟩ ------- ∥x∥∥y ∥ 。(2.10)

PIC

图 2.10:由 x、y 和 x + y 组成的三角形

如果在向量空间中没有定义向量之间的角度怎么办?我们已经看到过一些向量空间(第 1.1.1 节),其中元素是多项式、函数以及其他数学对象。在这些情况下,(2.10)可以用来定义角度!

让我们进一步探讨这个想法,看看如何使用内积来度量相似性。

结合我们将内积解释为正交投影的几何理解,让我们关注当 x 和 y 都是单位范数时的情况。在这种特殊情况下,正交投影等于

projy(x) = ⟨x,y ⟩y (∥x∥ = ∥y∥ = 1).

因此,⟨x,y⟩ 精确地描述了正交投影的符号大小。(当projy(x) 和 y 方向相反时,它可能是负数。)

鉴于此,我们可以看到,内积等于两个向量之间夹角的余弦值。让我们画一张图来说明!(回想一下,在直角三角形中,余弦是邻边和斜边长度的比值。在这种情况下,邻边的长度是 ⟨x,y⟩,而斜边的长度是单位长度。)

PIC

图 2.11:两个单位向量的内积等于它们夹角的余弦值

在机器学习中,这个量常用于衡量两个向量的相似性。

因为任何向量 x 都可以通过变换 x → x∕∥x∥ 缩放为单位范数,我们定义余弦相似度为

cos(x,y) = ⟨ ‑x‑‑ ∥x∥ , ‑y‑‑ ∥y∥ ⟩ 。(2.11)

如果 x 和 y 表示两个数据样本的特征向量,cos(x,y) 告诉我们这些特征是如何共同变化的。注意,由于缩放的原因,两个具有较高余弦相似度的样本可能相距很远。因此,这并不能揭示它们在特征空间中的相对位置。

2.2.4 正交与正交归一基

从相似性的角度来看,正交意味着一个向量不包含另一个向量的“信息”。当我们学习相关性时,我们将更加精确地定义这个概念,但它对于内积空间的结构有着明确的影响。回想一下,在介绍基向量时(第 1.2 节),我们的动机是找到一个最小的向量集合,可以用来表示其他任何向量。通过引入正交性,我们可以更进一步。

定义 11.(正交与正交归一基)

V 是一个向量空间,S = {v1,...,vn } 是其基。我们说 S 是正交基,如果 ⟨vi,vj⟩ = 0 当且仅当 i ⁄= j

此外, S 被称为正交归一,如果

 ( |{ 1, 如果 i = j, ⟨vi,vj⟩ = |( 0, 如果 i ⁄= j.

换句话说, S 是正交归一的,如果,除了是正交的外,每个向量都有单位范数。

正交和正交归一基非常方便使用。如果一个基是正交的,我们可以通过简单地将其向量缩放到单位范数,轻松获得一个正交归一基。因此,我们大多数时候会使用正交归一基向量。

为什么我们如此喜欢正交归一基?为了看到这一点,设 {v[1],…,v[n]} 是任意基,且 x 是任意向量。我们知道

 ∑n x = xivi, i=1

但是我们如何找到系数 x[i] 呢?有一种涉及线性方程的一般方法,我们将在第六章中看到,不过如果 {v[i]}[i=1]^n 是正交归一的,情况就简单多了。

这一点在以下定理中得到了更精确的阐述。

定理 12\。

V 是一个向量空间,且 S = {v1,...,vn } V 的正交归一基。那么,对于任意的 x ∈ V

x = ∑ [i=1] ^n ⟨x,v[i]⟩ v[i] (2.12)

保持成立。

证明。由于 v1,...,vn 形成一个基,我们可以将 x 表示为

 ∑n x = xivi i=1

对某些标量 xi 。通过内积的线性性,我们得到

 ∑n ∑n ⟨x,vj⟩ = ⟨ xivi,vj⟩ = xi⟨vi,vj⟩. i=1 i=1

由于 v ,...,v 1 n 形成一个正交归一基,我们有

 (| {1, 如果 i = j, ⟨vi,vj⟩ = | (0, 如果 i ⁄= j.

因此,求和简化为

⟨x,vj ⟩ = xj.

这证明了这个结果。

因此,系数可以通过内积计算得到。换句话说,对于正交归一基,x[j] 仅依赖于第 j 个基向量。

由于正交归一性的另一个结果,计算范数也变得更加容易,因为我们总是可以用系数来表示它。更准确地说,我们有

2 ∥x∥ = ⟨x,x⟩ ∑n ∑n = ⟨ xivi, xjvj ⟩ i=1 j=1 ∑n ∑n = xixj⟨vi,vj⟩ i=1j=1 n = ∑ x2. i i=1 (2.13)

这被称为帕尔塞瓦尔恒等式。换句话说,如果 x 是用正交归一基表示的,那么它的范数很容易找到。这个公式与欧几里得范数如此相似并非偶然!(请注意,这里 ∥⋅∥ 是一个一般范数。) 事实上,平方的欧几里得范数

 n ∥x ∥2= ∑ x2, x = (x ,...,x ) ∈ ℝn 2 i 1 n i=1

这只是使用标准基的 (2.13)。

2.2.5 格拉姆-施密特正交化过程

正交基很棒,但我们如何找到它们呢?

有一个通用的方法叫做 Gram-Schmidt 正交化过程,它解决了这个问题。该算法接受任何一组基向量 {v[1],…,v[n]},并输出一个正交规范基 {e[1],…,e[n]}

使得

span(v1,...,vk) = span (e1,...,ek), k = 1,...,n,

也就是说,两组中前 k 个向量生成的子空间相匹配。

我们怎么做呢?这个过程很简单。让我们首先集中精力找到一个正交系统,我们可以稍后将其归一化以达到正交规范化。我们将迭代地构建我们的集合 {e[1],…,e[n]}。很明显,

e1 := v1

是一个不错的选择。现在,我们的目标是找到 e[2],使得 e[2] ⊥ e[1],并且它们共同生成与 {v[1],v[2]} 相同的子空间。还记得我们在第 2.2.3 节讨论过的正交性的几何解释吗?v[2] 相对于 e[1] 的正交分量将是 e[2] 的一个好选择。因此,设

 ⟨v2,e1⟩ e2 := v2 − proje1(v2) = v2 −-------e1. ⟨e1,e1⟩

根据定义,显然有 e[2] ⊥ e[1],同时也可以看出 {e[1],e[2]} 生成的子空间与 {v[1],v[2]} 相同。

在下一步中,我们执行相同的过程。我们将 v[3] 投影到由 e[1] 和 e[2] 生成的子空间上,然后将 e[3] 定义为 v[3] 和投影的差异。即,

e3 := v3 − proje1,e2(v3 ) = v3 − ⟨v3,e1⟩e1 − ⟨v3,e2⟩e2. ⟨e1,e1⟩ ⟨e2,e2⟩

这样,我们本质上去除了 e[1] 和 e[2] 对 v[3] 的“贡献”,从而得到一个与之前的向量正交的 e[3]。

一般而言,如果我们已经有 e[1],…,e[k],那么可以通过以下方式找到向量 e[k+1]:

ek+1 := vk+1 − proj (vk+1), e1,...,ek

其中

∑k ⟨x, ei⟩ proje1,...,ek(x) = -------ei i=1⟨ei,ei⟩ (2.14)

是广义正交投影算子,将一个向量投影到由 {e[1],…,e[k]} 生成的子空间上。

为了验证 e[k+1] ⊥ e[1],…,e[k],我们有

∑ k ⟨vk+1,ei⟩ ⟨ek+1,ej⟩ = ⟨vk+1 − ⟨ei,ei⟩ ei,ej⟩ i=1 ∑k ⟨vk+1,ei⟩ = ⟨vk+1,ej⟩− ---------⟨ei,ej⟩ i=1 ⟨ei,ei⟩ = ⟨v ,e ⟩− ⟨v ,e ⟩ k+1 j k+1 j = 0. (2.15)

由于 e[i] 的正交性和内积的线性性质。由于 {e[1],…,e[k]} 和 {v[1],…,v[k]} span 同一子空间,并且 e{k + 1} 是 v[k+1] 和 e[1],…,e[k] 的线性组合(其中 v[k+1] 的系数非零),

span(v ,...,v ) = span (e ,...,e ) 1 k+1 1 k+1

也可以推导出来。这可以重复进行,直到我们用完所有向量并找到 {e[1],…,e[n]}。

为了进一步参考、数学正确性,以及一点点完美主义,我们将上述所有内容总结成一个定理。

定理 13.(Gram-Schmidt 正交化过程)

设 V 为一个内积向量空间,{v[1],…,v[n]} ⊆ V 是一组线性无关的向量。那么,存在一个正交规范集 {e[1],…,e[n]} ⊆ V,使得

span(e1,...,ek) = span(v1,...,vk)

对于任何 k = 1,…,n,公式成立。

因此,我们可以断言,每个有限维的内积空间都有一个正交归一化基。我们甚至可以通过 Gram-Schmidt 过程显式构造它。

推论 1.(正交归一化基的存在)

设 V 为有限维内积空间。那么,V 中存在一个正交归一化基。

更进一步,我们可以将定理 13 及其证明视为一个算法。

(Gram-Schmidt 正交化过程)输入:一组线性无关的向量 {v[1],…,v[n]}⊆V。输出:一组正交归一化的向量 {e[1],…,e[n]},使得span(e1,...,ek) = span(v1,...,vk) 对于任何 k = 1,…,n 均成立。

注释 3.(Gram-Schmidt 过程中的线性相关输入)如果我们将 Gram-Schmidt 正交化过程应用于一组线性相关的向量,会发生什么?

为了更好地理解这个问题,让我们考虑一个简单的情况:两个向量 {v[1],v[2] = cv[1]},其中 c 是一个任意的标量。e[1] 被选择为 v[1],而 e[2] 定义为

e = v − proj (v ). 2 2 e1 2

通过展开投影项,我们得到

e2 = v2 − ⟨v2,e1⟩e1. ⟨e1,e1⟩

由于 v[1] = e[1] 且 v[2] = cv[1],我们得到

e[2] = cv[1] −c⟨v1,v1-⟩ ⟨v1,v1⟩v[1] = cv[1] −cv[1]= 0

这个结果可以推广:当 Gram-Schmidt 过程遇到与之前的向量线性相关的输入向量时,输出会生成一个零向量。

2.2.6 正交补

之前我们看到,给定一个固定向量 y ∈V,我们可以将任何 x ∈V 分解为 x = x[o] + x[p],其中 x[o] 与 y 正交,而 x[p] 与 y 平行。(我们用这个来提供内积几何解释的动机,见 2.2.3 节。)

这是一个重要工具,在这一节中,我们将看到当 y 被任意子空间 S ⊂V 替代时,这一分解的类似物仍然成立。为了理解这一点,让我们讨论子空间的正交性。(如果你想回顾子空间的定义,查看定义 5。)

定义 12.(正交子空间)

设 V 为任意的内积空间。我们说子空间 S[1] 和 S[2] ⊆V 是正交的,当且仅当对于每一对向量 x ∈S[1] 和 y ∈S[2],有 ⟨x,y⟩ = 0。记作 S[1] ⊥S[2]。

例如,x 轴和 y 轴是 ℝ² 中的正交子空间。(就像在 ℝ³ 中,xy 平面与 z 轴正交一样。)

类似地,我们可以讨论一个向量与子空间的正交性:如果 x 正交于 S 中的所有向量,则称 x 与子空间 S 正交,符号为 x ⊥S。

构造正交子空间最直接且重要的方法之一是取正交补。

定义 13.(正交补)

设 V 为一个任意的内积空间,S ⊆ V 为子空间。定义的集合为

S⊥ := {x ∈ V | x ⊥ S } (2.16)

称为 S 的正交补。

S^⊥ 不仅仅是一个集合,它是一个子空间,正如我们即将看到的那样。

定理 14\。

V 为任意内积空间,![1. S ⊥ S 正交,

  1. S ⊥ V 的一个子空间,

  2.  ⊥ S ∩ S = {0}

证明:根据子空间的定义,我们只需证明 S^⊥ 在加法和标量乘法下是封闭的。由于内积是双线性的,这一点很容易证明:

⟨ax + by,z⟩ = a⟨x,z⟩+ b⟨y,z⟩ = 0

对任意向量 x,y ∈S^⊥,z ∈S 和标量 a,b,成立。

为了证明 S ∩ S^⊥ = {0},我们取任意 x ∈ S ∩ S^⊥。根据 S^⊥ 的定义,我们有 ⟨x,x⟩ = 0。由于内积按定义是正定的,x 必须是零向量 0。

回想一下,任意 x ∈V 如何相对于固定向量 y 被分解为平行分量和正交分量?从子空间的角度来看,我们可以将其重新表述为

 ⊥ V = span(y) + span (y) ,

也就是说,V 可以写成由 y 张成的向量空间与其正交补的直和形式。这是一个非常强大的结果,因为它使我们能够将 x 从 y 中解耦。例如,如果我们将向量看作特征的集合(就像我们最喜爱的鸢尾花数据集中的花萼和花瓣宽度与长度测量一样),y 可以表示我们希望从分析中排除的某个特征。

利用正交补的概念,我们可以使这个命题在数学上更加精确。我们还可以更加一般化。事实上,这个分解

V = S + S⊥

对任意子空间 S 成立!让我们来看一下证明!

定理 15\。

设 V 为任意有限维内积空间,S ⊂V 为其子空间。那么,

V = S + S⊥

成立。

证明:设 v[1],…,v[k] ∈S 是 S 的一组基。像在 Gram-Schmidt 过程中的做法一样,我们可以定义广义的正交投影(3.2.5),其形式为

 ∑k proj (x ) = ⟨x,vi⟩vi. v1,...,vk i=1

利用这个,我们可以将任意 x ∈V 分解为

x = (x − proj_{v1,...,vk}(x)) + proj_{v1,...,vk}(x) (2.17)

由于 projv[1],…,v[k] 是 v[i] 的线性组合,它属于 S。另一方面,内积的双线性性质使得 x − projv[1],…,v[k] ∈S^⊥。事实上,正如我们所见

 ∑k proj (x ) = ⟨x,vi⟩vi. v1,...,vk i=1

向量 x − projv[1],…,v[k] 与每个 v[j] 正交。因此,由于 v[1],…,v[k] 是 S 的一组基,x 也正交于 S,因此 x − proje[1],…,v[k] ∈S^⊥。

每个 x ∈V 都可以分解为 S 中一个向量与 S^⊥ 中一个向量的和,正如 (3.2.6) 所给出的,这意味着 V = S + S^⊥,这是我们要证明的。

2.3 摘要

在本章中,我们学到了,除了由加法和标量乘法给出的代数结构,向量还具有从内积中产生的美丽几何。通过内积,我们得到了范数;通过范数,我们得到了度量;通过度量,我们得到了几何和拓扑。

距离、相似性、角度和正交性都来源于内积这一简单概念。这些在理论和实践中都非常有用。例如,内积为我们提供了通过所谓的余弦相似性来量化两个向量相似度的方法,但它们也为我们提供了通过正交性的概念来寻找最优基的方法。

总结一下,我们已经学习了什么是范数和距离,内积的定义,内积如何给出角度和范数,以及这些在机器学习中的应用为何如此重要。

除了基本的定义和性质,我们还遇到了第一个算法:格拉姆-施密特过程,将一组向量转化为正交标准基。这是最好的基。

在下一节中,我们将把所有这些理论付诸实践,迈出计算线性代数的第一步。我们开始吧!

2.4 问题

问题 1. 设 V 为一个向量空间,定义函数 d : V ×V → 0,∞) 为:

![ ( |{ 0 如果 x = y, d(x,y) = |( 1 否则。

(a) 证明 d 是一个度量(见定义 8)。

(b) 证明 d 无法来自于范数。

问题 2. 设 S[n] 为所有长度为 n 的 ASCII 字符串集合,并定义任意两个 x,y ∈S[n] 的汉明距离 h(x,y) 为 x 和 y 在相应位置不同的个数。

例如,

h(”001101”,”101110”) = 2, h(”metal”,”petal”) = 1.

证明 h 满足度量的三个定义性质。(注意,S[n] 不是一个向量空间,因此从严格意义上说,汉明距离不是度量。)

问题 3. 设 ∥⋅∥ 为向量空间 ℝ^n 上的一个范数,定义映射 f : ℝ^n →ℝ^n,

f : (x1,x2,...,xn ) ↦→ (x1,2x2,...,nxn ).

证明:

∥x ∥∗ := ∥f(x)∥

是 ℝ^n 上的一个范数。

问题 4. 设 a[1],…,a[n] > 0 为任意正数。证明:

 n ∑ n ⟨x,y ⟩ := aixiyi, x,y ∈ ℝ . i=1

是一个内积,其中 x = (x[1],…,x[n]) 和 y = (y[1],…,y[n])。

问题 5. 设 V 为有限维内积空间,v[1],…,v[n] ∈V 为 V 中的一个基,定义:

ai,j := ⟨vi,vj⟩.

证明对于任意 x,y ∈V,

 ∑n ⟨x,y ⟩ = ai,jxiyj, i,j=1

其中 x = ∑ [i=1]^n x[i]v[i],y = ∑ [i=1]^n y[i]v[i]。

问题 6. 设 V 为有限维实内积空间。

(a) 设 y ∈V 为任意向量。证明:

f : V → ℝ, x ↦→ ⟨x,y⟩

是一个线性函数(即,对于所有 u,v ∈V 和 α,β ∈ℝ,满足 f(αu + βv) = αf(u) + βf(v))。

(b) 设 f : V →ℝ 为任意线性函数。证明存在一个 y ∈V,使得:

f(x) = ⟨x,y⟩.

(注意,(b) 是 (a) 的反向,是一个更有趣的结果。)

问题 7. 设 V 为实数内积空间,且 ∥x∥ = ∘ ------ ⟨x,x ⟩ 为生成的范数。证明

方程 2.16 (2.18)

这被称为平行四边形法则,因为如果我们将 x 和 y 看作确定平行四边形的两条边,(3.4) 将平行四边形两条边的长度与对角线的长度联系起来。

问题 8. 设 V 为实数内积空间,u,v ∈V 。证明若

⟨u, x⟩ = ⟨v, x⟩

对于所有的 x ∈V,若成立,则 u = v。

问题 9. 对输入向量应用格拉姆-施密特过程

v1 = (2,1,1), v2 = (1,1,1), v3 = (1,0,1).

加入我们在 Discord 上的社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过问我任何问题的环节与作者互动,等等。扫描二维码或访问链接加入社区。packt.link/math

图片

线性代数实战

现在我们已经理解了向量空间的几何结构,是时候再次将理论付诸实践了。在本章中,我们将动手实践范数、内积和 NumPy 数组操作。最重要的是,我们将首次接触矩阵。

上一次我们将理论转化为代码时,停留在为向量寻找理想表示形式的阶段:NumPy 数组。NumPy 是为线性代数而构建的,它比原生 Python 对象处理计算的速度要快得多。

那么,让我们初始化两个 NumPy 数组来玩一玩吧!

import numpy as np 

x = np.array([1.8, -4.5, 9.2, 7.3]) 
y = np.array([-5.2, -1.1, 0.7, 5.1])

在线性代数中,以及在大多数机器学习中,几乎所有的操作都涉及逐个遍历向量的分量。例如,加法可以这样实现。

def add(x: np.ndarray, y: np.ndarray): 
    x_plus_y = np.zeros(shape=len(x)) 

    for i in range(len(x_plus_y)): 
        x_plus_y[i] = x[i] + y[i] 

    return x_plus_y
add(x, y)
array([-3.4, -5.6,  9.9, 12.4])

当然,这远非最优。(如果向量的维度不同,这可能甚至不起作用。)

例如,加法是高度并行化的,而我们的实现并没有充分利用这一点。使用两个线程,我们可以同时执行两个加法操作。因此,两个二维向量的加法只需要一步,计算一个 x[0] + y[0],而另一个计算 x[1] + y[1]。原生 Python 无法访问如此高性能的计算工具,但 NumPy 可以通过 C 实现的函数来实现。反过来,C 使用 LAPACK(线性代数程序包)库,LAPACK 调用了 BLAS(基础线性代数子程序)。BLAS 在汇编级别进行了优化。

所以,每当可能时,我们应尽量以 NumPythonic 的方式(是的,我刚刚造了这个词)处理向量。对于向量加法,这仅仅是 + 运算符,如我们之前所见。

np.equal(x + y, add(x, y))
array([ True,  True,  True,  True])

顺便说一下,你永远不应该使用 == 运算符来比较浮点数,因为由于浮点数表示,可能会出现内部舍入误差。下面的例子说明了这一点。

1.0 == 0.3*3 + 0.1
False
0.3*3 + 0.1
0.9999999999999999

要比较数组,NumPy 提供了 np.allclose 和 np.equal 函数。这些函数逐元素比较数组,返回一个布尔数组。从中,可以使用内建的 all 函数来判断是否所有元素都匹配。

all(np.equal(x + y, add(x, y)))
True

在接下来的部分,我们将简要回顾如何在实践中使用 NumPy 数组。

第五章:3.1 NumPy 中的向量

我们有两个操作是必须对向量进行的:逐元素应用函数或求元素的和/积。由于 +、* 和 ** 运算符已为我们的数组实现,某些函数可以从标量扩展,如下例所示。

def just_a_quadratic_polynomial(x):
    return 3*x**2 + 1

x = np.array([1.8, -4.5, 9.2, 7.3])
just_a_quadratic_polynomial(x)
array([ 10.72,  61.75, 254.92, 160.87])

然而,我们不能直接将 ndarray 插入到每个函数中。例如,让我们看看 Python 内建的 exp 函数,它来自 math 模块。

from math import exp 

exp(x)
--------------------------------------------------------------------------- 
TypeError                               Traceback (most recent call last) 
Cell In[10], line 3 
     1 from math import exp 
---->/span> 3 exp(x) 

TypeError: only length-1 arrays can be converted to Python scalars

为了克服这个问题,我们可以手动逐元素应用函数。

def naive_exp(x: np.ndarray): 
    x_exp = np.empty_like(x) 

    for i in range(len(x)): 
        x_exp[i] = exp(x[i]) 

    return x_exp

(回想一下,np.empty_like(x) 创建一个未初始化的数组,该数组的维度与 x 相匹配。)

naive_exp(x)
array([6.04964746e+00, 1.11089965e-02, 9.89712906e+03, 1.48029993e+03])

一个略微不那么天真的实现会使用列表推导式来达到相同的效果。

def bit_less_naive_exp(x: np.ndarray): 
    return np.array([exp(x_i) for x_i in x]) 

bit_less_naive_exp(x)
array([   6\. ,    0\. , 9897.1, 1480.3])

尽管列表推导式更简洁且更具可读性,但它们仍然无法避免核心问题:Python 中的 for 循环。

这个问题通过 NumPy 著名的 ufuncs 解决,即对整个数组逐元素操作的函数(numpy.org/doc/stable/reference/generated/numpy.ufunc.html)。由于它们是用 C 实现的,因此运行速度非常快。例如,指数函数 f(x) = e^x 由 np.exp 给出。

np.exp(x)
array([6.04964746e+00, 1.11089965e-02, 9.89712906e+03, 1.48029993e+03])

不出所料,我们的实现结果一致。

all(np.equal(naive_exp(x), np.exp(x)))
True
all(np.equal(bit_less_naive_exp(x), np.exp(x)))
True

同样,使用 NumPy 函数和操作的优势不止于简单。在机器学习中,我们非常关心速度,正如我们即将看到的,NumPy 再次展现了其优势。

from timeit import timeit

n_runs = 100000
size = 1000

t_naive_exp = timeit(
    "np.array([exp(x_i) for x_i in x])",
    setup=f"import numpy as np; from math import exp; x = np.ones({size})",
    number=n_runs
)

t_numpy_exp = timeit(
    "np.exp(x)",
    setup=f"import numpy as np; from math import exp; x = np.ones({size})",
    number=n_runs
)

print(f"Built-in exponential:    \t{t_naive_exp:.5f} s")
print(f"NumPy exponential:       \t{t_numpy_exp:.5f} s")
print(f"Performance improvement: \t{t_naive_exp/t_numpy_exp:.5f} times faster")
Built-in exponential:         18.35177 s 
NumPy exponential:             0.87458 s 
Performance improvement:       20.98356 times faster

如需进一步参考,您可以在这里找到可用的 ufunc 列表:numpy.org/doc/stable/reference/ufuncs.html\#available-ufuncs

那么,关于汇总元素并返回单一值的操作呢?不出所料,这些操作在 NumPy 中也可以找到。例如,让我们看看求和。在数学公式中,我们要实现的是:

 n ∑ n sum (x) = xi, x = (x1,...,xn ) ∈ ℝ . i=1

一种基本方法可能是这样的:

def naive_sum(x: np.ndarray): 
    val = 0 

    for x_i in x: 
        val += x_i 

    return val 

naive_sum(x)
np.float64(13.799999999999999)

或者,我们可以使用 Python 内置的求和函数。

sum(x)
np.float64(13.799999999999999)

故事是一样的:NumPy 做得更好。我们可以调用函数 np.sum,或者使用数组方法 np.ndarray.sum。

np.sum(x)
np.float64(13.799999999999999)
x.sum()
np.float64(13.799999999999999)

你现在知道我喜欢计时函数,所以我们再来比较一次性能。

t_naive_sum = timeit( 
    /span>sum(x) 
    setup=f/span>import numpy as np; x = np.ones({size}) 
    number=n_runs 
) 

t_numpy_sum = timeit( 
    /span>np.sum(x) 
    setup=f/span>import numpy as np; x = np.ones({size}) 
    number=n_runs 
) 

print(f/span>Built-in sum:            \t{t_naive_sum:.5f} s 
print(f/span>NumPy sum:               \t{t_numpy_sum:.5f} s 
print(f/span>Performance improvement: \t{t_naive_sum/t_numpy_sum:.5f} times faster
Built-in sum:                 5.52380 s 
NumPy sum:                     0.35774 s 
Performance improvement:       15.44076 times faster

类似地,乘积

 ∏n prod (x ) = xi, x = (x1,...,xn) ∈ ℝn i=1

由 np.prod 函数和 np.ndarray.prod 方法实现。

np.prod(x)
np.float64(-543.996)

在很多情况下,我们需要找到一个数组的最大值或最小值。我们可以使用 np.max 和 np.min 函数来实现。(与其他函数类似,它们也可以作为数组方法使用。)经验法则是,如果你想进行任何数组操作,使用 NumPy 函数。

3.1.1 范数、距离和点积

现在我们已经回顾了如何高效地对向量进行操作,接下来是深入探讨真正有趣的部分:范数和距离。

让我们从最重要的一个开始:欧几里得范数,也称为 2-范数,定义为

 ∑n ∥x∥2 = ( x2i)1∕2, x = (x1,...,xn) ∈ ℝn. i=1

一个简单的实现如下。

def euclidean_norm(x: np.ndarray): 
    return np.sqrt(np.sum(x**2))

请注意,我们的 euclidean_norm 函数是与维度无关的;也就是说,它适用于所有维度的数组。

# a 1D array with 4 elements, which is a vector in 4-dimensional space 
x = np.array([-3.0, 1.2, 1.2, 2.1]) 

# a 1D array with 2 elements, which is a vector in 2-dimensional space 
y = np.array([8.1, 6.3]) 

euclidean_norm(x)
np.float64(4.036087214122113)
euclidean_norm(y)
np.float64(10.261578825892242)

等等,我刚刚不是说过,我们应该尽可能使用 NumPy 函数吗?范数足够重要,以至于有自己的函数:np.linalg.norm。

np.linalg.norm(x)
np.float64(4.036087214122113)

通过快速检查,我们可以验证这些结果是否与我们的向量 x 匹配。

np.equal(euclidean_norm(x), np.linalg.norm(x))
np.True_

然而,欧几里得范数只是 p-范数的特例。回想一下,对于任何 p ∈ 0,∞),我们通过以下公式定义 p-范数:

![ n ∥x∥ = (∑ |x |p)1∕p, x = (x ,...,x ) ∈ ℝn, p i=1 i 1 n 和∥x∥∞ = max {|x1 |,...,|xn |}, x = (x1,...,xn) ∈ ℝn

对于 p = ∞。保持代码库中的函数数量最少是一种好习惯,这样可以降低维护成本。我们能否将所有 p-范数合并成一个接收 p 值作为参数的 Python 函数?当然可以。我们只遇到了一个小问题:表示 ∞。Python 和 NumPy 都提供了各自的表示方法,但我们将使用 NumPy 的 np.inf。令人惊讶的是,这个是浮动类型。

type(np.inf)
float
def p_norm(x: np.ndarray, p: float): 
    if np.isinf(p): 
        return np.max(np.abs(x)) 
    elif p  1: 
        return (np.sum(np.abs(x)**p))**(1/p) 
    else: 
        raise ValueError("/span>p must be a float larger or equal than 1.0 or inf."

由于 ∞ 可以有多种表示方式,比如 Python 内建的 math.inf,我们可以通过使用 np.isinf 函数来检查一个对象是否表示 ∞,从而让我们的函数更加健壮。

快速检查显示 p_norm 按预期工作。

x = np.array([-3.0, 1.2, 1.2, 2.1]) 

for p in [1, 2, 42, np.inf]: 
    print(f/span>p-norm for p = {p}: \t {p_norm(x, p=p):.5f}"
p-norm for p = 1:        7.50000 
p-norm for p = 2:        4.03609 
p-norm for p = 42:     3.00000 
p-norm for p = inf:       3.00000

然而,再一次,NumPy 已经领先我们一步。事实上,熟悉的 np.linalg.norm 已经可以直接实现这一功能。我们可以通过将 p 的值作为参数 ord(即 order 的缩写)传递,从而用更少的代码实现相同的功能。对于 ord = 2,我们得到传统的 2-范数。

for p in [1, 2, 42, np.inf]: 
    print(f/span>p-norm for p = {p}: \t {np.linalg.norm(x, ord=p):.5f}"
p-norm for p = 1:        7.50000 
p-norm for p = 2:        4.03609 
p-norm for p = 42:     3.00000 
p-norm for p = inf:       3.00000

有些令人惊讶的是,距离度量并没有自己独立的 NumPy 函数。然而,由于最常见的距离度量是通过范数(第 2.1.1 节)生成的,我们通常可以自己编写。例如,这是欧几里得距离。

def euclidean_distance(x: np.ndarray, y: np.ndarray): 
    return np.linalg.norm(x - y, ord=2)

除了范数和距离,定义我们向量空间几何的第三个组成部分是内积。在我们的学习过程中,我们几乎只会使用点积,它在向量空间 ℝ^n 中的定义为:

 n ⟨x, y⟩ = ∑ x y, x, y ∈ ℝn. i i i=1

到现在为止,你应该能够轻松地编写一个计算该值的 Python 函数。原则上,下面的单行代码应该可以工作。

def dot_product(x: np.ndarray, y: np.ndarray): 
    return np.sum(x*y)

让我们试试看!

x = np.array([-3.0, 1.2, 1.2, 2.1]) 
y = np.array([1.9, 2.5, 3.9, 1.2]) 

dot_product(x, y)
np.float64(4.5)

当向量的维度不匹配时,函数会抛出我们预期的异常。

x = np.array([-3.0, 1.2, 1.2, 2.1]) 
y = np.array([1.9, 2.5]) 

dot_product(x, y)
--------------------------------------------------------------------------- 
ValueError                              Traceback (most recent call last) 
Cell In[39], line 4 
     1 x = np.array([-3.0, 1.2, 1.2, 2.1]) 
     2 y = np.array([1.9, 2.5]) 
---->/span> 4 dot_product(x, y) 

Cell In[37], line 2, in dot_product(x, y) 
     1 def dot_product(x: np.ndarray, y: np.ndarray): 
---->/span> 2    return np.sum(x*y) 

ValueError: operands could not be broadcast together with shapes (4,) (2,)

然而,在进一步尝试破坏代码时,发生了一件奇怪的事。我们的函数 dot_product 应该在传入一个 n 维向量和一个一维向量时失败,但实际情况并非如此。

x = np.array([-3.0, 1.2, 1.2, 2.1]) 
y = np.array([2.0]) 

dot_product(x, y)
np.float64(3.0)

我总是主张提前破坏解决方案,以避免后续的意外,而上面的例子很好地说明了这一原则的有用性。如果之前的现象发生在生产环境中,你可能会有一段正常执行的代码,却给出完全错误的结果。这是最糟糕的 bug。

在幕后,NumPy 正在执行名为广播(broadcasting)的操作。当对两个形状不匹配的数组执行操作时,它会尝试推测正确的大小,并重新调整它们的形状,以使操作得以进行。查看在计算 x*y 时发生了什么。

x*y
array([-6\. ,  2.4,  2.4,  4.2])

NumPy 猜测我们想要将 x 的所有元素乘以标量 y[0],所以它将 y = np.array([2.0]) 转换为 np.array([2.0, 2.0, 2.0, 2.0]),然后计算逐元素乘积。

广播非常有用,因为它通过自动执行转换让我们编写更简洁的代码。然而,如果你不清楚广播是如何以及何时进行的,它可能会给你带来麻烦。就像我们的情况一样,四维和一维向量的内积是没有定义的。

为了避免写过多的边界条件检查(或完全漏掉),我们在实际操作中使用 np.dot 函数来计算内积。

x = np.array([-3.0, 1.2, 1.2, 2.1]) 
y = np.array([1.9, 2.5, 3.9, 1.2]) 

np.dot(x, y)
np.float64(4.5)

当尝试调用 np.dot 时,如果数组不对齐,它会按预期失败,即便广播功能会让我们自定义的实现成功运行。

x = np.array([-3.0, 1.2, 1.2, 2.1]) 
y = np.array([2.0]) 

np.dot(x, y)
--------------------------------------------------------------------------- 
ValueError                              Traceback (most recent call last) 
Cell In[43], line 4 
     1 x = np.array([-3.0, 1.2, 1.2, 2.1]) 
     2 y = np.array([2.0]) 
---->/span> 4 np.dot(x, y) 

ValueError: shapes (4,) and (1,) not aligned: 4 (dim 0) != 1 (dim 0)

现在我们已经拥有了基本的数组操作和函数,是时候用它们做点事情了!

3.1.2 Gram-Schmidt 正交化过程

线性代数中最基本的算法之一是 Gram-Schmidt 正交化过程(定理 13),用于将一组线性无关的向量转化为标准正交集。

更精确地说,对于输入的线性无关向量集 v[1],…,v[n] ∈ℝ^n,Gram-Schmidt 过程会找到输出的向量集 e[1],…,e[n] ∈ℝ^n,使得∥e[i]∥ = 1 且⟨e[i],e[j]⟩ = 0 对所有 i≠j(即向量是正交标准的),并且 span(e[1],…,e[k]) = span(v[1],…,v[k]) 对所有 k = 1,…,n 成立。

如果你记不清楚怎么做了,可以随时回顾第 2.2.5 节,我们在其中首次描述了该算法。学习过程是一个螺旋式的过程,我们不断从新的角度回顾旧的概念。对于 Gram-Schmidt 过程来说,这是我们的第二次迭代,我们将数学公式转化为代码。

由于我们正在处理一系列向量,因此需要一个合适的数据结构来存储它们。在 Python 中,有几种可能的选择。目前我们选择了一个概念上最简单,尽管计算上不太优的方案:列表。

vectors = [np.random.rand(5) for _ in range(5)]    # randomly generated vectors in a list 
vectors
[array([0.85885635, 0.05917163, 0.42449235, 0.39776749, 0.89750107]), 
 array([0.49579437, 0.42797077, 0.21057023, 0.3091438 , 0.52590854]), 
 array([0.73079791, 0.58140107, 0.09823772, 0.14323477, 0.63606972]), 
 array([0.89495164, 0.40614454, 0.60637559, 0.61614928, 0.69006552]), 
 array([0.1996764 , 0.90298211, 0.70604567, 0.45721469, 0.02375226])]

算法的第一个组成部分是正交投影算子,定义为

 ∑k proj (x) = ⟨x,ei⟩ei. e1,...,ek i=1 ⟨ei,ei⟩

有了 NumPy 工具,实施变得非常直接。

from typing import List 

def projection(x: np.ndarray, to: List[np.ndarray]): 
    #x0022;"/span> 
    Computes the orthogonal projection of the vector ‘x‘ 
    onto the subspace spanned by the set of vectors ‘to‘. 
    #x0022;"/span> 
    p_x = np.zeros_like(x) 

    for e in to: 
        e_norm_square = np.dot(e, e) 
        p_x += np.dot(x, e)*e/e_norm_square 

    return p_x

为了检查它是否有效,让我们看一个简单的例子并可视化结果。由于本书是用 Jupyter Notebooks 编写的,我们可以直接在这里进行操作。

x = np.array([1.0, 2.0]) 
e = np.array([2.0, 1.0]) 

x_to_e = projection(x, to=[e])
import matplotlib.pyplot as plt 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(7, 7)) 
    plt.xlim([-0, 3]) 
    plt.ylim([-0, 3]) 
    plt.arrow(0, 0, x[0], x[1], head_width=0.1, color="/span>r label="/span>x linewidth=2) 
    plt.arrow(0, 0, e[0], e[1], head_width=0.1, color="/span>g label="/span>e linewidth=2) 
    plt.arrow(x_to_e[0], x_to_e[1], x[0] - x_to_e[0], x[1] - x_to_e[1], linestyle="-" 
    plt.arrow(0, 0, x_to_e[0], x_to_e[1], head_width=0.1, color="/span>b label="/span>projection(x, to=[e]) 
    plt.legend() 
    plt.show()

PIC

图 3.1:x 到 e 的投影

检查 e 和 x 的正交性——x 到 e 为我们提供了另一种验证手段。

np.allclose(np.dot(e, x - x_to_e), 0.0)
True

在为生产环境编写代码时,几个可视化和临时检查并不足够。通常会编写一套详尽的单元测试,确保函数按预期工作。为了保持讨论的进度,我们跳过了这部分,但你可以随时添加一些测试。毕竟,数学和编程不是观众的运动。

有了投影函数,我们准备好大展身手,实现 Gram-Schmidt 算法。

def gram_schmidt(vectors: List[np.ndarray]): 
    #x0022;"/span> 
    Creates an orthonormal set of vectors from the input 
    that spans the same subspaces. 
    #x0022;"/span> 
    output = [] 

    # 1st step: finding an orthogonal set of vectors 
    output.append(vectors[0]) 
    for v in vectors[1:]: 
        v_proj = projection(v, to=output) 
        output.append(v - v_proj) 

    # 2nd step: normalizing the result 
    output = [v/np.linalg.norm(v, ord=2) for v in output] 

    return output 

gram_schmidt([np.array([2.0, 1.0, 1.0]), 
              np.array([1.0, 2.0, 1.0]), 
              np.array([1.0, 1.0, 2.0])])
[array([0.81649658, 0.40824829, 0.40824829]), 
 array([-0.49236596,  0.86164044,  0.12309149]), 
 array([-0.30151134, -0.30151134,  0.90453403])]

让我们用一个简单的例子快速测试一下这个实现。

test_vectors = [np.array([1.0, 0.0, 0.0]), 
                np.array([1.0, 1.0, 0.0]), 
                np.array([1.0, 1.0, 1.0])] 

gram_schmidt(test_vectors)
[array([1., 0., 0.]), array([0., 1., 0.]), array([0., 0., 1.])]

所以,我们刚刚从头开始创建了我们的第一个算法。这就像是珠穆朗玛峰的基地营。我们已经走了很长一段路,但在我们能够从头开始创建神经网络之前,还有很长的路要走。直到那时,这段旅程将充满一些美丽的部分,而这就是其中之一。花点时间欣赏这一部分,准备好后再继续前行。

备注 4. (格拉姆-施密特过程中的线性相关输入)

记住,如果格拉姆-施密特的输入向量线性相关,则输出的一些向量为零(备注 3)。在实践中,这会引发许多问题。

例如,我们在最后对向量进行归一化,使用列表推导式:

output = [v/np.linalg.norm(v, ord=2) for v in output]

这可能会导致数值问题。如果任何 v 近似为 0,则其范数 np.linalg.norm(v, ord=2) 会非常小,而用这样的小数进行除法会引发问题。

这个问题也影响投影函数。请看下面的定义:

def projection(x: np.ndarray, to: List[np.ndarray]): 
    #x0022;"/span> 
    Computes the orthogonal projection of the vector ‘x‘ 
    onto the subspace spanned by the set of vectors ‘to‘. 
    #x0022;"/span> 
    p_x = np.zeros_like(x) 

    for e in to: 
        e_norm_square = np.dot(e, e) 
        p_x += np.dot(x, e)*e / e_norm_square 

    return p_x

如果 e 接近 0(当输入向量线性相关时可能发生这种情况),那么 e_norm_square 会很小。解决这个问题的一种方法是加上一个很小的浮动数,例如 1e-16。

def projection(x: np.ndarray, to: List[np.ndarray]): 
    p_x = np.zeros_like(x) 

    for e in to: 
        e_norm_square = np.dot(e, e) 

        # note the change below: 
        p_x += np.dot(x, e)*e / (e_norm_square + 1e-16) 

    return p_x

现在,让我们来认识机器学习中最重要的对象:矩阵。

3.2 矩阵,线性代数的主力军

我很确定在阅读这本书之前,你已经熟悉矩阵的概念。矩阵是最重要的数据结构之一,可以表示方程组、图、向量空间之间的映射等等。矩阵是机器学习的基本构建块。

从表面上看,我们将矩阵定义为一个数字表格。例如,如果矩阵 A 具有 n 行和 m 列的实数,我们写作

⌊ ⌋ | a1,1 a1,2 ... a1,m | || a2,1 a2,2 ... a2,m || | . . . . | n×m A = || .. .. .. .. || ∈ ℝ . || a a ... a || ⌈ n,1 n,2 n,m ⌉

(3.1)

当我们不想写出完整的矩阵时,如 (3.1),我们使用缩写 A = (a[i,j])[i=1,j=1]^(n,m)。

所有 n ×m 实数矩阵的集合记作 ℝ(n×m)。我们将专门讨论实矩阵,但在提到其他类型时,这个符号会相应地修改。例如,ℤ({n×m}) 表示整数矩阵的集合。

矩阵可以进行加法和乘法运算,或者与标量相乘。

定义 14. (矩阵运算)

(a) 设 A ∈ℝ^(n×m) 为一个矩阵,c ∈ℝ 为一个实数。矩阵 A 与标量 c 的乘积定义为

 n,m cA := (cai,j)i,j=1 ∈ ℝn ×m.

(b) 设 A, B ∈ℝ^(n×m) 为两个匹配维度的矩阵。它们的和 A + B 定义为

 n,m n×m A + B := (ai,j + bi,j)i,j=1 ∈ ℝ .

(c) 设 A ∈ℝ^(n×l) 且 B ∈ℝ^(l×m) 为两个矩阵。它们的乘积 AB ∈ℝ^(n×m) 定义为

 l ∑ n,m n×m AB := ( ai,kbk,j)i,j=1 ∈ ℝ . k=1

标量乘法和加法很容易理解,但矩阵乘法并不像加法那样简单。幸运的是,可视化可以帮助我们理解。实质上,(i,j)元素是矩阵 A 的第 i 行与矩阵 B 的第 j 列的点积。

图片

图 3.2:矩阵乘法可视化

除了加法和乘法,还有另一个值得提及的操作:转置。

定义 15.(矩阵转置)

设 A = (a[{]i,j})[{]i,j = 1}^({n,m}) ∈ ℝ^({n×m}) 是一个矩阵。矩阵 A^T 定义为:

AT = (aj,i)ni,,jm=1 ∈ ℝm ×n

被称为 A 的转置。操作 A→A^T 称为转置。

转置简单来说就是“翻转”矩阵,将行替换为列。例如:

 ⌊ ⌋ ⌊ ⌋ ⌈a b⌉ T ⌈a c⌉ A = c d , A = b d ,

或者:

 ⌊0 1⌋ ⌊ ⌋ | | 0 2 4 B = |⌈2 3|⌉ , BT = ⌈ ⌉ . 4 5 1 3 5

与加法和乘法不同,转置是一个一元操作。(一元操作意味着它只需要一个参数。二元操作需要两个参数,以此类推。)

让我们再看一遍矩阵乘法,这是计算中最常用的操作之一。由于现代计算机可以极其快速地执行矩阵乘法,很多算法都被向量化,以便通过矩阵乘法来表示。

因此,越了解它越好。为了更好地掌握这一操作,我们可以从不同的角度来看看它。让我们从一个特例开始!

在机器学习中,矩阵与列向量的乘积是某些模型的基础组成部分。例如,这本身就是线性回归,或者是神经网络中的著名全连接层。

为了看看这种情况发生了什么,设 A ∈ℝ^(n×m) 是一个矩阵。如果我们把 x ∈ℝ^m 看作一个列向量 x ∈ℝ^(m×1),那么 Ax 可以写成:

 ⌊ ⌋ ⌊ ⌋ ⌊ ∑m ⌋ a1,1 a1,2 ... a1,m x1 j=1a1,jxj || a2,1 a2,2 ... a2,m || || x2|| || ∑m a2,jxj|| Ax = || . . . . || || . || = || j=1. || . |⌈ .. .. .. .. |⌉ |⌈ .. |⌉ |⌈ .. |⌉ ∑m an,1 an,2 ... an,m. xm j=1 an,jxj

基于此,矩阵 A 描述了一个函数,它接受一块数据 x,然后将其转换为 Ax 的形式。

这就相当于对矩阵 A 的列进行线性组合,即:

⌊ ⌋⌊ ⌋ ⌊ ⌋ ⌊ ⌋ a1,1 a1,2 ... a1,m x1 a1,1 a1,m || |||| || || || || || ||a2,1 a2,2 ... a2,m |||| x2|| = x1|| a2,1|| + ⋅⋅⋅+ xm ||a2,m|| |⌈ ... ... ... ... |⌉|⌈ ...|⌉ |⌈ ... |⌉ |⌈ ... |⌉ an,1 an,2 ... an,m. xm an,1 an,m

使用更具提示性的符号,我们可以将第 i 列表示为 a[i],这样就可以写成:

⌊ ⌋ | a1,1 a1,2 ... a1,m | || a2,1 a2,2 ... a2,m || | . . . . | n×m A = || .. .. .. .. || ∈ ℝ . || a a ... a || ⌈ n,1 n,2 n,m ⌉

(3.2)

如果我们用矩阵 B 替代向量 x,那么乘积矩阵 AB 中的列是矩阵 A 的列的线性组合,其中系数由 B 决定。

你应该真正意识到,对数据进行的某些操作可以写成 Ax 的形式。将这个简单的性质提升到更高的抽象层次,我们可以说数据和函数有着相同的表示。如果你熟悉像 Lisp 这样的编程语言,你会知道这有多美妙。

还有一种思考矩阵乘积的方式:取列内积。如果 a[i] = (a[i,1],…,a[i,n])表示 A 的第 i 列,那么 Ax 可以写成

⌊ ⌋ | a1,1 a1,2 ... a1,m | || a2,1 a2,2 ... a2,m || | . . . . | n×m A = || .. .. .. .. || ∈ ℝ . || a a ... a || ⌈ n,1 n,2 n,m ⌉

(3.3)

也就是说,变换 x→Ax 将输入 x 投影到 A 的行向量上,然后将结果压缩成一个向量。

3.2.1 操作矩阵

因为矩阵运算是定义良好的,我们可以像处理数字一样对矩阵进行代数运算。然而,还是存在一些主要的区别。由于操作矩阵表达式是一个基本技能,我们来看看它的基本规则!

定理 16. (矩阵加法和乘法的性质)

(a) 设 A,B,C ∈ℝ^(n×l)是任意矩阵。那么,

A + (B + C ) = (A + B )+ C

这成立。也就是说,矩阵加法是结合的。

(b) 设 A ∈ℝ^(n×l), B ∈ℝ^(l×k), C ∈ℝ^(k×m)是任意矩阵。那么,

A (BC ) = (AB )C

这成立。也就是说,矩阵乘法是结合的。

(c) 设 A ∈ℝ^(n×l) 和 B,C ∈ℝ^(l×m)是任意矩阵。那么,

A(B + C ) = AB + AC

这成立。也就是说,矩阵乘法对加法是左分配的。

(d) 设 A,B ∈ℝ^(n×l) 和 C ∈ℝ^(l×m)是任意矩阵。那么,

(A + B)C = AC + BC

这成立。也就是说,矩阵乘法对加法是右分配的。

由于证明非常技术性且枯燥,我们将跳过它。然而,有几点需要注意。最重要的是,矩阵乘法不是交换的;也就是说,AB 不总是等于 BA。(它甚至可能没有定义。)例如,考虑

 ⌊ ⌋ ⌊ ⌋ ⌈1 1 ⌉ ⌈1 0⌉ A = 1 1 , B = 0 2 .

你可以手动验证

 ⌊ ⌋ ⌊ ⌋ ⌈1 2⌉ ⌈1 1⌉ AB = 1 2 , BA = 2 2 ,

这些不相等。

与此一致,我们用于标量的代数恒等式是完全不同的。例如,如果 A 和 B 是矩阵,那么

(A + B)(A + B ) = A (A + B) + B (A + B ) = A2 + AB + BA + B2.

或者

(A + B)(A − B ) = A (A − B) + B (A − B ) = A2 − AB + BA − B2.

转置操作对于加法和乘法也表现得很好。

定理 17. (转置的性质)

(a) 设 A,B ∈ℝ^(n×m)是任意矩阵。那么,

(A + B )T = AT + BT

这成立。

(b) 设 A ∈ℝ^(n×l), B ∈ℝ^(l×m)是任意矩阵。那么,

(AB )T = BT AT

这成立。

我们也不打算证明这个,但你可以当做练习来做。

3.2.2 矩阵作为数组

为了在计算机中执行矩阵运算,我们需要一个能够表示矩阵 A 并支持以下功能的数据结构:

  • 通过 A[i, j]访问元素,

  • 通过 A[i, j] = value 进行元素赋值

  • 使用+和*运算符进行加法和乘法运算,

并且运行速度极快。这些要求只指定了我们矩阵数据结构的接口,并没有涉及实际实现。一个显而易见的选择是使用列表的列表,但正如在讨论计算中表示向量时所提到的(第 1.3 节),这种方式非常不理想。我们能否利用 C 语言的数组结构来存储矩阵?

是的,这正是 NumPy 所做的,它为矩阵提供了以多维数组形式表示的快速便捷方法。在学习如何使用 NumPy 的工具之前,让我们深入探讨一下问题的核心。

乍一看,似乎有个问题:计算机的内存是单维的,因此是通过单一的键来寻址(索引),而不是我们所需要的两个键。因此,我们不能直接将矩阵塞入内存中。解决方案是将矩阵展平,将每一行依次放在一起,就像图 3.3 中在 3×3 的情况下所示。这叫做行主序排列。

通过将任何 n×m 矩阵的行存储在一个连续数组中,我们可以在低成本的简单索引转换下,获得数组数据结构的所有优势,转换方式由以下公式定义:

(i,j) ↦→ im + j.

(请注意,对于使用列主序排列的编程语言(如 Fortran 或 MATLAB)——即列是连接起来的——此索引转换将不起作用。我将正确的转换作为一个练习留给你,以检查你对这一部分的理解。)

图片

图 3.3:展平矩阵

为了展示发生了什么,让我们在 Python 中创建一个典型的 Matrix 类,它使用一个单一的列表来存储所有值,但支持通过行列索引访问元素。为了便于说明,我们假设 Python 列表实际上是一个静态数组。(至少在这次演示结束之前是这样。)这仅用于教育目的,因为目前我们只想理解过程,而不是最大化性能。

花点时间查看下面的代码。我会逐行解释。

from typing import Tuple 

class Matrix: 
    def __init__(self, shape: Tuple[int, int]): 
        if len(shape) != 2: 
            raise ValueError("/span>The shape of a Matrix object must be a two-dimensional tuple." 

        self.shape = shape 
        self.data = [0.0 for _ in range(shape[0]*shape[1])] 

    def _linear_idx(self, i: int, j: int): 
        return i*self.shape[1] + j 

    def __getitem__(self, key: Tuple[int, int]): 
        linear_idx = self._linear_idx(*key) 
        return self.data[linear_idx] 

    def __setitem__(self, key: Tuple[int, int], value): 
        linear_idx = self._linear_idx(*key) 
        self.data[linear_idx] = value 

    def __repr__(self): 
        array_form = [ 
            [self[i, j] for j in range(self.shape[1])] 
            for i in range(self.shape[0]) 
        ] 
        return njoin(["tjoin([fx}"/span> for x in row]) for row in array_form])

矩阵对象通过__init__方法初始化。当一个对象被创建时,就会调用该方法,正如我们现在要做的那样。

M = Matrix(shape=(3, 4))

在初始化时,我们以二维元组的形式提供矩阵的维度,这个元组被传递给形状参数。在我们的具体示例中,M 是一个 3×4 的矩阵,通过一个长度为 12 的数组表示。为了简单起见,我们的简单 Matrix 默认填充为零。

总的来说,__init__方法执行三个主要任务:

  1. 验证形状参数以确保正确性

  2. 将形状存储在实例属性中,以便将来参考

  3. 初始化一个大小为 shape[0] * shape[1]的列表,作为主要数据存储。

第二个方法,暗示性地命名为 _linear_idx,负责在矩阵的行列索引和我们内部一维表示的线性索引之间进行转换。(在 Python 中,通常用下划线作为前缀表示方法不打算外部调用。许多其他语言,如 Java,支持私有方法,而 Python 不支持,因此我们只能依赖这种礼貌的建议,而不是严格执行的规则。)

我们可以通过索引提供__getitem__方法来实现项目检索,它期望一个二维整数元组作为键。对于任何键 = (i, j),该方法:

  1. 使用我们的 _linear_idx 方法计算线性索引。

  2. 从列表中检索位于给定线性索引位置的元素。

项目赋值的过程类似,由__setitem__魔法方法给出。让我们尝试一下,看看它们是否有效。

M[1, 2] = 3.14 
M[1, 2]
3.14

通过提供__repr__方法,我们指定了 Matrix 对象作为字符串的表示方式。因此,我们可以将其以美观的形式打印到标准输出。

M
0.0    0.0    0.0    0.0 
0.0    0.0    3.14    0.0 
0.0    0.0    0.0    0.0

相当棒。现在我们了解了一些内部实现,接下来是时候看看我们能用 NumPy 做些什么了。

3.2.3 NumPy 中的矩阵

如前所述,NumPy 为矩阵提供了非常出色的开箱即用表示,采用的是多维数组的形式。(这些通常被称为张量,但我将坚持使用数组这一命名方式。)

我有个好消息:这些就是我们一直在使用的 np.ndarray 对象!我们可以通过在初始化时简单地提供一个列表的列表来创建一个。

import numpy as np 

A = np.array([[0, 1, 2, 3], 
              [4, 5, 6, 7], 
              [8, 9, 10, 11]]) 

B = np.array([[5, 5, 5, 5], 
              [5, 5, 5, 5], 
              [5, 5, 5, 5]]) 
A
array([[ 0,  1,  2,  3], 
      [ 4,  5,  6,  7], 
      [ 8,  9, 10, 11]])

一切都和我们到目前为止看到的一样。操作按元素进行,你可以将它们传递到像 np.exp 这样的函数中。

A + B       # pointwise addition
array([[ 5,  6,  7,  8], 
      [ 9, 10, 11, 12], 
      [13, 14, 15, 16]])
A*B         # pointwise multiplication
array([[ 0,  5, 10, 15], 
      [20, 25, 30, 35], 
      [40, 45, 50, 55]])
np.exp(A)   # pointwise application of the exponential function
array([[1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01], 
      [5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03], 
      [2.98095799e+03, 8.10308393e+03, 2.20264658e+04, 5.98741417e+04]])

由于我们在处理多维数组,可以定义转置操作符。在这里,这个操作符方便地实现为 np.transpose 函数,但也可以通过 np.ndarray.T 属性进行访问。

np.transpose(A)
array([[ 0,  4,  8], 
      [ 1,  5,  9], 
      [ 2,  6, 10], 
      [ 3,  7, 11]])
A.T         # is the same as np.transpose(A)
array([[ 0,  4,  8], 
      [ 1,  5,  9], 
      [ 2,  6, 10], 
      [ 3,  7, 11]])

正如预期的那样,我们可以通过索引操作符[]来获取和设置元素。索引从零开始。(别让我开始讲这个。)

A[1, 2]    # 1st row, 2nd column (if we index rows and columns from zero)
np.int64(6)

可以使用切片访问整行或整列。与其给出精确的定义,我不如提供几个示例,让你通过内部的模式匹配引擎(也就是你的大脑)来弄清楚。

A[:, 2]    # 2nd column
array([ 2,  6, 10])
A[1, :]    # 1st row
array([4, 5, 6, 7])
A[2, 1:4]   # 2nd row, 1st-4th elements
array([ 9, 10, 11])
A[1]        # 1st row
array([4, 5, 6, 7])

当作为可迭代对象使用时,二维数组每一步都会返回其行。

for row in A: 
    print(row)
[0 1 2 3] 
[4 5 6 7] 
[ 8  9 10 11]

初始化数组可以通过熟悉的 np.zeros、np.ones 和其他函数来完成。

np.zeros(shape=(4, 5))
array([[0., 0., 0., 0., 0.], 
      [0., 0., 0., 0., 0.], 
      [0., 0., 0., 0., 0.], 
      [0., 0., 0., 0., 0.]])

正如你猜的那样,shape 参数指定了数组的维度。接下来我们将探索这个属性。让我们初始化一个具有三行四列的示例多维数组。

A = np.array([[0, 1, 2, 3], 
              [4, 5, 6, 7], 
              [8, 9, 10, 11]]) 
A
array([[ 0,  1,  2,  3], 
      [ 4,  5,  6,  7], 
      [ 8,  9, 10, 11]])

数组的形状存储在属性 np.ndarray.shape 中,是一个描述其维度的元组对象。在我们的示例中,既然我们有一个 3×4 的矩阵,那么形状就是(3, 4)。

A.shape
(3, 4)

这个看似无害的属性决定了你可以对数组执行哪些操作。让我告诉你,作为一名机器学习工程师,形状不匹配将成为你工作的最大障碍。你想计算两个矩阵 A 和 B 的乘积吗?A 的第二维必须与 B 的第一维匹配。逐点乘积?需要匹配或可广播的形状。理解形状至关重要。

然而,我们刚刚学到的多维数组其实是线性数组的伪装。(参见第 3.2.2 节。)因此,我们可以通过不同的切片方式重新排列数组的线性视图。例如,A 可以重塑为形状为(12, 1)、(6, 2)、(4, 3)、(3, 4)、(2, 6)和(1, 12)的数组。

A.reshape(6, 2)    # reshapes A into a 6 x 2 matrix
array([[ 0,  1], 
      [ 2,  3], 
      [ 4,  5], 
      [ 6,  7], 
      [ 8,  9], 
      [10, 11]])

np.ndarray.reshape 方法返回一个新构建的数组对象,但不会改变 A。换句话说,在 NumPy 中,重塑不会破坏原数组。

A
array([[ 0,  1,  2,  3], 
      [ 4,  5,  6,  7], 
      [ 8,  9, 10, 11]])

初次接触重塑时可能很难理解。为了帮助你更好地可视化这一过程,图 3.4 清晰地展示了我们案例中的具体情况。

如果你不知道某个特定轴的确切维度,可以在重塑时输入-1。由于维度的乘积是恒定的,NumPy 足够聪明,能够为你推算出缺失的维度。这个技巧能帮你经常脱困,因此值得记住。

A.reshape(-1, 2)
array([[ 0,  1], 
      [ 2,  3], 
      [ 4,  5], 
      [ 6,  7], 
      [ 8,  9], 
      [10, 11]])
A.reshape(-1, 4)
array([[ 0,  1,  2,  3], 
      [ 4,  5,  6,  7], 
      [ 8,  9, 10, 11]])

图片

图 3.4:将一维数组重塑为多种可能的形状

我们现在不讨论细节,但正如你可能猜到的,多维数组可以有多个维度。那么,操作允许的形状范围将会更复杂。因此,现在建立一个扎实的理解,将为将来提供巨大的领先优势。

3.2.4 矩阵乘法,重新审视

毫无疑问,关于矩阵的最重要操作之一就是矩阵乘法。计算行列式和特征值?矩阵乘法。通过全连接层传递数据?矩阵乘法。卷积?矩阵乘法。我们将看到这些看似不同的操作如何追溯到矩阵乘法;但首先,让我们从计算的角度讨论一下这一操作本身。

首先,回顾一下数学定义。对于任意的 A ∈ℝ^(n×m)和 B ∈ℝ^(m×l),它们的乘积由以下公式定义:

 m ∑ n,l n×l AB = ( ai,kbk,j)i,j=1 ∈ ℝ . k=1

注意,AB 中第 i 行第 j 列的元素是 A 的第 i 行与 B 的第 j 列的点积。

我们可以使用到目前为止学到的工具将其写成代码。

from itertools import product 

def matrix_multiplication(A: np.ndarray, B: np.ndarray): 
    # checking if multiplication is possible 
    if A.shape[1] != B.shape[0]: 
        raise ValueError("/span>The number of columns in A must match the number of rows in B." 

    # initializing an array for the product 
    AB = np.zeros(shape=(A.shape[0], B.shape[1])) 

    # calculating the elements of AB 
    for i, j in product(range(A.shape[0]), range(B.shape[1])): 
        AB[i, j] = np.sum(A[i, :]*B[:, j]) 

    return AB

让我们通过一个容易手动验证的例子来测试我们的函数。

A = np.ones(shape=(4, 6)) 
B = np.ones(shape=(6, 3)) 
matrix_multiplication(A, B)
array([[6., 6., 6.], 
      [6., 6., 6.], 
      [6., 6., 6.], 
      [6., 6., 6.]])

结果是正确的,正如我们预期的那样。

当然,矩阵乘法在 NumPy 中有对应的函数,形式为 numpy.matmul。

np.matmul(A, B)
array([[6., 6., 6.], 
      [6., 6., 6.], 
      [6., 6., 6.], 
      [6., 6., 6.]])

这产生的结果与我们的自定义函数相同。我们可以通过生成一堆随机矩阵并检查结果是否匹配来测试它。

for _ in range(100): 
    n, m, l = np.random.randint(1, 100), np.random.randint(1, 100), np.random.randint(1, 100) 
    A = np.random.rand(n, m) 
    B = np.random.rand(m, l) 

    if not np.allclose(np.matmul(A, B), matrix_multiplication(A, B)): 
        print(f/span>Result mismatch for\n{A}\n and\n{B}" 
        break 
else: 
    print("/span>All good! Yay!"
All good! Yay!

根据这个简单的测试,我们的 matrix_multiplication 函数与 NumPy 内建函数的结果是一样的。我们很高兴,但别忘了:在实际操作中,始终使用你所选择的框架的实现,无论是 NumPy、TensorFlow 还是 PyTorch。

由于在存在大量乘法时编写 np.matmul 会显得繁琐,NumPy 提供了一种使用 @ 运算符来简化的方法。

A = np.ones(shape=(4, 6)) 
B = np.ones(shape=(6, 3)) 

np.allclose(A @ B, np.matmul(A, B))
True

3.2.5 矩阵和数据

既然我们已经熟悉了矩阵乘法,现在是时候在其他领域理解它们了。假设有一个矩阵 A ∈ℝ^(n×m) 和一个向量 x ∈ℝ^m。通过将 x 视为列向量 x ∈ℝ^(m×1),可以通过以下方式计算 A 和 x 的乘积:

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ a a ... a x ∑m a x | 1,1 1,2 1,m | | 1| | ∑mj=1 1,j j| || a2,1 a2,2 ... a2,m || || x2|| || j=1a2,jxj|| Ax = || .. .. .. .. || || .. || = || .. || . ⌈ . . . . ⌉ ⌈ . ⌉ ⌈ ∑ . ⌉ an,1 an,2 ... an,m. xm mj=1an,jxj

从数学角度来看,将 x 视为列向量是完全自然的。可以将其看作是通过引入一个虚拟维度来扩展 ℝ^m,从而得到 ℝ^(m×1)。这个形式也可以通过考虑矩阵的列是基向量的像来理解。

在实践中,事情并不像看起来那么简单。隐含地,我们在这里做出了一个选择:将数据集表示为列向量的水平堆叠。为了进一步阐述,让我们考虑两个具有四个特征的数据点,以及一个将它们映射到三维特征空间的矩阵。也就是说,假设 x[1], x[2] ∈ℝ⁴ 和 A ∈ℝ^(3×4)。

x1 = np.array([2, 0, 0, 0])       # first data point 
x2 = np.array([-1, 1, 0, 0])      # second data point 

A = np.array([[0, 1, 2, 3], 
              [4, 5, 6, 7], 
              [8, 9, 10, 11]])    # a feature transformation

(我特意选择了这些数字,以便通过手工计算轻松验证结果。)为了确保准确无误,我们再次检查了形状。

A.shape
(3, 4)
x1.shape
(4,)

当我们调用 np.matmul 函数时,会发生什么?

np.matmul(A, x1)
array([ 0,  8, 16])

结果是正确的。然而,当我们有一堆输入数据点时,我们更倾向于通过一次操作计算这些数据点的映像。这样,我们就可以利用向量化代码、引用局部性以及到目前为止我们所看到的所有计算魔法。

我们可以通过水平堆叠列向量来实现这一点,每个列向量表示一个数据点。从数学上讲,我们希望在代码中执行该计算。

 ⌊ ⌋ ⌊0 1 2 3⌋ | 2 − 1| ⌊ 0 1⌋ | | || 0 1 || | | |⌈4 5 6 7|⌉ | | = |⌈ 8 1|⌉ |⌈ 0 0 |⌉ 8 9 10 11 0 0 16 1

查阅 NumPy 文档后,我们很快发现 np.hstack 函数可能是解决问题的工具,至少根据它的官方文档(numpy.org/doc/stable/reference/generated/numpy.hstack.html)来看是这样。耶!

np.hstack([x1, x2])    # np.hstack takes a list of np.ndarrays as its argument
array([ 2,  0,  0,  0, -1,  1,  0,  0])

不太高兴。发生了什么?np.hstack 对一维数组的处理方式不同,尽管通过巧妙地滥用符号,数学计算结果是正确的,但在实际的计算过程中,我们不能这么轻松地逃避。因此,我们必须手动调整输入数据的形状。让我们认识到初级与高级机器学习工程师之间的真正技能差距:正确地调整多维数组的形状。

# x.reshape(-1,1) turns x into a column vector 
data = np.hstack([x1.reshape(-1, 1), x2.reshape(-1, 1)]) 
data
array([[ 2, -1], 
      [ 0,  1], 
      [ 0,  0], 
      [ 0,  0]])

让我们再试一次。

np.matmul(A, data)
array([[ 0,  1], 
      [ 8,  1], 
      [16,  1]])

太棒了!(这次是真的。)

请注意,在这一章中我们做出了一个极为重要的选择:将单个数据点表示为列向量。我用粗体字写出这一点,以强调其重要性。

为什么?因为我们本可以选择另一种方式,将样本视为行向量。选择当前的方式后,我们得到了形状为

维度数 × 样本数,

与之相对的是

样本数 × 维度数。

前者称为批次最后(batch-last),而后者称为批次优先(batch-first)格式。像 TensorFlow 和 PyTorch 这样的流行框架使用批次优先,但我们选择了批次最后。之所以如此,是因为这与矩阵的定义密切相关,其中列是给定线性变换下基向量的像。这样,我们可以像从左到右书写乘法一样写出 Ax 和 AB。

如果我们将矩阵定义为基向量像的行,所有事情都会颠倒过来。这样,如果 f 和 g 是具有“矩阵”A 和 B 的线性变换,那么组合变换 f ∘ g 的“矩阵”将是 BA。这使得数学变得复杂且不美观。

另一方面,批次优先使得数据存储和读取更加方便。想象一下,当你有成千上万的数据点在一个 CSV 文件中时,文件的读取是逐行进行的,因此将每一行与一个样本对应是自然且便捷的。

这里没有绝对的好选择,任何选择都有其牺牲。由于批次最后的数学运算更为简便,我们将使用这种格式。然而,在实践中,你会发现批次优先更为常见。通过这本教科书,我并不打算仅仅给你一本手册。我的目标是帮助你理解机器学习的内部原理。如果我成功了,你将能够毫不费力地在批次优先和批次最后之间进行转换。

3.3 小结

在本章中,我们终于深入实践,而不只是从理论的高塔中展望。之前,我们看到 NumPy 数组是进行数值计算,尤其是线性代数的理想工具。现在,我们用它们来快速且优雅地实现我们在上一章中学到的内容:范数、距离、点积和 Gram-Schmidt 过程。

除了向量,我们最终还介绍了矩阵,这是机器学习中最重要的工具之一。这一次,我们以一种实际的方式来理解矩阵,将其视为一个数字表格。矩阵可以进行转置和加法运算,不同于向量,矩阵还可以相互相乘。

说到我们的“从头开始”的方法,在实际研究如何使用矩阵之前,我们用原生 Python 创建了我们自己的矩阵实现。结束这一章时,我们处理了二维 NumPy 数组的基本概念和最佳实践,这是 Python 能提供的最重要的矩阵表示。

在下一章,我们将再次采取理论方法。这就是我们本书的做法:同时查看两个方面,从而提升我们对数学(以及沿途的机器学习)的理解。我们将看到矩阵不仅仅是普通的数字表格;它们也是数据变换。这一特性美得无法用言语形容:数据及其变换由同一个对象表示。

让我们开始吧!

3.4 问题

问题 1. 实现均方误差

 ∑n MSE (x,y) = 1- (xi − yi)², x, y ∈ ℝⁿ, n i=1

无论是否使用 NumPy 函数和方法,均需实现。 (向量 x 和 y 应该在这两种情况下都由 NumPy 数组表示。)

问题 2. 使用 timeit.timeit 比较内置的最大值函数 max 和 NumPy 的 np.max 的性能,就像我们上面做的那样。尝试运行不同数量的实验,并改变数组大小,以找出两者性能的平衡点。

问题 3. 不像我们在本章第 3.1.1 节中实现一般的 p 范数,我们可以改变方法,得到下面的版本。

def p_norm(x: np.ndarray, p: float): 
    if p  1: 
        return (np.sum(np.abs(x)**p))**(1/p) 
    elif np.isinf(p): 
        return np.max(np.abs(x)) 
    else: 
        raise ValueError("/span>p must be a float larger or equal than 1.0 or inf."

但是,这在 ( p = \infty ) 时不起作用。它的问题是什么?

问题 4. 设 ( w \in \mathbb{R}^n ) 是一个包含非负元素的向量。使用 NumPy 实现加权 p 范数,公式为

 ∑n ∥x∥wp = ( wi|xi|p)¹/p, x = (x1,...,xn) ∈ ℝn. i=1

你能想出一个机器学习中可能用到这个的场景吗?

问题 5. 实现余弦相似度函数,定义公式为

cos(x,y ) = ⟨-x-,-y--⟩, x, y ∈ ℝn. ∥x ∥ ∥y∥

(尽可能使用内置的 NumPy 函数。)

问题 6. 计算以下矩阵的乘积。

(a)

 ⌊ ⌋ ⌊ ⌋ − 1 2 | 6 − 2| A = ⌈ ⌉, B = | 2 − 6|. 1 5 ⌈ ⌉ − 3 2

(b)

 ⌊ ⌋ ⌊ ⌋ 1 2 3 7 8 A = ⌈ ⌉ , B = ⌈ ⌉ . 4 5 6 9 10

问题 7. 著名的斐波那契数列由递归序列定义

F[0]= 0,

F[1]=1,

F[n]= F[n−1] + F[n−2]

(a) 编写一个递归函数,计算第 n 个斐波那契数。 (预期它会非常慢。)

(b) 展示

⌊ ⌋n ⌊ ⌋ ⌈1 1⌉ = ⌈Fn+1 Fn ⌉ , 1 0 Fn Fn− 1

并使用此恒等式编写一个非递归函数,计算第 n 个斐波那契数。

使用 Python 内置的 timeit 函数测量两种函数的执行时间。哪个更快?

问题 8. 设 ( A,B \in \mathbb{R}^{n \times m} ) 为两个矩阵。它们的 Hadamard 积定义为

 ⌊ ⌋ | a1,1b1,1 a1,2b1,2 ... a1,nb1,n| | a2,1b2,1 a2,2b2,2 ... a2,nb2,n| || . . . . || A ⊙ B = || .. .. .. .. || . || || ⌈ an,1bn,1 an,2bn,2 ... an,nbn,n⌉

实现一个函数,接收两个形状相同的 NumPy 数组,然后对它们执行 Hadamard 乘积。(有两种方法可以实现这一点:使用 for 循环和使用 NumPy 操作。实现这两种方法都很有启发性。)

问题 9. 设 A ∈ℝ^(n×n) 为一个方阵。形式如下的函数

B(x,y ) = xT Ay, x,y ∈ ℝn

这称为双线性形式。实现一个函数,接收两个向量和一个矩阵(都由 NumPy 数组表示),然后计算对应的双线性形式。

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过“问我任何问题”环节与作者聊天,等等。扫描二维码或访问链接加入社区。 packt.link/math

PIC

线性变换

“为什么我的眼睛痛?” “你以前从未使用过它们。”

— 莫非斯对尼奥说,当他第一次从矩阵中醒来时

在大多数线性代数课程中,课程内容都围绕矩阵展开。在机器学习中,我们一直在使用矩阵。问题是:矩阵并不能讲述整个故事。仅仅通过矩阵很难理解其中的模式。例如,为什么矩阵乘法定义得如此复杂?为什么像 B = T^(−1)AT 这样的关系很重要?为什么有些矩阵是可逆的,有些则不是?

要真正理解发生了什么,我们必须看看什么导致了矩阵的产生:线性变换。就像对于尼奥一样,这可能有些痛苦,但从长远来看,它会给我们带来巨大的回报。让我们开始吧!

第六章:4.1 什么是线性变换?

通过引入内积、正交性以及正交/规范正交基,我们现在已经了解了特征空间的结构。然而,在机器学习中,我们的主要兴趣在于数据的变换。

从这个角度来看,神经网络只是一个由更小部分(称为层)组成的函数,在每一步中将数据转化为新的特征空间。机器学习模型中的一个关键组件就是线性变换。

你可能遇到过形式为 f(x) = Ax 的函数,但这只是其中的一种表现方式。本节将从几何视角开始,然后过渡到你可能已经熟悉的代数表示方式。要理解神经网络如何学习数据的强大高层表示,观察变换的几何性质至关重要。

那么,线性变换究竟是什么呢?让我们不再犹豫,直接进入定义吧!

定义 16.(线性变换)

设 U 和 V 为两个向量空间(在相同的标量域上),并且设 f : U →V 为它们之间的一个函数。如果 f 是线性的,我们说 f 满足

f( ax + by) = af(x) + bf(y)

(4.1)

对于所有向量 x、y ∈U 和所有标量 a、b,公式成立。

这就是为什么线性代数被称为线性代数的原因。从本质上讲,线性变换是两个向量空间之间的一种映射,它保持代数结构不变:加法和标量乘法。(向量空间之间的函数通常称为变换,因此我们将使用这个术语。)

备注 5.

线性本质上是将两个性质结合在一起:对于所有向量 x、y 和所有标量 a,有 f(x + y) = f(x) + f(y) 和 f(ax) = af(x)。从这两个公式中,(4.1) 可以推出。

f(ax + by) = f(ax) + f(by) = af(x)+ bf (y ).

定义中立刻显现出两个性质。首先,由于

f(x) = f(x + 0) = f(x) + f(0),

f(0) = 0 对于每个线性变换都成立。此外,线性变换的复合仍然是线性的,如

f (g(ax + by)) = f(ag(x)+ bg(y)) = af(g(x))+ bf(g(y))

对于任意的线性 f 和 g 以及标量 a 和 b,公式如下。

像往常一样,让我们看一些例子来建立直觉。

示例 1. 对于任意标量 c,缩放变换 f(x) = cx 是线性的。

这是可能是最简单的例子,而且它可以在所有向量空间中定义。

PIC

图 4.1:作为线性变换的缩放

很容易看出,缩放是线性的:

c(ax + by) = c(ax)+ c(by) = a(cx)+ b(cy).

示例 2. 在 ℝ² 中,绕原点的旋转角度 α 也是线性的。

PIC

图 4.2:作为线性变换的欧几里得平面旋转

为了证明旋转确实是线性的,我从帽子里拿出了定义:平面向量 x = (x[1], x[2]) 以角度 α 旋转可以用以下公式描述

f(x) = (x1cosα − x2 sinα, x1sinα + x2cos α),

从中(16)可以很容易地确认。我知道这看起来像是魔法,但相信我,旋转公式会详细解释。你可以通过一些基本的三角学知识来推导,或者等到我们稍后使用矩阵来处理这个问题。

一般来说,线性变换与空间的几何结构有着紧密的联系。稍后我们将详细研究 ℝ² 的线性变换,重点讨论像这样的几何变换。(注意,在更高维度下,旋转会稍微复杂一些,因为它们需要一个旋转轴。)

示例 3. 在任何向量空间 V 中,若存在非零向量 v ∈ V,定义为 f(x) = x + v 的平移不是线性的,因为 f(0) = v≠0。

我们稍后将在本节中看到更多例子。目前,让我们继续讨论线性变换的一些一般性质。对于任何线性变换 f : U → V,其像

im(f) = {v ∈ V : v = f (u) 对某些 u ∈ U}

总是 V 的一个子空间,见节 1.2.7。这很容易验证:如果v1, v2 ∈ im f ,那么存在u1,u2 ∈ U ,使得 f(u[1]) = v[1] 和 f(u[2]) = v[2],如

av1 + bv2 = af(u1)+ bf (u2 ) = f (au1 + bu2) ∈ im f.

为了增加一个抽象的层次,我们将看到所有线性变换的集合形成了一个向量空间。

定理 18\。

设 U 和 V 是两个在相同域 F 上的向量空间。那么,所有线性变换的集合

L(U,V ) = {f : U → V | f 是线性的}

(4.2)

也是一个定义在 F 上的向量空间,具有通常的函数加法和标量乘法定义。

证明这个的过程只是一个无聊的检查清单,逐项检查向量空间定义中的条目(定义 2)。我建议你至少走一遍这个过程,以巩固你对向量空间的理解,但实际上并没有什么特别的地方。

4.1.1 线性变换与矩阵

如我们所见,线性变换的定义有点抽象。然而,有一种简单且富有表现力的方式来刻画它们。

为了验证这一点,设 f : U →V 是两个向量空间 U 和 V 之间的线性变换。假设 {u[1],…,u[m]} 是 U 中的基,而 {v[1],…,v[n]} 是 V 中的基。由于每个 x ∈U 都可以表示为

 m ∑ x = xiui, i=1

f 的线性性质意味着

f( ∑ [j=1]^m x[j] u[j] ) = ∑ [j=1]^m x[j] f(u[j]),

(4.3)

这意味着 f(x) 是 f(u[1]),…,f(u[m]) 的线性组合。换句话说,每个线性变换完全由基向量的像决定。为了扩展这个想法,假设对于每个 u[j],我们有

 ∑n f(uj) = ai,jvi i=1

对于某些标量 a[i,j]。

这 n × m 个数完全描述了 f。为了简化符号,我们将它们存储在一个 n × m 大小的表格中,称为矩阵,记作 A[f]:

 ⌊ ⌋ a1,1 a1,2 ... a1,m | | ||a2,1 a2,2 ... a2,m|| f ↔ Af = || .. .. ... .. || , ⌈ . . . ⌉ an,1 an,2 ... an,m

这意味着线性变换由矩阵表示。这个联系在机器学习中被广泛利用。

进一步扩展(4.3),对于每个 x = ∑ [j=1]^m x[j]u[j],我们有

 ∑m f(x) = xjf(uj) j=1 ∑m ∑n = xj ai,jvi j=1 i=1 n ( m ) = ∑ ( ∑ a x ) v . i,j j i i=1 j=1

因此,x 的像可以表示为 A[f]x:

 ⌊ ⌋ ⌊ ⌋ ⌊∑m ⌋ |a1,1 a1,2 ... a1,m | |x1 | | j=1 a1,jxj| |a2,1 a2,2 ... a2,m | |x2 | |∑m a2,jxj| f(x) = || . . . . || || . || = || j=1. || . |⌈ .. .. .. .. |⌉ |⌈ .. |⌉ |⌈ .. |⌉ a a ... a , x ∑m a x n,1 n,2 n,m m j=1 n,j j

这里有两点需要注意。首先,我们隐式地选择了将向量表示为列向量而非行向量。这是一个非常重要的决定,稍后的许多计算都会受到这个选择的影响,我们会一直指出这一点。

其次,矩阵表示依赖于基的选择!如果,例如,P = {p[1],…,p[n]} ⊂ U 是我们的矩阵基,我们将在下标中表示这种依赖关系,写作 A[f,P]。

为了避免混淆,我们几乎总是通过给出标准正交基中的矩阵来定义线性变换。在实际应用中,这使得理解发生了什么变得更容易。所以,每当我写下类似“让 A 是线性变换 f 的矩阵”的内容时,隐含的假设是 A 是在基 e[1] = (1,0,…,0),e[2] = (0,1,…,0),…,e[n] = (0,0,…,1) 中写的。

从哲学的角度讲,你听说过柏拉图的洞穴寓言吗?在这个思想实验中,人们被假设生活在一个洞穴里,永远面对一面墙,只观察由他们身后火光投射的影像。他们所观察到的,用来构建世界的内部表征,与现实大相径庭。将这一类比应用到线性代数中,矩阵就是我们在实际场景中观察到并使用的影像。在许多入门课程中,线性变换被隐藏,只教授矩阵运算。我最初接触这个主题时也是如此:我上过的第一门线性代数课程完全讲解了矩阵。那门课复杂且令人困惑,堪称数学课程中的典型(而且,我可以保证,它确实是非常复杂和困惑的)。在我后来的学习中,当我发现可以从线性变换的角度看待矩阵时,一切都恍若豁然开朗。

如果不深入了解矩阵背后的内容,是无法掌握线性代数的。如果我的方法对你来说太抽象,请记住这一点:多年后,当你成为一名实践中的数据科学家/机器学习工程师/研究员或者其他职业时,深入表面背后的内容会带来巨大的回报。

让我们回到正题,继续讨论线性变换。最常用的矩阵是单位变换 id : x→x 的矩阵。我们用 I 来表示它。很容易看出

L(U,V ) = {f : U → V | f 是线性变换}(4.4)

总结来说,对于一个矩阵 A,一个线性变换可以通过 x→Ax 来表示。事实上,映射

f ↦→ Af,P

定义了线性变换空间 L(U,V )(由 4.2 定义)与 n × m 矩阵集之间的一个一一对应关系,其中 n 和 m 是对应的维度。

4.1.2 矩阵运算重温

函数可以进行加法和复合操作。由于线性变换和矩阵之间的关系,矩阵运算继承了相应的函数运算。

在这个原则的指导下,我们定义了矩阵加法,使得两个线性变换的和对应的矩阵之和就是它们的矩阵之和。

从数学角度来看,假设 f,g : U →V 是两个具有矩阵的线性变换,f ↔︎A 和 g ↔︎B,则

 n (f + g)(u ) = f (u )+ g(u ) = ∑ (a + b )v . j j j i=1 i,j i,j i

因此,对应的矩阵可以按元素逐项相加:

A + B = (ai,j + bi,j)n,m . i,j=1

矩阵之间的乘法通过对应变换的复合作用来定义。

为了理解这个,我们首先研究一个特殊情况。(一般来说,首先看特殊情况是个好主意,因为它们通常可以减少复杂性,并帮助我们在没有信息过载的情况下看到模式。)因此,设 f,g : U → U 是两个线性变换,将 U 映射到自身。为了确定对应于 f ∘ g 的矩阵元素,我们必须将 f(g(u[j])) 表示为所有基向量 u[1],…,u[n] 的线性组合。为此,我们有:

 n (f g)(u ) = f(g(u )) = f(∑ b u ) j j k,j k n k=1 ∑ = bk,jf(uk) k=n1 n ∑ ∑ = bk,j ai,kui k=1 i=1 ∑n ∑n = ( ai,kbk,j)ui. i=1 k=1

通过考虑我们如何定义变换的矩阵,标量 (∑ [k=1]^n a[i,k]b[k,j]) 是 f ∘ g 的矩阵中第 i 行、第 j 列的元素。因此,矩阵乘法可以定义为:

 ( )n ∑n AB = ai,kbk,j . k=1 i,j=1

在一般情况下,只有当相应的线性变换可以组合时,我们才能定义矩阵的乘积。也就是说,如果 f : U → V,那么 g 必须从 V 开始。将其转化为矩阵语言,A 的列数必须与 B 的行数相匹配。因此,对于任意的 A ∈ ℝ^(n×m) 和 B ∈ ℝ^(m×l),它们的乘积定义为:

 ∑m AB = ( ai,kbk,j)n,l ∈ ℝn×l. k=1 i,j=1

4.1.3 线性变换的逆

关于线性变换,逆变换的问题极其重要。例如,你是否遇到过这样的方程组?

2x1 + x2 = 5 x1 − 3x2 = − 8

如果我们定义:

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ 2 1 5 x1 A = ⌈ ⌉ , b = ⌈ ⌉ , x = ⌈ ⌉ , 1 − 3 − 8 x2

上述系统可以写成 Ax = b 的形式。这些称为线性方程,能够描述从金融到生物学的各种过程。

如何写出这样方程的解呢?如果存在一个矩阵 A^(−1),使得 A^(−1)A 是单位矩阵 I(由 (4.4) 定义),那么将方程 Ax = b 左乘 A^(−1) 会得到解的形式 x = A^(−1)b。

矩阵 A^(−1) 被称为 A 的逆矩阵。它可能并不总是存在,但当它存在时,它在多个方面都是非常重要的。我们稍后会讨论线性方程,但首先,让我们研究逆变换的基础!这是一般定义:

定义 17.(线性变换的逆)

设 f : U → V 是在向量空间 U 和 V 之间的线性变换。如果存在一个线性变换 f^(−1),使得 f^(−1) ∘ f 和 f ∘ f^(−1) 是单位函数,即:

f− 1(f (u )) = u, − 1 f(f (v )) = v

对于所有 u ∈ U, v ∈ V 都成立。那么,f^(−1) 就叫做 f 的逆变换。

不是所有的线性变换都有逆。例如,如果 f 将所有向量都映射到零向量,那么就无法定义逆变换。

有一些条件可以保证逆的存在。最重要的条件之一是将基的概念与可逆性联系起来。

定理 19. (线性变换的可逆性)

设 f : U → V 是一个线性变换,且 u[1],…,u[n] 是 U 中的一个基。则 f 可逆当且仅当 f(u[1]),…,f(u[n]) 是 V 中的一个基。

以下证明是直接的,但可能有点复杂。第一次阅读时可以跳过,您可以稍后再回过头来看。

证明。像往常一样,if 和 only if 类型的定理的证明由两部分组成,因为这些陈述涉及两个推论。

(a) 首先,我们证明 f 是可逆的,然后 f(u[1]),…,f(u[n]) 是一个基。也就是说,我们需要证明 f(u[1]),…,f(u[n]) 是线性无关的,并且每个 y ∈V 都可以表示为它们的线性组合。

由于 f 是可逆的,f(0) = 0,此外没有非零向量 x ∈ U 使得 f(x) = 0。换句话说,0 不能表示为 f(u[1]),…,f(u[n]) 的非平凡线性组合,因此定理 3 推导出线性无关性。

另一方面,可逆性意味着每个 y ∈ V 都可以表示为 y = f(x) ,其中 x ∈ U 。 (选择 x = f −1(y) 。)由于 u1,...,un 是一个基,  ∑ x = ni=1xiui 。因此,

y = f(x) ∑n = f( xiui) i=1 ∑n = xif(ui), i=1

证明 span(f(u[1]),…,f(u[n])) = V。

线性无关的 f(u[1]),…,f(u[n]) 以及它覆盖整个 V 的事实,证明它确实是一个基。

(b) 现在我们证明另一个推论:如果 f(u[1]),…,f(u[n]) 是一个基,那么 f 是可逆的。

如果 f(u[1]),…,f(u[n]) 确实是一个基,那么每个 y ∈V 都可以表示为

 n n ∑ ∑ y = yif (ui) = f( yiui), i=1 i=1

这证明了满射性。关于单射性,如果 y = f(a) = f(b),其中 a,b ∈U,那么,由于 a 和 b 都可以表示为 u[i] 基向量的线性组合,我们会得到

 n n ∑ ∑ y = f(a) = f( aiui) = aif (ui) i=1 i=1

 ∑n ∑n y = f(b) = f( biui) = yif(ui). i=1 i=1

因此,0 = ∑ [i=1]^n(a[i]−b[i])u[i],由于 u[1],…,u[n] 是 U 中的一个基,必须有 a[i] = b[i]。因此,f 是单射的。

这个定理的一个推论是,如果 U 和 V 的维数不同,那么线性变换 f : U → V 不是可逆的。我们也可以从矩阵的角度来看可逆性。对于任意 A ∈ℝ^(n×n),如果对应的线性变换是可逆的,那么存在一个矩阵 A^(−1) ∈ℝ^(n×n),使得 A^(−1)A = AA^(−1) = I。如果一个矩阵不是方阵,它在经典意义下不可逆。

4.1.4 核和像

关于线性变换的可逆性,有两个特殊的集合发挥着至关重要的作用:核(kernel)和像(image)。我们来看看它们!

定义 18. (线性变换的核与像)

设 f : U →V 是一个线性变换。其像和核分别定义为

im f := {f(u) : u ∈ U} ⊆ V

kerf := {u ∈ U : f(u ) = 0} ⊆ U.

通常,我们使用 imA 和 kerA 来表示某个矩阵 A 所定义的线性变换 x→Ax。由于 f 的线性性质,我们可以很容易地看出,imf 是 V 的一个子空间,kerf 是 U 的一个子空间。如前所述,它们与可逆性密切相关,接下来我们将看到这一点。

定理 20. (关于线性变换的可逆性)

设 f : U →V 是一个线性变换。

(a)A 是单射当且仅当 kerf = {0}。

(b)A 是满射当且仅当 imf = V。

(c)A 是双射(即可逆)当且仅当 kerf = {0} 且 imf = V。

证明。(a)如果 f 是单射,那么 U 中只能有一个向量映射到 0。由于对任何线性变换来说,f(0) = 0,因此 kerf = {0}。

另一方面,如果存在两个不同的向量 x, y ∈ U,使得 f(x) = f(y),那么 f(x−y) = f(x) − f(y) = 0,因此 x−y ∈ kerf。于是,kerf = {0} 意味着 x = y,从而得出单射性。

(b)这只是满射的定义。

(c)这立即可以通过结合上述(a)和(b)得出。

由于矩阵定义了线性变换,因此讨论矩阵的逆是有意义的。

从代数的角度来看,A ∈ ℝ^(n×n) 的逆矩阵是 A^(−1) ∈ ℝ^(n×n),满足 A^(−1)A = AA^(−1) = I。线性变换与矩阵之间的联系意味着 A^(−1) 就是 f^(−1) 的矩阵,因此这里没有什么令人惊讶的。

如果这一节关于可逆性的内容让你觉得有些代数上的复杂,不必担心。稍后在讲到变换的行列式时,我们将从几何角度研究可逆性。在矩阵方面,我们将在本章的第 5.1.6 节中看到一种计算逆矩阵的通用方法。我们很快就会讲到,但首先,我们来看看基的选择如何决定矩阵表示。

4.2 基变换

在本节之前,我们已经看到,任何线性变换都可以通过基向量的像来描述(参见第 4.1.1 节)。这给了我们通常使用的矩阵表示。然而,这很大程度上取决于基的选择。不同的基会为同一个变换生成不同的矩阵。

比如,我们可以看一下 f : ℝ² → ℝ²,它将 e[1] = (1,0) 映射到向量 (2,1),将 e[2] = (0,1) 映射到 (1,2)。在标准正交基 E = {e[1],e[2]} 下,它的矩阵表示为

L(U,V ) = {f : U → V | f 是线性的}(4.5)

PIC

图 4.3:线性变换 f,由于基变换定义的(参见 5.2)

f 的作用在图 4.3 中可视化。

如果我们选择不同的基,比如 P = {p[1] = (1,1), p[2] = (−1,1)},那么通过快速计算,我们可以检查到

⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ |2 1| 1 3 |2 1| − 1 − 1 |⌈1 2|⌉ ⌈ ⌉ = ⌈ ⌉ , |⌈1 2|⌉ ⌈ ⌉ = ⌈ ⌉ . 1 3 1 1

换句话说,f(p[1]) = 3p[1] + 0p[2] 和 f(p[2]) = 0p[1] + p[2]。这可以通过图形 4.4 来直观展示。

PIC

图 4.4:f 对 p[1] = (1,1) 和 p[2] = (−1,1) 的作用

这意味着,如果 P = {p[1],p[2]} 是我们的基(因此,若写作 (a,b) 就意味着 ap[1] + bp[2]),则 f 的矩阵为

 ⌊ ⌋ |3 0| Af,P = |⌈0 1|⌉ .

在这种形式下,A[f,P] 是一个对角矩阵。(也就是说,它的对角线下方和上方的元素是零。)如你所见,选择正确的基可以显著简化线性变换。例如,在 n 维空间中,应用对角矩阵形式的变换只需要进行 n 次操作,因为

⌊ ⌋⌊ ⌋ ⌊ ⌋ d1 0 ... 0 x1 d1x1 || 0 d ... 0 |||| x || || d x || || 2 |||| 2|| = || 2 2|| |⌈ ... ... ... ... |⌉|⌈ ...|⌉ |⌈ ... |⌉ 0 0 ... dn xn dnxn

否则,可能需要 n² 次操作。因此,我们可以节省很多计算。

4.2.1 变换矩阵

我们刚才看到,线性变换的矩阵依赖于我们选择的基。然而,对于同一变换的矩阵之间存在一种特殊的关系。我们接下来将探讨这一点。设 f : U → U 是一个线性变换,P = {p[1],…,p[n]} 和 Q = {q[1],…,q[n]} 是两个基。如前所述,A[f,S] 表示在某基 S 中 f 的矩阵。

假设我们知道 A[f,P],但是我们的向量是通过另一组基 Q 来表示的。我们如何计算在该线性变换下向量的像呢?一种自然的思路是,首先将向量从 Q 基变换到 P 基,应用 A[f,P],然后再将表示转换回来。接下来,我们将精确地描述这一过程。

t : U → U 是一个变换,定义为 pi ↦→ qi ,适用于所有 i ∈ {1,...,n } 。 (换句话说,t 将一组基向量映射到另一组。)由于 P Q 是基(因此这些集合是线性无关的),所以 t 是可逆的。

假设矩阵  Q Af,Q = (ai,j)ni,j=1 已知,即

 ∑n Q f(qj) = Af,Qqj = ai,jqi i=1

对于所有 j 都成立。因此,我们有

 −1 −1 (t ft)(pj ) = t f(qj) ∑n = t−1( aQi,jqi) i=1 ∑n = aQi,jt−1(qi) i=1 ∑n = aQi,jpi. i=1

换句话说,在基 P 中,组合变换 t^(−1)ft 的矩阵与基 Q 中 f 的矩阵相同。用公式表示,

T^(−1) A [f,P] T = A[f,Q],

(4.6)

其中 T 表示 t 在基 P 中的矩阵。(为了简化符号,我们省略了下标。通常,我们并不关心变换在哪个基下进行。)

我们将 T 称为基变换矩阵。这类关系在线性代数中很常见,因此我们将花时间正式介绍这个定义。

定义 19.(相似矩阵)

设 A,B ∈ℝ^(n×n) 为两个任意矩阵。如果存在一个矩阵 T ∈ℝ^(n×n),使得 A 和 B 称为相似矩阵,则有

 − 1 B = T AT

成立。我们称形式为 A→T^(−1)AT 的映射为相似变换。

用这些术语,(4.6)说明了给定线性变换的矩阵彼此相似。

掌握了这些,我们可以完成例子(4.5)。在这种情况下,T 和 T^(−1) 可以写作

 ⌊ ⌋ ⌊ ⌋ | 1 − 1| | 1∕2 1∕2| T = |⌈ 1 1|⌉ , T −1 = |⌈− 1∕2 1∕2|⌉ .

(稍后,我们将看到计算任何矩阵的逆的一般方法,但现在,你可以手动验证这一点。)因此,

L(U,V ) = {f : U → V | f 是线性的}(4.7)

或等价地,

L(U,V ) = {f : U → V | f 是线性的}(4.8)

图 4.5 显示了 (4.8) 在几何上的表现。

PIC

图 4.5:基变换示意图

从这个例子中,我们可以看到,正确选择的相似变换能够对某些矩阵进行对角化。这是巧合吗?剧透:不是的。在第七章中,我们将精确了解何时以及如何做到这一点。

我知道,这有点抽象。正如往常一样,示例能最好地阐明一个概念,所以我们来看一些示例!

4.3 欧几里得平面中的线性变换

我们刚刚看到,线性变换可以通过基集的映射来描述。从几何角度来看,它们是将平行六面体映射到平行六面体的函数。

由于是线性变换,你可以将其想象为扭曲由基向量确定的网格。

PIC

图 4.6:线性变换如何扭曲由基向量确定的网格

在二维中,我们已经看到了几个几何映射的例子,例如缩放和旋转,它们作为线性变换。现在我们可以将它们转化为矩阵形式。我们将研究其中的五种:拉伸、剪切、旋转、反射和投影。

这些简单的变换不仅对建立直觉至关重要,而且在计算机视觉中也经常应用。翻转、旋转和拉伸是图像增强管道的关键部分,能够显著提升模型的性能。

4.3.1 拉伸

最简单的一个是缩放的推广。我们已经在上面的示例 1 中看到了这种变体(见第 4.1 节)。在矩阵形式下,这可以表示为

 ⌊ ⌋ c1 0 A = ⌈ ⌉ , c1,c2 ∈ ℝ. 0 c2

这类线性变换可以通过绘制由标准基 e[1] = (1,0),e[2] = (0,1) 确定的单位正方形的图像来可视化。

PIC

图 4.7:拉伸

4.3.2 旋转

旋转由矩阵给出

 ⌊ ⌋ cosα − sinα R α = ⌈ ⌉. sinα cosα

要理解为什么,回想一下变换矩阵的每一列描述了基向量的图像。旋转 (1,0) 给出的结果是 (cosα, sinα),而旋转 (0,1) 给出的结果是 (cos(α + π∕2), sin(α + π∕2))。这一点在图 4.8 中有所说明。

PIC

图 4.8:旋转矩阵说明

就像上面一样,我们可以通过可视化单位正方形的图像,来获得对发生的几何学理解。

PIC

图 4.9:旋转

4.3.3 剪切

另一个重要的几何变换是剪切,这在物理学中有广泛应用。剪切力(en.wikipedia.org/wiki/Shear_force)是一对方向相反的力作用于同一物体。

PIC

图 4.10:剪切

它的矩阵以如下形式给出

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ Sx = ⌈1 kx⌉ , Sy = ⌈ 1 0⌉, S = ⌈1 kx⌉ , 0 1 ky 1 ky 1

其中 S[x]、S[y] 和 S 代表在 x、y 方向及两者方向上的剪切变换。

4.3.4 反射

到目前为止,我们在欧几里得平面上看到的所有变换都保持了空间的“方向性”。然而,情况并非总是如此。由以下矩阵给出的变换

 ⌊ ⌋ ⌊ ⌋ − 1 0 1 0 R1 = ⌈ ⌉ , R2 = ⌈ ⌉ 0 1 0 − 1

相对于 x 轴和 y 轴,它们充当反射。

PIC

图 4.11:反射

当与旋转结合使用时,我们可以利用反射来翻转基向量。例如,变换将 e[1] 映射到 e[2],并将 e[2] 映射到 e[1]。

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ R = ⌈0 − 1⌉ ⌈1 0⌉ = ⌈0 1⌉ 1 0 0 − 1 1 0 ◟--◝◜--◞ ◟---◝◜--◞ 旋转 π∕2 =R2

PIC

图 4.12:交换 e[1] 和 e[2] 是反射和旋转

这些类型的变换在理解行列式时起着至关重要的作用,正如我们将在下一章中看到的那样。

一般来说,反射可以很容易地定义在更高维空间中。例如,

 ⌊ ⌋ 1 0 0 R = ||0 1 0 || ⌈ ⌉ 0 0 − 1

是 ℝ³ 中的一个反射,它将 e[3] 翻转到相反的方向。这就像照镜子:它把左变右,把右变左。

反射可以多次翻转方向。由以下变换给出的

 ⌊ ⌋ 1 0 0 R = || || ⌈0 − 1 0⌉ 0 0 − 1

翻转 e[2] 和 e[3],改变了方向两次。稍后我们将看到,给定变换的“方向变化次数”是其一个重要的描述性特征。

4.3.5 正交投影

其中一个最重要的变换(不仅仅是在二维空间中)是正交投影。我们已经在讨论内积及其几何表示时提到过这一点,在 2.2.3 节中有讲解。仔细观察会发现,它们是线性变换。

PIC

图 4.13:正交投影

回忆一下(3.2.3 节),x 到某个 y 的正交投影可以写成

proj(x) = ⟨x,y⟩y. y ⟨y,y⟩

⟨⋅,⋅⟩的双线性特性立即意味着 projy 也是线性的。通过一些代数,我们可以用矩阵的形式重新写出这个公式。我们有

projy(x) = ⟨x,y-⟩y ⟨y,y ⟩ ⌊ ⌋ = x1y1-+-x2y2⌈y1 ⌉ ∥y ∥2 y2 ⌊ ⌋ ⌊ ⌋ 1 y21 y1y2 x1 = ----2⌈ 2 ⌉ ⌈ ⌉ ∥y ∥ y1y2 y2 x2 1 = ----2yyT x, ∥y ∥

因此,

 ⌊ ⌋ 1 y2 y y projy = ----2⌈ 1 1 2⌉ ∥y ∥ y1y2 y22 1 = ----2yyT x ∥y ∥

注意到

 y2 projy(e2) = y1 projy(e1),

所以标准基向量的像不是线性无关的。结果,平面在 proj[y]下的像是 span(y),这是一个一维子空间。通过这个例子,我们可以看到,线性变换下的向量空间的像不一定与起始空间的维度相同。

通过这些例子和所掌握的知识,我们对线性变换有了基本的理解,这也是神经网络最基本的构建块。在下一节中,我们将研究线性变换如何影响向量空间的几何结构。

4.4 行列式,或者线性变换如何影响体积

在第 4.3 节中,我们已经看到,线性变换(定义 16)可以看作是扭曲由基向量决定的网格。

跟随我们的几何直觉,我们怀疑,测量变换如何扭曲体积和距离能够提供一些有价值的洞察。正如我们在本章中所见,确实如此。保持距离或范数不变的变换是特殊的,这也催生了主成分分析等方法。

4.4.1 线性变换如何缩放面积

让我们再回到欧几里得平面。考虑任何线性变换 A,将单位正方形映射到一个平行四边形。

图片

图 4.14:单位正方形在线性变换下的像

这个平行四边形的面积描述了 A 如何缩放单位正方形。暂时将其称为λ,即,

area(A(C)) = λ ⋅area(C),

其中 C = [0,1] × [0,1]是单位正方形,A(C)是其像

A(C) := {Ax : x ∈ C}.

由于线性性,λ也符合任何矩形(其边与坐标轴平行)面积与其在 A 下像的比例。如图 4.15 所示,我们可以将任何平面物体近似为矩形的并集。

如果所有矩形都被λ缩放,那么矩形的并集也会按该比例缩放。因此,λ也就是任何平面物体 E 与其像 A(E) = {Ax : x ∈E}之间的缩放比例。

图片

图 4.15:用矩形并集近似平面物体

这个数量λ揭示了变换本身的很多信息,但仍然有一个问题:我们如何计算它?

假设我们的线性变换由以下给出:

 ⌊ ⌋ x y A = ⌈ 1 1⌉ , x2 y2

因此,其列向量 x = (x[1],x[2])和 y = (y[1],y[2])描述了平行四边形的两条边。这是单位正方形的图像。

PIC

图 4.16:线性变换下单位正方形的图像

我们的面积缩放因子λ等于这个平行四边形的面积,所以我们的目标是计算这个。

任何平行四边形的面积都可以通过将底边的长度(此处为∥x∥)与高度 h 相乘来计算。(你可以通过在平行四边形的右侧切去一个三角形并将其放到左侧,重新排列成一个矩形来轻松看到这一点。)h 是未知的,但通过基础三角学,我们可以看到 h = sinα∥y∥,其中α是 x 和 y 之间的角度。

因此,

area = sin α∥y∥∥x ∥.

这几乎是 x 和 y 的点积。(回想一下,点积可以表示为⟨x,y⟩ = ∥x∥∥y∥cosα。)然而,sinα部分并不匹配。

幸运的是,我们可以使用一个巧妙的技巧将其转换为点积!由于 sinα = cos(α −π 2),我们有

 π- area = cos(α− 2)∥x∥∥y ∥.

问题是,x 和 y 之间的角度不是α −π 2。然而,我们可以通过应用旋转来轻松解决这个问题(第 4.3.2 节)。应用变换

 ⌊ ⌋ 0 1 R = ⌈ ⌉, − 1 0

我们得到

y = Ry = (y,− y ). rot 2 1

由于∥yrot∥ = ∥y∥ ,我们有

area = sin α∥y∥∥x ∥ π = cos(α − 2)∥x ∥∥y∥ π = cos(α − 2)∥x ∥∥yrot∥ = ⟨x,yrot⟩.

数量⟨x,y[rot]⟩可以仅使用矩阵 A 的元素来计算:

⟨x, yrot⟩ = x1y2 − x2y1.

注意,⟨x,y[rot]⟩可能为负!当 y = Ae[2]和 x = Ae[1]之间的角度,从逆时针方向测量时,大于π时,就会发生这种情况,因为这意味着 cos(α −π 2)/span>0\。

因此,⟨x,y[rot]⟩被称为平行四边形的带符号面积。

在二维空间中,我们称之为线性变换的行列式。也就是说,对于任何给定的线性变换/矩阵 A ∈ℝ^(2×2),其行列式定义为

L(U,V ) = {f : U → V | f is linear}(4.9)

行列式通常写作 jAj,但我们将避免使用这种符号。我们将处理任何矩阵 A ∈ℝ^(n×n)的行列式,但为了直观起见,我们先保持在 2 × 2 的情况。

行列式还揭示了向量的方向性:正行列式表示正方向,负行列式表示负方向。(直观地说,正方向意味着从 x 到 y 的角度在逆时针方向上位于 0 到π之间;等价地,顺时针方向上从 x 到 y 的角度在π到 2π之间。)这一点在下图 4.17 中得到了体现。

PIC

图 4.17:平面中两个向量的方向性

总的来说,

area(A(E)) = |detA|area(E)

(4.10)

成立,其中 E ⊆ℝ²是一个平面对象,且

A (E) = {Ax : x ∈ E }

是变换 A 下 E 的像。

尽管我们只展示了二维空间中的(5.4.1),但这在一般情况下是成立的。(尽管我们尚未知道如何在其他维度定义行列式。)

因此,如果 e[1]和 e[2]是平面上的一组基,方程式(5.4.1)和(5.4.1)告诉我们二维空间中的行列式等于

det A = orientation(Ae ,Ae )× area(Ae ,Ae ) 1 2 1 2

基于欧几里得平面的示例,我们已经建立了足够的几何直觉来理解线性变换如何扭曲体积并改变空间的方向。这些是通过行列式的概念来描述的,我们已经在特殊情况下(5.4.1)定义了行列式。接下来,我们将继续研究行列式的完整一般性概念。

为了引入行列式的正式定义,我们将采用不同于常规的路径。最常见的做法是直接用一个复杂的公式定义线性变换 A 的行列式,然后展示其所有几何性质。

与其如此,我们将通过推广我们在前一节学到的几何概念来推导行列式公式。在这里,我们大致按照 Peter D. Lax 的《线性代数及其应用》的大纲进行。

我们通过引入一些关键符号奠定了基础。设

A = (ai,j)ni,j=1 ∈ ℝn ×n

设矩阵为具有列 a[1],…,a[n]的矩阵。当我们在 4.1.1 节引入矩阵作为线性变换的概念时,我们看到第 i 列是第 i 个基向量的像。为了简便起见,假设 e[1],e[2],…,e[n]是标准正交归一基,即 e[i]是第 i 个坐标为 1,其余坐标为 0 的向量。因此,Ae[i] = a[i]。

在我们对欧几里得平面 4.3 节的探索中,我们已经看到行列式是基向量像的方向与它们所定义的平行四边形面积的乘积。按照这个逻辑,我们可以通过以下方式定义 n×n 矩阵的行列式

det A = orientation(Ae1,...,Aen )× volume(Ae1, ...,Aen )

很快出现了两个问题。首先,如何在 n 维空间中定义多个向量的方向?其次,我们如何计算面积?

我们不是要找到这些问题的答案,而是要给故事增加一点曲折:首先,我们将找到一个方便的行列式公式,然后用它来定义方向。

4.4.2 行列式的多线性

为了更明确地表示行列式与矩阵列 a[i] = Ae[i] 之间的关系,我们将写作

det A = det(a1,...,an ).

从这个角度思考行列式,det 只是多个变量的一个函数:

det : ℝn × ⋅⋅⋅× ℝn → ℝ. ◟-----◝◜----◞ n times

好消息是:det(a[1],…,a[n]) 在每个变量上都是线性的。也就是说,

det(a1,...,αai+ βbi,...an) = α det(a1,...,ai,...an )+β det(a1,...,bi,...an)

这是成立的。我们不会证明这一点,但由于行列式代表的是带符号的体积,你可以通过查看图 4.18 来自我验证。

PIC

图 4.18:det(a[1],a[2]) 的多线性

线性性的一个结果是,我们可以将行列式表示为标准基向量 e[1],…,e[n] 的行列式的线性组合。例如,考虑以下内容。因为

 n Ae = a = ∑ a e, 1 1 i,1 i i=1

我们得到

 n ∑ det(a1,a2,...,an) = ai,1 det(ei,a2,...,an). i=1

再进一步,利用

 ∑n a2 = aj,2ej, j=1

我们开始注意到一个规律。借助线性性,我们得到

det(a[1], a[2],…, a[n]) = ∑ [i=1]^n ∑ [j=1]^n a[i,1] a[j,2] det(e[i], e[j], a[3],…, a[n])。

(4.11)

我们可以看到,系数 a[i,1]a[j,2] 中的行索引与 det(e[i],e[j],a[3],…,a[n]) 中的 e[k] 的索引相匹配。在一般情况下,这个模式可以通过置换来形式化;即,{1,2,…,n} 集合的排列。

你可以将置换想象成一个函数 σ,它将 {1,2,…,n} 映射到自身,使得对于每个 j ∈{1,2,…,n},都有一个 i ∈{1,2,…,n},使得 σ(i) = j。换句话说,你把从 1 到 n 的每个整数按某种顺序排列。{1,2,…,n} 上的所有可能置换的集合用 S[n] 表示。

继续 (5.4.2) 并进一步展开 A 的行列式,我们得到

 ∑ ∏n det(a1,...,an) = [ aσ(i),i]det(eσ(1),...,eσ(n)). σ∈Sn i=1

这个公式并不是最容易理解的。你可以把每一项 ∏ [i=1]^n a[σ(i),i] 想象成把 n 个棋子放置在一个 n×n 的棋盘上,使得它们互不攻击。

PIC 图 4.19:项 a[σ(1)1]⋅⋅⋅a[σ(n)n] 的解剖

公式

∑ [∏n ] aσ(i),i det(eσ(1),...,e σ(n)) σ∈Sn i=1

结合了我们可以这样做的所有可能方式。

只剩下一件事:计算 det(e[σ(1)],…,e[σ(n)])。

记得我们在讨论欧几里得平面中的反射和旋转的组合时(第 4.3.4 节)吗?由 e[i]→e[σ(i)] 确定的变换与此类似。当谈到排列时,了解每个排列都可以通过一次交换两个元素得到是很有用的。在排列中,交换两个元素的次数——也就是影响两个元素的排列——被称为 σ 的符号。在我们讨论的线性变换 e[i]→e[σ(i)] 中,σ 的交换次数就是所需反射的次数,sign(σ) 是 (e[σ(1)],…,e[σ(n)]) 的方向。

因此,借助这些,我们终于可以为行列式和方向给出一个正式的定义。

定义 20. (行列式与方向)

设 A ∈ℝ^(n×n) 为任意矩阵,a[i] ∈ℝ^n 为它的第 i 列。A 的行列式定义为

detA = det( a[1],…, a[n]) = ∑ [σ∈S[n]] sign(σ) [ ∏ [i=1]^n a [σ(i),i] ],

(4.12)

以及向量 a[1],…,a[n] 的方向是

orientation(a1,...,an ) := sign(detA ).

当 det 表示法不方便时,我们通过将矩阵的元素放入一个大的绝对值符号中来表示行列式:

 || || ||a1,1 a1,2 ... a1,n|| |a2,1 a2,2 ... a2,n| detA = || . . . . ||. || .. .. .. .. || || || an,1 an,2 ... an,n

当我还是一名年轻的数学学生时,行列式公式(4.12)是我在第一节线性代数课上直接看到的。没有解释它与体积和方向的联系,我花了几年时间才真正理解它。我依然认为,行列式是线性代数中最复杂的概念之一,尤其是在没有几何动机的情况下进行定义时。

现在你已经对行列式有了基本的理解,你可能会问:我们如何在实践中计算行列式呢?对所有排列求和并计算它们的符号,从计算角度来看并不是一项简单的操作。

好消息是:行列式有一个递归公式。坏消息是:对于一个 n×n 的矩阵,它涉及 n 个 (n − 1) × (n − 1) 的矩阵。尽管如此,相比于排列公式,这已经是一个很大的进步了。让我们来看看!

定理 21. (行列式的递归公式)

设 A ∈ℝ^(n×n) 为任意方阵。那么

detA = ∑ [j=1]^n (−1)^(j+1) a [1,j] detA[1,j],

4.13

其中 A[i,j] 是从 A 中通过去除其第 i 行和第 j 列得到的 (n − 1) × (n − 1) 矩阵。

我们不提供证明,而是通过一个例子来演示这个公式。对于 3 × 3 的矩阵,它看起来是这样的:

| | ||a b c|| || || || || || || || || ||e f|| ||d f|| ||d e|| ||d e f||= a ||h i||− b||g i||+ c||g h||. |g h i|

现在我们既有几何直觉,又有递归公式,让我们来看看行列式的最重要的性质!

4.4.3 行列式的基本性质

在计算行列式时,我们更倾向于构造基本的构建模块和组合规则。(正如我们多次见到的模式,即使在推导 (4.12) 时也是如此。)这些规则由行列式的基本性质体现,我们现在将讨论这些基本性质。

第一个性质涉及到复合与行列式的关系。

定理 22. (行列式的乘积)

∈ℝ^(n×n) 是两个矩阵。那么

detAB = detAdetB。

(4.14)

方程式 (4.14) 被称为行列式乘积法则,它的证明涉及一些基于公式 (4.12) 和 (4.13) 的复杂计算。我们不提供完整的证明,而是给出一个直观的解释。毕竟,我们希望用数学构建算法,而不是构建数学。

所以,detAB = detAdetB 的解释非常简单。如果我们把矩阵 A,B ∈ℝ^(n×n) 看作是线性变换,我们刚刚看到 detA 和 detB 决定了它们如何缩放单位立方体。

由于这些线性变换的组合是矩阵乘积 AB,线性变换 AB 将单位立方体缩放为带符号体积 detAdetB 的平行六面体。(因为应用 AB 相当于先应用 B,再在结果上应用 A。)

因此,通过我们对行列式的理解,由于 AB 的缩放因子也是 detAB,(4.14)成立。

我们可以通过递归公式 (4.13) 进行实际证明,从而得到一个长而复杂的计算过程。

乘积法则的一个直接推论是矩阵的行列式与其逆矩阵之间的特殊关系。

定理 23\。

设 A ∈ℝ^(n×n) 是一个任意可逆矩阵。那么

detA −1 = (det A)−1.

证明。使用乘积法则,我们有

1 = detI = detAA −1 = (det A)(detA −1),

从而得出定理。

由于这个原因,我们也可以得出结论,行列式在相似关系下保持不变。

定理 24\。

设 A,B ∈ ℝ^(n×n) 是两个相似矩阵,且 B = T^(−1)AT,其中 T ∈ℝ^(n×n)。则

detA = detB.

证明。这个结论直接跟随于

 −1 detB = det T AT = det T−1 detA detT = det A,

这就是我们需要证明的。

另一个重要的结论是行列式与矩阵所处的基底无关。如果 A : U →U 是一个线性变换,且 P = {p[1],…,p[n]} 和 R = {r[1],…,r[n]} 是 U 的两个基底,那么我们知道,变换的矩阵(第 4.2.1 节)之间的关系为

AP = T−1ART,

其中 A[S]是变换 A 在基 S 下的矩阵,T ∈ℝ^(n×n)是基变换矩阵(第 4.2.1 节)。前面的定理意味着 detA[P] = detA[R]。因此,行列式不仅仅是矩阵的定义,它也适用于线性变换!

关于行列式,有一个重要的对偶关系:你可以交换矩阵的行和列,同时保持所有与行列式相关的恒等式成立。

定理 25\。

设 A ∈ℝ^(n×n)是一个任意矩阵。那么

detA = detAT .

证明。假设 A = (a[i,j])[i,j=1]^n。我们用 a[i,j]^t = a[j,i]表示其转置矩阵的元素。根据(4.12),我们有

 T ∑ ∏n t detA = sign (σ ) aσ(i),i σ∈Sn i=1 ∑ ∏n = sign (σ ) ai,σ(i). σ∈Sn i=1

现在来一个技巧。由于积∏ [i=1]^n a[i,σ(i)]遍历所有的 i,并且项的顺序不重要,我们可以将项的顺序排列为 i = σ(−1)(1),…,σ(−1)(n)。由于

sign(σ −1) = sign(σ),

继续上述计算,我们得到

 n n ∑ sign(σ)∏ a = ∑ sign(σ−1)∏ a . i,σ(i) σ− 1(j),j σ∈Sn i=1 σ∈Sn j=1

因为每个置换都是可逆的,并且σ→σ^(−1)是一个双射,因此对σ ∈S[n]求和与对σ^(−1) ∈S[n]求和是一样的。

综合上述所有内容,我们得到

 ∑ ∏n detA = sign(σ−1) aj,σ−1(j) σ∈Sn j=1 n = ∑ sign(σ)∏ a σ(j),j σ∈Sn j=1 = detAT .

这是我们需要证明的内容。

定理 26\。

设 A ∈ℝ^(n×n)是一个任意矩阵,设 A^(i,j)表示通过交换 A 的第 i 列和第 j 列得到的矩阵。那么

detAi,j = − detA,

换句话说,交换 A 的任何两列会改变行列式的符号。类似地,交换两行也会改变行列式的符号。

证明。这个结论可以通过巧妙应用(4.14)得到,注意到 A^(i,j) = AI^(i,j),其中 I^(i,j)是通过交换单位矩阵的第 i 列和第 j 列得到的。detI^(i,j)是形如 det(e[σ(1)],…,e[σ(n)])的行列式,其中σ是一个简单交换 i 和 j 的置换。(即σ是一个置换)。因此,

 i,j i,j detA = detA det I = − detA,

这是我们需要证明的内容。

关于交换行,我们可以应用前面的结果,因为转置矩阵保持行列式不变。

结果是,具有两个相同的行的矩阵行列式为零。

定理 27\。

A ∈ ℝn ×n 是一个具有两个相同的行或列的矩阵。那么detA = 0

证明。假设 i -th 和 j -th 列是相同的。由于这两列相等,detAi,j = det A 。然而,应用之前的定理(即交换两列会改变行列式的符号),我们得到  i,j detA = − detA 。只有当 detA = 0 时,这才成立。

再次,转置矩阵给出了行的结论。

作为另一个结果,我们得到线性相关的向量系统与行列式之间的重要联系。

定理 28。

A ∈ ℝn×n 是一个矩阵。则其列是线性相关的,当且仅当 detA = 0 。类似地,矩阵 A 的行是线性相关的,当且仅当 detA = 0

证明。(i) 首先,我们将证明线性相关的列(或行)意味着 detA = 0。像往常一样,设 A 的列为 a[1],…,a[n],为了简便起见,假设

 n ∑ a1 = αiai. i=2

由于行列式是列的线性函数,我们得到

 n ∑ det(a1,a2,...,an) = αidet(ai,a2,...,an ). i=2

根据之前的定理,所有项 det(a[i],a[2],…,a[n]) 都为零,这意味着 detA = 0,这就是我们需要证明的。如果行是线性相关的,我们可以应用上述内容得到 detA = detA^T = 0。

  1. 现在,让我们证明 detA = 0 意味着列是线性相关的。与其提供一个非常复杂的精确证明,不如给出一个直观的解释。

回顾行列式是由列向量所给出的平行六面体的方向和体积之积。由于方向是 ±1,detA 意味着平行六面体的体积为 0。只有当 n 列向量位于一个 n − 1 维的子空间中时,这才可能发生,也就是说,它们是线性相关的。

我们可以立即应用这个结果得到以下结论。

推论 2。

设 A ∈ ℝ^(n×n) 为一个矩阵,其中有一列(或一行)是常数零列(或零行)。则 detA = 0。

由于行列式是基向量像的带符号体积,它在某些情况下可以为零。这些变换是非常特殊的。什么时候会发生这种情况呢?让我们回到欧几里得平面,构建一些直观的理解。

在这里,我们得到

|| || ||x1 y1|| ||x y || = x1y2 − x2y1 = 0, 2 2

或换句话说,x1 y1- = x2 y2-。还有一种解释:向量 (y[1],y[2]) 是 (x[1],x[2]) 的标量倍;也就是说,它们是共线的,意味着它们位于通过原点的同一条直线上。从线性变换的角度来看,这意味着 e[1] 和 e[2] 的像位于 ℝ² 的一个子空间中。正如我们接下来将看到的,这与变换的可逆性密切相关。

定理 29.(可逆性与行列式)线性变换A ∈ ℝn×n 是可逆的,当且仅当detA ⁄= 0

证明。当我们引入可逆性的概念(定义 17)时,我们看到!A 只有在其列!a1,...,an 构成基时才可逆。因此,它们是线性独立的。

由于列的线性独立性(定义 3)等同于行列式非零,因此结果成立。

4.5 小结

那么,你的眼睛在第一次使用它们之后会疼吗?我第一次学习矩阵作为线性变换时肯定是疼的。这是抽象观点第一次显示出其价值的地方,相信我,后来它将带来更多的回报。

让我们快速回顾一下这一章。

我们已经学到,除了数字表格,矩阵还可以表示线性变换:

 ⌊ ⌋ ⌊ ⌋ ⌊ ∑m ⌋ a1,1 a1,2 ... a1,m x1 j=1a1,jxj || a a ... a || || x || || ∑m a x || Ax = || 2,.1 2.,2 2,m. || || .2|| = || j=1.2,j j|| , |⌈ .. .. ... .. |⌉ |⌈ .. |⌉ |⌈ .. |⌉ ∑m an,1 an,2 ... an,m, xm j=1an,jxj

其中,A 的列描述了基向量在线性变换 x →Ax 下的像。

为什么这对我们有用呢?把数学看作一种解决问题的工具。解决问题的关键往往是找到我们感兴趣的对象的合适表示。把矩阵看作是一种变换数据的方法,给我们提供了急需的几何视角,开启了一整套新的方法。

以这种方式看矩阵,我们很快就能理解为什么矩阵乘法会按这种方式定义。定义是

 ∑l n,m AB = ( ai,kbk,j)i,j=1 k=1

起初看起来令人畏惧,但从线性变换的角度来看,它都揭示出是一个简单的复合:首先应用变换 B,然后是 A。

然而要小心:线性变换和矩阵并不完全相同,因为矩阵表示依赖于我们向量空间的基础(基)。(看吧,我早就告诉过你,基是有用的。)

矩阵还具有一个重要的量,叫做行列式,最初由复杂的公式定义

 ∑ ∏n det(a1,...,an) = [ aσ(i),i]det(eσ(1),...,eσ(n)), σ∈Sn i=1

但是,通过利用我们新获得的几何视角的研究发现,行列式只是描述了线性变换如何扭曲领域空间的体积,以及它如何改变基向量的方向。

对于我们这些机器学习的从业者来说,从矩阵到线性变换的概念跃迁是更有趣的(与理论学习相比,在理论中我们通常先学习线性变换,再学习矩阵)。 例如,这让我们能够将神经网络中的一层看作是对特征空间的拉伸、旋转、剪切,甚至可能是反射。

在下一章,我们将从一个略有不同的角度重新审视矩阵:方程组。 当然,一切都是相互联系的,我们最终会回到原点,从更高的视角看我们所知道的知识。 这是因为学习是一个螺旋过程,我们正在快速上升。

4.6 问题

问题 1. 证明如果 A ∈ℝ^(n×n)是可逆矩阵,则

 −1 T T −1 (A ) = (A ) .

问题 2. 设 R[α]为二维旋转矩阵,由以下定义:

 ⌊ ⌋ ⌈cosα − sinα ⌉ R α = sinα cosα .

证明 R[α]R[β] = R[α+β]。

问题 3. 设 A = (a[i,j])[i,j=1]^n ∈ℝ^(n×n)为矩阵,D ∈ℝ^(n×n)为对角矩阵,定义如下:

 ⌊ ⌋ | d1 0 ... 0 | D = |⌈ 0 d2 ... 0 |⌉ , 0 0 ... dn

其中所有元素在对角线外均为零。证明

 ⌊ ⌋ d1a1,1 d2a1,2 ... dna1,n || || DA = || d1a2,1 d2a2,2 ... dna2,n || |⌈ ... ... ... ... |⌉ d1an,1 d2an,2 ... dnan,n

 ⌊ ⌋ |d1a1,1 d1a1,2 ... d1a1,n| |d2a2,1 d2a2,2 ... d2a2,n| AD = || . . . . || . |⌈ .. .. .. .. |⌉ dnan,1 dnan,2 ... dnan,n

问题 4. 设∥⋅∥为ℝ^n 上的范数,A ∈ℝ^(n×n)为任意矩阵。

证明 A 是可逆的当且仅当该函数

∥x∥∗ := ∥Ax ∥

是ℝ^n 上的一个范数。

问题 5. 设 U 是一个有范空间,f : U →U 是一个线性变换。

如果

∥x ∥∗ := ∥f(x)∥

是一个范数,f 是否一定可逆?

提示:考虑向量空间ℝ[x],其范数为

 n n ∑ 2 1∕2 ∑ i ∥p∥ = ( pi) , p(x) = pix i=0 i=0

以及线性变换 f : p(x)→xp(x)。

问题 6. 设⟨⋅,⋅,⟩为ℝ^n 上的内积。证明存在矩阵 A ∈ℝ^(n×n),使得

⟨x,y ⟩ = xT Ay, x,y ∈ ℝn.

(回想一下,我们将向量 x, y ∈ℝ^n 视为列向量。)

问题 7. 设 A ∈ℝ^(n×n)为矩阵。如果 x^T Ax/span>0 对所有非零 x ∈ℝ^n 成立,则 A 称为正定矩阵。

证明 A 是正定的当且仅当

⟨x,y⟩ := xTAy

是一个内积。

问题 8. 设 A ∈ℝ^(n×m)为矩阵,并将其列向量记为 a[1],…,a[n] ∈ℝ^n。

(a) 证明对于所有 x ∈ℝ^m,我们有 Ax ∈ span(a[1],…,a[n])。

(b) 设 B ∈ℝ^(m×k),并将 AB 的列记为 v[1],…,v[k] ∈ℝ^n。证明

v1,...,vk ∈ span(a1,...,an).

问题 9. 设 A ∈ℝ^(n×n)为矩阵。证明

⟨Ax, y⟩ = ⟨x,AT y⟩

对所有 x, y ∈ℝ^n 成立,其中⟨⋅,⋅⟩为欧几里得内积。

问题 10. 计算以下矩阵的行列式:

 ⌊1 2 3⌋ | | A = |⌈4 5 6|⌉ . 7 8 9

问题 11. 设 A ∈ℝ^(n×n) 为矩阵,c ∈ℝ 为常数。

(a) 证明

| | ||a1,1 ... ca1,i ... a1,n || ||a ... ca ... a || || 2.,1 .2,i 2.,n ||= cdetA || .. ... .. ... .. || || || an,1 ... can,i ... an,n

对所有 i = 1,…,n 成立。

(b) 证明

| | ||a1,1 a1,2 ... a1,n || || . . . . || | .. .. .. .. | ||ca ca ... ca ||= cdet A || .i,1 i.,2 .i,n|| || .. .. ... .. || || || an,1 an,2 ... an,n

对所有 i = 1,…,n 成立,并且

(c) 证明

det(cA ) = cndet A.

问题 12. 设 A ∈ℝ^(n×n) 为上三角矩阵。(即,所有对角线以下的元素为零。)证明

 ∏n detA = ai,i. i=1

证明对下三角矩阵同样成立。(即,矩阵中对角线以上的元素为零。)

问题 13. 设 M ∈ℝ^(n×m) 为具有区块结构的矩阵

 ⌊ ⌋ ⌈A B ⌉ M = 0 C ,

其中 A ∈ℝ^(k×k), B ∈ℝ^(k×l), C ∈ℝ^(l×l)。

证明

detM = detA detC.

加入我们的 Discord 社区

与其他用户、机器学习专家和作者本人一起阅读本书。提问、为其他读者提供解决方案,通过“问我任何问题”环节与作者交流,更多内容尽在其中。扫描二维码或访问链接加入社区。packt.link/math

PIC

矩阵与方程

因此,矩阵不仅仅是数字的表格,而是线性变换;我们已经花费了长篇幅的章节来探索这一关系。

现在,我希望我们再次回到那些老旧的数字表格,但这次是代表线性方程组。为什么?很简单。因为解线性方程是推动关键理论和技术创新的动力。在上一章中,我们谈到了逆矩阵,但没有实际计算它。通过我们即将学习的内容,我们不仅能够计算逆矩阵,还能迅速地完成这个过程。

开始工作吧!

第七章:5.1 线性方程

在实践中,我们可以将多个问题转化为线性方程。例如,一个现金分配机有$900,其中包含$20 和$50 钞票。我们知道,$20 钞票的数量是$50 钞票的两倍。问题是,这台机器分别有多少张$20 和$50 钞票?

如果我们将$20 钞票的数量记作 x[1],$50 钞票的数量记作 x[2],那么我们得到以下方程:

 x1 − 2x2 = 0 20x1 + 50x2 = 900.

对于现在我们所拥有的两个变量,这些方程可以通过将其中一个表示为另一个的形式轻松求解。在这里,第一个方程意味着 x[1] = 2x[2]。将其代入第二个方程,我们得到 90x[2] = 900,从而得出 x[2] = 10。然后将其代入 x[1] = 2x[2],最终得到解:

x = 20 1 x2 = 10.

然而,对于像实际应用中那样有成千上万的变量,我们需要更多的技巧。这就是线性代数的用武之地。通过引入矩阵和向量

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ A = ⌈ 1 − 2⌉ , x = ⌈x1 ⌉, b = ⌈ 0 ⌉ , 20 50 x2 900

这个方程可以写成 Ax = b 的形式。也就是说,从线性变换的角度来看,我们可以重新表述问题:哪个向量 x 通过变换 A 映射到 b?这个问题在线性代数中非常重要,我们将专门用这一部分来解决它。

5.1.1 高斯消元法

让我们回顾一下之前的例子:

 x1 − 2x2 = 0 20x1 + 50x2 = 900.

我们可以使用第一个方程 x[1] − 2x[2] = 0 来消去第二个方程 20x[1] + 50x[2] = 900 中的 x[1]项。我们可以通过将其乘以 20 并从第二行中减去来做到这一点,得到 90x[2] = 900,从而得出 x[2] = 10。然后可以将其代入第一行,得到 x[1] = 20。

那么一般情况呢?对于一般的 A ∈ℝ^(n×n) 和 x,b ∈ℝ^n,这是否有效?绝对有效。到目前为止,我们已经使用了两条规则来操作线性系统中的方程:

  1. 将方程乘以一个非零的标量不会改变解。

  2. 将一行的标量倍数加到另一行上也不会改变解。

之前,我们反复应用这些方法来逐步消去变量。对于 n 个变量,我们也能轻松地做到!首先,让我们看看我们在谈论什么!

定义 21.(线性方程组)

设 A ∈ ℝ^(n×n) 是一个矩阵,b ∈ ℝ^n 是一个向量。方程组的集合为

Equation image

(5.1)

称为由 A 和 b 决定的线性方程组。

线性方程组通常写成简短的形式 Ax = b,其中 A 称为系数矩阵。如果向量 x 满足 Ax = b,它就叫做解。

说到解,是否存在解,如果存在,我们如何找到它们呢?

如果 a[11] 不为零,我们可以将 (5.1) 中的第一个方程乘以 aak111,并从第 k 个方程中减去它。

这样,x[1] 将从除第一行以外的所有行中消去,得到

Equation image

(5.2)

为了更清楚地表示这个符号,我们将新的系数表示为 a[ij]^((1)) 和 b[i]^((1))。所以,我们得到

L(U,V ) = {f : U → V | f is linear}(5.3)

我们可以重复上述过程,并使用第二个方程消去第三个方程中的 x[2] 变量,以此类推。

总共可以进行 n − 1 次这样的操作,最终得到一个方程系统 A^((n−1))x = b^((n−1)),其中 A^((n−1)) 的所有对角线以下的系数都是 0:

L(U,V ) = {f : U → V | f is linear}(5.4)

请注意,第 k 步的消元仅影响从第 (k + 1) 行开始的系数。现在我们可以倒退操作:最后一个方程 a[nn]^((n−1))x[n] = b[n]^((n−1)) 可用于求解 x[n]。将其代入 (n − 1) 阶方程中,得到 x[n−1]。以此类推,我们最终可以求出所有的 x[1],…,x[n],从而获得线性方程组的解。

这个过程叫做高斯消元法,它非常重要。它不仅仅用于解线性方程,还可以用来计算行列式、将矩阵分解成简单矩阵的乘积等等。我们将详细讨论所有这些内容,但现在先多关注方程吧。

不幸的是,并非所有线性方程都能解。例如,考虑以下系统

 x1 + x2 = 1 2x1 + 2x2 = − 1.

从第二个方程中减去第一个方程得到

x1 + x2 = 1 x1 + x2 = − 2

在第一步中,明确表明该方程无解。

在我们进入技术细节之前,让我们看看高斯消元法如何在实际中应用的一个简单例子!

5.1.2 手动高斯消元法

为了更深入地理解高斯消元法,让我们考虑这个简单的方程系统

 x + 0x − 3x = 6 1 2 3 2x1 + 1x2 + 5x3 = 2 − 2x1 − 3x2 + 8x3 = 2.

为了跟踪我们的进展(并且因为我们懒得写太多),我们记录下中间结果:

 | 1 0 − 3|6 2 1 5 |2 | − 2 − 3 8 |2

系数矩阵 A 在左边,b 在右边。为了更好地掌握该方法,我建议你亲自动手计算,跟着步骤做。

在从第二个和第三个方程中消去第一个变量之后,我们得到

 | 1 0 − 3 | 6 0 1 11 |− 10 | , 0 − 3 2 | 14

最终步骤的结果是

 | 1 0 − 3| 6 0 1 11 |− 10 | . 0 0 35 |− 16

从这个形式中,我们可以逐个解开方程的解。

在 21 世纪,手动解线性方程的机会几乎为 0。(如果你是在 22 世纪或更晚的时代阅读这本书,我同时感到无比荣幸和惊讶。或者,如果我还活着,我肯定会这么感受。)不过,理解解决线性方程背后的基本原理仍然能带你走得很远。

5.1.3 什么时候可以进行高斯消元?

如果你仔细跟随高斯消元的描述,可能会注意到过程可能会出现问题。在任何消元步骤中,我们可能会不小心除以 0!

例如,在方程(5.2)给出的第一步之后,新的系数呈现如下形式

(aij − a1j ai1), a11

如果 a[11] = 0,则此公式无效。一般来说,步骤 k 涉及到除以 a[kk]^((k−1))。由于 a[kk]^((k−1))是递归定义的,因此用 A 来描述它并不简单。为此,我们引入了主子式的概念,即矩阵的左上方子行列式。

定义 22\。(主子式)

 n n×n A = (aij)i,j=1 ∈ ℝ 为任意方阵。定义子矩阵 Ak ∈ ℝk×k 为去掉所有大于 k 的 A 的行列。例如,

 ⌊ ⌋ [ ] ⌈a11 a12⌉ A1 = a11 , A2 = a a , 21 22

以此类推。A 的第 k 个主子式,记作 M[k],定义为

Mk := detAk.

第一个和最后一个主子式是特殊的,因为 M[1] = a[11]且 M[n] = detA。通过主子式,我们可以描述何时可以进行高斯消元。事实上,结果是

 M M a11 = M1, a(21)2 = --2, ...,a(nnn−1)= ---n-- M1 Mn −1

一般来说,a[kk]^((k−1)) = -Mk-- Mk−1

总结一下,我们可以得出以下结论。

定理 30\。

 n×n A ∈ ℝ 为任意方阵,且 Mk 为其第 k 主子式。如果对于所有 k = 1,2,...,n − 1 ,都有 Mk ⁄= 0 ,则可以成功进行高斯消元。

由于证明过程稍显复杂,我们这里不做详细讲解。(困难的步骤是证明 a[kk]^((k−1)) = M[k]∕M[k−1];其余部分立刻可以推导出。)关键是,如果没有主子式为 0,算法就完成了。

我们可以稍微简化这一要求,用行列式而不是主子式来描述高斯消元。

定理 31\。

设 A ∈ℝ^(n×n) 为任意方阵。如果 detA≠0,则所有主子式也都不为零。

结果是,如果行列式不为零,则高斯消元成功。一个简单而美妙的要求。

5.1.4 高斯消元的时间复杂度

为了掌握高斯消元算法的执行速度,让我们做一点复杂度分析。如(5.2)中所描述,第一次消元步骤涉及到每个元素的加法和乘法,除了第一行的元素。也就是说,总共有 2n(n − 1) 次操作。

下一步本质上是第一次步骤,在从 A^((1)) 中去掉第一行和第一列后得到的 (n − 1) × (n − 1) 矩阵上执行。这时,我们有 2(n − 1)(n − 2) 次操作。

按照这个思路,我们很快得到总操作次数是

∑n 2(n − i+ 1)(n− i), i=1

这看起来并不那么友好。由于我们关注的是复杂度的阶数而不是精确的次数,我们可以宽松地假设在每次消元步骤中,我们执行的是 O(n²) 次操作。所以,我们的时间复杂度是

∑n O(n²) = O(n³), i=1

这意味着我们需要大约 cn³ 次操作来进行高斯消元,其中 c 是一个任意的正常数。乍看之下,这似乎很多,但在算法的美妙领域中,这是非常不错的。O(n³) 是多项式时间,而且我们可以遇到更差的情况。

5.1.5 何时可以解线性方程组?

所以,我们刚刚看到,对于任何线性方程

Ax = b, A ∈ ℝn×n, x, b ∈ ℝn,

如果主子式 M[1],…,M[n−1] 非零,则高斯消元可以成功执行。请注意结果的一个警告:M[n] = detA 也可以为零。事实证明,这是一个非常重要的细节。

如果你一直紧跟到现在的讨论,你会发现我们漏掉了一个关键点:对于给定的线性方程,是否存在解?有三种选择:

  1. 没有解。

  2. 只有一个解。

  3. 有多个解。

从某种角度来看,这些都是相关的,但让我们从最直接的一个开始:何时我们有唯一解?答案很简单:当 A 可逆时,解可以明确写成 x = A^(−1)b。从线性变换的角度讲,我们可以找到一个唯一的向量 x,它被映射到 b。我们在以下定理中总结了这一思想。

定理 32\。

A ∈ ℝn ×n 是一个可逆矩阵。那么,对于任何 b ∈ ℝn ,方程 Ax = b 有一个唯一解,可以写成 x = A− 1b

如果 A 可逆,那么 detA 不为零。因此,利用我们之前学到的知识,可以执行高斯消元,从而得到唯一解。简单明了。

如果 A 不可逆,则剩下的两种可能性是:没有向量被映射到 b,这意味着没有解,或者多个向量被映射到 b,导致无限多个解。

你还记得我们在定理 20 中如何使用线性变换的核来描述其可逆性吗?事实证明,kerA 还可以用来求解线性系统的所有解。

定理 33\。

设 A ∈ℝ^(n×n) 为一个任意矩阵,且 x[0] ∈ℝ^n 为线性方程组 Ax = b 的一个解,其中 b ∈ℝ^n。则所有解的集合可以写为

x0 + kerA := {x0 + y : y ∈ kerA}.

证明。我们需要证明两件事:(a) 如果 x ∈ x[0] + kerA,则 x 是解;(b) 如果 x 是解,则 x ∈ x[0] + kerA。

(a) 假设 x ∈ x[0] + kerA,即 x = x[0] + y,其中 y ∈ kerA。那么,

Ax = A(x0 + y) = Ax0 + Ay = b, ◟=◝b◜◞ ◟◝=◜0◞

这表明 x 确实是一个解。

(b) 现在设 x 为一个任意解。我们需要证明 x −x[0] ∈ kerA。这很容易,因为

A (x− x0) = Ax − Ax0 = b − b = 0.

因此,(a) 和 (b) 表明 x[0] + kerA 是所有解的集合。

从理论上讲,这一定理为求解线性方程组提供了一种极好的方法,广泛地推广到有限维向量空间以外。(请注意,这一证明对所有向量空间和线性变换适用。)例如,这一精确结果用于描述非齐次线性微分方程的所有解。

5.1.6 矩阵求逆

到目前为止,我们已经看到矩阵 A ∈ℝ^(n×n) 的可逆性是解线性方程的关键。然而,我们还没有找到计算矩阵逆的办法。

让我们回顾一下逆矩阵在线性变换中的含义。如果 A 的列由向量 a[1],…,a[n] ∈ℝ^n 表示,那么 A 是将标准基向量映射到这些向量的线性变换:

A : ei ↦→ ai, i = 1,...,n.

如果箭头的方向可以被反转,也就是说,

A −1 : ai ↦→ ei, i = 1,...,n

如果这是一个定义良好的线性方程,则 A^(−1) 称为 A 的逆矩阵。

根据本章内容,求解逆矩阵的方法很简单:对于每个 i 求解 Ax = e[i],其中 e[1],…,e[n] ∈ℝ^n 为标准基。

假设 Ax[i] = e i。如果 b 可以表示为 b = ∑ [i=1]^nb[i]e i,则向量 x = ∑ [i=1]^nb[i]x[i] 是方程 Ax = b 的解:

 ∑n ∑n A ( bixi) = biAxi i=1 i=1 ∑n = biei i=1 = b.

因此,逆矩阵是其第 i 列为 x[i] 的矩阵。

我知道,这看起来很矛盾:为了找到 Ax = b 的解,我们需要逆矩阵 A^(−1)。为了求逆,我们需要解 n 个方程。答案是高斯消元法(第 5.1.1 节),它为我们提供了一种精确的计算方法来获得 A^(−1)。在下一节中,我们将把这一方法付诸实践,编写我们的矩阵求逆算法。从头开始,非常棒。

5.2 LU 分解

在上一章,我承诺你永远不需要手动解线性方程。事实证明,这个任务非常适合计算机处理。在这一章中,我们将深入探讨解线性方程的艺术,从头开始开发工具。

我们首先通过矩阵来描述高斯消元法的过程。为什么我们要这样做呢?因为矩阵乘法在现代计算机中可以非常快速地执行。将任何算法表示为矩阵形式是加速的可靠方式。

在开始时,我们的线性方程 Ax = b 由系数矩阵给出

 ⌊ ⌋ a11 a12 ... a1n || || | a21 a22 ... a2n| A = || ... ... ... ... || ∈ ℝn×n, || || |⌈ an1 an2 ... ann|⌉

在消元过程结束时,A 被转化为

 ⌊ ⌋ | a11 a12 a13 ... a1n | || 0 a(1) a (1) ... a(1)|| (n−1) | 22 23(2) 2(n2)| A = || 0 0 a 33 ... a3n || . || ... ... ... ... ... || ⌈ ⌉ 0 0 0 ... a(nnn−1)

A^((n−1)) 是上三角矩阵;也就是说,它的对角线以下的所有元素都是 0。

高斯消元法逐步执行这个任务,专注于连续的列。在第一次消元步骤后,这转化为方程式 (5.1.1),由系数矩阵描述

 ⌊ ⌋ | a11 a12 ... a1n| || 0 a(212) ... a(21)n|| (1) || . . . . || n×n A = | .. .. .. .. | ∈ ℝ . || 0 a(1) ... a(1)|| ⌈ n2 nn⌉

我们能否通过与某个矩阵相乘从 A 获得 A^((1));也就是说,我们能否找到 G[1] ∈ℝ^(n×n),使得 A^((1)) = G[1]A 成立?

是的。通过定义 G[1] 为

L(U,V ) = {f : U → V | f is linear}(5.5)

我们可以看到,A^((1)) = G[1]A 就是执行高斯消元法的第一步。(拿起笔和纸手动验证一下,这是一个很好的练习。)G[1]是下三角矩阵;也就是说,它的对角线以上的所有元素都是 0。事实上,除了第一列外,所有对角线下方的元素也都是 0。(请注意,G[1]依赖于 A。)

通过类比定义

L(U,V ) = {f : U → V | f is linear}(5.6)

我们得到 A^((2)) = G[2]A^((1)) = G[2]G[1]A,这个矩阵在前两列上是上三角矩阵。(也就是说,所有元素在对角线以下为 0,但仅限于前两列。)

我们可以继续这个过程,直到得到上三角矩阵。

A^((n−1)) = G[n−1]…G[1]A。

算法开始逐渐形成了良好的结构。G[i] 矩阵是可逆的,具有逆矩阵

 ⌊ ⌋ ⌊ ⌋ 1 0 0 ... 0 |1 0 0 ... 0| ||a21 || ||0 1 0 ... 0|| ||a11 1 0 ... 0|| || a(1) || |a31 0 1 ... 0| |0 3a(21) 1 ... 0| G −11= ||a1.1 . . . ||, G −21 = || . 22. . . || ,..., || .. .. .. .. || || .. .. .. .. || ||an1 0 0 ... 1|| || a(n12) || ⌈a11 ⌉ |⌈0 a(212) 0 ... 1|⌉

依此类推。通过逐个乘以它们的逆矩阵,我们可以将 A 表示为

 −1 −1 (n−1) A = G 1 ...G n−1A .

幸运的是,我们可以手动计算 L := G[1](−1)…G[n−1](−1)。经过快速计算,我们得到:

L(U,V ) = {f : U → V | f is linear}(5.8)

这是一个下三角矩阵。(再次提醒,不要羞于手动验证(5.8))。通过定义上三角矩阵 U := A^((n−1)),我们得到了著名的 LU 分解,将 A 分解为下三角矩阵和上三角矩阵:

A = LU.

注意,使用这个算法,我们用一个代价完成了两个任务:将 A 分解为上三角矩阵和下三角矩阵的乘积,同时执行高斯消元。

从计算角度看,LU 分解是一个极其重要的工具。由于它本质上是高斯消元法的改进版,它的复杂度是 O(n³),正如我们在之前(第 5.1.4 节)所看到的。

坏消息:LU 分解并不总是可用的。由于它与高斯消元法相关联,我们可以用类似的方式描述其存在性。回顾一下,高斯消元法要成功完成,要求主小行列式非零(定理 31)。这一要求同样适用于 LU 分解。

定理 34.(LU 分解的存在性)

设 A ∈ℝ^(n×n) 是一个任意的方阵,M[k] 是它的第 k 个主小行列式。如果对所有 k = 1, 2, …, n − 1,M[k] ≠ 0,则 A 可以写作:

A = LU, L, U ∈ ℝn×n,

其中 L 是下三角矩阵,U 是上三角矩阵。此外,L 的对角线元素均为 1。

关键点是:如果在算法中避免除以 0,一切都没问题。请注意,LU 算法不要求非零 M[n] = detA,也就是说,不需要是一个可逆矩阵!

经过所有这些准备后,我们终于可以付诸实践了!

5.2.1 实现 LU 分解

总结 LU 分解,它本质上是两个步骤的迭代:

  1. 计算输入的消元矩阵。

  2. 将输入乘以消元矩阵,将输出反馈到第一步。

计划很清晰:首先,我们写一个函数计算消元矩阵及其逆矩阵;然后,使用矩阵乘法迭代执行消元步骤。

import numpy as np 

def elimination_matrix( 
    A: np.ndarray, 
    step: int, 
): 
    #x0022;"/span> 
    Computes the step-th elimination matrix and its inverse. 

    Args: 
        A (np.ndarray): The matrix of shape (n, n) for which 
            the LU decomposition is being computed. 
        step (int): The current step of elimination, an integer 
            between 1 and n-1 

    Returns: 
        elim_mtx (np.ndarray): The step-th elimination matrix 
            of shape (n, n) 
        elim_mtx_inv (np.ndarray): The inverse of the 
            elimination matrix of shape (n, n) 
    #x0022;"/span> 

    n = A.shape[0] 
    elim_mtx = np.eye(n) 
    elim_mtx_inv = np.eye(n) 

    if 0 /span> step /span> n: 
        a = A[:, step-1]/A[step-1, step-1] 
        elim_mtx[step:, step-1] = -a[step:] 
        elim_mtx_inv[step:, step-1] = a[step:] 

    return elim_mtx, elim_mtx_inv

现在,我们准备执行消元步骤了。

def LU(A: np.ndarray): 
    #x0022;"/span> 
    Computes the LU factorization of a square matrix A. 

    Args: 
        A (np.ndarray): A square matrix of shape (n, n) to be factorized. 
            It must be non-singular (invertible) for the 
            decomposition to work. 

    Returns: 
        L (np.ndarray): A lower triangular matrix of shape (n, n) 
            with ones on the diagonal. 
        U (np.ndarray): An upper triangular matrix of shape (n, n). 
    #x0022;"/span> 

    n = A.shape[0] 
    L = np.eye(n) 
    U = np.copy(A) 

    for step in range(1, n): 
        elim_mtx, elim_mtx_inv = elimination_matrix(U, step=step) 
        U = np.matmul(elim_mtx, U) 
        L = np.matmul(L, elim_mtx_inv) 

    return L, U

让我们在一个小矩阵上测试我们的函数。

A = 10*np.random.rand(4, 4) - 5 
A
array([[-4.61990165, -3.97616553, -1.34258661,  0.50835913], 
      [-2.39491833, -2.3919011 , -1.3266581 ,  2.8658852 ], 
      [ 4.32658116,  0.43607725,  4.41630776, -4.46731714], 
      [-0.68329877,  4.76659965, -1.13602896, -2.12305592]])
L, U = LU(A) 

print(f/span>Lower:\n{L}\n\nUpper:\n{U}"
Lower: 
[[  1\.          0\.          0\.          0\.       ] 
 [  0.51839163   1\.          0\.          0\.       ] 
 [ -0.93650936   9.94174964   1\.          0\.       ] 
 [  0.14790331 -16.19246049  -1.18248523   1\.       ]] 

Upper: 
[[-4.61990165e+00 -3.97616553e+00 -1.34258661e+00  5.08359130e-01] 
 [ 0.00000000e+00 -3.30690182e-01 -6.30672445e-01  2.60235608e+00] 
 [ 0.00000000e+00  0.00000000e+00  9.42895038e+00 -2.98632067e+01] 
 [ 1.11022302e-16 -8.88178420e-16  1.77635684e-15  4.62750317e+00]]

结果正确吗?让我们通过将 L 和 U 相乘来测试,看看是否能还原回 A。

np.allclose(np.matmul(L, U), A)
True

总体而言,LU 分解是一种非常灵活的工具,在实现基本算法中作为一个重要的基础步骤。其中之一是计算逆矩阵,正如我们接下来将看到的。

5.2.2 逆矩阵的计算,真正的计算

到目前为止,我们已经谈论了很多关于逆矩阵的内容。我们从多个角度探讨了可逆性的问题,包括核与像、行列式和线性方程的可解性。

然而,我们还没有讨论实际计算逆矩阵的过程。通过 LU 分解,我们得到了一种可以用于此目的的工具。如何实现?通过将下三角矩阵代入高斯消元过程,我们可以将其逆矩阵作为副作用获得。因此,我们

  1. 计算 LU 分解 A = LU,

  2. 反转下三角矩阵 L 和 U^T,

  3. 使用恒等式(U[−1])T = (U[T])−1 来得到 U^(−1),

  4. 将 L^(−1)和 U^(−1)相乘,最终得到 A^(−1) = U(−1)L(−1)。

这就是计划!让我们从反转下三角矩阵开始。

设 L ∈ℝ^(n×n)是一个任意的下三角矩阵。按照与(5.7)相同的过程,我们可以得到

D = Gn −1...G1L,

其中 D,经过高斯消元后的最终结果,是一个对角矩阵

 ⌊ ⌋ |d1 0 ... 0 | | 0 d2 ... 0 | D = diag(d1,...,dn) = || . . . . ||, |⌈ .. .. .. .. |⌉ 0 0 ... d n

G[i]是由(5.5)、(5.6)等定义的消元矩阵。

由于D 的逆矩阵就是 − 1 −1 −1 D = diag(d1 ,...,dn ) ,我们可以将L −1 表示为

L −1 = D −1Gn− 1...G1.

我们可以非常类似地实现这一点,LU 分解的实现方法几乎相同;我们甚至可以重用我们的 elimination_matrix 函数。

def invert_lower_triangular_matrix(L: np.ndarray): 
    #x0022;"/span> 
    Computes the inverse of a lower triangular matrix. 

    Args: 
        L (np.ndarray): A square lower triangular matrix of shape (n, n). 
                        It must have non-zero diagonal elements for the 
                        inversion to succeed. 

    Returns: 
        np.ndarray: The inverse of the lower triangular matrix L, with 
                        shape (n, n). 
    #x0022;"/span> 
    n = L.shape[0] 
    G = np.eye(n) 
    D = np.copy(L) 

    for step in range(1, n): 
        elim_mtx, _ = elimination_matrix(D, step=step) 
        G = np.matmul(elim_mtx, G) 
        D = np.matmul(elim_mtx, D) 

    D_inv = np.eye(n)/np.diagonal(D)   # NumPy performs this operation elementwise 

    return np.matmul(D_inv, G)

完成这些后,我们就准备好反转任何矩阵(前提是矩阵确实可逆)。

我们几乎快到终点了。每个组件都已准备好,剩下的就是将它们组合起来。我们可以通过几行代码来完成这一切。

def invert(A: np.ndarray): 
    #x0022;"/span> 
    Computes the inverse of a square matrix using its LU decomposition. 

    Args: 
        A (np.ndarray): A square matrix of shape (n, n). The matrix must be 
                        non-singular (invertible) for the inversion to succeed. 

    Returns: 
        np.ndarray: The inverse of the input matrix A, with shape (n, n). 
    #x0022;"/span> 
    L, U = LU(A) 
    L_inv = invert_lower_triangular_matrix(L) 
    U_inv = invert_lower_triangular_matrix(U.T).T 
    return np.matmul(U_inv, L_inv)

Voilà!亲眼见证这个结果。

A = np.random.rand(3, 3) 
A_inv = invert(A) 

print(f/span>A:\n{A}\n\nA^{-1}:\n{A_inv}\n\nAA^{-1}:\n{np.matmul(A, A_inv)}"
A: 
[[0.17180745 0.79269571 0.36879642] 
 [0.37772174 0.94712553 0.55310582] 
 [0.93418085 0.38813821 0.51581695]] 

A^{-1}: 
[[  9.14230036  -8.87123133   2.97602074] 
 [ 10.74480189  -8.5427258    1.47801811] 
 [-24.64252111  22.49459369  -4.56327945]] 

AA^{-1}: 
[[ 1.00000000e+00 -1.37050081e-15  1.56962305e-17] 
 [-4.06841165e-16  1.00000000e+00 -4.50714074e-16] 
 [ 1.26848123e-14 -1.04564970e-14  1.00000000e+00]]

为了测试我们的反转函数的正确性,我们快速检查几种随机生成的矩阵结果。

for _ in range(1000): 
    n = np.random.randint(1, 10) 
    A = np.random.rand(n, n) 
    A_inv = invert(A) 
    if not np.allclose(np.matmul(A, A_inv), np.eye(n), atol=1e-5): 
        print("/span>Test failed."

由于上面没有错误消息,函数(可能)是正确的。

几章之前看似复杂抽象的内容,现在已掌握在我们手中。我们能够反转任何矩阵,不依赖内置函数,而是通过从零开始编写的函数来实现。我喜欢这种时刻,当所有的部分终于组合起来,一切都变得清晰明了。放松一下,欣赏一下带我们走到这里的过程吧!

5.2.3 如何实际反转矩阵

当然,我们的 LU 分解!矩阵反转实现远非最优。当处理 NumPy 数组时,我们可以依赖内置函数。在 NumPy 中,这是 np.linalg.inv。

A = np.random.rand(3, 3) 
A_inv = np.linalg.inv(A) 

print(f/span>A:\n{A}\n\nNumPy’s A^{-1}:\n{A_inv}\n\nAA^{-1}:\n{np.matmul(A, A_inv)}"
A: 
[[0.08503998 0.31186637 0.71032538] 
 [0.48973954 0.77358354 0.96303592] 
 [0.31250848 0.14359491 0.05593863]] 

NumPy’s A^{-1}: 
[[ 2.13348825 -1.89861153  5.59470678] 
 [-6.14268693  4.87769374 -5.97239945] 
 [ 3.84931433 -1.91423645  1.95236829]] 

AA^{-1}: 
[[ 1.00000000e+00 -1.86546922e-16  2.74435800e-16] 
 [-1.62367293e-16  1.00000000e+00  9.21871975e-17] 
 [-1.41854334e-18  3.64838601e-17  1.00000000e+00]]

让我们比较一下我们实现的运行时间与 NumPy 的运行时间。

from timeit import timeit 

n_runs = 100 
size = 100 
A = np.random.rand(size, size) 

t_inv = timeit(lambda: invert(A), number=n_runs) 
t_np_inv = timeit(lambda: np.linalg.inv(A), number=n_runs) 

print(f/span>Our invert:              \t{t_inv} s 
print(f/span>NumPy’s invert:          \t{t_np_inv} s 
print(f/span>Performance improvement: \t{t_inv/t_np_inv} times faster
Our invert:                    14.586225221995846 s 
NumPy’s invert:                0.46499890399718424 s 
Performance improvement:       31.368300218798304 times faster

一次巨大的改进。不错!(别忘了执行时间是 LU 分解!矩阵的逆运算是硬件相关的。)为什么 NumPy 会更快呢?有两个主要原因。首先,它直接调用了 LAPACK 中的 SGETRI 函数,这个函数极其快速。其次,根据它的文档(www.netlib.org/lapack/explore-html/da/d28/group__getri_gaa3bf1bb1432917f0e5fdf4c48bd6998c.html),SGETRI 使用了一个更快的算法:

#x0022;"/span> 
SGETRI computes the inverse of a matrix using the LU factorization 
computed by SGETRF. 

This method inverts U and then computes inv(A) by solving the system 
inv(A)*L = inv(U) for inv(A). 
 #x0022;"/span>

因此,NumPy 调用了 LAPACK 函数,而该函数又使用了 LU 分解。(我并不是特别擅长深入研究比我还要古老的 Fortran 代码,所以如果我在这里错了,请告诉我。不过,现代框架仍然调用这个古老的库,足以证明它的强大。永远不要低估像 LAPACK 和 Fortran 这样的老技术。)

我们刚刚学到的强大 LU 分解,还有其他应用吗?很高兴你问了!当然有;这就是数学的美妙之处。即使是最古老的工具,总能找到新的和意想不到的应用。这一次,我们终于会看到如何快速计算行列式!(当然也会有慢的计算方法。)

5.3 行列式的实际应用

在数学的理论与实践中,概念的发展通常有一个简单的流程。定义首先源于模糊的几何或代数直觉,最终凝练为数学形式化。

然而,数学定义常常忽略实际情况。你可得理解,这背后有非常好的理由!把实际考虑抛在一边使得我们能够有效地推理结构,这就是抽象的力量。最终,如果能找到有意义的应用,发展方向就会朝向计算问题,速度和效率便成为焦点。

神经网络本身就是这一点的典型。无论是从理论构想到如今可以在智能手机上运行的先进算法,机器学习研究遵循了这一同样的轨迹。

这也是我们在本书中从微观层面上所经历的事情。在许多其他例子中,想想行列式。我们将行列式定义为列向量的方向性以及由它们定义的平行六面体的体积。然而,我们实际上并没有真正进行过计算它们的实践。我们当然给出了一个或两个公式,但很难判断哪个公式最复杂,实际上它们都一样复杂。

另一方面,行列式的数学研究产生了大量有用的结果:线性变换的可逆性、高斯消元的特征化等等(甚至更多的结果还在后面)。

在这一节中,我们准备偿还我们的债务,并开发实际计算行列式的工具。像之前一样,我们将采用直接的方式,使用之前推导出的某个行列式公式。剧透一下:这远非最优方法,因此我们将找到一种更快速地计算行列式的方法。

5.3.1 两害相权取其轻

让我们回顾一下迄今为止我们关于行列式的了解(定义 20)。给定一个矩阵 A ∈ℝ^(n×n),它的行列式 detA 量化了线性变换 x → Ax 对体积的扭曲。也就是说,如果 e[1],…,e[n] 是标准的正交归一基,那么非正式地说,

det A = (orientation of Ae1, Ae2,...,Aen ) × (area of the parallelepiped determined by Ae1, Ae2,...,Aen ).

我们已经推导出了两种计算这个量的公式。最初,我们通过对所有排列求和来描述行列式:

det A = ∑ sign(σ)a ...a . σ(1),1 σ(n),n σ∈Sn

这很难理解,更不用说编程计算了。因此,推导出了一个递归公式,我们也可以使用。它指出:

 ∑n detA = (− 1)j+1a1,j detA1,j, j=1

其中 A[i,j] 是通过删除 A 的第 i 行和第 j 列得到的矩阵。你更倾向于使用哪一种方法?花几分钟思考一下你的推理。

不幸的是,这里没有正确的选择。使用排列公式时,必须首先找到生成所有排列的方法,然后计算它们的符号。此外,S[n] 中有 n! 种唯一排列,因此这个和包含了很多项。使用这个公式似乎非常困难,因此我们将采用递归版本。递归也有它的问题(我们很快就会看到),但从编码的角度来看,它比较容易处理。让我们开始吧!

5.3.2 递归方式

让我们来看一下这个公式:

 ∑n detA = (− 1)j+1a1,j det A1,j j=1

在我们的放大镜下。如果 A 是一个 n×n 矩阵,那么 A[1,j](通过删除 A 的第一行和第 j 列得到的矩阵)是一个 (n−1) × (n−1) 的矩阵。这是一个递归步骤。对于每个 n×n 的行列式,我们需要计算 n 个 (n−1) × (n−1) 的行列式,依此类推。

到最后,我们得到了大量的 1 × 1 行列式,这些可以很容易计算。因此,我们有了一个边界条件,有了它,我们准备将这些合并在一起。

def det(A: np.ndarray): 
    #x0022;"/span> 
    Recursively computes the determinant of a square matrix A. 

    Args: 
        A (np.ndarray): A square matrix of shape (n, n) for which the 
        determinant is to be calculated. 

    Returns: 
        float: The determinant of matrix A. 

    Raises: 
        ValueError: If A is not a square matrix. 
    #x0022;"/span> 

    n, m = A.shape 

    # making sure that A is a square matrix 
    if n != m: 
        raise ValueError("/span>A must be a square matrix." 

    if n == 1: 
        return A[0, 0] 

    else: 
        return sum([(-1)**j*A[0, j]*det(np.delete(A[1:], j, axis=1)) for j in range(n)])

让我们在一个小例子中测试一下 det 函数。对于 2 × 2 矩阵,我们可以轻松使用以下规则计算行列式:

 ⌊ ⌋ a b det⌈ ⌉ = ad − bc. c d

A = np.array([[1, 2], 
              [3, 4]]) 
det(A)    # should be -2
np.int64(-2)

结果似乎没问题。到目前为止,一切顺利。那问题出在哪里呢?递归。让我们计算一个小的 10 × 10 矩阵的行列式,并测量所需的时间。

from timeit import timeit 

A = np.random.rand(10, 10) 
t_det = timeit(lambda: det(A), number=1) 

print(f/span>The time it takes to compute the determinant of a 10 x 10 matrix: {t_det} seconds
The time it takes to compute the determinant of a 10 x 10 matrix: 
63.98369195000123 seconds

那真是漫长且令人无法忍受。对于这么简单的任务,感觉像过了一个世纪。

对于 n×n 的输入,我们递归地调用 det 函数 n 次,每次处理 (n−1) × (n−1) 的输入。也就是说,如果 a[n] 表示我们算法在 n×n 矩阵上的时间复杂度,那么由于递归步骤,我们有:

an = nan−1,

它爆炸得非常快。实际上,a[n] = O(n!),这就是令人害怕的阶乘复杂度。与其他一些递归算法不同,缓存也没有帮助。出现这种情况有两个原因:子矩阵很少匹配,而且 numpy.ndarray 对象是可变的,因此不能进行哈希处理。

实际上,n 可以达到数百万,所以这个公式完全没有用。我们该怎么办?很简单:LU 分解。

5.3.3 如何实际计算行列式

除了这两个公式外,我们还看到了许多矩阵和行列式的有用性质。我们能否将目前学到的知识应用到简化问题上呢?

让我们考虑 LU 分解。根据此分解,如果 detA ≠ 0,则 A = LU,其中 L 是下三角矩阵,U 是上三角矩阵。由于行列式在矩阵乘法下表现良好(参见公式(4.11)),我们有

detA = detL detU.

表面上看,我们把情况弄得更糟了:我们需要处理两个行列式,而不是一个。然而,L 和 U 是非常特殊的,因为它们是三角矩阵。事实证明,计算三角矩阵的行列式是非常简单的。我们只需要把对角线上的元素相乘!

定理 35(一个三角矩阵的行列式)

让 A ∈ ℝ^(n×n)是一个三角矩阵。(也就是说,它可以是下三角矩阵或上三角矩阵。)那么,

 n ∏ det A = aii. i=1

证明:假设 A 是下三角矩阵。(即,所有位于对角线上的元素都为 0。)根据 detA 的递归公式,我们得到

 ∑n detA = (− 1)j+1a1,j detA1,j. j=1

因为 A 是下三角矩阵,当 j > 1 时,a[1,j] = 0。所以,

detA = a11 detA1,1.

A[1,1] = (a[ij])[i,j=2]^n 也是下三角矩阵。通过迭代上一步,我们得到

detA = a11a22 ...ann,

这正是我们需要证明的。

如果 A 是上三角矩阵,则其转置 A^T 是下三角矩阵。因此,我们可以应用之前的结果,所以

detA = detAT = a11a22...ann

这个式子同样成立。

回到我们最初的问题。由于对角矩阵 L 的元素为常数 1(见(5.8)),这是 LU 分解所保证的,因此我们有

 ∏n det A = detU = uii. i=1

所以,计算行列式的算法非常简单:先获取 LU 分解,再计算 U 对角线元素的乘积。让我们实际操作一下!

def fast_det(A: np.ndarray): 
    #x0022;"/span> 
    Computes the determinant of a square matrix using LU decomposition. 

    Args: 
        A (np.ndarray): A square matrix of shape (n, n) for which the determinant 
                         needs to be computed. The matrix must be non-singular (invertible). 

    Returns: 
        float: The determinant of the matrix A. 
    #x0022;"/span> 
    L, U = LU(A) 
    return np.prod(np.diag(U))

是的,就是这么简单。让我们看看它的效果吧!

A = np.random.rand(10, 10) 

t_fast_det = timeit(lambda : fast_det(A), number=1) 
print(f/span>The time it takes to compute the determinant of a 10 x 10 matrix: {t_fast_det} seconds
The time it takes to compute the determinant of a 10 x 10 matrix: 
0.0008458310039713979 seconds

它的速度提高了一个巨大的幅度。到底提高了多少呢?

print(f/span>Recursive determinant:   \t{t_det} s 
print(f/span>LU determinant:          \t{t_fast_det} s 
print(f/span>Performance improvement: \t{t_det/t_fast_det} times faster
Recursive determinant:         63.98369195000123 s 
LU determinant:                0.0008458310039713979 s 
Performance improvement:       75645.95250065446 times faster

这真是一个疯狂的改进!如果我们使用更好的 LU 分解算法(例如 scipy.linalg.lu,它依赖于我们熟悉的 LAPACK),速度甚至可以更快。

仅仅看到这个结果,我就感动得不行。看看我们能用一点点线性代数做到多远?这就是为什么理解高斯消元法等基础知识至关重要的原因。机器学习和深度学习仍然是非常新的领域,尽管已经投入了大量的研究力量,但这样的时刻却时常发生。简单的想法往往会催生新的范式。

5.4 小结

在本章中,我们从线性方程组的角度研究了矩阵,即形式为

a11x1 + a12x2 + ⋅⋅⋅+ a1nxn = b1 a21x1 + a22x2 + ⋅⋅⋅+ a2nxn = b2 . .. an1x1 + an2x2 + ⋅⋅⋅+ annxn = bn.

不出所料,这些都可以用矩阵来描述,上述表达式等同于 Ax = b。解线性方程是一个古老的艺术,那么为什么我们在 AI 时代还要谈论它呢?

记住:如果你在和投资者谈论,那才叫 AI。从根本上说,这只是线性代数、微积分和概率论。

我们想要解线性方程,这将我们引向了高斯消元法(嗯,引导高斯到了高斯消元法)。这又引导我们到 LU 分解。然后引导我们到快速矩阵求逆,以及我们当前技术所建立的一系列其他创新。让我告诉你,快速的矩阵乘法和求逆是计算线性代数的基础,它们都源于那门古老的解线性方程的艺术。

让我们一一回顾一下在本章中取得的成就:

  • 通过高斯消元法求解线性方程,

  • 通过线性方程组来描述矩阵的可逆性,

  • 发现了一种矩阵分解技巧,称为 LU 分解,

  • 使用 LU 分解构建一个疯狂快速的矩阵求逆算法,

  • 并且使用——鼓声!——LU 分解,构建一个疯狂快速的行列式计算算法。

然而,我们与矩阵的关系还没有结束。回想一下我们之前建立的矩阵与线性变换之间的关系,将矩阵视为扭曲底层特征空间的数据变换。事实证明,如果从正确的角度来看,这种扭曲总是拉伸。嗯,几乎总是。嗯,几乎是拉伸。

好吧,那些“嗯”太多了,让我们在下一章中彻底清理这些内容,深入研究特征值和特征向量。出发吧!

5.5 问题

问题 1. 证明上三角矩阵的乘积是上三角矩阵。同理,证明下三角矩阵的乘积是下三角矩阵。(我们在本节中广泛使用了这些事实,但没有给出证明。所以,如果你还没有这么做,现在正是自己验证这些事实的好时机。)

问题 2. 编写一个函数,给定一个可逆的方阵 A ∈ℝ^(n×n) 和一个向量 b ∈ℝ^n,求解线性方程 Ax = b 的解。(如果你使用我们在这里构建的工具之一,可以通过一行代码完成。)

问题 3。 在我们结束本章之前,回到行列式的定义。尽管有很多理由反对使用行列式公式,但我们有一个支持它的理由:它是一个很好的练习,实现它会加深你的理解。因此,在这个问题中,你将构建

 ∑ detA = sign(σ)aσ(1)1 ...a σ(n)n, σ∈Sn

一步一步来。

(i) 实现一个函数,给定整数 n,返回集合{0,1,…,n − 1}的所有排列。将每个排列σ表示为一个列表。例如,

[2, 0, 1]

将表示排列σ,其中σ(0) = 2,σ(1) = 0,σ(2) = 1。

(ii) 让σ ∈S[n]是集合{0,1,…,n − 1}的一个排列。它的逆序数定义为

inversion(σ) = |{(i,j) : i <j and σ(i) >σ (j)}|,

其中 j ⋅j 表示集合中元素的数量。本质上,逆序描述了排列中反转一对数字顺序的次数。

结果,σ的符号可以写成

sign(σ) = (− 1)inversion(σ).

实现一个函数,首先计算逆序数,然后计算任意排列的符号。(排列如前一个问题中所示)

(iii) 将问题 1 和问题 2 的解法结合在一起,并使用排列公式计算矩阵的行列式。你认为这个算法的时间复杂度是多少?

加入我们的社区,参与 Discord 讨论

与其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过“问我任何问题”环节与作者交流,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

特征值和特征向量

到目前为止,我们已经看到了线性变换的三个方面:函数、矩阵和扭曲底层向量空间网格的变换。在欧几里得平面中,我们看到了一些示例(第 4.3 节),这些示例为它们的几何性质提供了一些启示。

延续这个思路,让我们考虑由矩阵给出的线性变换

L(U,V ) = {f : U → V | f 是线性映射}6.1

由于 A 的列是标准基向量 e[1] = (1,0)和 e[2] = (0,1)的像,我们可以通过图 6.1 来直观地理解 A 的作用。(如果你不记得这个事实,请查看第 4.1.1 节。)

这似乎是剪切、拉伸并旋转了整个网格。然而,有些特殊方向下,A 只是一个拉伸。例如,考虑向量 u[1] = (1,1)。通过简单的计算,你可以验证 Au[1] = 3u[1]。

由于线性性,这意味着如果一个向量 x 在 span(u[1])中,那么它在 A 作用下的像是 3x。

PIC

图 7.1:A 给出的线性变换下标准基向量的像

另一个是 u[2] = (−1,1),我们有 Au[2] = u[2]。因此,任何 x ∈ span(u[2])都会保持不变。

如果我们选择 u[1],u[2]作为基础,那么该变换的矩阵是

 ⌊ ⌋ Au ,u = ⌈3 0⌉ , 1 2 0 1

也就是说,A[u[1],u[2]]是对角化的。

PIC

图 7.2:A 给出的线性变换下 u[1] = (1,1)和 u[2] = (−1,1)的像

在实践中,我们喜欢对角矩阵,因为与对角矩阵的乘法要快得多,它只需要 O(n)次操作,相对于 O(n²)的操作复杂度。

这是一个普遍现象吗?这些有用吗?答案是对这两个问题都回答“是”。我们刚才看到的内容通过特征值和特征向量的概念得到了形式化。这个术语来源于德语单词“eigen”,意思是“自己的”,也因此导致了数学中最丑陋的命名约定之一。

定义 23.(特征值和特征向量)

设 f : V → V 为任意线性变换。如果 f(x) = λx 成立,那么标量λ和 x ∈V ∖{0}非零向量就是 f 的特征值-特征向量对。

第八章:6.1 矩阵的特征值

尽管我们已经正式定义了线性变换的特征值和特征向量,但我们通常在矩阵的上下文中讨论它们。(因为,正如我们所见,矩阵和线性变换是同一事物的两种不同表现形式。)让我们从将定义转换为矩阵语言开始。

如果 A ∈ℝ^(n×n)是一个矩阵,则定义 23 可转化为以下内容:标量λ和向量 x ∈ℝ ∖{0}是矩阵的特征值-特征向量对,如果

Ax = λx

(6.2)

这可以简化:线性变换 x→λx 对应的矩阵是λI,(6.2)等价于

(A − λI)x = 0

(6.3)

如果你回忆起第四章,第 4.1.1 节,我们在那一节中学到了矩阵是如何从线性变换中产生的,你可能会问:特征值不会依赖于矩阵的选择吗?

以下定理指出情况并非如此:线性变换及其矩阵的特征值是相同的。

定理 36.(相似矩阵的特征值)

设 A,B ∈ℝ^(n×n)是两个相似矩阵,即假设存在一个可逆矩阵 T ∈ℝ^(n×n),使得 B = T^(−1)AT。那么,如果对于某个标量λ和向量 x ∈ℝ^n,有 Ax = λx,

则:

 ′ ′ Bx = λx

对某些 x′∈ℝn 同样成立。

证明。让我们稍微调整一下特征值 (6.3)!我们有:

 −1 (A − λI)x = (A − λT T )x = T (T−1AT − λI)T −1x = 0.

由于 T 是可逆的,T[(T^(−1)AT −λI)T^(−1)x] = 0 只有在 (T^(−1)AT −λI)T^(−1)x = 0 时才成立。(回顾定理 20 中关于核与可逆性的关系。)这看起来几乎和 (6.3) 一样,只是稍微复杂了一点。让我用一些提示性的括号来突出相似之处:

 −1 − 1 [T AT − λI ][T x] = 0.

所以,使用选择 x^′ = T^(−1)x,我们有:

 −1 ′ ′ T AT x = λx ,

这就是我们需要证明的。

换句话说,相似矩阵的特征值是相同的。因此,我们可以谈论矩阵的特征值,而不仅仅是线性变换的特征值。上述定理意味着变换及其对应矩阵的特征值是相同的。而且,矩阵的特征值不依赖于基的选择。

更精确地说,假设 A : U →U 是一个线性变换,P,Q 是 U 的基。A 在某个基 S 中的矩阵表示为 A[Q]。我们知道,存在一个变换矩阵 T ∈ℝ^(n×n),使得:

 −1 AQ = T AP T.

所以,特征值是相同的。

上述所有内容都引出了一个问题:我们到底该如何实际找到特征值呢?接下来我们就来讨论这个问题。

6.2 查找特征值-特征向量对

尽管从几何解释来看,特征值-特征向量对的定义很容易理解,但它并没有为我们提供实际寻找它们的工具。利用它们来获得矩阵的简化表示是其中的一项应用,但如果没有方法来寻找它们,我们还是停留在原点。

首先,我们关注特征值。假设对于某个λ,存在一个非零向量 x 使得 Ax = λx。由 x →λx 定义的变换是线性的,其矩阵是对角矩阵:

 ⌊ λ 0 ... 0⌋ ⌊ x ⌋ | | | 1| || 0 λ ... 0|| || x2|| λx = || .. .. .. ..|| || .. || , ⌈ . . . .⌉ ⌈ . ⌉ 0 0 ... λ xn

其中对角线上是λ的矩阵就是λI,也就是λ倍的单位矩阵。

因为线性变换可以相加和相减(正如我们在第 4.1.2 节中看到的),所以定义方程 Ax = λx 等价于

(A − λI)x = 0,

其中 I 表示单位变换,如公式(4.3)所定义。换句话说,变换 A−λI 将一个非零向量映射到 0,这意味着它是不可逆的,正如定理 20 所暗示的那样。我们可以用行列式来表征这一点:我们需要找到所有λ,使得

det(A − λI ) = 0.

我们可以将上述结论总结为以下定理。

定理 37.

A : U → U 是一个任意的线性变换。那么λ 是其特征值,当且仅当

det(A − λI ) = 0.

虽然我们已经更进一步,但基于这个方法找到特征值仍然看起来很复杂。接下来,我们将看看 det(A−λI)到底是什么,以及我们如何在实际中找到 det(A −λI) = 0 的解。

在深入讨论一般情况之前,让我们回顾一下例子(6.1)。在这个例子中,我们有

 | | | | det(A − λI) = ||2 − λ 1 || || 1 2− λ|| = (2 − λ)2 − 1 = λ2 − 4λ + 3.

为了找到特征值,我们必须解这个二次方程

λ2 − 4λ + 3 = 0,

这是我们可以轻松做到的。回想一下,任何二次方程 ax² + bx + c = 0 的解是

 √ 2------- x1,2 = − b-±--b-−-4ac. 2a

应用这一点,我们得到λ[1] = 3 和 λ[2] = 1 作为解。没有其他解,因此 1 和 3 是 A 的唯一两个特征值。

让我们看看在一般情况下会发生什么!

6.2.1 特征多项式

正如上面的例子所示,如果底层向量空间 U 是 n 维的,也就是说 A 是一个 n ×n 的矩阵,那么 det(A −λI)是λ的 n 次多项式。

为了看清这一点,让我们显式地将 det(A−λI)写成矩阵的形式。考虑到这一点,我们得到

 | | |a − λ a ... a | ||11 12 1n || || a21 a22 − λ ... a2n || det(A − λI) = || .. .. .. .. ||. || . . . . || | an1 an2 ... ann − λ |

如果你考虑计算行列式的公式(4.12),你会发现每一项都是一个多项式。根据σ的固定点数(即σ(i) = i 的点有多少),这个多项式的次数介于 0 和 n 之间。

(或者,你可以通过使用递归公式(4.13)并应用归纳法,看到 det(A−λI)是一个 n 次的多项式。)

定义 24. (矩阵的特征多项式)

设 A ∈ℝ^(n×n)是任意矩阵。多项式

p(λ) = det(A − λI)

被称为 A 的特征多项式。

特征多项式的根就是特征值。如果 U 是一个 n 维复向量空间(即标量集是 ℂ),代数基本定理(定理 156)保证 det(A −λI) = 0 有正好 n 个根。

结果是,任何矩阵 A ∈ℂ^(n×n) 至少有一个特征值。注意,根可以具有更高的代数重数。例如,矩阵的特征多项式

 ⌊ ⌋ | 1 0 0| B = | 0 1 0| ⌈ ⌉ 0 0 2

为 (1 −λ)²(2 −λ)。所以,它的根是 1(代数重数为 2)和 2。

如果我们只局限于实矩阵和实向量空间,那么特征值和特征向量的存在并不能得到保证。例如,考虑

 ⌊ ⌋ 0 − 1 C = ||1 0 ||. ⌈ ⌉

它的特征多项式是 λ² + 1,它没有实数根,只有复数根:λ[1] = i 和 λ[2] = −i。从数学上讲,如果我们想在实向量空间的范围内停留,C 没有特征值。然而,我们现在是在做机器学习,而不是代数。因此,我们将稍微不精确一些,将实数矩阵视为复数矩阵。我们通常不需要复数来描述数据集的数学模型,但它们在矩阵分析过程中经常出现。

6.2.2 寻找特征向量

当一个特征值 λ 被识别时,我们可以开始寻找对应的特征向量;即,寻找向量 x,使得 (A−λI)x = 0。更精确地说,我们在寻找 ker(A −λI)。

正如我们在第 4.1.4 节中提到的,任何线性变换的核是一个子空间。由于它可能是多维的,因此确定它通常需要像 x[1] + x[2] = 0 这样的隐式描述。

让我们检查一下我们反复出现的例子

 ⌊ ⌋ 2 1 A = ⌈ ⌉ . 1 2

之前,我们已经看到 λ[1] = 3 和 λ[2] = 1 是特征值。为了识别对应的特征向量,例如 λ[1],我们必须找到线性方程 (A −λ[1]I)x = 0 的所有解。展开后,我们有

− x1 + x2 = 0 x1 − x2 = 0.

两个方程意味着所有 x = (x[1],x[2]) 是解,其中 x[1] = x[2]。

6.3 特征向量、特征空间及其基

定义 25.(特征空间)

设 f : V →V 是一个任意的线性变换,λ 是它的特征值。由此定义的特征向量子空间

Uλ = {x : Ax = λx}

称为 λ 的特征空间。

特征空间在理解线性变换的结构中起着重要作用。首先,我们注意到线性变换保持其特征空间不变。(也就是说,如果 x 属于 U[λ] 特征空间,那么 f(x) 也属于 U[λ]。)这一性质使我们能够将线性变换限制在其特征空间内。

为了说明特征空间的概念,让我们回顾一下已经熟悉的矩阵

 ⌊ ⌋ 2 1 A = ⌈ ⌉ 1 2

再次考虑。它的特征值为λ[1] = 3 和λ[2] = 1,通过解方程(A −λ[1]I)x = 0,我们得到λ[1]的特征空间是

U = {x ∈ ℝ2 : x = x }. λ1 1 2

同样,你可以检查 U[λ[2]] = {x ∈ ℝ² : x[1] = −x[2]}。(如果你回到图 6.2,可以直观地看到 U[λ[1]]和 U[λ[2]]。)

特征空间不一定是一维的。例如,考虑前面提到的一个例子

 ⌊ ⌋ 1 0 0 || || B = ⌈0 1 0⌉ , 0 0 2

具有两个特征值λ[1] = 1 和λ[2] = 2。将λ[1]代入方程并解方程(B −I)x = 0,得到

U = {x ∈ ℝ3 : x = 0}, λ1 3

这只是由前两个坐标轴确定的平面。

特征空间的结构决定了我们是否可以通过基变换对矩阵 A 进行对角化(第 4.2 节)。以下的定理建立了这种联系。

定理 38。(对角化与特征空间)

设 f : V → V 为线性变换,A ∈ ℝ^(n×n)是其在某个基下的矩阵,U[λ1,…,U]{λ[k]}是 f 的特征空间。以下是等价的。

(a)存在一个矩阵 T ∈ ℝ^(n×n),使得

 −1 Λ = T AT,

其中Λ是一个对角矩阵。

(b)存在一个基 u[1],…,u[n],它可以从 f 的特征向量中选取。

(c)V 可以表示为特征空间的直和,即,

V = Uλ1 + ⋅⋅⋅+ Uλk.

(注意,k,即特征空间的数量,不一定等于 n。)

证明。(a)⇒(b)。如果A 是某个基下的f 矩阵,那么相似变换等同于基的变化。

也就是说,新的矩阵Λ = T^(−1)AT 是 f 在不同基下的矩阵,例如 u[1],…,u[n]。

如果Λ是对角矩阵,它可以写成以下形式

 ⌊ ⌋ |λ1 0 ... 0 | || 0 λ2 ... 0 || Λ = | . . . .| . |⌈ .. .. .. ..|⌉ 0 0 ... λ n

(注意,λ[i] 不一定彼此不同。)因此,Λu[i] = λ[i]u[i],这意味着 u[1],…,u[n]是 f 的特征向量所组成的基。

(b)⇒(a)。如果 u[1],…,u[n]是 f 的特征向量所组成的基,那么在该基下它的矩阵Λ是对角的。因此,A 与Λ相似,这就是我们需要证明的。

(b)⇒(c)。根据定义,特征空间的直和(定义 6)包含所有形式的线性组合

 n x = ∑ xu . i i i=1

由于 u[1],…,u[n]是一个基,V = U[λ[1]] + ⋅⋅⋅ + U[λ[k]]成立。

(c)⇒(b)。从每个特征空间 U[λ[i]]中,我们可以选择一个基。由于 U[λ[i]]的构造,其基将由特征向量组成。

由于 V = U[λ[1]] + ⋅⋅⋅ + U[λ[k]],这些基 u[1],…,u[n]的并集将是 V 的基。

尽管这个定理没有给我们任何有用的矩阵对角化的具体方法,但它为我们提供了一个极为宝贵的见解:对角化等价于找到一个特征向量基。并非总是可能做到这一点,但一旦能够实现,我们就可以大展拳脚了。

在下一章,我们将深入探讨这一主题,提供多种简化矩阵的方法。如果我们的线性代数之旅像登山一样,我们很快就会到达巅峰。

6.4 小结

在这一章中,我们再次进入了数学的理论领域。这一次,我们探讨了矩阵的特征值和特征向量,即标量 λ 和向量 x,其中

 n×n Ax = λx, A ∈ ℝ

保持。

就像大多数数学对象一样,这一概念开始可能让人感到畏惧,但从几何角度来看,这意味着在线性变换 A 的方向 x 上,A 就是对 λ 的拉伸。实际上,我们可以通过解所谓的特征方程来找到特征向量。

det(A − λI) = 0

对于 λ。

特征值有什么用?应用非常广泛,但有一个尤为突出:根据定理 38,如果你能通过矩阵 A ∈ℝ^(n×n) 的特征向量构建一个基,那么你可以找到一个 T ∈ℝ^(n×n),使得 T^(−1)AT 是对角矩阵。这个过程非常有用。首先,与对角矩阵的乘法运算快速且简单,我们在可能的情况下都倾向于使用它。其次,对角化揭示了关于底层线性变换的许多内部结构。

我们在这一章结束时提出了许多问题。我们如何找到特征值?哪些矩阵是可对角化的?如果一个矩阵是可对角化的,我们该如何找到这样的形式?

我们将在下一章回答所有这些问题。请注意:我们正接近线性代数的巅峰。下一章可能是我们最难的一章,就像攀登珠穆朗玛峰的最后一段路程一样。然而,你完全可以相信我。如果你能来到这里,你一定能征服它。

加油!

6.5 问题

问题 1. 计算矩阵的特征值

 ⌊ ⌋ ⌊ ⌋ | 4 1 − 1| | 2 1 1| A = |⌈ 1 3 1 |⌉ , B = |⌈ 1 2 1|⌉, − 1 1 2 1 1 2

并为每个特征值找到一个特征向量。

问题 2. 设 A ∈ℝ^(n×n) 为上三角矩阵或下三角矩阵。证明 A 的特征值是其对角线元素。

问题 3. 设 A ∈ℝ^(n×n) 为一个方阵。证明

det(A − λI )

是一个关于 λ 的 n 次多项式。

这是我们之前谈到的特征多项式,我们甚至提到过这个事实。不过,我们省略了证明,所以这是你填补空白的机会。

问题 4. 设 A ∈ℝ^(n×n)、B ∈ℝ^(n×m),C ∈ℝ^(m×m) 为任意矩阵,我们定义所谓的块矩阵

 ⌊ ⌋ ⌈A B ⌉ (n+m )× (n+m ) D = 0 C ∈ ℝ .

证明如果 λ 是 A 或 B 的特征值,那么它也是 C 的特征值。

加入我们的 Discord 社区

与其他读者、机器学习专家以及作者本人一起阅读本书。提出问题,提供解决方案,参与与作者的“问我任何问题”环节,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

矩阵分解

本书中的一个反复出现的观点是,问题解决的关键在于找到你研究对象的最佳表示。 比如,向量空间的线性变换由矩阵表示。 研究一个就等同于研究另一个,但每种视角都有其独特的工具。 线性变换是几何的,而矩阵则是代数的,二者是同一枚硬币的两面。

这个思路也可以应用于更小的范围。 回想一下第六章中的 LU 分解。 你可以将其视为矩阵的另一种视角。

猜猜看:这不是唯一的。 本章将重点介绍三个最重要的分解方法:

  • 光谱分解,

  • 奇异值分解,

  • 以及 QR 分解。

系好安全带。 这是我们迄今为止最具挑战性的冒险。

第九章:7.1 特殊变换

到目前为止,我们一直在努力发展线性代数的几何视角。 向量是通过其方向和大小来定义的数学对象。在向量空间中,距离和正交性的概念衍生出了几何结构。

线性变换是机器学习的构建模块,它们只是扭曲结构的映射:旋转、拉伸和扭曲几何形状。 然而,也有一些变换类型能够保持部分结构。 实际上,这些变换提供了宝贵的见解,此外,它们也更容易处理。 在本节中,我们将重点介绍最重要的变换类型,那些我们在机器学习中会遇到的变换。

7.1.1 伴随变换

在机器学习中,最重要的空间是欧几里得空间 ℝ^n。 这是数据表示和操作的地方。 在那里,整个几何结构由内积定义。

 ∑n ⟨x,y ⟩ = xiyi, i=1

产生了大小、方向(以角度的形式)和正交性的概念。 因此,可以与内积相关的变换是特殊的。 例如,如果对所有 x ∈ℝ^n 和线性变换 f : ℝ^n →ℝ^n,满足 ⟨f(x), f(x)⟩ = ⟨x, x⟩,我们知道 f 保持范数不变。 也就是说,原始和变换后的特征空间中的距离具有相同的意义。

首先,我们将建立一个关于变换下向量映像与它们内积之间的普遍关系。 这将成为本章讨论的基础。

定理 39.(伴随变换)

设 f : ℝ^n →ℝ^n 是一个线性变换。 那么,存在一个线性变换 f^∗ : ℝ^n →ℝ^n,使得

⟨f(x), y⟩ = ⟨x, f^*(y)⟩

(7.1)

对所有 x,y ∈ℝ^n 都成立。 f^∗ 被称为 f 的伴随变换*。

此外,如果 A ∈ℝ^(n×n) 是 f 在标准正交基下的矩阵,那么 f^∗ 的矩阵就是 A^T。 即,

⟨Ax, y⟩ = ⟨x, A^Ty⟩。

(7.2)

证明。假设 A ∈ ℝ^(n×n) 是 f 在标准正交基中的矩阵。对于任何 x = (x[1],…,x[n]) 和 y = (y[1],…,y[n]),内积定义为

 ∑n ⟨x,y ⟩ = xiyi, i=1

Ax 可以写成

⌊ ⌋ |a11 a12 ... a1n |⌊ ⌋ ⌊ ∑n ⌋ |a a ... a || x1| | ∑ j=1a1jxj| || 21. 2.2 . 2n. |||| x2|| || nj=1a2jxj|| || .. .. .. .. |||| ..|| = || .. || . || ||⌈ .⌉ ⌈ . ⌉ ⌈an1 an2 ... ann ⌉ xn ∑n anjxj j=1

使用这个形式,我们可以将 ⟨Ax,y⟩ 表示为 a[ij]、x[i] 和 y[i] 的形式。为此,我们有

 ∑n ∑n ⟨Ax, y⟩ = ( aijxj)yi i=1 j=1 ∑n ∑n = ( aijyi) xj j=1 i=1 ◟--◝◜---◞ T j- th component of A y = ⟨x,AT y⟩.

这表明由 f^∗ : x→A^T x 给出的变换满足 (7.1) 和 (7.2),这正是我们需要证明的。

为什么内积 ⟨Ax,y⟩ 对我们来说如此重要?因为内积定义了一个向量空间的几何结构。回想方程 (2.12),它允许我们仅通过与正交基的内积来完全描述任何向量。此外,⟨x,x⟩ = ∥x∥² 定义了距离和大小的概念。正因为如此,(7.1) 和 (7.2) 对我们来说将是非常有用的。

正如我们即将看到的,保持内积的变换是非常特殊的,这些关系为我们提供了代数和几何上描述它们的方式。

7.1.2 正交变换

让我们直接进入定义。

定义 26. (正交变换)

设 f : ℝ^n → ℝ^n 是一个任意的线性变换。如果 f 是正交的,那么

⟨f(x),f(y)⟩ = ⟨x,y ⟩

对所有的 x, y ∈ ℝ^n 都成立。

因此,正交变换 f 保持范数:∥f(x)∥² = ⟨f(x),f(x)⟩ = ⟨x,x⟩ = ∥x∥²。由于两个向量之间的夹角由它们的内积定义,见方程 (2.9),性质 ⟨f(x),f(y)⟩ = ⟨x,y⟩ 意味着正交变换也保持角度。

我们也可以将定义转换为矩阵的语言。在实际应用中,我们总是会使用矩阵,因此这个表述是至关重要的。

定理 40. (正交变换的矩阵)

设 f : ℝ^n → ℝ^n 是线性变换,A ∈ ℝ^(n×n) 是 f 在标准正交基中的矩阵。那么,f 是正交的,当且仅当,A^T = A^(−1)。

证明。像往常一样,我们需要在两个方向上都证明这个蕴含。

(a) 假设 f 是正交的。那么,(7.2) 给出了

 T ⟨x, y⟩ = ⟨Ax, Ay ⟩ = ⟨x, A Ay ⟩.

因此,对于任何给定的 y,

 T ⟨x,(A A − I)y ⟩ = 0

对所有的 x 都成立。通过让 x = (A^T A −I)y,内积的正定性意味着 (A^T A −I)y = 0 对所有 y 成立。因此,A^T A = I,这意味着 A^T 是 A 的逆矩阵。

(b) 如果 A^T = A^(−1),我们有

⟨Ax, Ay ⟩ = ⟨x,AT Ay ⟩ −1 = ⟨x,A Ay ⟩ = ⟨x,y ⟩,

显示 f 是正交的。

A^T = A^(−1) 这一事实对 A 的列有着深远的影响。如果你回想一下第 4.1.2 节中矩阵乘法的定义,AB 中第 i 行第 j 列的元素就是 A 的第 i 行与 B 的第 j 列的内积。

更精确地说,若第 i 列记作 a[i] = (a[1,i],a[2,i],…,a[n,i]),则我们有

AT A = (⟨ai,aj⟩)n = I, i,j=1

也就是说,

 ( |{ ⟨a ,a ⟩ = 1 如果 i = j, i j |( 其他情况为 0.

换句话说,A 的列构成了一个正交归一系统。这个事实应该不让人感到意外,因为正交变换保持了大小和正交性,而 A 的列是标准正交基底 e[1],…,e[n] 的像。

在机器学习中,对特征进行正交变换相当于从另一个角度看待它们,而不会产生失真。你可能已经知道,但这正是主成分分析(PCA)所做的事情。

7.2 自伴随变换与谱分解定理

除了正交变换,还有另一类重要的变换:其伴随矩阵就是它本身。稍微忍耐一下,我们很快就会看到一个例子。

定义 27.(自伴随变换)

设 f : ℝ^n →ℝ^n 是一个线性变换。如果 f 是自伴随的,则有 f^∗ = f,也就是说,

⟨f(x), y⟩ = ⟨x, f(y)⟩

(7.3)

对所有 x,y ∈ℝ^n 都成立。

一如既往,我们将这转化为矩阵的语言。如果 A 是 f 在标准正交基底下的矩阵,我们知道 A^T 是其伴随矩阵。对于自伴随变换,意味着 A^T = A。像这样的矩阵称为对称矩阵,它们具有许多良好的性质。

对我们而言,最重要的一点是对称矩阵可以对角化!(也就是说,它们可以被转换成一个对角矩阵,并有检查参考)以下定理精确地说明了这一点。

定理 41.(实对称矩阵的谱分解)

设 A ∈ ℝ^(n×n) 是一个实对称矩阵。那么,A 恰好有 n 个实特征值 λ[1] ≥⋅⋅⋅≥λ[n],并且可以选择对应的特征向量 u[1],…,u[n],使它们构成一个正交归一基底。

此外,如果我们令 Λ = diag(λ[1],…,λ[n]) 且 U 是一个正交矩阵,其列是 u[1],…,u[n],则

A = UΛU^T

(7.4)

成立。

注意,特征值 λ[1] ≥⋅⋅⋅≥λ[n] 不一定彼此不同。

证明。(大致思路)由于证明过程相当复杂,我们最好先了解其中的主要思路,而不必深入所有的数学细节。

主要步骤如下。

  1. 如果矩阵 A 是对称的,则它的所有特征值都是实数。

  2. 利用这一点,可以证明可以从 A 的特征向量中形成一个正交归一基底。

  3. 在这个正交基下将变换 x → Ax 的矩阵写成对角矩阵。因此,基变换得到了 (7.4)。

显示特征值为实数需要一些复数魔法(这超出了本章的范围)。困难的部分是第二步。一旦完成这一步,移动到第三步就很直接了,就像我们在讨论特征空间及其基向量时看到的那样(第 6.3 节)。

我们仍然没有一种实际的方法来对角化矩阵,但这个定理让我们更接近一步:至少我们知道对称矩阵的对角化是可能的。这是一个重要的步骤,因为我们可以将一般情况简化到对称情况。

矩阵对称的要求看起来是一个非常特殊的条件。然而,在实践中,我们可以通过几种不同的方式使矩阵对称化。对于任意矩阵 A ∈ℝ^(n×m),乘积 AA^T 和 A^T A 将是对称的。对于方阵,平均值 A+AT- 2 也适用。因此,对称矩阵比你想象的更常见。

正交矩阵 U 和相应的正交基 {u[1],…,u[n]},对称矩阵 A 的对角化具有一个非常重要的特性,在机器学习中进行数据样本的主成分分析时非常重要。

在看下一个定理之前,我们引入 argmax 符号。回想一下,表达式 max[x∈A]f(x) 表示函数在集合 A 上的最大值。通常,我们想知道这个最大值取到的位置,这由 argmax 定义:

 ∗ x = argmaxx ∈Af (x), f(x∗) = max f(x). x∈A

现在,让我们看看 PCA 的基础!

定理 42。

设 A ∈ ℝ^(n×n) 是一个实对称矩阵,λ[1] ≥ ⋅⋅⋅ ≥ λ[n] 是它的实特征值按降序排列。此外,设 U ∈ ℝ^(n×n) 是对角化 A 的正交矩阵,对应的正交基为 {u[1],…,u[n]}。

然后,

 T arg m∥axx∥=1 x Ax = u1,

 max xT Ax = λ1. ∥x∥=1

证明。由于 {u[1],…,u[n]} 是正交基,任何 x 都可以表示为它们的线性组合:

 n ∑ x = xiui, xi ∈ ℝ. i=1

因此,由于 u[i] 是 A 的特征向量,

 ∑n ∑n ∑n Ax = A( xiui) = xiAui = xiλiui. i=1 i=1 i=1

将其代入 x^T Ax,我们有

 T ∑n T ∑n x Ax = ( xjuj) A ( xiui) j=1 i=1 ∑n ∑n = ( xjuTj )A ( xiui) j=1 i=1 ∑n ∑n = ( xjuTj )( xiλiui) j=1 i=1  ∑n T = xixjλiuj ui. i,j=1

由于 u[i] 形成正交基,

 (| T {1 if i = j, uj ui = ⟨ui,uj⟩ = | (0 otherwise.

换句话说,当 i≠j 时,u[j]^T u[i] 为零。继续以上计算时,考虑到这一观察结果,

 时,和  是特征值  的加权平均。因此,

从中可以得出 x^T Ax ≤ λ[1]。(回忆一下,我们可以在不失一般性的情况下假设特征值是递减的。)另一方面,通过代入 x = u[1],我们可以看到 u[1]^T Au[1] = λ[1],因此最大值确实达到了。从这两点,定理得以证明。

注释 6\。

换句话说,定理 42 表明,函数 x→x^T Ax 在 u[1]处取得最大值,并且该最大值为 u[1]^T Au[1] = λ[1]。x^T Ax 这个量看起来也相当神秘,因此让我们稍作澄清。如果我们从特征角度来看,向量 u[1],…,u[n]可以看作是“旧”特征 e[1],…,e[n]的组合。当我们有实际观察数据(即数据)时,可以使用上述过程对协方差矩阵进行对角化。所以,如果 A 表示这个协方差矩阵,u[1]^T Au[1]就是新特征 u[1]的方差。

因此,定理表明 u[1]是唯一能够最大化方差的特征。因此,在所有可能的新特征选择中,u[1]传达了关于数据的最多信息。

此时,我们没有所有工具来看到,但在主成分分析中,这表明第一个主向量是最大化方差的那个。

定理 42 只是以下一般定理的一个特例。

定理 43\。

设 A ∈ ℝ^(n×n)是一个实对称矩阵,λ[1] ≥ ⋅⋅⋅ ≥ λ[n]是其按降序排列的实特征值。此外,设 U ∈ ℝ^(n×n)是对角化 A 的正交矩阵,对应的正交规范基为{u[1],…,u[n]}。

然后,对于所有 k = 1,…,n,我们有

u = argmax{xT Ax : ∥x∥ = 1,x ⊥ {u ,...,u }}, k 1 k−1

λ max {xT Ax : ∥x∥ = 1,x ⊥ {u ,...,u }}. k 1 k− 1

(有时,当条件过于复杂时,我们写 max{f(x) : x ∈A}来代替 max[x∈A]f(x)。)

证明。证明与之前的几乎相同。由于 x 需要与 u[1],…,u[k−1]正交,它可以表示为

 n ∑ x = xiui. i=k

根据前一条定理证明中的计算,我们得出

 ∑n xTAx = x2iλi ≤ λk. i=k

另一方面,与之前类似,u[k]^T Au[k] = λ[k],因此定理得以证明。

7.3 奇异值分解

所以,我们可以通过正交变换对任何实对称矩阵进行对角化。这很好,但如果我们的矩阵不是对称的呢?毕竟,这是一个相当特殊的情况。

我们如何对一般矩阵做同样的操作呢?我们将使用一个非常强大的工具,直接来自数学家的工具箱:异想天开。我们假装有解决方案,然后反向推导。具体来说,设 A ∈ ℝ^(n×m)是任何实矩阵。(它可能不是方阵。)由于 A 不是对称的,我们必须放宽对将其因式分解为 UΛU^T 形式的要求。最直接的方法是假设左侧和右侧的正交矩阵不是彼此的转置。

因此,我们正在寻找正交矩阵 U ∈ℝ^(n×n) 和 V ∈ℝ^(m×m),使得

A = U ΣV T

对于某些对角矩阵 Σ ∈ℝ^(n×m),成立。(非方阵矩阵 Σ = (σ[i,j])[i,j=1]^(n,m) 是对角矩阵,当 i≠j 时,σ[i,j] 为 0。)

你可能会好奇为何符号从 Λ 变为 Σ。这是因为 Σ 不一定包含特征值,而是包含奇异值。我们很快会解释。

下面是逆向工程部分。首先,正如我们之前讨论的,AA^T 和 A^T A 是对称矩阵。其次,我们可以利用 U 和 V 的正交性来简化它们,得到

 T T T AA = (UΣV )(VΣU ) 2 T = U Σ U .

类似地,我们有 A^T A = V Σ²V ^T。好消息是:我们实际上可以通过将谱分解定理(定理 41)应用于 AA^T 和 A^T A 来找到 U 和 V。因此,分解 A = UΣV ^T 是有效的!这种形式被称为奇异值分解(SVD),是线性代数的巅峰成就之一。

当然,我们还没有完成;我们只是知道该从哪里开始。让我们把这个数学上精确化!

定理 44. (奇异值分解)

设 A ∈ℝ^(n×m) 为任意矩阵。那么,存在对角矩阵 Σ ∈ℝ^(n×m) 和正交矩阵 U ∈ℝ^(n×n) 和 V ∈ℝ^(m×m),使得

A = U ΣV T.

什么是非方阵对角矩阵?让我给你两个例子,你立刻就能理解要点:

⌊ ⌋ ⌊ ⌋ 1 0 1 0 0 ||0 2|| , ||0 2 0|| . ⌈ ⌉ ⌈ ⌉ 0 0

我们总是可以将矩形矩阵 M ∈ℝ^(n×m) 写成以下形式

 ⌊ ⌋ M1 m×m (n−m)×m M = ⌈ ⌉ , M1 ∈ ℝ , M2 ∈ ℝ M2

如果 m/span>n,并且

 [ ] n×n n×(m−n) M = M1 M2 , M1 ∈ ℝ , M2 ∈ ℝ

否则,现在,让我们来看一下奇异值分解的证明!

证明。(概要。)为了说明证明的主要思想,我们假设 1)A 是方阵,2)A 是可逆的;即,0 不是 A 的特征值,

由于 A^T A ∈ ℝ^(m×m) 是实对称矩阵,我们可以应用谱分解定理,得到对角矩阵 Σ ∈ ℝ^(m×m) 和正交矩阵 V ∈ℝ^(m×m),使得

 T 2 T A A = V Σ V

成立。(回忆一下,对称矩阵的特征值是非负的,因此我们可以将 A^T A 的特征值写成 Σ² 的形式。)

由于 A 可逆,A^T A 也可逆;因此,0 不是 A^T A 的特征值。结果,ΣA^(−1) 是良好定义的。现在,通过定义 U := AV ΣA^(−1),V 的正交性表明

 T −1 T U ΣV = (AV Σ )ΣV T = AV V = A,

因此,我们几乎完成了。剩下要证明的是 U 确实是正交的,即 U^T U = I。我们开始:

U TU = (AV Σ− 1)TAV Σ−1 −1 T T −1 = Σ V A◟◝◜A◞ V Σ =VΣ2VT = Σ−1(V TV )Σ2(V TV)Σ −1 = Σ−1Σ2 Σ− 1 = I,

这样,我们得到了平方且可逆 A 的奇异值分解特例。

为了保持复杂度可控,我们不会详细推导剩下的部分;我将把它留给你作为练习。到现在为止,你已经掌握了所有工具。

让我们稍作停留,欣赏一下奇异值分解的威力。U 和 V 的列是正交矩阵,这是相当特殊的变换。因为它们保持了内积和范数不变,所以保持了底层向量空间的结构。对角矩阵 Σ 也是特殊的,它只是沿着基方向的拉伸。令人惊讶的是,任何线性变换都是这三种特殊变换的组合。

除了揭示线性变换的精细结构外,SVD 还提供了更多功能。例如,它推广了特征向量的概念,而特征向量仅定义于方阵。通过这个,我们有了

AV = U Σ,

我们可以按列查看这个矩阵。这里,Σ 是对角矩阵,但其元素的数量取决于 n 或 m 中较小的那个。

所以,如果 u[i] 是 U 的第 i 列,v[i] 是 V 的第 i 列,那么身份式 AV = UΣ 就可以转化为

Av = σ u , 0 ≤ i ≤ min (n,m ). i ii

这与特征值-特征向量对的定义非常相似,不同的是这里我们有两个向量,而不是一个。u[i] 和 v[i] 被称为所谓的左奇异向量和右奇异向量,而标量 σ[i] = √λ- i 被称为奇异值。

总结来说,正交变换给出了奇异值分解,但这就是全部吗?还有其他特殊的变换和矩阵分解吗?当然有。

引入正交投影。

7.4 正交投影

线性变换本质上是数据的操作,揭示了其他(希望是更有用的)表示。直观地,我们将它们看作一一映射,忠实地保留输入的所有“信息”。

这通常不是这样,以至于有时候数据的有损压缩是非常有益的。举个具体的例子,考虑一个包含一百万个特征的数据集,其中只有几百个是有用的。我们可以做的是识别出重要的特征并丢弃其余的,得到一个更紧凑的表示,便于操作。

这个概念通过正交投影的定义被形式化。我们在首次接触内积时就遇到了它们(见(2.7))。

投影在格拉姆-施密特过程(定理 13)中也起着基础作用,用于将任意基向量正交化。由于我们已经对正交投影有一定的了解,因此需要给出正式的定义。

定义 28. (投影与正交投影)

设 V 为一个任意的内积空间,P : V → V 是一个线性变换。如果 P² = P,那么 P 就是一个投影。

如果投影 P 是正交的,那么子空间 kerP 和 imP 是彼此正交的。(也就是说,对于 kerP 中的每个 x 和 imP 中的每个 y,我们有 ⟨x,y⟩ = 0。)

让我们重新审视迄今为止见过的例子,以便理解其定义!

例 1. 最简单的是到单个向量的正交投影。也就是说,如果 u ∈ℝ^n 是任意向量,则变换

proju(x) = ⟨x,u⟩-u ⟨u,u ⟩

是 (由) u 的正交投影。(在讨论内积的几何解释时,我们谈到过这一点,见第 2.2.3 节。)反复应用这一变换,我们得到

 ⟨x,u⟩ ⟨⟨u,u⟩u,-u⟩ proju(proju (x )) = ⟨u,u ⟩ u ⟨x,u⟩ -⟨u,u⟩⟨u,-u⟩ = ⟨u,u ⟩ u = ⟨x,u-⟩u ⟨u,u ⟩ = proj (x). u

因此,忠实于其名字,proju 确实是一个投影。为了看到它是正交的,让我们检查其核和图像!由于 proju(x ) 的值是 u 的标量倍,其图像是

im (proj) = span(u). u

其核心,被 proj[u] 映射到 0 的向量集合,也很容易找到,因为 ⟨⟨xu,,uu⟩⟩u = 0 只有当 ⟨x,u⟩ = 0 时才会发生,即 x ⊥u。

换句话说,

ker(proju) = span (u)⊥,

其中 span(u)^⊥ 表示 span(u) 的正交补(定义 13)。这意味着 proj[u] 确实是一个正交投影。

我们还可以用矩阵描述 proju(x) 。通过按分量写出 proju (x ) ,我们有

 ⌊ ⌋ | ⟨x,u⟩u1| ⟨x,u⟩ 1 | ⟨x,u⟩u2| proju(x) = -----u = ----2|| . || , ⟨u, u⟩ ∥u ∥ |⌈ .. |⌉ ⟨x,u⟩u n

其中 u = (u[1],…,u[n])。这看起来像某种矩阵乘法!正如我们之前看到的,将矩阵和向量相乘可以用行向量点积来描述。(见 (3.3)。)

因此,根据这种矩阵乘法的解释,我们有

L(U,V ) = {f : U → V | f is linear}(7.5)

注意,与 ∥u∥² 的比例可以通过将其写成 “矩阵” 乘积中的一部分来整合。

 T T uu---= -u--⋅ u--, ∥u∥2 ∥u ∥ ∥u∥

矩阵 uu^T ∈ℝ^(n×n),由向量 u ∈ℝ^(n(×1)) 和其转置 u^T ∈ℝ^(1×n) 的乘积得到,是一个非常特殊的矩阵。它们被称为秩-1 投影矩阵,并且在数学中经常出现。

(一般来说,矩阵 uv^T 被称为向量 u 和 v 的外积。我们不会广泛使用它,但它在线性代数中经常出现。)

示例 2\。如我们在引入格拉姆-施密特正交化过程时所看到的(定理 13),前面的例子可以通过对多个向量进行投影来推广。如果 ( u[1], \dots, u[k] \in \mathbb{R}^n ) 是一组线性无关且两两正交的向量,则线性变换

 ∑k ⟨x, ui⟩ proju1,...,uk(x) = -------ui i=1 ⟨ui,ui⟩

是对子空间 ( \text{span}(u[1], \dots, u[k]) ) 的正交投影。这很容易看出来,我建议读者将此作为练习来完成。(这也可以在问题部分找到。)

从 (7.5),我们还可以确定 ( \text{proj}[u[1], \dots, u[k]] ) 的矩阵形式:

 ∑k T proj (x) = ( uiu-i) x. u1,...,uk i=1 ∥ui∥2 ◟----◝◜---◞ ∈ℝn×n

这是一个重要的知识点,因为投影矩阵通常在某些算法的实现中需要用到。

7.4.1 正交投影的性质

现在我们已经看到了一些例子,是时候以更一般的方式讨论正交投影了。正交投影有很多有用的理由,我们将在本节中探讨它们。首先,让我们从最重要的一点开始:正交投影也使得向量能够分解为给定子空间加上一个正交向量。

定理 45\。

设 ( V ) 为一个内积空间,( P : V \rightarrow V ) 为一个投影算子。那么,( V = \ker P + \text{im} P ); 也就是说,任意向量 ( x \in V ) 都可以表示为

x = x + x , x ∈ ker P, x ∈ im P. ker im ker im

如果 ( P ) 是一个正交投影,则 ( x[\text{im}] \perp x[\ker] )。

证明:每个 ( x ) 可以表示为

x = (x − P x) + P x.

由于 ( P ) 是幂等的,即 ( P² = P ),我们有

P (x − P x) = P x − P (P x) = P x − P x = 0,

也就是说,( x - P x \in \ker P )。根据定义,( P x \in \text{im} P ),因此 ( V = \ker V + \text{im} V ),这证明了我们的主要命题。

如果 ( P ) 是一个正交投影,那么根据定义,( x[\text{im}] \perp x[\ker] ),这正是我们需要证明的。

此外,正交投影是自伴的。听起来似乎不算什么大事,但自伴性导致了一些非常愉快的性质。

定理 46\。

设 ( V ) 为一个内积空间,( P : V \rightarrow V ) 为一个正交投影。则,( P ) 是自伴的。

证明:根据定义(27),我们只需要证明

⟨P x, y⟩ = ⟨x, P y⟩

对于任意 ( x, y \in V ),成立。在之前的结果中,我们已经看到 ( x ) 和 ( y ) 可以表示为

x = x[(\ker P)] + x[(\text{im} P)], x[(\ker P)] ∈ (\ker P), x[(\text{im} P)] ∈ (\text{im} P) y = y[(\ker P)] + y[(\text{im} P)], y[(\ker P)] ∈ (\ker P), y[(\text{im} P)] ∈ (\text{im} P)

由于 ( P² = P ),我们有

⟨P x, y⟩ = ⟨P x_{\ker P} + P x_{\text{im} P}, y_{\ker P} + y_{\text{im} P}⟩ = ⟨x_{\text{im} P}, y_{\ker P} + y_{\text{im} P}⟩ = ⟨x, y⟩ + ⟨x, y⟩ ◟-im-P◝◜kerP◞ im P im P =0 = ⟨x_{\text{im} P}, y_{\text{im} P}⟩.

类似地,可以证明 ( \langle x, P y \rangle = \langle x[\text{im} P], y[\text{im} P] \rangle )。这两个恒等式意味着 ( \langle P x, y \rangle = \langle x, P y \rangle ),这正是我们需要证明的。

自伴算子的一个直接推论是,正交投影的核是其像的正交补。

定理 47. 设 ( V ) 是一个内积空间,( P : V \to V ) 是一个正交投影。那么,

 \perp \text{ker} P = (\text{im} P).

证明。为了证明这两个集合的相等,我们需要证明 (a) ( \text{ker} P \subseteq (\text{im} P)^\perp ),以及 (b) ( (\text{im} P)^\perp \subseteq \text{ker} P )。

(a) 设 ( x \in \text{ker} P ),即假设 ( Px = 0 )。我们需要证明,对于任何 ( y \in \text{im} P ),有 ( \langle x, y \rangle = 0 )。为此,设 ( y[0] \in V ),使得 ( P y[0] = y )。(这是可以保证存在的,因为我们从 ( P ) 的像中取了 ( y )。)那么,

\langle x, y \rangle = \langle x, P y_0 \rangle = \langle P x, y_0 \rangle = \langle 0, y_0 \rangle = 0,

其中 ( P ) 是自伴的。因此,( x \in (\text{im} P)^\perp ) 也成立,意味着 ( \text{ker} P \subseteq (\text{im} P)^\perp )。

(b) 现在,设 ( x \in (\text{im} P)^\perp )。那么,对于任何 ( y \in V ),我们有 ( \langle x, Py \rangle = 0 )。然而,

\langle Px, y \rangle = \langle x, P y \rangle = 0.

特别地,当选择 ( y = Px ) 时,我们有 ( \langle Px, Px \rangle = 0 )。由于内积的正定性,这意味着 ( Px = 0 ),即 ( x \in \text{ker} P )。

总结以上内容,如果 ( P ) 是内积空间 ( V ) 的正交投影,那么

V = \text{im} P + (\text{im} P)^\perp.

你还记得当我们首次遇到正交补的概念时(定义 13),我们证明了对于任何有限维内积空间 ( V ) 及其子空间 ( S ),有 ( V = S + S^\perp ) 吗?我们是通过使用一个特殊的正交投影来实现的。我们离看到一般模式已经不远了。

因为正交投影 ( P ) 的核是其像的正交补,所以变换 ( I - P ) 也是一个正交投影,且像与核的角色发生了反转。

定理 48\。

设 ( V ) 是一个内积空间,( P : V \to V ) 是一个正交投影。那么,( I - P ) 也是一个正交投影,并且

\text{ker}(I - P) = \text{im} P, \text{im}(I - P) = \text{ker} P.

证明非常简单,因此留给读者作为练习。

还有一点需要提到。如果两个正交投影的像空间相同,那么这些投影本身是相等的。这是一个非常强的唯一性性质,因为如果你仔细想一想,对于其他类线性变换,这种情况并不成立。

定理 49. (正交投影的唯一性)

设 ( V ) 是一个内积空间,( P, Q : V \to V ) 是两个正交投影。如果 ( \text{im} P = \text{im} Q ),那么 ( P = Q )。

证明。由于 ( \text{ker} P = (\text{im} P)^\perp ),像空间的相等也意味着 ( \text{ker} P = \text{ker} Q )。

由于 ( V = \text{ker} P + \text{im} P ),每个 ( x \in V ) 都可以分解为

x = x_{\text{ker} P} + x_{\text{im} P}, x_{\text{ker} P} \in \text{ker} P, x_{\text{im} P} \in \text{im} P.

这个分解和核与像空间的相等给出了

Px = P x_{\text{ker} P} + P x_{\text{im} P} = x_{\text{im} P}.

通过类似的推理,我们有 ( Qx = x[\text{im} P] ),因此对于所有 ( x \in V ),有 ( Px = Qx )。这证明了 ( P = Q )。

换句话说,给定一个子空间,只有一个正交投影可以映射到它。那么,这样的投影存在吗?当然存在,在下一节中,我们将看到它可以用几何术语来描述。

7.4.2 正交投影是最佳投影

正交投影具有极其愉悦且在数学上非常有用的性质。从某种意义上讲,如果 P : V →V 是一个正交投影,则 Px 在所有 imP 中的向量中提供了 x 的最佳近似。为了精确表达这一点,我们可以陈述如下。

定理 50.(正交变换的构造)

设 V 是一个有限维内积空间,S ⊆V 是它的子空间。那么,定义变换 P : V →V,如下所示

P : x → argmyi∈nS ∥x − y∥

是 S 的正交投影。

换句话说,由于正交投影到给定子空间是唯一的(如定理 49 所示),因此 Px 是子空间 S 中最接近 x 的向量。因此,我们可以将其表示为 P[S],以强调其唯一性。

除了有明确的方法来描述正交投影之外,还有一个额外的好处。回想之前,我们展示了

V = im P + (im P)⊥

恒成立。由于对于任何子空间 S,都存在一个正交投影 P[S],其像集为 S,因此也可以得出 V = S + S^⊥。虽然我们在讨论正交补时已经看到过这一点(定义 13),但通过不需要构造 S 中的正交标准基的证明来看到这一点仍然很有趣。

有趣的是,这正是数学分析和线性代数交汇的地方。虽然我们目前还没有相关工具,但通过使用收敛的概念,以上定理可以推广到无限维空间。尽管在实际的机器学习中无限维空间并不特别相关,但它为函数研究提供了一个美丽的数学框架。谁知道呢,也许有一天这些先进工具会为机器学习带来重大突破。

7.5 计算特征值

在上一章中,我们达到了奇异值分解,这是线性代数的一个巅峰结果。我们为此打下了理论基础。

然而,有一件事还没解决:在实践中计算奇异值分解。没有这个工具,我们无法充分发挥这一强大工具的所有优势。在这一节中,我们将开发两种方法来实现这一目的。一种方法为特征向量的行为提供了深刻的洞察,但在实践中并不适用。另一种方法则提供了出色的性能,但难以理解公式背后的具体含义。我们先从第一种方法开始,阐明特征向量是如何决定线性变换的效果的!

7.5.1 用幂迭代法计算实对称矩阵的特征向量

如果你记得,我们通过追溯问题到对称矩阵的谱分解,发现了奇异值分解。反过来,我们可以通过从矩阵的特征向量中找到正交归一基来获得谱分解。计划如下:首先,我们定义一个过程,用来找到对称矩阵的正交归一特征向量集。然后,利用这个过程计算任意矩阵的奇异值分解。

一种简单的方法是通过解多项式方程 det(A−λI) = 0 来找到特征值,然后通过解线性方程 (A −λI)x = 0 来计算相应的特征向量。

然而,这种方法存在问题。对于一个 n × n 矩阵,特征多项式 p(λ) = det(A−λI) 是一个 n 次多项式。即使我们能够有效地评估 det(A −λI) 对任意 λ 的值,也存在严重的问题。不幸的是,与二次方程 ax² + bx + c = 0 不同,当 n > 4 时,并没有找到解的公式。(并不是数学家们不够聪明,而是根本没有这样的公式。)

我们如何找到一种替代方法呢?我们再次使用之前非常有效的空想法。让我们假设我们知道特征值,玩弄它们,看看这是否能给我们一些有用的启示。

为了简化问题,假设 A 是一个小的对称 2 × 2 矩阵,特征值为 λ[1] = 4 和 λ[2] = 2。由于 A 是对称的,我们甚至可以找到一组对应的特征向量 u[1]、u[2],使得 u[1] 和 u[2] 形成一个正交归一基。 (也就是说,它们的范数为 1,且相互正交。) 这一点由谱分解定理(定理 41)保证。

因此,任何 x ∈ℝ² 都可以写成 x = x[1]u[1] + x[2]u[2],其中 x[1]、x[2] 是某些非零标量。如果我们将变换 A 应用到向量 x 上,会发生什么呢?因为 u[i] 是特征向量,所以我们有

Ax = A (x1u1 + x2u2 ) = x1Au1 + x2Au2 = x1λ1u1 + x2λ2u2 = 4x1u1 + 2x2u2.

再次应用 A,我们得到

 2 2 2 A x = x1λ1u1 + x2λ2u2 2 2 = 4x1u1 + 2 x2u2.

一种模式开始显现。一般来说,A 的第 k 次迭代结果为

Akx = x1 λk1u1 + x2λk2u2 = 4kx1u1 + 2kx2u2.

通过对 A^kx 进行深入观察,我们可以注意到 u[1] 的贡献比 u[2] 更加显著。为什么?因为系数 x[1]λ[1]^k = 4^kx[1] 比 x[2]λ[2]^k = 2^kx[2] 增长得更快,无论 x[1] 和 x[2] 的值如何。从技术上讲,我们可以说 λ[1] 支配了 λ[2]。

现在,通过将数值缩放为 λ[1]^k,我们可以提取特征向量 u[1]! 也就是说,

Akx- λ2- k λk = x1u1 + x2(λ1 ) u2 1 = x1u1 + (something very small)k.

如果我们让 k 无限增大,u[2] 对 Akkx- λ1 的贡献将消失。如果你熟悉极限的概念,你可以写出

lim[k → ∞] (A_k x - λ_k 1) = x[1]u[1]。

(7.6)

注释 7.(极限基础)

如果你不熟悉极限,这里有一个简短的解释。恒等式

 Akx lim --k- = x1u1 k→ ∞ λ1

这意味着随着 k 的增加,量 Akx- λk1 会越来越接近 x[1]u[1],直到它们之间的差异变得微不足道。实际上,这意味着我们可以通过选择一个非常大的 k 来近似 x[1]u[1],即 Akx- λk1

方程式(7.6)对我们来说是个好消息!我们所要做的就是反复应用变换 A,以识别主特征值λ[1]的特征向量。然而,有一个小警告:我们必须知道λ[1]的值。我们稍后会处理这个问题,但首先,让我们以定理的形式记录这一里程碑。

定理 51。

使用幂迭代法求主特征值的特征向量。

设 A ∈ℝ^(n×n)是一个实对称矩阵。假设:

(a) A 的特征值为λ[1]…λ[n](即λ[1]是主特征值)。

(b) 对应的特征向量 u[1],…,u[n]构成一个正交归一基。

设 x ∈ℝ^n 是一个向量,使得当写作线性组合 x = ∑ [i=1]^nx[i]u[i]时,系数 x[1] ∈ℝ是非零的。那么,

lim[k → ∞] (A_k x - λ_k 1) = x[1]u[1]。

(7.7)

在我们进入证明之前,需要做一些解释。回想一下,如果 A 是对称的,谱分解定理(定理 41)保证它可以通过相似变换对角化。在它的证明(概要)中,我们提到过对称矩阵具有:

  • 实特征值

  • 由特征向量构成的正交归一基

因此,假设(a)和(b)是有保证的,除了一个警告:特征值不一定是不同的。然而,这在实践中很少引发问题。原因有很多,但最重要的是,具有重复特征值的矩阵非常罕见,以至于它们形成了零概率集。(稍后我们将学习概率论。现在,我们可以假设随机选择一个具有重复特征值的矩阵是不可能的。)因此,碰到这种情况的可能性极小。

证明。由于 u[k]是特征值λ[k]的特征向量,我们有

A^kx = ∑[i=1]^n x[i]λ[i]^ku[i]。(7.8)

因此,

 n Akx- ∑ λi-k λk = x1u1 + xi(λ1) ui. 1 i=2

由于λ[1]是主特征值,对于 i = 2,…,n,∥λ[i]∕λ[1]∥/span>1,因此(λ[i]∕λ[1])^k → 0 当 k →∞时。因此,

 Akx- kli→m∞ λk = x1u1. 1

这就是我们需要证明的。

现在,让我们修复一下需要知道λ1 的小问题。由于λ1 是最大特征值,前述定理表明Akx 等于x1 λk1u1 加上一些更小的项,至少相较于这个主项更小。我们可以通过取上确界范数∥Akx ∥∞ 来提取这个量。(回想一下,对于任何y = (y1,...,yn) ,上确界范数定义为∥y ∥∞ = max {|y1|,...,|yn|} 。请记住,yi -s 是y 在我们向量空间原始基下的系数,这不一定是我们特征向量基u1,...,un 。)

通过将 jλ[1]j^k 从 A^kx 中提取出来,我们有

 ∑n ∥Akx ∥∞ = |λ1|k∥x1u1 + xi(λi)kui∥∞. i=2 λ1

直观地说,余项∑ [i=2]nxi)ku[i]很小,因此我们可以将范数近似为

∥Akx ∥∞ ≈ |λ1 |k∥x1u1∥ ∞.

换句话说,我们可以用∥Akx ∥∞ 来代替λk 1 进行缩放。

因此,我们已经准备好完整描述我们的一般特征向量求解过程。首先,我们随机初始化一个向量 x[0],然后定义递归序列

 Axk −1 xk = ----------, k = 1,2,... ∥Axk −1∥∞

利用 A 的线性性质,我们可以看到,实际上,

 k xk = --A--x0--, ∥Akx0 ∥∞

但是缩放有一个额外的好处,因为我们在任何计算步骤中都不需要使用大数字。这样,(51)表明

 --Akx0--- lki→m∞ xk = kl→im∞ ∥Akx0 ∥∞ = u1.

换句话说,我们可以在不实际知道特征值的情况下,提取出对应于主特征值的特征向量。

7.5.2 幂迭代法的实践

让我们将幂迭代法付诸实践!我们 power_iteration 函数的输入是一个方阵 A,我们期望输出的是对应于主特征值的特征向量。

由于这是一个迭代过程,我们应该定义一个条件来确定何时终止该过程。如果序列{x[k]}[k=1]^∞的连续项足够接近,我们就可以得到一个解。也就是说,如果,例如,

∥xk+1 − xk∥2 <1× 10−10,

我们可以停止并返回当前值。然而,这可能永远不会发生。对于这种情况,我们定义一个截止点,比如 k = 100,000,当计算达到此点时即使没有收敛,也终止计算。

为了给我们更多控制权,我们还可以手动定义初始化向量 x_init。

import numpy as np 
def power_iteration( 
    A: np.ndarray, 
    n_max_steps: int = 100000, 
    convergence_threshold: float = 1e-10, 
    x_init: np.ndarray = None, 
    normalize: bool = False 
): 
    #x0022;"/span> 
    Performs the power iteration method to find an approximation of the dominant eigenvector 
    of a square matrix. 

    Parameters 
    ---------- 
    A : np.ndarray 
        A square matrix whose dominant eigenvector is to be computed. 
    n_max_steps : int, optional 
        The maximum number of iterations to perform. Default is 100000\. 
    convergence_threshold : float, optional 
        The convergence threshold for the difference between successive approximations. 
        Default is 1e-10\. 
    x_init : np.ndarray, optional 
        The initial guess for the eigenvector. If None, a random vector is used. 
        Default is None. 
    normalize : bool, optional 
        If True, the resulting vector is normalized to unit length. Default is False. 

    Returns 
    ------- 
    np.ndarray 
        The approximate dominant eigenvector of the matrix ‘A‘. 

    Raises 
    ------ 
    ValueError 
        If the input matrix ‘A‘ is not square. 
    #x0022;"/span> 

    n, m = A.shape 

    # checking the validity of the input 
    if n != m: 
        raise ValueError("/span>the matrix A must be square 

    # reshaping or defining the initial vector 
    if x_init is not None: 
        x = x_init.reshape(-1, 1) 
    else: 
        x = np.random.normal(size=(n, 1)) 

    # performing the iteration 
    for step in range(n_max_steps): 
        x_transformed = A @ x    # applying the transform 
        x_new = x_transformed / np.linalg.norm(x_transformed, ord=np.inf)    # scaling the result 

        # quantifying the difference between the new and old vector 
        diff = np.linalg.norm(x - x_new) 
        x = x_new 

        # stopping the iteration in case of convergence 
        if diff /span> convergence_threshold: 
            break 

    # normalizing the result if required 
    if normalize: 
        return x / np.linalg.norm(x) 

    return x

为了测试这个方法,我们应该使用一个容易手工计算出正确输出的输入。我们常用的示例

 ⌊ ⌋ 2 1 A = ⌈ ⌉ . 1 2

应该是完美的,因为我们已经知道很多关于它的信息。

我们之前在 6.2.2 节中看到,它的特征值为λ[1] = 3 和λ[2] = 1,且对应的特征向量为 u[1] = (1,1)和 u[2] = (−1,1)。

让我们看看我们的函数是否正确地恢复了(u[1] = (1,1))的标量倍数!

A = np.array([[2, 1], [1, 2]]) 
u_1 = power_iteration(A, normalize=True) 
u_1
array([[0.70710678], 
      [0.70710678]])

成功!为了恢复特征值,我们只需应用线性变换并计算比例。

A @ u_1 / u_1
array([[3.], 
      [3.]])

结果是 3,正如预期的那样。

7.5.3 剩余特征向量的幂迭代

我们能否修改幂迭代算法来恢复其他特征值?理论上可以。实际操作中不行。让我详细解释!

为了掌握如何推广这个想法,让我们再次查看方程(7.8),表示

 ∑n Akx = xiλkiui. i=1

 k Aλxk- 1 收敛的条件之一是 x 应该包含特征向量 u[1]的非零分量,也就是说,x[1]≠0。

如果 x[1] = 0 怎么办?在这种情况下,我们得到

Akx = x2λk2u2 + ⋅⋅⋅+ xnλk2un,

其中 x[2]λ[2]^ku[2]成为主导项。

因此,我们有

Akx ∑n λi k --k-= x2u2 + xi( --) uk λ 2 i=3 λ2 = x u + (something very small), 2 2 k

这意味着

 Akx lim --k-= x2u2. k→∞ λ 2

让我们在以下定理中将其数学化。

定理 52。(广义幂迭代)

令 A ∈ℝ^(n×n)为一个实对称矩阵。假设:

(a) A 的特征值为λ[1]/span>…/span>λ[n](即,λ[1]是主特征值)。

(b) 相应的特征向量 u[1]、…,u[n]构成一个正交规范基。

x ∈ ℝn 为一个向量,使得当它被表示为基向量 u1,...,un 的线性组合时,它的第一个非零分量沿着 ul 方向,其中 l = 1,2,...,n (即,  ∑ x = ni=l xiui )。

然后,

 k lim A--x = xlul k→ ∞ λkl

成立。

证明过程与我们之前看到的几次非常相似。问题是,如何从任何向量中消除 u[1]、…,u[l−1]分量?答案很简单:正交投影(7.4 节)。

为了简化问题,让我们看看如何用幂迭代提取第二主特征向量。回想一下变换

proj (x) = ⟨x, u ⟩u u1 1 1 1

描述了任何 x 到 u1 的正交投影。

具体来说,如果 x = ∑ [i=1]^nx[i]u[i],那么

 ∑n proju (x) = proju ( xiui) 1 1 i=1 ∑n = xiproj (ui) i=1 u1 = x1u1.

这正是我们所寻找的相反方向!然而,在此时,我们可以看到 I − proj[u[1]]将适合我们的需求。这仍然是正交投影。此外,我们有

 ∑n ∑n (I − proju1)( xiui) = xiui, i=1 i=2

即,I − proj[u[1]] 消除了 x 中的 u[1] 分量。因此,如果我们用 x^∗ = (I − proj[u[1]])(x) 初始化幂迭代,序列 --Akx∗-- ∥Akx∗∥∞ 将收敛到 u[2],第二个主特征向量。

我们如何实际计算 (I − proj )(x) u1 ?回忆一下,在标准正交基下,![proju1] 的矩阵可以写成:

proju1 = u1uT1.

(请记住,![ui] 向量构成一个正交归一基,因此 ∥u1∥ = 1 。)因此,![I − proju1] 的矩阵是:

I − u1uT1,

我们可以很容易地计算出这个结果。

对于一个一般的向量 u,我们可以这样在 NumPy 中实现。

def get_orthogonal_complement_projection(u: np.ndarray): 
    #x0022;"/span> 
    Compute the projection matrix onto the orthogonal complement of the vector u. 

    This function returns a projection matrix P such that for any vector v, 
    P @ v is the projection of v onto the subspace orthogonal to u. 

    Parameters 
    ---------- 
    u : np.ndarray 
        A 1D or 2D array representing the vector u. It will be reshaped to a column vector. 

    Returns 
    ------- 
    np.ndarray 
        The projection matrix onto the orthogonal complement of u. This matrix 
        has shape (n, n), where n is the length of u. 
    #x0022;"/span> 

    u = u.reshape(-1, 1) 
    n, _ = u.shape 
    return np.eye(n) - u @ u.T / np.linalg.norm(u, ord=2)**2

所以,找出所有特征向量的过程如下。

  1. 初始化一个随机的  (1) x ,并使用幂迭代找到 u1

  2. x (1) 投影到由 u 1 所张成的子空间的正交补空间,从而得到

    x (2) := (I − proju1)(x(1)),

    我们将其作为第二轮幂迭代的初始向量,从而得到第二个主特征向量 u2

  3. x (2) 投影到由 u 1 u 2 所张成的子空间的正交补空间,从而得到

    x(3) = (I − proju2)(x(2)) (1) = (I − proju1,u2)(x ),

    我们将其作为第三轮幂迭代的初始向量,从而得到第三个主特征向量 u3

  4.  (3) x 投影到……

你已经明白了这个模式。为了在实践中实现这一点,我们添加了 find_eigenvectors 函数。

def find_eigenvectors(A: np.ndarray, x_init: np.ndarray): 
    #x0022;"/span> 
    Find the eigenvectors of the matrix A using the power iteration method. 

    This function computes the eigenvectors of the matrix A by iteratively 
    applying the power iteration method and projecting out previously found 
    eigenvectors to find orthogonal eigenvectors. 

    Parameters 
    ---------- 
    A : np.ndarray 
        A square matrix of shape (n, n) for which eigenvectors are to be computed. 

    x_init : np.ndarray 
        A 1D array representing the initial vector used for the power iteration. 

    Returns 
    ------- 
    List[np.ndarray] 
        A list of eigenvectors, each represented as a 1D numpy array of length n. 
    #x0022;"/span> 

    n, _ = A.shape 
    eigenvectors = [] 

    for _ in range(n): 
        ev = power_iteration(A, x_init=x_init) 
        proj = get_orthogonal_complement_projection(ev) 
        x_init = proj @ x_init 
        x_init = x_init / np.linalg.norm(x_init, ord=np.inf) 
        eigenvectors.append(ev) 

    return eigenvectors

让我们在我们的老朋友上测试一下 find_eigenvectors

 ⌊ ⌋ 2 1 A = ⌈ ⌉! 1 2

A = np.array([[2.0, 1.0], [1.0, 2.0]]) 
x_init = np.random.rand(2, 1) 
find_eigenvectors(A, x_init)
[array([[1.], 
       [1.]]), 
 array([[ 1.], 
       [-1.]])]

结果如我们所料。(不要惊讶于特征向量未被归一化,因为我们在 find_eigenvectors 函数中并没有显式执行这一操作。)

我们已经准备好真正对称矩阵进行对角化。回忆一下,通过将特征向量一个个垂直堆叠,可以获得对角化的正交矩阵 U。

def diagonalize_symmetric_matrix(A: np.ndarray, x_init: np.ndarray): 
    #x0022;"/span> 
    Diagonalize a symmetric matrix A using its eigenvectors. 

    Parameters 
    ---------- 
    A : np.ndarray 
        A symmetric matrix of shape (n, n) to be diagonalized. The matrix should 
        be square and symmetric. 

    x_init : np.ndarray 
        A 1D array representing the initial guess for the power iteration. 

    Returns 
    ------- 
    Tuple[np.ndarray, np.ndarray] containing: 
        - U : np.ndarray 
            A matrix of shape (n, n) whose columns are the normalized eigenvectors 
            of A. 
        - np.ndarray 
            A diagonal matrix (n, n) of the eigenvalues of A, computed as U @ A @ U.T. 
    #x0022;"/span> 

    eigenvectors = find_eigenvectors(A, x_init) 
    U = np.hstack(eigenvectors) / np.linalg.norm(np.hstack(eigenvectors), axis=0, ord=2) 
    return U, U @ A @ U.T 

diagonalize_symmetric_matrix(A, x_init)
(array([[ 0.70710678,  0.70710678], 
       [ 0.70710678, -0.70710678]]), 
 array([[ 3.00000000e+00, -3.57590301e-11], 
       [-3.57589164e-11,  1.00000000e+00]]))

太棒了!

什么问题?不幸的是,幂迭代在数值上不稳定。对于 n × n 的矩阵,其中 n 可能达到数百万,这个问题非常严重。

那么,为什么我们要讲这么多关于幂迭代的内容呢?除了它是最简单的,它还提供了对线性变换如何工作的深刻洞察。

这个恒等式

 ∑n Ax = xiλiui, i=1

其中 λ[i] 和 u[i] 是对称矩阵 A 的特征值-特征向量对,反映了特征向量和特征值如何决定变换的行为。

如果幂迭代在实践中不可用,我们该如何计算特征值呢?我们将在下一节中看到这一点。

7.6 QR 算法

在实践中用于计算特征值的算法是所谓的 QR 算法,由 John G. R. Francis 和苏联数学家 Vera Kublanovskaya 独立提出。这就是我们在学习线性代数时所有知识的汇聚点。描述 QR 算法非常简单,因为它是矩阵分解和乘法步骤的迭代。

然而,理解它为什么有效是另一个问题。在幕后,QR 算法结合了我们之前学习的许多工具。首先,让我们回顾一下经典的格拉姆-施密特正交化过程。

7.6.1 QR 分解

如果你记得,我们在引入正交基的概念时遇到了格拉姆-施密特正交化过程(定理 13)。

本质上,这个算法将一个任意基 v[1],…,v[n] 转换为一个正交标准基 e[1],…,e[n],使得对于所有 1 ≤k ≤n,e[1],…,e[k] 与 v[1],…,v[k] 张成相同的子空间。自从我们上次遇到这个问题以来,我们已经对线性代数有了更多的了解,所以我们已经准备好看到更大的图景。

通过定义的正交投影

 ∑k ⟨x,ei⟩ proje1,...,ek(x) = ------ei, i=1 ⟨ei,ei⟩

我们可以递归地描述格拉姆-施密特过程为

e1 = v1, ek = vk − proje1,...,ek−1(vk),

其中 e[k] 向量在之后是标准化的。

扩展并明确写出 e[k] 后,我们得到

e1 = v1 e2 = v2 − ⟨v2,e1⟩e1 ⟨e1,e1⟩ .. . ⟨vn, e1⟩ ⟨vn, en−1⟩ en = vn − ⟨e-,e-⟩e1 − ⋅⋅⋅− ⟨e----,e---⟩en−1. 1 1 n− 1 n−1

一种模式开始显现出来。通过将 e[1],…,e[n] 项排列在一边,我们得到

v1 = e1 ⟨v2,e1⟩ v2 = ⟨e-,e-⟩e1 + e2 1 1 ... vn = ⟨vn,e1⟩e1 + ⋅⋅⋅ + -⟨vn,en−1⟩-en−1 + en. ⟨e1,e1⟩ ⟨en−1,en−1⟩

这开始像某种矩阵乘法了!回想一下,矩阵乘法可以看作是对列向量的线性组合。(如果不确定这一点,可以查阅 (4.2)。)

通过将列向量 v[k] 水平拼接成矩阵 A,并类似地通过 e[k] 向量定义向量 Q,我们得到

A = Q∗R ∗

对于某些上三角矩阵 R,它由根据格拉姆-施密特正交化方法确定的 e[k] 在 v[k] 中的系数定义。更精确地说,定义

 ⌊ ⌋ ⌊ ⌋ A = ||v ... v || , Q ∗ = || e ... e || , ⌈ 1 n⌉ ⌈ 1 n⌉

 ⌊ ⟨v2,e1⟩ ⟨vn,e1⟩⌋ |1 ⟨e1,e1⟩ ... ⟨e1,e1⟩| ||0 1 ... ⟨vn,e2⟩|| R ∗ = |. . ⟨e2,.e2⟩|. ||.. .. ... .. || ⌈ .. ⌉ 0 0 . 1

结果 A = Q∗R∗ 几乎就是我们所说的 QR 分解。Q^∗ 的列是正交的(但不是标准正交的),而 R^∗ 是上三角矩阵。我们可以通过按列提取范数轻松将 Q^∗ 正交标准化,从而得到

 ⌊ ⌋ ∥e ∥ √⟨v2,e1⟩-- ... √⟨vn,e1⟩- ⌊ ⌋ | 1 ⟨e1,e1⟩ ⟨e1,e1⟩| | | || 0 ∥e2∥ ... √⟨vn,e2⟩|| Q = |⌈ e1-- ... en-|⌉ , R = || ⟨e2,e2⟩|| . ∥e1∥ ∥en∥ || ... ... ... ... || ⌈ . ⌉ 0 0 .. ∥en∥

很容易看出,A = QR 仍然成立。这个结果被称为 QR 分解,我们刚刚证明了以下定理。

定理 53. (QR 分解)

设 A ∈ ℝ^(n×n)为可逆矩阵。那么,存在一个正交矩阵 Q ∈ ℝ^(n×n)和一个上三角矩阵 R ∈ ℝ^(n×n),使得

A = QR

公式成立。

正如我们将要看到的,QR 分解是一个极其有用且多功能的工具(就像所有其他矩阵分解一样)。在我们继续讨论它如何用于实际计算特征值之前,让我们先将迄今为止的内容转化为代码!

QR 分解算法本质上是 Gram-Schmidt 正交化,我们显式地记住一些系数并由此形成一个矩阵。(如果你觉得有些困惑,回顾一下我们在第 3.1.2 节的实现。)

def projection_coeff(x: np.ndarray, to: np.ndarray): 
    #x0022;"/span> 
    Compute the scalar coefficient for the projection of vector x onto vector to. 

    Parameters 
    ---------- 
    x : np.ndarray 
        A 1D array representing the vector onto which the projection is computed. 

    to : np.ndarray 
        A 1D array representing the vector onto which x is being projected. 

    Returns 
    ------- 
    float 
        The scalar coefficient representing the projection of x onto to. 
    #x0022;"/span> 
    return np.dot(x, to)/np.dot(to, to)
from typing import List

def projection(x: np.ndarray, to: List[np.ndarray], return_coeffs: bool = True):
    """
    Computes the orthogonal projection of a vector `x` onto the subspace 
    spanned by a set of vectors `to`.

    Parameters
    ----------
    x : np.ndarray
        A 1D array representing the vector to be projected onto the subspace.

    to : List[np.ndarray]
        A list of 1D arrays, each representing a vector spanning the subspace
        onto which `x` is projected.

    return_coeffs : bool, optional, default=True
        If True, the function returns the list of projection coefficients.
        If False, only the projected vector is returned.

    Returns
    -------
    Tuple[np.ndarray, List[float]] or np.ndarray
        - If `return_coeffs` is True, returns a tuple where the first element
        is the projected vector and
          the second element is a list of the projection coefficients
          for each vector in `to`.
        - If `return_coeffs` is False, returns only the projected vector.
    """

    p_x = np.zeros_like(x)
    coeffs = []

    for e in to:
        coeff = projection_coeff(x, e)
        coeffs.append(coeff)
        p_x += coeff*e

    if return_coeffs:
        return p_x, coeffs
    else:
        return p_x

现在,我们可以将这些工具结合起来,得到一个任意方阵的 QR 分解。(令人惊讶的是,这对于非方阵也适用,但我们不在此讨论。)

def QR(A: np.ndarray): 
    #x0022;"/span> 
    Computes the QR decomposition of matrix A using the Gram-Schmidt 
    orthogonalization process. 

    Parameters 
    ---------- 
    A : np.ndarray 
        A 2D array of shape (n, m) representing the matrix to be decomposed. 
        The matrix A should have full column rank for a valid QR decomposition. 

    Returns 
    ------- 
    Tuple[np.ndarray, np.ndarray] 
        - Q : np.ndarray 
            An orthogonal matrix of shape (n, m), whose columns are orthonormal. 
        - R : np.ndarray 
            An upper triangular matrix of shape (m, m), representing the coefficients of the 
            linear combinations of the columns of A. 
    #x0022;"/span> 
    n, m = A.shape 

    A_columns = [A[:, i] for i in range(A.shape[1])] 
    Q_columns, R_columns = [], [] 

    Q_columns.append(A_columns[0]) 
    R_columns.append([1] + (m-1)*[0]) 

    for i, a in enumerate(A_columns[1:]): 
        p, coeffs = projection(a, Q_columns, return_coeffs=True) 
        next_q = a - p 
        next_r = coeffs + [1] + max(0, m - i - 2)*[0] 

        Q_columns.append(next_q) 
        R_columns.append(next_r) 

    # assembling Q and R from its columns 
    Q, R = np.array(Q_columns).T, np.array(R_columns).T 

    # normalizing Q’s columns 
    Q_norms = np.linalg.norm(Q, axis=0) 
    Q = Q/Q_norms 
    R = np.diag(Q_norms) @ R 
    return Q, R

让我们对一个随机的 3×3 矩阵进行尝试。

A = np.random.rand(3, 3) 
Q, R = QR(A)

需要检查三件事:(a)A = QR,(b)Q 是正交矩阵,以及(c)R 是上三角矩阵。

np.allclose(A, Q @ R)
True
np.allclose(Q.T @ Q, np.eye(3))
True
np.allclose(R, np.triu(R))
True

成功!只剩下一个问题了。这个如何帮助我们计算特征值呢?我们现在来看看。

7.6.2 迭代 QR 分解

令人惊讶的是,我们可以通过一个简单的迭代过程发现矩阵 A 的特征值。首先,我们找到 QR 分解。

A = Q R , 1 1

并通过以下方式定义矩阵 A[1]

A1 = R1Q1,

也就是说,我们只是反转 Q 和 R 的顺序。然后,我们从头开始,找到 A[1]的 QR 分解,依此类推,定义这个序列

L(U,V ) = {f : U → V | f is linear}(7.9)

从长远来看,A[k]的对角元素将越来越接近 A 的特征值。这就是所谓的 QR 算法,简单到我第一次看到时都不敢相信。

有了所有这些工具,我们可以在几行代码中实现 QR 算法。

def QR_algorithm(A: np.ndarray, n_iter: int = 1000): 
    #x0022;"/span> 
    Computes the QR algorithm to find the eigenvalues of a matrix A. 

    Parameters 
    ---------- 
    A : np.ndarray 
        A square matrix of shape (n, n) for which the eigenvalues are to be computed. 

    n_iter : int, optional, default=1000 
        The number of iterations to run the QR algorithm. 
        More iterations may lead to more accurate results, 
        but the algorithm typically converges quickly. 

    Returns 
    ------- 
    np.ndarray 
        A matrix that has converged, where the diagonal elements are the eigenvalues of the 
        original matrix A. 
        The off-diagonal elements should be close to zero. 
    #x0022;"/span> 

    for _ in range(n_iter): 
        Q, R = QR(A) 
        A = R @ Q 

    return A

让我们立即测试一下。

A = np.array([[2.0, 1.0], [1.0, 2.0]]) 
QR_algorithm(A)
array([[3.00000000e+00, 2.39107046e-16], 
      [0.00000000e+00, 1.00000000e+00]])

我们几乎达到了最前沿的技术。不幸的是,传统的 QR 算法有一些问题,因为它可能无法收敛。一个简单的例子是由矩阵给出的

 ⌊ ⌋ A = ⌈0 1⌉ . 1 0

A = np.array([[0.0, 1.0], [1.0, 0.0]]) 
QR_algorithm(A)
array([[0., 1.], 
      [1., 0.]])

在实际操作中,我们可以通过引入位移来解决这个问题:

L(U,V ) = {f : U → V | f is linear}(7.10)

其中α[k]是某个标量。定义这些位移的方法有很多种(例如 Rayleigh 商位移、Wilkinson 位移等),但这些细节超出了我们的研究范围。

7.7 总结

我告诉过你,攀登高峰并不容易:到目前为止,这是我们遇到的最难的一章。然而,我们所学的工具已经达到了线性代数的顶峰。我们首先研究了两种特殊的变换:自伴随变换和正交变换。前者带来了谱分解定理,而后者则给出了奇异值分解。

毋庸置疑,奇异值分解(SVD)是线性代数中最重要的结果之一,它表明每个矩形矩阵 A 都可以写成如下形式:

A = U ΣV T,

其中 U ∈ℝ^(n×n),Σ ∈ℝ^(n×m),V ∈ℝ^(m×m)是相当特殊的:Σ是对角矩阵,而 U 和 V 是正交矩阵。

当将 A 视为一个线性变换时,奇异值分解告诉我们,它可以写成两个保持距离的变换(正交变换)和一个简单的缩放的组合。这个表述相当准确!

说到奇异值和特征值,我们如何在实际中找到它们呢?绝对不是通过解多项式方程。

det(A − λI ) = 0,

这是一个计算上非常痛苦的问题。

我们已经看到了两种实际方法来解决这个任务。一个是复杂、缓慢、不稳定,但富有启发性的幂迭代算法,它通过极限为实对称矩阵 A 的主特征值求得特征向量

 Akx0 lk→im∞ ∥Akx--∥-- = u1. 0 ∞

尽管幂迭代法为我们提供了有关这类矩阵结构的宝贵见解,但真正的关键是 QR 算法(与二维码无关),它源自格拉姆-施密特算法的矢量化版本。QR 算法难以直观理解,但尽管其神秘性,它提供了一种在实践中快速计算特征值的方法。

下一步是什么?现在我们已经登上了顶峰,是时候稍微放松一下,享受美丽的景色了。下一章正是如此。

你看,线性代数中最美丽和最有用的主题之一是矩阵与图形之间的关系。因为从正确的角度来看,矩阵就是图形,我们可以利用这种关系来研究两者的结构。让我们来看看!

7.8 问题

问题 1. 设 u[1],…,u[k] ∈ℝ^n 为一组线性无关且两两正交的向量。证明线性变换

 ∑k ⟨x, ui⟩ proju1,...,uk(x) = ⟨u-,u-⟩ui i=1 i i

是一个正交投影。

问题 2. 设 u[1],…,u[k] ∈ℝ^n 为一组线性无关的向量,并定义线性变换

 ∑k ⟨x,ui⟩- fakeproju1,...,uk(x ) = ⟨ui,ui⟩ui. i=1

这是一个投影吗?(提示:研究特殊情况 k = 2 和ℝ³。如果需要,你可以将其可视化。)

问题 3. 设 V 是一个内积空间,P: V →V 是一个正交投影。证明 I −P 也是一个正交投影,并且

ker(I − P ) = im P, im (I − P ) = ker P

成立。

问题 4. 设 A,B ∈ℝ^(n×n)为两个方阵,它们以块矩阵形式写出

 ⌊ ⌋ ⌊ ⌋ A1,1 A1,2 B1,1 B1,2 A = ||A A ||, B = ||B B ||, ⌈ 2,1 2,2⌉ ⌈ 2,1 2,2⌉

其中 A[1,1],B[1,1] ∈ℝ^(k×k),A[1,2],B[1,2] ∈ℝ^(k×l),A[2,1],B[2,1] ∈ℝ^(l×k),A[2,2],B[2,2] ∈ℝ^(l×l)。

证明以下内容

 ⌊ ⌋ A1,1B1,1 + A1,2B2,1 A1,1B1,2 + A1,2B2,2 || || AB = ⌈A2,1B1,1 + A2,2B2,1 A2,1B1,2 + A2,2B2,2⌉ .

问题 5. 设 A ∈ℝ^(2×2)是由以下定义的方阵:

 ⌊ ⌋ A = ⌈1 1⌉ . 1 0

(a) 证明 A 的两个特征值为

 − 1 λ1 = φ, λ2 = − φ ,

其中φ = 1+√5 --2-- 是黄金比例。

(b) 证明λ[1]和λ[2]的特征向量为

 ⌊ ⌋ ⌊ −1⌋ f = ⌈φ ⌉, f = ⌈− φ ⌉ , 1 1 2 1

并证明它们是正交的;即,⟨f[1],f[2]⟩ = 0。

(c) 设 U 为由 A 的特征向量组成的矩阵,定义为

 ⌊ ⌋ φ − φ−1 U = ⌈ ⌉ . 1 1

证明以下内容

 ⌊ ⌋ 1 φ −1 U −1 = √1-⌈ ⌉ . 5 − 1 φ

(d) 证明以下内容

 − 1 A = UΛU ,

其中

 ⌊ ⌋ ⌊ ⌋ λ1 0 φ 0 Λ = ⌈ ⌉ = ⌈ −1⌉ . 0 λ2 0 − φ

(e) 你是否曾经想过这些日常计算的目的?接下来是关键。首先,证明

 ⌊ ⌋ ⌊ ⌋ n An = ⌈1 1⌉ = ⌈Fn+1 Fn ⌉ , 1 0 Fn Fn−1

其中 F[n]是第 n 个斐波那契数,定义为递归序列

F0 = 0, F1 = 1, Fn = Fn−1 + Fn−2.

因此,证明 A = UΛU^(−1)意味着

 n −n Fn = φ--−−(√−-φ)-- . 5

真酷!

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者互动,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

矩阵与图

现在,我们已经度过了困难的部分(即奇异值分解和其他矩阵分解),是时候以一个震撼的结尾完成我们的线性代数之旅了。在我的教学经验中,学生们最常见的一个担忧是实践与理论之间的明显脱节。在机器学习从业者和软件工程师中,通常有一种不愿接触那些在实践中不立即有价值的内容的倾向。

作为一名数学家,我完全理解这种恐惧的来源。我们常常被教授一些无实际意义的深奥话题,浪费了宝贵的时间,而这些时间本可以用来通过数据进行快速的分析和处理。

在本章中,我们将讨论一个对你的机器学习实践不立即有用,但未来会带来丰厚回报的主题。考虑到它的美丽,它或许会成为你下一个天才创意的灵感来源。(不过不敢保证。)

让我给你介绍线性代数中最被低估的事实:矩阵就是图,图就是矩阵。将矩阵编码为图是一种作弊方式,使得复杂的行为变得容易研究。

请查看下面的图 8.1。

PIC

图 8.1:一个矩阵及其有向图

你能弄明白它是如何构建的吗?你能猜到它为什么有用吗?我们将在接下来的几页中回答这些问题。具体来说,我们将看到:

  • 图与矩阵之间的关系是什么

  • 矩阵乘法如何转化为图上的路径

  • 图的连通结构揭示了其对应矩阵的什么信息

让我们开始吧!

第十章:8.1 非负矩阵的有向图

如果你仔细观察图 8.1,你可能会弄明白如何从矩阵构建一个加权图。只需比较每一行和节点的出边权重。

每一行是一个节点,每个元素代表一条有向且加权的边。零元素的边会被省略。第 i 行第 j 列的元素对应着一条从 i 到 j 的边。得到的图叫做矩阵的有向图(或称为有向图)。

为了稍微展开定义,让我们看一下之前的矩阵图

 ⌊ ⌋ 0.5 1 0 | | 3×3 A = |⌈0.2 0 2.2|⌉ ∈ ℝ . 1.8 2 0

这是第一行,对应于从第一个节点出来的边。

PIC

图 8.2:第一行对应于从第一个节点出来的边

同样,第一列对应于进入第一个节点的边。

PIC

图 8.3:第一列对应于进入第一个节点的边

现在,我们可以将所有这些内容结合起来。图 8.4 展示了完整的图像,节点被明确标注。

PIC

图 8.4:构建矩阵的图

现在是时候查看正式定义了,我们将其分为两部分。首先,让我们讨论有权的有向图。

定义 29\。

令 V = {v[1],v[2],…,v[n]} 为一个任意有限集合。我们说 G = (V,E,w) 是一个加权有向图,如果

(a)V 表示顶点集合(也称为节点)。(b) E ⊆V ×V 表示有向边集合。 (c) 函数 w : E →ℝ 表示边的权重。

例如,查看图 8.4,我们可以将其形式化为(V,E,w),其中 V = {v[1],v[2],v[3]},

E = {(v1,v1),(v1,v2), (v2,v1),(v2,v3), (v3,v1),(v3,v2)},

并且

w(v1,v1) = 0.5, w (v1,v2) = 1, w(v2,v1) = 0.2, w (v2,v2) = 2.2, w(v3,v1) = 1.8, w (v3,v2) = 2.

现在,我们可以开始讨论矩阵了。

定义 30.(不可约和可约矩阵)

令 A ∈ℝ^(n×n) 为一个非负矩阵,即只含有非负元素的矩阵。如果有向加权图 G = (V,E,w),则称其为 A 的有向图(或简称有向图),当且仅当:

(a)V = {1,2,…,n},(b)当且仅当,a[i,j]/span>0 时,(i,j) ∈E,(c)w(i,j) = a[i,j]。

(有时为了说明问题,我们会省略权重,并假设它们都等于 1。)那么,这有什么用呢?因为这样,我们可以将代数问题转化为图论问题。因此,我们可以使用图论的强大工具集。

8.2 图表示法的好处

让我们来讨论图表示法的具体优势。首先,矩阵的幂对应于图中的行走。假设,对于任意的,A = (ai,j)ni,j=1 ∈ ℝn×n 。它的平方记为 A2 = (a(2))ni,j=1 ∈ ℝn ×n i,j ,其中元素  (2) ai,j 的定义为

 n a(2) = ∑ a a . i,j i,k k,j k=1

(注意,a[i,j]^((2)) 上标中的(2)不是指数,它只是一个索引,表示 a[i,j]^((2)) 是 A² 的元素。)

图 8.5 显示了矩阵平方和其图的元素:所有可能的两步行走都包含在定义 A² 元素的和中。

PIC

图 8.5:矩阵的幂描述了其有向图上的行走

这个连接性有更多的内容;例如,它让我们深入理解非负矩阵的结构。为了更好理解这一点,我们来讨论强连通分量的概念。

8.2.1 图的连通性

直观地说,我们可以将连通性视为从一个节点到达其他所有节点的能力。为了形式化这一点,我们需要几个定义。首先是“从其他节点到达每个节点”这一部分。

定义 31.(图上的行走)

令 G = (V,E,w) 为一个加权有向图。序列 v[k[1]]v[k[2]]…v[k[l]] 是 G 上的(有向)行走,当且仅当对所有 i,(v[k[i]],v[k[i+1]]) ∈E。

(为了保持一致性,我们定义了加权有向图的行走,但该定义同样适用于简单图——即没有边或没有方向的图——大多数即将出现的概念也是如此。)

通常,我们说从 v[k[1]]v[k[2]]…v[k[l]] 开始的行走是从 v[k[1]] 出发并以 v[k[l]] 结束。

“步行”这个术语出奇地形象,因为它真正描述了在有向边上从一个节点到另一个节点的过程。然而,图论中的步行是一个定义明确的数学对象,而不仅仅是一个模糊的直觉。再次拿起笔和纸,画出一个图,并画出它的几个步行,来更好地理解这个概念。

步行和连通性有什么关系?很简单:如果你能从每个节点到达其他所有节点,那么这个图就被称为连通图。由于我们在讨论有向图,让我们在讨论中加入一些细微的差别,并给出一个正式的定义。

定义 32.

(强连通性)

设 G = (V,E,w) 是一个加权有向图。我们说 G 是强连通的,如果对于每个 u,v ∈V ,都存在一条从 u 开始到 v 结束的步行。

换句话说,如果每个节点都可以从其他任何节点到达,那么一个有向图就是强连通的。如果这一点不成立,那么这个图就不是强连通的。图 8.6 展示了这两种情况的例子。

图 8.6 还说明了,强连通性与简单图的连通性概念不完全相同。

仅仅能从 v 到达 u 还不够;你还必须能够从 u 回到 v。

图片

图 8.6:连通与强连通

现在,让我们把我们学到的知识转化为矩阵语言。与强连通图对应的矩阵叫做不可约矩阵。所有其他非负矩阵都叫做可约矩阵。稍后我们会看到为什么,但首先,这里是正式的定义。

定义 33.

(不可约和可约矩阵)

设 A ∈ℝ^(n×n) 是一个非负矩阵。

(a) 如果 A 的有向图是强连通的,那么称 A 为不可约的。(b) 如果 A 不是不可约的,那么称 A 为可约的。

让我们看一个例子!图 8.7 展示了一个不可约矩阵。

图片

图 8.7:强连通有向图及其矩阵

回到一般情况!尽管并非所有有向图都是强连通的,我们可以将节点划分为强连通分量(如图 8.8 所示)。

图片

图 8.8:强连通分量

让我们给这个图的节点标上标签,并构建相应的矩阵!如果你假设权重简单地等于 1,并且将所有的边转化为我们已经学习过的行列式形式,你将得到:

 ⌊ ⌋ |0 1 1 0 0 0| ||0 0 1 0 0 0|| | | ||1 0 0 1 0 0|| A = ||0 0 0 0 1 1||. || || |⌈0 0 0 0 0 1|⌉ 0 0 0 1 0 0

这只是一大块 1 和 0 的矩阵,但你不应该感到失望:其中有规律!通过将 A 分成块,我们可以将这个示例图的矩阵简化为更简单的形式:

 ⌊ | ⌋ 0 1 1 |0 0 0 || | || || 0 0 1 |0 0 0 || ⌊ | ⌋ || 1 0 0 |1 0 0 || A1,1|A1,2 A = ||---------|--------|| = ⌈ -----|-----⌉ , | 0 0 0 |0 1 1 | A2,1|A2,2 || 0 0 0 |0 0 1 || ⌈ | ⌉ 0 0 0 |1 0 0

其中

 ⌊ ⌋ ⌊ ⌋ 0 1 1 0 0 0 || || || || A1,1 = ⌈ 0 0 1⌉, A1,2 = ⌈0 0 0⌉ , 1 0 0 1 0 0 ⌊ ⌋ ⌊ ⌋ 0 0 0 0 1 1 A = || ||, A = || || . 2,1 ⌈ 0 0 0⌉ 2,2 ⌈0 0 1⌉ 0 0 0 1 0 0

对角块 A[1,1] 和 A[2,2] 表示的是强连通的图(即这些块是不可约的)。此外,对角线下方的块是 0。对于所有非负矩阵,这个结论都成立吗?

当然。我们来看看!

8.3 Frobenius 正常形

一般来说,我们刚才看到的块矩阵结构被称为 Frobenius 正常形。下面是它的精确定义。

定义 34.

(Frobenius 正常形)

令 A ∈ ℝ^(n×n) 为一个非负矩阵。如果它能写成块矩阵形式,则称 A 为 Frobenius 正常形。

L(U,V ) = {f : U → V | f 是线性的}

其中 A[1],…,A[k] 是不可约矩阵。

让我们反过来问这个问题:我们能否将一个任意的非负矩阵转化为 Frobenius 正常形?是的,并且借助有向图,这比单纯使用代数要容易展示得多。以下是这个著名定理的完整形式。

定理 54.(Frobenius 正常形的存在性)

令 A ∈ ℝ^(n×n) 为一个非负矩阵。存在一个置换矩阵 P ∈ ℝ^(n×n),使得 P^T AP 处于 Frobenius 正常形。

严格地说明定理 54 的证明相当复杂。然而,证明背后的思想很容易展示。因此,我们将采用一种不那么严谨但更有趣的方法。

那么,为什么 Frobenius 正常形如此重要,置换矩阵到底是什么?让我们深入了解一下。

8.3.1 置换矩阵

数学通常是从具体到抽象进行的。这就是为什么我们经常从特殊情况开始:如果我们将一个 2 x 2 矩阵与

 ⌊ ⌋ P1,2 = ⌈0 1⌉ , 1 0

一个简单的零一矩阵?通过快速计算,我们可以验证

⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌈0 1⌉ ⌈a b⌉ = ⌈c d⌉ , 1 0 c d a b ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ a b 0 1 b a ⌈ ⌉ ⌈ ⌉ = ⌈ ⌉ , c d 1 0 d c

也就是说,

  1. 它从左侧乘时会交换行,

  2. 它从右侧乘时会交换列。

从左右两侧同时乘以 P 会复合其效果:它交换了行和列,如下所示

⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ 0 1 a b 0 1 d c ⌈ ⌉ ⌈ ⌉ ⌈ ⌉ = ⌈ ⌉ 1 0 c d 1 0 b a

如下所示。(顺便说一句,这是一个相似变换,因为我们特殊的零一矩阵是它自身的逆。这并非偶然,稍后我们会详细讲解。)

我们为什么要看这个?因为在幕后,这个变换并不会改变底层的图结构,只是重新标记了它的节点!

你可以轻松地手动验证这一点,图 8.9 也进行了说明。

PIC

图 8.9:重新标记节点

在一般的 n×n 情况下也存在类似的现象。在这里,我们通过交换单位矩阵的第 i 行和第 j 行来定义所谓的转置矩阵,例如:

 ⌊ ⌋ ⌊ ⌋ |0 1 0 0 0| |1 0 0 0 0| ||1 0 0 0 0|| ||0 1 0 0 0|| || || 5×5 || || 5×5 P1,2 = |0 0 1 0 0| ∈ ℝ , P3,5 = |0 0 0 0 1| ∈ ℝ . ||0 0 0 1 0|| ||0 0 0 1 0|| ⌈ ⌉ ⌈ ⌉ 0 0 0 0 1 0 0 1 0 0

排列矩阵的两个最重要的属性是 P[i,j]^T = P[i,j] 和 P[i,j]^T P[i,j] = I。也就是说,它们的逆矩阵就是它们的转置矩阵。

与转置矩阵相乘具有相同的效果:它交换左侧的行和右侧的列。更确切地说,

  1. P[i,j]A 交换 A 的第 i 和第 j 行,

  2. 并且 AP[i,j] 交换 A 的第 i 和第 j 列。

最重要的是,相似性变换

Pi,jAPi,j

重新标记 A 的有向图中的第 i 和第 j 个节点,保持图形结构不变。

现在,关于上述排列矩阵。排列矩阵只是转置矩阵的乘积:

P = Pi1,i2Pi3,i4 ...Pi2k−1,i2k.

排列矩阵继承了它们构建块的一些特性。最重要的是,

  1. 它们的逆矩阵就是它们的转置矩阵,

  2. 与它们的相似性变换只是一个重新标记节点的过程,保持图形结构不变。

要看这一点,可以考虑

P TAP = (Pi1,i2Pi3,i4 ...Pi2k−1,i2k)TA (Pi1,i2Pi3,i4 ...Pi2k− 1,i2k) = (Pi2k− 1,i2k ...(Pi1,i2APi1,i2)...Pi2k−1,i2k), =

依次重新标记节点。(回想一下,转置矩阵的乘积顺序会发生变化,而且转置矩阵是其自身的转置。)相反,每一个节点的重新标记都等同于一个与构建良好的排列矩阵相似性变换。

为什么我们要讨论这个?因为节点的正确标记是 Frobenius 正常形式的关键。

8.3.2 有向图及其强连通分量

现在,让我们来谈谈图。我们将看到每个有向图如何分解成强连通分量。让我们看一个具体的例子:

PIC

图 8.10:一个足够复杂供我们研究的有向图

这将是我们的教材示例。给定一个节点,能到达多少个节点呢?不一定是全部。例如,对于图 8.11 中高亮的点,只有部分图形是可达的。

PIC

图 8.11:从一个起始点出发的下游节点

然而,互相可达的节点集要小得多:图 8.12 显示,在我们的示例中,它只包含两个点。

PIC

图 8.12:互相可达的节点

从代数的角度来说,“a b 是彼此可达的”是一种特殊的关系,它将节点集合划分为互不重叠的子集,使得

  1. 来自同一子集的两个节点是彼此可达的,

  2. 来自不同子集的两个节点是不可相互到达的。

这个划分的子集称为强连通分量,我们总是可以通过这种方式分解一个有向图。

PIC

图 8.13:我们示例图的强连通组件

现在,让我们把所有东西连接起来(不是以图的方式,而是以一个完整的数学方式来连接)!

8.3.3 将图形和置换矩阵结合起来

我们距离证明每个非负方阵都可以通过一个置换矩阵转换为 Frobenius 正常形只有两步之遥。以下是计划。

  1. 构建我们非负矩阵的图形。

  2. 找到强连通组件。

  3. 以巧妙的方式重新标记节点。

就这样!为什么?因为,正如我们所见,重新标记等同于通过置换矩阵进行相似变换。唯一的小问题是:巧妙的方式是什么?我来展示给你看。

首先,我们“骨架化”图形:将组件合并在一起,以及它们之间的所有边。

把每个组件视为一个黑盒子:我们不关心内部的内容,只关心它们的外部连接。

PIC

图 8.14:强连通组件

在这个骨架中,我们可以找到无法从其他组件进入的组件。这些将是我们的起始点,即零类组件。在我们的例子中,我们只有一个。

PIC

图 8.15:寻找“零类”组件

现在,事情变得有点棘手。我们通过从最远的零类组件开始,按最长路径对每个组件进行编号。

这甚至很难阅读,更不用说理解了。图 8.16 展示了这个过程。

PIC

图 8.16:组件编号

关键在于,如果你能够从第 n 类到达第 m 类,那么 n < m。最终,我们会得到类似于图 8.17 的结果。

PIC

图 8.17:已编号的组件

这定义了组件的一个顺序(如果你想精确一点,它是一个偏序)。

现在,我们为节点标记,使得

  1. 高阶类排在前面,

  2. 连续的索引如果可能,表示来自同一组件的节点。

就是这样。

PIC

图 8.18:标记节点

这里是这个特定例子中的矩阵,为了简化,使用了零和一:

⌊ | | | | ⌋ 0 1 1 |1 0 0 0 |0 0 | 0 |0 0 0 || | | | | || || 0 0 1 |0 0 1 0 |0 0 | 1 |0 0 0 || || 1 0 0 |0 0 0 0 |1 0 | 0 |0 0 0 || | ---------|-----------|----|---|-------| || 0 0 0 |0 1 0 1 |0 0 | 0 |0 0 0 || || 0 0 0 |0 0 1 0 |0 0 | 0 |0 0 0 || || | | | | || || 0 0 0 |1 0 0 0 |0 0 | 0 |0 0 0 || || 0 0 0 |0 1 1 0 |0 0 | 0 |0 0 0 || | ---------|-----------|----|---|-------| || 0 0 0 |0 0 0 0 |0 1 | 1 |0 0 0 || || 0 0 0 |0 0 0 0 |1 0 | 0 |0 0 0 || || ---------|-----------|----|---|-------|| || -0--0--0-|0--0--0--0-|0-0-|-0-|1-0--0-|| || 0 0 0 |0 0 0 0 |0 0 | 0 |0 1 0 || | | | | | | || 0 0 0 |0 0 0 0 |0 0 | 0 |0 0 1 || || 0 0 0 |0 0 0 0 |0 0 | 0 |1 0 0 || ⌈ ⌉

这样,定理 54 的证明思路就清晰了!现在,我们也终于明白了为什么不可约矩阵被称为不可约:因为它们描述的是强连通图,因此不能以有意义的方式进一步分解成更小的块。

8.4 总结

随着线性代数与图论之间关系的研究,我们的线性代数学习之旅已经结束。

在本章以及前七章中,我们学到,向量和矩阵不仅仅是存储观察值和测量数据的数据结构。向量和矩阵具有丰富而美丽的几何结构,能够同时描述数据及其变换!

首先,我们了解到,向量存在于所谓的向量空间中,它们是我们所处的三维空间的高维推广(根据一些弦理论学者的说法,可能是 26 维,但我们暂且只讨论地球上的空间)。我们可以通过范数来测量长度和距离,范数通常定义为

 ∑n ∥x ∥ = x2, x ∈ ℝn, i=1 i

或通过内积来测量角度(等等),内积通常定义为

 ∑n ⟨x, y⟩ = xiyi i=1 = cos(α)∥x ∥∥y∥.

从数学的角度来看,矩阵来源于向量空间的线性变换,即满足线性关系 f(ax + by) = af(x) + bf(y) 的函数 f : U →V 。矩阵通过代数方式表示线性变换,具体表现为

 ⌊ ⌋ ⌊ ∑n ⌋ ⌊a a ... a ⌋| x1| | i=1 a1,ixi| | 1,1 1,2 1,m ||| x2|| || ∑n a2,ixi|| ||a2,1 a2,2 ... a2,m ||| .| | i=1. | f(x) = Af x = || .. .. .. .. |||| ..|| = || .. || , ⌈ . . . . ⌉|| x || || ∑n a x || an,1 an,2 ... an,m, ⌈ n⌉ ⌈ i=1 n,i i⌉

使我们能够从几何角度推理数据变换。这在机器学习中是一个极其强大的工具。想想看:Ax 可以是回归模型、神经网络中的一层,或其他各种机器学习构建模块。归根结底,这就是我们要学习向量空间的原因:数据存在于其中,而数据变换则通过矩阵来描述。

然而,建立模型并不止步于线性代数。为了捕捉更复杂的模式,我们需要引入非线性。例如,考虑著名的 Sigmoid 函数,它由以下公式定义

σ(x) = ---1---. 1+ e− x

由σ(Ax)定义的变换(其中σ是按元素应用的)是一个简单的逻辑回归模型,使我们能够对多维特征空间进行二分类。基于这一思路,我们得到的表达式

N (x) = σ(B σ(Ax))

定义了一个两层神经网络。

所以,我们接下来的旅程将进入微积分的领域,我们将学习函数到底是什么,如何从函数构建预测模型,以及如何通过梯度下降调节参数来拟合这些模型。

让我们开始吧!

8.5 问题

问题 1. 设 G = (V,E) 是一个有向图,u,v ∈V 是其两个节点。证明如果从 u 到 v 存在一条路径,则存在一条没有重复边和重复顶点的路径。

问题 2. 设 G = (V,E) 是一个强连通有向图。证明 jEj ≥ jV j,其中 jSj 表示集合 S 中元素的个数。(换句话说,证明为了强连通,G 至少需要有与节点数相等或更多的边。)

问题 3. 设 A ∈ℝ^(n×n) 是一个不可约矩阵。A² 也不可约吗?(如果是,请证明。如果不是,请给出反例。)

问题 4. 设 A ∈ℝ^(4×4) 是一个矩阵,其定义如下:

 ⌊ ⌋ | 0 0 1 0| | 0 0 0 1| A = || || . |⌈ 1 0 0 0|⌉ 0 1 0 0

找到将 A 转换为 Frobenius 标准型的置换矩阵 P!

加入我们的 Discord 社区

与其他用户、机器学习专家和作者本人一起阅读本书。提出问题、为其他读者提供解决方案、通过“问我任何问题”环节与作者互动等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

第十一章

参考文献

  1. VanderPlas, J. (2016). 《Python 数据科学手册》。O’Reilly Media, Inc.

  2. Strang, G. (2005). 《线性代数及其应用》(第四版)。Brooks Cole.

  3. Lax, P. D. (2007). 《线性代数及其应用》。Wiley.

  4. Trefethen, L. N., & Bau III, D. (1997). 《数值线性代数》(第一版)。费城:SIAM. ISBN 978-0-89871-361-9.

  5. Axler, S. (2024). 《线性代数正确做法》(第四版)。Springer.

第二部分

微积分

本部分包含以下章节:

  • 第九章,函数

  • 第十章,数字、数列与级数

  • 第十一章,拓扑、极限与连续性

  • 第十二章,微分

  • 第十三章,优化

  • 第十四章,积分

函数

“数学家就像法国人:你对他们说的任何话,他们都会翻译成自己的语言,立刻就变成完全不同的东西。”

——约翰·沃尔夫冈·冯·歌德

是时候处理机器学习数学中的下一个大主题了:函数与微积分。我们与函数有何关系?

正如我们之前所见,预测模型无非就是一个多变量参数函数。线性回归?Ax。逻辑回归?σ(Ax)。神经网络?一系列σ(Ax)层,以及其他无数层。

但这不仅仅是对模型的描述,它还包括将模型拟合到数据。比如,对于一个简单的线性回归模型,“训练”就是最小化均方误差;也就是说,寻找

 n 1-∑ 2 argmina,b∈ℝn (axi + b − yi), i=1

其中 x[i]是训练数据,y[i]是实际标签。这是通过微分完成的,微分是人类最重要的发明之一。(我们是否可以称数学发现为发明,始终是一个持续争论的问题。)

对于一个函数 f(x),它在 x[0]处的导数定义为

f ′(x0) = lim f-(x)−-f(x0), x→x0 x − x0

描述 x 的变化率。这里有很多需要解开的谜团:那个奇怪的 lim[x→x[0]]符号是什么意思?比率f(x)−f(x0)- x−x0表示什么?我们会在适当的时候详细解答。这一切的要点是,微分是梯度下降的推动力,而梯度下降是训练神经网络的首选算法。

但事情并没有到此为止。微分有一个同样重要的对立面:积分。公式

∫ b f(x)dx a

被称为 f(x)的积分,描述了其图形下方的有符号面积。积分在实践中并不那么普遍,但它是理论中的一项重要工具。例如,概率论的一半是围绕它构建的。比如,著名的期望值就是通过积分定义的;多个重要的概率分布也是如此。

在接下来的几章中,我们将深入探讨微积分,即单变量函数的理论。为什么?因为尽管机器学习是多变量的,但我们在这里发展出的思想将为后续所有数学内容奠定基础。让我们开始吧!

第十二章:9.1 理论中的函数

每个人对于函数都有一种直观的理解。在某个时刻,我们所有人都接触过这个概念。对大多数人来说,函数就是在笛卡尔坐标系上画出的用连续线条表示的曲线。

PIC

图 9.1:显然看起来像一个函数

然而,在数学中,直觉往往会引导我们得出错误的结论。很多时候,事物的本质与你对它的思考方式之间存在差异——也就是你的心理模型。举个机器学习中实际场景的例子,考虑以下这段代码:

import numpy as np 

def cross_entropy_loss(X, y): 
    #x0022;"/span> 
    Args: 
        X: numpy.ndarray of shape (n_batch, n_dim). 
            Contains the predictions in form of a probability distribution. 
        y: numpy.ndarray of shape (n_batch, 1). 
            Contains class labels for each data point in X. 

    Returns: 
        loss: numpy.float. 
            Cross entropy loss of the predictions. 
    #x0022;"/span> 

    exp_x = np.exp(X) 
    probs = exp_x / np.sum(exp_x, axis=1, keepdims=True) 
    log_probs = - np.log([probs[i, y[i]] for i in range(len(probs))]) 
    loss = np.mean(log_probs) 

    return loss

假设你写了这个函数,它现在位于你的代码库的某个地方。根据我们的需求,我们可能会将它视为交叉熵损失,但实际上,它是 Python 语言中一个 579 个字符长的字符串,最终由解释器处理。然而,在使用它时,我们通常会运用一个将这些信息压缩成易于使用的块的心理模型,就像“交叉熵损失”这三个词一样。当我们思考高层次的过程,比如训练神经网络时,像这样的抽象帮助我们走得更远、迈得更大。

但是有时候,事情并不会按照我们的预期进行。当这个函数抛出错误并导致计算崩溃时,交叉熵损失将无法解决问题。这时,我们需要解开定义,并将一切放大来看。之前可能阻碍你思考的东西,现在却变得至关重要。

这些原则不仅对实践适用,同样对理论也适用。数学是在逻辑精确性和清晰理解之间找到平衡的艺术,这两者常常是互相矛盾的目标。

让我们回到最初的起点:数学意义上的函数。正如前面提到的,一种可能的心理模型是通过连续的线条绘制的曲线。这使我们能够直观地思考函数,并回答一些问题。然而,这种特定的心理模型也可能大错特错。

举个例子,图 9.2 描述的是一个函数吗?

PIC

图 9.2:这是一种函数吗?

尽管这条曲线是用连续的线段绘制的,但这并不是一个函数,因为存在多个值对应同一个像,这在函数中是不能发生的。为了避免以后的混淆,我们必须在讨论数学对象时,先打好基础。本章的目标是建立一个基本的词汇表,帮助我们正确理解在机器学习中所使用的对象。

9.1.1 函数的数学定义

让我们直接深入探讨函数的精确定义! (如果第一次阅读时不理解,也不用担心,我会详细解释。这是第一次遇到定义时常有的体验。)

定义 35. (函数)

设 X 和 Y 是两个集合。如果对于每个 x ∈X,最多有一个 y ∈Y 使得 (x,y) ∈f,那么子集 f ⊆X ×Y 就是一个函数。

(集合 X ×Y 表示 X 和 Y 的笛卡尔积。如果你不熟悉这个概念,可以查看附录 C。)

为了简便,我们引入符号

f : X → Y,

这就是 f 是从 X 到 Y 的函数的简写。

注意,X 和 Y 可以是任何集合。在我们遇到的示例中,这些通常是实数集或向量集,但并没有这样的限制。

为了形象地展示定义,我们可以画出两个集合,并用箭头指向 X 的元素到 Y 的元素。每个元素 (x,y) ∈f 代表一根箭头,指向从 x 到 y。

PIC

图 9.3:作为集合间箭头的函数

唯一的标准是任何 x ∈X 处最多只能有一根箭头起始。这就是为什么图 9.2 不是一个函数的原因。

将函数定义为一个子集在数学上是精确的,但非常基础。为了更有用,我们可以通过用公式定义函数来引入抽象,例如:

f : ℝ → ℝ, x ↦→ x²,

或简写为 f(x) = x²。我们大多数人在处理函数时都会这样思考。

现在我们已经熟悉了定义,我们应该了解一些函数最基本的结构性质。

9.1.2 定义域和值域

我们看到,本质上,函数是集合之间的箭头。在这一点上,我们对它们没有任何有用的了解。什么时候一个函数是可逆的?我们如何找到它们的最小值和最大值?为什么我们要关心这些?你可能有一堆问题。我们将慢慢地、一步一步地解决这些问题。

我们旅程的第一步是关注箭头起始和指向的集合。函数的生命中有两个重要的集合:它的定义域和值域。

定义 36.(函数的定义域和值域)

设 f : X →Y 为一个函数。集合

domf := {x ∈ X : there is an y ∈ Y such that f(x) = y} ⊆ X

im f := {y ∈ Y : there is an x ∈ X such that f(x) = y} ⊆ Y

分别称为 f 的定义域和值域。

换句话说,定义域是箭头起始的 X 的子集;值域是箭头指向的 Y 的子集。

PIC

图 9.4:函数的定义域和值域

为什么这很重要?首先,这些直接与函数的可逆性相关。如果你考虑“点和箭头”的心理表征,反转一个函数就像简单地反转箭头的方向一样。当我们什么时候能做这件事?在某些情况下,做这件事甚至可能无法得到一个函数,如图 9.5 所示。

PIC

图 9.5:该函数不可逆,因为反转箭头并不能得到一个定义良好的函数。

为了将函数的研究建立在坚实的理论基础上,我们引入了单射、满射和双射函数的概念。

定义 37.(满射、单射和双射函数)

设 f : X →Y 为一个任意的函数。

(a) 如果对于每个 y ∈Y,最多只有一个 x ∈X 使得 f(x) = y,则 f 是单射。(我们常说一个单射 f 是一对一函数。)

(b) 如果对于每个 y ∈Y,存在一个 x ∈X 使得 f(x) = y,则 f 是满射。(我们常说一个满射 f 将 X 映射到 Y 上。)

(c) 如果 f 同时是单射和满射,则 f 是双射。

从箭头的角度看,单射意味着值域中的每个元素最多只有一个箭头指向它,而满射意味着每个元素确实至少有一个箭头指向它。当两者都满足时,我们就得到一个双射函数,一个可以被正确反转的函数。当逆函数 f^(−1) 存在时,它是唯一的。f^(−1) ∘f 和 f ∘f^(−1) 在各自的定义域中都等于恒等函数。

让我们来看一些具体的例子!例如,

f : ℝ → ℝ, f(x) = x2

既不是单射也不是满射。(如果你一时不理解,可以稍作思考。画个图会有帮助。)

PIC

图 9.6:单射、满射和双射函数

相反,

 3 g : ℝ → ℝ, g(x) = x

它是双射和可逆的,反函数是 g^(−1)(x) = x^(1∕3)。

可逆函数表现良好,从某种角度来看,它们更容易处理。

9.1.3 函数的运算

就像数字一样,函数也有操作定义。两个数字可以相乘或相加,但你能对函数做同样的操作吗?没有任何困难,它们可以相加,并且可以与标量相乘,如下所示

(f + g)(x) := f(x)+ g(x ), (cf)(x) := cf(x),

其中 c 是一个标量。

PIC

图 9.7:函数的组合

另一个重要的操作是组合。让我们考虑一下著名的逻辑回归吧!估计器本身是通过以下方式定义的

f(x) = σ(ax + b),

其中

σ(x) = ---1--- 1 + e−x

是 sigmoid 函数。估计器 f(x) 是两个函数的组合:l(x) = ax + b 和 sigmoid 函数,因此

f(x) = σ(l(x)).

由于 σ 将 ℝ 映射到 [0,1],我们可以将 f(x) 看作 x 属于正类的概率。

图 9.7 展示了函数组合的点和箭头示意图。

再举一个例子,一个拥有多个隐藏层的神经网络实际上是多个函数的组合。每一层的输出被送入下一层,这正是函数组合的定义。

通常,如果 f : Y →Z 和 g : X →Y 是两个函数,则它们的组合形式定义为

f ∘ g : X → Z, x ↦→ f (g(x )).

注意在 (f ∘g)(x) = f(g(x)) 中,函数 g 是先应用的。应用顺序通常是一个困扰点,所以要记住这一点。

备注 8. (函数加法作为组合)

给定这些函数

f : X → ℝ, g : X → ℝ,

我们可以通过以下方式定义它们的和

(f + g)(x) := f(x)+ g(x ).

信不信由你,这又是函数组合的另一种形式。为什么?定义加法函数为

add : X × X → ℝ, add(x1,x2) = x1 + x2.

现在我们可以写出加法为

(f + g )(x) = add(f(x),g(x)).

组合是一个极其强大的工具。事实上,它如此强大,以至于给定一小组巧妙定义的构建块,“几乎所有的函数”都可以通过这些块的组合得到。(我把“几乎所有的函数”放在引号中,因为如果我们要保持数学精确性,需要进行长时间的绕道。为了保持专注,我们在这里可以稍微放松一下。)

9.1.4 函数的思维模型

到目前为止,我们已经看到,函数可以被定义为从两个集合的元素之间绘制的箭头。尽管这种定义在数学上是严谨的,但它没有为我们提供有效的思维模型来推理这些函数。正如你在我们旅程结束时必定会看到的,在数学中,关键通常是找到合适的视角来看待事物。

关于函数,最常见且有用的思维模型之一就是它们的图形。

如果 f : ℝ → ℝ 是一个将实数映射到实数的函数,我们可以通过它的图形来可视化它,图形定义为:

graph(f) := {(x,f(x)) : x ∈ ℝ }.

这一组点可以在二维平面上绘制。例如,著名的修正线性单元(ReLU)的情况。

 ( |{ ReLU (x ) = 0 如果 x <0 |( x 如果 0 ≤ x,

该图看起来是这样的。

import numpy as np 
import matplotlib.pyplot as plt 

x = np.linspace(-10, 10, 400) 
relu = np.maximum(0, x) 

with plt.style.context("/span>seaborn-v0_8-white: 
    plt.figure() 
    plt.plot(x, relu, label="/span>ReLU(x) color="/span>blue 
    plt.axhline(0, color=’black’, linewidth=0.8, linestyle="-" 
    plt.title("/span>ReLU fontsize=14) 
    plt.xlabel("/span>x fontsize=12) 
    plt.ylabel("/span>ReLU(x) fontsize=12) 
    plt.grid(alpha=0.3) 
    plt.show()

图片

图 9.8:ReLU 函数的图形

虽然通过图形识别函数是有用的,但对于更复杂的映射,这种方法并不可推广。如果函数的定义域和映像不是实数集,直观地表示它将会很具挑战性。

图片

图 9.9:作为空间变换的函数。在这里,空间的向量围绕原点旋转。

在处理神经网络时,关于函数(在此上下文中即为层)最好的思考方式是将其视为对底层特征空间的变换。一个简单的例子是二维欧几里得平面中的旋转,如图 9.9 所示。

图片

图 9.10:Albumentations 中的图像变换示例应用

图像变换提供了一组更复杂的示例。你很少将图像模糊视为空间之间的变换,但事实就是如此。毕竟,图像只是某个向量空间中的一个巨大的向量。

图像操作作为变换,由 Albumentations (albumentations.ai/) 库执行。图片来源:Albumentations:快速灵活的图像增强,作者:Alexander Buslaev, Vladimir I. Iglovikov, Eugene Khvedchenya, Alex Parinov, Mikhail Druzhinin 和 Alexandr A. Kalinin (www.mdpi.com/2078-2489/11/2/125)。

本质上,神经网络仅仅是一个变换堆栈,每一层都从前一层的输出中获取输入。如你所见,神经网络之所以特殊,是因为这些变换并非手工设计,而是从数据中学习得到的。

9.2 实践中的函数

在我们对函数的研究中,我们从集合之间的箭头出发,最终得到了像公式和图形这样的思维模型。对于纯数学目的,这些模型足够用来进行深入的研究。然而,一旦我们离开理论领域,开始将其付诸实践时,我们就必须考虑函数在编程语言中的表示方式。

在 Python 中,函数使用简单的语法来定义。例如,平方函数 square(x) = x² 可以这样实现。

def square(x): 
    return x**2

结果是一个类型为函数的对象。(在 Python 中,一切都是对象。)

type(square)
function

使用 () 操作符来调用函数。

square(12)
144

Python 以简洁著称,函数也不例外。然而,这并不意味着它们的功能受限——恰恰相反,通过巧妙使用函数,你可以实现很多功能。

9.2.1 函数操作

我们想对函数执行三种操作:组合、加法、乘法。最简单的方法是直接调用函数本身,然后回退到为数字类型定义的操作。为了看到一个例子,让我们实现立方函数 cube(x) = x³,并将其与平方函数相加、相乘或组合。

def cube(x): 
    return x**3 
x = 2 

square(x) + cube(x)    # addition
 12
square(x)*cube(x)      # multiplication
32
square(cube(x))        # composition
64

然而,存在一个主要问题。如果你再看一下函数操作,你会发现它们接受函数并返回函数。例如,组合由以下公式定义:

组合:f,g ↦→ (x ↦→ f(g(x))) ,◟-----◝◜----◞ 组合后的函数

通过将返回值传递给外部函数,我们并没有做这种事情。没有函数对象来表示这种组合。

在 Python 中,函数是第一类对象,这意味着我们可以将它们传递给其他函数并从函数中返回它们。(这是一个绝妙的特性,但如果这是你第一次遇到这个概念,可能需要一些时间来适应。)

因此,我们可以通过使用第一类函数特性来实现上面的组合函数。

def compose(f, g): 

    def composition(*args, **kwargs): 
        return f(g(*args, **kwargs)) 

    return composition 
square_cube_composition = compose(square, cube) 

square_cube_composition(2)
64

加法和乘法可以像这样完成。(它们甚至作为练习题在 9.4 节中被分配。)

9.2.2 函数作为可调用对象

标准的函数定义方式并不适合我们需要的应用:带参数的函数。想一下线性函数 ax + b 的情况,其中 a 和 b 是参数。在第一次尝试时,我们可以这样做:

def linear(x, a, b):
    return a*x + b

将参数作为参数传递似乎是可行的,但存在一些严重的潜在问题。例如,函数可能有很多参数。即使我们将参数压缩成多维数组,我们也可能需要处理几十个这样的数组。手动传递这些参数容易出错,而且我们通常需要与多个函数一起工作。例如,神经网络由多个层组成。每一层都是一个带参数的函数,它们的组合会产生一个预测模型。

我们可以通过应用经典的面向对象封装原则来解决这个问题,将函数实现为可调用对象。在 Python 中,我们可以通过为类实现魔法方法 __call__ 来做到这一点。

class Linear: 
    def __init__(self, a, b): 
        self.a = a 
        self.b = b 

    def __call__(self, x): 
        return self.a*x + self.b 
f = Linear(2, -1) # this represents the function f(x) = 2*x - 1 
f(2.1)
3.2

这样,我们可以使用属性来存储、访问和修改参数。

f.a, f.b
(2, -1)

由于可能会有很多参数,我们应该实现一个方法,将它们收集到一个字典中。

class Linear: 
    def __init__(self, a, b): 
        self.a = a 
        self.b = b 

    def __call__(self, x): 
        return self.a*x + self.b 

    def parameters(self): 
        return {"/span>a self.a, /span>b self.b}
f = Linear(2, -1) 
f.parameters()
{’a’: 2, ’b’: -1}

交互性是 Python 最有用的特性之一。在实践中,我们经常在 REPL 环境中工作,检查对象并手动调用函数。我们通常会为我们的类添加简洁的字符串表示,以应对这些情况。

默认情况下,打印一个 Linear 实例会显示一条晦涩的信息。

f
/span>__main__.Linear at 0x7c9fbef31190/span>

这并不是非常有用。除了类名和它在内存中的位置,我们没有获得任何信息。我们可以通过实现负责返回我们对象字符串表示的__repr__方法来改变这一点。

class Linear: 
    def __init__(self, a, b): 
        self.a = a 
        self.b = b 

    def __call__(self, x): 
        return self.a*x + self.b 

    def __repr__(self): 
        return f/span>Linear(a={self.a}, b={self.b})/span> 

    def parameters(self): 
        return {"/span>a self.a, /span>b self.b} 
f = Linear(2, -1) 
f
Linear(a=2, b=-1)

这看起来好多了!添加一个漂亮的字符串表示看似是一件小事,但在做机器学习工程时,这可以起到很大的作用。

9.2.3 Function 基类

我们刚刚看到的 Linear 类只是冰山一角。在机器学习中,有成百上千个函数族。我们最终会实现其中的许多,为了保持接口的一致性,我们将添加一个基类,所有其他类都将从这个基类继承。

class Function: 
    def __init__(self): 
        pass 

    def __call__(self, *args, **kwargs): 
        pass 

    def parameters(self): 
        return dict()

有了这个,我们可以以以下方式实现函数和函数族。

import numpy as np 

class Sigmoid(Function):            # the parent class is explicitly declared 
    def __call__(self, x): 
        return 1/(1 + np.exp(-x)) 

sigmoid = Sigmoid() 
sigmoid(2)
np.float64(0.8807970779778823)

即使我们还没有为 Sigmoid 类实现 parameters 方法,它还是继承自基类。

sigmoid.parameters()
{}

现在,让我们保持基类尽可能简单。在本书的过程中,我们会逐步增强 Function 基类,以涵盖神经网络及其层所需的所有方法。(例如,梯度。)

9.2.4 面向对象的组合方式

回想一下我们在处理纯 Python 函数时是如何进行函数组合的(在第 9.2.1 节中)?从语法上讲,这也可以与我们的 Function 类一起使用,尽管有一个巨大的问题:返回值不是 Function 类型。

composed = compose(Linear(2, -1), Sigmoid())
composed(2)
np.float64(0.7615941559557646)
isinstance(composed, Function)
False

这种组合方式并没有继承我们需要的接口。

composed.parameters()
--------------------------------------------------------------------------- 
AttributeError                          Traceback (most recent call last) 
Cell In[33], line 1 
---->/span> 1 composed.parameters() 

AttributeError: ’function’ object has no attribute ’parameters’

为了解决这个问题,我们将函数组合实现为 Function 基类的一个子类。回忆一下,组合是一个函数,接受两个函数作为输入并返回一个函数:

compose : f, g ↦→ (◟x-↦→-f◝(g◜-(x-)))◞ . 组合后的函数

记住这一点,这就是我们如何进行组合的方式。

class Composition(Function): 
    def __init__(self, *functions): 
        self.functions = functions 

    def __call__(self, x): 

        for f in reversed(self.functions): 
            x = f(x) 

        return x

(注意,由于组合是这样定义的,我们以相反的顺序遍历函数列表。这是因为 (f ∘ g)(x) = f(g(x)),我们首先应用 g。)

composed = Composition(Linear(2, -1), Sigmoid()) 
composed(2)
np.float64(0.7615941559557646)

通过这种方式,我们可以保持 Function 接口。

composed.parameters()
{}
isinstance(composed, Function)
True

9.3 小结

现在我们已经了解了函数的真正含义,我敢打赌你的世界有些动摇了。函数作为由连续线条绘制的图形,当然可以。也许甚至是像 f(x) = x² + 1 这样的表达式。但作为点和箭头的函数呢?

出人意料的是,点和箭头表示法最接近真实定义。图表和表达式紧随其后。这是我们在本章学到的内容,到目前为止,我感觉自己像个魔术师。我向你展示了数学对象,并揭示了它们深藏的本质并非你所想象的那样。我们用向量、矩阵,现在又用函数做到了这一点。

除了“幕后秘密”的技巧外,将理论付诸实践也成为我们的一种惯例。因此,我们使用面向对象的 Python 来了解函数的实现。

接下来,我们将把目光转向更高层次的视角。对于我们来说,最重要的函数将数字映射到数字。但是,什么是数字?即使在编程语言中,我们也有几种不同的数字类型,如整数、浮点数、双精度数等等。这些深深植根于数学中,熟悉数字结构对每个开发者和工程师都是必需的。

下一章见!

9.4 问题

问题 1. 这些函数中哪些是单射、满射或双射?找到双射函数的反函数。

(a) f : ℝ → (0,∞),x→e^x (b) g : ℝ → [0,∞),x→x² (c) h : [0,∞) → [0,∞),x→x² (d) sin : ℝ → [0,1],x→sin(x) (e) tan : ℝ →ℝ,x→tan(x),

问题 2. 找到一个函数 f : ℝ →ℝ,使得 (f ∘f)(x) = −x。

问题 3. 任何实函数 g : ℝ →ℝ 都能通过 g = f ∘f 的形式得到吗?

问题 4. 在函数组合章节 9.2.1 的示例之后,实现

  • 加法函数,接受 f 和 g,并返回 f + g,

  • 乘法函数,接受 f 和 g,并返回 fg,

  • 以及除法函数,接受 f 和 g,并返回 f∕g。

加入我们的 Discord 社群

与其他用户、机器学习专家和作者一起阅读这本书。提问,为其他读者提供解决方案,通过问答环节与作者交流,以及更多内容。扫描二维码或访问链接加入社群。packt.link/math

PIC

数字、数列和级数

“这就像问,为什么路德维希·范·贝多芬的《第九交响曲》那么美。如果你看不出来为什么美,那么没人能告诉你。我知道数字是美的。如果它们不美,那么没有什么是美的。”

— 保罗·厄尔德什

当我从高中直接进入大学准备上第一堂数学分析课时,我曾想,为什么我们要花几节课讲实数。当时,我对自己的知识很有信心,认为我知道什么是数字。这是我第一次痛苦地遇到Dunning–Kruger 效应:你知道得越少,信心越大。可以说,几节课之后,我对数字感到困惑,花了些时间才最终理解它们。

如果你用放大镜看数字,它们会变得非常复杂。在这一章中,我们将理清这些概念。为了前瞻性地看待并关注机器学习,考虑到梯度下降(你知道的,那个到处都在用的优化算法)对于不可微分的函数是无法应用的。反过来,函数 f 在 x 点可微分,当且仅当极限

lim f(x)−-f-(y)- x→y x− y

极限存在。要理解极限,我们必须首先理解实数。

另一个深入探究数字模式和结构的好理由:它们很美(正如保罗·厄尔德什所说,他是有史以来最伟大的数学家之一)。从深层次理解那些看似熟悉的事物是有一种特殊的乐趣的。即使你可能并不每天都用到这些知识,它仍然能教会你对工作中遇到的对象的全新视角。

所以,让我们开始吧!

第十三章:10.1 数字

有五个著名的数字类别,任何想要精通数学的人都必须知道:

  • 自然数,用符号ℕ表示,

  • 整数,用符号ℤ表示,

  • 有理数,用符号ℚ表示,

  • 实数,用符号ℝ表示,

  • 最后是复数,用符号ℂ表示。

这些类别是按顺序递增的,也就是说,

ℕ ⊆ ℤ ⊆ ℚ ⊆ ℝ ⊆ ℂ.

在这一节中,我们将关注前四个自然数。(复数将有自己的章节。)

10.1.1 自然数和整数

自然数被简单地定义为

ℕ := {1,2,3,...}.

有时候 0 被包含在内,有时候不包含。信不信由你,经过几千年的讨论,数学家们仍然无法决定 0 是否是自然数。这个问题听起来可能有些滑稽,但相信我,我见过一些资深教授几乎因为讨论这个问题而打起来。对某些人来说,这是一个宗教性的问题。

我其实不太在意,你也不必太在意。我建议使用更常见且实用的定义,也就是不包含 0 的定义。当我们真正需要讨论包括 0 在内的自然数时,我会使用符号ℕ[0] = {0,1,2,…}。

自然数集合的基数是可数的无限大。实际上,可数性定义为 jℕj。(如果你不熟悉基数的概念,请参见附录 C。)

为了能够表达负数和零的数量,我们将自然数扩展为整数集合,定义如下:

ℤ = {...,− 2,− 1,0,1,2,...}.

到目前为止,一切顺利。整数也是可数的:我们可以通过枚举它的所有元素来实现这一点。

0,1,− 1,2,− 2,3,− 3,....

整数相较于自然数的一个重要优势是它们包含每个元素的加法逆元。简单来说,如果 n ∈ℤ,那么 −n 也 ∈ℤ。这样我们就能够在整数上定义各种代数结构,从而为我们提供数学工具来推理与这些现象相关的模型。

请注意,如果 n, m ∈ℤ,那么 n + m ∈ℤ。在数学术语中,我们说 ℤ 对加法封闭。

总结一下,ℤ 是

  • 对加法封闭,

  • 并且每个元素都有加法逆元。

这两个性质将指导我们如何从自然数扩展到实数。每个扩展的构造方式都确保这两个性质成立,但在不同的运算下有所不同。

10.1.2 有理数

因此,我们通过向 ℕ 中添加 0 和每个元素的加法逆元来得到 ℤ。那么,乘法逆元呢?这个想法引出了有理数的概念,即可以表示为两个整数的比率的数字。它的定义是:

ℚ = {p-: p,q ∈ ℤ,q ⁄= 0}, q

它对乘法封闭,并且每个元素(除 0 外)都有乘法逆元。这不仅仅是为了数学构造而构造的艺术:有理数描述了我们周围的各种量。例如“7.9 秒”到“100.0 公里/小时”,78.4 公斤需要搬运,0.5 个披萨要吃。你明白了。

这可能令人惊讶,但 ℚ 也是可数的。

证明这一点的一个简单方法是注意到它可以作为可数集合的可数并集获得:

 ⋃ ℚ = {p-: q ∈ ℤ ∖{0}}. p∈ℤ q

(如果你不熟悉基本的集合运算,如并集和差集,请参见附录 C。)

由于可数集合的并集仍然是可数的,因此 ℚ 也是可数的。另一种(或许更加直观)的方法是简单地将它们按顺序枚举,如图 10.1 所示。

PIC

图 10.1:有理数的枚举,其中箭头表示顺序

有理数可以写成小数形式,例如 1 2 = 0.5。一般来说,以下是成立的:

定理 55.

任何有理数 x 都可以表示为:

(a) 有限小数

x = x0 ...xk.xk+1 ...xn, xi ∈ {0,1,2,...,9}

(b) 或者是重复小数

x = x0...xk.xk+1 ...x˙n ...x˙m, xi ∈ {0,1,2,...,9},

其中,两个点之间的小数部分会无限重复。(这也可以是一个单一的数字。)

请注意,小数表示法不是唯一的:例如,1.0 0.˙9 是相等的。

上述定理完全描述了有理数。那么,具有无限不重复小数形式的数呢?

就像著名的数学常数 π 描述单位圆的半周长一样,

π = 3.14159265358979323846264338327950288419716939937510...,

这些数没有重复的模式。它们叫做无理数,和有理数一起构成了实数。

10.1.3 实数

想象实数的最简单方式是将它们表示为一条线,其中每个点代表一个数字。

PIC

图 10.2:实数线

如果我们暂时忽略数学的严格性,可以说

ℝ = 有限小数∪有限循环小数∪无限不循环小数

实数是我们旅程中遇到的第一个不可数的集合,我们将证明这一点!它的证明如此美丽,以至于它应该出现在《数学之书》中——这本书收录了最优雅、最美丽的数学证明。

定理 56\。

ℝ 是不可数的。

证明。为了证明 ℝ 是不可数的,我们采取间接方法:假设它是可数的,并推导出矛盾。这个方法叫做间接证明,是数学家工具箱中的一项高级工具。

由于 0,1) ⊆ ℝ,只需证明 [0,1) 是不可数的。如果它是可数的,我们可以列举出它的元素:

![[0,1) = {a1,a2,...}. 我们可以写出这些数的十进制形式:a = 0.a a a ..., 1 11 12 13 a2 = 0.a21a22a23..., a3 = 0.a31a32a33.... . ..

让我们聚焦于对角线!通过改变这里的数字,我们定义

 ( |{5, 如果 ann ⁄= 5, ˆann := |(1, 如果 ann = 5.

这个数字可以表示为

aˆ:= 0.ˆa11ˆa22ˆa33...

是否可以在序列 {a[1],a[2],…} 中找到?不可以,因为对于所有 i ∈ ℕ,a[i] 和 â 的第 i 位小数必须不同!我们通过改变 a[i] 的第 i 位小数构造了 â。

总结一下,我们假设 0,1) 可以被列举出来的假设导致了矛盾,因为我们发现了一个不可能出现在列举中的元素。因此,[0,1) 是不可数的,进而 ℝ 也是不可数的。这就是我们需要证明的!

你刚才看到的证明方法叫做康托尔对角线论证。这是一个美丽且强大的思想,尽管我们以后不会再遇到它,但它是证明几个困难定理的关键。(比如哥德尔著名的不完备性定理,实质上是说公理系统要么是表达不充分的,要么是自相矛盾的。这些定理在 20 世纪初给数学的运作带来了巨大冲击。)

请注意,实数打破了我们之前观察到的模式。整数是通过扩展自然数并引入加法逆元来构造的,并且对加法封闭;有理数则是通过类似的方式获得的,不过是针对乘法进行封闭。正如我们稍后所见,实数遵循类似的过程:我们通过对有理数进行极限封闭来得到它们。

但是极限是什么呢?让我们通过研究序列来看看——序列为极限提供了上下文!

10.2 序列

序列是数学的核心所在。序列及其极限描述了长期行为,比如梯度下降(偶尔)收敛到局部最优点。根据定义,序列是数学对象的列举。

序列的元素可以是任何数学对象,比如集合、函数或希尔伯特空间。(这些是什么呢?)对于我们来说,序列由数字组成。我们正式地表示它们为

![ ∞ {an }n=1, an ∈ ℝ.

为了简便,通常省略下标和上标,所以如果你看到{a[n]},不用惊慌,它只是一个缩写。(或者是 a[n]。数学家喜欢使用缩写。)如果序列的所有元素都属于集合 A,我们通常写作{a[n]}⊆A。

序列也可以是双向的。这些序列表示为{a[n]}[n=−∞]^∞。我们目前不需要它们,但它们在概率分布的上下文中经常会出现。

有时我们不需要整个序列,只需要一个子序列。我们暂时不会对它们做任何特别的处理,但这是它们的正式定义。

定义 38.(子序列)

设 a[nn=1]^∞,并且设 n[kk=1]∞⊆ℕ为一个严格递增的自然数序列。那么,序列{a[n[k]]}[k=1]∞是{a[n]}的一个子序列。

可以把它想象成把元素从序列中丢弃。

10.2.1 收敛

序列的一个重要方面是它们的渐近行为,换句话说,就是它们在长期中的表现。我们常常关注的一个特性是收敛性。用通俗的语言来说,序列{a[n]}收敛到 a,如果无论我们定义一个多小的区间(a−𝜀, a + 𝜀)(其中𝜀可以非常小),最终所有{a[n]}的元素都会落入其中。

以下是收敛的数学精确定义。

定义 39.(序列的收敛性)

序列{a[n]}⊆ℝ说是收敛到某个 a ∈ℝ,如果对于每个𝜀/span>0,都存在一个截止索引 n[0] ∈ℕ,使得

|an − a| <𝜀

对所有的索引 n/span>n[0]成立。值 a 被称为{a[n]}的极限,我们写作

lni→m∞ an = a

或者

an → a (n → ∞ ).

请注意,截止索引 n[0]依赖于𝜀。我们可以写作 n0 来强调这种依赖关系,但我们很少这么做。为了避免一直提到并命名截止索引 n[0],我们通常简单地说“对于所有足够大的 n,这个属性成立。”(我提过数学家喜欢缩写吗?)

用通俗的语言来说,这个定义意味着无论你定义一个多小的区间来包含 a,序列的所有成员最终都会落入这个区间。

尽管在数学上非常精确且正确,但这个定义并没有提供很多工具来判断一个序列是否收敛。首先,我们必须构造极限 a,然后构建截断索引。例如,考虑 a[n] := 1 n

为了简化我们的工作,我们可以绘制图形来可视化这种情况。

import numpy as np 
import matplotlib.pyplot as plt 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(8, 5)) 
    plt.scatter(range(1, 21), [1/n for n in range(1, 21)]) 
    plt.xticks(range(1, 21, 2)) 
    plt.title("/span>the 1/n sequence 
    plt.show()

PIC

图 10.3:1∕n 序列

在这里,我们可以显式地为每个 𝜀 构造截断索引 n[0]。因为我们希望有

1 --< 𝜀, n

我们可以重新组织不等式以得到

1- 𝜀 <n.

所以,

n0 := ⌊ 1⌋+ 1 𝜀

这就能解决问题。

在这个例子中,我们比较容易解决问题,但这基本上是我们能够用定义解决的问题的极限了。例如,如何证明

 1 1 1 an := (-+ ----- + ⋅⋅⋅+ ---)−1 n n + 1 2n

仅使用定义来处理吗?不行。

对于此问题,存在更为先进的工具,正如我们将看到的那样。(顺便提一下,lim[n→∞]a[n] = -1- ln 2)。对于递归定义且没有解析公式的序列,例如

{L (⃗wn,⃗x,⃗y)}∞n=1,

其中 L 是神经网络的损失函数,带有权重 ⃗w[n] 和训练数据 (⃗x,⃗y),我们遇到的情况更为复杂。现在不必担心它们,让我们一次只关注一个问题。

10.2.2 收敛性质

本质上,研究特定序列的收敛性就是将其分解为越来越简单的部分,直到知道极限为止。

  1. 这是一个“著名的”序列,已知其极限吗?如果是,我们就完成了。如果不是,进入下一步。

  2. 你能将它分解成更简单的部分吗?如果可以,它们的收敛性是否已知?如果收敛性未知,你能进一步简化吗?

我们之所以能这样做,是因为收敛具有一些特别好的性质,正如下述定理所总结的那样。

定理 57. (收敛性质)

设 {a[n]} 和 {b[n]} 是两个收敛序列,且

ln→im∞ an = a and nli→m∞ bn = b.

那么:

(a)

 lim (an + bn) = a + b, n→ ∞

(b)

 lim can = ca for all c ∈ ℝ, n→ ∞

(c)

nli→m∞ anbn = ab,

(d) 如果 a[n]≠0 且 a≠0,则

 1-- 1- nli→m∞ an = a .

性质(a)和(b)合起来被称为收敛的线性性质。

正如我们稍后将看到的,函数的连续性也为研究序列的收敛性质提供了一个很好的工具。事实上,连续性不过是极限和函数的可交换性:

lnim→∞ f(xn) = f(nl→im∞ xn).

收敛序列的一个重要性质是,在某些情况下,它们保持不等式。这对于函数极限也同样成立,因此对我们来说非常重要。

定理 58. (传递原理)

设 {a[n]}[n=1]^∞ 是一个收敛序列。如果对于所有 n ∈ℕ,a[n] ≥α,其中 α ∈ℝ 是某个下界,那么 lim[n→∞]a[n] ≥α。

证明。我们将通过间接方式来证明。如果 lim[n→∞]a[n] = α,那么根据收敛的定义,

|a[n] −a|/span> |a−α| 2 对所有足够大的 n 成立。这意味着这些 a[n] 实际上低于 α,与我们的假设矛盾。

这个证明如果画图并可视化发生了什么,会很容易理解,因此我鼓励你这样做。如果我们将上式中的 ≥ 替换为 ≤,并且证明的措辞保持不变,得到的结果是一样的。

注意,如果对于所有的 n 都有 a[n]/span>α,lim[n→∞]a[n]/span>α 并不保证!最好的例子是 a[n] := 1∕n,它收敛到 0,尽管它的所有项都是正数。

作为推论,我们得到了一个工具,它将对证明特定序列的收敛性非常有用。

推论 3.(挤压原理)

{an} ∞n=1 {bn}∞n=1 {cn}∞n=1 是三个序列,使得对于所有足够大的 n ,有 an ≤ bn ≤ cn 。如果

lim an = lim cn = α, n→∞ n→ ∞

那么

lnim→∞ bn = α.

换句话说,将 {b[n]} 夹在两个具有相同极限的收敛序列之间,意味着 b[n] 收敛到该共同极限。

10.2.3 知名收敛序列

因为收敛在某些操作下表现得很好(见第 10.2.2 节),我们通过将序列分解为构建块来研究它们。让我们看看哪些最重要的构建块将对我们后续的研究有所帮助!

示例 1. 对于任意 x ≥ 0,

L(U,V ) = {f : U → V | f 是线性的}(10.1)

如果你想一下,这很容易看出来。x = 0 和 x = 1 的情况是显然的。至于其他情况,因为取对数将指数运算转化为乘法,所以我们有 log x^n = nlog x。因此,

 ( |{ − ∞ 如果 0 <x <1, lim nlogx = n→ ∞ |( ∞ 如果 x >1.

由于对数是递增且可逆的,(10.1)成立。

示例 2. 对于任意 x ≥ 0,

L(U,V ) = {f : U → V | f 是线性的}(10.2)

类似于前面的例子,这可以通过对数的运用来证明。

示例 3. 让我们考虑以下序列

a = log n, b = n, c = 2n, d = n!. n n n n

你能按照增长速度对它们进行排序吗?这在计算机科学中非常重要,因为这些可能代表时间复杂度。几乎可以说,对数时间复杂度优于线性,线性优于指数,指数优于阶乘。换句话说,

 logn n 2n lim -----= lim -n-= lim ---= 0. n→ ∞ n n→ ∞ 2 n→ ∞ n!

示例 4. 我们刚刚学到 n 的增长速度快于 log n。从中可以推得

 lim n√n-= 1, n→ ∞

一个令人惊讶的结果(至少,对于年轻的我来说是惊讶的)!为了证明这一点,我们应用那个老生常谈的对数技巧:

 √n-- 1 lnim→∞ n = nli→m∞ nn 1n = nli→m∞ elogn logn = nli→m∞ e n = e0 = 1.

10.2.4 收敛性在机器学习中的作用

收敛性无处不在。你现在才刚刚接触到这个概念,可能还没有意识到它的重要性。但它在数学和机器学习中是至关重要的。

先来预览并给出几个例子,微分是通过极限来定义的:

f ′(x) := lim f-(x-)−-f(y). y→x x − y

关于导数,积分(微分的“逆”运算)是收敛序列的极限。例如,

∫ 1 2 ∑n k2- 0 x dx = lni→m∞ n3. k=1

因为积分是极限,所以用积分计算的每一个量也是极限,比如标准正态分布的期望值,

 ∫ ∞ 𝔼[𝒩 (0,1)] = x √1--e−x2∕2dx. −∞ 2π

收敛性在概率论和统计学中也至关重要。

有两个著名的定理:大数法则,表明

 ∑n lim 1- Xk = μ n→∞ nk=1

公式成立;还有中心极限定理,它说

√ --X1 + ⋅⋅⋅ + Xn 2 n(------n------ − μ) → 𝒩 (μ, σ ) (n → ∞ )

在分布中,对于独立同分布的随机变量 X[1],X[2],…,它们有有限的期望值 𝔼[X[i]] = μ 和方差 var(X[i]) = σ²。它们在机器学习和神经网络中非常重要;例如,大数法则是随机梯度下降法背后的一个基本思想。

即使是梯度下降优化过程也是一个递归定义的模型权重序列,它会收敛到一个最优点,在这个点上模型最符合数据。

我们将详细讨论所有这些内容。所以,即使你现在还不理解这些,也不用担心。很快就会明了。在结束序列部分之前,我们将讨论当一个序列不收敛时会发生什么。

10.2.5 发散序列

我们已经讨论过收敛序列无处不在,它们是数学和机器学习的核心。然而,并非所有序列都是收敛的。

试想一下以下例子:

a := sin(n ). n

当绘制出来时,它看起来是这样的。

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(8, 5)) 
    plt.scatter(range(1, 21), [np.sin(n) for n in range(1, 21)]) 
    plt.xticks(range(1, 21, 2)) 
    plt.title("/span>the sin(n) sequence 
    plt.show()

PIC

图 10.4:sin(n) 序列

尽管证明起来困难,但这个序列是不会收敛的。它的值在区间 [ − 1,1] 内不断震荡。我们称这种不收敛的序列为发散序列。在这些序列中,有一种特殊的发散类型:趋近于无穷大。

定义 40.(∞-发散)

序列 {a[n]} 被称为 ∞-发散的,如果对于每一个任意大的数 x,都存在一个截止指数 n[0],使得

a >x n

对所有索引 n/span>n[0] 都成立。

我们用 x[n] →∞ 来表示 ∞-发散性。类似地,可以定义(−∞)-发散序列。

一个显而易见的例子是 {n} 或 {nlog n}。这些也遍布计算机科学领域:算法的运行时间与步骤数量或输入大小相关,呈 ∞-发散。

当你看到像 a[n] = O(n) 这样的表达式时,意味着存在一个常数 c,使得

0 ≤ an ≤ cn

对所有足够大的 n 都成立。

10.2.6 大 O 和小 o 符号

如果你有一些计算机科学的经验,你可能对大 O 小 o 符号有所了解。在那里,它用来表示算法的运行时间,但并不限于此。一般来说,它用于比较序列的长期行为。让我们先从定义开始,然后我会解释直觉和一些应用场景。

定义 41.(大 O 和小 o 符号)

{an}∞ n=1 {bn}∞ n=1 为两个任意的序列。我们说:

  • bn = O (an) ,如果存在一个常数 C > 0 ,使得对于所有足够大的 n ,都有 |bn | ≤ Can

  • bn = o(an) ,如果对于每个 𝜖 >0 ,存在一个截止索引 N ∈ ℕ ,使得对于所有 n >N ,都有 |bn| ≤ 𝜖an

用简单的英语来说,“b[n] 是 a[n] 的大 O”意味着 b[n] 的增长速度大致与 a[n] 相同,而“b[n] 是 a[n] 的小 o”意味着 b[n] 比 a[n] 小一个数量级。

所以,当我们说一个算法的运行时间是 O(n) 步时,其中 n 是输入大小,意味着该算法将在 Cn 步内完成。通常,我们不关心常数因子,因为从长远来看,它不会带来数量级上的差异。

10.2.7 实数是序列

现在我们已经熟悉了收敛序列的概念,我们将再次审视有理数和实数。当从 ℕ 扩展到 ℝ 时,我们选择一种运算,对其进行闭包处理,并为该运算添加逆元素。

扩展 ℕ 为所有 n ∈ℕ 的加法逆元 −n,得到 ℤ。将 ℤ 扩展为所有 n 的乘法逆元 1∕n,并对乘法进行闭包,得到 ℚ。在 ℝ 的情况下,似乎有不同的模式,但事实并非如此。在理解了收敛性之后,我们就有工具来理解为什么。

考虑以下序列:

 1 n an := (1+ n-) , n = 1,2,....

由于有理数对加法和乘法封闭,我们可以看出 a[n] 是有理数。然而,

lim (1 + 1)n = e, n→∞ n

这个著名的欧拉常数,并不是有理数。

因此,我们找到了缺失的部分:ℚ 对极限运算不封闭。所以,我们可以通过对 ℚ 进行极限运算闭包来获得实数集。

每个无理数都可以通过有理数尽可能接近地进行近似这一事实常常被低估。想一想:你能用计算机表示所有的实数吗?不行。这是因为一个简单的基数论证:浮点数的数量是有限的,但实数是不可数的。然而,某些数字(比如π或 e)在工程计算和仿真中是至关重要的。没有近似值的帮助,处理无理数将变得不可行。

说到 e:你会如何为计算目的近似它?理论上,只需取一个足够大的 n 并使用(1 + 1∕n)^n 的值就足够了。在实践中,存在几个潜在的问题:收敛可能会很慢,且对一个接近 1 的数进行大次幂运算可能会引起数值不稳定。

然而,存在一种解决方案:这种形式

 N e = lim ∑ -1 N→ ∞ n! n=0

这解决了这两个问题!从数值角度来看,加法比乘法更好。而且,由于 n! 增长得非常快,即使对于小的 n,项  1 n! 也变得可以忽略不计。因此,收敛速度很快。看看这个。

from math import factorial 

x = range(1, 21) 

e_def = [(1 + 1/n)**n for n in x] 
e_sum = [np.sum([1/factorial(k) for k in range(n)]) for n in x] 
with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(8, 5)) 
    plt.scatter(x, e_def, label="1 + 1/n) ** n 
    plt.scatter(x, e_sum, label="/span>sum approximation 
    plt.xticks(range(1, 21, 2)) 
    plt.title("/span>Approximating the value of e 
    plt.legend() 
    plt.show()

PIC

图 10.5:近似 e:定义与级数

形式为 ∑ [n=0]^∞a[n] 的表达式称为级数,它们是数学中最重要的对象之一,既在理论上也在实践中。让我们看看它们是什么,以及我们如何与它们合作!

10.3 级数

我家乡有一家很棒的披萨店,我以前经常去。那里,每个披萨都装在一个盒子里,盒子里有一张披萨优惠券。十张披萨优惠券可以兑换一份免费的披萨。这就引出了一个问题:你通过一次购买能获得多少披萨?

立即,你将得到一份披萨。你还将获得 1/10 的披萨作为优惠券,使得购买的“价值”至少为 1 + -1 10 披萨。

然而,兑换十张优惠券后,你还会得到另一张。因此,一张优惠券代表着 1- 10 + -1- 100 披萨。按照这个逻辑,我们可以得到每次购买的真实价值是:

∑∞ -1--= 1+ 1--+ -1--+ .... n=0 10n 10 100

这个数值是多少呢?为了找出答案,我们将研究无限级数,数学的一个重要支柱。无限级数(或简称级数)是这种形式的求和:

 ∞ ∑ an. n=1

将无限多个项加在一起可能看起来是一件微不足道的事,但我向你保证,情况远非如此。例如,考虑以下求和:

∞∑ (− 1)n = 1− 1 + 1 − 1+ .... n=0

一方面,

 ∞ ∑ (− 1)n = (1− 1)+ (1 − 1)+ ... n=0 = 0+ 0 + ... = 0,

另一方面,

∑∞ (− 1)n = 1 + (− 1 + 1)+ (− 1+ 1) + ... n=0 = 1 + 0+ 0 + ... = 1.

这到底是哪一个呢?是零还是一?都不是。我们将在本节中看到为什么。

10.3.1 收敛级数与发散级数

使无限级数合理的自然方法

 ∞ ∑ an n=1

是通过取所谓的部分和的极限得到的

 ∑N SN = an. n=1

这是通过以下定义来形式化的。

定义 42。

(收敛和发散级数)

{an}∞n=1 为任意实数序列。无限级数 ∑ ∞ n=1 an 定义为

∑∞ ∑N an := Nli→m∞ an. n=1 n=1

如果上述极限存在,我们称∑ ∞ n=1 an 是收敛的。否则,它是发散的。

听起来很简单。让我们来看一些例子!

示例 1. 几何级数,如下所示

∞ ∑ qn = --1--, q ∈ (− 1,1). n=0 1 − q

由于 S[N] + q^(N+1) = S[N+1] = 1 + qS[N],因此,S[N] = 1−qN+1 1−q。因此,

 (| ∞ ||| 如果 q ≤ − 1 未定义,∑ n 1-−-qN+1- { 1 q = Nli→m∞ 1 − q = | 1−-q 如果 − 1 <q <1, n=0 |||( ∞ 如果 1 ≤ q。

这就是著名的公式 ∑ [n=1]^∞1- 2n = 1 的来源。(图 10.6 也说明了这一点。)

PIC

图 10.6:几何级数收敛性的可视化证明,适用于 q = 1∕2

几何级数也是我们在入门比萨优惠券示例中看到的那个。现在,我们可以看到单次购买的值为

 ∞∑ 1 1 10 --n-= ----1-= --- n=0 10 1− 10 9

比萨饼。

示例 2. 调和级数,如下所示

∑∞ -1 = ∞. n=1n

为什么调和级数发散?要看为什么,首先注意到

2k+1−1 2k+1−1 ∑ 1- ∑ --1-- n ≥ 2k+1 n=2k n=2k -2k-- = 2k+1 1 = 2-.

因此,通过适当分组项,我们得到

 k+1 ∞∑ 1- ∞∑ 2∑ −11- n = ( n ) n=1 k=0 n=2k ∞∑ 1 ≥ -- k=02 = ∞.

如果你难以想象这一点,这里有一个图示。

xs = range(1, 41) 
an = [1/n for n in xs] 
ys = np.cumsum(an) 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(8, 5)) 
    plt.scatter(xs, ys) 
    plt.xticks(range(1, 41, 5)) 
    plt.title("/span>the harmonic series 
    plt.show()

PIC

图 10.7:调和级数

根据欧拉-麦克劳林公式,

N∑ 1- n ≈ logN + γ, n=1

其中 γ ≈ 0.5772156649… 是著名的欧拉–马歇罗尼常数。查一下。 (当省略底数时,log 表示自然对数,也常记作 ln。)

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(8, 5)) 
    plt.plot(xs, np.log(xs) + np.euler_gamma, c="/span>r linewidth=5, zorder=1, label="/span>log(x + y) 
    plt.scatter(xs[::2], ys[::2], label="/span>harmonic series 
    plt.xticks(range(1, 41, 5)) 
    plt.title("/span>the harmonic series 
    plt.legend() 
    plt.show()

PIC

图 10.8:调和级数和 log(x + γ) 函数

示例 3. 交替调和级数,如下所示

∑∞ 1 (− 1)n+1--= log2. n=1 n

出人意料的是,交替调和级数是收敛的,求和结果为 log 2。

示例 4. 巴塞尔问题。信不信由你,解多项式方程和评估无限和曾是数学家们的一种运动。最著名的其中之一就是巴塞尔问题,涉及反平方的无限和。1735 年,传奇人物欧拉证明了

 ∞ ∑ 1-- π2- n2 = 6 , n=1

这是一个令人震惊的结果。π到底在这里做什么?常数π被定义为半径为 1 的圆的半周长,而它在反平方的无限和中出现,至少是让人困惑的。(有一种解释:1∕n²这些数字是函数 f(x) = x 的(缩放的)傅里叶系数,而傅里叶系数的和总是等于该函数在区间[ −π,π]上的积分。然而,这种方法超出了本书的讨论范围。)

个人而言,∑ [n=1]^∞1 n2 = π2 6-是我微积分教授最喜欢的恒等式。那位教授我非常喜爱,以至于我最终选择了数学分析作为我的专业。曾经,他在一次事故中撞到了头。急救车到达时,急救员让他说话,目的是测试他的认知能力是否正常。所以,他开始向医护人员解释巴塞尔问题。差点就被送到创伤中心了,但幸运的是,他的妻子在场,向大家解释说他没事,他只是个数学家。

示例 5. 阿佩里常数。我们已经看过调和级数和巴塞尔问题,那么反立方和的和呢?虽然已知该级数

∑∞ 1-- n=1 n3

是收敛的,并且它的值是无理数,我们还没有得到一个封闭的解析解!

示例 6. 交替级数

∞∑ (− 1)n. n=0

这是一个发散级数的第一个例子:因为

 ( ∑N |{ 1 如果 n 为偶数 (− 1)n = n=0 |( 0 如果 n 为奇数,

极限 lim[N→∞]∑ [n=0]N(−1)n 不存在。这就是我们在引言中所说的看似矛盾的结果的解决办法:由于∑ [n=0]∞(−1)n 是发散的,结合律失效了。

示例 7. 著名的欧拉数,定义为 e = limn→∞)^n,也可以表示为无限级数

e = ∑ [n=0]^∞1- n!

目前,这超出了我们的讨论范围,但稍后我们将看到为什么这个结论是正确的。(剧透:这是所谓的泰勒级数,我们将在第十二章讨论它。)

这引出了一个问题:是否存在一种方法可以判断一个级数是否收敛?并不是所有级数的部分和都能找到封闭形式的公式。我鼓励你尝试为∑ [n=0]^N1∕n!找出一个公式,如果你不相信我。

10.3.2 级数的性质

让我们来看看级数的一些最重要的性质。首先,很明显,收敛级数的通项也应该收敛于 0。

命题 2\。

设∑ n = 1^∞a[n]是一个收敛级数。那么 lim[N→∞]a[N] = 0。

证明。因为 a[N] = ∑ [n=1]^Na[n] −∑ [n=1]^(N−1)a[n],我们得到

 N N−1 lim a = lim (∑ a − ∑ a ) N→ ∞ N N →∞ n=1 n n=1 n N N −1 ∑ ∑ = Nlim→∞ an − Nlim→∞ an ∞ n=1 ∞ n=1 ∑ ∑ = an − an n=1 n=1 = 0,

这就是我们要证明的。

命题 2 可以用来快速判断某些级数的收敛性。例如,∑ [n=1]^∞n+1- n 不可能收敛,因为 lim[n→∞]n+1- n = 1。

另一方面,注意到命题 2 的逆命题不成立:有些发散级数的通项收敛于 0。一个直接的例子是调和级数 ∑ [n=1]^∞1 n

就像数列一样,收敛级数在加法和数乘方面表现得正如我们所期望的那样。

定理 59.(收敛级数的线性)

令 ∑ n = 1^∞a[n] 和 ∑ n = 1^∞b[n] 为两个收敛级数,且令 α,β ∈ℝ 为任意实数。那么,

 ∞ ∞ ∞ α ∑ a + β ∑ b = ∑ (αa + βb ). n n n n n=1 n=1 n=1

证明是定理 57 的直接推论。可以通过亲自推导来进行练习!

注意,定理 59 不适用于发散级数。例如,

∑∞ n n+1 [(− 1) + (− 1) ] = 0, n=0

然而,

∑∞ ∑∞ (− 1)n 和 (− 1)n+1 n=0 n=0

都是发散的。

级数的乘积稍微复杂一点。我们稍后会在本章讨论这个问题,我保证。

10.3.3 条件收敛与绝对收敛

让我们把怪异程度提高一点。回想一下交替调和级数 ∑ [n=1]∞(−1)(n+1)1 n,它的和是 log 2。如果我们重新排列它的项会发生什么?

与其在一个奇数项和一个偶数项之间交替,不如做一个奇数项和两个偶数项的交替。(奇数和偶数是相对于它们的索引而言的。)

这由图 10.9 说明。

PIC

图 10.9:交替调和级数的重新排列

变化虽小,但却带来了深远的影响。事实证明,通过简单地重新排列项,我们改变了和的值!查看图 10.10 了解原因。

PIC

图 10.10:交替调和级数的重新排列,解释

这是因为在级数的世界中,重新排列并不是有效的操作。然而,收敛性的概念可以被细化,使我们能够区分可重新排列的和不可重新排列的级数。

进入绝对收敛与条件收敛。

定义 43.(绝对收敛与条件收敛)

令 ∑ [n=1]^∞a[n] 为一个无穷级数。

(a) 如果 ∑ [n=1]^∞ja[n]j 收敛,那么 ∑ [n=1]^∞a[n] 被称为绝对收敛。

(b) 如果 ∑ [n=1]^∞a[n] 收敛但不是绝对收敛的,那么它称为条件收敛。

例如,几何级数 ∑ [n=0]^∞( ) − 1 2^n 是绝对收敛的,而交替调和级数 ∑ [n=1]∞(−1)n1 n 仅是条件收敛的。

如果一个级数是绝对收敛的,那么它也是收敛的。(我们跳过证明,因为其中的细节对我们来说不那么重要。)

10.3.4 重访重排

绝对收敛或条件收敛对级数的行为有着深远的影响。一个重要的例子就是它的重排性。事实证明,绝对收敛级数可以重排,而条件收敛级数则变得不稳定。

从数学角度讲,重排可以通过对索引集合的排列来形式化。

定义 44. (排列)

设 A 是一个任意集合。如果映射 σ : A →A 是双射,则称 σ 是 A 的一个排列。

这看起来相当抽象,但排列是很容易理解的。可以将索引集合看作是递增的序列

1,2,3,4,5,6,....

(它可能包括 0,甚至可能从更大的数字开始。)这个简单的排列

 (| { 2k − 1 if n = 2k, σ(n) = | ( 2k if n = 2k − 1

交换相邻的偶数和奇数,将索引集合转变为

σ(1) = 2,σ(2) = 1,σ (3) = 4,σ(4) = 3,....

对于一个一般的级数 ∑ [n=1]^∞a[n],其由排列 σ 给出的重排为 ∑ [n=1]^∞a[σ(n)]。

那么,你能重排一个收敛级数吗?如果它是绝对收敛的,毫无疑问可以。

定理 60.

∑ ∞ n=1 an 是一个绝对收敛级数,![∑∞ ∑∞ aσ(n) = an. n=1 n=1

我们在这里不打算证明这个,但本质上是,收敛级数的行为完全符合我们的预期:我们可以改变项的顺序而不影响结果。

那么,条件收敛的级数呢?我们已经看到重排可以改变其值,但情况更为有趣。接下来介绍黎曼重排定理。

定理 61. (黎曼重排定理)

设 ∑ [n=1]^∞a[n] 是一个条件收敛级数,设 c ∈ ℝ 是一个任意的实数。那么,存在一个排列 σ : ℕ →ℕ,使得

∑∞ aσ(n) = c. n=1

这非常疯狂。黎曼重排定理指出,如果你给我一个任意的实数,我可以构造一个重排,使得其值变为你给定的那个数。

我们也不会证明这个,但这里有一个直观的解释。假设你想重排级数以改变其和为 10。你先将正项按降序排列在前,当部分和超过 10 时,你继续使用负项。当部分和小于 10 时,你再次转为正值,并不断重复这个过程,直到无穷。条件收敛的性质保证了这个方法的可行性。

现在我们已经熟悉了绝对收敛与条件收敛之间的细微差别,让我们转向一个我们本应早早提出的迫切问题:我们如何知道一个级数是否收敛?

10.3.5 级数的收敛性检验

判断一个级数是否收敛是一项艰巨的任务。即使在看似最简单的情况下,比如 ∑ [n=1]^∞1n2,找到其部分和的闭式形式通常是不可能的。

那么该怎么办呢?最简单的方法是将该级数与我们熟悉的其他级数进行比较。

定理 62\。 (直接比较检验)

∑ ∞ an n=1 为一个任意级数。

(a) 如果 ∑ [n=1]^∞b[n] 是绝对收敛的,并且对于某个截断点后的所有 n,ja[n]j ≤jb[n]j,那么 ∑ [n=1]^∞a[n] 是绝对收敛的。

(a) 如果 ∑ [n=1]^∞b[n] = ∞并且 jb[n]j ≤ja[n]j 对于某个截断点之后的所有 n,则 ∑ [n=1]^∞a[n] = ∞。

这是定理 62 的一个简单应用案例:该级数

∞∑ -1-. n=1n α

对于 α >2 ,由于 n1α < 12 n ,因此该级数

∑∞ 1 -α- n=1 n

通过与...的比较收敛

∑∞ 1 --2. n=1n

另一方面,对于 α/span>1, 因为 1- nα/span>1 n, ∑ [n=1]^∞1- nα 通过与调和级数的比较是发散的。

尽管直接比较检验非常强大,但它有一个显著的缺点:你必须想出一个级数来进行比较。这并不总是简单的。因此,我们需要其他的检验来展示收敛性。我们将讨论其中的两个:根号检验和交替级数检验。

定理 63\。

(根号检验)

设 ∑ [n=1]^∞a[n] 为一个任意级数。如果存在一个正整数 N,使得

∘ ---- n |an | <1

对于所有 n ≥N,则 ∑ [n=1]^∞a[n] 是绝对收敛的。

根号检验的最重要应用案例是展示 ∑ ∞n=0 1n! 的收敛性(它的和是欧拉常数 e ,但我们目前还没有方法来计算该和)。我们很快会有一种方法,它来自一个最意想不到的地方。不过我还不想提前剧透。

关于该和。由于 lim √nn!- = ∞ n→ ∞ (相信我,它真的存在),limn → ∞ n√1n!-= 0 。因此,定理 63 的条件得到满足,从而 ∑ ∞ 1- n=1 n! 是收敛的。

根号检验的缺点是,研究 n√--- an 的行为可能出乎意料地困难。我们必须是真正的微积分忍者,才能应对根号检验所抛出的所有界限和极限。因此,我们将再看一个收敛性检验。

定理 64\。

(交替检验,也叫莱布尼茨准则)

{an}∞n=0 为一个序列,其项要么全是非负的,要么全是负的。如果

(a) ja[n]j 单调递减

(b) 且 limn →∞ an = 0

则 ∑ [n=0]∞(−1)na[n] 是条件收敛的。

就这么简单。交替检验是最容易应用的,但它仅仅意味着条件收敛。有了这个,我们终于可以看到交替调和级数 ∑ [n=1]∞(−1)(n+1)1 n 是条件收敛的。

10.3.6 级数的 Cauchy 积

为了结束这一章,我们来谈谈两个级数的积。我们已经看到,收敛级数的线性组合表现得很好,但积的情况如何呢?这比加法稍微复杂一些。这里是准确的结果。

定理 65. (梅尔腾定理)

∑ ∞ n=0 an ∑ ∞ n=0 bn 是两个收敛级数,并假设其中至少一个是绝对收敛的。那么,该级数

∑[n=0]^∞ ( ∑[k=0]^n a[k]b[n−k] ) (10.3)

是收敛的,并且

 ∑∞ ∑∞ ∑∞ ∑n ( ak)( bn) = ( akbn−k). k=0 n=0 n=0 k=0

该级数 (65) 被称为 Cauchy 积,表示 ∑ ∞n=0 an ∑ ∞n=0bn 的积。请注意,在和式 ∑n k=0akbn−k 中,每项的索引 akbn− k 的和为 n

这里没有严格的证明,而是一个直观的解释。让我们来拆解一下在求和积时发生了什么。

由于这个积是逐项计算的,我们得出

 ∑∞ ∑∞ ∑∞ ∑∞ ( ak )( bn) = (ak bn) k=0 n=0 k=0 n=0 = a0(b0 + b1 + b2 + ...) + a1(b0 + b1 + b2 + ...) + a2(b0 + b1 + b2 + ...) + ... = a0b0 + a0b1 + a0b2 + ... + a b + a b + a b + ... 1 0 1 1 12 + a2b0 + a2b1 + a2b2 + ... + ....

在逐项展开积的过程之后,项 a[k]b[l] 被排列成一个表格。在计算 ∑ [n=0]^∞a[n] 和 ∑ [n=0]^∞b[n] 的积时,我们只需将表格中的项逐行相加。

然而,我们可以通过对角线求和。通过观察这个表格

a0b0 + a0b1 + a0b2 + ... a1b0 + a1b1 + a1b2 + ... a2b2 + a2b1 + a2b2 + ...

你可以注意到对角线的和是 ∑ [k=0]^na[k]b[n−k]。因此,考虑所有这些项,我们得出

 ∞∑ ∑∞ ∑∞ ∑n ( ak)( bn ) = ( akbn−k). k=0 n=0 n=0 k=0

当然,这不是一个严格的证明,因为我们在没有展示绝对收敛的情况下使用了重新排列。

10.4 小结

直到这一章,我们的数学研究与机器学习非常接近。向量、矩阵、函数:它们都是理论与实践的基础。

然而,这一次,我们已经远离表面了。我们在实际中很少直接处理序列,但尽管表面上看起来不是这样,序列无处不在,为所有定量的事物提供了坚实的理论基础。

那么,我们学到了什么?首先,数字是什么。从自然数到实数的过渡简直是一次启示,让我们看到了数字概念的演变。但从深层次看,序列将数字的概念联系在一起。而每当我们谈论序列时,极限和收敛就会进入视野。

总结来说,梯度下降法是关于序列的极限

 ′ xn+1 = xn − hf (xn),

如果星星对齐,x[n+1] = x[n] −hf^′(x[n]) 会收敛到 f 的局部最小值。在接下来的章节中,我们的主要目标是理解 x[n+1] = x[n] −hf′(x[n])。f′(x)是什么,为什么 x[n] 会收敛到局部最小值?为了看到完整的画面,我们需要学习微分和积分,换句话说,就是微积分。

然而,要做到这一点,还有一步要走。是时候超越序列,研究函数上下文中的极限概念了!

10.5 问题

问题 1. 哪些序列是收敛的,哪些是∞发散的,或者两者都不是?

(a) a[n] = 2^(−n²) (b) b[n] = √2n-- (c) c[n] = nn+1- (d) d[n] = sin(1∕n) (e) e[n] = e(n(−1)n)

问题 2. 计算以下极限。

(a) lim -5n2+2- n→∞ 3n2−12

(b)  ( 5n2+2-) limn →∞ ln 3n2− 12

(c) limn→ ∞ √1+n2n2-

(d) limn →∞ n1sin (n )

(e) limn→∞)^n

问题 3. 哪些级数是绝对收敛、条件收敛或发散的?(使用直接比较法、根判别法和交错级数测试。)

(a) ∑ ∞ 2 n=0e− n

(b) ∑ ( ) ∞n=0(− 1 )n sin 1n

(c) ∑ ∞ -nn n=02

(d) ∑ ∞ n+1- n=1 n2

(e) ∑ [n=0]^∞--1-- 2n+n

加入我们的 Discord 社区

和其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过问我任何问题与作者互动等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

拓扑、极限和连续性

在上一章中,我们学习了所有(与我们相关的)关于数字、序列和级数的知识。这些是微积分的基础对象:数字定义序列,序列定义极限,极限定义了几乎所有我们感兴趣的量。然而,这里有一个难点。让我们往前看,看看导数的定义:

 ′ f-(x-)−-f(y) f (y) = xli→my x − y

如果你感到有些似曾相识,别感到惊讶。我们在上一章的引言中也看到了这个公式,而且我们离理解它已经更近了。我们已经学习了极限,但似乎存在一个问题:极限是通过序列来定义的,而表达式 lim[x→y]whatever(x)似乎并不是极限的定义。

那么这到底是什么呢?这是我们将在本章学习的内容,从实数的拓扑开始。我们出发吧!

第十四章:11.1 拓扑

根据剑桥英语词典,"拓扑"一词的意思是

某物的各个部分是如何组织或连接的。

从数学角度看,拓扑研究结构和空间的局部属性。

在机器学习中,我们通常关注的是像最小值和最大值这样的全局属性,但我们只有局部工具来寻找它们。一个例子是函数的导数。导数描述了切平面的斜率,正如图 11.1 所示,如果函数在导数求取的点附近发生变化,斜率并不会改变。

PIC

图 11.1:如果两个函数在某个点周围的任何小区间内相等,则它们的导数也相等。

在数学中,局部属性是通过序列和邻域来处理的。我们在上一章学习了序列,现在我们将探讨邻域的概念。

我们将关注三个基本方面:

  • 开集合和闭集合,

  • 集合中序列的行为,

  • 以及它们的最小元素和最大元素,上界和下界。

我们进行数学分析的主要目标是理解梯度下降,这是训练模型的基本工具。为了做到这一点,我们需要理解极限。为此,我们需要理解序列和实数,这将把我们带入当前所处的深奥领域。

想象一下,学习 Python 语言和学习 TensorFlow 或 PyTorch 的区别。由于我们想做机器学习,最终我们想学习一个高级框架。然而,如果我们没有掌握 Python 的基本关键字,比如 import 或 def,我们就不准备学习并有效使用这些高级工具。序列、开集合、闭集合、极限等是数学分析的基础构件,而数学分析则是优化的语言。

11.1.1 开集合和闭集合

让我们从开集和闭集的讨论开始!(在本章中,当我们提到子集或集合时,隐含假定它是在 ℝ 中。)

定义 45.(开集和闭集)

让 A ⊆ℝ 为实数的子集。

(a) 如果对于每一个 x ∈A,都存在一个 𝜀/span>0,使得 (x−𝜀,x + 𝜀) ⊆A,则 A 是开的。

(b) 如果 A 是闭的,则它的补集 ℝ ∖A 是开的。

在开始分析开集和闭集的性质之前,这里有一些关键示例,以帮助建立有用的思维模型。

示例 1. 形式为 (a,b) = {x ∈ℝ : a/span>x/span>b} 的区间是开的。通过选择任何 x ∈ (a,b) 并令 𝜀 = min{jx −aj∕2,jx −bj∕2},可以很容易地看出这一点。本质上,我们取距离最近端点的距离并将其减半。任何比距离最近端点的一半还要接近 x 的点也将位于 (a,b) 中。

示例 2. 形式为 [a,b] = {x ∈ℝ : a ≤x ≤b} 的区间是闭的。事实上,它的补集是 ℝ ∖ [a,b] = (−∞,a) ∪ (b,∞)。根据上述推理,很容易看出 (−∞,a) ∪ (b,∞) 是开集。

示例 3. 形式为 (a,b] = {x ∈ℝ : a/span>x ≤b} 的区间既不是开集也不是闭集。要看它不是开的,观察到没有一个包含 b 的区间完全位于 (a,b] 内,因为 b 是端点。出于类似的原因,它的补集 ℝ ∖ (a,b] = (−∞,a] ∪ (b,∞) 也不是开的。

上一个例子中的一个重要结论是,如果一个集合不是闭的,并不意味着它是开的,反之亦然。

我们可以通过引入邻域的概念来重新表述开集的定义。给定点 x 的邻域是包含 x 的开区间 (a,b)。使用这个术语,如果对于任何 x ∈A,存在一个包含 x 的邻域完全位于 A 内,则集合 A 是开的。从这个角度来看,开集意味着从任何点出发,仍然有“活动的空间”。

开集和闭集的最基本性质是它们在并集和交集下的行为。由于这一点适用于任何开集和闭集的集合,我们需要在此引入一种新的符号。回顾一下,如果我们有集合 A[1],…,A[n],它们的并集可以简写为

∪nk=1Ak = A1 ∪⋅⋅⋅∪ An.

交集也可以类似表示。如果我们有可数集,可以写成

⋃∞ Ak, k=1

如果我们有不可数集合的集合,我们该怎么做呢?假设 Ac = [0,c] 对所有 c ∈ (0,∞ ) 都成立。在这种情况下,我们使用表达式

⋃ Ac. c>

对于一般集合 Γ ,我们讨论由 {Aγ }γ∈ Γ 定义的集合,以及其并集/交集

 ⋃ A , ⋂ A . γ∈Γ γ γ∈Γ γ

现在,让我们来看第一个定理!

定理 66.

{Aγ} γ∈ Γ 为任意集合的集合。

(a) 如果每个 A γ 都是开的,则

 ⋃ A γ∈Γ γ

也是开的。

(b) 如果每个 Aγ 都是闭集,则

 ⋂ Aγ γ∈Γ

也是闭集。

证明。(a)假设 A[γ], γ ∈ Γ 是开集,并且设 x ∈ ∪[γ∈Γ] A[γ]。因为 x 在并集中,所以存在某个 γ[0] ∈ Γ,使得 x ∈ A[γ[0]]。由于 A[γ[0]] 是开集,因此存在一个以 x 为中心的小邻域 (a,b),使得 (a,b) ⊆ A[γ[0]]。因此,(a,b) ⊆ ∪[γ∈Γ] A[γ],这就是我们需要证明的内容。

(b)现在设 A[γ], γ ∈ Γ 是闭集。在这种情况下,德·摩根定律(定理 153)表明,ℝ ∖ (∩[γ∈Γ] A[γ]) = ∪[γ∈Γ] (ℝ ∖ A[γ])。由于每个 A[γ] 都是闭集,因此 ℝ ∖ A[γ] 是开集。正如我们之前所见,开集的并集仍然是开集。

集合的闭合性和开集性影响其在集合序列中的行为。关于这一点的第一个基本结果就是康托尔公理。

定理 67.(康托尔公理)

设 I[n] = [a[n], b[n]] ⊆ ℝ 为一列区间,满足对于每个 n ∈ ℕ,I[n+1] ⊆ I[n]。则交集 ∩[n=1]^∞ I[n] 非空。

这个看似简单的命题是实数的一个深刻特性,最终来源于它们的数学构造。例如,如果我们讨论的是 ℚ 的子集而不是 ℝ,那么康托尔公理就不成立。考虑一个从下方逼近π的有理数序列 a[n] → π,以及一个从上方逼近π的序列 b[n] → π,也就是说,

an <π <bn, an,bn ∈ ℚ.

区间 [a[n], b[n]] 的交集仅包含 π,而 π 不是有理数。因此,在有理数空间中,∩[n=1]^∞ [a[n], b[n]] = ∅,因此康托尔公理在这里不成立。

有一句古老的谚语,说的是因为一颗钉子丢失了战斗。大意是这样的:

因为缺少一颗钉子,鞋子丢失了。因为缺少一只鞋,马丢失了。因为缺少一匹马,骑士丢失了。因为缺少一位骑士,消息丢失了。因为缺少一条消息,战斗丢失了。因为缺少一场战斗,王国丢失了。所有这一切都是因为缺少一颗马蹄铁钉。

将康托尔公理看作是马蹄铁中的钉子。没有它,我们无法讨论序列的极限。没有极限,就没有梯度。没有梯度,就没有梯度下降,因此我们无法拟合机器学习模型。

11.1.2 距离与拓扑

最初,我们通过类似 (x − 𝜖, x + 𝜖) 这样的开区间定义开集。我们称一个集合为开集,如果对其中的每个点都能找到一个这样的小区间。通过抽象化,我们可以将定义重新表述为范数(定义 7)。

从这个角度看,区间 (x − 𝜖, x + 𝜖) 与一维开球是相同的。给定一个带范数 j ⋅ j 的范数空间 V,半径为 r/span>0 且以 x 为中心的球定义为

B(x,r) := {y ∈ V : ∥x − y∥ <r}.

等价地,半径为 r 的球体是与中心点距离小于 r 的所有点的集合。在欧几里得空间 ℝ^n 中,使用范数 ∥x∥ = ∘ -2---------2 x1 + ⋅⋅⋅+ x n,这与我们的直观理解相符。此内容在图 11.2 中进行了说明。

PIC

图 11.2:球体,从一维到三维

然而,在一维中,欧几里得范数简化为 ∥x∥ = jxj。因此,我们有

B (x,r) = {y ∈ ℝ : |x − y| <r} = (x − r,x+ r).

我们通常不将区间 (x −𝜀,x + 𝜀) 视为一维球体 B(x,𝜀)。然而,建立这种联系将使得以后将 ℝ 的拓扑扩展到 ℝ^n 变得容易,而这正是我们最终希望工作的地方。

利用范数和球体,我们可以用以下方式重新表述开集的定义。

定义 46.(开集,第二版)

设 A ⊆ℝ 是实数集合的一个子集。

如果对于每个 x ∈A,存在一个以 x 为中心、半径为 𝜀 的一维球体 B(x,𝜀),使得 B(x,𝜀) ⊆A,则 A 是开集。

因此,从某种意义上说,开集是由开球确定的。

11.1.3 集合与序列

闭集可以通过它们的序列来刻画。以下定理给出了闭集的等价定义,为我们提供了一种有用的思考方式。

定理 68.(用序列刻画闭集)

设 A ⊆ℝ 是一个集合。以下是等价的。

(a) A 是闭集。

(b) 如果 {a[n]}[n=1]^∞⊆A 是一个收敛序列,那么 lim[n→∞]a[n] ∈A。

证明。为了证明这两个命题等价,我们需要证明两点:即 (a) 推出 (b),以及 (b) 推出 (a)。如果你第一次阅读这个证明时觉得它很复杂,不用担心。如果你不能立即理解,建议将 A 看作一个闭区间并画一个图。你也可以跳过这个部分,因为我会在以后的每次需要时再提到这个事实。

首先,我们来看 (a) 如何推导出 (b)。假设 A 是闭集,且 {a[n]}[n=1]^∞ ⊆ A 是一个收敛序列,设 a := lim[n→∞]a[n]。我们需要证明 a ∈A,并且将通过反证法来证明。证明的思路如下:假设 a∕∈A,并推导出 {a[n]} 必定最终离开 A。

实际上,假设 a ∈ℝ ∖A。由于 A 是闭集,ℝ ∖A 是开集,因此存在一个小的邻域 (a −𝜀,a + 𝜀) ⊆ℝ ∖A。通俗来说,这意味着我们可以将 a 与 A 分开。这与 {a[n]} ⊆ A 且 a[n] → a 的事实相矛盾,因为根据收敛的定义(定义 39),最终序列中的所有成员都必须落入 (a −𝜀,a + 𝜀) 中。由于产生了矛盾,因此 a ∈A。

其次,我们将证明 (b) 推出 (a),即如果 A 中每个收敛序列的极限也在 A 中,那么该集合是闭集。我们的目标是证明 ℝ ∖A 是开集。更精确地说,如果 x ∈ ℝ ∖A,我们希望找到一个与 A 不相交的小邻域 (x −𝜀,x + 𝜀)。同样,我们可以通过反证法来证明这一点。

假设无论𝜖 > 0 有多小,我们都可以找到一个 a ∈ A∩(x−𝜖,x+𝜖)。因此,我们可以定义一个序列{a[n]}[n=1]^∞,使得 a[n] ∈A ∩ (x − 1∕n,x + 1∕n)。由于构造过程,lim[n→∞]a[n] = a,而根据前提(b)的规定,A 是闭合的,因此这意味着 a ∈A,这与假设矛盾。这就是我们需要证明的内容。

这个结果也解释了“闭集”术语的来源。闭集之所以被称为“闭集”,是因为它对极限是闭合的。

11.1.4 有界集合

从一个(非常)高层次的角度来看,机器学习可以被描述为一个优化问题。对于输入 x 和预测 y,我们考虑一个参数化的函数族 f(x,w),其中我们的参数集中在变量 w 中。

给定一组样本和观察值,我们的目标是找到这个集合的最小值。

{Loss(f(x, w), y) : w ∈ ℝ^(一个非常大的数字)} ⊆ ℝ, (11.1)

以及参数配置 w,其中最优解达到。为了确保我们的基础不缺少这一构建块,我们将花些时间研究这个问题。

定义 47. (有界集合)

设 A ⊆ℝ。

(a) 如果存在一个 m ∈ ℝ,使得对于所有 x ∈ A 都有 m ≤ x,则称 A 从下方有界。这个值 m 称为下界。

(b) 如果存在一个 M ∈ ℝ,使得对于所有 x ∈ A 都有 x ≤ M,则称 A 从上方有界。类似之前,M 称为上界。

(c) 如果一个集合从上界和下界都有界,则称其为有界的。

有界意味着我们可以将该集合包含在一个大区间[m, M]中。对于优化问题,有一些与界限相关的重要量:最小和最大元素、最小上界和最大下界。

让我们从形式化集合中最小和最大元素的概念开始。

定义 48. (最小元素和最大元素)

设 A ⊆ℝ。

(a) 如果对于每个 a ∈ A,都有 m ≤ a,那么 m ∈ A 称为 A 的最小元素。最小元素记作 m = minA。

(b) 如果对于每个 a ∈ A,都有 a ≤ M,那么 M ∈ A 称为 A 的最大元素。最大元素记作 M = maxA。

如常见,让我们看一些例子。

示例 1. 集合 A = [0,1] ∪ [2,3]既有最小元素,也有最大元素。它的最小元素是 0,最大元素是 3。

示例 2. 集合 A = {1n : n ∈ℕ}没有最小元素。0 是最大的下界,但由于它不在集合中,因此不是最小元素。

关键的结论是,最小元素和最大元素不一定存在。为了避免不便,我们需要一些始终存在的相关量。

这些将是下确界和上确界。

定义 49. (下确界和上确界)

设 A ⊆ℝ。

(a) inf A ∈ℝ是 A 的最大下界,如果对于每个下界 m 都有 m ≤ inf A。这个值叫做下确界。

(b) supA ∈ℝ是 A 的最小上界,如果对于每个上界 M,都有 M ≥ supA。这个值叫做上确界。

我们在这里不打算深入讨论,但下确界和上确界总是存在的。然而,必须注意的是,存在一个序列 {a[n]}[n=1]^∞ ⊆ A,使得 a[n] → inf A。(对上确界同样成立。)

11.1.5 紧致集

通过下确界和上确界的概念,我们可以将机器学习中的优化问题形式化,描述为(11.1):

inf {Loss(f (x, w), y) : w ∈ ℝn },

其中,这个数字表示损失函数的最小可能值,而 w 是我们模型的参数。

然而,w ∈ ℝ^n 部分存在一个显著问题。首先,我们的参数空间是高维的。在实际应用中,n 可能达到百万级别。除此之外,我们正在处理一个无界的参数空间,在这个空间中,可能根本不存在这样的最优解。最后,是否存在一个参数 w 使得下确界得以实现?毕竟,这正是我们最关心的问题。

我们可以将参数空间限制在一个闭且有界的集合上,以解决这些问题。这些集合非常常见,以至于有了专门的名称:紧致集。

定义 50. (紧致集)

集合 A ⊆ ℝ 是紧致的,当且仅当它是有界的并且是闭集。

我们喜欢紧致集。尽管它们的定义看似简单,但这两条性质对于优化有着深远的影响。目前,我们还不准备详细讨论这个问题,但实际上我们可以在紧致集上找到最小值或最大值,因为连续函数在紧致集上表现得很好。

关于紧致集有一个关键结果,它将在我们研究函数的过程中不断浮现:博尔扎诺-魏尔施特拉斯定理。

定理 69. (博尔扎诺-魏尔施特拉斯定理)

在紧致集中,每个序列都有一个收敛的子序列。

证明。设 A ⊆ ℝ 是一个紧致集,{a[n]}[n=1]^∞ 是 A 中的一个任意序列。

因为 A 是紧致的,存在一个区间 I[1] := [m, M],它包含 A 的所有元素。通过将这个区间一分为二,我们得到 [m, (m+M) / 2] 和 [(m+M) / 2, M]。至少有一个区间包含 {a[n]} 中的无限多个点;设该区间为 I[2]。重复这个过程会得到一系列闭区间 I[1] ⊇ I[2] ⊇ I[3] ……。区间 I[k] 的长度是 (M − m) / 2^(k − 1),因此这些区间最终会变得非常小。

由于这些区间的构造,我们还可以通过选择 a[n[k]] 使得 a[n[k]] ∈ I[k] 来定义一个子序列 {a[n[k]]}[k=1]^∞。

根据康托尔公理(定理 67),∩[k=1]^∞ I[k] 是非空的,所以设 a ∈ ∩[k=1]^∞ I[k]。由于 a[n[k]] 和 a 都是 I[k] 的元素,我们有:

 M − m |a − ank| ≤ --k−1--, k ∈ ℕ. 2

这表明 lim[k→∞] a[n[k]] = a,这正是我们需要证明的。

我们在这里使用的技巧叫做“捕狮术”。一个数学家如何在沙漠中捕捉一只狮子?通过将沙漠一分为二。狮子将位于其中的一半。这个区间可以不断对半切割,直到面积变得非常小。最终,狮子就会被困在其中。

现在我们理解了实数的拓扑结构以及与收敛序列的关系,我们可以继续讨论函数的极限!

11.2 极限

回顾在章节 10.2 中关于序列的内容,我们定义了收敛序列的极限。从直观上看,极限捕捉了这样一个概念:最终,所有元素都可以接近极限值到任意精度。这个概念也可以扩展到函数。

定义 51.(函数的极限)

令 f : ℝ →ℝ 为任意函数。我们说

 lim f (x ) = a x→x0

如果对于每个序列 x[n] →x[0],其中所有 n 的 x[n] 不等于 x[0],

nl→im∞ f(xn) = a

继续成立。

首先,有两件事需要注意。

  1. 函数的极限是通过序列的极限来定义的。如果所有可能的序列 {f(x[n])},其中 x[n] → x[0],都具有相同的极限,则 lim[x→x[0]]f(x) 定义为这个共同的极限。

  2. 对于无穷大发散的序列,定义 ±∞ 处的极限。

这里有一张帮助可视化该过程的图。

PIC

图 11.3:极限的说明:当 x[n] 越来越接近 0 时,f(x[n]) 越来越接近 lim[n→∞]f(x[n])

为了进一步说明函数极限的概念,下面是一些例子。

示例 1. 定义函数

 ( |{1 if x = 0, f(x) = |(0 otherwise,

如图 11.4 所示。换句话说,f(x) 在 0 处为 1,其他地方为 0。

PIC

图 11.4:f(x) 的图像

lim[x→0]f(x) 是否存在?是的。因为对于任意不为 0 的序列 x[n] → 0,极限 lim[n→∞]f(x[n]) = 0。另一方面,注意到 lim[x→0]f(x)≠f(0)。

示例 2. 对于我们的第二个例子,定义

L(U,V ) = {f : U → V | f is linear}(11.2)

这是著名的(或不太著名的)狄里希雷函数,难以想象且无法绘制图像:它在有理数时取值为 1,在无理数时取值为 0。毫不奇怪,lim[x→x[0]]D(x) 对于所有 x[0] 都不存在,因为有理数和无理数是“稠密的”:每个数 x[0] 都可以通过有理数和无理数的极限得到。

由于函数的极限是作为序列的共同极限来定义的,因此它的许多性质来自于序列。序列在运算下的行为(定理 57)决定了函数极限的行为。

定理 70.(运算与极限)

令 f 和 g 为两个函数。

(a)

xli→mx0 f(x)+ g(x) = xli→mx0 f(x)+ xli→mx0 g(x),

(b)

 lim cf(x) = c lim f(x) for all c ∈ ℝ, x→x0 x→x0

(c)

lim f(x)g(x) = lim f (x ) lim g(x), x→x0 x→x0 x→x0

(d) 如果在某小区间 (x[0] − 𝜖,x[0] + 𝜖) 内,f(x)≠0,且 lim[x→x[0]]f(x)≠0,那么

 lim -1---= -----1------. x→x0 f(x) limx →x0 f(x)

证明:这直接来自定理 57。

类似于我们在收敛序列中看到的,(a) 和 (b) 被称为极限的线性。

记得大 O 和小 o 符号(定义 41)是如何表示序列的渐近性质的吗?我们对于函数也有类似的工具。

定义 52.(函数的大 O 和小 o 符号)

设 f : ℝ →ℝ 和 g : ℝ →ℝ 为任意函数。我们说

(a) 当 x → a 时,g(x) = O(f(x)) 如果存在一个常数 C,使得对于某个 δ >0,所有 x ∈ (a −δ, a) ∪ (a, a + δ) 中都有 |g(x)| ≤ C |f(x)|。类似地,当 x →∞ 时,g(x) = O(f(x)) 如果存在一个常数 C 和一个截断数 N,使得对于所有 x > N,都有 |g(x)| ≤ C |f(x)|。

(b) 当 x →a 时,g(x) = o(f(x)) 如果对于任意 𝜖 >0,都存在一个 δ >0 使得对于所有 x ∈ (a−δ,a) ∪ (a,a + δ),都有 |g(x)| ≤ 𝜖 |f(x)|。类似地,当 x →∞ 时,g(x) = o(f(x)) 如果对于任意 𝜖 >0,都存在一个截断数 N,使得对于所有 x > N,都有 |g(x)| ≤ 𝜖 |f(x)|。

11.2.1 极限的等价定义

如果你眼光敏锐(并且有一定的数学经验),你可能已经提出了这样一个问题:对于所有序列 x[n] → x[0],证明 {f(x[n])} 的收敛性会不会很困难?

事实上,考虑函数极限时,这通常不是最方便的思考方式。另一种等价的定义是通过描述围绕给定点越来越小的邻域来表达极限。

定理 71.(极限作为误差项)

设 f : ℝ →ℝ 为任意函数,且 x[0] ∈ℝ。那么,以下等价。

(a) lim[x→x[0]]f(x) = a。

(b) 对于每个 𝜖 >0,存在一个围绕 x[0] 的小邻域 (x[0] −δ, x[0] + δ),使得对于每个 x ∈ (x[0] −δ, x[0]) ∪ (x[0], x[0] + δ),

|f (x )− a| <𝜖

成立。

证明。(a)⇒(b)。我们将采用间接证明法,因此假设(a)成立而(b)不成立。(b)的否定表明存在一个 𝜖 >0,使得对于每个 δ >0,都有一个 x ∈ (x[0] −δ, x[0]) ∪ (x[0], x[0] + δ),使得 |f(x) −a| ≥ 𝜖。(如果你不明白为什么这是否定,查阅附录 A 中关于逻辑的内容。)

现在我们定义一个序列来与 (a) 矛盾。如果我们选择 δ = 1∕n,可以让 x[n] 位于 (x[0] −δ, x[0]) ∪ (x[0], x[0] + δ) 中,使得 |f(x[n]) −a| < 𝜖,这一点由我们假设 (b) 错误所保证。由于该序列的构造,{f(x[n])}[n=1]^∞ 不收敛于 a。这与 (a) 矛盾,从而完成了我们的间接证明。

(b) ⇒ (a)。设 {x[n]}[n=1]^∞ 是一个收敛于 x[0] 的任意序列。如果 n 足够大(即大于某个截断指数 N),则 x[n] 会落入 (x[0] −δ, x[0] + δ),其中 δ >0 是任意小常数。由于 (b) 表明对于所有这样的 n 都有 |f(x[n]) −a| < 𝜖,按照收敛的定义(定义 39),我们有 lim[n→∞]f(x[n]) = a。

用简单的英语来说,这个定理表明,如果 x 足够接近 x[0],那么 f(x) 会无限接近 lim[x→x[0]]f(x)。类似于 (b) 的定义称为 epsilon-delta 定义。

还有另一个等价的定义,尽管看似微不足道,但在思考极限时,它是一个有用的思维模型。

定理 72.

设 f : ℝ →ℝ 为任意函数,x[0] ∈ℝ。则,以下是等价的。

(a) lim[x→x[0]]f(x) = a。

(b)存在一个函数 error(x),使得 lim[x→x[0]]error(x) = 0 并且

f(x) = a+ error(x).

证明。(a)⇒(b)。由于极限在操作上的行为(定理 70),很容易看出

error(x) := f(x)− a

满足要求。

(b)⇒(a)。同样,由于极限操作的线性,这一点也显而易见:

lim f (x ) = lim (a+ error(x )) = a. x→x0 x→x0

这就是我们要证明的。

通常,我们不需要知道函数的确切极限;知道极限在某个特定的上下界之上或之下就足够了。

为了给出一个具体的例子,我们将稍微提前谈一下微分。我将在下一章详细解释所有内容,但函数 f 在点 x[0] 处的导数定义为极限

 f (x )− f(x0) lx→imx0 ---x-−-x----. 0

如果函数是递增的,我们有

f(x) − f(x ) ----------0- ≥ 0, x − x0

由于我们接下来要看到的内容,这意味着导数是正的。

不再废话,让我们看看结果!

定理 73.(函数的转移原则)

设 f : ℝ → ℝ 为任意函数。如果对于所有 x ∈ (a −δ,a) ∪ (a,a + δ) 和某个常数 δ/span>,以及 α ∈ℝ 的下界,f(x) ≥ α,则当极限存在时,lim[x→a]f(x) ≥ α。

证明。由于函数极限的定义,这是收敛序列的转移原则(定理 58)的直接结果。

有几个特殊的极限经常出现在计算中。这些是计算更复杂极限的基础,因为它们通常可以简化为已知极限的形式。

我们不会在这里包含证明,因为它们对我们理解机器学习算法的目的并没有太大帮助。

定理 74.(a)

lim[x→0] sin x x = 1. (11.3)

(b)

lim[x → 0] x log x = 0. (11.4)

(c)

lim[x → ∞] x^k e^(−ax) = 0, a ∈ (0, ∞), k = 0, 1, 2, … (11.5)

随着极限从序列扩展到函数,我们看到,如果极限存在,它不一定等于函数在给定点的值。然而,当它存在时,函数就变得更容易处理。这称为连续性,接下来我们将讨论这个问题。

11.3 连续性

如果我让你从脑海中想出一个随机函数,我几乎可以确定你会想出一个既连续又可微的函数。(除非你有奇怪的口味,像许多数学家一样)。

然而,绝大多数函数既不是连续的也不是可微的。从基数的角度来看,如果你计算所有实数函数 f : ℝ→ℝ,结果表明总共有 2^c 个函数,但其中连续的子集的基数是 c。很难想象这样的数量:c 和 2^c 都是无限的,但,嗯,2^c 比 c 更无限。是的,我知道。集合论很奇怪。(回想一下,c 表示实数集合的基数。如果你想复习这个话题,可以查阅集合论附录 C。)

总的来说,正如我们将看到的,连续性和可微性使我们能够对函数进行有意义的操作。例如,通常基于梯度下降的神经网络优化方法,如果损失函数和层不是可微的,就无法工作。仅这一点就足以使机器学习的齿轮卡住,因为在该领域的深度学习部分中,这种方法被频繁使用。

本节探讨这些概念如何协同工作,并最终使我们能够训练神经网络。因此,让我们直接跳入深水区,精确定义连续性的概念!

定义 53.(连续性)

设 f : ℝ →ℝ为任意函数。我们说 f 在 a 处连续,当且仅当

lxim→a f(x) = f (a )

成立。

换句话说,连续性意味着如果 x 接近 y,那么 f(x)也会接近 f(y)。这就是我们大多数心智模型的工作方式。这也是我们希望许多机器学习模型具备的特性。例如,如果 f 是一个模型,它接受图像并判断其中是否有猫,我们希望在改变 x 的几个像素之后,预测结果保持不变。(然而,一般来说,情况并非如此,这被某些对抗性攻击所利用。)

我们可以通过展开极限来重新表述上述定义。如果你稍加思考,就会发现 f 在 a 处连续等价于

 lim f(a ) = f ( lim a ) n→ ∞ n n→∞ n

对于所有收敛序列 a[n] →a。换句话说,极限和函数应用是可以互换的。我们将非常频繁地使用这一点。

和往常一样,我们首先会看到一些例子。我们将回顾在第 11.2 节中看到的例子。

示例 1. 让我们重新回顾一下

 (| {1 if x = 0, f(x) = | (0 otherwise.

尽管 f(x)在 0 处不连续,因为 lim[x→0]f(x) = 0≠f(0),但正如我们之前所见,f(x)在其他地方是连续的(因为它是常数 0)。

注意,尽管该函数在 0 处不连续,但极限是存在的!

示例 2. 那么 Dirichlet 函数 D(x)怎么样?(见 11.2 节中的定义。)由于极限甚至不存在,它根本不是连续函数。

示例 3. 定义

 ( |{ f(x) = x if x ∈ ℚ, |( − x otherwise.

令人惊讶的是,f(x) 在 0 处连续,但在其他地方都不连续。正如你所看到的,关于连续性,几乎没有什么是不可行的。一般来说,函数可以是复杂的对象,没有一定的规律性条件,优化它们是非常困难的。本章的核心目标是理解我们如何在训练机器学习模型时优化这些函数。

最后一个例子!

示例 4. 我们称一个函数为初等函数,如果它可以通过有限次的求和、乘积和组合得到。

  • 常数函数,

  • 幂函数 x, x², x³,…,

  • n 次根函数 x^(1∕2), x^(1∕3), x^(1∕4),…,

  • 指数函数 a^x,

  • 对数函数 log [a]x,

  • 三角函数和反三角函数 sinx, cosx, arcsinx, arccosx,

  • 双曲函数和反双曲函数 sinhx, coshx, sinh^(−1)x, cosh^(−1)x。

例如,

 2 x 1-−-3x-+√-5x4 f(x) = sin(x + e )− 2x − x

是一个初等函数。初等函数在其定义域内都是连续的。这对我们来说非常有用,因为仅通过定义来展示像 f(x) 这样复杂函数的连续性是困难的。这样,如果它是初等的,我们知道它是连续的。这对多元函数(如神经网络)也同样适用。

11.3.1 连续函数的性质

数学中的典型模式,正如你在讨论收敛序列的性质时所看到的定理 57,是首先研究基本构建块的某些属性,然后展示它们在操作下的表现。

我们将按照与前一个示例类似的模式,讨论初等函数的连续性。

定理 75\。

设 f 和 g 为两个函数。

(a) 如果 f 和 g 在 a 处连续,则 f + g 和 fg 也在 a 处连续。

(b) 如果 f 和 g 在 a 处连续,且 g(a)≠0,则 f∕g 也在 a 处连续。

(c) 如果 g 在 a 处连续,且 f 在 g(a) 处连续,则 f ∘ g 也在 a 处连续。

证明:(a) 和 (b) 直接来自极限的性质(定理 70)。

为了看清 (c),我们只需令 {a[n]}[n=1]^∞ 是一个收敛到 a 的序列。然后,假设 f 在 g(a) 处连续,且 g 在 a 处连续,我们有:

lim f(g(an)) = f( lim g(an )) = f(g( lim an)) = f (g(a )), n→∞ n→ ∞ n→ ∞

这显示了 f ∘ g 在 a 处的连续性。

到目前为止,我们只定义了单点的连续性。一般来说,函数 f : ℝ →ℝ 在集合 A 上连续,当且仅当它在集合 A 的每个点上连续。

我们已经到达了一个部分解释我们为何喜爱连续函数和紧集的点。

原因很简单:在紧集上连续的函数在该集合内有界并且能达到它们的最优值。

定理 76\。

设 f 在紧集 K 上连续。存在 α, β ∈K,使得对于所有 x ∈K,f(α) ≤ f(x) ≤ f(β)。

设 {α[n]}[n=1]^∞ ⊆ K 是一个序列,使得 f(α[n]) → inf{f(x) : x ∈K}。(由于下确界和上确界的性质,这一定存在。)

现在,根据博尔扎诺-魏尔施特拉斯定理(定理 69),{α[n]}[n=1]^∞ 有一个收敛的子序列 {α[n[k]]}[k=1]^∞,且 lim[k→∞]α[n[k]] = α。由于 K 是紧的,α ∈K。(回想一下,紧集是闭集,且闭集包含其收敛序列的极限,正如定理 68 所暗示的。)

因为 f 是连续的,我们有

f(α) = f( lim αnk) = lim f(αnk) = inf{f (x) : x ∈ K }, k→ ∞ k→ ∞

这正是我们需要证明的。通过相同的论证,可以证明存在一个 β ∈K,使得对所有 x ∈K,f(x) ≤ f(β)。

这个命题对于那些不是闭合且有界的集合不成立。例如,f(x) = 1 x 在(0,1]上是连续的,但没有上界。

11.4 总结

我承认,拓扑、极限和连续性的细节可能显得复杂和抽象。然而,根据我的经验,走最难的路往往是最有收获的,尤其是在学习技术性话题时。总之,我们已经学到了

  • 什么是拓扑,

  • 它与序列和极限有什么关系,

  • 如何求取函数的极限,

  • 最后,什么是连续函数。

现在,我们已经掌握了上述所有内容,准备好攻克机器学习核心内容之一:微分。我们将研究如何分析函数以及什么样的函数“表现良好”。

如果你仔细思考机器学习的本质,你会发现从宏观角度来看,它其实非常简单。实质上,我们想做的就是:1. 设计参数化函数来解释数据和观察之间的关系;2. 找到最适合我们数据的参数。

为了找到既具有表达能力又易于处理的模型,我们需要将自己限制在满足某些性质的函数中。最重要的两个性质是连续性和可微性。既然我们已经了解了什么是连续函数,我们可以继续学习可微函数。

在接下来的章节中,我们将专门讨论单变量实函数。这样做是为了引入概念,而不一次性增加太多复杂性。在后续章节中,我们会逐步转向多变量函数,到机器学习部分时,我们将掌握它们的使用。

11.5 问题

问题 1。以下哪些 ℝ 的子集是开集、闭集,或者既不是开集也不是闭集?

(a)ℤ (b)ℚ (c)∩[n=1]^∞(−n1,1n) (d)∪[n=1]^∞0,1 −![1 n]

问题 2。设 A ⊆ ℝ 是一个任意集合。证明存在一个序列 {an} ∞n=1 ⊆ A ,使得

lnim→∞ an = sup A.

(对 inf A 也成立,并且可以通过相同的方式证明。)

问题 3。设 D(x) 为狄利克雷函数,定义为

 ( |{ D (x) = 1 如果 x ∈ ℚ, |( 0 否则。

给出一个数学上严格的证明,证明对于任何 x[0] ∈ ℝ,极限 lim[x→x[0]]D(x) 不存在。

问题 4. 设 X 为任意集合,且 τ ⊆ P(X) 为其子集的集合。(回顾一下,P(A) 表示 A 的所有子集。)结构 (X, τ) 称为拓扑空间,如果

  1. ∅∈τ 且 X ∈τ,

  2. 对于 τ 中的任何集合,其并集也在 τ 中。

  3. 对于 τ 中的任何有限集合,其交集也在 τ 中。

τ中的集合称为开集。证明以下是拓扑空间。

(a) X 是任意集合,τ = {∅, X}。 (b) X 是任意集合,τ = P(X)。 (c) (ℕ, τ),其中 τ = {S ⊆ ℕ : 0∕∈τ}。

问题 5. 设 f : ℝ → ℝ 为任意函数。证明 f 在 x[0] 处连续当且仅当对于每个 𝜖/span>0,存在 δ/span>0,使得对于任意 x ∈ (x[0] − δ, x[0] + δ),|f(x) − f(x[0])|/span>𝜖。

上述是连续性的等价定义,称为ε-δ定义。

问题 6. 设 f : ℝ → ℝ 为任意函数。证明 f 在每个点处连续当且仅当下述集合

f(X ) = {f(x) : x ∈ X }

对于每个开集 X 都是开集!

加入我们的 Discord 社区

阅读本书,与其他用户、机器学习专家以及作者本人一起讨论。提问,向其他读者提供解决方案,参加“问我任何问题”环节,与作者互动,更多精彩内容等你来参与。扫描二维码或访问链接加入社区。packt.link/math

图片

微分

我转身,充满恐惧和恐慌,远离这个没有导数的连续函数的悲惨灾难。

— 查尔斯·埃尔米特

在科学史上,很少有里程碑像发明轮子一样具有重要意义。在这些里程碑中,微分尤为突出。随着微积分的发明,牛顿创造了我们今天所知的力学。微分在科学和工程中无处不在,事实证明,它也是机器学习的关键组成部分。

为什么?因为优化!事实证明,函数的极值点可以通过其导数来表征,而这些极值点可以通过梯度下降法反复找到。

在本章中,我们将学习微分是什么,它的起源是什么,以及最重要的,如何在实践中使用它。让我们开始吧!

第十五章:12.1 理论中的微分

在直接进入数学定义之前,让我们先从一个简单的例子开始:一个沿直线运动的质点。它的运动完全由时间-距离图(图 12.1)描述,该图展示了它在某一时刻离起点的距离。

PIC

图 12.1:我们运动物体的时间-距离图

我们的目标是计算物体在某一时刻的速度。在高中时,我们学到:

距离 平均速度 = -时间---.

为了将其转化为定量形式,假设 f(t) 表示时间-距离函数,t[1] 和 t[2] 是任意的两个时间点,那么

平均速度在 t 和 t = f-(t2)−--f(t1). 1 2 t2 − t1

f(t2t2)−−ft(1t1)- 这样的表达式被称为微分商。请注意,如果物体是倒退的,则平均速度为负值。(与速度不同,速度总是正值。速度包括速率和方向。)

平均速度有一个简单的几何解释:如果你将物体的运动替换为一个恒定速度的运动,且该速度与平均速度相同,你最终将到达完全相同的位置。从图形的角度来看,这等同于用一条直线连接 (t[1], f(t[1])) 和 (t[2], f(t[2]))。

平均速度就是这条直线的斜率。图 12.2 直观地展示了这一点。

PIC

图 12.2:t[1] 和 t[2] 之间的平均速度

鉴于此,我们可以计算在某一时间点 t 上的精确速度,我们将其表示为 v(t)。这个思想很简单:如果 Δt 足够小,那么在 t 和 t + Δt 之间的小时间间隔内的平均速度应该越来越接近 v(t)。(Δt 也可以是负值。)

所以,

v(t) = lim[Δt → 0] (f(t + Δt) - f(t)) / Δt, (12.1)

如果上述极限存在。图 12.3 说明了由 (12.1) 定义的极限。在那里,我们可以看到,随着 Δt 越来越接近 0,连接 (t, f(t)) 和 (t + Δt, f(t + Δt)) 的直线的斜率也越来越接近切线的斜率。

PIC

图 12.3:在 t 时刻近似速度

根据我们的几何直觉,我们看到 v(t) 就是 f 在 t 时刻的切线斜率。记住这一点后,我们准备介绍正式定义。

定义 54.(可微性)

设 f : ℝ →ℝ 为任意函数。我们说 f 在 x[0] ∈ℝ 处是可微的,当且仅当极限

-df-(x0) = lim f(x)-−-f(x0) dx x→x0 x − x0

存在。如果存在,这被称为 f 在 x[0] 处的导数。

换句话说,如果 f 描述的是一个物体运动的时间-距离函数,那么导数就是物体的速度。

类似于连续性,可微性是局部性质。然而,我们更关注的是那些几乎到处都可微的函数。在这些情况下,导数是一个函数,通常记作 f^′(x)。

有时会让人困惑的是,x 既可以表示 f 的自变量,也可以表示求导数时的具体点。以下是一个快速的术语表,用于澄清导数和导数函数之间的区别。

  • df- dx(x[0]):f 关于变量 x 在点 x[0] 处的导数。这是一个标量,通常记作 f^′(x[0])。

  • df- dx:f 关于变量 x 的导数函数。这是一个函数,通常记作 f^′。

注释 9.(极限中的变量)

不要被符号的变化从 t 和 t + Δt 到 x[0] 和 x 所困惑;定义导数的极限与之前完全相同:

 f(x0 + h)− f(x0) f(x)− f(x0) lhim→0 -------h---------= xli→mx0 ---x−--x----. 0

另外需要注意的是

lim f(x)−-f(x0)-= lim f(x0)−--f(x). x→x0 x− x0 x→x0 x0 − x

有时,我们甚至可能使用 x 和 y 来代替 x[0] 和 x,写成

 ′ f-(x-)−-f(y) f (x) = yli→mx x − y

我们会使用更方便的表示方式。

让我们来看一些例子!

示例 1. f(x) = x。对于任意的 x,我们有

lim f(x)−-f(y)-= lim x-−-y = 1. y→x x − y y→x x − y

因此,f(x) = x 在每一点都是可微的,它的导数是常数函数 f^′(x) = 1。

示例 2. f(x) = x²。在这里,我们有

 f(x)− f (y ) x² − y² lim -----------= lim ------- y→x x− y y→x x − y (x-−-y)(x+-y) = yli→mx x − y = yli→mx x + y = 2x.

所以,f(x) = x² 在每一点都是可微的,且 f^′(x) = 2x。稍后,当我们讨论初等函数时,我们将看到更一般的情况 f(x) = x^k。

示例 3. f(x) = |x| 在 x = 0 处。对于这个例子,我们有

lim f(0)−-f-(y-)= lim |y|. y→0 0− y y→0 y

因为

 ( |{ 1 if y >0, |y|-= y |( − 1 if y <0,

这个极限不存在,如图 12.4 所示。这是我们第一个非可微函数的例子。然而,|x|在其他地方是可微的。

在这里画一张图来增强我们对可微性的理解是值得的。回忆一下,某一点的导数值等于函数图像的切线斜率。

由于 jxj 在 0 处有尖锐的拐角,切线不定义。

PIC

图 12.4:f(x) = jxj 在 0 处的切平面

可微分性意味着图形中没有尖锐的拐角,因此可微函数通常被称为光滑函数。这是我们更喜欢可微函数的原因之一:变化率是可控的。

接下来,我们将看到微分的等价定义,涉及到用线性函数进行局部近似。从这个角度看,可微分性意味着可控的行为:没有皱纹、角落或剧烈的值变化。

12.1.1 微分的等价形式

要真正理解导数和微分,我们将从另一个角度来看待它:局部线性近似。

近似是数学中的一个非常自然的概念。比如,你有没有想过当你在计算器中输入 sin(2.18) 时会发生什么?我们无法用有限次加法和乘法表达 sin 函数,因此我们必须对其进行近似。实际上,我们使用形式为的函数

p(x) = p0 + p1x+ ⋅⋅⋅+ pnxn,

这些可以很容易地计算出来。它们被称为多项式,它们只是有限次加法和乘法的组合。

我们能否仅通过将函数替换为多项式来简化计算?(即使这以失去完美精度为代价。)

事实证明我们可以这么做,微分是其中的一种方法。从本质上讲,导数描述了用线性函数进行的最佳局部近似。

以下定理使这一点变得清晰。

定理 77. (作为局部线性近似的微分)

设 f : ℝ → ℝ 为任意函数。以下是等价的。

(a) f 在 x[0] 处可微。

(b) 存在一个 α ∈ ℝ 使得

f(x) = f(x₀) + α(x − x₀) + o(|x − x₀|) x → x₀。 (12.2)

回想一下,小 O 符号(见定义 52)意味着该函数在 x[0] 附近比函数 |x − x[0]| 小一个数量级。

如果存在,上述定理中的 α 就是导数 f^′(x[0])。换句话说,f(x) 可以局部写成

f(x[0]) + f′(x[0])(x − x[0]) + o(|x − x[0]|)。 (12.3)

证明。为了证明两个陈述的等价性,我们必须证明微分意味着所需的属性,反之亦然。虽然这看起来可能复杂,但其实是直接的,并且完全取决于函数如何可以写成它们的极限加上一个误差项(定理 71)。

(a) ⇒ (b)。极限的存在

 f-(x)−-f(x0) ′ lxi→mx0 x − x0 = f (x0)

这意味着我们可以将近似切线的斜率写成

f(x) − f(x0) ′ ---x-−-x----= f(x0) + error(x), 0

其中 lim[x→x[0]]error(x) = 0。

通过一些简单的代数运算,我们得到

f(x) = f (x0 )+ f′(x0)(x − x0)+ error(x)(x − x0).

由于误差项随着 x 接近 x[0] 而趋近于零,误差(x)(x − x[0]) = o(|x − x[0]|),这就是我们想要证明的。

(b) ⇒ (a)。现在,按照我们之前所做的,只是倒过来。我们可以重写

f (x) = f (x0) + α(x − x0)+ o(|x− x0|)

形式为

f(x)−-f-(x0) x− x = α + o(1), 0

根据我们之前使用的知识,这意味着

所以,f 在 x[0] 处可微,且其导数为 f^′(x[0]) = α。

这种形式的一个巨大优势是它可以很容易地推广到多变量函数。尽管我们离此还有很长一段距离,但可以先窥见一二。多变量函数将向量映射到标量,因此比例

f(x)-−-f(x0), x,x0 ∈ ℝn x − x0

甚至无法定义。(因为我们不能用向量进行除法。)然而,表达式

f (x ) = f (x0 )+ ∇f (x0)T(x− x0)+ o(∥x − x0∥)

完全有意义,因为 ∇f(x[0])^T (x −x[0]) 是标量。这里,∇f(x[0]) 表示 f 的梯度,即导数的多变量版本。∇f(x[0]) 是一个 n 维向量。如果你不熟悉这种符号,别担心,我们会在适当的时候讲解。重点是,这种替代定义将来对我们更为方便。

局部最佳近似思想的另一个优势是,定理 77 可以推广到更高阶的导数。

请看以下定理。

定理 78. (泰勒定理)

设 f : ℝ →ℝ 是在 x[0] 处 n 次可微的函数。那么

 ∑n f(k)(x0) f (x ) = -------(x − x0)k + o(|x− x0|n) k=0 k!

成立,其中 f^((k))(x[0]) 表示 f 在 x[0] 处的第 k 阶导数。

(注意,零阶导数 f^((0)) 等于 f。)

换句话说,定理 78 表示,如果 f 可以足够多次微分,它可以表示为一个多项式加上一个小的误差项。对于无限可微的函数,泰勒定理产生了著名的泰勒展开式。

定义 55. (泰勒展开式)

设 f : ℝ → ℝ 是在 x[0] 处可无限次微分的函数。由下式定义的级数

 ∞ (k) f(x) ∼ ∑ f---(x0)(x− x )k k! 0 k=0

称为 f 在 x[0] 处的泰勒展开。

举个例子,因 (ex)′ = e^x,所以 e^x 在 0 处的泰勒展开式为

 ∞ ex = ∑ 1-xk. k! k=0

注意,等式符号并非偶然:e^x 的泰勒展开式等于 e^x!(这并非总是如此。)现在我们看到为什么 e = ∑ [k=0]^∞1k!,如我们在讨论数列与级数时所暗示的那样。

换句话说,

 x ∑n 1 k e ≈ k!x , k=0

这意味着,在任意区间 [ −α,α] 上,对于任何任意小的 𝜖/span>0,

 ∑n |ex − -1xk| <𝜖 k=0k!

当 n 足够大时,成立。实际上,这个多项式被用来近似 e^x 的值。

12.1.2 微分与连续性

如下定理所述,微分是比连续性更严格的条件。

定理 79.

可微函数是连续的。

如果 f : ℝ → ℝ 在 a 处可微,那么它在该点也是连续的。

证明。我们将使用定理 77 来证明这个结果。根据一般形式(12.2),我们得到:

 lim f (x) = lim (f(x )+ f′(x )(x− x ) + o(|x − x |)) = f(x ), x→x0 x→x0 0 0 0 0 0

这表明 f 在 x[0] 处是连续的。

请注意,前面的定理并不是反过来成立的:一个函数可以是连续的,但并不是可微的。(正如 f(x) = jxj 在 x = 0 处的例子所示。)

这可以推到极限:存在一些函数在每一点上都是连续的,但在任何地方都不可微。第一个例子是由魏尔斯特拉斯提供的(来自博尔扎诺-魏尔斯特拉斯定理)。该函数本身由无限和式定义:

 ∑∞ W (x) = ancos(bnπx), n=0

其中 a ∈ (0,1),b 是一个正奇数,且 ab/span>1 + 3π∕2\。

我同意,这个定义看起来完全随机,您可能会想:作者是怎么想到这个的?为了理解这个函数,想象它是多个余弦波的叠加,每个波的振幅越来越小,但频率越来越高。记住,微分意味着“没有尖角”吗?这个定义在实数线上的每个点上都放置了一个尖角。

它的图形是一条具有自相似性的分形曲线,如下所示。

PIC

图 12.5:魏尔斯特拉斯函数的图形。来源:en.wikipedia.org/wiki/Weierstrass_function

这样的例子启发了本节的开头引述:

我从这个可悲的无导数连续函数的祸害中,满怀恐惧和惊恐地转过身来。 — 查尔斯·埃米特

19 世纪的数学家们显然没有太多关注不可微函数。然而,这类函数的数量远远超过了可微函数。我们不会深入讨论这一点,但在所有连续函数中,至少在一个点可微的函数集合是稀疏的。稀疏是一个专业的术语,虽然我们不需要确切理解它的含义,但它的名字暗示它是极其小的。

现在我们理解了什么是导数,是时候将理论付诸实践了。我们如何计算导数,又如何在机器学习中使用它们呢?我们将在下一节中看到。

12.2 实践中的微分

在我们第一次接触微分时,我们看到通过定义计算导数

f′(x0) = lim f(x0)−-f-(x-) x→x0 x0 − x

在实际操作中,如果遇到复杂的函数,如 f(x) = cos(x)sin(e^x),微分可能会非常困难。类似于收敛序列和极限,使用微分定义是无法快速解决的——复杂性迅速堆积。因此,我们必须找到方法将复杂性分解为其基本构件。

12.2.1 导数的规则

首先,我们将看看最简单的几种操作:标量乘法、加法、乘法和除法。

定理 80.(微分法则)

令 f : ℝ → ℝ 和 g : ℝ → ℝ 为两个任意函数,且令 x ∈ ℝ。假设 f 和 g 在 x 处都可微。则

(a) (cf)^′(x) = cf^′(x),对所有 c ∈ℝ成立。

(b) (f + g)^′(x) = f^′(x) + g^′(x),

(c) (fg)^′(x) = f^′(x)g(x) + f(x)g^′(x)(乘积法则),

(d) (f g)^′(x) = f′(x)g(x)−-f(x)g′(x) g(x)2,当 g(x)≠0 时(商法则)。

证明:(a)和(b)是定理 70 的直接结果。

为了证明(c),我们需要做一些代数运算:

lim f(x)g(x)−-f-(y)g(y)-= lim f(x)g(x)−--f(y)g-(x-)+-f(y)g(x)−-f-(y)g(y)- y→x x − y x→y x − y f(x)g(x)− f(y)g (x ) f(y)g(x)− f (y)g (y) = lyim→x -------x−--y-------+ lxi→my -------x−-y-------- = lim [f(x)−-f(y)g(x)]+ f (y ) lim g(x)−-g(y)- y→x x − y x→y x − y = f′(x)g(x)+ f(x)g′(x),

从中可以得到(c)。

对于(d),我们将从(1∕g)^′的特例开始。我们有

 g1(x) − g1(y) 1 g(y)− g (x ) lyim→x --x-−-y---= lyim→x g(x)g(y)---x−--y--- ′ = − g-(x-), g(x)2

从中,通过对 f 和 1∕g 应用(c),可以得到一般情况。

有一种操作我们在之前的定理中没有涉及:函数复合。在神经网络的研究中,复合起着至关重要的作用。每一层都可以看作一个函数,这些函数组合在一起形成整个网络。

定理 81.(链式法则/莱布尼茨法则)

令 f : ℝ→ℝ 和 g : ℝ→ℝ 为两个任意函数,且令 x ∈ ℝ。假设 g 在 x 处可微,且 f 在 g(x)处可微。则

′ ′ ′ (f ∘g) (x) = f (g(x))g(x)

保持不变。

证明:首先,我们将微分商重写成以下形式:

lim f(g(x))−-f(g(y)) = lim f(g(x))−-f(g(y))g(x)-−-g(y) y→x x − y y→x g(x)− g(y) x − y f(g(x))− f(g(y)) g(x) − g(y) = lyim→x ---g(x)−-g(y)---xli→my ---x-−-y---.

因为 g 在 x 处可微,它在该点也是连续的,因此 lim[y→x]g(y) = g(x)。因此,第一项可以重写为

lim f(g(x-))-−-f(g(y))= lim f(y)−-f(g(x))-= f′(g(x)). y→x g(x) − g(y) y→g (x) y − g(x)

由于 g 在 x 处可微,第二项是 g^′(x)。因此,我们有

lim f-(g(x-))-−-f(g(y))= f ′(g(x))g′(x), y→x x − y

这就是我们需要证明的内容。

由于神经网络本质上是由大量复合函数组成的,其导数是通过反复应用链式法则来计算的。(虽然它的各层导数是向量和矩阵,因为它们是多变量函数。)

12.2.2 初等函数的导数

按照已经熟悉的模式,现在我们计算最重要的类别的导数:初等函数。有一些是我们经常遇到的,比如均方误差、交叉熵、Kullback-Leibler 散度等。

定理 82。(基本函数的导数)

(a) (x⁰)^′ = 0 和 (xn)′ = nx^(n−1),其中 n ∈ℤ ∖ 0,

(b) (sinx)^′ = cosx 和 (cosx)^′ = −sinx,

(c) (ex)′ = e^x,

(d) (log x)^′ = 1 x

你不一定要知道如何证明这些。我将包括(a)的证明,但如果这是你第一次接触微积分,完全可以跳过它。你需要记住的,是这些导数本身。(不过,当需要时,我会回到这一部分。)

证明。(a)很容易看出,当 n = 0 时,导数 (x⁰)^′ = 0。n = 1 的情况也很简单:计算微分商可以得到 (x)^′ = 1。对于 n ≥ 2 的情况,我们将使用一个小技巧。写出 f(x) = x^n 的微分商,得到

 n n x--−-y- , x − y

我们想简化这个。如果你在数学上没有太多经验,可能觉得这像是魔法,但 x^n −y^n 可以写成

xn − yn = (x− y )(xn− 1 + xn− 2y + ⋅⋅⋅+ xyn− 2 + yn−1) n−1 ∑ n− 1− k k = (x− y ) x y . k=0

通过计算乘积,可以很容易看出这一点:

 n∑−1 n−1−k k n n−1 n−1 (x − y) x y .= x + [x y + ⋅⋅⋅+ xy ] k=0 − [xn− 1y + ⋅⋅⋅+ xyn− 1]− yn = xn − yn.

因此,我们得到

 ∑ xn-−-yn (x-−-y)--nk−=01xn−1−kyk- liy→mx x− y = xli→my x− y n∑−1 = lim xn−1−kyk y→x k=0 = nxn −1.

所以,(xn)′ = nx^(n−1)。有了这个以及微分法则,我们可以计算任何多项式 p(x) = ∑ [k=0]np[k]xk 的导数:

 ′ ∑n k−1 p(x) = kpkx . k=1

n/span>0 的情况可以通过使用微分法则将 x^(−n) = 1∕x^n 得出。

有了这些规则,我们可以计算一些最著名的激活函数的导数。

最经典的一个是 sigmoid 函数,定义为

 ---1--- σ(x) = 1+ e− x.

由于它是一个基本函数,所以它在任何地方都是可微的。为了计算它的导数,我们可以使用商法则:

L(U,V ) = {f : U → V | f 是线性映射}(12.4)

现在我们有了 sigmoid 函数及其导数,让我们一起绘制它们的图像吧!

def sigmoid(x): 
    return 1/(1 + np.exp(-x)) 

def sigmoid_prime(x): 
    return sigmoid(x) - sigmoid(x)**2
import numpy as np 
import matplotlib.pyplot as plt 

xs = np.linspace(-10, 10, 1000) 

with plt.style.context("/span>seaborn-v0_8": 
    plt.title("/span>Sigmoid and its derivative 
    plt.plot(xs, [sigmoid(x) for x in xs], label="/span>Sigmoid 
    plt.plot(xs, [sigmoid_prime(x) for x in xs], label="/span>Sigmoid prime 
    plt.legend() 
    plt.tight_layout() 
    plt.show()

PIC

图 12.6:Sigmoid 及其导数

另一个流行的激活函数是 ReLU,定义为

 ( |{x if x >0, ReLU (x) = |(0 otherwise.

让我们先绘制它的图像!

def relu(x): 
    if x /span> 0: 
        return x 
    else: 
        return 0
xs = np.linspace(-5, 5, 1000) 

with plt.style.context("/span>seaborn-v0_8": 
    plt.title("/span>Graph of the ReLU function 
    plt.plot(xs, [relu(x) for x in xs], label="/span>ReLU 
    plt.legend() 
    plt.tight_layout() 
    plt.show()

PIC

图 12.7:ReLU 函数的图像

通过观察它,我们可以怀疑它在 0 处不可微。确实,因

 (| ReLU (x) − ReLU (0) { 1 if x >0, ---------x--------- = | ( 0 if x <0,

微分商的极限不存在。

然而,除了 0 之外,它是可微的,并且

 (| ′ {1 if x >0, ReLU (x) = | (0 if x <0.

尽管 ReLU 在 0 点不可导,但在实际应用中这并不成问题。在进行反向传播时,ReLU^′(x)接收到 0 作为输入的情况极为不可能。即使发生这种情况,也可以通过将其定义为 0 来人为扩展导数为零。

12.2.3 高阶导数

在继续之前,我们要谈谈高阶导数。由于导数本身是函数,因此计算导数的导数是一个完全自然的想法。正如我们在第十四章学习优化基础时将会看到的,二阶导数包含了有关最小值和最大值的许多重要信息。

f 的 n 阶导数记作 f^((n)),其中 f^((0)) = f。有几个关于它们的规则值得记住。尽管我们需要注意,导数函数并不总是可导的,正如这个例子所示

 ( | { 0 if x <0, f (x) = |( 2 x otherwise

现在,关于高阶导数的那些规则。

定理 83。

设 f : ℝ →ℝ 和 g : ℝ →ℝ 是两个任意函数。

(a) (f + g)^((n)) = f^((n)) + g^((n))

(b) (fg)^((n)) = ∑ [k=0]^n(n) kf((n−k))g((k))

证明。(a) 显然来自于微分的线性性质。

关于(b),我们将使用归纳法证明。对于 n = 1,该命题简单地表示(fg)^′ = f^′g + fg^′,正如我们之前所见。

现在,我们假设对于 n 成立,并推导出 n + 1 的情况。为此,我们有

 ∑n ( ) (fg)(n+1) = ((fg)(n))′ = n (f(n−k)g(k))′ k=0 k ∑n ( ) = n [f(n−k+1)g(k) + f(n−k)g(k+1)] k=0 k ∑n ( ) ∑n ( ) = n f(n− k+1)g (k) + n f(n−k)g(k+1) k=0 k k=0 k ( ) n ( ) n− 1( ) ( ) = n f (n+1)g + [∑ n f(n+1−k)g(k)]+ [∑ n f(n−k)g(k+1)]+ n fg(n+1). 0 k k n k=1 k=0

首先,我们注意到!( ) n 0 = ( ) n+1 0 = 1,且!( ) n n = ( ) n+1 n+1 = 1。其次,二项式系数的递推关系告诉我们

( ) ( ) ( ) n+ 1 = n + n . k k k − 1

通过简单的重新索引,我们得到

n−1( ) n ( ) ∑ n f(n− k)g(k+1) = ∑ n f(n+1−k)g (k), k k − 1 k=0 k=1

因此,我们可以将两个和式合并,得到

 ( ) n ( ) ( ) ( ) (fg)(n+1) = n + 1 f(n+1)g + ∑ [ n + n f(n+1−k)g(k)]+ n+ 1 fg(n+1) 0 k k − 1 n+ 1 ( ) k=1 n∑+1 n + 1 (n+1−k) (k) = k f g , k=0

这就是我们需要证明的内容。

12.2.4 扩展函数基类

现在,我们已经掌握了计算导数的几种工具,是时候考虑实现方法了。由于我们有自己的 Function 基类(第 9.2.3 节),一个自然的想法是将导数实现为一个方法。这是一个简单的解决方案,也符合面向对象原则,所以我们应该采用它!

class Function: 
    def __init__(self): 
        pass 

    def __call__(self, *args, **kwargs): 
        pass 

    # new interface element for 
    # computing the derivative 
    def prime(self): 
        pass 

    def parameters(self): 
        return dict()

为了看到一个具体的例子,让我们回顾一下 sigmoid 函数,它的导数由(12.4)给出:

 ′ σ (x) = σ(x)(1− σ (x )).

class Sigmoid(Function): 
    def __call__(self, x): 
        return 1/(1 + np.exp(-x)) 

    def prime(self, x): 
        return self(x) - self(x)**2

简单的实现,强大的功能。现在我们已经处理了导数问题,接下来让我们计算更复杂函数的导数!

12.2.5 组合函数的导数

到现在为止,我可能已经强调了函数组合和链式法则(定理 81)的重要性数十次。我们终于达到了一个节点,准备实现一个简单的神经网络并计算其导数!(当然,最终我们的方法会更加精细,但这仍然是一个里程碑。)

我们如何计算 n 个函数组合的导数?

为了观察模式,让我们列出前几个情况。当 n = 2 时,我们得到经典的链式法则

 ′ ′ ′ (f2(f1(x))) = f2(f1(x))⋅f1(x).

当 n = 3 时,我们得到

(f (f (f (x))))′ = f ′(f (f (x ))) ⋅f′(f (x))⋅f ′(x). 3 2 1 3 2 1 2 1 1

在众多括号中,我们可以注意到一个模式。首先,我们应该计算组合函数 f[3] ∘ f[2] ∘ f[1] 在 x 处的值,并存储中间结果,然后将这些结果传递给适当的导数并求得结果的积。

class Composition(Function): 
    def __init__(self, *functions): 
        self.functions = functions 

    def __call__(self, x): 
        for f in self.functions: 
            x = f(x) 

        return x 

    def prime(self, x): 
        forward_pass = [x] 

        for f in self.functions: 
            try: 
                x = f(x) 
                forward_pass.append(x) 
            except ValueError as e: 
                print(f/span>Error in function {f}: {e}" 
                return np.nan 

        forward_pass.pop()    # removing the last element, as we won’t need it 

        derivative = np.prod([f.prime(x) for f, x in zip(self.functions, forward_pass)]) 

        return derivative

为了验证我们的实现是否有效,我们应该在一个简单的测试案例上进行测试,比如:

f1(x) = 2x, f2(x) = 3x, f3(x) = 4x.

组合函数 (f[3] ∘ f[2] ∘ f[1])(x) = 24x 的导数应为常数 24。

class Linear(Function): 
    def __init__(self, a, b): 
        self.a = a 
        self.b = b 

    def __call__(self, x): 
        return self.a*x + self.b 

    def prime(self, x): 
        return self.a 

    def parameters(self): 
        return {"/span>a self.a, /span>b self.b}
f = Composition(Linear(2, 0), Linear(3, 0), Linear(4, 0)) 

xs = np.linspace(-10, 10, 1000) 
ys = [f.prime(x) for x in xs] 

with plt.style.context("/span>seaborn-v0_8": 
    plt.title("/span>The derivative of f(x) = 24x 
    plt.plot(xs, ys, label="/span>f prime 
    plt.legend() 
    plt.tight_layout() 
    plt.show()

PIC

图 12.8:f(x) = 24x 的导数

成功!尽管我们现在只处理单变量函数,但我们的组合将成为神经网络的骨架。

12.2.6 数值微分

到目前为止,我们已经看到,当至少有一些公式可以用于给定的函数时,我们可以应用微分规则(参见定理 80)来求导。

然而,在实践中,情况往往并非如此。例如,想象一下函数表示的是录制的音频信号。如果我们无法准确地计算导数,一个自然的想法是进行近似,即提供一个足够接近真实值的估计。

举个例子,假设我们不知道要微分的函数的准确公式,实际上它是经典的正弦函数。

def f(x):
    return np.sin(x)

回顾一下,根据定义,导数由以下公式给出

f′(x ) = lim f(x-+-h)−-f(x). h→0 h

由于我们无法在计算机内求极限(因为计算机无法处理无穷大),第二好的做法是通过以下方法进行近似:

 f (x + h) − f(x) Δhf (x ) =-------h-------,

其中 h/span>0 是一个任意小但固定的量。Δ[h]f(x) 称为前向差分商。在理论上,当 h 足够小时,Δ[h]f(x) ≈ f^′(x) 成立。让我们看看它们是如何表现的!

def delta(f, h, x): 
    return (f(x + h) - f(x))/h 

def f_prime(x): 
    return np.cos(x)
hs = [3.0, 1.0, 0.1] 
xs = np.linspace(-5, 5, 100) 
f_prime_ys = [f_prime(x) for x in xs] 

with plt.style.context("/span>seaborn-v0_8": 
    _colormap = plt.cm.hot_r 
    plt.figure(figsize=(10, 5)) 
    plt.title("/span>Approximating the derivative with finite differences 

    true_color = _colormap(0.99)  # Get a fixed color for the true derivative 
    for i, h in enumerate(hs): 
        ys = [delta(f, h, x) for x in xs] 
        blend_ratio = 1 - (len(hs) - i) / len(hs)  # Progressively blend closer to the true color 
        approx_color = _colormap(blend_ratio) 
        plt.plot(xs, ys, label=f/span>h = {h}" color=approx_color) 

    plt.plot(xs, f_prime_ys, label="/span>the true derivative color=true_color, linewidth=2) 
    plt.legend() 
    plt.tight_layout() 
    plt.show()

PIC

图 12.9:用有限差分近似导数

虽然 Δ[h]f(x) 函数似乎接近 f^′(x),但当 h 很小时,仍然存在许多潜在问题。

例如,Δ[h]f(x) = f(x+h)−-f(x) h 仅仅是从 x 的右侧近似导数,因为 h/span>0. 为了解决这个问题,人们可能会使用后向差分商。

∇hf (x ) = f-(x-)−-f(x-−-h), h

但似乎也有相同的问题。问题的关键是,如果 f 在某个 x 处可微,那么

f(x+--h)−-f(x)-≈ f-(x)−-f(x-−-h), h h

但只有当 h 非常小时,且对于不同点来说,“足够好”的 h 值可能会有所不同。

一个折中的方法是所谓的对称差分商,定义为

 f(x + h)− f (x − h) δhf(x) = --------2h--------, h ∈ (0,∞ ),

这是前向差分和后向差分的平均值:δ[h]f(x) = Δhf(x)+-∇hf(x)- 2。这三种近似方法被称为有限差分。通过它们的重复应用,我们也可以逼近高阶导数。

即使对称差分在理论上证明更好,但在长期内,近似误差仍可能显著放大。

综合考虑,我们实际上不会在机器学习中使用有限差分。然而,正如我们将看到的,梯度下降法实际上是特殊微分方程的前向差分近似。

12.3 小结

本章讲解了微分,这是优化函数的关键组成部分。是的,即使是具有百万个变量的函数。

尽管我们目前专注于一元函数,但我们成功地构建了对微分的深刻理解。例如,我们已经了解到导数

f′(x) = lim f-(x-)−-f(y) y→x x − y

该式描述了切线在 x 处的斜率,若 f 是一维运动的轨迹,则描述了速度。从物理学的角度来看,导数描述了变化率。

然而,从数学的角度来看,微分提供的远不止是变化率:我们已经看到可微函数可以写成如下形式

f (x ) = f(x0) + f′(x0)(x − x0)+ o(|x− x0|)

在某个 x[0] ∈ℝ 附近。换句话说,从局部角度来看,一个可微函数是线性部分加上一个小的误差项。与商的极限定义不同,这种方法可以轻松推广到多个变量。更重要的是,我们可以应用类似的思想来得到所谓的泰勒展开

 ∑n f (k)(x0) f(x) = --------(x− x0 )k + o(|x − x0|n), k=0 k!

这使我们能够用多项式逼近像 log x, sinx, cosx, e^x 这样的超越函数。

除了理论,我们还学习了如何在实践中计算导数。

这可以通过 1) 将复杂函数分解为基本构件,然后使用规则计算导数来实现

 ′ ′ (cf) (x) = cf (x), (f + g)′(x) = f ′(x)+ g′(x), (fg)′(x) = f ′(x)g(x)+ f (x )g ′(x), (f ∘g)′(x) = f ′(g(x))g′(x),

或者 2) 使用有限差分近似导数,如下所示

 f (x + h) − f(x) Δhf (x ) =---------------, h

其中 h/span>0 是一个小常数。前者方法是反向传播的基础,而后者方法是梯度下降的核心。这些方法使得训练巨大的神经网络成为可能。

现在我们理解了微分,是时候讨论它的对立面:积分了。开始吧!

12.4 问题

问题 1. 计算由下式定义的 tanh(x) 函数的导数

 ex − e− x tanh(x) = -x---−-x. e + e

问题 2. 定义函数

 ( |{ 0 如果 x <0, f(x) = |( x2 否则。

求 f(x) 的导数。f^′(x) 是否在每个点都可导?

问题 3. 定义函数

 ( | { x2 如果 x ∈ ℚ, f (x ) = |( 2 − x 否则。

证明

(a) f 在 0 处可导且 f^′(0) = 0,(b) f 在其他地方不可导。

问题 4. 计算以下函数的导数。

(a) f(x) = e^(− 2 x2-) (b) f(x) = x²e^(sin x) (c) f(x) = sin(cosx²) (d) f(x) = x2+1 x2−-1

问题 5. 求以下函数在 0 附近的泰勒展开。

(a) f(x) = sinx (b) f(x) = cosx (c) f(x) = log x (d) f(x) = e^({-x)2}

问题 6. 求函数的泰勒展开

 ( |{ − 12 f(x) = e x 如果 x ⁄= 0, |( 0 如果 x = 0.

在 0 附近。f 的泰勒展开是否等于 f?

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,为其他读者提供解决方案,通过问我任何问题的环节与作者聊天,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

优化

如果有人给你一个由某个易处理的公式定义的函数,你将如何找到它的极小值和极大值?在继续之前,花点时间想想一些方法。

大多数人首先想到的方式是对所有可能的值评估函数,简单地找到最优解。但这种方法由于多种原因立即失败。我们只能进行有限的评估,因此这是不可能的。即使我们巧妙地定义一个离散的搜索网格并只在其上进行评估,这种方法也需要耗费不合理的时间。

另一种方法是使用某种不等式来提供一个特定的上界或下界,然后查看这个界限是否能达到。遗憾的是,对于更复杂的函数,如神经网络的损失函数,这几乎是不可能的。

然而,导数提供了一种极其有用的方式来优化函数。在本章中,我们将研究导数与最优点之间的关系,以及如何找到它们的算法。让我们开始吧!

第十六章:13.1 极小值、极大值和导数

直观地说,极小值和极大值的概念很简单。看看下面的图 13.1。

图片

图 13.1:局部和全局极值

山峰的顶部是极大值,山谷的底部是极小值。极小值和极大值统称为极值点或最优点。正如我们的例子所示,我们必须区分局部最优解和全局最优解。图形中有两个山谷,虽然它们都有底部,但其中一个底部比另一个低。

真正有趣的部分是找到这些点,正如我们接下来将看到的那样。让我们考虑上面的例子来演示导数是如何与局部极小值和极大值相连接的。

如果我们运用几何直觉,我们会发现切线在山峰和山谷的底部是水平的。直观地说,如果切线不是水平的,那么图形上就有一个上升或下降。这在图 13.2 中得到了说明。

图片

图 13.2:局部和全局极值点的切线

从导数的角度来看,由于它们描述了切线的斜率,这意味着在该点导数应该为 0。

如果我们把函数看作是沿着实数轴的运动描述,导数表明运动在该点停止并改变方向。它首先减速,停下来,然后立即开始向相反方向运动。例如,在局部最大值的情况下,函数在那个点之前增加,然后开始减少。

图片

图 13.3:函数的流动

再次,我们可以通过导数来描述这种单调性行为。注意,当函数增加时,导数是正的(物体在运动时速度是正的)。另一方面,减小部分的导数是负的。

图片

图 13.4:导数的符号

我们可以将这些直觉转化为数学形式。首先,我们从定义的单调性开始,以及它们与导数的关系。然后,我们将所有点连接起来,看看这如何结合起来描述最优解。

定义 56. (局部增大和减小函数)

设 f : ℝ → ℝ 为一个任意函数,且 a ∈ ℝ。我们说

(a) 如果 f 在 a 处局部增大,则在邻域 (a −δ, a + δ) 中存在这样一种情况:

 (| ||| ≥ f(x),如果 x ∈ (a − δ, a), { f(a)|| ≤ f(x),如果 x ∈ (a, a + δ), ||(

(b) 并且如果在邻域 (a −δ, a + δ) 中存在这样的情况:

 (| ||| > f(x),如果 x ∈ (a − δ, a), { f(a)|| < f(x),如果 x ∈ (a, a + δ)。 ||(

局部减小和严格局部减小的性质定义类似,只是不等式方向相反。

对于可微函数,导数的行为描述了它们在单调性方面的局部行为。

定理 84.

设 f : ℝ → ℝ 为一个在某个 a ∈ ℝ 处可微的任意函数。

(a) 如果 f^′(a) ≥ 0,那么 f 在 a 处局部增大。

(b) 如果 f^′(a)/span>0,那么 f 在 a 处严格地局部增大。

(c) 如果 f^′(a) ≤ 0,那么 f 在 a 处局部减小。

(d) 如果 f^′(a)/span>0,那么 f 在 a 处严格地局部减小。

证明。我们只证明 (a),因为其余的证明是一样的。由于极限的定义方式(定义 51),

 f(x)-−-f(a) ′ lxi→ma x − a = f (a) ≥ 0

这意味着一旦 x 足够接近 a,即 x 来自于小邻域 (a −δ, a + δ),

f(x)−-f-(a)-≥ 0,x ∈ (a− δ,a + δ) x− a

成立。如果 x >a,那么因为差商非负,必须有 f(x) ≥ f(a)。同样地,对于 x <a,差商的非负性意味着 f(x) ≤ f(a)。

(b)、(c) 和 (d) 的证明几乎完全相同,唯一的变化是在不等式中的显著改变。

与非严格单调性相关的命题也以相反的方式成立。

定理 85.

设 f : ℝ → ℝ 为一个在某个 a ∈ ℝ 处可微的任意函数。

(a) 如果 f 在 a 处局部增大,那么 f^′(a) ≥ 0。

(b) 如果 f 在 a 处局部减小,那么 f^′(a) ≤ 0。

证明。与之前类似,我们只证明 (a),因为 (b) 可以用相同的方式完成。如果 f 在 a 处局部增大,那么差商是正的:

f(x)− f(a) -----------≥ 0. x− a

使用极限的传递原理(定理 73),我们得到

 ′ f(x)-−-f(a) f (a ) = lxi→ma x − a ≥ 0,

这就是我们需要证明的内容。

在所有这些准备工作完成后,我们准备研究局部最优解。导数能告诉我们什么关于它们的信息呢?让我们看看!

13.1.1 局部最小值和最大值

正如我们在引言中看到的,极值点的切线是水平的。现在是时候将这个引言转化为一个数学上正确的形式了。

定义 57.(局部最小值和最大值)

设 f : ℝ →ℝ 是任意函数,且 a ∈ℝ。

(a) 如果存在一个邻域 (a−δ,a+δ),使得对于每一个 x ∈ (a−δ,a+δ),都有 f(a) ≤ f(x),则 a 是局部最小值。

(b) 如果存在一个邻域 (a−δ,a+δ),使得对于每一个 x ∈ (a−δ,a+δ),都有 f(a) < f(x),则 a 是严格局部最小值。

(c) 如果存在一个邻域 (a−δ,a+δ),使得对于每一个 x ∈ (a−δ,a+δ),都有 f(x) ≤ f(a),则 a 是局部最大值。

(d) 如果存在一个邻域 (a−δ,a+δ),使得对于每一个 x ∈ (a−δ,a+δ),都有 f(x) > f(a),则 a 是严格局部最大值。

极值点也有其全局版本。遗憾的是,尽管我们总是追求全局最优解,但我们只有工具能够找到局部最优解。

定义 58.(全局最小值和最大值)

设 f : ℝ →ℝ 是任意函数,且 a ∈ℝ。

(a) 如果对所有 x ∈ ℝ,f(a) ≤ f(x) 都成立,则 a 是全局最小值。

(b) 如果对所有 x ∈ ℝ,f(x) ≤ f(a) 都成立,则 a 是全局最大值。

请注意,全局最优解也是局部最优解,但反之则不成立。

定理 86\。

设 f : ℝ →ℝ 是在某点 a ∈ℝ 可微的任意函数。如果 f 在 a 处有局部最小值或最大值,则 f^′(a) = 0。

证明:根据定理 84,如果 f^′(a)≠0,则该点在局部范围内要么严格递增,要么严格递减。由于这与我们假设 a 是局部最优解相矛盾,因此该定理得证。

(如果你感兴趣,这就是逆命题原理(定理 150)的实际应用。通过否定结论,我们证明了前提的否定。)

强调这一点非常重要,即定理并非反过来也成立。例如,函数 f(x) = x³ 在任何地方都是严格递增的,但 f^′(0) = 0。

通常,我们称这种行为为拐点。因此,f(x) = x³ 被认为在 0 处具有拐点。拐点意味着行为的变化,在这种情况下,反映了其导数从递减变为递增的转换。(多维的拐点类比被称为“鞍点”,我们稍后会讨论。)

所以,我们的最终目标还没有实现,因为所承诺的另一半刻画还没有完成。

在局部极值点处导数为零,但我们能否提出一个准则,暗示最小值或最大值的存在?

PIC

图 13.5:f(x) = x³ 的图像,作为反例展示 f^′(0) = 0 并不意味着局部最优解

利用二阶导数,这是可能的。

13.1.2 利用高阶导数刻画极值

让我们再看一遍我们的例子,这次考虑 f^′ 的局部行为,而不仅仅是其符号。在图 13.6 中,导数与我们的函数一起绘制。

PIC

图 13.6:函数及其导数

这个模式看起来很简单:导数增大意味着局部最小值,导数减小意味着局部最大值。这与我们对于导数作为速度的直觉相符合:局部最大值意味着物体在正方向上运动,然后停下并开始反转。

我们可以通过以下定理将这一点数学化。

定理 87. (第二导数法则)

设 f : ℝ →ℝ是一个在某个 a ∈ℝ处二次可微的任意函数。

(a) 如果 f^′(a) = 0 且 f^(′′)(a)/span>0,那么 a 是局部最小值。

(b) 如果 f^′(a) = 0 且 f^(′′)(a)/span>0,那么 a 是局部最大值。

证明。再一次,我们只证明(a),因为(b)的证明几乎是相同的。

首先,正如我们在讨论导数与单调性之间的关系时所看到的(定理 84),f^(′′)(a) 0 意味着 f^′在 a 处严格局部递增。由于 f^′(a) = 0,这意味着

![ ( ′ |{ ≤ 0 如果 x ∈ (a− δ,a] f (x)| ( ≥ 0 如果 x ∈ a,a + δ)

对于某个δ > 0。根据定理 84,f 在(a −δ,a]内局部递减,在[a,a + δ)内局部递增。只有当 a 是局部最小值时,这种情况才可能发生。

总结一下,找到函数 f 的极值的方法如下。

  1. 解 f^′(x) = 0。它的解{x[1],…,x[n]} —— 被称为临界点 —— 是可能的极值点候选点。(但不一定所有解都是极值点。)

  2. 检查所有解 x[i]的 f^(′′)(x[i])的符号。如果 f^(′′)(x[i])/span>0,则为局部最小值。如果 f^(′′)(x[i])/span>0,则为局部最大值。

如果 f^(′′)(x[i]) = 0,我们仍然无法得出任何结论。函数 x⁴,−x²和 x³表明,具有零二阶导数的临界点可以是局部最小值、最大值或两者都不是。

即使我们有了一个“公式”,这对于实际应用来说仍然远远不够。更不用说我们关心的函数是多变量的,计算导数并解 f^′(x) = 0 并不可行。对于神经网络的损失函数,我们甚至懒得写出公式,因为对于数百个函数的组合,公式可能会变得异常复杂。

13.1.3 均值定理

在某些情况下,我们可以不显式计算导数就提取出很多关于导数的信息。这些结果在我们没有函数的显式公式或公式可能非常庞大的情况下(例如在神经网络的情况下)极为有用。接下来,我们将介绍著名的均值定理,连接函数在区间端点和区间内的行为。

首先,我们从一个特殊的情况开始,假设函数在某个区间[a,b]的末端取得相同的值,那么它的导数在该区间内某处为零。

定理 88. (罗尔均值定理)

设 f : ℝ →ℝ是一个可微函数,且假设对于某个 a≠b,f(a) = f(b)。则存在一个ξ ∈ (a,b),使得 f^′(ξ) = 0。

证明。如果你是一个喜欢可视化的人,可以看一下图 13.7。这就是我们需要证明的内容。

PIC

图 13.7:罗尔定理

为了数学上的精确性,存在两种情况。首先,如果 f 在 [a,b] 上是常数,那么它在整个区间上的导数为零。

如果 f 不是常数,那么它在 (a,b) 内达到某个不等于 f(a) = f(b) 的值 c。为了简化,假设 c > f(a)。(如果是 c < f(a) 的情况,接下来的论证也能成立,只需做一些显而易见的调整。)由于 f 是连续的,它在 [a,b] 上会在某个点 ξ ∈ [a,b] 处达到最大值。(见定理 76)。根据我们刚刚看到的关于局部极值与导数关系的内容(定理 86),有 f^′(ξ) = 0,这就是我们要证明的内容。

罗尔定理是通向拉格朗日均值定理的重要跳板,接下来我们将证明这一点。

定理 89.(拉格朗日均值定理)

设 f : ℝ → ℝ 为可微函数,且 [a,b] 为某个 a≠b 的区间。那么存在 ξ ∈ (a,b),使得

 ′ f(b)− f(a) f (ξ) = --b-−-a----

成立。

证明。让我们再次从一个可视化开始,以便更好地理解该定理。图 13.8 展示了我们需要证明的内容。

PIC

图 13.8:拉格朗日均值定理

回想一下,f(b)−f(a)- b−a 是通过 (a,f(a)) 和 (b,f(b)) 两点的直线的斜率。该直线由以下函数描述:

f(b)− f(a) -----------(x− a) + f(a), b− a

如通过直线的点斜式方程给出。利用这一点,我们引入函数

 f(b)− f (a) g(x) := f (x)− (-----------(x − a) + f(a)). b− a

我们可以将罗尔定理应用于 g(x),因为 g(a) = g(b) = 0。因此,对于某个 ξ ∈ (a,b),我们有

g′(ξ) = 0 = f ′(ξ)− f(b)−-f(a), b − a

这意味着 f^′(ξ) = f(b)b−−fa(a)-,这是我们需要证明的内容。

为什么均值定理如此重要?在数学中,它们在多个结果中起着基石的作用。举个例子,想一想积分。(或许你已经熟悉这个概念了。如果不熟悉也没关系,我们稍后会详细学习它。)积分本质上是微分的逆过程:如果 F^′(x) = f(x),那么

∫ b a f (x )dx = F (b)− F (a),

这将是拉格朗日均值定理的一个简单推论。

13.2 梯度下降的基础

我们需要解决两个计算问题来训练神经网络:

  • 计算损失 L(w) 的导数,

  • 并且通过导数找到它的最小值。

通过求解ddw--L(w) = 0 来寻找最小值在实际中行不通。存在几个问题。首先,正如我们所看到的,并不是所有的解都是最小点:还有最大点和拐点。其次,除非在最简单的情况下,比如线性回归和均方误差,否则解这个方程是不可行的。训练神经网络不是简单的案例。

幸运的是,对于我们这些机器学习从业者来说,有一个解决方案:梯度下降!著名的梯度下降提供了一种方法来应对寻找精确解的复杂性,使我们能够大规模地进行机器学习。让我们看看它是如何实现的!

13.2.1 再访导数

当我们第一次在第十二章探讨导数的概念时,我们看到了它的多种形式。我们已经学到,导数可以被看作

  • 速度(当函数描述一个移动物体的时间-距离图时),

  • 函数切线的斜率,

  • 在给定的点上的最佳线性近似器。

为了理解梯度下降是如何工作的,我们将看到另一个解释:导数作为向量。对于任何可微分函数 f(x),导数 f^′(x)可以被看作一个一维向量。如果 f^′(x)是正的,它指向右侧。如果是负的,它指向左侧。我们可以通过为 f(x)图形上的每个点画一条水平向量来可视化这一点,其中向量的长度代表|f^′(x)|,方向代表符号。图 13.9 展示了这一过程。

PIC

图 13.9:导数作为一个向量

你是否还记得单调性是如何通过导数的符号来描述的?(正如定理 84 所述。)负导数意味着函数递减,正导数意味着函数递增。换句话说,这意味着导数作为一个向量指向增大的方向。

想象你自己是 x-y 平面上的一个徒步旅行者,y 表示高度。你如何攀登面前的山?通过朝着增高的方向迈出一步;也就是说,跟随导数。如果你还没有到达山顶,你仍然可以在正确的方向上再迈出一步(也许更小),反复如此,直到到达顶峰。如果你已经在山顶,导数就是零,你就不会再移动。

这个过程如图 13.10 所示。

PIC

图 13.10:一步一步爬山

你看到的正是梯度上升的过程。现在我们已经理解了主要思想,准备处理数学细节。

13.2.2 梯度下降算法

设 f : ℝ →ℝ是我们想要最大化的可微函数,也就是说,找到

xmax = argmaxx ∈ℝf(x).

基于我们的直觉,过程相当简单。首先,我们设定一个任意的起始点 x[0],然后定义序列

x[n+1] := x[n] + h f′(x[n]), (13.1)

其中 h ∈ (0,∞)是我们梯度下降算法的一个参数,称为学习率。

在英语中,公式 x[n] + hf^′(x[n])描述了从 x[n]出发,朝着增大方向迈出一步,步长为 hf^′(x[n])。(回想一下,导数的符号表示增大的方向。)

如果一切顺利,序列 x[n]会收敛到 f 的局部最大值。然而,事情并不总是按照我们的计划进行。我们将在讨论梯度下降的问题时谈到这一点。

那么,如何找到最小值呢?在机器学习中,我们试图最小化损失函数。有一个简单的技巧:f(x)的最小值就是−f(x)的最大值。因此,既然( −f)^′ = −f^′,逼近序列 x[n]的定义就变为

x := x − hf′(x ). n+1 n n

这就是梯度下降的核心概念。

13.2.3 实现梯度下降

到现在为止,我们已经具备了实现梯度下降算法的所有知识。我们将使用之前介绍的 Function 基类;在这里再次给出它,以免你需要查找类定义。

class Function: 
    def __init__(self): 
        pass 

    def __call__(self, *args, **kwargs): 
        pass 

    def prime(self): 
        pass 

    def parameters(self): 
        return dict()

和往常一样,我鼓励你在查看我的实现之前,先尝试实现你自己的梯度下降版本。编程是学习最有效的方式之一,即使在人工智能的时代——特别是在人工智能的时代。

def gradient_descent( 
    f: Function, 
    x_init: float,                  # the initial guess 
    learning_rate: float = 0.1,     # the learning rate 
    n_iter: int = 1000,             # number of steps 
    return_all: bool = False        # if true, returns all intermediate values 
): 
    xs = [x_init]    # we store the intermediate results for visualization 

    for n in range(n_iter): 
        x = xs[-1] 
        grad = f.prime(x) 
        x_next = x - learning_rate*grad 
        xs.append(x_next) 

    if return_all: 
        return xs 
    else: 
        return x

让我们在一个简单的例子上测试梯度下降,假设 f(x) = x²!如果一切顺利,算法应该会在短时间内找到最小值 x = 0。

class Square(Function): 
    def __call__(self, x): 
        return x**2 

    def prime(self, x): 
        return 2*x 

f = Square() 

gradient_descent(f, x_init=5.0)
7.688949513507002e-97

结果如预期:我们的 gradient_descent 函数成功找到了最小值。

为了可视化发生的过程,我们可以绘制整个过程。由于我们将重复使用相同的图表,这里有一个通用函数来完成这个工作。

import numpy as np 
import matplotlib.pyplot as plt 

def plot_gradient_descent(f, xs: list, x_min: float, x_max: float, label: str = /span>f(x): 
    ys = [f(x) for x in xs] 

    grid = np.linspace(x_min, x_max, 1000) 
    fs = [f(x) for x in grid] 

    with plt.style.context("/span>seaborn-v0_8-whitegrid: 
        plt.figure(figsize=(8, 8)) 
        plt.plot(grid, fs, label=label, c="/span>b lw=2.0) 
        plt.plot(xs, ys, label="/span>gradient descent c="/span>r lw=4.0) 
        plt.scatter(xs, ys, c="/span>r s=100.0) 
        plt.legend() 
        plt.show() 

xs = gradient_descent(f, x_init=5.0, n_iter=25, learning_rate=0.2, return_all=True) 
plot_gradient_descent(f, xs, x_min=-5, x_max=5, label="/span>x²"

PIC

图 13.11:通过梯度下降找到 f(x) = x²的最小值

那么,一切都是快乐和阳光吗?不是,但这没关系。让我们看看可能出现的问题,以及如何解决它们。

13.2.4 缺点与警告

尽管梯度下降背后的思想是合理的,但它仍然存在一些问题。在我们的机器学习旅程中,我们会看到大多数这些问题通过算法的变种得到解决,但在此时了解基础版本可能出现的问题也是值得的。

首先,基础的梯度下降算法可能会在局部最小值处陷入无限循环。

为了说明这一点,让我们看看 f(x) = cos(x) + 1 2x 函数,它没有全局最小值,只有局部最小值。

class CosPlusSquare(Function): 
    def __call__(self, x): 
        return np.sin(x) + 0.5*x 

    def prime(self, x): 
        return np.cos(x) + 0.5 

f = CosPlusSquare() 
xs = gradient_descent(f, x_init=7.5, n_iter=20, learning_rate=0.2, return_all=True) 
plot_gradient_descent(f, xs, -10, 10, label="/span>sin(x) + 0.5x

PIC

图 13.12:运行梯度下降于 f(x) = sin(x) + 1/2x

请注意,如果初始点 x[0]选择不当,算法的效果会大打折扣。换句话说,对初始条件的敏感性是另一个弱点。在我们刚刚看到的简单一维案例中,这个问题可能不那么明显。然而,在训练神经网络时,我们会遇到百万维的参数空间,这时它就是一个巨大的难题。

起始点并不是算法的唯一参数;它还依赖于学习率 h。这里有几个潜在的错误:过大的学习率会导致算法在空间中四处弹跳,永远找不到最优解。另一方面,过小的学习率会导致收敛速度极慢。

在 f(x) = x²的情况下,从 x[0] = 1.0 开始梯度下降,学习率为 h = 1.05,算法发散,x[n]在越来越大的幅度下振荡。

f = Square() 

xs = gradient_descent(f, x_init=1.0, n_iter=20, learning_rate=1.05, return_all=True) 
plot_gradient_descent(f, xs, -8, 8, label="/span>x²"

PIC

图 13.13:梯度下降,由于学习率过大导致错过最优解

你能想到一些解决这些问题的方案吗?无需进行详细的计算,只需花几分钟进行头脑风暴,记下你想到的内容。在后续章节中,我们会看到这些问题的几种解决方案,但花些时间思考这些问题是一个非常有益的练习。

然而,如果你注重细节,可能会问:梯度下降总是会收敛到局部最优解吗?为什么它在实践中如此有效?让我们来看看。

13.3 为什么梯度下降有效?

年轻人,在数学中你并不理解事物。你只是习惯它们。——约翰·冯·诺依曼

在机器学习的实践中,我们如此频繁地使用梯度下降,以至于我们已经习惯了它。我们几乎从不质疑它为何有效。

通常的解释是爬山类比:为了找到一个崎岖地形的山顶(或山谷),需要观察最陡上升(或下降)方向,并朝该方向迈出一步。这个方向由梯度描述,而通过跟随梯度迭代寻找局部极值的过程称为梯度上升/下降。(上升用于寻找山顶,下降用于寻找山谷。)

然而,这并不是一个数学上精确的解释。仍然存在一些未解答的问题,并且根据我们的爬山直觉,甚至不清楚该算法是否有效。

如果没有对梯度下降的精确理解,我们实际上是在盲目地操作。在本节中,我们的目标是深入探讨梯度下降背后的原理,揭示其中的奥秘。

理解梯度下降的“为什么”从数学中最美丽的领域之一——微分方程开始。

13.3.1 微分方程入门

什么是微分方程?方程在数学中扮演着至关重要的角色;这是一条常识,但其背后有着深刻的真理。方程常常来源于对系统的建模,例如生化网络中的相互作用、经济过程等成千上万的应用。例如,建模有机体中的代谢过程会得到如下形式的线性方程:

Ax = b, A ∈ ℝn×n, x,b ∈ ℝn

其中向量 x 和 b 表示分子浓度(其中 x 为未知量),矩阵 A 表示它们之间的相互作用。线性方程容易求解,我们对它们了解颇多。

然而,我们迄今为止看到的方程并不适合用来描述动力学系统,因为它们缺少时间组件。例如,要描述绕地球轨道运行的空间站的轨迹,我们必须用函数及其导数来描述我们的模型。

例如,摆动的钟摆的轨迹可以通过方程描述:

x″(t) + (g/L) sin x(t) = 0, (13.2)

其中

  • x(t) 描述钟摆相对于竖直方向的角度,

  • L 是(无质量)杆的长度,物体的质量为 m,物体悬挂在其上,

  • 其中 g 是重力加速度常数 ≈ 9.81 m/s²。

根据微分的原始解释,如果 x(t) 描述了钟摆在时刻 t 的运动,那么 x^′(t) 和 x^(′′)(t) 分别描述了它的速度和加速度,其中微分是相对于时间 t 进行的。

(实际上,微分方程(13.2)是牛顿第二定律的直接结果。)

PIC

图 13.14:摆动的钟摆

包含函数及其导数的方程,如(13.2),被称为常微分方程,简称 ODE。从 17 世纪起,它们的研究几乎可以说是推动数学发展的主要动力。相信我,当我说微分方程是数学中最美丽的对象之一时,你会明白的。正如我们将看到的,梯度下降算法实际上是微分方程的一个近似解。

本节的第一部分将作为微分方程的快速入门。我将主要跟随 Steven Strogatz 的《非线性动力学与混沌》这本精彩的书。如果你有兴趣深入研究动力学系统,我全心全意推荐这本书给你。(这是我最喜欢的数学书籍之一——它读起来像小说一样。书中阐述的质量和清晰度始终是我写作的持续灵感来源。)

13.3.2 常微分方程的(稍微更一般的)形式

让我们直接跳入深水,先从一个例子开始,以掌握微分方程。很可能,最简单的例子是方程

 ′ x(t) = x (t),

其中,微分是相对于时间变量 t 进行的。例如,如果 x(t) 是细菌群落的大小,那么方程 x^′(t) = x(t) 描述的是其种群动态,假设增长是无限的。可以将 x^′(t) 理解为种群增长的速度:如果空间和养分没有限制,每个细菌细胞都可以随时自由繁殖。因此,由于每个细胞都可以自由分裂,增长的速度与群落的大小成正比。

用通俗的语言来说,方程 x^′(t) = x(t) 的解是其导数等于自身的函数。稍加思考,我们可以得到一个解的族:x(t) = ce^t,其中 c ∈ℝ 是一个任意常数。(回想一下,e^t 是一个初等函数,我们在定理 82 中已经看到它的导数是它自己。)

如果你是一个视觉型的人,可以在图 13.15 上看到一些解的图像。

这里有两个关键要点:微分方程描述了随时间变化的动态过程,而且它们可能有多个解。每个解由两个因素决定:方程 x^′(t) = x(t),和初始条件 x(0) = x^∗。如果我们指定 x(0) = x^∗,那么常数 c 的值为

x(0) = ce0 = c = x∗.

因此,常微分方程有一组解,每一个解由初始条件确定。

所以,现在是时候更一般性地讨论微分方程了!

PIC

图 13.15:指数增长方程的一些解

定义 59. (一维常微分方程)

设 f : ℝ →ℝ 是一个可微函数。方程

x′(t) = f(x(t)) (13.3)

称为一阶齐次常微分方程。

当情况明确时,t 的依赖性通常会被省略,所以我们只写 x^′ = f(x)。 (一些资源用 ẋ 表示时间导数,这种符号可能源自牛顿。我们不会使用这个符号,尽管了解它是有益的。)

“一阶齐次常微分方程”这个术语不容易说出口,而且它充满了繁重的术语。所以,让我们解开其中的含义。

微分方程部分很明确:它是一个涉及导数的函数方程。由于时间 t 是唯一的变量,因此该微分方程是常微分方程。(与涉及多变量函数和偏导数的微分方程相对,后者将在以后讲解。)由于只涉及一阶导数,方程成为一阶微分方程。二阶方程则涉及二阶导数,以此类推。最后,由于右侧的 f(x) 不显式依赖于时间变量 t,因此该方程在时间上是齐次的。齐次性意味着支配我们动态系统的规则不会随着时间变化。

不要让 f(x(t)) 部分吓到你!例如,在我们的例子 x^′(t) = x(t) 中,f 的角色被映射为恒等函数 f(x) = x。一般来说,f(x) 建立了量 x(t)(可以是位置、密度等)及其导数(即变化率)之间的关系。

正如我们所看到的,我们通过微分方程和初始条件来思考,这些条件确定了解在一组函数中的位置。让我们将这一点写成正式的数学定义!

定义 60. (初值问题)

设 x^′ = f(x) 是一阶齐次常微分方程,且 x[0] ∈ℝ 为任意值。系统

( |{ ′ x = f (x ) |( x(t0) = x0

被称为初值问题。如果函数 x(t)满足这两个条件,则称其为初值问题的解。

最常见的是,我们选择 t[0]为 0。毕竟,我们可以自由选择时间的原点。

不幸的是,事情并不像看起来那么简单。通常,微分方程和初值问题是很难求解的。除了少数简单的例子,我们无法找到精确的解。(当我说“我们”时,我指的是地球上的每一个人。)在这种情况下,我们可以做两件事:要么通过数值方法构造近似解,要么转向定性方法,研究解的行为,而不需要实际找到它们。

我们将讨论这两者,但首先让我们转向定性方法。如我们所见,从几何的角度来看,可以深刻理解微分方程是如何运作的。

13.3.3 微分方程的几何解释

当无法找到解析解时,我们会寻求对解的定性理解,关注局部和长期行为,而不是公式。

假设给定一个微分方程

x′(t) = f(x(t)),

你对一个特定的解感兴趣,它在时间 t[0]时假设值为 x^∗。

例如,假设你正在研究细菌群体的动态,并希望提供一个预测模型,以适应你最新的测量值 x(t[0]) = x^∗。在短期内,你的解会走向哪里?

我们可以立即注意到,如果 x(t[0]) = x^∗且 f(x) = 0,则常数函数 x(t) = x 是一个解!这些被称为平衡解,而且它们非常重要。所以,让我们给出一个正式定义!

定义 61(平衡解)

x′(t) = f(x(t)) (13.4)

设为一阶齐次常微分方程,并让 x^∗∈ℝ为任意点。如果 f(x^∗) = 0,则称 x^∗为方程 x^′ = f(x)的平衡点。

对于平衡点,常数函数 x(t) = x^∗是方程(13.4)的解。这被称为平衡解。

思考我们反复出现的例子,最简单的常微分方程 x^′(t) = x(t)。如前所述,我们可以将这个方程解释为在理想条件下无限制的种群增长模型。在这种情况下,f(x) = x,而只有当 x = 0 时,这个方程的解才为零。因此,常数解 x(t) = 0 是一个解。这是完全合乎逻辑的:如果一个种群的个体数量为零,那么它的大小就不会发生变化。换句话说,系统处于平衡状态。

这就像一个停止运动并到达底部静止点的摆。然而,摆有两个平衡态:一个在顶部,一个在底部。(假设质量由一根无质量的杆支撑,否则它会坍塌。)在底部,你可以尽情推摆的质量,它会返回静止状态。然而,在顶部,任何轻微的推力都会打破平衡状态,摆将永远无法恢复到平衡。

为了阐明这一现象,让我们来看另一个例子:著名的逻辑方程

x′(t) = x(t)(1 − x(t))。 (13.5)

从种群动态的角度来看,如果我们最喜欢的方程 x^′(t) = x(t)描述了细菌群落的无限制增长,那么逻辑方程则模拟了在资源限制下的种群增长。如果我们假设 1 是我们种群的总容量,那么当种群接近这一限制时,增长会变得越来越困难。因此,种群的变化率 x^′(t)可以通过 x(t)(1 −x(t))来建模,其中 1 −x(t)这一项会随着群体接近承载能力而减缓增长过程。

我们可以通过将 f(x) = x(1 −x)代入通用形式(13.3)来写出逻辑方程。你还记得关于导数与单调性关系的定理 84 吗?将其转化为微分方程 x^′ = f(x),这揭示了我们解的流动!具体来说,

 (| |||{ 当 f(x) > 0 时 x(t)递增, || 当 f(x) < 0 时 x(t)递减, || 当 f(x) = 0 时 x(t)恒定。

我们可以在所谓的相位图中可视化这个过程。

PIC

图 13.16:x^′ = x(1 −x)的解的流动,在相位图上可视化。(箭头表示给定初值的解的方向。)

因此,单调性描述了长期行为:

L(U,V ) = {f : U → V | f 是线性}(13.6)

通过一些计算(其细节对我们来说并不重要),我们可以得到我们可以将解写为 noindent。

x(t) = ---1----, 1 + ce−t

其中 c ∈ℝ是一个任意常数。对于 c = 1,这是著名的 S 型函数。

你可以手动检查这些确实是解。我们甚至可以绘制它们,如下图 13.17 所示。

PIC

图 13.17:逻辑微分方程 x^′ = x(1 −x)的解

如图 13.17 所示,解的单调性正如我们在(13.6)中预测的那样。

我们可以根据附近解的长期行为来表征平衡态。(在我们的逻辑方程中,平衡态是 0 和 1。)这可以与函数 f 的局部行为相联系:如果它在平衡点 x^∗周围减小,它就会吸引附近的解。另一方面,如果 f 在 x^∗周围增大,则附近的解会被排斥。

这引出了稳定和平衡不稳定点的概念。

定义 62.(稳定和平衡不稳定点)

令 x^′ = f(x) 为一阶齐次常微分方程,并假设 f 是可微的。此外,设 x^∗ 为该方程的平衡点。

x^∗ 被称为稳定的平衡点,如果存在一个邻域 (x^∗−𝜖, x^∗ + 𝜖),对于所有的 x[0] ∈ (x^∗−𝜖, x^∗ + 𝜖),初值问题的解

( |{ x′ = f(x) |( x(0) = x0

收敛到 x^∗。(也就是说,lim[t→∞]x(t) = x^∗ 成立。)

如果 x^∗ 不是稳定的,那么它被称为不稳定。

在逻辑斯蒂常微分方程 x^′ = x(1 −x) 的情况下,x^∗ = 1 是稳定平衡点,而 x^∗ = 0 是不稳定平衡点。这是符合其种群动态解释的:平衡点 x^∗ = 1 意味着种群处于最大容量。如果种群数量略高或略低于容量 1,部分个体会因饥饿而死亡,或者种群达到其约束。另一方面,无论种群有多小,在这个理想模型中,它永远不会灭绝。

回想一下微分定理 84 如何表征可微函数的单调性?有了这个,我们就有了一个简单的工具,帮助我们判断一个给定的平衡点是否稳定。

定理 90.

令 x^′ = f(x) 为一阶齐次常微分方程,并假设 f 是可微的。此外,设 x^∗ 为该方程的平衡点。

如果 f^′(x)/span>0,则 x 是一个稳定的平衡点。

稳定平衡的概念是基本的,即使在最一般的情况下也是如此。此时,是时候退后一步,提醒我们自己为什么在这里:为了理解梯度下降。如果稳定平衡让你想起梯度下降过程所收敛到的局部最小值,那并非偶然。我们已经准备好了解幕后隐藏的内容。

13.3.4 梯度上升的连续版本

现在,让我们讨论最大化一个函数 F : ℝ → ℝ。假设 F 是二阶可微的,并且我们将它的导数记作 F^′ = f。幸运的是,可以通过查看 f(x) = 0 和 f^′(x)/span>0 来找到 F 的局部最大值(定理 87)。

这看起来熟悉吗?如果 f(x) = 0 确实成立,那么 x(t) = x 是一个平衡解;且由于 f′(x∗)/span>0,它也会吸引附近的解。这意味着,如果 x[0] 来自吸引盆地,并且 x(t) 是初值问题的解

L(U,V ) = {f : U → V | f 是线性映射}(13.7)

那么,lim[t→∞]x(t) = x^∗。换句话说,解收敛到 x^∗,这是 F 的局部最大值!这是连续版本的梯度上升。

我们很高兴,但有一个问题。我们已经讨论过解微分方程有多困难。对于一般的 F,我们没有办法实际找到解。幸运的是,我们可以对它们进行近似。

13.3.5 梯度上升作为离散化的微分方程

在实际研究微分时,我们看到导数可以通过前向差分进行数值近似。

x ′(t) ≈ x(t+-h)−-x-(t), h

其中 h/span>0 是一个小步长。如果 x(t)确实是初值问题的解(13.7),那么我们很幸运!使用前向差分,我们可以从 0 出发,采用小步长近似 x(h),将前向差分代入微分方程。准确地说,我们有

x(h)−-x-(0)- h ≈ f (x (0)),

从中

x(h) ≈ x (0) + hf(x(0))

可得。通过定义 x[0]和 x[1]为

x0 := x(0), x := x + hf(x ), 1 0 0

我们有 x[1] ≈ x(h)。如果这看起来像是梯度上升的第一步(13.1),那么你走在正确的道路上。再次使用前向差分,这次从点 x(h)开始,我们得到

x(2h) ≈ x(h)+ hf (x(h)) ≈ x1 + hf (x1),

因此,通过定义 x[2] := x[1] + hf(x[1]),我们得到 x[2] ≈ x(2h)。注意,在 x[2]中,积累了两种类型的近似误差:首先是前向差分,然后是上一步的近似误差。

这激励我们定义递归序列

L(U,V ) = {f : U → V | f is linear}(13.8)

它通过 x[n]近似 x(nh),正如其定义所暗示的那样。这个递归序列就是梯度上升本身,而小步长 h 就是学习率!

查看(13.1),如果你不相信我。13.8 被称为欧拉方法。

不深入细节,如果 h 足够小并且 f“表现得当”,欧拉方法将收敛到平衡解 x^∗。(“表现得当”是什么意思呢?)

我们只需要再做一步:将一切变成梯度下降而不是上升。这非常简单,因为梯度下降就是对−f 应用梯度上升。想想看:最小化一个函数 f 就等同于最大化它的负值−f。这样,我们就完成了!著名的梯度下降是动态系统向其稳定平衡收敛的结果,这非常美妙。

13.3.6 梯度上升的实际应用

为了看到梯度上升(即欧拉方法)在实际中的应用,我们应该回到我们熟悉的例子:逻辑斯蒂方程(13.5)。所以,假设我们想找到该函数的局部最大值。

 1 1 F (x) = -x2 − -x3, 2 3

如图 13.18 所示。

PIC

图 13.18:F(x) = 12x² −13x³的图形

首先,我们可以运用学到的知识,利用导数 f(x) = F^′(x) = x(1 −x) 来找最大值,得出在 x^∗ = 1 处有一个局部最大值。(不要只听我说,查阅定理 87,自己推导一下!)

由于 f(x) = F^′(x) = 0 且 f^′(x)/span>0,点 x 是逻辑方程的稳定平衡点

x′ = x(1 − x).

因此,如果初始值 x(0) = x[0] 足够接近 x^∗ = 1,初值问题的解 x(t) 将是

( ||| x′ = x(1− x), |{ x(0) = x0, |||| (

那么 lim[t→∞]x(t) = x^∗。(实际上,我们可以从无限区间 (0,∞) 中选择任何初始值 x[0],并且收敛性将成立)。通过欧拉方法的离散化,我们得到递归序列

 x0 = x (0), xn+1 = xn + hxn (1− xn).

这个过程由图 13.19 可视化。

PIC

图 13.19:通过欧拉方法求解 x^′ = x(1 −x)。 (为了可视化,初始值设为 t[0] = −5。)

我们甚至可以取欧拉方法提供的离散解,并将其绘制在 x −F(x) 平面上。

PIC

图 13.20:将欧拉方法映射到 x, F(x) 平面

如果你查看图 13.20,你会看到这是 F 的梯度上升!如果你考虑 F^′ = f 并且考虑欧拉方法给出的解是

L(U,V ) = {f : U → V | f 是线性的}(13.9)

你会注意到(13.9)正是我们定义梯度上升的方法。

13.4 小结

最后,我们做到了。直到这一章之前,我们还没有接触到机器学习,但现在,我们已经进入了它的核心。梯度下降是训练神经网络的第一算法。是的,即使是最先进的神经网络。

一切都始于微积分。为了达到梯度下降的高度,我们研究了单调性、局部极值和导数之间的关系。这个模式很简单:如果 f^′(a)/span>0,那么 f 是递增的;但如果 f^′(a)/span>0,那么 f 在 a 周围是递减的。从物理角度讲,如果速度为正,物体在远离;但如果速度为负,物体在靠近。

基于这一观察,我们可以推导出找到局部极值和极大值的必要和充分条件:如果 f^′(a) = 0

  • 如果 f^(′′)(a)/span>0,那么 a 是局部最小值,

  • 但如果 f^′(a) = 0 且 f^(′′)(a)/span>0,那么 a 是局部最大值。

所以,找到局部极值应该和解 f^′(a) = 0 一样简单,对吧?理论上,不是,因为当 f^(′′)(a) = 0 时,结果是未定的。实践中,仍然不行,因为即使是复杂的 f,也很难找到 f^′,更不用说解 f^′(x) = 0 了。

然而,还是有办法的。我们可以采用带有梯度下降的迭代方法:如果学习率 h 和起始点 x[0] 选择得当,那么由以下递归序列定义的

xn+1 = xn − hf′(xn )

收敛到局部最小值。

一如既往,当一个问题得到解决时,往往会产生十个其他问题。例如,梯度下降可能无法收敛,或者会陷入局部最小值而不是找到全局最小值。但这只是我们面临的最小问题。真正的问题是,我们在实践中必须优化多个变量的函数,通常参数的范围可以达到数十亿。

在我们继续学习多变量微积分之前,还有一个话题需要讲解。我曾多次提到过积分,即神秘的“逆向微分”。现在是时候看看它是什么,以及为什么它对于学习高级数学至关重要。

13.5 问题

问题 1. 找出 f(x) = sinx 的局部最小值和最大值。

问题 2. 使用二阶导数法则找出 f(x) = 2x³ + 5x² + 4x + 6 的局部最小值和最大值。

问题 3. 设 f : ℝ →ℝ 为可微函数。由以下递推式定义的序列

δn+1 = αδn − hf′(xn), xn+1 = xn + δn,

其中 δ[0] = 0,x[0] 为任意值,称为带动量的梯度下降法。实现它!

加入我们的 Discord 社区

和其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者交流,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

积分

当我们在第十二章首次遇到导数的概念时,我们通过物理学中的一个例子进行了介绍。正如牛顿所创建的,导数描述了通过时间-距离图计算出的物体的速度。换句话说,速度可以从时间-距离信息中推导出来。

给定速度能否重构出距离?从某种意义上说,这正是微分的逆过程。

这些问题如果我们仅仅从最一般的角度来看,很难回答,因此我们考虑一个特殊的情况。假设我们的物体以恒定速度 v(t) = v[0]ms-运动,持续时间为 T 秒。通过一些基本的逻辑推理,我们可以得出总的移动距离是 v[0]T 米。

当我们查看时间-速度图时,可以立即看到,距离就是时间-速度函数图 v(t) = v[0]下的面积。

v(t)的图像描述了一个宽度为 v[0],长度为 T 的矩形,因此它的面积确实是 v[0]T。

PIC

图 14.1:物体的时间-速度图,物体以恒定速度运动

在一般情况下,v(t)下的面积是否等于行驶的距离?例如,当时间-速度图像看起来像这样时会发生什么?

PIC

图 14.2:物体的时间-速度图,物体以变化的速度运动

这里速度不是恒定的。在这种情况下,我们可以做一个简单的技巧:将时间区间[0,T]划分为更小的区间,并在这些区间上将物体的运动近似为匀速运动。

PIC

图 14.3:用匀速运动进行近似

如果时间区间[t[i],t[i+1]]足够精细,所行驶的距离将大致匹配以平均速度进行的匀速运动,即,如果我们引入符号

vi := 在时间区间[ti−1,ti]内的平均速度,i = 1,2,...,n,

我们应该有

∑n vi(ti − ti−1) ≈ 在[0,T]期间的总行驶距离。 i=1

我们可以将整个过程看作用阶梯常数函数 vapprox 来逼近 v(t)。从这个角度来看,我们有

 n ∑ vi(ti − ti−1) = vapprox(t)下的面积,i=1

vapprox(t)下的面积 ≈ v(t)下的面积。

(非常)粗略地说,如果时间区间[t[i],t[i+1]]的粒度变得极其小,近似值将变成等式。因此,

在[0,T]期间的总行驶距离 = v(t)在[0,T]中的面积。

我们需要记住两个关键点:如果 s(t)是行驶的距离,v(t)是速度,那么

  • v(t)是导数 s^′(t),

  • 而 s(T)是图形 v(t)在 0 和 T 之间的面积。

换句话说,计算曲线下的面积就相当于反向微分。这个过程称为积分。

不幸的是,事情并不像看上去那么简单。我们在上述讨论中错过了很多数学细节。比如,和下面的和

∑n vi(ti − ti−1) i=1

如果 [0,T] 的划分变得更细,极限会收敛吗?极限是否依赖于划分?我们能否为所有函数定义“图形”下的面积?像 Dirichlet 函数,由

L(U,V ) = {f : U → V | f is linear}(14.1)

我们如何在实践中计算 ∑ [i=1]^nvi 的极限?此外,这一切与机器学习有什么关系?

系好安全带!接下来是对积分的严谨研究,解答所有这些问题。

第十七章:14.1 理论中的积分

让我们为直观的解释构建一个坚实的理论基础!设 f : [a,b] →ℝ 为一个任意有界函数,我们的目标是计算图形下方的有符号面积。(注意,如果图形低于 x 轴,则有符号面积为负。在上述的时间-速度图例中,这相当于倒退,从而减少从起点出发的行进距离。)

设 a = x[0]/span>x[1]/span>…/span>x[n] = b 为区间 [a,b] 的一个任意划分。

为了便于记号,我们也将这个划分记为 X = {x[0],…,x[n]}。X 的粒度(或网格)由

|X | := max |xi − xi−1|, i=1,...,n

这是 X 中最大间隔的长度。请注意,划分不一定是均匀的,因此 jx[i] −x[i−1]j 不是常数。

我们将使用类似于挤压原理的论证(推论 3)来使得这个近似思想更加严谨。(你知道的,就是我们用分段常数函数代替了移动物体的速度的那个原理。)我们将不再使用 f(x) 在每个区间 [x[i−1],x[i]] 上的平均值,而是通过使用上下估算来提供估算值。

mi := inf f (x ) x∈[xi−1,xi]

以及

Mi := sup f(x). x∈ [xi− 1,xi]

从数学角度来看,极小值和极大值比平均值更容易处理。现在我们可以通过从上方和下方用分段常数函数来近似 f(x)。这一点通过图 14.4 进行了可视化。

PIC

图 14.4:使用划分 X 估算函数 f 曲线下的面积

我们的计划是将面积挤压在上下和之间。

L[f, X] := ∑[i=1]^n mi (14.2)

以及

U[f, X] := ∑[i=1]^n Mi,(14.3)

然后研究这两个是否匹配。(如常,如果从上下文中能够清楚看出,则可以省略对 f 和 X 的依赖。)

从构造中可以清楚地看出,

L[f,X ] ≤ area under the graph ≤ U [f,X ]

是否对任何划分 X 都成立?

随着划分 X 的粒度趋近于零,理想情况下,L[f,X] 和 U[f,X] 都将收敛到相同的数值。直观地,这个共同的极限应该是“函数图下的面积”,但目前我们对面积的理解还不够通用,无法做出如此大胆的声明。例如,如何定义 Dirichlet 函数下的“面积”,它由 (14.1) 定义?正如我们很快将看到的,积分将会推广我们对面积的启发式理解。为了达到这一点,我们还有很多工作要做。首先,我们需要更仔细地观察划分。

14.1.1 划分及其细化

我们需要引入一些关于细化划分的基本事实,以便构建关于逼近和 L[f,X] 和 U[f,X] 收敛性的数学正确论证。

定义 63.(划分的细化)

设 X = {x[0],…,x[n]} 和 Y = {y[0],…,y[m]} 为 [a,b] 的两个划分。我们说 Y 是 X 的细化,当且仅当 X ⊆Y。

我们可以很容易地将其可视化。

PIC

图 14.5:划分 Y,作为 X 的细化

细化对理解积分的工作原理至关重要。核心原因之一就是以下结果。

定义 64. 上和与下和的单调性

设 f : [a,b] →ℝ 为有界函数,X 和 Y 为 [a,b] 的两个划分。假设 Y 是 X 的细化。则

L[f, X] ≤ L[f, Y] (14.4)

U[f, Y] ≤ U[f, X]。(14.5)

证明。我们将证明 L[f,X] ≤ L[f,Y ],如 (14.5) 所示,推理类似。假设 x[i−1] ≤ y[j] ≤ ⋅⋅⋅ ≤ y[l] ≤ x[i]。从数学上讲,我们有

 inf f(x) ≤ inf f(x), k = j + 1,...,l. x∈[xi−1,xi] x∈[yk−1,yk]

由于 x[i] − x[i−1] = ∑ [k=j+1]^ly[k] − y[k−1],上述推论意味着

L(U,V ) = {f : U → V | f 是线性的}(14.6)

如果这些数学形式让你很难跟上,不用担心。只要看看下面的图 14.6,它总结了我们到目前为止所做的所有工作。

PIC

图 14.6:下和的细化

由于 L[f,X] 和 L[f,Y ] 由类似于 (14.6) 的部分组成,对 i 求和,立即得出 L[f,X] ≤ L[f,Y ]。

我们快到了。我们只剩下一件事要展示:对于任意两个划分,下和总是小于上和。因此,可以应用挤压原理(推论 3)来证明,在某些情况下,下和和上和会收敛到相同的极限。

为此,我们需要一个关于划分的简单但重要的事实。

命题 3.

X Y 为 [a,b] 的两个划分。则存在一个划分 Z ,它是 X Y 的细化。

证明。很容易看出 Z = X ∪Y 满足我们的要求。

上述 Z 被称为 X 和 Y 的互细化。通过这个概念,我们可以展示上和下和之间的一个基本关系。

命题 4.

设 f : [a,b] → ℝ 是一个有界实函数,且 X 和 Y 是区间 [a,b] 的两个分割。那么

L [f,X ] ≤ U[f,Y ]

成立。

证明。设 Z 是 X 和 Y 的互细化,正如前述结果所保证的那样。那么,(14.4) 和 (14.5) 说明

L[f,X ] ≤ L [f,Z ] ≤ U [f,Z ] ≤ U [f,Y],

这正是我们要证明的。

14.1.2 里曼积分

让我们将区间 [a,b] 上所有分割的集合表示为 ℱ[a,b]:

ℱ [a,b] = {X : X 是 [a,b] 的一个分割}。

现在我们已经准备好定义函数的积分为将上和下和的界限分开的单一值。

定义 65. (里曼可积性)

设 f : [a,b] → ℝ 是一个有界函数。如果

X∈suℱp[a,b]L[f,X ] = X i∈nℱf[a,b]U [f,X ].

这个值被称为 f 在 [a,b] 上的里曼积分(或简称积分),表示为

∫ b f (x )dx. a

在 ∫ [a]^b f(x)dx 中的函数 f 被称为被积函数。我们如何计算积分本身?最困难的方法是定义一个分割序列 X[n] 并证明

 lim L[f,Xn ] = lim U[f,Xn ], n→ ∞ n→ ∞

所以这个数必定是 ∫ [a]^b f(x)dx。我们很快会看到简单的方法,但先来看一个示例,演示这个过程。

让我们计算 ∫ [0]¹ x² dx!

最简单的方法是使用均匀分割 X[n] = i∕n [i=0]^n,从而得到

 ∑n i − 1 21 L[x2,Xn ] = (-----) -- i=1 n n 1 ∑n = -3- (i− 1)2. n i=1

由于 ∑ [k=1]^n k² = n(n+1)6(2n+1)(可以通过归纳法证明),很容易看出

 2 1 nli→m∞ L[x ,Xn ] = 3.

通过类似的论证,你也可以检查到 lim[n→∞] U[x², X[n]] = 1 3,因此,∫ [0]¹ x² dx 存在并且

∫ 1 1 x² dx = -. 0 3

尽管这种方法适用于像 f(x) = x² 这样简单的情况,但对于更复杂的函数,它会出现问题,因为计算上下和的极限可能很困难。此外,选择合适的分割也是一个挑战。例如,你能通过定义计算 ∫ [0]^π sin(x)dx 吗?

因为我们懒(就像任何好的数学家一样),我们想找到一种通用方法来计算积分。上下和的引入使得积分的概念在数学上更加精确。结合挤压原理(推论 3),它们用于提供定义。

然而,一旦我们知道一个函数是可积的,其他工具就可以使用了。例如,常见的近似和方法,正如我们接下来要看到的那样。

定理 91. 设 f : ℝ → ℝ 为任意有界函数,且 Xn = {x0,n,...,xn,n} [a,b] 上的划分序列,满足 |Xn | → 0 。则当且仅当极限

 ∑n lim f(ξi)(xi,n − xi− 1,n) n→∞ i=1

存在,在这种情况下,

 ∑n ∫ b nl→im∞ f (ξi)(xi,n − xi−1,n) = f(x)dx i=1 a

成立,其中 ξ[i] ∈ [x[i−1,n],x[i,n]]。

(注意 jX[n]j → 0 意味着 X[n] 的最大子区间的长度变得非常小。换句话说,X[n] 的分辨率变得非常大。)

我们不会证明上述定理,因为证明过程较为技术性,且不提供有价值的见解。然而,关键点很明确:在下和和上和中的局部最小值和最大值可以被任何局部值替代。

为了简便,我们将这个和表示为

S[f, X, \xi_X] = \sum_{i=1}^n f(\xi_i)(x_i - x_{i-1}) (14.7)

对于任意的 X = {x[0],…,x[n]} 和 ξ[X] = {ξ[1],…,ξ[n]},其中 ξ[i] ∈ [x[i−1],x[i]]。

14.1.3 积分作为微分的逆操作

现在我们理解了积分的数学定义,是时候找到一些实际使用的工具了。最重要的结果是牛顿-莱布尼茨公式,它以牛顿和莱布尼茨的名字命名,他们是微积分的发明者。(有趣的事实:这两位科学家是独立发现微积分的,并且终生是死对头。)

定理 92. (微积分基本定理,亦称牛顿-莱布尼茨公式)

设 f : ℝ →ℝ 为在 [a,b] 上可积的函数,且存在一个 F : ℝ →ℝ,使得 F^′(x) = f(x)。则

∫ₐᵇ f(x) dx = F(b) − F(a) (14.8)

成立。

换句话说,通过定义 x→F(a) + ∫ [a]^xf(x)dx,我们可以有效地从导数重构一个函数。

证明。设 a = x0 <x1 <⋅⋅⋅ <xn = b 为任意的 [a,b] 划分。根据拉格朗日中值定理(定理 89),对于所有 i = 1,...,n ,存在一个 ξi ∈ (xi−1,xi) ,使得

 ′ F(xi)− F (xi−1) = F (ξi)(xi − xi−1) = f (ξ )(x − x ). i i i−1

因此,我们可以将这些数相加,去除除了第一个和最后一个元素之外的所有元素:

∑n ∑n f(ξi)(xi − xi−1) = F(xi)− F (xi− 1) i=1 i=1 = F (b)− F (a ).

另一方面,由于下和上和的性质,我们有

 ∑n L[f,X ] ≤ f(ξ)(x − x ) ≤ U[f,X ] i=1 i i i−1

由于 f 可积,挤压原理(推论 3)和定理 91 表明

∫ b ∑n f (x )dx = f(ξi)(xi − xi− 1) = F (b) − F(a) a i=1

必须成立。这就是我们需要证明的内容。

备注 10. (函数的增量)

为了简化,函数 F 在区间 [a,b] 上的增量也表示为

 x=b [F(x)]x=a := F (b)− F (a ).

因此,根据微积分基本定理(定理 92),

∫ b f(x)dx = [F (x)]x=x=ba a

如果 F^′(x) = f(x),则成立。

请注意,积分对 f(x) 在可数多个点处的值的改变是无关的。更准确地说,假设 f : ℝ →ℝ 是在 [ − 1,1] 上可积的函数。让我们在一个单一的点上改变它的值并定义

 ( |{ f(0)+ 1 如果 x = 0 f∗(x) = |( f(x) 否则。

如果给定一个划分 −1 = x[0]/span>…/span>x[k−1] ≤ 0/span>x[k]/span>…/span>x[n] = 1,那么

|L[f,X] − L[f∗,X ]| = | inf f (x)− inf f∗(x)|(xk − xk− 1) x∈[xk−1,xk] x∈[xk−1,xk] ◟------------=◝:◜m--------------◞ k

 ∗ ∗ |U [f,X] − U[f ,X ]| = |x∈s[xukp−1,xk]f(x)− x∈[sxuk−p1,xk]f (x )|(xk − xk −1) ◟-------------◝◜--------------◞ =:Mk

我们可以选择一个划分,使得 xk − xk−1 <𝜖 对于某个任意的 𝜖 >0 ,因此,

|L[f,X ]− L[f∗,X ]| 和 |U [f,X ]− U[f∗,X ]|

可以根据需要使其变得任意小。这意味着

∫ b ∫ b f(x)dx = f∗(x)dx. a a

因此,说积分是微分的逆操作在数学上稍显不精确。对于一个可微函数 F(x),它的导数是唯一的,但有无限多种函数的积分 F(a) + ∫ [a]^xg(y)dy 可以重建 F。

微积分基本定理使我们能够用积分的形式表述拉格朗日均值定理(定理 89)。

定理 93.(定积分的均值定理)

设 f : ℝ →ℝ 是在 [a,b] 上连续的函数。那么存在一个 ξ ∈ [a,b],使得

∫ b f(x)dx = (b− a)f(ξ). a

证明。根据微积分基本定理(定理 92),该函数

 ∫ t F (t) = a f(x)dx

在 [a,b] 上是可微的,且 F^′(t) = f(t)。

因此,拉格朗日均值定理(定理 89)给出了

F-(b)−-F-(a-) b− a = f (ξ)

对于某个 ξ ∈ (a,b),由此得出

∫ b f(x)dx = F(b)− F (a) a = (b− a)f(ξ)

如此。这就是我们需要证明的。

在经历了这些理论之后,你可能会问:积分和机器学习有什么关系?虽然不进行严格的数学推导,以下是一个(非常)简要的概述。

首先,你可以将积分看作是算术平均数的连续推广。正如你所见,对于等距划分,近似求和

 ∑n S[f,X,ξ] = 1- f(ξi) n i=1

恰好是 f(ξ[1]),…,f(ξ[n]) 的平均值。在机器学习中,平均值常常用来表示各种量,比如均方误差。想一想:损失函数通常是某些单个损失的平均值。在足够细的尺度上,平均值变成了积分。

与线性代数和微积分一起,机器学习的核心支柱是概率论和统计学,它为我们提供了一种基于观察结果建模世界的方法。概率和统计是科学与决策的逻辑。在这其中,积分被用来表示概率、期望值、信息等多种概念。如果没有严格的积分理论,我们无法在某些阶段构建更复杂的概率模型。

14.2 实际中的积分

尽管我们理解积分的含义,但在实际中计算它们仍然非常困难。与微分不同,解析地评估积分通常非常困难,有时几乎不可能。公式 (92) 表示,关键在于找到其导数为被积函数的函数,称为反导数或原始函数。这比你想象的要难得多。不过,还是有一些工具可以帮助我们,接下来我们将专注于研究最重要的这些工具。

通常,关键在于找到反导数,因此我们引入符号

 ∫ F(x) = f (x)dx,

对于满足 F^′ = f 的函数。(有时我们简写为 F = ∫ fdx。)请注意,由于 (F + 常数)^′ = F^′,因此反导数 ∫ f(x)dx 不是唯一确定的。然而,这对我们来说不是问题,因为牛顿-莱布尼茨公式指出:

∫ b f (x )dx = F (b)− F (a). a

因此,任何额外的常数都会被消除。

掌握了这一点后,我们准备深入实际评估积分。

14.2.1 积分与操作

如我们在多次讨论中所见(例如,在定理 80 中讨论微分法则时),操作与加法、乘法以及其他操作之间的关系对于深入理解和开发实际工具非常有用。

积分也是如此。与之前类似,积分的线性性质是我们评估它的主要工具。

定理 94.(黎曼积分的线性)

设 f,g : ℝ → ℝ 是在 [a,b] 上可积的两个函数。那么

(a)∫ [a]^b(f(x) + g(x))dx = ∫ [a]^bf(x)dx + ∫ [a]^bg(x)dx,

(b)∫ [a]^bcf(x)dx = c∫ [a]^bf(x)dx。

证明:(a)如果 f 和 g 可积,那么对于任何 𝜖 > 0,都存在分割 X[f]、X[g],使得:

∫ ∫ b b a f(x)dx− 𝜖 ≤ L [f,Xf ] ≤ U [f,Xf] ≤ a f(x)dx + 𝜖

∫ b ∫ b g(x)dx− 𝜖 ≤ L [g,Xg ] ≤ U [g,Xg ] ≤ g(x )dx + 𝜖, a a

其中,较低和较高的和分别由(14.2)和(14.3)定义。因此,对于互相细化的 X = X[f] ∪ X[g],我们有:

L[f,Xf ] ≤ L [f,X ], L [g,Xg ] ≤ L [g,X ], U[f,X ] ≤ U [f,Xf ], U[g,X ] ≤ U [g,X ] g

由于命题 4 的存在。因此,

∫ ∫ b b f(x)dx+ g(x)dx− 2𝜀 ≤ L [f,Xf ]+ L [g,Xg ] a a ≤ L [f,X ]+ L [g,X ] ≤ S [f,X, ξX]+ S[g,X,ξX ] ≤ U [f, X]+ U[g,X ] ≤ U [f, Xf]+ U [g,Xg] ∫ ∫ b b ≤ a f (x)dx+ a g(x )dx + 2𝜀,

其中 S 的定义见(14.7)。从这个定义也可以看出,

S[f + g,X, ξX ] = S[f,X, ξX]+ S [g,X, ξX].

因此,

这意味着

定理 91 关于近似和 S 的求和公式表明,f + g 在 [a,b] 上是可积的,并且

(b) 这可以从事实得出,S[cf,X,ξ[X]] = cS[f,X,ξ[X]]。

14.2.2 分部积分法

正如我们在学习求导法则时所了解到的(定理 80),对于任意的 f 和 g,我们有

(fg)′ = f ′g + fg′.

将此逻辑应用于不定积分,

 ∫ f g = (f ′g + fg′)dx

等式成立。稍微调整方程,我们得到分部积分法的公式:

∫ f′g dx = fg − ∫ fg′ dx. 14.9

这可以通过以下定理总结。

定理 95.(分部积分法)

设 f,g : ℝ →ℝ 是两个函数。如果它们在区间 [a,b] 上都可微,那么

∫ b ∫ b f′(x)g(x)dx = [f(x)g(x)]x=b − f(x)g′(x)dx a x=a a

这个等式成立。

这对我们有什么用处呢?考虑一种情况,其中 f 的不定积分和 g 的导数容易求得,但 fg 的不定积分却很难求。例如,您能快速计算以下积分吗?

∫ x log xdx

应用 (14.9) 时,取 f^′(x) = x 和 g(x) = log x 可立即得出

∫ ∫ 1-2 1- x log x = 2x log x− 2xdx 1 1 = -x2log |x|− -x2 + C, 2 4

其中 C ∈ℝ 是任意常数。

14.2.3 代换积分法

由于分部积分法公式是“对立”于乘积的求导规则,因此也有类似的链式法则。回想一下,对于两个可微函数,我们有

(f ∘ g)′(x) = f′(g(x ))g′(x).

将其转化为积分的语言,我们可以得到以下结果。

定理 96.(代换积分法)

设 f,g : ℝ → ℝ 是 [a,b] 区间上的可积函数。假设 f 是连续的,g 是可微的。那么,

∫ b ∫ g(b) f(g(y))g′(y)dy = f(x)dx a g(a)

等式成立。

这称为代换积分法。为了给你一个应用示例,考虑

∫ 2 x sin(x )dx.

当 y(x) = x² 时,我们有

∫ 1∫ xsin(x2 )dx = -- sin ydy 2 = − 1-cosy + C 2 = − 1-cos(x2)+ C, 2

其中 C ∈ℝ 是任意常数。

分部积分法和代换法是我们在纸面上计算积分的主要工具。大多数我们可能遇到的积分都可以通过这两条规则的创造性(并可能是迭代的)应用来求解。其步骤很简单:找到反导数,然后使用牛顿-莱布尼茨公式(定理 92)来计算积分值。

然而,这里存在一个严重的问题:反导数可能极难找到,甚至可能无法找到。这使得积分在符号计算中变得非常困难。例如,考虑

∫ −x2 e dx,

其中,函数 e^(−x²)描述了著名的高斯钟形曲线。令人惊讶的是,∫ e^(−x²) dx 无法用闭式公式表示!(也就是说,不能用有限的运算和仅有的初等函数表示。)并不是说数学家们不够聪明,找不到反导数的闭式公式;实际上,这种公式不存在。

因此,计算积分在数值上要简单得多。这与微分形成鲜明对比,微分在符号计算中很容易,但在数值计算中却很难。

14.2.4 数值积分

我们将再次采用近似方法,而不是使用符号计算来获取积分的精确值。之前的定理 91 向我们展示了积分是黎曼和的极限:

∫[a]^b f(x) dx = lim[n→∞] ∑[i=1]^n f(ξ[i]) (x[i,n] − x[i−1,n]), (14.10)

其中 X[n] = {x[0,n],…,x[n,n]} 是区间[a,b]的划分,ξ[i] ∈ [x[i−1,n],x[i,n]] 是任意的中间值。

换句话说,如果 n 足够大,那么和∑ [i=1]^n f(ξ[i])(x[i,n] − x[i−1,n])就接近于∫ [a]^b f(x) dx。这里有两个关键问题:首先,如何选择划分和中间值;其次,收敛速度有多快?

如果我们想使得(14.11)方法有效,我们必须设计一个具体的方法,规定 x[i]、ξ[i]的取值,并告诉我们应该选择多大的 n。这是一个极其丰富的主题,自从积分被引入以来,学者们一直在关注这一领域。因此,这里有很多内容需要讨论。为了简单起见,我们只关注核心要点。

最直接的方法是选择均匀划分,然后用一系列梯形来近似函数曲线下的面积。

也就是说,设 X = {a, a + b−a n, a + 2b−a n,…, b} 为一个等距划分,我们将利用该划分通过计算由划分和图形确定的梯形的面积来估算积分,如图 14.7 所示。

PIC

图 14.7:用连续梯形近似函数下的面积

由于梯形的面积为 ha+2b,因此区间[x[i−1],x[i]]下的面积近似为

 f (xi) + f(xi− 1) b− a (xi − xi−1)------2-------= -2n-(f(xi)+ f (xi− 1))

并且我们有近似值

L(U,V ) = {f : U → V | f is linear}(14.11)

这就是梯形法则。它可能看起来很复杂,但(14.11)只是 f(x[i]) 值的加权和。

它的收敛速度是二次的,如以下定理所述。

定理 97.(梯形法则)

设 f : [a,b] →ℝ 是一个二次可微的函数,并且设

 n I := 1-∑ f(xi−-1)+-f(xi)- n n 2 i=1

这是由梯形法则给出的近似值。然后

 ∫ b 3 | f (x )dx − In| = O((b-−-a)-). a n2

还有其他方法,例如辛普森法则,它通过分段二次函数来近似该函数。(而不是像梯形法则那样使用分段线性函数。)由于近似更精确,收敛速度也更快:辛普森法则以 O(n^(−4)) 的速度收敛。无需深入细节,它由下式给出

S[n] = b − a ----- 3n ∑[i=1]^(⌊n/2⌋) ( f(x[2i−2]) + 4f(x[2i−1]) + f(x[2i]) ), (14.12)

误差为

 ∫ b 5 | f(x)dx − S | = O((b-−-a)-), a n n4

其中 x[i] 再次是等距划分 x[i] = a + ib−na

公式(14.12)可能难以理解,但其本质保持不变:我们在给定点上计算函数值,然后取它们的加权和。

14.2.5 实现梯形法则

为了展示梯形法则是多么简单,我们来实践一下!为了保持简单,我们将其实现为一个接收另一个函数作为输入的函数。

def trapezoidal_rule(f, a, b, n): 
    # Define the partition of the interval [a, b] 
    partition = [a + i*(b - a)/n for i in range(n+1)] 

    # Evaluate the function at each partition point 
    vals = [f(x) for x in partition] 

    # Apply the trapezoidal rule 
    I_n = (b - a) / (2 * n) * (vals[0] + vals[-1]) + (b - a) / n * sum(vals[1:-1]) 

    return I_n

使用 NumPy 甚至可以更简单地实现这一点,不过我会把这留给你作为练习。我们来通过一个例子测试一下吧!

使用牛顿-莱布尼茨公式(定理 92),你可以验证

∫ 1 x2dx = 1. 0 3

(我们甚至用手工计算过,使用了下和上和。)将函数 lambda x: x**2 代入 trapezoidal_rule 后,我们可以看到这种方法确实是正确的。

import matplotlib.pyplot as plt 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure() 
    ns = range(1, 25, 1) 
    Is = [trapezoidal_rule(lambda x: x**2, 0, 1, n) for n in ns] 
    plt.axhline(y=1/3, color=’r’, label="/span>the true integral 
    plt.scatter(ns, Is, label="/span>trapezoidal_rule(f, a, b, n) 
    plt.ylim([0.3, 0.52]) 
    plt.title("/span>the trapezoidal rule 
    plt.show()

PIC

图 14.8:梯形法则

14.3 总结

在本章中,我们学习了积分,这是迄今为止技术上最具挑战性的主题之一。从直观上看,一个函数的积分描述了其图形下方的有符号面积,但从数学上讲,它由极限给出

∫ b ∑n f(x)dx = lim (xi − xi− 1)f (xi), a n→∞ k=1

其中 a = x[0]/span>x[1]/span>…/span>x[n] = b 是区间 [a,b] 的划分。当然,我们不常按定义来计算积分;我们有牛顿-莱布尼茨公式来处理这个问题:

∫ b f (x )dx = F (b)− F (a), a

其中 F 是所谓的反导数,满足 F^′(x) = f(x)。这就是为什么积分被认为是微分的逆过程。

正如我的一位教授曾经说的,符号微分很容易,数值微分很难。对于积分来说,情况正好相反:符号积分很难,数值积分很容易。我们已经学到了一些技巧来掌握符号部分,即分部积分法公式

∫ ∫ b ′ x=b b ′ a f(x)g(x)dx = [f(x)g(x)]x=a − a f(x)g (x)dx

以及代换积分公式

∫ b ∫ g(b) f(g(y))g′(y)dy = f(x)dx. a g(a)

当符号积分困难时(几乎总是很困难),我们可以借助数值方法,比如辛普森法则,它由以下公式给出

∫ b b − a⌊n∑∕2⌋ f(x)dx ∼ ----- (f(x2i−2) + 4f(x2i− i) + f(x2i)). a 3n i=1

尽管积分技术性很强且复杂,但如果你想理解数学,正确的阐述方式是极其重要的。用一系列矩形来近似复杂形状(如图 14.3 所示)是测度理论的基础,而测度理论又是概率论的基础。(实际上,用简单的东西来近似复杂对象是数学的基础。)

到目前为止,我们已经掌握了单变量函数的微分和积分。然而,单变量函数在实际中很少出现:在机器学习中,我们常常处理数百万或数十亿个变量。为了在实际中处理这些变量,我们将把我们所学的知识推广到更高维度。这就是多变量微积分的主题,我们的下一个重大里程碑。让我们开始吧!

14.4 问题

问题 1. 使用分部积分法求以下反导数。

(a) ∫ sin(x)cos(x)dx (b) ∫ xe^xdx (c) ∫ x²e^xdx (d) ∫ e^x sinxdx

问题 2. 使用代换积分法求以下反导数。

(a) ∫ xcos(x²)dx (b) ∫ sin(x)e^({-x)2}dx (c) ∫ sinx cosx- (d) ∫ x√x2-+-1dx

问题 3. 设 f : [a,b] →ℝ 为一个可积函数。证明

∫ b ∫ b | f(x)dx| ≤ |f(x)|dx. a a

问题 4. 设 f,g : [a,b] →ℝ 为两个可积函数,并且 jfj² 和 jgj² 也可积。证明

 ∫ ∫ ∫ b 2 b 2 b 2 | a f(x)g(x)dx| ≤ a |f(x)| dx a |g(x)| dx.

提示:回顾第二章关于规范空间的内容,找出一个与此不等式类似的结论。

问题 5. 著名的狄里赫勒函数定义为

 ( |{ 1 if x ∈ ℚ, D (x) = |( 0 otherwise.

D(x) 可积吗?

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,向其他读者提供解决方案,通过“问我任何问题”环节与作者交流,还有更多精彩内容。扫描二维码或访问链接加入社区。packt.link/math

PIC

第十八章

参考文献

  1. Ramalho, L. (2022). 《流利的 Python》(第 2 版)。O'Reilly Media, Inc.

  2. Rudin, W. (1976). 《数学分析原理》(第 3 版)。McGraw Hill 出版社。

  3. Spivak, M. (2008). 《微积分》(第 4 版)。剑桥大学出版社。

第三部分

多变量微积分

本部分包含以下章节:

  • 第十五章,多变量函数

  • 第十六章,导数与梯度

  • 第十七章,多变量优化

多变量函数

多变量微积分与单变量微积分有何不同?当我还是学生时,我有一位教授曾经说过类似的话:“多变量和单变量函数的行为是相同的,只不过你得写更多的东西。”

好吧,这个说法远离事实。试想一下我们在机器学习中做的事情:通过梯度下降训练模型;也就是说,找到一个使参数函数最小化的参数配置。在一维(虽然这个假设并不现实)中,我们可以通过导数来实现,就像我们在第 13.2 节中看到的那样。那么我们如何将导数扩展到多维呢?

多变量函数的输入是向量。因此,给定一个函数 f : ℝ^n →ℝ,我们不能仅仅定义

df- f(x0)-−-f(x) n dx (x0 ) = xli→mx0 x0 − x , x0,x ∈ ℝ

对应于定义 54 的类比。为什么?因为与向量 x[0] −x 的除法是没有定义的。

正如我们将看到的,多维微分要复杂得多。想想看:在一维中,只有两个方向,左和右。而即使是在二维中,每个点的方向也是无限多的。

那么,究竟什么是多变量函数呢?

第十九章:15.1 什么是多变量函数?

我们在第九章中介绍了函数,作为两个集合之间的映射。然而,我们只讨论了将实数映射到实数的函数。简单的标量-标量函数很适合传达思想,但我们周围的世界比这些可以描述的要复杂得多。在范围的另一端,集合-集合函数过于一般化,无法实际使用。

实际上,三个类别足够特殊,可以进行数学分析,但又足够通用,能够描述科学和工程中的模式:那些

  1. 将标量映射到向量,也就是说,f : ℝ →ℝ^n,

  2. 将向量映射到标量,也就是说,f : ℝ^n →ℝ,

  3. 以及那些将向量映射到向量的函数,也就是说,f : ℝ^n →ℝ^m。

标量-向量变种称为曲线,向量-标量变种称为标量场,而向量-向量函数则是我们所说的向量场。这个命名有点抽象,所以让我们看看一些例子。

标量-向量函数,或者称为曲线,是运动的数学表示。环绕地球运行的空间站描述了一条曲线。股票市场中的股票轨迹也是如此。

给你一个具体的例子,标量-向量函数

 ⌊ ⌋ cos(t) f(t) = ⌈ ⌉ sin(t)

描述了单位圆。这在图 15.1 中有所说明。

PIC

图 15.1:标量-向量函数,即曲线

并非所有曲线都是闭合的。例如,曲线

 ⌊ ⌋ | cos(t)| g(t) = | sin(t)| ⌈ ⌉ t

表示一个螺旋上升的运动,如图 15.2 所示。这些曲线称为开口曲线。

PIC

图 15.2:一条开放曲线

由于它们固有的描述轨迹的能力,标量-矢量函数在数学和科学中至关重要。你熟悉牛顿第二定律吗?它表明力等于质量乘以加速度。这可以通过方程 F = ma 来描述,这是一个常微分方程的实例。它的所有解都是曲线。

表面上看,标量-矢量函数与机器学习几乎没有关系,但实际上并非如此。尽管我们不会深入探讨它们,但它们在幕后有着重要的作用。例如,梯度下降就是一个离散化的曲线,正如我们在第 13.3 节看到的那样。

标量-矢量函数将是我们接下来几章的重点。当我说“多变量函数”时,我通常指的是标量-矢量函数。

想象一下山地景观的地图。它将高度——一个标量——映射到每个坐标,从而定义了表面。从数学角度来看,这只是一个函数 f : ℝ² → ℝ。

将标量场视为表面有助于构建几何直觉,给我们提供了一种可视化它们的方式,正如图 15.3 所示。(注意,对于大于二的维度,表面类比就不成立了。)

PIC

图 15.3:由标量-矢量函数给出的表面

首先让我们澄清符号。如果 f : ℝ^n → ℝ 是一个 n 变量的函数,我们可以写作 f(x),其中 x ∈ ℝ^n,或者写作 f(x[1],…,x[n]),其中 x[i] ∈ ℝ,如果我们想强调它对变量的依赖关系。n 变量的函数与单一向量变量的函数是相同的。我知道这看起来很混乱,但相信我,你很快就会习惯的。

为了给出一个标量-矢量函数的具体例子,我们来考虑压力。压力是力的大小与接触表面积之比:

p = F-. A

这可以看作是一个二变量函数:p(x,y) = x∕y。

为了说明在多维空间中问题可能变得多么复杂,考虑 (0,0) 周围的压力。尽管我们还没有讨论多变量函数的极限,但你觉得怎么样呢?

 x- (x,yli)→m(0,0)y

应该是?

基于我们为单变量函数定义极限的方式(见定义 51),

 xn- nl→im∞ yn

必须对所有可能的 x[n] 和 y[n] 选择匹配。这并非如此。考虑 x[n] = α²∕n 和 y[n] = α∕n,其中 α 是任意实数。通过这个选择,我们得到

 2 lim xn-= α-∕n-= α. n→ ∞ yn α∕n

因此,上述极限是没有定义的。我们在这里所做的只是沿着略微不同的轨迹逼近零,然而结果却完全混乱。在单变量的情况下,我们需要动用我们的智力才能产生这样的例子;而在多变量的情况下,简单的 x∕y 就足够了。

向量-向量函数称为向量场。例如,考虑我们的太阳系,用 ℝ³ 来建模。每个点都受到一个引力的影响,这个引力是一个向量。因此,引力可以通过 f : ℝ³ → ℝ³ 函数来描述,这也是向量场这个名称的由来。

尽管它们经常隐藏在背景中,向量场在机器学习中扮演着至关重要的角色。还记得我们在第 13.3 节中讨论的为什么梯度下降有效吗?(至少在一个变量的情况下。)我们在那儿遇到的所有微分方程都等同于向量场。

为什么?考虑微分方程 x^′ = f(x)。如果 x(t) 描述了一个物体的运动轨迹,那么它的导数 x^′(t) 就是它的速度。因此,我们可以将方程 x^′(t) = f(x(t)) 解释为在每个位置规定物体的速度。当物体在一维空间中运动时,这并不特别惊艳,但如果轨迹 x : ℝ → ℝ² 描述了平面上的运动,那么函数 f : ℝ² → ℝ² 可以直观地呈现出来。

例如,考虑一个简单的捕食者-猎物系统的人口动态。捕食者以猎物为食,因此在食物充足时,它们的数量会增长。反过来,过度捕食会减少猎物种群,导致捕食者的饥荒并减少它们的数量。这导致猎物种群的增长,循环再次开始。

如果 x1 和 x2 分别是猎物和捕食者种群的大小,那么它们的动态由著名的 Lotka-Volterra 方程描述:

x′1 = x1 − x1x2 ′ x2 = x1x2 − x2.

如果我们将轨迹表示为标量-向量函数

 ⌊ ⌋ x : ℝ → ℝ2, x (t) = ⌈x1(t)⌉, x2(t)

然后导数为

 ⌊ ⌋ ′ ⌈x ′1(t)⌉ x (t) = x ′(t) 2

由向量-向量函数给出

 ⌊ ⌋ 2 2 x1 − x1x2 f : ℝ → ℝ , f (x1,x2) = ⌈ ⌉ . x1x2 − x2

f 可以通过在平面上的每个点绘制一个向量来可视化,如图 13.4 所示。

PIC

图 15.4:由 Lotka-Volterra 方程给出的向量场

向量场在机器学习中有重要应用。正如我们很快会看到的,多变量导数(称为梯度)定义了一个向量场。

此外,正如单变量情况所示(见第 13.3 节),梯度下降算法将是由梯度的向量场确定的离散化轨迹。

现在我们理解了多变量函数的概念,让我们来看一个特殊情况。你知道我们的方法:举例子至关重要,每当可能时我们都会从举例开始。这次,我们将线性函数放大显微镜下观察。

15.2 多变量中的线性函数

数学中最重要的函数之一是线性函数。在一个变量的情况下,它的形式为 l(x) = ax + b,其中 a 和 b 是任意实数。

我们已经多次见过线性函数。例如,定理 77 说明,微分等同于找到最好的线性逼近。

线性函数,也就是形如

 ∑n f (x1,...,xn) = b+ aixi, b,ai ∈ ℝ i=1

在多变量中,它们和在单变量中一样重要。

为了建立深刻的理解,我们将看看最简单的情况:二维平面上的一条直线。

PIC

图 15.5:平面上的一条直线

给定其法向量 m = (m[1],m[2]) 和任意点 v[0],如果且仅当 m 和 x −v[0]正交时,向量 x 才在直线上,也就是说,如果

⟨m, x − v[0]⟩ = 0 (15.1)

保持不变。(15.1) 被称为直线的法向量方程。

通过利用内积的双线性特性,并用坐标表示⟨m,x⟩,我们可以简化 (15.1)。假设 m[2]≠0,也就是说,直线不与 x[2]轴平行,快速计算得到

 m1- -1- x2 = −m2 x1 + m2 ⟨m, v0⟩.

这是单一变量 x[1]的线性函数的完整形式。系数 −m1- m2 描述了斜率,而  1 m2-⟨m,v[0]⟩ 描述了截距。

换句话说,线性函数等同于形如(15.1)的向量方程,至少在一个变量的情况下是如此。

如果我们在更高维空间中应用相同的论证,会发生什么呢?在ℝ^(n+1)中,法向量方程

⟨m, x − v[0]⟩ = 0, m, x, v[0] ∈ ℝ^(n+1) (15.2)

定义了一个超平面,也就是一个 n 维平面。(比嵌入平面少一个维度,在我们的情况下是ℝ^(n+1)。)解开 (15.2),我们得到

 1 ∑n mi xn+1 = m----⟨m, v0⟩− m----xi. n+1 i=1 n+1

因此,n 个变量的线性函数的通用形式

 n f (x ,...,x ) = b+ ∑ ax , b,a ∈ ℝ 1 n i=1 i i i

起源于嵌入在(n + 1)维空间中的 n 维平面的法向量方程。

这也可以写成向量化形式

L(U,V ) = {f : U → V | f 是线性的}(15.3)

这是我们以后大多数情况下使用的方式。(注意,在查看向量 u ∈ ℝ^n 的矩阵表示时,我们总是使用列向量形式ℝ^(n×1)。此外,a 并不是平面的法向量。)

在我们继续研究多变量微积分的内部机制之前,我想强调一下,在机器学习中,多维度是如何让事情变得复杂的。

15.3 高维诅咒

首先,让我们谈谈优化。如果其他方法都失败了,优化一个单变量函数 f : [a,b] →ℝ 可能就像将 [a,b] 划分为 n 个点的网格,计算每个点的函数值,然后找到最小值/最大值一样简单。

在更高维度下我们无法做到这一点。为了理解为什么,考虑 ResNet18 这一著名的卷积神经网络架构。它有精确的 11,689,512 个参数。因此,训练等同于优化一个拥有 11,689,512 个变量的函数。如果我们在每个维度上构建一个包含两个点的网格,我们就会有 2¹¹⁶⁸⁹⁵¹² 个点需要对函数进行评估。相比之下,我们可观测到的宇宙中的原子数大约为 10⁸²。这个数字远远小于我们网格的大小。因此,在如此巨大的网格上进行网格搜索在当前是不可能的。我们不得不设计巧妙的算法来应对大维度空间的规模和复杂性。

另一个问题是,在高维空间中,球体会开始出现一种奇怪的现象。回想一下,根据定义,n 维空间中以点 x[0] ∈ℝ^n 为中心、半径为 r 的单位球是由以下方式定义的:

Bn (r,x0) := {x ∈ ℝn : ∥x − x0∥ <r},

我们将其体积记作 V n。 (体积仅依赖于半径和维度,而与中心无关。)

结果表明,

 π n2 n Vn(r) = Γ (1+-n)r , 2

其中 Γ(z) 是著名的伽马函数(en.wikipedia.org/wiki/Gamma_function),它是阶乘的推广。

体积公式看起来可能很复杂,因为它涉及到伽马函数、π以及其他所有项,但我们可以聚焦于问题的核心。如果我们从单位球中切割出一个𝜀宽的外壳,会发生什么呢?

结果表明,单位球的体积集中在其外壳附近,如体积公式所示:

 lim Vn(1−--𝜀)-= lim (1 − 𝜀)n = 0. n→ ∞ Vn(1) n→ ∞

从启发式角度来看,这意味着如果你从单位球中随机选择一个点,那么在高维空间中,它距离中心的距离将接近 1。

换句话说,距离的变化并不像你直觉上所期望的那样。另一种看待这个问题的方法是研究从原点开始、朝每个可能的方向迈一步后到达某一点的影响。

1 = (1,1,...,1) ∈ ℝn,

如图 15.6 所示,这种现象在三维空间中也有类似的表现。

PIC

图 15.6:在三维空间中每个方向上迈出一步

我们走过的欧几里得距离是:

 ┌ ----- ││ ∑n √ -- ∥1∥ = ∘ 1 = n, i=1

随着维度的增加,它会趋向无穷大。也就是说,单位立方体的对角线非常大。

这两种现象在实际应用中可能会带来显著的头痛。更多的参数意味着更具表现力的模型,但也使得训练变得更加困难。这就是所谓的“维度的诅咒”。

15.4 小结

在本章中,我们刚刚触及了多变量函数的表面。只要我们增加更多的维度,复杂度就会急剧上升。

例如,我们有三类:

  1. 标量-向量函数 f : ℝ →ℝ^n,

  2. 向量-标量函数 f : ℝ^n →ℝ,

  3. 和向量-向量函数 f : ℝ^n →ℝ^m。

它们在机器学习中都是至关重要的。特征转换,如神经网络中的层,是向量-向量函数。损失地形由向量-标量函数给出,但训练是通过沿着(离散化的)标量-向量函数进行的,也被称为曲线。

除了更复杂的符号表示外,我们还必须应对维度的诅咒。这就是为什么优化百万变量的函数很困难:不仅参数空间变得巨大,而且距离的概念也开始失效。

现在我们对多变量函数有了一些直觉并熟悉了符号表示,接下来该深入探讨了。我们如何在更高维度中做微积分呢?让我们在下一章看看吧!

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读这本书。提问、为其他读者提供解决方案,通过“问我任何问题”环节与作者聊天,还有更多内容。扫描二维码或访问链接加入社区。packt.link/math

图片

导数与梯度

现在我们理解了为什么多变量函数和高维空间比之前学习的单变量情况更复杂,是时候看看如何在一般情况下处理这些问题了。

快速回顾一下,我们在机器学习中的目标是优化拥有数百万个变量的函数。例如,考虑一个为二元分类训练的神经网络 N(x,w),其中

  • x ∈ℝ^n 是输入数据,

  • w ∈ℝ^m 是压缩所有权重参数的向量,

  • 而 N(x,w) ∈ [0,1]是预测值,表示属于正类的概率。

在比如二元交叉熵损失的情况下,我们有损失函数

 d L(w ) = − ∑ y log N (x ,w ), i i k=1

其中 x[i]是第 i 个数据点,真实值 y[i] ∈{0,1}。看,我告诉过你,我们在多变量微积分中要写更多内容。(我们将在第二十章讨论二元交叉熵损失。)

训练神经网络与寻找 L(w)的全局最小值是一样的,如果它存在的话。我们已经看到如何在单变量情况下进行优化:

  • 通过计算导数来找出增加的方向,

  • 采取一个小步骤,

  • 然后进行迭代。

为了使这在多变量情况下有效,我们需要推广导数的概念。我们可以迅速发现问题:因为向量的除法没有定义,所以差商

f(x)−-f-(y-) x− y

当 f : ℝ^n →ℝ是一个 n 变量的函数且 x,y ∈ℝ^n 是 n 维向量时,这个公式没有意义。

那么我们该如何理解它呢?这就是我们将在下一章学习的内容。

第二十章:16.1 偏导数与全导数

让我们更仔细地看看多变量函数!为了简化起见,设 f : ℝ² →ℝ为我们的二元函数。为了强调对单个变量的依赖,我们通常写成

f(x1,x2), x1,x2 ∈ ℝ.

这里有个技巧:通过固定其中一个变量,我们就能得到两个单变量函数!也就是说,如果固定 x[1] ∈ℝ²,我们就得到 x→f(x[1],x),如果固定 x[2] ∈ℝ²,我们就得到 x→f(x,x[2]),这两者都是定义良好的单变量函数。把这个看作是通过平行于 x−z 或 y−z 轴的平面来切割函数图像,就像图 16.1 所示。被平面切割出来的部分是一个单变量函数。

PIC

图 16.1:用 x−z 平面切割曲面

我们可以通过差商的极限来定义这些函数的导数。这些被称为偏导数:

∂f-- f(x,x2)-−-f(x1,x2) ∂x1 (x1,x2 ) = xli→mx1 x − x1 , ∂f f(x ,x) − f(x ,x ) ----(x1,x2 ) = lim ---1---------1--2-. ∂x2 x→x2 x − x2

(请记住,x[1]表示∂f- ∂x1中的变量,但在∂f- ∂x1(x[1],x[2])的参数中是一个实际的标量值。这可能会让人感到困惑,但你很快就能理解它。)

对于一般的多变量函数,定义是类似的;我们只需要写得更多。在那里,f : ℝ^n →ℝ 在点 x = (x[1],…,x[n])处关于第 i 个变量的偏导数通过以下方式定义:

L(U,V ) = {f : U → V | f is linear}(16.1)

在多变量微积分中,最大的挑战之一就是管理不断增加的符号复杂性。只要看看上面的差商:

f(x1,...,x,...,xn)-−-f(x1,...,xi,...,xn). x − xi

这不是最美观的表示方式,并且这种符号复杂性可能会迅速堆积。幸运的是,线性代数来解救我们!我们不仅可以将变量压缩成向量 x = (x[1],…,x[n]),还可以使用标准基

ei = (0,...,0, 1 ,0,...,0) ◟◝◜◞ i- th component

将差商写成

f(x+-hei)-−-f(x), h ∈ ℝ. h

因此,(19.1) 可以简化。通过这种新发现的形式,我们准备好为偏导数做出简洁和正式的定义。

定义 66.(偏导数)

设 f : ℝ^n →ℝ 是一个 n 变量的函数。f 在点 x = (x[1],…,x[n])处关于第 i 个变量的偏导数通过以下方式定义:

-∂f-(x ) = lim f(x-+-hei)−-f-(x-). ∂xi h→0 h

如果上述极限存在,我们称 f 在第 i 个变量 x[i]处是部分可微的。

偏导数再次是一个向量-标量函数。因为这个原因,它通常被写作 -∂- ∂xif,反映出符号 ∂-- ∂xi 可以被看作一个将函数映射到函数的函数。我知道,这有点抽象,但你很快就会习惯的。

如往常一样,偏导数有几种替代符号。包括符号

  • fx[i],

  • D[i]f(x),

  • ∂[i]f(x)

表示 f 在 x 处的第 i 个偏导数。为了简化,我们将使用老式符号 -∂f ∂xi(x)。

最好从几个例子开始,来说明偏导数的概念。

示例 1. 让

f(x1,x2) = x21 + x22.

为了计算,例如,∂f∕∂x[1],我们固定第二个变量,并将 x[2]视为常数。形式上,我们得到单变量函数

 1 2 2 f (x) := x + x2, x2 ∈ ℝ,

其导数给出了第一个偏导数:

 1 ∂f-(x ,x ) = df-(x ) = 2x . ∂x1 1 2 dx 1 1

同样,我们得到

∂f ∂x-(x1,x2) = 2x2. 2

一旦你习惯了固定变量的思维方式,就可以在不写出所有中间步骤的情况下执行偏微分。

示例 2. 让我们来看一个更复杂的例子。定义

f(x1,x2) = sin(x21 + x2).

通过固定 x[2],我们得到一个复合函数。因此,链式法则用于计算第一个偏导数:

 ∂f ----(x1,x2 ) = 2x1 cos(x21 + x2). ∂x1

类似地,我们得到

∂f-- 2 ∂x2(x1,x2) = cos(x1 + x2).

(我强烈建议您逐步进行上述计算,即使您理解所有中间步骤,也要作为练习完成。)

示例 3. 最后,让我们看一个在一个变量上部分可微但在另一个变量上不可微的函数。定义函数

 ( |{ − 1 if x2 <0, f(x1,x2) = |( 1 otherwise.

由于 f(x[1],x[2]) 不依赖于 x[1],我们可以看到,通过固定 x[2],结果函数是常数。因此,

-∂f-(x ,x ) = 0 ∂x1 1 2

在所有地方成立。然而,在 x[2] 中,0 处存在不连续性;因此,-∂f ∂x2 在该处未定义。

16.1.1 梯度

如果一个函数在每个变量上都部分可微,我们可以将所有的导数合并成一个单一的向量来形成梯度。

定义 67. (梯度)

设 f : ℝ^n → ℝ 是一个在其所有变量上部分可微的函数。那么,其梯度由(列)向量定义为

 ⌊ ⌋ ∂∂x1f (x ) ||-∂-f (x )|| ∇f(x ) := ||∂x2 . || ∈ ℝn ×1. |⌈ .. |⌉ -∂- ∂xnf (x )

需要做一些说明。首先,符号 ∇ 被称为 nabla,是一个用于表示梯度的符号。

第二,梯度可以看作是一个向量-向量函数。为了理解这一点,考虑已经熟悉的函数 f(x[1],x[2]) = x[1]² + x[2]²。f 的梯度是

 ⌊ ⌋ 2x1 ∇f (x1,x2) = ⌈ ⌉ , 2x2

或者

∇f (x ) = 2x

以向量化形式表示。我们可以通过在每个点 (x[1],x[2]) ∈ ℝ² 画出向量 ∇f(x[1],x[2]) 来可视化这一点。

PIC

图 16.2:由 x[1]² + x[2]² 的梯度给出的向量场

因此,您可以将 ∇f 看作是一个向量-向量函数 ∇f : ℝ^n → ℝ^n。给定点 x 处的梯度是通过评估该函数得到的,得到 (∇f)(x)。

为了清晰起见,省略了括号,得到了大家熟悉的符号 ∇f(x)。

16.1.2 高阶偏导数

向量-标量函数 f : ℝ^n → ℝ 的偏导数本身也是向量-标量函数。因此,我们可以再进行一次偏微分!

如果它们存在,二阶偏导数由以下公式定义:

L(U,V ) = {f : U → V | f 是线性函数}(16.2)

其中 a ∈ ℝ^n 是任意向量。(当第二次偏导数是关于同一变量的偏导时,(16.2) 由 ∂2f ∂x2i(a) 简写表示。)

这个定义引出了一个问题:求导的顺序是否可以互换?也就是说,是否有

 2 2 --∂-f--(a) = -∂-f--(a) ∂xi∂xj ∂xj∂xi

这个问题的答案相当令人惊讶:在一些温和的假设下,求导顺序是可以互换的,但在一般情况下不是。关于这一点有一个著名的定理,我们不会证明,但它是非常重要的。

定理 98.

设 f : ℝ^n → ℝ为任意的向量-标量函数,且 a ∈ ℝ^n。如果存在一个以 a 为中心的球 B(𝜖,a) ⊆ ℝ^n,使得 f 在 B(𝜖,a)的所有点处具有连续的二阶偏导数,则

--∂2f-- -∂2f--- ∂xi ∂xj(a) = ∂xj∂xi(a)

对于所有 i = 1,…,n 成立。

定理 98 被称为施瓦茨定理、克莱罗定理或杨氏定理。

16.1.3 总导数

偏导数似乎将可微性的概念推广到多变量函数。然而,似乎还有什么缺失。让我们稍微回顾一下单变量的情况。

回想一下,根据定理 77,单变量函数 f : ℝ → ℝ在给定点 a 处的可微性等价于由线性函数进行的局部近似

l(x ) = f(a) + f′(a)(x − a).

如果 x 靠近 a,l(x)也将接近 f(x)。此外,这是我们在 a 附近所能做的最佳线性近似。在单变量中,这相当于求导。

这给了我们一个想法:尽管像f(x)−f(y) x−y这样的差商在多变量中不存在,但多变量线性函数的最佳局部近似却存在!

因此,总可微性的概念就此诞生。

定义 68.(总可微性)

设 f : ℝ^n → ℝ为一个 n 变量的函数。如果 f 在 a ∈ ℝ^n 处完全可微(或简称为可微),则存在一个行向量 Df ∈ ℝ^(1×n),使得

f(x) = f(a) + Df(x − a) + o(∥x − a∥) (16.3)

对于所有 x ∈ B(𝜖,a),其中𝜖/span>0,B(𝜖,a)由下式定义

B(𝜖,a) = {x ∈ ℝn : ∥x − a∥} <𝜖.

(换句话说,B(𝜖,a)是以 a 为中心,半径为𝜖/span>0 的球。)当存在时,向量 Df 称为 f 在 a 处的总导数。

回想一下,当没有明确说明时,我们使用列向量,因为我们希望将线性变换写成 Ax 的形式,其中 A ∈ ℝ^(m×n)且 x ∈ ℝ^(n×1)。因此,公式的“维度学”

 f(x) = f(a) + Df (a )(x− a )+o(∥x − a∥) ∈ ℝ1×1 ◟◝1◜×◞1 ◟◝◜1◞×1 ◟-◝1◜× ◞n ◟-◝◜n×◞1 ∈ℝ ∈ℝ ∈ℝ ∈ℝ

计算得出。(别被愚弄了,ℝ^(1×1)是一个标量。)

让我们解开总可微性的概念。形式(16.3)意味着一个完全可微的函数 f 等于线性部分 f(a) + Df(x − a)加上一个小误差。

由线性部分给出的表面称为切平面。我们可以为二变量函数可视化它。

PIC

图 16.3:切平面

不出所料,偏导数和总导数有着密切的关系。

定理 99.(总导数与偏导数)

设 f : ℝ^n → ℝ为一个在 a ∈ ℝ^n 处完全可微的函数。则,它的所有偏导数在 a 处都存在,并且

f(x) = f(a) + ∇f(a)^T (x − a) + o(∥x − a∥) (16.4)

对于所有 a ∈ B(𝜀,a),𝜀/span>0,成立。(即,Df = ∇f(a)^T 。)

换句话说,方程 (16.4) 表明最佳线性近似的系数等于偏导数。

证明。因为 f 在 a 处是完全可微的,定义告诉我们 f 可以写成如下形式

f(x) = f(a)+ Df (a)(x− a)+ o(∥x − a∥),

其中 Df = (d[1],…,d[n]) 是描述线性部分系数的向量。

我们的目标是证明

 f-(a-+-hei)−-f-(a) hli→m0 h = di,

其中 e[i] 是单位(列)向量,其第 i 个分量为 1,其余分量为 0。

让我们快速做个计算!根据我们所知道的,我们有

f(a-+-hei)−-f(a)-= Df-(a)hei +-o(∥hei∥) h h = Df (a)ei + o(1) = di + o(1),

从而确认 lim[h→0]f(a+hei)−-f(a)- h = d[i],这正是我们需要证明的。

那么,总微分的麻烦在哪里呢?定理 99 告诉我们,总微分是比偏微分更强的条件。

出人意料的是,反方向不成立:偏导数的存在并不意味着总微分性,如示例所示。

 ( |{ f (x, y) = 1 如果 x = 0 或 y = 0, |( 0 否则

如图所示。这个函数在 0 处有所有的偏导数,但总导数不存在。(你可以通过画图或注意到函数 1 −d^T x 永远不可能是 o(∥x∥) 来证明这一点,无论 d 的选择如何。)

备注 11. (总导数作为算子)

就像单变量函数一样,f : ℝ^n →ℝ 的总导数是一个函数 D[f] : ℝ^n →ℝ^n。

在最高层次的抽象中,我们可以将总导数看作是一个算子,它将一个向量标量函数映射到一个向量向量函数:

 n ℝ n ℝn D : (ℝ ) → (ℝ ) , D : f ↦→ D , f

其中 A^B 表示将 A 映射到 B 的所有函数的集合。

你不需要完全理解这一点,但相信我,你的思维越抽象,你就会越强大。

16.1.4 方向导数

到目前为止,我们谈了两种导数:描述沿固定轴变化速率的偏导数,以及给出给定点函数最佳线性近似的总导数。

偏导数只关注几个特定的方向。然而,在多变量中,这不是故事的全部。通过标准的正交归一基向量 e[i],偏导数的定义为

L(U,V ) = {f : U → V | f 是线性的}(16.5)

正如我们之前看到的,这些描述了沿各维度的变化速率。然而,标准的正交归一向量仅是一些特定的方向。

那么在任意方向 v 下呢?我们可以定义沿这些方向的导数吗?当然!没有任何东西阻止我们在 (16.5) 中用 v 替换 e[i]。因此,方向导数应运而生。

定义 69.(方向导数)

设 f : ℝ^n →ℝ 是一个 n 个变量的函数,v ∈ℝ^n 是一个任意向量。f 在 v 方向上的方向导数由极限定义:

∂f f(a + hv) − f(a) ∂v-:= lhim→0 -------h--------.

好消息:方向导数可以通过梯度来描述!

定理 100.

设 f : ℝ^n →ℝ 是一个 n 个变量的函数。如果 f 在 a ∈ℝ^n 处全微分,那么它在所有方向上的方向导数都存在,并且

∂f(a) = ∇f (a)Tv. ∂v

证明。由于全微分性,定理 103 表明:

f (x ) = f (a )+ ∇f (a)T(x − a)+ o(∥x − a∥)

在 a 附近。因此,

f(a+ hv )− f (a ) h∇f (a)Tv + o(h) ----------------= ---------------- h h = ∇f (a)Tv + o(1),

给出:

∂f-(a) = lim f-(a-+-hv-)−-f(a) ∂v h→0 h = lim ∇f (a)Tv + o(1) h→0 = ∇f (a)T v,

如我们所需的那样证明。

换句话说,定理 100 表示,无论方向 v 如何,方向导数都可以用梯度和 v 来表示。如果你仔细想一想,这真的很惊人:沿着 n 个特定方向的变化率决定了其他任何方向的变化率。

16.1.5 梯度的性质

在一维情况下,我们已经学到,如果 f 在某个 a 处的导数为正,那么 f 在 a 附近是增加的。(如果导数为负,则 f 是减少的。)如果我们将 f'(a) 看作一个一维向量,那么导数指向增大的方向。

这在高维空间中也成立吗?是的,这就是梯度下降法有效的原因。

定理 101.(梯度决定增加的方向)

设 f : ℝ^n →ℝ 是一个 n 个变量的函数,假设 f 在 a ∈ℝ^n 处全微分。

然后

L(U,V ) = {f : U → V | f is linear}(16.6)

我知道,(16.6) 很复杂,所以让我们来详细解析一下。首先,从神秘的 argmax 开始。对于给定的函数 f,

argmaxx ∈Sf(x)

表示在集合 S 上最大化 f 的值。由于最大值可能不唯一,argmax 可能会得到一个集合。(argmin 的定义相同,只是取最小值而不是最大值。)

因此,英文中,(16.6) 表明,在 a ∈ℝ^n 处,最大化方向导数的单位方向是归一化的梯度。现在我们准备好看到证明了!

证明。你还记得柯西-施瓦茨不等式(定理 8)吗?那是很久以前的事了,让我们来回顾一下!在向量空间 ℝ^n 中,柯西-施瓦茨不等式告诉我们,对于任何 x, y ∈ℝ^n,

xT y ≤ ∥x∥∥y∥.

现在,正如定理 100 所暗示的,方向导数可以写作

∂f- T ∂v(a) = ∇f (a) v.

结合柯西-施瓦茨不等式,我们得到

∂f ---(a) = ∇f (a)T v ∂v ≤ ∥ ∇f (a )∥∥v ∥.

通过将方向限制为单位向量,

L(U,V ) = {f : U → V | f 是线性的}(16.7)

如此,方向导数必须小于或等于梯度的范数。(至少,在单位长度的方向向量上。)

然而,通过让 v[0] = ∇f(a)∕∥∇f(a)∥,我们得到

∂f--(a ) = ∇f (a)Tv0 ∂v0 ∇f (a)T∇f (a) = ---∥∇f-(a)∥-- 2 = ∥∇f-(a)∥- ∥∇f (a)∥ = ∥∇f (a)∥.

因此,通过选择 v[0] = -∇f(a)-,可以在(16.7)中达到等式。这意味着 在 a 点最大化了方向导数,这就是我们需要证明的。

到此,我们已经掌握了多变量的微分基础。总结一下,我们已经学到,导数的差商定义不能直接推广到多变量,但我们可以将除一个变量外的其他变量固定,从而使差商成立,从而得到偏导数。

另一方面,线性近似定义在多维空间中也适用,但不是

 ′ f(a)+ f (a)(x− a), f : ℝ → ℝ, x,a ∈ ℝ,

就像我们在单变量时那样,我们得到

f (a )+ ∇f (a)T(x− a), f : ℝn → ℝ, x,a ∈ ℝn,

其中,导数的类比是梯度向量 ∇f(a) ∈ℝ^n。

即使我们第一次学习单变量微分时,我也曾告诉你,局部线性近似定义总有一天会派上用场。那个时候就是现在,我们正在收获成果。很快,我们将全面了解梯度下降。

16.2 向量值函数的导数

在单变量情况下,定义高阶导数是很简单的。我们只需要不断地进行求导:

 ′′ ′ ′ f (x) = (f (x)), f′′′(x) = (f′′(x))′,

等等。然而,对于多变量函数来说,这并不是那么简单。

到目前为止,我们只讨论了梯度,这是向量-标量函数的导数的推广。

由于 ∇f(a) 是列向量,梯度是一个向量-向量函数 ∇ : ℝ^n →ℝ^n。我们只知道如何计算向量-标量函数的导数。是时候改变这一点了!

16.2.1 曲线的导数

曲线,通常描述动态系统的解,是数学中最重要的对象之一。虽然我们在机器学习中并不显式使用它们,但它们在诸如梯度下降的算法中潜在地起作用。(在这里,我们遍历一个离散化的曲线,最终到达局部最小值。)

从形式上讲,曲线——即标量-向量函数——由一个函数给出

 ⌊γ (t)⌋ | 1 | n ||γ2(t)|| n(×1) γ : ℝ → ℝ , γ (t) = || .. || ∈ ℝ , ⌈ . ⌉ γn(t)

其中 γ[i] : ℝ →ℝ 函数是经典的一元标量-标量函数。由于自变量通常表示时间,因此习惯上用 t 来表示它。

我们可以逐分量地对 γ 求导:

 ⌊ ⌋ γ′1(t) ||γ′(t)|| γ′(t) := || 2. || ∈ ℝn(×1). |⌈ .. |⌉ ′ γn(t)

如果我们确实将 γ(t) 想象为空间中的一条轨迹,则 γ′(t) 是 γ 在 t 处的切向量。由于微分是逐分量进行的,定理 77 说明,如果 γ 在某个 a ∈ℝ 处可微,

γ(t) = γ(a) + γ′(t)^T (t − a) + o(|t − a|) (16.8)

在那里。方程式(16.8)是一个真正的向量化公式:一些分量是向量,一些是标量。然而,这很简单,并且对我们来说非常有意义。隐藏向量和矩阵的复杂性是线性代数的真正力量。

很容易看出,对于任意两条曲线 γ, η : ℝ →ℝ^n,微分是可加的,即 (γ + η)^′ = γ^′ + η^′。当我们将标量-向量函数与向量-标量函数复合时,会发生什么呢?

这种情况在机器学习中非常常见。比如,假设 L : ℝ^n →ℝ 描述的是损失函数,而 γ : ℝ →ℝ^n 是我们在参数空间 ℝ^n 中的轨迹,复合函数 f(γ(t)) 描述了时间 t 时刻的模型损失。因此,为了计算 (f ∘γ)^′,我们必须推广链式法则。

定理 102.(标量-向量函数和向量-标量函数复合的链式法则)

设 γ : ℝ →ℝ^n 和 f : ℝ^n →ℝ 为任意函数。如果 γ 在某个 a ∈ℝ 处可微,且 f 在 γ(a) 处可微,则 f ∘γ : ℝ →ℝ 也在 a 处可微,且

(f ∘γ)′(a) = ∇f (γ(a))Tγ′(a )

在那里。

证明。由于 f 在 γ(a) 处可微,定理 99 给出:

f(γ(t)) = f (γ (a )) + ∇f (γ(a))T (γ (t)− γ (a )) + o(∥γ(t) − γ(a)∥).

因此,

(f ∘γ )′(a) = lim f(γ(t))−f(γ(a)) t→a t− a T γ(t)−γ(a)- = ∇f (γ(a)) lit→ma [ t− a + o(1)] T ′ = ∇f (γ(a)) γ (a),

这就是我们要证明的。

16.2.2 雅可比矩阵和海森矩阵

现在,我们的任务是将导数扩展到向量-向量函数。设 f : ℝ^n →ℝ^m 为一个这样的函数。通过显式地写出 f 的输出,我们可以将其分解为多个分量:

 ⌊ ⌋ | f1(x )| f(x) = |⌈ ... |⌉ ∈ ℝm (×1) fm(x )

其中 f[i] : ℝ^n →ℝ 是向量-标量函数。

自然的想法是计算 f[i] 的偏导数,将它们压缩成一个矩阵。我们就这么做!

定义 70.(雅可比矩阵)

设 f : ℝ^n →ℝ^m 为任意的向量-向量函数,假设

f(x) = (f (x ),...,f (x)), 1 m

其中所有的 f[i] : ℝ^n → ℝ 都是在某个 a ∈ ℝ^n 处(部分)可微的。矩阵为

 ⌊ ∂f1 ∂f1 ∂f1 ⌋ | ∂x1(a) ∂x2(a) ... ∂xn(a)| | ∂f2(a) ∂f2(a) ... ∂f2(a)| Jf(a) := || ∂x1. ∂x2. . ∂xn. || ∈ ℝm ×n |⌈ .. .. .. .. |⌉ ∂fm- ∂fm- ∂fm-- ∂x1 (a) ∂x2 (a) ... ∂xn(a)

这被称为 f 在 a 处的 Jacobian。

换句话说,Jacobian 的行是 f[i] 的梯度:

 ⌊ T ⌋ | ∇f1(a) | || ∇f2(a)T || || . || Jf(a) = | .. |. ||∇f (a )T || ⌈ m ⌉

我有个好消息:f 在 a 附近的最佳局部线性近似由下式给出

f(x ) = f(a)+ J (a)(x − a)+ o(∥x − a∥), f

如果最佳局部线性近似存在。于是,Jacobian 成为梯度的适当概括。

我们可以利用 Jacobian 来概括向量-标量函数的二阶导数的概念:通过计算梯度的 Jacobian,我们得到一个特殊的矩阵,它是二阶导数的类比。

定义 71.(Hessian 矩阵)

设 f : ℝ^n →ℝ 是一个任意的向量-标量函数,并假设其所有二阶偏导数在 a ∈ℝ^n 处存在。

矩阵

 ⌊ ⌋ ∂2f2(a) -∂2f-(a) ... -∂2f-(a) || ∂x21 ∂x1∂2x2 ∂x1∂2xn || || ∂∂x2f∂x1(a ) ∂∂xf2(a) ... ∂x∂2∂fxn(a)|| n×n Hf (a) := | .. 2.. .. .. | ∈ ℝ |⌈ . . . . |⌉ -∂2f--(a ) -∂2f-(a) ... ∂2f2(a) ∂xn∂x1 ∂xn∂x2 ∂xn

这被称为 f 在 a 处的 Hessian。

换句话说,

Hf (a) = J ∇f(a)T

通过定义得以成立。此外,如果 f 行为良好(例如,所有二阶偏导数都存在且连续),定理 98 表明 Hessian 是对称的;也就是说,Hf = Hf^T。

16.2.3 向量-向量函数的总导数

最后一个概括。(我保证。)回忆一下,梯度的存在(即,部分可微性)并不意味着向量-标量函数的整体可微性,如该例所示

 ( |{ f (x, y) = 1 if x = 0 or y = 0, |( 0 otherwise

显示在零处。

对于向量-向量函数同样成立,因为 Jacobian 是梯度的概括,而不是总导数。

最好快速“撕下创可贴”,并为向量-向量函数定义总导数。这个定义可能有点抽象,但相信我,这项投资将在讨论链式法则时得到回报。(链式法则是反向传播算法的基础,而反向传播使得梯度下降的计算成为可能。)

定义 72.(向量-向量函数的整体可微性)

设 f : ℝ^n →ℝ^m 是一个任意的向量-向量函数。我们说 f 在 a ∈ℝ^n 处是整体可微的(或简略地称为可微的),如果存在矩阵 Df ∈ℝ^(m×n),使得

f(x) = f(a) + Df(x − a) + o(∥x − a∥)(16.9)

对所有 x ∈B(𝜖,a) 都成立,其中 𝜖/span>0 且 B(𝜖,a) 定义为

B(𝜖,a) = {x ∈ ℝn : ∥x − a∥ <𝜖}.

(换句话说,B(𝜖,a) 是以 a 为中心、半径为 𝜖/span>0 的球体。)当存在时,矩阵 Df 被称为 f 在 a 处的总导数。

注意到定义 72 几乎与定义 68 完全相同,唯一不同的是这次“导数”是一个矩阵。

你可能不惊讶地听到,它与雅可比矩阵的关系与向量-标量情况下的梯度和全导数相同。

定理 103.(全导数与偏导数)

设 f : ℝ^n →ℝ^m 是一个在 a ∈ℝ^n 处完全可微的函数。那么,它的所有偏导数在 a 处存在,并且

Df (a) = Jf(a ).

证明几乎与定理 99 的证明相同,只是符号更复杂。我强烈建议你逐行推导,因为这种脑力训练有助于你更好地适应矩阵的实际应用。

按分量来看,全导数可以写成

 ⌊ ∂f1 ∂f1 ∂f1- ⌋ | ∂x1(a) ∂x2(a) ... ∂xn (a )| | ∂f2(a) ∂f2(a) ... ∂f2-(a )| Df(a) = || ∂x1. ∂x2. . ∂xn. ||∈ ℝm ×n. |⌈ .. .. .. .. |⌉ ∂fm-(a) ∂fm(a) ... ∂fm-(a) ∂x1 ∂x2 ∂xn

通过引入记号

 ⌊ ⌋ ∂∂fx1(a ) || ∂fi2 || || ∂xi(a )|| -∂f(a) = | ... | ∈ ℝm ×1, ∂xi || ∂f || |⌈ ∂mxi (a)|⌉

全导数 Df 可以写成块状形式。

 [-∂f -∂f -∂f ] Df (a ) = ∂x1(a) ∂x2(a) ... ∂xn(a)

 ⌊ ⌋ ∇f1 (a )T || T || D (a ) = || ∇f2 (a ) || . f | ... | ⌈ ⌉ ∇fm (a)T

16.2.4 导数与函数运算

我们已经将导数的概念推广到尽可能广泛的程度。现在是时候研究它们与两个基本的函数运算——加法和复合——之间的关系了。(由于高维空间中没有向量乘法,向量-向量函数的乘积和比值是未定义的。)

我们从更简单的开始:加法。

定理 104.(全导数的线性性质)

设 f,g : ℝ^n →ℝ^m 是两个在某个 a ∈ℝ^n 处可微的向量-向量函数,且设α,β ∈ℝ为两个任意标量。

然后,αf + βg 在 a 处也是可微的,并且

D αf+ βg(a) = αDf (a)+ βDg (a )

那里。

证明。由于全微分,(16.9)意味着

αf(x) + βg(x) = αf(a) + βg(a) + (αD (a )+ βD (a))(x − a) f g + o(∥x− a∥),

这意味着

Dαf+βg(a) = αDf (a)+ βDg (a).

这是我们需要展示的内容。

线性是始终可取的,但我们需要的是链式法则的终极推广。我们之前看到过标量-向量和向量-向量函数的特殊情况(见定理 102),但我们需要更进一步。

多变量链式法则在机器学习中极其重要。神经网络是一个复合函数,层次结构充当了其组成部分。在梯度下降过程中,我们使用链式法则来计算这个复合函数的导数。

定理 105.(多变量链式法则)

设 f : ℝ^m →ℝ^l,g : ℝ^n →ℝ^m 是两个向量-向量函数。如果 g 在 a ∈ ℝ^n 处完全可微,且 f 在 g(a)处完全可微,则 f ∘g 在 a 处也完全可微,并且

Df∘g = Df) Dg (16.10)

成立。

对我们有利的是,复合函数的导数(16.10)由两个矩阵的乘积给出。由于矩阵乘法可以快速进行,这是个好消息。

我们将看到定理 105 的两个证明。一个使用超光速引擎,另一个通过将一般情况简化为定理 102 展示了更多内容。两者都提供了丰富的见解。让我们从重型机械开始。

证明。(第一种方法。)

由于 f 在 g(a)处完全可微,方程(16.9)意味着

f(g(x)) = f(g(a)) + D(g(a))(g(x) − g(a)) + o(∥g(x) − g(a)∥). f

反过来,由于 g 在 a 处完全可微,我们有

g(x)− g(a) = Dg (a)(x− a)+ o(∥x − a∥).

因此,我们可以继续计算

f(g (x )) = f(g(a)) + Df (g(a))(g(x )− g(a)) + o(∥g(x) − g(a)∥) = f(g(a)) + D (g(a))D (a)(x − a) f g + Df (g (a ))[o(∥x− a ∥)+ o(∥g(x)− g(a)∥)], ◟-----------------◝◜----------------◞ =o (∥x−a∥)

证明 f ∘g 在 a 处完全可微,且具有全导数

Df∘g(a) = Df(g(a))Dg(a),

这是我们需要展示的内容。

现在,关于第二个证明。

证明。(第二种方法。)

让我们稍微分析一下 Df∘g。写出 f∘g 的各个组成部分,我们得到

 ⌊ ⌋ | (f ∘g )1(x )| | (f ∘g )2(x )| (f ∘ g)(x) = || . || ∈ ℝl, x ∈ ℝn. |⌈ .. |⌉ (f ∘ g)(x) l

根据定义,Df∘g 的第 i 行第 j 列是

 ∂ (f ∘g )i (Df∘g(a))i,j = ---∂x---(a). j

如果你足够长时间看它,你会意识到∂(f∘g) --∂xji(a)是单变量函数的导数。事实上,要求导的函数是曲线的组合

γ : t ↦→ g(a1,...,aj−1,t,aj+1,...,an)

以及向量-标量函数 f[i] : ℝ^m → ℝ。因此,标量-向量和向量-标量函数的链式法则(由定理 102 给出)可以应用:

∂(f ∘g)i T ∂ --∂x----(a) = ∇fi(g(a)) ∂x--g(a), j j

其中∂∂xjg(a)是按分量计算的导数

 ⌊ ⌋ ∂g1(a) || ∂∂g2xj(a)|| -∂-- || -∂xj--|| ∂xj g(a) = | .. | . |⌈ . |⌉ ∂gm∂(xa) j

总结一下,我们得到

这是矩阵乘积 Df)Dg 中第 i 行第 j 列的元素,因此

这是我们必须展示的内容。

通过掌握向量-向量函数的全导数概念和一般的链式法则,我们准备好实际处理多变量函数了。因此,我们的下一站是奠定优化的基础。

16.3 总结

你现在应该知道:数学成功的一半在于选择正确的表示法和符号。尽管多变量微积分看起来异常复杂,但如果我们对线性代数有很好的理解,它就变得轻松了。这也是我们为何从向量和矩阵开始的原因!从 f(x[1],…,x[n]) 到 f(x) 是一项重要的进展。

在这一章中,我们学到了多维度的微分比单变量情况稍微复杂一些。首先,我们定义了偏导数:

∂f-(a) = lim f(a+-hei)-−-f(a), a ∈ ℝn, ∂xi h→0 h

其中,e[i] 是一个向量,其第 i 个分量为 1,其他分量为 0。我们可以将 ∂∂fx- i 视为通过固定 f 的除第 i 个变量外所有变量所得到的单变量函数的导数。所有的偏导数组成梯度:

 ⌊-∂- ⌋ |∂x1f (a )| ||∂∂x2f (a )|| n×1 ∇f (a ) := || .. || ∈ ℝ . ⌈ . ⌉ -∂-f (a ) ∂xn

然而,偏导数并不完全是单变量导数的完美类比。在单变量的情况下,我们知道导数是最好的局部线性近似,而这正是可以推广到多变量的版本。因此,我们说 f 在 a ∈ ℝ^n 处是全微分的,如果它可以写成以下形式:

f(x) = f(a) + ∇f (a )T (x − a) + o(∥x− a ∥).

在机器学习中,最重要的工具之一就是多变量链式法则。

Df∘g(a) = Df(g (a))Dg (a),

这就是用来实际计算导数的工具。如果没有链式法则,我们就没有有效的方法来计算梯度。因此,正如名字所示,梯度是梯度下降法的基石。我们已经理解了单变量版本,现在是时候深入研究一般的多变量版本了。下一章见!

16.4 问题

问题 1. 计算以下函数的偏导数和 Hessian 矩阵。

(a) f(x[1],x[2]) = x[1]^(3x[2]²) + 2x[1]x[2] + x[2]³ (b) f(x[1],x[2]) = e^(x[1]²−x[2]) + sin(x[1]x[2]) (c) f(x[1],x[2]) = ln(x[1]² + x[2]²) + x[1]e^(x[2]) (d) f(x[1],x[2]) = cos(x[1]x[2]) + x[1]² sin(x[2]) (e) f(x[1],x[2]) = f(x[1],x[2]) = x2+x2 x11−x22

问题 2. 计算以下函数的 Jacobian 矩阵。

(a)

 ⌊ ⌋ x21x2 + ex2 f(x1,x2) = ⌈ x2⌉ sin(x1x2)+ x1e

(b)

 ⌊ ⌋ ln(x21 + x22) + x1x2 f(x1,x2) = ⌈ 2 x1 ⌉ cos(x1)+ x 2e

(c)

 ⌊ ⌋ x3 − x2 f(x1,x2) = ⌈ 1 2 ⌉ ex1x2 + x1 cos(x2 )

(d)

 ⌊ ⌋ ⌈ tan (x1x2 )+ x32 ⌉ f(x1,x2 ) = ∘x2-+-x2-+ sin(x ) 1 2 1

(e)

 ⌊ ⌋ x ex2 − ln(1 + x2) f(x1,x2) = ⌈ 1 1 ⌉ x22cos(x1)+ x1x2

问题 3. 设 f(x[1],x[2]) = x[1]∘ |x2|-。证明 f 在 (0,0) 处是部分可微的,但不是全微的。

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过问我任何问题环节与作者交流,等等。扫描二维码或访问链接加入社区。packt.link/math

图片

多变量优化

嘿!我们已经到了微积分学习的最后一个检查点。还缺什么?当然是梯度下降。

在之前的八章中,我们将所有内容都整理好了,现在是时候下手了。首先,我们将多变量函数代码化。之前,我们构建了一个方便的接口形式——Function 类,用来表示可微函数。在上一章进行了较长的设置后,我们现在可以轻松扩展它,并且利用向量化的强大功能,我们甚至不需要做太多修改。开始吧!

第二十一章:17.1 多变量函数的代码实现

自从我们将理论转化为代码以来已经有一段时间了。那么,让我们来看看多变量函数吧!

上一次,我们构建了一个函数基类,包含两个主要方法:一个用于计算导数(Function.prime),另一个用于获取参数字典(Function.parameters)。

这并不会让人感到惊讶:多变量函数基类与此没有太大区别。为了清晰起见,我们将适当重命名主方法为 grad。

class MultivariableFunction: 
    def __init__(self): 
        pass 

    def __call__(self, *args, **kwargs): 
        pass 

    def grad(self): 
        pass 

    def parameters(self): 
        return dict()

让我们立即来看几个例子。最简单的一个是平方欧几里得范数 f(x) = ∥x∥²,它与均方误差函数非常相似。它的梯度由下式给出:

∇f (x) = 2x,

因此,一切准备就绪,可以进行实现。由于我们使用 NumPy 数组来表示向量,我们也将它们作为输入。

import numpy as np

class SquaredNorm(MultivariableFunction):
    def __call__(self, x: np.array):
        return np.sum(x**2)

    def grad(self, x: np.array):
        return 2*x

注意,SquaredNorm 与 f(x) = ∥x∥² 在数学意义上是不同的,因为它接受任何 NumPy 数组,而不仅仅是一个 n 维向量。现在这不是问题,但稍后会有问题,所以请记住这一点。

另一个例子可以通过参数线性函数给出

g(x, y) = ax + by,

其中 a, b ∈ ℝ 是任意参数。让我们看看如何实现 g(x, y)!

class Linear(MultivariableFunction): 
    def __init__(self, a: float, b: float): 
        self.a = a 
        self.b = b 

    def __call__(self, x: np.array): 
        #x0022;"/span> 
        x: np.array of shape (2, ) 
        #x0022;"/span> 
        x = x.reshape(2) 
        return self.a*x[0] + self.b*x[1] 

    def grad(self, x: np.array): 
        return np.array([self.a, self.b]).reshape(2, 1) 

    def parameters(self): 
        return {"/span>a self.a, /span>b self.b}

为了检查我们的实现是否正确,我们可以在一个简单的例子中快速测试一下。

g = Linear(a=1, b=-1) 

g(np.array([1, 0]))
np.int64(1)

或许我们直到现在才开始关注这个问题,但相信我,指定输入和输出的形状至关重要。在做数学时,我们可以灵活地使用符号,将任何向量 x ∈ ℝ^n 视为列向量或行向量,但令人痛苦的是,实践中情况并非如此。

正确地跟踪数组形状至关重要,它可以为你节省数百小时的时间。绝不是开玩笑。

17.2 最小值和最大值,再次探讨

在单变量的情况下,我们成功地使用导数找到了可微函数的局部最优解。

回顾一下,如果 f : ℝ → ℝ 在处处可微,那么定理 87 给出的结论是

(a) f^′(a) = 0 且 f^(′′)(a) > 0 表示局部最小值。(b) f^′(a) = 0 且 f^(′′)(a) < 0 表示局部最大值。

(简单的 f^′(a) = 0 不足以确定最值,正如 f(x) = x³ 在 0 处的例子所示。)

我们能在多变量的情况下做类似的事情吗?

从一开始似乎就有一个问题:导数不是标量(因此,我们不能将其等于 0)。

这很容易解决:条件 f^′(a) = 0 的类似条件是 ∇f(a) = (0,0,…,0)。为了简便,零向量 (0,0,…,0) 也可以表示为 0\。别担心,这不会让人困惑;从上下文中可以清楚地理解。引入零向量的新符号只是增加了复杂性。

我们可以通过切平面来直观地理解临界点的情况。在单变量情况下,我们已经看到过这个:如图 17.1 所示,f^′(a) = 0 表明切线是水平的。

PIC

图 17.1:单变量的局部极值

在多变量情况下,情况类似:∇f(a) = 0 表示最佳的局部线性逼近(16.3)是常数;即切平面是水平的。(如图 17.2 所示。)

PIC

图 17.2:多变量的局部极值

那么,∇f(a) = 0 表示什么呢?类似于单变量的情况,a ∈ℝ^n 如果满足 ∇f(a) = 0,则称为 f 的临界点。这种相似性不仅体现在术语上,我们在多变量情况下也有三种选择:a 是

  1. 局部最小值,

  2. 局部最大值,

  3. 或者都不是。

在多变量情况下,非极值的临界点称为鞍点,因为二维情况下的形状与实际的马鞍非常相似,正如你将要看到的那样。鞍点是高维空间中单变量拐点的类比。函数

 2 2 f (x,y) = x + y , 2 2 g(x,y) = − (x + y ), h (x,y) = x2 − y2

在 (0,0) 处提供了所有三种情况的示例,如图 17.3、图 17.4 和图 17.5 所示。(记住,局部极值可能是全局极值。)

PIC

图 17.3:一个(局部)最大值

PIC

图 17.4:一个(局部)最小值

PIC

图 17.5:一个鞍点

为了理清思路,我们从定义和定理开始制定。

定义 73.(临界点)

设 f : ℝ^n →ℝ 为任意向量-标量函数。我们说 a ∈ℝ^n 是 f 的临界点,如果满足以下条件之一:

∇f (a) = 0

保持成立,或者 f 在至少一个变量上不可偏微分。

第二种情况(f 在 a 处不可微)是为了处理像 f(x,y) = jxj + jyj 这样的情况。

为了精确起见,我们也来定义多维的局部极值。

定义 74.(局部最小值和最大值)

设 f : ℝ^n →ℝ 为任意向量-标量函数,a ∈ℝ^n 为任意点。

(a) 如果存在 𝜀/span>0,使得

f(a) ≤ f(x), x ∈ B(𝜀,a).

(b) a 是严格局部最小值,如果存在 𝜀/span>0,使得

f(a) <f(x), x ∈ B(𝜀,a).

(c) a 是局部最大值,如果存在 𝜀/span>0,使得

f(a) ≥ f(x), x ∈ B (𝜀,a) ∖{a}.

(d) a 是严格局部最大值,如果存在 𝜀/span>0,使得

f(a) >f(x), x ∈ B (𝜖,a) ∖{a}.

正如 x² −y² 的例子所示,临界点不一定是局部极值,但局部极值总是临界点。接下来的结果,类似于定义 73,对此进行了数学上的精确描述。

定理 106\。

令 f : ℝ^n →ℝ 为一个任意的向量-标量函数,假设 f 在某个 a ∈ℝ^n 处对所有变量部分可微。

如果 f 在 a 处有局部极值,则 ∇f(a) = 0。

证明:这是定理 86 的直接结果,因为如果 a = (a[1],…,a[n]) 是向量-标量函数 f 的局部极值,那么它也是单变量函数 h→f(a + he[i]) 的局部极值,其中 e[i] 是一个向量,其第 i 个分量为 1,其余分量为 0。

根据定义 66 给出的偏导数定义,

-d-f(a + hei) =-∂f-(a). dh ∂xi

因此,定理 86 给出了

 ∂f ∂x--(a ) = 0 i

对所有 i = 1,…,n,给定 ∇f(a) = 0。

那么,我们如何通过导数来找到局部极值呢?正如我们之前所建议的,研究二阶导数将帮助我们在临界点中定位极值。不幸的是,n 变量的情况要复杂得多,所以我们首先集中讨论二元情况。

定理 107\。(二元函数的二阶导数检验)

令 f : ℝ² →ℝ 为一个任意的向量-标量函数,假设 f 在某个 a ∈ℝ² 处部分可微。并且假设 a 是一个临界点,即 ∇f(a) = 0。

(a) 如果 detHf/span>0 且 ∂2f- ∂x22/span>0,则 a 是局部最小值。

(b) 如果 detHf/span>0 且 ∂2f- ∂x22/span>0,则 a 是局部最大值。

(c) 如果 detHf/span>0,则 a 是一个鞍点。

我们不会证明这一点,但有一些说明是必要的。首先,由于 Hessian 行列式可能为 0,定理 107 并未涵盖所有可能的情况。

最好看几个例子,因此让我们回顾一下之前看到的函数

f (x,y) = x2 + y2, g(x,y) = − (x2 + y2), 2 2 h (x,y) = x − y .

这三个函数在 0 处都有一个临界点,因此 Hessian 可以提供更清晰的图像。Hessian 的矩阵为

 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋ ⌈2 0⌉ ⌈− 2 0 ⌉ ⌈2 0 ⌉ Hf (x,y) = 0 2 , Hg(x,y) = 0 − 2 , Hh (x,y) = 0 − 2 .

对于二元函数,定理 107 说明,研究 detHf 和 ∂2f ∂y2(a) 就足够了。

对于 f(x,y) = x² + y²,我们有 Hf = 4 且 ∂2f ∂y2(0,0) = 2,说明 0 是 f(x,y) = x² + y² 的局部最小值。

同样地,我们可以得出结论,0 是 g(x,y) = −(x² + y²) 的局部最大值(这并不令人惊讶,因为 g = −f)。

最后,对于 h(x,y) = x² − y²,二阶导数检验确认 0 确实是一个鞍点。

那么,通用情况如何呢?不幸的是,仅仅研究 Hessian 矩阵的行列式是不够的。我们需要引入重磅人物:特征值。(见定义 23)。这就是二阶导数检验的完整形式。

定理 108. (多变量二阶导数检验)

设 f : ℝ^n →ℝ 是一个任意的向量-标量函数,假设 f 对所有变量在某个 a ∈ℝ^n 处部分可微。还假设 a 是一个临界点,即 ∇f(a) = 0。

(a) 如果 Hf 的所有特征值都是正数,则 a 是局部最小值。

(b) 如果 Hf 的所有特征值都是负数,则 a 是局部最大值。

(c) 如果 Hf 的所有特征值都是正数或负数,则 a 是鞍点。

没错:如果任何特征值为 0,则该检验无法得出结论。你可能还记得在线性代数中,实际上计算特征值的速度不如计算二阶导数快,但有很多数值方法(比如我们在第 7.5 节中看到的 QR 算法)。

总结一下,优化(可微)多变量函数的方法是一个简单的两步过程:

  1. 通过解方程 ∇f(x) = 0 来找到临界点,

  2. 然后使用二阶导数检验来确定哪些临界点是极值点。

我们在实际中使用这种方法来优化函数吗?不使用。为什么?最重要的原因是,对于具有数百万变量的向量-标量函数,计算 Hessian 矩阵的特征值是非常困难的。为什么二阶导数检验如此重要?因为理解函数在极值点附近的行为是深入理解梯度下降的关键。信不信由你,这就是理论上保证收敛性的关键。

说到梯度下降,现在是时候深入探讨驱动神经网络的算法了。

17.3 梯度下降的完整形式

梯度下降是机器学习中最重要的算法之一。我们已经讨论过很多次,尽管到目前为止,我们仅仅看到它用于单变量函数(我承认,这并不是最实际的应用案例)。

然而,现在我们已经掌握了所有讨论梯度下降一般形式所需的工具。让我们开始吧!

假设我们有一个可微的向量-标量函数 f : ℝ^n →ℝ ,我们想要最大化它。这可以描述投资策略的回报,或任何其他量。计算梯度并找到临界点通常不可行,因为解方程 ∇f(x) = 0 在计算上可能不可行。因此,我们采用迭代解法。

该算法与单变量函数的情况相同(如第 13.2 节所见):

  1. 从一个随机点开始。

  2. 计算它的梯度。

  3. 向它的方向迈出一步。

  4. 重复直到收敛。

这被称为梯度上升。我们可以按照以下方式进行形式化。

(梯度上升算法)步骤 1:初始化起始点 x[0] ∈ℝ^n 并选择学习率 h ∈ (0,∞)。步骤 2:令xn+1 := xn + h∇f (xn )。步骤 3:重复步骤 2,直到收敛。

如果我们想最小化 f,那么不妨最大化−f。这样做的唯一影响是梯度的符号变化。以这种形式,算法被称为梯度下降法,这也是广泛用于训练神经网络的版本。

(梯度下降算法)步骤 1:初始化起始点 x[0] ∈ℝ^n 并选择学习率 h ∈ (0,∞)。步骤 2:令xn+1 := xn − h∇f (xn )。步骤 3:重复步骤 2,直到收敛。

完成所有这些设置后,实现梯度下降法非常直接。

def gradient_descent( 
    f: MultivariableFunction, 
    x_init: np.array,               # the initial guess 
    learning_rate: float = 0.1,     # the learning rate 
    n_iter: int = 1000,             # number of steps 
): 
    x = x_init 

    for n in range(n_iter): 
        grad = f.grad(x) 
        x = x - learning_rate*grad 

    return x

注意,这与第 13.2 节中的单变量版本几乎完全相同。为了验证它是否有效,让我们在之前由 SquaredNorm 实现的平方欧几里得范数函数上进行测试!

squared_norm = SquaredNorm() 
local_minimum = gradient_descent( 
    f=squared_norm, 
    x_init=np.array([10.0, -15.0]) 
) 

local_minimum
array([ 1.23023192e-96, -1.84534788e-96])

实际上并没有什么特别之处。多变量梯度下降法的问题与我们在单变量版本中讨论的相同:它可能会陷入局部最小值,对学习率的选择非常敏感,而且在高维度下,梯度的计算可能非常困难。

17.4 小结

尽管本章简短而精炼,但通过详细剖析高维度梯度下降法的细节,我们迈出了一个很大的步伐。本章的简洁是向量化强大功能的见证:相同的公式、代码和超级充能的功能。这几乎难以置信,但这个简单的算法

xn+1 = xn − h∇f (xn)

这是大多数神经网络模型背后的原理。是的,甚至是最先进的模型。

这与单变量情况有相同的理论基础,但我们不再检查二阶导数的正性,而是需要研究完整的 Hessian 矩阵 H[f]。更准确地说,我们已经了解到,临界点∇f(a) = 0 是

  1. 如果 Hf 的所有特征值都是正数,则为局部最小值,

  2. 如果 Hf 的所有特征值都是负数,则为局部最大值。

从根本上讲,这就是梯度下降法有效的原因。有了这个,我们已经完成了对微积分的学习,包括单变量和多变量情况。

深呼吸,放松一下。我们即将进入冒险的最后阶段:我们最后的目的地是概率论,这是预测建模背后的思维范式。例如,最著名的损失函数,如均方误差或交叉熵,都是基于概率概念的。理解和驾驭不确定性是科学领域中最大的智力成就之一,我们即将开始这段旅程。

下章见!

17.5 问题

问题 1. 设 y ∈ ℝ^n 是一个任意向量。著名的均方误差的一般版本定义为

 n MSE (x) = 1-∑ (x − y )². n i i i=1

计算其梯度,并使用 MultivariateFunction 基类实现它!

问题 2. 设 f : ℝ² → ℝ 是由以下方式定义的函数

 2 2 f(x,y) = (2x − y)(y − x )².

f 在 x = (0,0) 处有局部极值吗?

问题 3. 使用之前实现的 gradient_descent 函数来找到以下函数的最小值

 2 2 f(x,y) = sin(x+ y)+ x y .

尝试不同的学习率和初始值!

问题 4. 在第十三章的问题部分,我们看到了梯度下降的改进版本,称为带动量的梯度下降。我们可以在多变量的情况下做相同的事情:定义

dn+1 = αdn − hf ′(xn), xn+1 = xn + dn,

其中 d[0] = 0 且 x[0] 是任意的。实现它!

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读这本书。提问、为其他读者提供解决方案、通过“问我任何问题”环节与作者互动,等等。扫描二维码或访问链接加入社区。 packt.link/math

PIC

第二十二章

参考文献

  1. Rudin, W. (1976). 《数学分析原理》(第 3 版)。麦格劳-希尔出版社。

  2. Rudin, W. (1986). 《实变函数与复变函数分析》(第 3 版)。麦格劳-希尔出版社。

  3. Spivak, M. (2008). 《微积分》(第 4 版)。剑桥大学出版社。

第四部分

概率论

本部分包括以下章节:

  • 第 18,概率是什么

  • 第 19,随机变量和分布

  • 第 20,期望值

什么是概率?

在我们日常生活中,我们几乎总是以二元的方式来思考。一个陈述要么为真,要么为假。一个结果要么已经发生,要么没有发生。

在实践中,我们很少能够拥有绝对的确定性。我们必须在不完全的信息下操作。当科学家观察实验结果时,他们能以 100% 的确定性验证自己的假设吗?不能。因为他们无法完全控制所有变量(如天气或星星的排列),观察到的效果可能是偶然的。每个结果要么增强,要么削弱我们对假设的信心,但没有结果能提供最终的证明。

在机器学习中,我们的工作不仅仅是对某个类别标签进行预测,而是构建一个数学模型,总结我们对数据的理解,并以一种能够传达我们对预测准确度的信心的方式来表达。

因此,拟合一个参数化函数 f : ℝ^n →ℝ^m 来建模数据与要预测变量之间的关系是不够的。我们需要一个全新的词汇来表达这样的模型。我们需要从概率的角度来思考。

第二十三章:18.1 思维的语言

首先,让我们谈谈我们是如何思考的。从最基本的层面来看,我们对世界的知识存储在命题中。从数学角度来看,命题是一个要么为真,要么为假的声明。(在二进制的术语中,真用 1 表示,假用 0 表示。)

“天空是蓝色的。”

“质数是无限多的。”

“1 + 1 = 3。”

“我得了流感。”

命题通常被缩写为变量,例如 A = “外面在下雨”。

使用证据和推理来确定给定命题的真值被称为推理。为了能够制定有效的论证并理解推理是如何工作的,我们将简要了解一下数学逻辑的世界。

18.1.1 绝对思维

因此,我们有命题,例如 A = “外面在下雨” 或 B = “人行道是湿的”。我们需要更多的表达能力:命题是构建块,我们想要将它们组合起来,得到更复杂的命题。(我们将在这里回顾数学逻辑的基础知识,但请查看附录 A 获取更多信息。)

我们可以通过逻辑连接词将简单命题构造为复杂命题。考虑命题“如果外面下雨,则人行道是湿的”。这是 A 和 B 的结合,通过蕴含连接词串联在一起。

有四个基本的连接词:

  • NOT (¬),也称为否定,

  • AND ( ∧),也称为合取,

  • OR (∨),也称为析取,

  • THEN (→),也称为蕴含。

连接词通过结果命题的真值来定义。例如,如果 A 为真,则 ¬A 为假;如果 A 为假,则 ¬A 为真。用 1 表示真,用 0 表示假,我们可以通过真值表来描述连接词。以下是否定的真值表:

| | | |A-|¬A--| |0 | 1 | | | | |1 | 0 | | |

AND (∧) 和 OR (∨) 用来连接两个命题。如果 A ∧B 为真,那么 A 和 B 都必须为真;如果 A ∨B 为真,那么 A 或 B 中至少有一个为真。

| | | | | |A-|B--|A-∧-B--|A-∨-B-| |0 | 0 | 0 | 0 | | | | | | |0 | 1 | 0 | 1 | |1 | 0 | 0 | 1 | | | | | | |1 | 1 | 1 | 1 | | |

连接词 THEN (→) 形式化了从前提 A 推导结论 B 的过程。根据定义,A →B 在 B 为真或 A 和 B 都为假时为真。举个例子:“如果外面在下雨”,那么“人行道是湿的”。

| | | | |A--|B-|A--→-B--| | 0 |0 | 1 | | | | | | 0 |1 | 1 | | 1 |0 | 0 | | | | | | 1 |1 | 1 | | |

请注意,A →B 并不意味着 B →A。这种常见的逻辑谬误被称为肯定后件,我们在生活中都曾经陷入过这种错误。举个具体的例子:“外面在下雨”可以推出“人行道是湿的”,但反过来就不成立。人行道也可以因为其他原因变湿,例如有人打翻了一桶水。

连接词与集合运算相对应。为什么?让我们来看看集合运算的正式定义。

定义 75. (集合运算与关系的(合理的)正式定义)

设 A 和 B 为两个集合。

(a) A 和 B 的并集定义为

A ∪ B := {x : (x ∈ A )∨ (x ∈ B )},

即 A ∪B 包含所有在 A 或 B 中的元素。

(b) A 和 B 的交集定义为

A ∩ B := {x : (x ∈ A )∧ (x ∈ B )},

即 A ∩B 包含所有既在 A 又在 B 中的元素。

(c) 我们说 A 是 B 的子集,即 A ⊆B,当且仅当

(x ∈ A ) → (x ∈ B )

对于所有 x ∈A,命题成立。

(d) A 相对于 Ω ⊃A 的补集定义为

Ω ∖A := {x ∈ Ω : ¬ (x ∈ A )},

即 Ω ∖A 包含所有在 Ω 中但不在 A 中的元素。

如果你仔细阅读这些定义,你会发现连接词和集合运算之间的关系。∧ 是交集,∨ 是并集,¬ 是补集,→ 是子集关系。这在图 18.1 中有所说明。(在这里我稍微滥用了符号,因为像 A ∧B ⟺ A ∩B 这样的表达式在数学上是不正确的。A 和 B 不能同时是命题和集合,因此这个等式并不精确。)

PIC

图 18.1:连接词和集合运算

为什么这很重要?因为概率作用于集合,而集合充当命题的角色。我们稍后会讨论这个问题,但首先,让我们深入了解数学逻辑如何形式化科学思维。

让我们细化数学逻辑的推理过程。一个命题要么为真,要么为假,公平而明确。那么我们如何在实际中确定这一点呢?例如,我们如何找到命题“素数有无穷多个”的真值呢?

通过使用证据和推理。就像福尔摩斯通过连接事实来破案一样,我们依赖于形式为“如果 A,则 B”的知识。我们对世界的知识是通过真实的蕴含存储的。例如:

  • “如果下雨,那么人行道会湿。”

  • “如果ABC是一个直角三角形,那么 A² + B² = C²。”

  • “如果一个系统是封闭的,那么它的熵不能减少。”

正如我们所见,蕴含可以转化为集合论的语言(和其他所有连接词一样)。其中,∧对应交集,∨对应并集,而蕴含则是子集关系。记住这一点,因为它非常重要。

在推理过程中,我们以以下方式使用蕴含:

  1. 如果 A,那么 B。

  2. A。

  3. 因此,B。

这被称为“假言推理”(modus ponens)。如果听起来有些抽象,下面是一个具体的例子:

  1. 如果下雨,人行道是湿的。

  2. 正在下雨。

  3. 因此,人行道是湿的。

因此,我们可以在不查看人行道的情况下推断出它的状态。这比听起来更重要:假言推理是科学思维的基石。没有它,我们仍然会生活在洞穴里。假言推理使我们能够建立起强大的知识大厦。

然而,这并不完美。经典的演绎逻辑可能有助于证明素数的无穷性,但当面对数学和哲学以外的推理问题时,它会失败得很惨。

经典逻辑有一个致命的缺陷:它无法处理不确定性。想想这个简单的命题“外面在下雨”。如果我们无法实际观察天气,但有一些间接证据(比如人行道是湿的,或者天空多云,或者外面是秋天),那么“外面在下雨”是可能的,但不是确定的。

我们需要一个工具来在 0 到 1 的范围内衡量真值。这就是概率派上用场的地方。

18.1.2 概率思维

从数学角度来看,概率是一个将介于零和一之间的数值分配给代表事件的各个集合的函数。(你可以把事件看作是命题。)事件是事件空间的子集,通常用大写希腊字母欧米伽(Ω)表示。图 18.2 展示了这一点。

PIC

图 18.2:事件与事件空间

这听起来相当抽象,让我们看看一个简单的例子:掷一个公平的六面骰子。我们可以用事件空间Ω = {1,2,3,4,5,6}来编码所有可能的结果。像 A = “结果是偶数”或 B = “结果大于 3”这样的事件,可以通过集合表示为

A = {2,4,6}, B = {4,5,6}.

由于骰子是公平的,每个结果的概率是相同的:

 1- P ({1}) = ⋅⋅⋅ = P({6}) = 6.

有两个属性使得这样的函数 P 成为一个合适的概率度量:

  1. 事件空间的概率是 1,

  2. 不相交事件的并集的概率是各个事件概率之和。

在我们的掷骰子例子中,这可以转化为例如

P (结果为偶数) = P({2,4,6}) = P({2}) + P({4}) + P({6}) 1 1 1 = --+ --+ -- 6 6 6 = 1. 2

我们将在下一节中详细讨论这些性质。由于逻辑连接词可以用集合论语言表示,集合运算将逻辑的语义转化为概率。交集表示事件的联合发生,联集表示任一事件的发生。

PIC

图 18.3:交集和并集的概率

通过这种方式,我们能够构建涉及不确定性的模型,并发展出一种微积分来操作这些模型。在数学的“巴别塔”中,统计学处理建模部分,而概率论处理微积分部分。

即使是技术上训练有素的工程师也常常混淆建模和使用模型。例如,当我们讨论抛公平硬币时,正反面朝上的概率都是 1∕2。即使我们对模型完全确信,但如果连续十次都是正面,许多人也会立即得出结论:我们的硬币是有偏的。

为了确保我们不犯这个错误,首先我们要学习概率是什么。

18.2 概率公理

在前一节中,我们已经讨论了概率作为数学逻辑的扩展。就像形式逻辑一样,概率也有它的公理,我们需要理解这些公理才能使用概率模型。现在,我们要回答一个基本问题:概率的数学模型是什么,我们如何与它打交道?

概率是在实验和结果的背景下定义的。要讨论概率,我们需要定义我们将概率赋予什么。正式来说,我们用 P(A)表示事件 A 的概率。首先,我们将讨论什么是事件。

18.2.1 事件空间和σ-代数

让我们回顾一下前一节中的六面骰子示例。这里有六种不同的互斥结果(也就是说,无法同时发生的事件),它们共同构成事件空间,记作Ω:

Ω := {1,2,3,4,5,6}.

一般来说,事件空间是所有互斥结果的集合。它可以是任何集合。

我们可以为哪些类型的事件赋予概率?显然,个别的结果是我们首先想到的。然而,我们也可以考虑像“结果是奇数”、“结果是 2 或 6”或“结果不是 1”这样的事件。根据这个逻辑,我们的预期是,对于任何两个事件 A 和 B,

  • “A 或 B”,

  • “A 和 B”,

  • 和“非 A”

这些也是事件。它们可以转化为集合论的语言,并通过σ-代数的概念进行形式化。

定义 76\。

(σ-代数)

设Ω为一个事件空间。如果Ω的子集的集合Σ ⊆ 2^Ω满足以下条件,那么称Σ为Ω上的σ-代数:

(a) Ω ∈ Σ。(也就是说,所有结果的集合是一个事件。)

(b) 对于所有 A ∈ Σ,集合Ω∖A 也是Σ的元素。(也就是说,σ-代数对补集是封闭的。)

(c) 对于所有 A[1],A[2],⋅⋅⋅∈ Σ,集合∪[n=1]^∞A[n]也是Σ的一个元素。(也就是说,σ-代数对并运算是封闭的。)

由于事件是通过集合来建模的,因此诸如“与”、“或”和“非”等逻辑概念可以转化为集合运算:

  • 事件 A 和 B 的联合发生相当于 A ∩B,

  • “A 或 B”相当于 A ∪B,

  • “非 A”相当于Ω ∖A。

定义的一个直接结果是,对于任何事件 A[1],A[2],⋅⋅⋅∈ Σ,它们的交集∩[n=1]^∞A[n]也是Σ的一个成员。事实上,正如德摩根定律(定理 153)所暗示的那样,

 ∞ ∞ Ω ∖(∩ n=1 An ) = ∪n=1(Ω ∖ An).

由于Ω ∖ (Ω ∖A) = A,我们得到

 ∞ ∞ ∩ n=1An = Ω ∖ (Ω ∖(∩n=1An )) ∞ = Ω ∖ ∪n=1( Ω◟-∖◝A◜n◞). ∈Σ

因此,σ-代数的定义特性保证了∩[n=1]^∞A[n]确实是Σ的一个元素。定义的另一个直接结果是,由于Ω ∈ Σ,空集∅也是Σ的一个成员。

乍一看,σ-代数似乎有点抽象。像往常一样,适当的抽象现在将为我们的后续学习带来巨大的回报。为了让这个概念更贴近实际,这里有一个关于σ-代数的英文总结:

  • 所有可能结果的集合是一个事件。

  • 对于任何事件,它的不发生也是一个事件。

  • 对于任何事件,它们的联合发生也是一个事件。

  • 对于任何事件,至少有一个事件发生也是一个事件。

现在我们已经掌握了正式定义,接下来看看第一个例子。

示例 1. 投掷一个六面骰子。在这里,σ-代数就是事件空间的幂集:

Ω = {1,2,3,4,5,6}, Σ = 2Ω.

(回想一下,A 的幂集是集合 2^A,它包含了 A 的所有子集,如定义在定义 108 中的所述。)尽管这是最简单的一个例子,但它将作为一个原型,并为构建更复杂的事件空间提供一个基础。

示例 2. 投掷硬币 n 次。一次投掷有两个可能的结果:正面或反面。为了简化,我们用 0 表示正面,用 1 表示反面。由于我们投掷硬币 n 次,实验的结果将是一个包含 n 个 0 和 1 的序列,如:(0,1,1,1,…,0,1)。因此,完整的事件空间是Ω = {0,1}^n。

(我们还没有讨论概率,但可以花些时间弄清楚如何将它们分配给这些事件。如果现在不清楚也没关系,我们稍后会详细讲解。)

就像之前的例子一样,σ-代数 2^Ω是一个不错的选择。它涵盖了我们需要的所有事件,例如,“尾巴的个数是 k”。

在实践中,σ-代数很少被明确给出。当然,对于像上面的简单情况,是可以的。

那么,在事件空间不可数的情况下该如何处理呢?例如,假设我们从 0 到 1 之间选择一个随机数。那么,Ω = [0,1],但选择Σ = 2^([0,1])是极其有问题的。回想一下,我们希望为Σ中的每个事件分配一个概率。幂集 2^([0,1])是如此之大,以至于会发生一些非常奇怪的事情。在某些情况下,我们可以将集合切割成有限个部分,并从这些部分重新组装成两个相同的集合。(如果你对更多内容感兴趣,可以查看Banach-Tarski 悖论。)

为了避免像上述那样奇怪的情况,我们需要另一种方式来描述σ-代数。

18.2.2 描述σ-代数

让我们从一个简单但基本的σ-代数性质开始,这个性质我们很快会用来对σ-代数给出一个友好的描述。

定理 109。(事件代数的交集。)

设Ω为样本空间,Σ[1]和Σ[2]为其上的两个σ-代数。则Σ[1] ∩ Σ[2]也是一个σ-代数。

证明。正如我们在σ-代数的定义中所看到的(定义 76),我们需要验证三个性质以证明Σ[1] ∩ Σ[2]确实是一个σ-代数。这个验证非常简单,所以我建议你先自己试试看,再阅读我的解释。

(a) 由于Σ[1]和Σ[2]都是σ-代数,Ω ∈ Σ[1]且Ω ∈ Σ[2]都成立。因此,根据交集的定义,Ω ∈ Σ[1] ∩ Σ[2]。

(b) 设 A ∈ Σ[1] ∩ Σ[2]。由于它们都是σ-代数,Ω ∖ A ∈ Σ[1]且Ω ∖ A ∈ Σ[2]。因此,Ω ∖ A 也是交集的元素。

(c) 设 A[1],A[2],⋅⋅⋅ ∈ Σ[1] ∩ Σ[2]为任意事件。我们可以使用之前完全相同的论证:由于Σ[1]和Σ[2]都是σ-代数,我们有

⋃∞ ∞⋃ An ∈ Σ1 且 An ∈ Σ2。n=1 n=1

所以,联合也是交集的成员,即,

⋃∞ An ∈ Σ1 ∩ Σ2。 n=1

有了这些,我们准备好通过生成集来描述σ-代数了。

定理 110。(生成的σ-代数)

设Ω为一个事件空间,S ⊆ 2^Ω为其任意集合的集合。则存在唯一的最小σ-代数σ(S),它包含 S。

(所谓最小,指的是如果Σ是包含 S 的σ-代数,则σ(S) ⊆ Σ。)

证明。我们之前的结果表明,σ-代数的交集也是一个σ-代数。因此,让我们取所有包含 S 的σ-代数,并取它们的交集。正式地,我们定义

σ(S) = ∩ {Σ : Σ是一个σ-代数且 S ⊆ Σ。}

根据定义,σ(S)显然是最小的,并且它也包含 S。

还显然,生成的σ-代数是唯一的,因为如果存在另一个σ̂(S)满足条件,那么根据构造,σ̂(S) ⊆ σ(S)且σ(S) ⊆ σ̂(S),因此σ̂(S) = σ(S)。

立刻,我们可以利用这一点精确构造一个σ-代数,用于一个极其常见的任务:从 0 到 1 之间选一个数。

示例 3. 在 0 到 1 之间选择一个随机数。显然,事件空间是Ω = [0,1]。那么事件呢?在这种情况下,我们想问的问题是,随机数 X 落在某些 a,b ∈ [0,1]之间的概率是多少。即,像(a,b)、(a,b]、[a,b]、[a,b]这样的事件(无论我们是否需要 a 和 b 之间严格的不等式)。

因此,一个合适的σ-代数可以通过由形如(a,b]的事件生成的代数给出。即,

![Σ = σ({(a,b] : 0 ≤ a <b ≤ 1}). ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1634.png)

这个Σ具有丰富的结构。例如,它包含了简单的事件,如{x},其中 x ∈ [0,1],但也包含更复杂的事件,如“X 是一个有理数”或“X 是一个无理数”。花几分钟思考一下为什么这是对的。如果你没有看到答案,不用担心——我们将在问题部分讲解这个问题。(如果你思考这个问题,你也会明白为什么我们选择了形如(a,b]的区间,而不是(a,b)或[a,b]这样的其他形式。)

18.2.3 实数上的σ-代数

从我们到目前为止看到的所有例子来看,很明显,最常见的做法是我们在ℕ或ℝ上定义概率空间。当Ω ⊆ℕ时,σ-代数的选择是明确的,因为Σ = 2^Ω总是适用的。

然而,正如上面示例 3 所示,选择Σ = 2^Ω当Ω ⊆ℝ时可能会导致一些奇怪的结果。因为我们关心的是事件的概率,例如[a,b],所以我们的标准选择是生成的σ-代数。

ℬ(ℝ) = σ({ (a,b) : a,b ∈ ℝ }), (18.1)

被称为 Borel 代数,以著名的法国数学家埃米尔·博雷尔的名字命名。由于其构造,ℬ包含了所有对我们重要的事件,如区间和区间的并集。ℬ的元素称为 Borel 集合。

因为σ-代数对并集是封闭的,可以看出所有类型的区间都可以在ℬ(ℝ)中找到。这个结果可以通过以下定理来总结。

定理 111\。

对于所有 a,b ∈ℝ,集合[a,b]、(a,b]、[a,b)、(−∞,a]、(−∞,a)、(a,∞)和 a,∞)都是ℬ(ℝ)的元素。

作为一个练习,试着自己推导出证明。启发思路的一个技巧是开始画一些图形。如果你能够直观地理解发生了什么,你会很快发现证明。

证明。一般来说,对于给定的集合 S,我们可以通过将其表示为已知的 Borel 集合的并集/交集/差集来证明它属于ℬ(ℝ)。首先,我们有

![ ∞ ⋃ (a,∞ ) = (a,n), n=1

所以 (a,∞) ∈ ℬ(ℝ)。通过类似的论证,我们可以得出(−∞,a) ∈ ℬ(ℝ)。

接下来,

![(− ∞, a] = ℝ ∖ (a,∞ ), a,∞ ) = ℝ ∖ (− ∞, a),

所以 (−∞,a],[a,∞) ∈ ℬ(ℝ) 对于所有的 a。通过这些集合,可以通过交集生成[a,b],(a,b],[a,b)等集合。

既然我们理解了事件和σ-代数的概念,我们可以开始详细地探讨概率。在下一节中,我们将引入其精确定义。

18.2.4 概率度量

让我们回顾一下到目前为止所学的内容!在数学语言中,具有内在不确定性的实验使用结果、事件空间和事件来描述。

实验的所有可能的互斥结果的集合称为事件空间,记作 Ω。Ω 的某些子集称为事件,我们希望为这些事件分配概率。

这些事件形成了所谓的 σ-代数,记作 Σ。我们用 P(A) 来表示事件 A 的概率。

直观来说,我们对概率有三个合理的期望:

  1. P(Ω) = 1,也就是说,至少有一个结果发生的概率为 1. 换句话说,我们的事件空间是对实验的完整描述。

  2. P(∅) = 0,也就是说,任何结果都不发生的概率为 0. 这再次意味着我们的事件空间是完整的。

  3. 对于两个互斥事件,任意一个事件发生的概率是各自概率的和。

这些通过以下定义被形式化。

定义 77.(概率度量和空间)设 Ω 为事件空间,Σ 为 Ω 上的 σ-代数。我们说函数 P : Σ → [0,1] 是 Σ 上的概率度量,如果满足以下性质:

(a) P(Ω) = 1。

(b) 如果 A[1],A[2],… 是互不相交的事件(即,A[i]∩A[j] = ∅ 对于所有 i≠j),那么 P(∪[n=1]^∞A[n]) = ∑ [n=1]^∞P(A[n])。这个性质称为概率度量的 σ-可加性。

与概率度量 P 一起,结构 (Ω,Σ,P) 被称为形成一个概率空间。

一如既往,让我们首先看看一些具体的例子!我们将继续使用在讨论 σ-代数时已经解决的例子。

示例 1,继续。掷一个六面骰子。回忆一下,事件空间和代数是通过以下方式定义的

 Ω Ω = {1,2,3,4,5,6}, Σ = 2 .

如果我们对骰子没有额外的知识,合理的假设是每个结果的概率相等。也就是说,由于有六种可能的结果,我们有

P ({1}) = ⋅⋅⋅ = P({6}) = 1. 6

请注意,在这种情况下,知道单个结果的概率就足以确定任何事件的概率。这是因为概率的(σ-)可加性。例如,事件“掷骰子的结果是奇数”可以通过以下方式描述:

 3 P ({1,3,5} ) = P ({1})+ P ({3 })+ P ({5} ) = 6-.

用英语表达,任何事件的概率可以通过以下公式写出:

P (event) = -favorable outcomes-. all possible outcomes

你可能记得这个公式,它来自你的小学和高中的学习(具体取决于你所在国家的课程)。这是一个有用的公式,但有一个警告:它只在假设每个结果的概率相等时有效。

当我们的骰子不是均匀加权时,各种结果发生的概率是不相等的。(想象一下一个铅制骰子,其中一面明显比其他面重。)目前,我们不考虑这种情况,稍后会详细讨论这一普遍情况。

示例 2,继续。投掷硬币 n 次。在这里,我们的事件空间和代数是Ω = {0,1}^n,Σ = 2^Ω。为了简化,假设 n = 5。

某一特定结果的概率是多少呢?例如 HHTTT?逐步分析,第一次投掷为正面的概率是 1∕2. 也就是说,

 1 P(第一次投掷是正面) =- 2

由于第一次投掷与第二次投掷是独立的,

P(第二次投掷是正面) = 1 2

也是如此。为了结合这一点并计算第一次和第二次投掷都为正面的概率,我们可以这样思考。对于第一次投掷为正面的结果,其中一半的第二次投掷也是正面。因此,我们要寻找的是一半中的一半。也就是,

P(前两次投掷是正面) = P(第一次投掷是正面)P(第二次投掷是正面) 1- = 4.

按照相同的逻辑继续推演,我们得到

 1 1 P (HHTTT ) = -5 = --. 2 32

如果我们稍微深入分析,就会发现这遵循了之前看到的“有利/总数”公式。事实上,通过一些组合学知识可以发现,总共有 2⁵种可能性,且它们的概率相等。

考虑到这一点,五次投掷中恰好有两个正面的概率是多少呢?用集合的语言来说,我们可以将每次五次投掷的实验编码为{1,2,3,4,5}的子集,元素表示结果为正面的投掷。(例如,{1,4,5}表示结果为 HTTHH。)在这种情况下,恰好有两个正面的实验正是{1,2,3,4,5}的二元子集。

从我们的组合学研究中,我们知道给定元素的子集数目是

( ) n = ---n!----, k k!(n − k)!

其中 n 是我们集合的大小,k 是我们期望的子集大小。因此,总共有(5) 2次出现恰好两个正面的情况。按照“有利/总数”公式,我们有

 ( ) P(五次投掷中出现两个正面) = 5 -1-= 10. 2 32 32

再举一个例子,我们就可以继续往下讲了。

示例 3,继续。选择一个 0 到 1 之间的随机数。在这里,我们的事件空间是Ω = [0,1],我们的σ代数是生成的代数

![ ( ) Σ = σ {(a,b] : 0 ≤ a <b ≤ 1} . ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1650.png)

在没有任何进一步信息的情况下,合理的假设是每个数字都能以相等的概率被选中。这对于像Ω = [0,1]这样的无限事件空间意味着什么呢?我们无法将 1 分成无数个相等的部分。

所以,我们不应只考虑个别结果,而应开始考虑事件。我们用 X 来表示随机选取的数字。如果所有数字“同样可能”,那么 P(X ∈ (0,1∕2]) 是多少?直观上,基于我们“同样可能”的假设,这个概率应该与 [0,1∕2] 的大小成正比。因此,

P (X ∈ I) = |I|,

其中 I 是某个区间,jIj 是它的长度。例如,

P (a <X <b) = P(a ≤ X < b) = P(a <X ≤ b) = P(a ≤ X ≤ b) = b− a.

通过给定 σ-代数生成集上的概率,可以推导出所有其他事件的概率。例如,

P (X = x) = P(0 ≤ X ≤ x)− P (0 ≤ X <x) = x − x = 0.

因此,选中一个特定数字的概率是零。这里有一个重要的教训:概率为零的事件是可以发生的。一开始这听起来违反直觉,但根据上面的例子,你可以看到这是正确的。

18.2.5 概率的基本性质

现在我们已经熟悉了概率的数学模型,可以开始使用它们了。操作概率表达式让我们能够处理复杂的情景。

如果你还记得,概率测度有三个简单的定义性质(见定义 77):

(a) P(Ω) = 1,

(b) P(∅) = 0,且

(c) P⋃ ∞ ( n=1 An) = ∑ [n=1]^∞P(A[n]),如果事件 A[n] 互不相交。

从这些性质中,许多其他性质可以推导出来。为了简化,下面是一个定理,概括了最重要的几个性质。

定理 112\。

设 (Ω,Σ,P) 是一个概率空间,A, B ∈ Σ 是两个任意事件。

(a) P(A ∪B) = P(A) + P(B) −P(A ∩B)。

(b) P(A) = P(A ∩B) + P(A ∖B)。具体地,P(Ω ∖A) + P(A) = 1。

(c) 如果 A ⊆B,那么 P(A) ≤P(B)。

这个证明非常简单,因此作为练习留给你自己。所有这些都来自于对互不相交事件的概率测度加法性。(如果你没看出解决办法,可以画一些维恩图!)

另一个基本工具是全概率法则,它在处理更复杂的事件时经常被使用。

定理 113. (全概率法则)

设 (Ω,Σ,P) 是一个概率空间,A ∈ Σ 是一个任意事件。如果 A[1],A[2],⋅⋅⋅∈ Σ 是互不相交的事件(即 A[i] ∩A[j] = ∅ 当 i≠j 时),并且 ∪[n=1]^∞A[n] = Ω,那么:

P(A) = ∑[n=1]^∞ P(A ∩ A[n])。(18.2)

我们称那些互不相交且其并集构成整个事件空间的事件为分割事件。

证明。这简单地遵循了概率测度的 σ-加法性。可以自己尝试一下证明,来检验你的理解。

如果你看不懂这个,没关系。这里有一个简短的解释。由于 A[1],A[2],… 是互不相交的,A ∩A[1],A ∩A[2],… 也是互不相交的。此外,由于 ∪[n=1]^∞A[n] = Ω,我们还可以得到:

∞⋃ ( ∞⋃ ) (An ∩ A) = An ∩ A n=1 n=1 = Ω ∩ A = A.

因此,概率测度的 σ-加法性意味着:

 ∞ P (A) = ∑ P (A ∩ A ), n=1 n

这就是我们要证明的。

让我们立即看一个例子!假设我们投掷两个骰子。那么,结果之和为 7 的概率是多少?

首先,我们应该正确描述概率空间。为了简化符号,假设投掷的结果为 X 和 Y。我们要找的是 P(X + Y = 7)。如果我们对两个骰子投掷加上顺序的要求,那么建模就变得最简单。考虑到这一点,事件空间 Ω 可以通过笛卡尔积来描述

Ω = {1,2,3,4,5,6}× {1,2,3,4,5,6} = {(i,j) : i,j ∈ {1,2,3,4,5,6}},

结果是 (i,j) 形式的元组。(也就是说,元组 (i,j) 编码了基本事件 {X = i, Y = j}。)由于投掷是相互独立的,

P (X = i,Y = j) = 1-⋅ 1-=-1-. 6 6 36

(当事件{X = i, Y = j}明确时,我们可以省略括号。)

由于第一次投掷的结果在 1 到 6 之间,我们可以通过形成事件空间来划分它

A := {X = n}, n = 1,...,6. n

因此,总概率法则给出了

 6 P (X + Y = 7) = ∑ P ({X + Y = 7} 和 {X = n }). n=1

然而,如果我们知道 X + Y = 7 且 X = n,那么我们也知道 Y = 7 −n 必须成立。因此,继续上面的计算,

 ∑6 ( ) P (X + Y = 7) = P {X + Y = 7} 和 {X = n} n=1 ∑6 = P(X = n,Y = 7 − n) n=1 6 ∑ 1-- = 36 n=1 = 1. 6

所以,总概率法则通过将复杂事件分解为简单事件,帮助我们处理复杂的事件。我们现在已经看到了这种模式好几次了,而且它再次证明了其重要性。

作为 σ-加性(σ-additivity)的另一个结果,我们可以通过取极限来计算递增事件序列的概率。

定理 114. (概率测度的下连续性)

设 (Ω, Σ, P) 是一个概率空间,且 A[1] ⊆ A[2] ⊆ ⋅⋅⋅ ∈ Σ 是一个递增事件序列。那么,

P(∪[n=1]^∞ A[n]) = lim[n→∞] P(A[n]) (18.3)

成立。这个性质称为概率测度的下连续性。

证明。由于事件是递增的,即 A[n−1] ⊆ A[n],我们可以将 A[n] 写为

An = An−1 ∪ (An ∖An −1),

其中 A[n−1] 和 A[n] ∖A[n−1] 是不相交的。

因此,

 ∞⋃ ∞⋃ An = (An ∖ An− 1), A0 := ∅, n=1 n=1

这给出了

 ∑∞ P (∪∞n=1An ) = P(An ∖ An− 1) n=1 N∑ = lim P (An ∖ An− 1) N → ∞n=1 N = lim ∑ [P(A )− P (A )] N → ∞ n n−1 n=1 = lim P (AN ), N → ∞

其中我们用到了 P(∅) = 0。

我们可以为递减事件序列给出上述定理的类似命题。

定理 115. (概率测度的上连续性)

设 (Ω, Σ, P) 是一个概率空间,且 A[1] ⊇ A[2] ⊇ ⋅⋅⋅ ∈ Σ 是一个递减事件序列。那么,

P(∩[n=1]^∞ A[n]) = lim[n→∞] P(A[n]) (18.4)

成立。这个性质称为概率测度的上连续性。

证明。为了简化,我们将无穷交集记为 A := ∩[n=1]^∞ A[n]。

通过定义 B[n] := A[1] ∖A[n],我们有 ∪[n=1]^∞B[n] = A[1] ∖A。由于 A[n] 是递减的,B[n] 是递增的,因此我们可以应用定理 114 得到

P (A1 ∖A ) = nl→im∞ P (A1 ∖ An ) = P (A1) − lni→m∞ P (An).

由于 P(A[1] ∖A) = P(A[1]) −P(A),我们得到 P(A) = lim[n→∞]P(A[n])。

现在我们已经有了一个概率模型的数学定义,是时候迈出一步进入机器学习所在的空间:ℝ^n。

18.2.6 ℝ^n 上的概率空间

在机器学习中,每个数据点是一个基本结果,位于欧几里得空间 ℝ^n 的某个位置。因此,我们有兴趣在这个空间中模拟实验。

如何在 ℝ^n 上定义概率空间?正如我们在第 18.2.1 节中对实数轴所做的那样,我们通过生成一个方便的 σ-代数来描述它。在那里,我们可以使用 (a,b) 区间的高维对应物:n 维球体。为此,我们定义集合

 n B (x,r) := {y ∈ ℝ : ∥x − y ∥ <r},

其中 x 是球体的中心,r/span>0 是它的半径,∥⋅∥ 表示通常的欧几里得范数。(B 表示球体的意思。在数学中,n 维球体通常称为球。)与实数轴类似,Borel σ-代数的定义为

ℬ(ℝ^n) := σ({B(x, r) : x ∈ ℝ^n, r > 0}) (18.5)

正如我们在实数轴上所看到的(见第 18.2.3 节),ℬ(ℝ^n) 的结构比定义所暗示的更为丰富。这里,区间的类似物是矩形,其定义为

(a,b) = (a1,b1)× ⋅⋅⋅× (an,bn) n = {x ∈ ℝ : ai <xi <bi,i = 1,...,n},

其中 A×B 是笛卡尔积。(见定义 110)类似地,我们可以定义 [a,b],(a,b],[a,b] 等等。

定理 116.

对于任意 a,b ∈ ℝ^n,集合 [a,b],[a,b),(a,b],(a,∞),[a,∞),(−∞,a),(−∞,b] 都是 ℬ(ℝ^n) 的元素。

证明。证明过程与 ℬ(ℝ) 中的对应部分相同。因此,留给你作为练习。

作为提示,首先,我们可以证明 (a,b) 可以写成可数个球的并集。我们还可以证明这种情况对于一些集合也成立,例如

ℝ × ⋅⋅⋅× (◟−-∞◝,◜-ai)◞ × ⋅⋅⋅× ℝ i- th component

通过这两个,我们可以将其他集合写成并集/交集/差集的形式。

举个例子,假设我们在一面矩形墙上投掷几只飞镖。假设我们是糟糕的飞镖玩家,打到墙上的任何一点的概率是一样的。

我们可以用 Ω = [0,1] × [0,1] ⊆ ℝ² 来模拟这个事件空间,代表我们的墙。可能的事件是什么?例如,墙上挂着一个圆形的飞镖靶,我们想找出打中它的概率。在这种情况下,我们可以将通过 (22.2.6) 定义的 Borel 集合限制为

 ( ) ℬ Ω := {A ∩Ω : A ∈ ℬ(ℝn )}.

既然事件空间和代数已经清晰,我们需要思考如何赋予事件概率。我们的假设是,任何一个点的出现概率是相等的。所以,通过推广我们在离散情况下看到的faallvo proassblibele ouotcutocmomeess公式,我们定义了概率度量:

P (A ) = volume-(A-). volume (Ω )

(在二维空间中,我们使用的是面积而不是体积。)这在图 18.4 中有所说明。

PIC

图 18.4:投掷飞镖打墙的概率空间。来源:https://unsplash.com/photos/black-and-white-round-logo-i3WlrO7oAHA

正如我们稍后将看到的,这其实是均匀分布的一个特殊案例,均匀分布是概率论中最常见的分布之一。然而,在那之前还有很多要讨论的内容。在我们结束对概率基础的讨论之前,让我们来讨论一下如何解释它们。

18.2.7 如何解释概率

既然我们知道了如何处理概率,接下来就该研究如何将概率赋予现实生活中的事件了。

首先,我们将看看频率派解释,用相对频率来解释概率。(如果你是那些对此问题有宗教般信仰的人,冷静一下。我们会详细讨论贝叶斯解释,但现在还不是时候。)

让我们回到一开始,考虑掷硬币实验。如果我公平地投掷硬币 1000 次,其中多少次会是正面?大多数人会立刻回答 500 次,但这是不正确的。没有正确答案,因为正面次数可以是 0 到 1000 之间的任何一个数。当然,最有可能的是接近 500,但也有非常小的概率会出现 0 次正面。

一般来说,事件的概率描述了在无限多次尝试中该事件的相对频率。也就是说,

 number of occurrences P (event) ≈ --------------------. number of attempts

当尝试次数趋向于无限时,事件发生的相对频率会收敛到真实的基本概率。换句话说,如果 X[i]定量描述了我们的第 i 次尝试,

 ( |{ 1 if the event occurs Xi = | ( 0 otherwise,

然后

 X + ⋅⋅⋅+ X P (event) = lim --1---------n. n→ ∞ n

我们可以通过快速模拟掷硬币的例子来说明这一点。如果你不理解代码,不用担心;我们将在接下来的章节中详细讲解。

import numpy as np 
from scipy.stats import randint 

n_tosses = 1000 
# coin tosses: 0 for tails and 1 for heads 
coin_tosses = [randint.rvs(low=0, high=2) for _ in range(n_tosses)] 
averages = [np.mean(coin_tosses[:k+1]) for k in range(n_tosses)]

让我们绘制一些结果以获得一些洞察:

import matplotlib.pyplot as plt 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(16, 8)) 
    plt.title("/span>Relative frequency of the coin tosses 
    plt.xlabel("/span>Number of tosses 
    plt.ylabel("/span>Relative frequency 

    # plotting the averages 
    plt.plot(range(n_tosses), averages, linewidth=3) # the averages 

    # plotting the true expected value 
    plt.plot([-100, n_tosses+100], [0.5, 0.5], c="/span>k 
    plt.xlim(-10, n_tosses+10) 
    plt.ylim(0, 1) 
    plt.show()

PIC

图 18.5:掷硬币的相对频率

相对频率很好地稳定在 1/2 左右,这就是我们公平硬币正面朝上的真实概率。这是偶然吗?不是。

我们将在第 20.5 节讨论大数法则时将这一切数学化,但首先,我们将介绍贝叶斯观点,这是一种基于新观察更新模型的概率框架。

18.3 条件概率

在前面的章节中,我们学习了概率的基础知识。现在我们可以使用结果、事件和机会来进行讨论。然而,在实际应用中,这些基本工具不足以构建有用的预测模型。

为了说明这一点,让我们构建一个概率垃圾邮件过滤器!对于我们收到的每封邮件,我们希望估算其垃圾邮件的概率 P(邮件是垃圾邮件)。这个概率越接近 1,说明我们看到的邮件越有可能是垃圾邮件。

基于我们的收件箱,我们可能会计算出垃圾邮件的相对频率,并得出

 垃圾邮件数量 P(邮件是垃圾邮件 ) ≈-------------------------. 收件箱中的邮件数量

然而,这对我们没有任何帮助。根据这个信息,我们可以以概率 P(邮件是垃圾邮件) 随机丢弃每封邮件,但这将是一个糟糕的垃圾邮件过滤器。

为了改进,我们需要更深入地挖掘。当分析垃圾邮件时,我们开始注意到一些模式。例如,“立即行动”这一短语几乎只出现在垃圾邮件中。经过快速统计,我们得出

 #包含“立即行动”短语的垃圾邮件 P(包含“立即行动”短语的邮件是垃圾邮件 ) = ---#包含“立即行动”短语的邮件- ≈ 0.95.

这看起来对我们垃圾邮件过滤工作更有帮助。通过检查是否包含“立即行动”这一短语,我们可以自信地将一封邮件归类为垃圾邮件。

当然,垃圾邮件过滤还有更多内容,但这个例子展示了基于其他事件的条件概率的重要性。为了将其数学化,我们引入以下定义。

定义 78.(条件概率)

设(Ω,Σ,P) 是一个概率空间,设 A,B ∈ Σ 为两个事件,并假设 P(A)/span>0。给定 A 的条件下 B 的条件概率定义为

P (B | A ) := P-(A-∩-B). P (A)

你可以将 P(B∣A) 理解为将事件空间限制在 A 中,如图 18.6 所示。

PIC

图 18.6:条件概率的可视化表示

当有更多条件时,例如 A[1] 和 A[2],定义形式为

 P-(B-∩-A1-∩A2-) P(B | A1,A2) = P (A1 ∩A2 ) ,

以此类推,适用于更多的事件。

为了让这个概念更清晰,我们来回顾一下掷骰子实验。假设你的朋友掷了一个六面骰子,并告诉你结果是一个奇数。基于这一信息,结果是 3 的概率是多少?为了简化问题,我们用 X 来表示掷骰子的结果。从数学角度讲,这可以通过以下公式计算:

P (X = 3 | X ∈ {1,3,5}) = P-(X-=-3-and-X-∈-{1,3,5}) P (X ∈ {1,3,5}) ---P-(X--=-3)--- = P (X ∈ {1,3,5}) = 1∕6- 1∕2 1- = 3.

这是我们预期的数字。

虽然这个简单的例子没有展示条件概率的实用性,但它是机器学习中的基石。实质上,从数据中学习可以表述为估计 P(标签∣数据)。我们将在本章后面进一步扩展这个观点。

18.3.1 独立性

条件概率的核心思想是,观察某些事件会改变其他事件的概率。但是,难道每次都是这样吗?

在概率建模中,认识到观察一个事件不会影响另一个事件同样重要。这推动了独立性概念的发展。

定义 79.(事件的独立性)

设(Ω,Σ,P)为一个概率空间,A, B ∈ Σ为两个事件。如果 A 和 B 是独立的,则我们说

P (A ∩ B) = P(A)P(B)

成立。

等效地,可以通过条件概率来表述此问题。根据定义,如果 A 和 B 是独立的,我们有

P (B | A ) = P(A∩B) / P(A ) = P(A)P(B)

举个例子,回到掷硬币的情景,假设我们投掷一枚硬币两次。设第一次和第二次的投掷结果分别用 X[1]和 X[2]表示。那么这两次投掷都是正面的概率是多少?正如我们在 18.2.4 节中讨论过的,我们可以看到

P (X1 = 正面且 X2 = 正面) = P(X1 = 正面)P(X2 = 正面) = 1. 4

也就是说,这两个事件是彼此独立的。

关于概率,有很多常见的误解。其中之一是关于独立性的解释。假设我投掷一枚公平的硬币十次,结果每次都是正面。那么我下一次投掷得到正面的概率是多少?

大多数人会立即得出结论:这是一个非常小的概率,因为连续十一次得到正面是极不可能的。然而,一旦我们得到了前十次的结果,我们就不再讨论十一次投掷的概率,而只是讨论最后一次!由于硬币投掷彼此独立,第十一投(在前十次结果已知的条件下)得到正面的概率仍然是 50%。

这种现象被称为赌徒谬误,我敢肯定,在你的一生中,你曾经成为过它的受害者。(我自己也有过。)

在实际场景中,处理条件概率可能更为简便。(例如,有时候我们可以直接估计条件概率,而标准概率则很难衡量。)因此,我们需要一些工具来处理它们。

18.3.2 总概率法则再探

还记得定理 113 中的总概率法则吗?我们可以使用条件概率将其转换为稍微不同的形式。

定理 117.(总概率法则,条件版)

设(Ω, Σ, P)为一个概率空间,且 A ∈ Σ为任意事件。如果 A[1], A[2], ⋅⋅⋅ ∈ Σ为互不相交的事件(即 A[i] ∩ A[j] = ∅,当 i ≠ j 时),并且∪[n=1]^∞A[n] = Ω,则

P(A) = ∑[k=1]^∞ P(A | A[k]) P(A[k]) (18.6)

证明:这个证明是全概率法则(定理 113)和条件概率定义的简单应用:因为 P(A ∩ A[k]) = P(A∣A[k])P(A[k]),

P(A) = ∑[k=1]^∞ P(A ∩ A[k]) = ∑[k=1]^∞ P(A | A[k]) P(A[k])

成立,这就是我们需要证明的内容。

为什么这对我们有用呢?让我们通过一个例子来展示。假设我们有三个 urn,其中包含浅色和深色球。

第一个 urn 包含 4 个深色球,第二个 urn 包含 2 个浅色球和 2 个深色球,而最后一个 urn 包含 1 个浅色球和 3 个深色球。

PIC

图 18.7:包含彩色球的 urn

我们随机选择一个 urn;然而,选择第一个 urn 的概率是选择另外两个的两倍。(也就是说,我们选择第一个 urn 的概率是 50%,而选择第二个和第三个 urn 的概率分别为 25%和 25%。)然后,我们从该 urn 中随机挑选一个球。那么,我们选择一个浅色球的概率是多少?如果不使用全概率法则,这个计算是比较困难的。

设选中的球的颜色为 X,并假设事件 A[n]表示选择了第 n 个 urn。那么,我们有

 ∑3 ∑3 P (X = light) = P ({X = light} ∩ Ak) = P (X = light | Ak )P (Ak). k=1 k=1

不使用条件概率,计算 P({X = light}∩A[k])是困难的(因为我们并非以等概率选择每个 urn)。但是,我们可以通过计算每个 urn 中浅色球的数量来简化条件概率的计算。也就是说,我们有

P(X = light | A1 ) = 0 2 P(X = light | A2 ) = 4 1 P(X = light | A3 ) =-. 4

由于 P(A[1]) = 1/2, P(A[2]) = 1/4, 且 P(A[3]) = 1/4,我们要寻找的概率是

 ∑3 P (X = light) = P (X = light | Ak)P (Ak) k=1 1 2 1 1 1 = 0⋅ 2 + 4 ⋅ 4 + 4 ⋅ 4 3 = --. 16

注意,由于 urn 的选择并非等概率,

 浅色球的数量 P (X = light) ⁄= ------------------, 球的总数

如同直觉上猜测的那样。

条件概率的另一个有用性质是,由于其定义,我们可以利用它来表示事件的联合概率:

P (A ∩ B ) = P (B | A )P (A).

尽管这听起来很简单,但有时我们可以估计/计算条件概率,但却无法计算联合概率。事实上,这个简单的恒等式可以推广到任意数量的条件,这就是链式法则。(尽管它叫做链式法则,但与微分中的链式法则无关。)

定理 118. (链式法则)

设(Ω, Σ, P)为一个概率空间,A[1], A[2], ⋅⋅⋅ ∈ Σ为任意事件。那么,

P(A) = ∑[k=1]^∞ P(A | A[k]) P(A[k]) (18.7)

成立。

证明:首先,我们注意到 P(A[1] ∩⋅⋅⋅∩A[n])可以写为

 P (A1 ∩ ⋅⋅⋅∩An ) P (A1 ∩ ⋅⋅⋅∩ An −1) P (A1 ∩ A2) P(A1 ∩⋅⋅⋅∩An ) =------------------------------------...-----------P (A1), P (A1 ∩ ⋅⋅⋅ ∩An −1)P (A1 ∩ ⋅⋅⋅∩ An −2) P(A1 )

因为这些项互相抵消。

由于

-P(A1∩Ak) - P(A1 ∩ Ak−1) = P (Ak | A1,...,Ak− 1),

链式法则(18.7)随之得出。

18.3.3 贝叶斯定理

本质上,机器学习是将观察转化为预测模型。概率论为我们提供了表达模型的语言。例如,回到我们的垃圾邮件过滤示例,我们可以注意到 5%的邮件是垃圾邮件。然而,仅凭这些信息不足以过滤垃圾邮件。经过检查,我们观察到 95%的包含“立即行动”这一短语的邮件是垃圾邮件(但只有 1%的邮件包含“立即行动”)。用条件概率的语言来说,我们得出结论:

P (垃圾邮件 | 包含“立即行动”) = 0.95.

这样,我们可以开始查找包含“立即行动”这一短语的邮件,并以 95%的置信度将其丢弃。这个垃圾邮件过滤器有效吗?其实不太有效,因为垃圾邮件中可能还包含其他常见关键词,而我们并没有检查这些。我们该如何检查呢?

举个例子,我们可以看一下条件概率 P(包含“立即行动”|垃圾邮件),它描述了在所有垃圾邮件中“立即行动”这一关键词的出现频率。低频率意味着我们错过了其他可以用于过滤的关键词。

一般来说,我们通常希望计算/估算数量 P(A|B),但我们的观察只能推断出 P(B|A)。因此,我们需要一种方法来反转条件和事件。通过一些代数运算,我们可以轻松做到这一点。

定理 119. (贝叶斯公式)

设 (Ω, Σ, P) 为一个概率空间,A, B 为两个任意事件,且假设 P(A), P(B) > 0,那么,

L(U,V ) = {f : U → V | f 是线性的}(18.8)

成立。

证明:根据条件概率的定义,我们有

 P(A∩B) P(B | A ) = P(A ) = P(A∩B)P(B) P(B )P(A ) P-(B-) = P(A | B )P (A),

这是我们需要证明的。

为了验证它的实际效果,让我们在垃圾邮件过滤示例中进行测试。根据我们所知道的信息,我们有

P (垃圾邮件 | 包含“立即行动”) = 0.95, P(包含“立即行动”) = 0.01, P(垃圾邮件) = 0.05.

所以,根据贝叶斯公式,

 P-(垃圾邮件 | 包含“立即行动”) P(包含“立即行动”) P(包含“立即行动” | 垃圾邮件) = P(垃圾邮件) 0.95⋅0.01 = --------- 0.05 = 0.19.

因此,仅仅过滤包含“立即行动”的邮件,我们会错过很多垃圾邮件。

我们可以通过将贝叶斯公式与定理 117 中的全概率法则结合,进一步应用它。(见方程式(18.6)。)

定理 120. (贝叶斯定理)

设(Ω, Σ, P)为一个概率空间,且 A, B ∈ Σ为任意事件。此外,设 A[1], A[2],⋅⋅⋅ ∈ Σ为事件空间Ω的一个划分。(即,A[n]是两两不相交的,并且它们的并集是整个事件空间。)那么,

 ----P-(A-| B-)P-(B)--- P (B | A) = ∑ ∞ P (A | An)P (An) n=1

保持。

证明。这个证明直接来自贝叶斯公式(定理 119)和全概率法则(定理 117)。

18.3.4 概率的贝叶斯解释

历史上,概率是作为观察事件的相对频率引入的,见第 18.2.7 节。然而,条件概率和贝叶斯公式的发明使得另一种解释逐渐在统计学和机器学习中流行起来。

在纯英文中,贝叶斯公式可以被理解为通过新的观察结果来更新我们的概率模型。假设我们对事件 B 感兴趣。在没有任何观察的情况下,我们可以通过给 B 分配一个概率来构建一个概率模型,即估计 P(B)。这就是我们所说的先验。然而,观察到另一个事件 A 可能会改变我们的概率模型。

因此,我们希望估计后验概率 P(B∣A)。我们不能直接这样做,但多亏了我们的先验模型,我们可以知道 P(A∣B)。这个量 P(A∣B)被称为似然。将这些与贝叶斯公式结合起来,我们可以看到后验与似然和先验成正比。

PIC

图 18.8:贝叶斯公式,作为似然和先验的乘积

让我们来看一个具体的例子,以便更清楚地理解这个概念。假设我们正在为一种罕见疾病开发诊断测试。那么,随机一个人是否患有这种疾病的概率有多大?

在不了解任何具体情况的情况下,我们只能通过统计来构建概率模型。假设只有 2%的人口受影响。那么,我们的概率模型是

P (infected) = 0.02, P(healthy) = 0.98.

然而,一旦某人产生了阳性测试结果,情况就会发生变化。目标是估计后验概率 P(infected∣positive),一个更准确的模型。

由于没有任何医疗测试是完美的,因此可能会发生假阳性和假阴性。从制造商那里,我们知道该测试在 99%的情况下会给出真正的阳性结果,但假阳性的概率是 5%。用概率术语来说,我们有

P (positive | infected) = 0.99, P (positive | healthy) = 0.05.

基于这些,贝叶斯定理给出了

 P (positive | infected)P(infected) P(infected | positive) = P-(positive-| infected)P(infected)+-P-(positive-| healthy)P-(healthy) = ------0.99⋅0.02------ 0.99⋅0.02 + 0.05 ⋅0.98 ≈ 0.29.

所以,在给出阳性测试结果时,感染的概率令人惊讶地为 29%(假设这些特定的真正阳性和假阳性率)。

这些概率思维原则同样适用于机器学习。如果我们抽象化地看待从数据中学习的过程,实际上我们是在进行 1)观察,2)根据新的观察更新我们的模型,以及 3)重新开始这个过程。贝叶斯定理为这个过程提供了具体的工具。

18.3.5 概率推理过程

正如我们之前所见,概率论是数学逻辑的扩展。到目前为止,我们讨论了逻辑连接词如何与集合运算对应,以及概率如何通过加入不确定性成分来推广真值。那么,如何处理概率推理过程呢?我们能否将经典推理推广,利用概率推理来构建论证?答案是肯定的。

为了说明这一点,我们从一个故事开始。现在是早上 6 点,闹钟响个不停,但你起床非常困难。你感觉不舒服,肌肉无力,头痛欲裂。经过短暂的挣扎后,你终于拨通了医生的电话,列出了所有症状。喉咙痛让你说话都感到疼痛。

“这可能只是流感,”他们说。

像这样的互动是日常常见的事。然而,我们很少考虑它们背后的推理过程。毕竟,你可能只是宿醉了。同样,如果警察在你家里发现谋杀武器,他们会怀疑你是凶手。两者之间有关联,但不完全相同。例如,谋杀武器可能是被安置的。

人类知识的主要来源就是通过这种方式获得的:我们收集证据,然后建立假设。我们如何从观察结果推断出潜在原因?最重要的是,我们如何避免自欺欺人,得出错误的结论?

我们来关注一下“肌肉疲劳、头痛、喉咙痛 → 流感”。从绝对意义上来说,这显然不对,因为这些症状更像是你在金属音乐会中大声喊叫和过度饮酒后会感觉到的那种不适,离流感相差甚远。然而,流感的阳性诊断是有可能的。鉴于当前的证据,我们对这一假设的信心增强了。

不幸的是,经典逻辑无法处理“可能性”,只能处理“绝对”。概率论通过在 0 到 1 的区间内测量可能性,解决了这个问题,而不是固守极端。零是不可能的。 一是确定的。介于两者之间的所有值表示不确定性的不同程度。

我们用数学术语来表达这一点!

我们如何在因果关系之间建立概率联系?在经典逻辑中,事件在其他事件的上下文中才具有意义。之前,蕴涵和模态肯定提供了这种上下文。用概率的语言来说,问题是这样的:在观察到 A 的情况下,B 发生的概率是多少?答案是:条件概率。

为什么条件概率能够推广蕴含的概念?画图会更容易理解,考虑图 18.9 中的两个极端情况。(回忆一下,蕴含对应于子集关系,正如我们之前所看到的。)

PIC

图 18.9:条件概率作为逻辑蕴含

本质上,P(B∣A) = 1 表示 A →B 成立,而 P(B∣A) = 0 则表示不成立。我们可以进一步类比:小的 P(B∣A) 表示 A →B 可能为假,而大的 P(B∣A) 则表示它很可能为真。

这在图 18.10 中得到了说明。

PIC

图 18.10:条件概率作为逻辑蕴含的扩展

因此,“概率性模式假言推理”是这样的:

  1. P(B∣A) ≈ 1。

  2. A。

  3. 因此,B 是可能的。

这让人松了一口气,因为现在我们对大多数决策都有了坚实的理论依据。因此,启动我们调查的诊断过程现在更有意义了:

  1. P(流感∣头痛、肌肉疲劳、喉咙痛) ≈ 1。

  2. “头痛和肌肉疲劳”。

  3. 因此,“流感”是可能的。

然而,仍然有一个悬而未决的问题。我们如何知道 P(B∣A) ≈ 1 是成立的?

让我们关注“头痛、喉咙痛、肌肉疲劳 →流感”的概率版本。我们知道这不是确定的,只是合理的。然而,反向蕴含“流感 →头痛、喉咙痛、肌肉疲劳”几乎是确定的。

当我们天真地认为证据意味着假设时,我们心里想的是相反的情况。我们没有应用模式假言推理,而是使用了错误的论证。

  1. A →B。

  2. B。

  3. 因此,A。

我们之前讨论过:这种逻辑谬误叫做肯定后件,从纯粹逻辑的角度来看是完全错误的。然而,贝叶斯定理提供了一个概率性的转折。

命题 A →B 转化为 P(B∣A) = 1,这意味着当观察到 A 时,B 也会发生。为什么?因为这样我们就得到了

 P-(B-| A-)P-(A) P(A | B) = P (B) = P-(A) P (B) ≥ P(A ).

这是个好消息,因为反转蕴含并非完全错误。相反,我们有了概率性的肯定后件:

  1. A →B。

  2. B。

  3. 因此,A 更可能。

有了这个,概率推理过程变得完全合理。回顾一下,“如果你有肌肉疲劳、喉咙痛和头痛,那么你得了流感”这样的论证有问题,因为这些症状也可能由其他疾病引起,而且在少数情况下,流感并不会出现所有这些症状。

然而,这种思维方式在现实生活中的决策中可以出奇有效。概率和条件概率通过三步推理扩展了我们的推理工具包:

  1. 将二元的 0 − 1 真值扩展到允许表示不确定性。

  2. 定义了“如果 A,则 B”类型的蕴含的类比,使用条件概率。

  3. 提供了一种从观察效果推断原因的方法。

这三个思路非常强大,它们的诞生使得科学能够取得难以置信的成就。(如果你对概率论与逻辑的关系感兴趣,我推荐你阅读 E. T. Jaynes 的经典著作《Probability Theory: The Logic of Science》)

还有一件事我想给你展示。让我们回到二十世纪中期,看看一个电视节目是如何塑造概率思维的。

18.3.6 蒙提霍尔悖论

在我们结束条件概率之前,我们将讨论一个重要问题。在概率论中,我们经常会遇到一些看似矛盾的现象,违背了我们的直觉预期。这些现象被称为悖论。为了掌握概率思维,我们需要解决这些悖论,并消除我们思维过程中的常见谬误。到目前为止,我们在讨论独立性概念时已经看到过赌徒谬误(在 18.3.1 节中)。现在,我们将讨论著名的蒙提霍尔悖论。

在 60 年代,美国有一个叫《Let's Make a Deal》(en.wikipedia.org/wiki/Let\%27s_Make_a_Deal)的电视节目。作为参赛者,你面对三扇关闭的门,其中一扇门后面藏着一辆车(你可以带回家),其他两扇门后面什么也没有。你有机会打开其中一扇门。

PIC

图 18.11:三扇关闭的门,其中一扇门后面藏着奖励

假设在选择了第一扇门后,节目主持人蒙提霍尔打开了第三扇门,显示它不是获胜的那扇门。现在,你有机会改变主意,选择打开第二扇门,而不是第一扇门。你会选择换门吗?

PIC

图 18.12:蒙提为你打开了第三扇门。你是否要换门?

乍一看,你的获胜机会是 50%/50%,因此你可能觉得换门没有什么优势。然而,这并不是真的!

为了澄清问题,让我们进行一个仔细的概率分析。设 A[i]表示奖品在第 i 扇门后面的事件,B[i]表示蒙提打开第 i 扇门的事件。在蒙提打开第三扇门之前,我们的模型是

P (A1) = P(A2 ) = P (A3) = 1, 3

我们现在想计算 P(A[1]∣B[3])和 P(A[2]∣B[3])。

从节目的主持人角度思考,你会选择打开哪扇门?如果你知道奖品在第一扇门后面,你会平等概率地打开第二和第三扇门。然而,如果奖品实际上在第二扇门后面(并且参赛者选择了第一扇门),你总是会打开第三扇门。也就是说,

P (B | A ) = P (B | A ) = 1, 3 1 2 1 2 P (B3 | A2) = 1.

因此,通过应用贝叶斯公式,我们得出

 P(B3 | A1 )P(A1) P(A1 | B3 ) =---------------- P(B3 ) = -1∕6--, P(B3 )

 P(B3 | A2 )P(A2) P(A2 | B3 ) =---------------- P(B3 ) = -1∕3--. P(B3 )

总结来说,P(A[2]∣B[3]) 是 P(A[1]∣B[3]) 的两倍, 从中我们可以推断出

P (A1 | B3) = 1, P (A2 | B3) = 2-. 3 3

所以,你应该总是换门。是不是很令人惊讶?这里的悖论是,尽管我们可能预期相反,改变主意才是更好的选择。通过清晰的概率思维,我们可以轻松解决这个问题。

18.4 总结

呼!我们终于结束了一个令人畏惧的、虽然极其重要的章节。尽管我们已经讨论了几十页关于概率的数学细节,但最重要的收获可以用一句话总结:概率理论通过处理不确定性,扩展了我们的推理工具包。它不是通过真或假的二元尺度来衡量命题的真实性,而是开启了一个从 0 到 1 的谱系,其中 0 代表(几乎)不可能,1 代表(几乎)确定。

从数学角度看,概率模型由概率度量和空间定义,即形如 (Ω, Σ, P) 的结构,其中 Ω 是可能的基本结果的集合,Σ 是事件的集合,P 是概率度量,满足

  1. P(Ω) = 1

  2. 并且 P(∪[n=1]^∞−A[n]) = ∑ [n=1]^∞P(A[n]) 对所有互不相交的 A[n] ∈ Σ 成立,

这些被称为 Kolmogorov 公理。思考概率使我们能够在不确定性下进行推理:如果 P(A) 是命题的概率版本,那么条件概率

P (B | A) = P-(A-∩-B) P (A)

是蕴含命题 A → B 的概率版本。

然而,我们所学的所有工具仅仅是巨大冰山的一角。要构建真正强大且有用的模型,我们需要像科学和数学的许多进展那样,将定性转化为定量。你还记得骰子掷出实验吗?我们使用一个神秘的变量 X 来表示掷骰子的结果。通过这种方式,我们可以讨论像“X = k”这样的事件,将一个概率空间转化为一系列数字。

这不是巧合;它是一种方法。X 是随机变量的一个实例,随机变量是概率论和统计学中的重要对象。随机变量在抽象的概率空间与数字和向量之间进行转换,我们的老朋友。让我们把它们作为我们工具箱中的永久工具。

18.5 问题

问题 1. 让我们掷两个六面骰子!描述这个实验的事件空间、σ-代数以及相应的概率。

问题 2. 让 Ω = [0,1],对应的 σ-代数是生成代数

![ ( ) Σ = σ {(a,b] : 0 ≤ a <b ≤ 1} . ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1725.png)

证明以下事件是 Σ 的成员:

(a) S[1] = {x},对于所有 x ∈ [0,1]。

(b) S[2] = ∪[i=1]^n(a[i],b[i])。(证明当区间 […] 被替换为开区间和半开区间(…),(…],(…)时,这也成立。)

(c) S[3] = [0,1] ∩ℚ。 (即 [0,1] 中的有理数集合。)

(d) S[4] = [0,1] ∩(ℝ ∖ℚ)。 (即 [0,1] 中的无理数集合。)

问题 3. 让我们掷两个六面骰子。计算以下事件的概率:

(a) 两次掷骰子都是奇数吗?

(b) 至少有一个是奇数吗?

(c) 它们都不是奇数吗?

问题 4. 设 Ω = ℝ² 为事件空间,其中我们定义了开放圆盘

= D (x,r) := {z ∈ ℝ2 : ∥x − z∥ <r}, x = (x1,x2) ∈ ℝ2, r >0,

以及由开放矩形构成的集合

R(x,y ) = (x1,y1)× (x2,y2) 2 = {z = (z1,z2) ∈ ℝ : x1 <z1 <y1,x2 <z2 <y2}.

证明由这些集合生成的 σ-代数是相同的,即,

 ( 2 ) ( 2 ) σ {D (x, r) : x ∈ ℝ ,r >0} = σ {R (x,y) : x,y ∈ ℝ } .

问题 5. 让我们考虑蒙提霍尔问题的一个变体。假设有一百扇门,而不是三扇;只有一扇门背后藏有奖励。在选择一扇门后,蒙提会打开另外九十八扇门,所有这些门后都是空的。现在你应该换门吗?

加入我们的 Discord 社区

与其他用户、机器学习专家和作者本人的一起阅读此书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者聊天,还有更多内容。扫描二维码或访问链接加入社区。packt.link/math

图片

随机变量与分布

拥有一个概率空间来建模我们的实验和观察是可以的,但在几乎所有的情况下,我们关心的是结果的定量度量。举个例子,假设我们进行 n 次公正的硬币抛掷,但我们只关心正面朝上的次数。我们该如何建模这个概率空间呢?

一步步来;首先,我们通过列举所有可能的结果在一个集合中构造一个事件空间,就像我们在第 18.2.1 节已经做的那样:

Ω = {0,1}n, Σ = 2Ω.

由于硬币是公正的,每个结果 ω 的概率 P(ω) = 12n。到目前为止,这个概率空间 (Ω, Σ, P) 非常简单。利用概率度量的可加性(见定义 77),我们可以计算任何事件的概率。即,对于任何 A ∈ Σ,我们有

P (A) = |A|, |Ω|

其中 j ⋅j 表示给定集合中的元素个数。

然而,正如前面所提到的,我们只关心正面朝上的次数。我们是否应该仅仅把这个信息纳入概率空间的某个地方?当然,我们可以这样做,但那样会把基本结果(即,正面或反面的序列)与测量结果耦合在一起,这会显著地复杂化我们的模型。

我们可以做一些更简单的事情:引入一个函数 X : Ω → ℕ,将结果映射到测量值,而不是直接使用这个概率空间来处理所需的测量。

这些函数被称为随机变量,它们是概率论和统计学的核心。通过收集数据,我们在观察随机变量;通过拟合预测模型,我们使用这些观察值来近似它们。现在我们明白了为什么需要它们,接下来我们将把这个概念数学化。

第二十四章:19.1 随机变量

不过,慢一点;事情并不像看起来那么简单。随机变量在一般形式下很难理解,因此我们将放慢速度,专注于特殊情况,一步一步地进行。这是学习最有效的方法,我们也将遵循这条路径。

让我们首先处理所谓的离散随机变量(例如上面的例子),其次是实际的随机变量,最后是一般情况。

19.1.1 离散随机变量

根据我们描述抛硬币中正面朝上的次数的激励性例子,我们可以创建一个正式的定义。

定义 80. (离散随机变量)

设 (Ω, Σ, P) 为概率空间,{x[k]}[k=1]^∞ 为一任意的实数序列。如果函数 X : Ω → {x[1], x[2], …},则称其为离散随机变量,如果这些集合

S = {ω ∈ Ω : X (ω) = x } k k

对于任何整数 k ∈ ℤ(即,S[k] ∈ Σ),这些都是事件。

你可能会问,为什么我们要求集合 {ω ∈ Ω : X(ω) = x[k]} 必须是事件。这看起来只是一个技术性条件,但它起着至关重要的作用。归根结底,我们定义随机变量是为了测量我们观察到的事件的概率。这个条件确保了我们能够做到这一点。

为了简化我们的符号表示,我们写作:

 ( ) P (X = xk) := P {ω ∈ Ω : X (ω) = xk}

每当我们谈论这些概率时,我们都会这样表示。让我们看一个具体的例子!

在上述抛硬币的例子中,我们的随机变量由以下定义:

X = 正面朝上的硬币数。

即使我们可以通过公式来定义 X,

 ∑n X (ω) = ωk, ω = (ω1, ...,ωn ) ∈ Ω, k=1

这不是必须的。通常情况下,这种做法甚至是不可能的。关于我们的随机变量,我们并不关心知道整个映射,只关心像是 n 次抛掷中正面朝上的次数 k 的概率等问题。

如果我们记录下“时间戳”,即每次结果为正面的时间,我们可以将每个 ω 编码为 {1,2,…,n} 的子集。例如,如果第 1 次、第 3 次和第 37 次抛掷是正面朝上,其余的是反面朝上,这就是 {1,3,37}。要计算 k 次正面的概率,我们需要计算 n 个元素集合的 k 大小子集的数量。这由二项式系数 (n) k 给出。所以,

 ( ) P (X = k) = n 1-. k 2n

当我们讨论二项分布时,我们会详细了解这一点,无论它是什么。现在,我们已经准备好将随机变量进行一般化!

19.1.2 实值随机变量

如果我们的测量不是离散的,会怎么样呢?比如说,假设我们面前有一班学生。我们对他们的身高分布感兴趣。于是,我们随机挑选一名学生,使用我们新买的、能够精确测量身高的工具来测量他的身高。

在这种情况下,离散随机变量不足以应对,但我们可以定义类似的东西。

定义 81.(实值随机变量)

设 (Ω,Σ,P) 为一个概率空间。函数 X : Ω → ℝ 称为随机变量,如果集合

 ( ) X −1 (a,b) := {ω ∈ Ω : a <X (ω) <b}

是对所有 a,b ∈ℝ 的事件。(即,X^(−1)((a,b)) ∈ Σ 对于所有 a,b ∈ℝ 成立。)

让我们展开这个定义。首先,X 是从事件空间 Ω 到实数集 ℝ 的映射,如图 19.1 所示。

PIC

图 19.1:实值随机变量是从事件空间到实数集的映射。

类似于离散情况,我们对事件如 X^(−1)((a,b)) 的概率感兴趣。同样,为了简化,我们写作:

 ( −1( )) P (a <X <b) = P X (a,b) .

你可以把 X^(−1)((a,b)) 想象成映射到 (a,b) 的 Ω 的子集。(一般来说,形式为 X^(−1)(A) 的集合称为逆像。)

PIC

图 19.2:区间的逆像

让我们马上看一个例子。假设我们正在向墙上的圆形靶子投掷飞镖。(为了简便起见,假设我们非常厉害,总是能够命中靶子。)正如我们在讨论高维度中的σ-代数时所见(第 18.2.6 节),我们可以通过选择

Ω = B(0,1) = {x ∈ ℝ2 : ∥x∥ <1}

 ( ) Σ = ℬ B (0,1) ( n ) = σ {A ∩ B (0,1) : A ∈ ℬ (ℝ ) ,

P (A) = area(A)-= area(A). area(Ω) π

由于飞镖靶被划分为同心圆,因此评分由距离中心的距离决定。因此,我们也可以通过以下方式定义我们的随机变量:

X = 距离中心的撞击点距离。

X 编码了我们在评分方面感兴趣的所有内容。一般来说,我们有

 ( || 0 if r ≤ 0, ||{ P(X <r) = r2 if 0 <r <1, ||| |( 1 otherwise.

如果我们有多个测量值怎么办?例如,在著名的鸢尾花数据集的情况下(en.wikipedia.org/wiki/Iris_flower_data_set)(这是我们到目前为止已经看到过几次的数据集),我们有四个测量值。当然,我们可以定义四个随机变量,但那样我们就不能利用到目前为止所构建的所有复杂工具:线性代数和多变量微积分。

为此,我们将先看看一般情况下的随机变量。

19.1.3 一般情况下的随机变量

让我们直接切入正题。

定义 82.(随机变量)

设(Ω[1],Σ[1],P[1])是一个概率空间,设(Ω[2],Σ[2])是另一个事件空间Ω[2],具有σ-代数Σ[2]。函数 X : Ω[1] → Ω[2]是一个随机变量,如果对于每个 E ∈ Σ[2],集合

X −1(E ) := {ω ∈ Ω1 : X (ω) ∈ E }

是Σ[1]的成员。(也就是说,X^(−1)(E) ∈ Σ[1]。)

在数学文献中,随机变量通常用大写拉丁字母如 X, Y 表示,或者用希腊字母(大多数是从ξ开始的)表示。

随机变量本质上是将概率测度从抽象的概率空间推送到更易处理的空间。在事件空间(Ω[2],Σ[2])上,我们可以通过

 ( −1 ) P2(E ) := P1 X (E) , E ∈ Σ2,

使得在保持基础概率模型不变的情况下,将一个概率空间转换到另一个概率空间成为可能。

这个一般情况涵盖了我们在机器学习中感兴趣的所有数学对象。继续使用鸢尾花数据集(en.wikipedia.org/wiki/Iris_flower_data_set),随机变量

X : 鸢尾花的集合 → ℝ4,鸢尾花 ↦→ (花瓣宽度,花瓣长度,萼片宽度,萼片长度)

描述了数据集的生成分布,而对于分类任务,我们关注的是近似随机变量

Y : set of iris flowers → {setosa, versicolor, virginica}, iris flower ↦→ class label.

现在我们将深入探讨为什么随机变量是这样定义的。这部分会有点技术性,如果你觉得难以理解,可以跳过它。这不会影响你对随机变量的理解和应用。

19.1.4 随机变量的定义背后

因此,随机变量是函数,将概率空间映射到度量空间。唯一的问题是,为什么 X^(−1)(E)这些集合如此特殊?让我们回顾一下我们的动机示例:挑选一个随机学生并测量他们的身高。我们关心的是类似学生身高在 155 厘米到 185 厘米之间的概率这样的问提。(如果你更习惯使用英制单位,那么 155 厘米大约是 5.09 英尺,185 厘米大约是 6.07 英尺。)将这个问题转化为公式,我们关注的是

 ( ( )) P(155 ≤ X ≤ 185) = P X − 1[155,185] .

(在上面的公式中,我使用了两种不同的符号表示相同的内容。)

那么,为什么 X^(−1)([155,185])是一个事件呢?为了找出原因,我们先来看一下逆像的一般情况。

定义 83. (关于函数的集合逆像)

设 f : E →H 是两个集合 E 和 H 之间的一个函数,A ⊆ H 是任意集合。则 A 关于函数 f 的逆像定义为

f− 1(A ) := {x ∈ E : f (x ) ∈ A}.

我们喜欢集合的逆像,因为它们在集合操作下行为良好。

这一点通过下面的定理进行了形式化。

定理 121.

设 f : E → H 是两个集合 E 和 H 之间的一个函数。对于任意的 A[1], A[2], ⋅⋅⋅ ⊆ H,以下条件成立:

(a)

 ( ) −1 ⋃∞ ∞⋃ −1 f An = f (An ), n=1 n=1

(b)

f− 1(A1 ∖ A2) = f−1(A1) ∖f− 1(A2 ),

(c)

 ( ∞ ) ∞ −1 ⋂ ⋂ −1 f An = f (An ). n=1 n=1

证明。(a)我们可以通过直接写出定义来轻松看到这一点。即,我们有

 ( ) −1 ⋃∞ { ∞ } f An = x ∈ E : f(x) ∈ ∪ n=1An n=1 ∞⋃ { } = x ∈ E : f(x) ∈ An n=1 ∞⋃ −1 = f (An ), n=1

这就是我们需要证明的内容。(如果你不太熟悉集合论的内容,可以随时回顾附录 C 中的入门集合理论。)

(b) 这可以像(a)一样进行。

(c) 德·摩根定律(定理 153)意味着

 ( ⋃∞ ) ∞⋂ H ∖ An = (H ∖An ) n=1 n=1

成立。将其与(a)和(b)结合起来,(c)得以推导。

为什么这很重要?回想一下,Borel 集合,即我们在实数上使用的标准σ-代数(如在第 18.2.3 节所见),是通过以下方式定义的:

ℬ := σ({(−∞, x] : x ∈ ℝ}) (19.1)

这些包含了我们关心的与测量相关的所有事件。结合我们之前的结果,我们可以揭示随机变量中不明显的部分。

定理 122.

设 (Ω, Σ, P) 为一个概率空间,X : Ω → ℝ 为一个随机变量,且 A ∈ ℬ,其中 ℬ 为由 (23.1.4) 定义的 Borel 代数。那么,X^(−1)(A) ∈ Σ。

也就是说,我们可以测量 X^(−1)(A) 对任何 Borel 集合 A 的概率。没有这个,我们的随机变量就不那么有用了。为了使我们的符号更直观,我们写作

 ( −1 ) P (X ∈ A ) := P X (A ) .

用简单的语言来说,P(X ∈A) 是我们测量 X 落入集合 A 的概率。

现在我们理解了这一切的含义,让我们看一下简单的证明!

证明。这是基于 ℬ 是由形如 (−∞,x] 的集合生成的 σ-代数,并且逆像在集合运算下行为良好的事实(如定理 121 所示)。

19.1.5 随机变量的独立性

在构建外部世界的概率模型时,独立性的假设大大简化了后续的数学分析。回想一下在概率空间 (Ω, Σ, P) 上,事件 A, B ∈ Σ 是独立的,当且仅当

P (A ∩B ) = P(A )P(B ),

或者等价地,

P (A | B) = P (A ).

用简单的语言来说,观察一个事件不会改变我们对另一个事件的概率信念。

由于随机变量 X 由形如 X^(−1)(E) 的事件描述,我们可以将独立性概念推广到随机变量。

定义 84. (随机变量的独立性)

设 X, Y : Ω[1] → Ω[2] 为两个随机变量,定义在概率空间 (Ω[1], Σ[1], P) 和 σ-代数 (Ω[2], Σ[2]) 之间。

我们说 X 和 Y 是独立的,如果对于每个 A, B ∈ Σ[2],

P (X ∈ A, Y ∈ B ) = P (X ∈ A )P(Y ∈ B )

成立。

再次考虑两个掷硬币的情况。X[1] 描述第一次掷硬币,X[2] 描述第二次掷硬币。由于掷硬币是独立的,第一次掷硬币的结果不会为第二次掷硬币提供任何额外的信息。这就是上述定义所形式化的内容。

另一方面,要展示两个相关的随机变量,考虑以下情况。我们掷一个六面骰子,将结果记为 X。接着,我们用 X 个六面骰子进行掷骰,记下它们值的总和为 Y。X 和 Y 是相互依赖的。例如,考虑 P(X = 1, Y > 7) = 0,但 P(X = 1) 和 P(Y > 7) 都不为零。

独立性是我们经常做的假设。当处理由 X[1], X[2], … 表示的随机变量序列时,我们几乎总是假设它们是独立同分布的;即 i.i.d. 随机变量。

现在我们理解了如何处理随机变量,是时候展示如何以紧凑的形式表示它们了。

19.2 离散分布

让我们回顾一下到目前为止所学的内容。在概率论中,我们的目标是首先建模受不确定性影响的现实场景,然后使用数学工具如微积分来分析这些场景。

对于后者,概率空间不易处理。概率测度是定义在σ-代数上的一个函数,因此我们无法在这里使用微积分。

随机变量使我们离解决方案更近了一步,但它们也可能难以处理。尽管实值随机变量 X : Ω →ℝ将抽象的概率空间映射到实数集,但仍然存在一些复杂性。Ω可以是任何东西,而且如果你回想一下,我们可能甚至没有 X 的可解公式。

例如,如果 X 表示一只灯泡的寿命,我们没有公式。因此,我们同样无法使用微积分。然而,有一种方法可以将随机变量所包含的信息表示为序列、向量-标量函数或标量-标量函数。

引入概率分布和密度函数。

考虑一个简单的实验,比如公平地投掷一枚硬币 n 次并统计正面朝上的次数,用 X 表示。正如我们在定义 80 中看到的那样,X 是一个离散随机变量,其

 (| ( ) { nk 12n 如果 k = 0,1,...,n, P (X = k) = | ( 0 其他情况.

然而,序列{P(X = k)}[k=0]^n 完全描述了随机变量 X!

想一想吧。因为我们的事件空间是Ω = {0,1,…,n},所以任何事件的形式都是 A = {a[1],a[2],…,a[l]}⊂ Ω,其中 l ≤n + 1. 因此,

 l ∑ P (X ∈ A ) = P(X = ai), i=1

其中我们使用了概率的加法性(σ -)。序列{P(X = k)}[k=0]^n 包含了我们所需的所有信息。

因此,我们可以不再考虑 X : Ω →ℕ,而仅仅使用{P(X = k)}[k=0]^n。这对我们有什么好处呢?

因为序列非常强大。与神秘的随机变量不同,我们有许多工具可以处理它们。最重要的是,我们可以将它们表示为编程语言中的数字数组。对于纯随机变量,我们无法做到这一点。

定义 85\。 (概率质量函数)

设 X 为一个实值离散随机变量。由以下函数 p[X] : ℝ → [0,1]定义:

p (x) = P(X = x), x ∈ ℝ X

该函数称为离散随机变量 X 的概率质量函数(简称 PMF)。

一般来说,如果实数序列的元素为非负数并且其和为 1,则该序列定义了一个离散分布。

定义 86\。 (离散概率分布)

设{p[k]}[k=1]^∞为一列实数序列。我们说{p[k]}是一个离散概率分布,当且仅当

(a) 对于所有 k,p[k] ≥ 0,

(b) 并且 ∑ [k=1]^∞p[k] = 1。

备注 12\。

请注意,如果随机变量取有限个值(例如我们之前的投硬币实验),则在分布中只有有限个值是非零的。

如前所述,每个离散随机变量 X 定义了分布 {P(X = x[k])}[k=1]^∞,其中 {x[1], x[2], …} 是 X 可以取的可能值。反向也是成立的:给定一个离散分布 p = {p[k]}[k=1]^∞,我们可以构造一个随机变量 X,其概率质量函数(PMF)为 p。

因此,X 的概率质量函数也被称为其分布。我知道,这有点令人困惑,因为“分布”这个词在数学中用得很广泛。你会习惯的。

这些离散概率分布非常适合进行定量分析,而不是随机变量的基本形式。作为额外的好处,想一下分布是如何推广随机变量的。不管我们讨论的是抛硬币还是医学检测,成功的概率率都是由上述离散概率分布给出的。

在继续讨论离散分布的基本属性之前,让我们先看一些例子!

19.2.1 伯努利分布

让我们从最基本的概率分布开始:伯努利分布,描述一个简单的抛硬币实验。我们抛掷一个硬币,正面朝上的概率是 p,反面朝上的概率是 1 − p。实验通过随机变量 X 编码,若抛掷结果为正面,则 X 取值为 1,否则取值为 0:

 ( |{ 1 如果抛掷结果为正面,X = |( 0 否则。

因此,

 ( || ||{ 1 − p 如果 k = 0, P (X = k) = p 如果 k = 1, ||| |( 0 否则。

当随机变量 X 按照此分布进行时,我们写作

X ∼ Bernoulli(p),

其中 p ∈ [0,1] 是分布的参数。

注释 13.(伯努利分布的另一种形式)

有一种巧妙的伯努利分布的替代表述方式,可以避免使用 if-else 定义。由于 k 只能是零或一,P(X = k) 可以写作

 k 1−k P (X = k) = p (1− p) .

请记住这种形式,它将在后续非常有用。

是时候谈论实践中的分布了。Python 有多个统计包,但我们将使用强大的 scipy(虽然它不完全是一个统计包,但它有一个优秀的统计模块):

from scipy.stats import bernoulli

我们可以使用 bernoulli 对象的 rvs 方法生成随机值(就像 scipy 中的任何其他分布一样):

[bernoulli.rvs(p=0.5) for _ in range(10)]    # ten Bernoulli(0.5)-distributed random numbers
[1, 1, 1, 1, 0, 1, 0, 1, 1, 0]

在 scipy 中,概率质量函数通过 pmf 方法实现。

我们甚至可以使用 Matplotlib 可视化这个分布:

import matplotlib.pyplot as plt 

params = [0.25, 0.5, 0.75] 

with plt.style.context("/span>seaborn-v0_8": 
    fig, axs = plt.subplots(1, len(params), figsize=(4*len(params), 4), sharey=True) 
    fig.suptitle("/span>The Bernoulli distribution 
    for ax, p in zip(axs, params): 
        x = range(2) 
        y = [bernoulli.pmf(k=k, p=p) for k in x] 
        ax.bar(x, y) 
        ax.set_title(f/span>p = {p}" 
        ax.set_ylabel("/span>P(X = k) 
        ax.set_xlabel("/span>k 
    plt.show()

PIC

图 19.3:伯努利分布

如果你对细节感兴趣,随时可以查看 SciPy 文档(docs.scipy.org/doc/scipy/reference/generated/scipy.stats.bernoulli.html)了解更多方法!

19.2.2 二项分布

让我们将之前的投掷硬币的例子再推进一步。假设我们投掷同一枚硬币 n 次,X 表示 n 次投掷中正面的次数。那么,恰好得到 k 次正面的概率是多少?

假设,n = 5 且 k = 3。例如,配置 11010(其中 0 表示反面,1 表示正面)有概率 p³(1 −p)²,因为在五次独立的(定义 84)投掷中,有三次正面和两次反面。

有多少种这样的配置呢?选择三个正面的排列位置就等于从五个元素的集合中选择一个三元素的子集。因此,总共有 (5) 3 种可能性。一般来说,从 n 个元素的集合中选择一个 k 元素的子集,总共有 (n) k 种可能性。

结合这些,我们得到

 ( |{ (n) k n−k P (X = k ) = k p (1− p) 如果 k = 0,1,...,n, |( 0 否则。

这就是著名的二项分布,它是概率论与统计学中最常遇到的分布之一。我们用符号表示为

X ∼ 二项分布(n,p),

其中 n ∈ℕ 且 p ∈ [0,1] 是它的两个参数。让我们来可视化这个分布!

from scipy.stats.distributions import binom 

params = [(20, 0.25), (20, 0.5), (20, 0.75)] 

with plt.style.context("/span>seaborn-v0_8": 
    fig, axs = plt.subplots(1, len(params), figsize=(4*len(params), 4), sharey=True) 
    fig.suptitle("/span>The binomial distribution 
    for ax, (n, p) in zip(axs, params): 
        x = range(n+1) 
        y = [binom.pmf(n=n, p=p, k=k) for k in x] 
        ax.bar(x, y) 
        ax.set_title(f/span>n = {n}, p = {p}" 
        ax.set_ylabel("/span>P(X = k) 
        ax.set_xlabel("/span>k 

    plt.show()

PIC

图 19.4:二项分布

19.2.3 几何分布

再来点硬币投掷。我们继续投掷同一枚硬币,直到出现正面。令 X 表示需要的投掷次数。通过一些基本的概率思维,我们可以推导出

 ( |{ k−1 P(X = k) = (1 − p) p 如果 k = 1,2,... |( 0 否则。

(因为如果第 k 次投掷的结果是正面,那么之前我们投掷了 k − 1 次反面。)这就是所谓的几何分布,通常表示为

X ∼ 几何分布(p),

其中 p ∈ [0,1] 是唯一的参数。类似地,我们可以绘制直方图来可视化分布家族:

from scipy.stats import geom 

params = [0.2, 0.5, 0.8] 

with plt.style.context("/span>seaborn-v0_8": 
    fig, axs = plt.subplots(1, len(params), figsize=(5*len(params), 5), sharey=True) 
    fig.suptitle("/span>The geometric distribution 
    for ax, p in zip(axs, params): 
        x = range(1, 20) 
        y = [geom.pmf(p=p, k=k) for k in x] 
        ax.bar(x, y) 
        ax.set_title(f/span>p = {p}" 
        ax.set_ylabel("/span>P(X = k) 
        ax.set_xlabel("/span>k 

    plt.show()

PIC

图 19.5:几何分布

注意,P(X = k) 的概率没有一个是零,但随着 k 的增大,它们会变得非常小。(当 p 趋近于 1 时,衰减速度更快。)

∑ [k=1]^∞(1 −p)^(k−1)p = 1 可能并不立即显现出来。为了证明这一点,我们将使用一个魔术技巧。(你知道的,改编自著名的阿瑟·C·克拉克的名言:“任何足够先进的数学都与魔术无法区分。”)

事实上,对于任意 x ∈ (−1,1),有一个惊人的恒等式

L(U,V ) = {f : U → V | f 是线性的}(19.2)

恒等式成立。

这就是著名的几何级数。利用 (23.2.3),我们得到

 ∞ ∞ ∑ ∑ k−1 P (X = k) = (1− p ) p k=1 k=1∞ ∑ k = p (1 − p) k=0 = p-----1---- 1 − (1− p) = 1.

使用几何级数是数学家常用的技巧之一。例如,在讨论某些分布的期望值时,我们会用到它。

19.2.4 均匀分布

让我们丢掉硬币,换成掷一个六面骰子。我们以前见过这种情况:每种结果的概率相同,也就是说,

P(X = 1) = P(X = 2) = ⋅⋅⋅ = P(X = 6) = 1, 6

其中 X 表示掷骰子的结果。这是均匀分布的一个特殊实例。

一般而言,令 A = {a[1], a[2], …, a[n]} 为有限集合。离散随机变量 X : Ω → A 在 A 上均匀分布,即

X ∼ Uniform(A ),

如果

 1- P (X = a1) = P (X = a2) = ⋅⋅⋅ = P(X = an) = n.

请注意,A 必须是有限集合:无限集合上不存在离散均匀分布。当我们在 {1, 2, …, n} 上有均匀分布时,我们通常将其缩写为 Uniform(n)。

这里是掷一个六面骰子的概率质量函数。知道这并不算特别激动人心,没错:

from scipy.stats import randint 

with plt.style.context("/span>seaborn-v0_8": 
    fig = plt.figure(figsize=(10, 5)) 
    plt.title("/span>The uniform distribution 

    x = range(-1, 9) 
    y = [randint.pmf(k=k, low=1, high=7) for k in x] 
    plt.bar(x, y) 
    plt.ylim(0, 1) 
    plt.ylabel("/span>P(X = k) 
    plt.xlabel("/span>k 

    plt.show()

PIC

图 19.6: (离散) 均匀分布

19.2.5 单点分布

我们将最简单的一个留到最后:单点分布。为此,令 a ∈ ℝ 为任意实数。我们说随机变量 X 根据 δ(a) 分布,如果

 ( |{ 1 if x = a, P (X = x) = |( 0 otherwise.

即,X 以概率 1 取 a。它们对应的累积分布函数为

 (| { 1 if x ≥ a, FX (x) = | ( 0 otherwise,

这是一个具有单个跳跃的简单阶跃函数。

相信我,明确地命名这样一个简单的分布是非常有用的。想到的主要原因有两个。首先,单点分布通常作为随机变量序列的极限分布出现。

其次,任何离散分布都可以用单点分布表示。现在理解这一点并非绝对必要,但在更高阶的学习中,它将是非常重要的。

注 14.(离散分布作为单点分布的线性组合)

令 (Ω, Σ, P) 为概率空间,且令 X : Ω → {x[1], x[2], …} 为离散随机变量,其概率质量函数为 p[i] = P(X = x[i])。

通过引入单点分布 X[i] ∼ δ(x[i]),我们得到

 ∑∞ FX (x) = piFX (x). i=1 i

这个分解可以非常有用。

19.2.6 全概率法则,再次回顾

借助离散随机变量,我们可以用全概率公式(定理 117)的新形式来描述它。

定理 123.(全概率法则,离散随机变量版本)

令 (Ω, Σ, P) 为概率空间,且令 A ∈ Σ 为任意事件。如果 X : Ω → {x[1], x[2], …} 是离散随机变量,则

P(A) = ∑[k=1]^∞ P(A | X = x[k]) P(X = x[k])。(19.3)

证明。对于任何离散随机变量 X : Ω → {x[1], x[2], …},事件 {X = x[k]} 划分了事件空间:它们是互不相交的,且它们的并集为 Ω。因此,可以应用全概率法则,得到

 ∑∞ P(A ) = P(A, X = xk) k=1 ∑∞ = P(A | X = xk)P (X = xk), k=1

这正是我们需要证明的。

换句话说,我们可以在离散随机变量的背景下研究事件。这在实践中非常有用。(很快,我们会看到它不仅适用于离散情况。)

让我们立即运用 (19.3)。

19.2.7 离散随机变量的和

由于离散概率分布由序列表示,我们可以使用数学分析中的各种工具来处理它们。(这就是将随机变量转化为分布的原因。)因此,我们可以通过从简单的随机变量构造更复杂的随机变量来轻松描述它们。

例如,考虑掷两个骰子,我们对和的分布感兴趣。所以,我们可以将其表示为随机变量 X[1] 和 X[2] 的和,分别表示第一次和第二次投掷的结果。我们知道

 (| { 16 如果 k = 1,2,...,6, P(Xi = k) = | ( 0 其它情况

对于 i = 1,2。利用(19.3) 和两个结果独立这一事实,我们得到

 6 P (X + X = k) = ∑ P(X + X = k | X = l)P (X = l) 1 2 l=1 1 2 2 2 6 ∑ = P(X1 = k − l)P(X2 = l) l=1

如果这看起来熟悉,那并非巧合。

你在这里看到的是著名的卷积操作的应用。

定义 87.(离散卷积)

设 a = {a[k]}[k=−∞]^∞ 和 b = {b[k]}[k=−∞]^∞ 为两个任意序列。它们的卷积定义为

 { ∞∑ } ∞ a∗ b := ak−lbl . l= −∞ k=−∞

即,序列 a ∗b 的第 k 个元素通过求和 ∑ [l=−∞]^∞a[k−l]b[l] 来定义。虽然这可能难以想象,但从概率解释的角度来看,定义变得清晰。随机变量 X[1] + X[2] 可以取值 k,如果 X[1] = k −l 且 X[2] = l,对于所有可能的 l ∈ℤ。

注 15. 注 19.2.4(交换下标)

由于对称性,

 ∑∞ ∑∞ ak−lbl = albk−l. l=−∞ l=−∞

因此,a ∗b 的另一种定义是

 { ∑∞ }∞ a ∗b = albk−l . l=− ∞ k= −∞

这个技巧通常非常有用,因为当 a[k] 和 b[k] 明确给出时,有时 ∑ [l=−∞]^∞a[l]b[k−l] 比 ∑ [l=−∞]^∞a[k−l]b[l] 更容易计算,反之亦然。

卷积由 NumPy 支持,因此借助它,我们可以可视化 X[1] + X[2] 的分布:

import numpy as np 

dist_1 = [0, 1/6, 1/6, 1/6, 1/6, 1/6, 1/6] 
dist_2 = [0, 1/6, 1/6, 1/6, 1/6, 1/6, 1/6] 
sum_dist = np.convolve(dist_1, dist_1) 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(10, 5)) 
    plt.bar(range(0, len(sum_dist)), sum_dist) 
    plt.title("/span>Distribution of X1 + X2" 
    plt.ylabel("/span>P(X1 + X1 = k) 
    plt.xlabel("/span>k 
    plt.show()

PIC

图 19.7:两个随机变量和的分布

让我们谈谈一般情况。模式很清晰,因此我们可以公式化一个定理。

定理 124.(离散随机变量的和)

如果 X,Y : Ω → ℤ 都是整数值随机变量,则 X + Y 的分布由各自分布的卷积给出:

 ∞ ∑ P (X + Y = k ) = P (X = k − l)P (Y = l), l=−∞

即,

pX+Y = pX ∗ pY.

证明。证明是对全概率定律的直接应用 (19.3):

P(X + Y = k) = ∑ [l=−∞]^∞ P(X + Y = k | Y = l)P(Y = l) = ∑ [l=−∞]^∞ P(X = k − l)P(Y = l) = (p[X] ∗ p[Y])(k),

这就是我们需要证明的。

另一个随机变量求和的例子是二项分布本身。我们可以将核心实验建模为伯努利分布,而不是考虑 n 次独立试验中的成功次数。也就是说,如果 X[i]是一个伯努利(p)分布的随机变量,描述第 i 次尝试的成功情况,那么我们有

 ∑ P (X1 + ⋅⋅⋅ + Xn = k) = P (X1 = i1,...,Xn = in) i1+ ⋅⋅⋅+in=k = ∑ P (X = i )...P(X = i) ◟---1----1-◝◜-----n---n◞ i1+ ⋅⋅⋅+in=k X1,...,Xn 是独立的 = ∑ pk(1 − p)n−k i1(+ ⋅⋅⋅)+in=k = n pk(1 − p)n−k, k

其中,求和∑ [i[1]+⋅⋅⋅+i[n]=k]遍历所有元组(i[1],…,i[n]) ∈{0,1}^n,使得 i[1] + ⋅⋅⋅ + i[n] = k。(因为有(n) k这样元组的个数,我们得到∑ [i[1]+⋅⋅⋅+i[n]=k] p^k(1 −p)^(n−k) = ( ) nk p^k(1 −p)^(n−k)在最后一步。)

19.3 实值分布

到目前为止,我们讨论了离散随机变量,即具有可数多个值的随机变量。然而,并非所有的实验/观察/测量都是这样。例如,一个人的身高是一个可以取连续值的随机变量。

为了给出一个易于处理的例子,我们从[0,1]中选取一个数字 X,每个数字具有“相等的机会”。在这个上下文中,相等的机会意味着

P (a < X ≤ b) = |b − a|.

我们能用一个单一的实函数来描述 X 吗?就像离散情况一样,我们可以尝试

F(x) = P(X = x),

但这行不通。为什么?

因为对于每个 x ∈ X,我们有 P(X = x) = 0。也就是说,选取特定数字 x 的概率为零。相反,我们可以尝试 FX = P(X ≤ x),即

 ( ||| 0 如果 x ≤ 0,|{ FX(x) = x 如果 0 <x ≤ 1,|||| ( 1 否则。

我们可以绘制这个图形来进行可视化:

from scipy.stats import uniform 
X = np.linspace(-0.5, 1.5, 100) 
y = uniform.cdf(X) 

with plt.style.context(’seaborn-v0_8’): 
    plt.figure(figsize=(10, 5)) 
    plt.title("/span>The uniform distribution 
    plt.plot(X, y) 
    plt.show()

PIC

图 19.8:均匀分布

在接下来的章节中,我们将对所有实值随机变量正确地定义并详细研究这个对象。

19.3.1 累积分布函数

在我们的动机示例中看到的就是一个累积分布函数(简称 CDF)的实例。让我们直接跳入正式的定义。

定义 88: (累积分布函数)

设 X 为一个实值随机变量。由下式定义的函数

FX := P(X ≤ x) (19.4)

称为 X 的累积分布函数(CDF)。

再次让我们解开这个。回顾在实值随机变量的定义(定义 81)中,我们使用了逆映像 X^(−1)((a,b))。

这里发生的事情类似。P(X ≤ x)是 P(X^(−1)((−∞,x]))的简写,我们懒得写出。类似于 X^(−1)((a,b))),你可以通过使用映射 X 将区间(−∞,x]拉回到Ω,从而可视化 X^(−1)((−∞,x]))。

形式为 X^(−1)((−∞,x])的集合称为 X 的水平集。

PIC

图 19.9:随机变量的水平集

根据《牛津英语词典》,累积一词的意思是“通过连续增加而在数量、程度或力量上逐渐增大。”对于离散随机变量,使用 P(X = k)就足够了,但由于实际的随机变量更加细致,我们必须使用累积概率 P(X ≤x)来有意义地描述它们。

为什么我们喜欢使用分布函数?因为它们将关于随机变量的所有相关信息浓缩成一个实际函数。

例如,我们可以像这样表示概率

P (a <X ≤ b) = F (b) − F (a ). X X

举个例子,让我们回顾一下介绍部分,我们在其中选择了一个介于零和一之间的随机数。在这里,具有 CDF 的随机变量 X

L(U,V ) = {f : U → V | f is linear}(19.5)

被称为在[0,1]上均匀分布,或者简写为 X ∼ Uniform(0,1)。我们稍后会看到许多例子,但请记住这一点,因为均匀分布将是本节的教科书示例。

19.3.2 分布函数的性质

累积分布函数具有三种性质来表征它们:它们总是非递减的,右连续的(无论那意味着什么),并且它们的极限在−∞和∞处分别为 0 和 1。你可能从定义中猜到了一些这些内容,但这里是总结这些内容的正式定理。

定理 125.(CDF 的性质)

设 X 为一个具有 CDF F[X]的实值随机变量。然后,F[X]是

(a) 非递减(也就是说,x ≤y 意味着 FX ≤FX),

(b) 右连续(即 lim[x→x[0]+]FX = FX,或者换句话说,取右极限与 F[X]是可以互换的),

(c) 以及极限

 lim F (x ) = 0, lim F (x) = 1 x→ −∞ X x→ ∞ X

成立。

证明。证明相对简单。(a) 由下述事实得出:如果 x/span>y,那么我们有

![ ( ) ( ) X −1 (− ∞, x] ⊆ X −1 (− ∞, y]. ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1837.png)

换句话说,事件 X ≤ x 是 X ≤ y 的子集。因此,由于概率度量的单调性,我们有 P(X ≤ x) ≤ P(X ≤ y)。

(b) 在这里,我们需要证明 lim[x→x[0]+]P(X ≤ x) = P(X ≤ x[0])。为此,请注意,对于任意 x[n] → x[0]且 x[n]/span>x[0],事件序列{ω ∈ Ω : X(ω) ≤x[n]}是递减的,且

![∩∞ X− 1((− ∞, xn]) = X− 1((− ∞, x0]). n=1 ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1838.png)

由于概率度量的上连续性(参见定理 115),F[X]的右连续性随之得到。

(c) 同样,这由以下事实得出:

![ ( ) ∩ ∞n=1X −1 (− ∞, n ] = ∅ ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1839.png)

![∪∞n=1X −1((− ∞, n]) = Ω. ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1840.png)

由于 P(∅) = 0 且 P(Ω) = 1,该陈述根据概率度量的上连续性和下连续性成立。(参见定理 114 和定理 115。)

备注 16.(CDF 的另一种定义)

在文献中,你有时会看到,X 的 CDF 定义为

F∗ (x ) := P(X <x), X

即,X > x,而不是 X ≤ x。这不会改变大体框架,但一些细节会有所不同。例如,这个变化使得 F[X]变为左连续而非右连续。如果你深入挖掘,这些微小的细节很重要,但在机器学习中,我们不用过多关注它们也能正常工作。

定理 125 是反向成立的:如果给我一个右连续的非递减函数 F(x),并且满足 lim[x→−∞]F(x) = 0 和 lim[x→∞]F(x) = 1,我可以构造一个随机变量,使其分布函数与 F(x)匹配。

19.3.3 离散随机变量的累积分布函数

离散和实值情况并不是完全分离的:事实上,离散随机变量也有累积分布函数。(但反过来不行;即,实值随机变量不能用序列来描述。)

假设 X 是一个离散随机变量,取值为 x[1],x[2],…,那么它的 CDF 是

 ∑ FX (x ) = P (X = xi), xi≤x

这实际上是一个分段连续函数。例如,图 19.10 展示了二项分布的 CDF。

PIC

图 19.10:二项分布(10,0.5)的 CDF

它的优势或概率在于能够将现实世界的现象转化为抛硬币、掷骰子、投掷飞镖、灯泡寿命等随机事件。这一切都得益于分布。分布就像是将一大束随机变量串联在一起的丝带。

让我们来了解一些最重要的分布!

19.3.4 均匀分布

我们已经看到均匀分布的一个特殊情况:从区间[0,1]中选择一个随机数,使得所有的结果是“同等可能”的。一般的均匀分布捕捉到相同的概念,只是它适用于任意区间[a,b],其中 a < b。也就是说,随机变量 X 在区间[a,b]上均匀分布,或者用符号表示为 X ∼ Uniform(a,b),如果

![ | | P (α <X ≤ β ) = -1--||[a,b]∩(α,β ]|| b− a ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1844.png)

对于所有α < β,其中|[c,d]|表示区间[c,d]的长度,

换句话说,我们的随机数落入给定区间的概率与该区间的长度成正比。这就是“同等可能”条件的意义所在:由于可能的结果是不可数的,每个单独结果的概率为零,但等长的区间有相同的机会。

根据定义,X 的分布函数为

 ( |||| 0 if x ≤ a, { FX (x) = | xb−−aa- if a <x ≤ b, |||( 1 otherwise.

19.3.5 指数分布

让我们关注一个不同的问题:灯泡。根据某些神秘(且可能完全不准确)的传说,灯泡具备所谓的无记忆性特性。也就是说,它们的期望寿命在任何时候都是一样的。

将这一内容转化为数学形式,设 X 为一个随机变量,表示给定灯泡的寿命。无记忆性特性表明,如果灯泡已经持续了 s 秒,那么其持续再 t 秒的概率与它刚开始时的寿命概率相同。即,

P (X >t + s | X >s) = P(X >t).

展开左侧,我们得到

 P (X >t+ s,X >s) P (X >t + s | X >s) =------------------- P(X >s) P-(X-->t+-s)- = P(X > s) ,

由于 {X/span>t + s}∩{X/span>s} = {X/span>t + s}。因此,无记忆性特性意味着

P(X > t + s) = P(X > t) P(X > s)。 (19.6)

如果我们把概率看作是一个函数 f(t) = P(X/span>t),那么(19.6)可以视为一个泛函方程。而且它是一个著名的方程。不深入讨论痛苦的细节,唯一的连续解就是指数函数 f(t) = e^(at),其中 a ∈ ℝ 是一个参数。

由于我们这里讨论的是灯泡的寿命,因此它永远持续的概率为零。也就是说,

lim P (X >t) = 0 t→ ∞

这个公式成立。因此,公式为:

 ( || ||{ 0 if a <0, lim eat = 1 if a = 0, t→ ∞ ||| |( ∞ if a >0,

在我们的案例中,只有负参数是有效的。这就表征了指数分布。一般来说,若 λ > 0,则 X ∼ exp(λ)。

 ( |{ 0 if x <0, FX (x) = |( 1− e−λx if x ≥ 0.

让我们绘制图形来进行可视化!

from scipy.stats import expon 
X = np.linspace(-0.5, 10, 100) 
params = [0.1, 1, 10] 
ys = [expon.cdf(X, scale=1/l) for l in params] 

with plt.style.context(’seaborn-v0_8’): 
    plt.figure(figsize=(10, 5)) 

    for l, y in zip(params, ys): 
        plt.plot(X, y, label=f/span>lambda = {l}" 

    plt.title("/span>The exponential distribution 
    plt.legend() 
    plt.show()

PIC

图 19.11:指数分布

指数分布在实际应用中极为有用且经常遇到。例如,它用于模拟服务器的请求、排队的顾客、公交车到站等情形。

我们将在后面的章节中讨论更多的特殊分布,并且会增加一些其他的分布。

19.3.6 正态分布

你可能在某个时刻见过钟形曲线,因为它被用来描述各种统计现象。薪资、价格、身高、智力:它们似乎都遵循相同的对称钟形分布。

这由著名的正态分布描述:我们说 X 服从正态分布,或 X ∼𝒩(μ,σ²),如果

 1 ∫ x (t−-μ)2 FX (x ) =--√--- e− 2σ2 dt, σ 2 π −∞

其中 μ,σ ∈ ℝ。参数μ被称为 X 的均值,而σ²是它的方差,σ是其标准差。(当我们讨论期望值和方差时,会看到这些量的更多内容,详见第二十章。)

让我们先看看图形的内部分布,你知道的,就是著名的钟形曲线:

from scipy.stats import norm
X = np.linspace(-10, 10, 1000)
sigmas = [0.5, 1, 2, 3]
ys = [norm.pdf(X, scale=sigma) for sigma in sigmas]

with plt.style.context('seaborn-v0_8'):
    plt.figure(figsize=(10, 5))

    for sigma, y in zip(sigmas, ys):
        plt.plot(X, y, label=f"sigma = {sigma}")

    plt.title("The bell curves")
    plt.savefig("bell_curve.png", dpi=300)
    plt.legend()
    plt.show()

PIC

图 19.12:钟形曲线

出乎意料的是,它的累积分布函数(CDF)没有闭式表达式。

 1 ∫ x − (t−-μ)2 FX (x ) =--√--- e 2σ2 dt. σ 2 π −∞

不,不是因为数学家们不够聪明无法搞明白;它证明是不存在的。在古代,统计学家们曾从巨大的统计表中查找它的值。

现在,让我们绘制 F[X]:

X = np.linspace(-10, 10, 1000) 
sigmas = [0.5, 1, 2, 3] 
ys = [norm.cdf(X, scale=sigma) for sigma in sigmas] 

with plt.style.context(’seaborn-v0_8’): 
    plt.figure(figsize=(10, 5)) 

    for sigma, y in zip(sigmas, ys): 
        plt.plot(X, y, label=f/span>sigma = {sigma}" 

    plt.title("/span>The normal distribution 
    plt.legend() 

    plt.show()

PIC

图 19.13:正态分布

正态分布是统计学中最重要的分布,我们将会看到它无处不在,不仅在实践中,而且在理论上也是如此。

总结一下,分布是概率论的命脉,分布可以通过累积分布函数来表示。

然而,累积分布函数(CDF)有一个显著的缺点:用它们很难表达更复杂事件的概率。稍后,我们将看到几个累积分布函数失效的具体例子。

不深入细节,一个例子指向多维分布。(我希望它们的存在和重要性不会让你感到惊讶。)在那里,分布函数可以用来表示矩形事件的概率,但不能表示例如球形事件的概率。

更准确地说,如果 X, Y ∼ Uniform(0,1),那么概率是:

P(X2 + Y 2 <1)

不能直接用二维累积分布函数 F[X,Y] (x,y)(无论那是什么)来表达。幸运的是,这并不是我们唯一的工具。回想一下正态分布的累积分布函数中 e^(−(x−2μσ2)2) 这一部分?这就是密度函数的一个特殊实例,我们将在下一节学习这个内容。

19.4 密度函数

分布函数并不是我们描述实值随机变量的唯一工具。如果你从一本/一堂由非数学家写的书籍/讲座/课程学习概率论,你可能见过类似这样的函数:

 √-1--− x22 p(x) = 2π e

在某些时候被称为“概率”。让我告诉你,这绝对不是一个概率。我曾见过这个错误很多次,以至于我决定写一系列简短的 X/Twitter 线程,来正确地解释概率概念,这本书正是由此而生。所以,我非常重视这个问题。

这里是累积分布函数的问题:它们表示关于局部对象的全局信息。让我们解析一下这个概念。如果 X 是一个实值随机变量,那么 CDF

FX (x) = P(X ≤ x)

描述了 X 小于给定 x 的概率。但如果我们关心的是 x 附近发生的情况呢?例如,在均匀分布的情况下(19.5),我们有:

P(X = x) = lim P (x− 𝜖 <X ≤ x) 𝜖→0 = lim (FX (x)− FX (x − 𝜖)) 𝜖→0 = lim 𝜖 𝜖→0 = 0.

(我们在求极限时使用了定理 115)。

因此,正如我们之前看到的,选择一个特定点的概率为零。与离散情况相反,P(X = x)不能告诉我们 X 在 x 附近的分布情况。

最糟糕的是,这在各种分布中都是相同的。例如,你可以手动检查指数分布。

这不是很奇怪吗?均匀分布和指数分布对于个别结果的概率都是零,但它们的分布却截然不同。我们换个角度来看这个问题。根据定义,

P(a <X ≤ b) = FX (b)− FX (a)

是成立的。这个式子对你来说是不是很熟悉?右边是 F[X] 的增量,左边是概率。我们在哪里见过增量?

在微积分的基本定理(定理 92)中,就是这么回事。也就是说,如果 F[X] 可导且其导数为 F[X]^′(x) = fX,那么

∫[a]^b fX dx = FX − FX。 (19.7)

函数 fX 似乎正是我们在寻找的:它表示 X 在 x 附近的局部行为。但它不是描述概率,而是描述其变化率。这被称为概率密度函数。

通过反转这个论证,我们可以使用 (19.7) 来定义密度函数。这里是数学上精确的版本。

定义 89. (密度函数)

设 (Ω,Σ,P) 是一个概率空间,X : Ω →ℝ 是一个实值随机变量。如果函数 f[X] : ℝ →ℝ 可积,则称 f[X] 是 X 的概率密度函数(PDF),并且

∫[a]^b fX dx = FX − FX (19.8)

对所有 a,b ∈ℝ 都成立。

再次提到,(19.8) 是牛顿-莱布尼茨公式(定理 92)的变体。

以下定理准确地阐明了这一联系。

定理 126. (作为导数的密度函数)

设 X 是一个实值随机变量。如果累积分布函数 FX 在所有地方可导,那么

fX(x) = -d-FX (x ) dx

是 X 的密度函数。

证明:这只是微积分基本定理(定理 92)的简单应用。如果导数确实存在,那么

∫ b-d- a dx FX (x )dx = FX (b)− FX (a),

这意味着 fX = d- dxFX 确实是一个密度函数。

注释 17. (密度函数并非唯一)

注意,密度函数并非唯一。如果 X 是一个具有密度 f[X] 的随机变量,那么,例如,在一个点上修改 f[X] 仍然可以作为 X 的密度函数。

更准确地说,定义

 ( |{ fX(x) if x ⁄= 0, f∗X(x) = |( fX(0)+ 1 if x = 0.

你可以手动检查,f[X]^∗ 仍然是 X 的密度,但 f[X] ≠ f[X]^∗。

在我们继续之前,再补充一点。回想一下,离散型随机变量是通过概率质量函数来表征的(定义 85)。质量函数和密度函数是同一个问题的两面。

概率质量函数类似于密度函数,但我们没有针对后者的随机变量的术语。我们现在来解决这个问题。

定义 90.(连续随机变量)

设(Ω,Σ,P)为概率空间,X : Ω → ℝ为实值随机变量。我们说 X 是连续的,如果它具有概率密度函数。

离散和连续随机变量是概率论的基础:最有趣的随机变量属于这两类中的任何一种。(在本章后面,我们会看到还有更多类型,但这两类是最重要的。)

现在我们准备动手,看看一些实际中的密度函数。

19.4.1 实际中的密度函数

经过所有这些介绍后,让我们看看几个具体的例子。到目前为止,我们已经看到两个实值非离散分布:均匀分布和指数分布。

示例 1. 我们从 X ∼ 均匀分布(0,1)开始。我们能直接应用定理 126 吗?没有一点问题的话不行。或者更精确地说,是两个问题。

为什么?因为分布函数

 (| ||| 0 if x ≤ 0, { FX(x) = | x if 0 <x ≤ 1, |||( 1 if x >1

在 x = 0 和 x = 1 处不可微分。然而,它在其他地方是可微的,并且其导数

 ( || 0 if x <0, ||{ F′X (x ) = 1 if 0 <x <1, ||| |( 0 if x >1

确实是一个密度函数。(你可以亲自验证这一点。)这个密度是由 FX 的导数在区间(−∞,0)、(0,1)和(1,∞)上拼接而成的。

PIC

图 19.14:均匀分布在[0,1]上的密度函数

示例 2. 在指数分布的随机变量 Y ∼ exp(λ)的情况下,函数

 ( |{0 if x <0, fY(x) = | (λe −λx if x ≥ 0

是一个合适的密度函数,我们通过对 F[Y] (x)进行可微分的操作获得。再次强调,密度 fX 是由在区间(−∞,0)和(0,∞)上的导数组合而成。

PIC

图 19.15:exp(1)分布的密度函数

示例 3. 现在,我将把一切颠倒过来。设 Z ∼ 伯努利(1∕2),这是一个离散随机变量,具有概率质量函数

pZ (0) = pZ(1) = 1, 2

和累积分布函数

 ( || 0 if x <0, ||{ FZ(x) = 1 if 0 ≤ x <1, ||| 2 |( 1 if x ≥ 1.

像均匀分布和指数分布一样,这个 CDF 也是可微的,除了少数几个点(即 0 和 1)。

因此,像以前一样,我们可以猜测,通过将其导数拼接在一起,可以得到一个密度函数。然而,存在一个更大的问题:F[Z]的导数为零,至少在它存在的地方是如此。事实证明,Z 根本没有密度函数!

有什么问题?我来告诉你:FZ 在 x = 0 和 x = 1 处的跳跃不连续性。尽管均匀分布和指数分布的累积分布函数在有限个点上不可导,但它们没有跳跃不连续性。

我们不打算深入细节,但要点是:如果累积分布函数中存在跳跃不连续性,则密度函数不存在。

注 18。(尽管没有跳跃不连续性,仍然不存在密度函数)

不幸的是,“累积分布函数中的跳跃不连续性 ⇒ 不存在概率密度函数”这一反向命题并不成立,我重复一遍,不成立

我们可以找到累积分布函数是连续的随机变量,但其密度不存在。一个著名的例子是康托尔函数(en.wikipedia.org/wiki/Cantor_function),也被称为“魔鬼楼梯”。(只有当你足够勇敢或在实分析方面经过良好训练时,才可以点击此链接,这二者是一样的。)

19.4.2 实值随机变量的分类

到目前为止,我们一直在关注两种特殊类型的实值随机变量:离散型随机变量(定义 80)和连续型随机变量(定义 90)。

我们已经看到了描述它们的各种对象。每个实值随机变量都有一个累积分布函数(定义 88),但离散型随机变量通过概率质量函数(定义 85)来表征,而连续型随机变量则通过密度函数(定义 89)来表征。

这两个就是全部了吗?

不。还有混合型情况。例如,考虑以下例子。我们从[0,1]区间选择一个随机数,但我们对选择过程做了一点小改动。首先,我们抛一个公平的硬币,如果是正面朝上,我们选择 0;否则,我们在 0 到 1 之间均匀地选择一个数。

为了描述这个奇怪的过程,让我们引入两个随机变量:令 X 为最终结果,Y 为抛硬币的结果。然后,使用全概率法则的条件版本(参见定理 117),我们得到

P (X ≤ x) = P (X ≤ x | Y = heads )P (Y = heads) + P (X ≤ x | Y = tails)P (Y = tails).

 ( |{ P (X ≤ x | Y = heads) = 0 if x <0, |( 1 if x ≥ 1,

和 P(X ≤x∣Y = tails) = FUniform(0,1),最终我们得到了

 (| ||| 0 if x <0, { x+1 FX (x) = | -2- if 0 ≤ x <1, |||( 1 if x ≥ 1.

最终,F[X]是两个累积分布函数的凸组合。(凸组合是系数为正且总和为 1 的线性组合。)

PIC

图 19.16:混合分布 X 的累积分布函数(CDF)

因此,随机变量 X 既不是离散型的,也不是连续型的。那么,它是什么呢?

是时候为混乱添加秩序了!在这一节中,我们将为我们的实值随机变量提供完整的分类。这是一个美丽的、虽然高级的话题,所以第一次阅读时可以跳过它。

让我们从一个看似遥远的话题开始:ℝ 的子集,它们小到几乎消失。由于 ℝ 是一维对象,我们通常在这里讨论的是长度,但让我们忘记那个术语,转而讨论测度。我们将用 λ(A) 来表示一个集合 A ⊆ ℝ 的测度,不管它是什么。

我们不会深入细节,并将继续直观地使用测度的概念。例如,区间 [a, b] 的测度是 λ([a, b]) = b - a。

我们的测度 λ 具有一些基本属性,例如,

  1. λ(∅) = 0,

  2. 如果 A ⊆ B,则 λ(A) ≤ λ(B)。

  3. 并且 λ(∪[k=1]^∞A[k]) = ∑ [k=1]^∞λ(A[k]),如果 A[i] ∩ A[j] = ∅。

这几乎表现得像一个概率度量,唯一显著的例外是:λ(ℝ) = ∞。这并不是偶然的。

一个有限集合 {a[1], …, a[n]} 的测度是多少?直观上,它是零,借此示例,我们将引出零测度集合的概念。

定理 127. (零测度集合)

设 A ⊆ ℝ 是任意集合。假设对于任何任意小的 𝜀/span>0,存在一个区间的并 E = ∪[k=1]^∞(a[i], b[i]),使得

(a) λ(E)/span>𝜀, (b) 且 A ⊆ E,

那么,λ(A) = 0。

证明。由于 A ⊆ E,因此 λ(A) ≤ λ(E) 𝜀。这意味着 λ(A) 小于任何正实数,因此它必须为零。

让我们看一些例子。

示例 1. 单元素集合的测度为零。由于任何 {a} 都可以被区间 (a−𝜀, a+𝜀) 覆盖,其中 𝜀/span>0。由于 λ((a−𝜀, a+𝜀)) = 2𝜀,定理 127 的条件适用,因此 λ({a}) = 0。

示例 2. 有限集合的测度为零。为了证明这一点,设 A = {a[1], …, a[n]} 是我们的有限集合。区间系统

 n ( ) E = ⋃ a − -𝜀-,a + -𝜀- , 𝜀 >0 k 2n k 2n k=1

这样做是可行的,因为对于足够小的 𝜀,区间是互不重叠的。

因此

 ∑n ( ( 𝜀 𝜀 )) λ(E) = λ ak − ---,ak +--- k=1 2n 2n ∑n 𝜀 = -- k=1n = 𝜀.

示例 3. 可数集合的测度为零。对于任意的 A = {a[1], a[2], …},区间系统

 ∞⋃ ( 𝜀 𝜀 ) E = ak − -k+1-,ak +-k+1- , 𝜀 >0 k=1 2 2

完全可以工作,因为

 ∞∑ -𝜀- λ (E ) ≤ 2k = 𝜀. k=1

例如,由于整数集合和有理数集合都是可数的,λ(ℤ) = λ(ℚ) = 0。

总的来说,零测度的集合名副其实:它们很小。(不过,它们不一定是可数的。)为什么这些重要?我们将在下一节看到。

备注 19. (密度函数不是唯一的,取两个)

你还记得备注 17 吗?我们曾看到,在 X 的单个点改变密度函数也是 X 的密度函数。

结果发现,实际上你可以在整个零测度集合上修改 f[X]。假设,

 ( | ∗ { fX (x) if x∈∕ℚ, fX (x) = |( 0 if x ∈ ℚ

对 X 来说,它仍然是一个密度函数。不幸的是,我们没有足够的工具来证明这一点,因为这需要超越经典的 Riemann 积分,这超出了我们的讨论范围。

离散随机变量与连续随机变量之间的主要区别在于它们所处的集合。从根本上说,它们都是实值随机变量,但离散变量的范围是一个零测度的集合。

让我们引入奇异随机变量的概念,以使这个概念更加精确。

定义 91. (奇异随机变量)

设(Ω, Σ, P)为概率空间,X : Ω →ℝ 为实值随机变量。我们称 X 为奇异的,如果它的范围 X(Ω) = {X(ω) : ω ∈ Ω}⊆ℝ 是一个零测度的集合,即,

 ( ) λ X (Ω ) = 0

成立。

所有离散随机变量都是奇异的,但反之则不然。例如,Cantor 函数(en.wikipedia.org/wiki/Cantor_function)就是一个很好的例子。

为什么奇异随机变量如此特别?因为每个分布都可以写成奇异随机变量和连续随机变量的和!这就是著名的 Lebesgue 分解定理。

定理 128. (Lebesgue 分解定理)

设(Ω, Σ, P)为概率空间,X : Ω →ℝ 为实值随机变量。那么,存在一个奇异随机变量 X[s] 和一个连续随机变量 X[c],使得

FX = αFXs + βFXc,

其中 α + β = 1,F[X]、F[X[s]]、F[X[c]]分别是对应的累积分布函数。

我们在这里不会证明这个结果,但其要点是:有奇异随机变量、连续随机变量及其和。

19.5 总结

随着随机变量的引入,我们学会了将抽象的概率空间表示为随机变量,将一个足够表达的事件集合映射到实数上。现在我们可以处理数字,而不再是 σ-代数和概率度量。正如我之前告诉你的,“概率的力量在于它能够将现实世界的现象转化为掷硬币、掷骰子、投掷飞镖、灯泡寿命等很多事件。”

最常见的随机变量有两种形式:离散型或连续型,这意味着它要么可以用概率质量函数描述

{ } ∞ P (X = xk ) k=1,

或者使用密度函数 f[X],满足

 ∫ b P(a ≤ X ≤ b) = a fX (x)dx.

将实验转化为分布是概率论和统计学的核心秘密。例如,呼叫中心通话之间的时间、公交到达、地震和保险索赔等都可以用指数分布建模,这是我们可以操作的数学对象。

我知道学习是一个终身的过程,但我们必须在某个时候结束这本书。还有一个概念我想告诉你:期望值,它让我们能够衡量分布的统计特性。下章见!

19.6 问题

问题 1. 设 X 和 Y 为两个独立的随机变量,a, b ∈ℝ 为两个任意常数。证明 X −a 和 Y −b 也是彼此独立的。

问题 2. 设 X 为连续随机变量。证明对于任何 x ∈ℝ,P(X = x) = 0。

问题 3. 设 X ∼ 伯努利(p) 和 Y ∼ 二项分布(n,p)。计算 X + Y 的概率分布。

问题 4. 设 X ∼ 伯努利(p) 为一次抛硬币的结果。我们根据抛硬币的结果从 [0,2] 中选择一个随机数 Y:如果 X = 0,我们从 [0,1] 中均匀分布地选取一个数;如果 X = 1,我们从 [1,2] 中选取一个数,同样采用均匀分布。求 Y 的累积分布函数。Y 是否有密度函数?如果有,求出该函数。

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者一起阅读本书。提出问题、为其他读者提供解决方案、通过“问我任何问题”环节与作者交流,等等。扫描二维码或访问链接加入社区。packt.link/math

图片

期望值

在上一章,我们学习了概率分布,即表示概率模型的对象,作为序列或函数。毕竟,整个微积分领域可以帮助我们处理函数,因此它们为我们提供了广泛的数学工具。

然而,我们可能并不需要所有可用的信息。有时,简单的描述性统计,如均值、方差或中位数就足够了。即便在机器学习中,损失函数也以这些量为基础。例如,著名的均方误差

 n MSE (x,y ) =-1∑ (f(x )− y )2, x,y ∈ ℝn n i i i=1

是预测误差的方差。深层次上,这些熟悉的量都根植于概率论中,我们将在本章中专门学习它们。

第二十五章:20.1 离散随机变量

让我们玩一个简单的游戏。我投掷一枚硬币,如果是正面,你赢得$1。如果是反面,你输掉$2。

到目前为止,我们一直在处理像是获胜的概率等问题。比如,投掷硬币时,无论你赢还是输,我们有

P(正面) = P(反面) = 1. 2

尽管赢和输的机会相等,你是否应该玩这个游戏呢?让我们一探究竟。

在 n 轮之后,你的收益可以通过正面次数乘以$1,减去反面次数乘以$2 来计算。如果我们将总收益除以 n,就得到每轮的平均奖金。也就是说,

 总奖金- 你的平均奖金 = n 1⋅#正面 − 2⋅#反面 = -------------------- n = 1⋅ #正面 − 2⋅ #反面, n n

其中,#正面和#反面分别表示正面和反面的次数。

还记得来自第 18.2.7 节的频率学派对概率的解释吗?根据我们的直觉,我们应该有

 lim #正面- = P(正面) = 1, n→ ∞ n 2 #反面- 1- lni→m∞ n = P(反面) = 2.

这意味着,如果你玩得足够长,你每轮的平均奖金是

你的平均奖金 = 1⋅P(正面)− 2⋅P(反面) = − 1. 2

因此,既然你每轮平均亏损半美元,那么你肯定不应该玩这个游戏。

让我们通过随机变量来正式化这个论点。假设,如果 X 表示你每轮的奖金,我们有

 1 P(X = 1) = P (X = − 2) =-, 2

所以平均奖金可以表示为

X 的平均值 = 1 ⋅P(X = 1) − 2 ⋅P(X = − 2) 1 = − -. 2

通过一点模式匹配,我们发现对于一般的离散随机变量 X,公式看起来像

 ∑ X 的平均值 = (值)⋅P(X = 值)。值

从中,期望值的定义应运而生。

定义 92.(离散随机变量的期望值)

设(Ω, Σ, P)为一个概率空间,X : Ω →{x[1], x[2], …}为离散随机变量。X 的期望值定义为

𝔼 [X ] := ∑ x P (X = x ). k k k

(注意,如果 X 取有限多个值,则和中只包含有限个项。)

在英语中,期望值描述的是随机变量在长期运行中的平均值。期望值也叫做均值,通常用μ表示。我们常常不使用随机变量,而是通过代入分布来使用期望值符号,比如𝔼[Bernoulli(p)]。虽然从数学上看这不够精确,但 1)在某些情况下,它更简单,2)而且期望值反正只依赖于分布。

是时候看一些例子了。

示例 1. 伯努利分布的期望值。(请参见 19.2.1 节中的伯努利分布定义。)设 X ∼ Bernoulli(p)。它的期望值很容易计算,计算如下:

𝔼[X] = 0⋅P (X = 0 )+ 1⋅P (X = 1) = = 0⋅(1 − p)+ 1 ⋅p = p.

我们之前见过这个:简单游戏的入门例子实际上是变换后的伯努利分布 3 ⋅ 伯努利(1∕2) − 2。

示例 2. 二项分布的期望值。(请参见 19.2.2 节中的二项分布定义。)设 X ∼ Binomial(n,p)。那么

 ∑n 𝔼[X] = kP (X = k ) k=0 ∑n (n ) = k pk(1 − p)n−k k=0 k ∑n = k----n!--- pk(1 − p)n−k. k=0 k!(n − k)!

计划如下:吸收那个 k 与分数--n!--- k!(n−k)!,并调整和,使其项形成二项分布(Binomial(n − 1,p))的概率质量函数。由于 n −k = (n − 1) − (k − 1),我们得到

 ∑n 𝔼[X ] = k ---n!----pk(1− p)n−k k=0 k!(n − k)! ∑n = np ---------(n-−-1)!--------pk−1(1 − p)(n−1)−(k−1) k=1(k − 1)!((n − 1)− (k − 1))! n−1 = np ∑ ---(n-−-1)!--pk(1− p)(n−1−k) k!(n − 1− k)! k=0 n∑−1 = np P (Binomial(n− 1,p) = k) k=0 = np.

这个计算看起来可能不算最简单,但一旦你熟悉了这个技巧,它将变得像第二天性一样。

示例 3. 几何分布的期望值。(请参见 19.2.3 节中的几何分布定义。)设 X ∼ Geo(p)。我们需要计算

 ∑∞ 𝔼[X ] = k (1 − p)k−1p. k=1

你还记得几何级数吗?(19.2) 这几乎就是它,除了那个 k 项,它让我们的计算变得复杂。为了解决这个问题,我们将使用另一个魔法技巧。回忆一下,

 1 ∑∞ ----- = xk. 1 − x k=0

现在,我们将对几何级数进行微分,从而得到

 d 1 d ∑∞ k dx-1-−-x = dx- x k=0 ∑∞ d k = dx-x k=0 ∑∞ k− 1 = kx , k=1

我们在这里使用了导数的线性性质和几何级数的良好解析性质。数学家们看到交换导数和无穷和时会尖叫,但别担心,这里的一切都是正确的。(数学家们真的害怕交换极限。你得知道,他们有理由这么做!)

另一方面,

d---1--- ---1---- dx 1− x = (1 − x)2,

因此

 ∞ ∑ k−1 ---1---- kx = (1− x )2. k=1

综合以上所有内容,我们最终得出

 ∑∞ 𝔼[X ] = k (1 − p)k−1p k=1 ∞∑ = p k(1− p)k−1 k=1 1 1 = p-2 = -. p p

示例 4. 常数随机变量的期望值。令 c ∈ℝ为任意常数,X 为一个在所有情况下都取值为 c 的随机变量。由于 X 是离散随机变量,它的期望值就是

𝔼[X ] = c⋅P (X = c) = c.

我知道,这个例子看起来很傻,但它实际上可以非常有用。当情况清晰时,我们通过将常数 c 表示为随机变量本身来滥用符号。

20.1.1 扑克中的期望值

在继续之前再举一个例子。曾经,我是一个中等水平的无限注德州扑克玩家,而我第一次听说期望值是在学习概率论之前的几年。

根据德州扑克的规则,每个玩家自己持有两张牌,同时发五张公共牌。这些公共牌对每个玩家都可用,持有最强牌的玩家获胜。

图 20.1 展示了最后一张牌(河牌)揭示之前桌面的样子。

PIC

图 20.1:河牌之前的扑克桌

底池里有钱可以赢,但要看到河牌,你必须跟注对手的下注。问题是,你应该这么做吗?期望值来帮忙。

让我们构建一个概率模型。某些河牌(river card)能让我们赢得底池,而其他所有河牌则让我们失去。若 X 代表我们的获胜金额,则

 #winning cards P(X = pot) = ---------------, #remaining cards P (X = − bet) = --#losing-cards-. #remaining cards

因此,期望值是

𝔼[X] = pot⋅P (X = pot )− bet⋅P (X = − bet) #winning cards #losing cards = pot⋅ ----------------− bet⋅----------------. #remaining cards #remaining cards

期望值什么时候为正?通过一些代数运算,我们得到𝔼[X]/span>0 当且仅当

#winning-cards bet- #losing cards > pot,

这被称为正底池赔率。如果满足这一条件,下注是正确的选择。尽管在单手牌中你可能会输,但从长远来看,你的收益将是正的。

当然,底池赔率在实践中非常难以确定。例如,你不知道别人持有什么牌,除非你对对手有很好的阅读能力,否则无法计算出哪些牌会为你赢得底池。扑克远不止是数学。优秀的玩家会特别选择下注方式,来让对手的底池赔率产生误导。

现在我们理解了期望值的基本概念,让我们继续探讨一般情况!

20.2 连续随机变量

到目前为止,我们只为离散随机变量定义了期望值。由于𝔼[X]描述的是 X 在长时间内的平均值,因此它对于连续随机变量也应该是存在的。

期望值的解释很简单:结果乘以概率,对所有可能的值求和。然而,连续随机变量有一个问题:我们没有这样的质量分布,因为单个结果的概率为零:P(X = x) = 0。此外,我们无法对无穷多个值求和。

我们能做什么呢?

一厢情愿的想法。这是数学中最强大的技巧之一,我不是在开玩笑。

计划是这样的。我们假设连续随机变量的期望值是定义良好的,让我们的想象力自由发挥。告别数学精确性,让我们的直觉展开。与其谈论某一结果的概率,我们可以讨论 X 落在一个小区间内。首先,我们将实数集划分为非常小的部分。更精确地说,设 x[0]/span>x[1]/span>…/span>x[n] 是实数线的精细划分。如果划分足够精细,我们应该得到:

𝔼[X] ≈ ∑[k=1]^n x[k] P(x[k−1] ≤ X ≤ x[k]) (20.1)

在 (20.1) 中的概率可以用 CDF 表示:

 n n ∑ x P(x <X ≤ X ) = ∑ x (F (x ) − F (x )). k k−1 k k X k X k−1 k=1 k=1

这些增量让我们想起了差分商。虽然在求和中我们没有完全得到这些,但通过“巧妙的乘以 1”,我们可以实现这一点:

∑n ∑n xk(FX (xk)− FX (xk−1)) = xk(xk − xk−1)FX-(xk)−-FX-(xk−1). k=1 k=1 xk − xk− 1

如果 x[i] 彼此接近(而且我们可以选择它们任意接近),那么差分商接近于 F[X] 的导数,即密度函数 f[X]。因此,

FX (xk)− FX (xk−1) ∑n FX (xk)− FX (xk−1) ∑n -----x-−-x-------- ≈ fX (xk ) xk(xk − xk−1)-----x-−-x-------- ≈ xk(xk − xk −1)fX(xk). k k− 1 k=1 k k− 1 k=1

这是一个黎曼和,定义见于 (14.7)!因此,最后的和接近于黎曼积分:

 n ∫ ∑ ∞ xk(xk − xk−1)fX(xk) ≈ −∞ xfX (x)dx. k=1

尽管我们在推理中并没有完全精确,但以上所有内容都可以在数学上得到正确处理。(不过我们这里不做,因为它与我们无关。)因此,我们最终得到了连续随机变量期望值的公式。

定义 93. (连续随机变量的期望值)

设 (Ω,Σ,P) 是一个概率空间,X : Ω → ℝ 是一个连续随机变量。X 的期望值由以下公式定义:

 ∫ ∞ 𝔼 [X ] := xfX(x)dx. −∞

和往常一样,让我们先看一些例子。

示例 1. 均匀分布的期望值。(参见 19.3.4 节中对均匀分布的定义。)设 X ∼ Uniform(a,b)。则

 ∫ ∞ 1 𝔼[X ] = x-----dx − ∞ b∫− a --1-- b = b − a xdx [ a ]x=b = ---1---x2 2(b − a) x=a a + b = --2--,

这表示区间 [a,b] 的中点,其中 Uniform(a,b) 分布存在。

示例 2. 指数分布的期望值。(参见第 19.3.5 节中关于指数分布的定义。)设 X ∼ exp(λ)。然后,我们需要计算积分

 ∫ ∞ − λx 𝔼[X ] = xλe dx. 0

我们可以通过分部积分法来解决这个问题(定理 95):设 f(x) = x 且 g^′(x) = λe^(−λx),我们得到

 ∫ ∞ 𝔼 [X ] = xλe−λxdx 0 [ ]x=∞ ∫ ∞ = − xe−λx x=0 + e− λxdx ◟-----◝=◜0-----◞ 0 [ ]x=∞ = − 1-e−λx λ x=0 1- = λ.

20.3 期望值的性质

和往常一样,期望值具有几个有用的性质。最重要的是,期望值对随机变量是线性的。

定理 129. (期望值的线性)

设 (Ω,Σ,P) 为一个概率空间,且让 X, Y : Ω → ℝ 为两个随机变量。进一步设 a, b ∈ ℝ 为两个标量。那么

𝔼[aX + bY ] = a𝔼 [X ]+ b𝔼 [Y ]

公式成立。

我们在这里不会证明这个定理,但要知道线性是一个重要的工具。你还记得我们用来引入离散随机变量期望值的游戏吗?我掷硬币,如果正面朝上,你赢得 $1;反面朝上,你输掉 $2。如果你思考一下,这就是

X = 3⋅Bernoulli(1∕2)− 2

分布,因此,

𝔼[X] = 𝔼[3⋅Bernoulli(1 ∕2)− 2] = 3⋅𝔼 [Bernoulli(1 ∕2)]− 2 = 3⋅ 1− 2 2 1- = − 2.

当然,线性远不止这个简单的例子。正如你已经习惯了的那样,线性是数学中的一个关键性质。我们喜欢线性。

备注 20.

请注意,定理 129 并未说明 X 和 Y 必须都是离散的或都是连续的。尽管我们只在这种情况下定义了期望值,但存在一个适用于所有随机变量的通用定义。

问题在于,它需要熟悉测度理论,而这超出了我们的范围。只需知道,这个定理按原样成立。

如果和的期望值是各自期望值的和,那么乘积是否也适用?一般来说不适用,但幸运的是,这在独立随机变量的情况下成立。(参见定理 84,其中给出了独立随机变量的定义。)

定理 130. (独立随机变量乘积的期望值)

设 (Ω,Σ,P) 为一个概率空间,且让 X, Y : Ω → ℝ 为两个独立的随机变量。

然后

𝔼 [XY ] = 𝔼 [X ]𝔼[Y]

公式成立。

这个性质非常有用,正如我们将在下一节中看到的,我们将讨论方差和协方差。

另一个帮助我们计算随机变量函数期望值的性质,比如 X² 或 sinX:

定理 131. (无意识统计学家的法则)

设 (Ω,Σ,P) 为一个概率空间,设 X : Ω → ℝ 为一个随机变量,且设 g : ℝ → ℝ 为一个任意函数。

(a) 如果 X 是离散的,且可能的取值为 x[1], x[2], …,那么

 ∑ 𝔼[g(X)] = g(xn)P (X = xn). n

(b) 如果 X 是具有概率密度函数 fX 的连续型随机变量,则

 ∫ ∞ 𝔼[g(X )] = g(x)f (x)dx. −∞ X

因此,计算连续随机变量的𝔼[X²]可以通过简单地取

 ∫ ∞ 𝔼[X²] = x²fX (x)dx, −∞

这将在整个过程中被频繁使用。

20.4 方差

简单来说,期望值衡量的是随机变量的平均值。然而,即使 Uniform(−1,1)和 Uniform(−100,100)的期望值都是零,后者的分布却比前者要广泛得多。因此,𝔼[X]并不是描述随机变量 X 的好方法。

为了再加一层,我们衡量的是期望值的平均偏差。这是通过方差和标准差来实现的。

定义 94. (方差与标准差)

设(Ω, Σ, P)为一个概率空间,设 X : Ω → ℝ为一个随机变量,且μ = 𝔼[X]为其期望值。X 的方差定义为

 [ 2] 方差 [X ] := 𝔼 (X − μ ) ,

其标准差定义为

Std[X] := ∘方差--[X-].

请注意,在文献中,期望值通常用μ表示,而标准差用σ表示。二者一起构成了描述随机变量的两个最重要的指标。

图 20.2 展示了在正态分布情况下,均值和标准差的可视化解释。均值表示平均值,而标准差可以解释为平均偏离均值的程度。(我们稍后会详细讨论正态分布,所以如果现在还不熟悉,也不用担心。)

图片

图 20.2:标准正态分布的均值(μ)和标准差 !(σ) ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file1935.png)

计算方差的常用方法不是取(X − μ)²的期望值,而是取 X²的期望值并从中减去μ²。以下命题展示了这一点。

命题 5.

设(Ω, Σ, P)为一个概率空间,且 X : Ω → ℝ为一个随机变量。

然后

方差[X] = 𝔼[X² ]− 𝔼[X ]².

证明。设μ = 𝔼[X]。由于期望值的线性性质,我们有

方差[X ] = 𝔼[(X − μ)²] = 𝔼[X² − 2μX + μ²] = 𝔼[X ]² − 2μ𝔼 [X ]+ μ² 2 2 2 = 𝔼[X ] − 2μ + μ = 𝔼[X² ]− μ² = 𝔼[X² ]− 𝔼[X ]²,

这就是我们需要证明的。

方差也具有线性性质吗?不,但关于标量乘法和加法,有一些重要的恒等式。

定理 132. (方差与线性运算)

设(Ω, Σ, P)为一个概率空间,且 X : Ω → ℝ为一个随机变量。

(a) 设 a ∈ ℝ为任意常数。那么

 2 方差[aX ] = a 方差[X ].

(b) 设 Y : Ω → ℝ为一个与 X 独立的随机变量。那么

方差[X + Y ] = 方差[X ]+ 方差[Y].

证明。(a) 设μ[X] = 𝔼[X]。则我们有

 [ ] [ ] Var[aX ] = 𝔼 (aX − a μX)2 = 𝔼 a2(X − μX )2 [ ] = a2𝔼 (X − μX )2 = a2Var[X ],

这就是我们需要展示的结果。(b)令μ[Y] = 𝔼[Y]。然后,由于期望值的线性性质,我们有:

 [ 2] Var[X + Y ] = 𝔼 (X + Y − (μX + μY )) [ 2] = 𝔼 ((X − μX ) + (Y − μY)) = 𝔼 [(X − μX)2]+ 2𝔼[(X − μX )(Y − μY)] + 𝔼[(Y − μY )2].

现在,由于 X 和 Y 是独立的,𝔼[XY ] = 𝔼[X]𝔼[Y]。因此,由于期望值的线性性质,

 [ ] [ ] 𝔼 (X − μX )(Y − μY) = 𝔼 XY − X μY − μX Y + μX μY = 𝔼[XY ] − 𝔼[X μ ]− 𝔼 [μ Y ]+ μ μ [ ] [ ] [Y ] X [ ] X Y = 𝔼 X 𝔼 Y − 𝔼 X μY − μX 𝔼 Y + μX μY = μXμY − μXμY − μXμY + μXμY = 0.

因此,继续进行第一次计算,

Var[X + Y ] = 𝔼 [(X − μ )2]+ 2𝔼[(X − μ )(Y − μ )] + 𝔼[(Y − μ )2] X X Y Y = 𝔼 [(X − μX)2]+ 𝔼[(Y − μY )2],

这就是我们需要展示的结果。

20.4.1 协方差和相关性

期望值和方差度量了一个随机变量的独立性。然而,在实际问题中,我们需要发现不同测量之间的关系。假设,X 表示给定房地产的价格,而 Y 表示其面积。这两者肯定是相关的,但并不是互相决定的。例如,位置可能是价格差异的因素。

测量相似性的最简单统计方法是协方差和相关性。

定义 95.(协方差与相关性)

设(Ω,Σ,P)为概率空间,设 X,Y : Ω → ℝ为两个随机变量,且μ[X] = 𝔼[X],μ[Y] = 𝔼[Y]为它们的期望值,σ[X] = Std[X],σ[Y] = Std[Y]为它们的标准差。

(a) X 和 Y 的协方差由下式定义:

 [ ] Cov [X,Y ] := 𝔼 (X − μX )(Y − μY ).

(b) X 和 Y 的相关性由下式定义:

 Cov[X, Y] Corr [X, Y ] := ---------. σXσY

类似于方差,协方差的定义可以简化,从而提供一种更简便的计算其确切值的方法。

命题 6.

设(Ω,Σ,P)为概率空间,设 X,Y : Ω → ℝ为两个随机变量,且μ[X] = 𝔼[X],μ[Y] = 𝔼[Y]为它们的期望值。

然后

Cov[X, Y] = 𝔼[XY ]− μX μY .

证明:这只是一个简单的计算。根据定义,我们有:

 [ ] Cov[X, Y] = 𝔼 (X − μX )(Y − μY) [ ] = 𝔼 XY − X μY − μX Y + μX μY = 𝔼[XY ] − 𝔼[X μ ]− 𝔼[μ Y ]+ μ μ [ ] [ ]Y X[ ] X Y = 𝔼 XY − 𝔼 X μY − μX 𝔼 Y + μX μY [ ] = 𝔼 XY − μX μY − μX μY + μX μY = 𝔼[XY ]− μX μY ,

这就是我们需要展示的结果。

协方差和相关性的一个最重要的性质是,对于独立的随机变量,它们的值为零。

定理 133.

设(Ω,Σ,P)为概率空间,且 X,Y : Ω → ℝ为两个独立的随机变量。

然后,Cov[X,Y ] = 0. (因此,Corr[X,Y ] = 0 也是如此。)

证明直接来自定义和定理 130,所以这部分留给你做练习。

请注意,这一点非常重要:独立性意味着零协方差,但零协方差并不意味着独立性。这里有一个例子。

设 X 是一个离散随机变量,其概率质量函数为:

 1 P(X = − 1) = P (X = 0) = P(X = 1) = 3,

并且设 Y = X²。

X 的期望值是

𝔼 [X ] = (− 1)⋅P (X = − 1)+ 0 ⋅P (X = 0)+ 1 ⋅P (X = 1) = − 1-+ 0 + 1- 3 3 = 0,

而无意识统计学家的法则(定理 131)表明:

 2 𝔼[Y ] = 𝔼[X ] = 1 ⋅P(X = − 1) + 0⋅P (X = 0) + 1⋅P (X = 1 ) 1- 1- = 3 + 0+ 3 2 = -, 3

𝔼[XY ] = 𝔼[X3] = 0.

因此,

Cov [X,Y ] = 𝔼 [XY ]− 𝔼[X ]𝔼 [Y ] 3 2 = 𝔼 [X ]− 𝔼 [X ]𝔼[X ] 2- = 0 − 0⋅ 3 = 0.

然而,X 和 Y 不是独立的,因为 Y = X² 是 X 的一个函数。(我毫不羞愧地借用了这个例子,来自一个精彩的 Stack Overflow 讨论,你应该阅读更多关于这个问题的内容:stats.stackexchange.com/questions/179511/why-zero-correlation-does-not-necessarily-imply-independence

你是否还记得我们如何将概率的概念解释为事件发生的相对频率?现在,我们已经掌握了期望值,我们终于可以精确地描述这个概念了。让我们来看看著名的大数法则!

20.5 大数法则

我们将继续前进,探索一个相当显著且著名的结果:大数法则。你可能已经听过一些错误的关于大数法则的论点。例如,赌徒们常常相信他们的不幸很快就会结束,因为大数法则。这是最常被误用的数学术语之一,我们在这里将澄清这一点。

我们将分两步进行。首先,我们将看到一个直观的解释,然后添加技术性但重要的数学细节。我会尽量温和一些。

20.5.1 投掷硬币……

首先,我们再投几次硬币。如果我们重复投掷硬币,长期来看,正面朝上的相对频率是多少?

我们应该已经有了一个相当不错的猜测:正面朝上的平均次数也应该趋向于 P(正面) = p。为什么?因为我们在第 18.2.7 节研究概率的频率解释时看到过这个现象。

我们的模拟显示,正面朝上的相对频率确实趋向于真实的概率。这一次,我们将进一步进行模拟。

首先,为了构造问题,我们引入独立随机变量 X[1], X[2], …,它们的分布是伯努利(p),其中 X[i] = 0 表示投掷结果为反面,而 X[i] = 1 表示投掷结果为正面。我们关注的是

-- X1-+-⋅⋅⋅+-Xn- Xn = n .

X[n] 被称为样本平均值。我们已经看到,随着 n 的增大,样本平均值越来越接近 p。让我们在继续之前再看一次模拟结果。(为了方便示例,参数 p 选择为 1∕2。)

import numpy as np 
from scipy.stats import bernoulli 

n_tosses = 1000 
idx = range(n_tosses) 

coin_tosses = [bernoulli.rvs(p=0.5) for _ in idx] 
coin_toss_averages = [np.mean(coin_tosses[:k+1]) for k in idx]

这是图表。

import matplotlib.pyplot as plt 

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(10, 5)) 
    plt.title("/span>Relative frequency of the coin tosses 
    plt.xlabel("/span>Relative frequency 
    plt.ylabel("/span>Number of tosses 

    # plotting the averages 
    plt.plot(range(n_tosses), coin_toss_averages, linewidth=3) # the averages 

    # plotting the true expected value 
    plt.plot([-100, n_tosses+100], [0.5, 0.5], c="/span>k 
    plt.xlim(-10, n_tosses+10) 
    plt.ylim(0, 1) 
    plt.show()

图片

图 20.3:掷硬币的相对频率

到目前为止没有什么新内容。然而,如果你眼尖的话,可能会问:这只是偶然吗?毕竟,我们研究的是平均值。

-- X1-+-⋅⋅⋅+-Xn- Xn = n ,

这几乎是一个二项分布的随机变量!更准确地说,如果 X[i] ∼ 伯努利分布(p),则

-- Xn ∼ 1Binomial(n,p). n

(我们在讨论离散随机变量的和时已经看到过这个内容,在第 19.2.7 节中。)

到目前为止,无法保证这个分布会集中在单一的值附近。因此,让我们做更多的模拟。这次,我们将掷硬币一千次,看看样本平均值的分布情况。确实很有 meta 的感觉,我知道。

more_coin_tosses = bernoulli.rvs(p=0.5, size=(n_tosses, n_tosses)) 
more_coin_toss_averages = np.array([[np.mean(more_coin_tosses[i][:j+1]) for j in idx] 
                                     for i in idx])

我们可以在直方图上可视化这些分布。

with plt.style.context("/span>seaborn-v0_8": 
    fig, axs = plt.subplots(1, 3, figsize=(12, 4), sharey=False) 
    fig.suptitle("/span>The distribution of sample averages 
    for ax, i in zip(axs, [5, 100, 999]): 
        x = [k/i for k in range(i+1)] 
        y = more_coin_toss_averages[:, i] 
        ax.hist(y, bins=x) 
        ax.set_title(f/span>n = {i}" 

    plt.show()

图片

图 20.4:掷硬币的样本平均值分布

换句话说,X[n]远离 p 的概率会变得越来越小。对于任何小的𝜖,我们可以将“X[n]远离 p 超过𝜖”的概率表示为 P(|X[n] −p| > 𝜖)。

因此,从数学角度来看,我们的猜测是

 -- nl→im∞ P(|Xn − p| >𝜖) = 0.

再次,这只是偶然吗?我们是否仅仅幸运地研究了一个符合这个规律的实验?对于除伯努利随机变量外的其他随机变量是否也成立?样本平均值最终会收敛到什么地方?(如果它们确实会收敛的话。)

我们会找到答案的。

20.5.2 …掷骰子…

让我们来玩掷骰子。为了简化问题,我们关注的是长期掷骰子的平均值。为了建立一个合适的概率模型,我们来引入随机变量!

单次掷骰子是均匀分布在{1,2,…,6}上的,并且每次掷骰子相互独立。因此,设 X[1], X[2], … 为独立随机变量,每个随机变量都按照均匀分布 Uniform({1,2,…,6})分布。

样本平均值 X[n] 会怎么变化?是时候进行模拟了。我们将随机生成 1000 次掷骰子的结果,然后探索 X[n] 的变化情况。

from scipy.stats import randint 

n_rolls = 1000 
idx = range(n_rolls) 

dice_rolls = [randint.rvs(low=1, high=7) for _ in idx] 
dice_roll_averages = [np.mean(dice_rolls[:k+1]) for k in idx]

再次为了获得一些直观的理解,我们将在图表上可视化这些平均值。

with plt.style.context("/span>seaborn-v0_8": 
    plt.figure(figsize=(10, 5)) 
    plt.title("/span>Sample averages of rolling a six-sided dice 

    # plotting the averages 
    plt.plot(idx, dice_roll_averages, linewidth=3) # the averages 

    # plotting the true expected value 
    plt.plot([-100, n_rolls+100], [3.5, 3.5], c="/span>k 

    plt.xlim(-10, n_rolls+10) 
    plt.ylim(0, 6) 
    plt.show()

图片

图 20.5:掷六面骰子的样本平均值

首先需要注意的是,这些数值异常接近 3.5。这不是一个概率,而是期望值:

𝔼[X1 ] = 𝔼[X2] = ⋅⋅⋅ = 3.5.

对于伯努利(p)分布的随机变量,期望值与概率 p 相同。然而,这一次,X[n] 并不像掷硬币时那样有一个明确的分布,样本平均值也不再是二项分布。因此,我们来多掷一些骰子,估计 X[n] 的分布情况。

more_dice_rolls = randint.rvs(low=1, high=7, size=(n_rolls, n_rolls)) 
more_dice_roll_averages = np.array([[np.mean(more_dice_rolls[i][:j+1]) for j in idx] 
                                     for i in idx])
with plt.style.context("/span>seaborn-v0_8": 
    fig, axs = plt.subplots(1, 3, figsize=(12, 4), sharey=False) 
    fig.suptitle("/span>The distribution of sample averages 
    for ax, i in zip(axs, [5, 100, 999]): 
        x = [6*k/i for k in range(i+1)] 
        y = more_dice_roll_averages[:, i] 
        ax.hist(y, bins=x) 
        ax.set_title(f/span>n = {i}" 

    plt.show()

图片

图 20.6:掷骰子时的样本平均分布

看起来,一次又一次地,X[n] 的分布集中在 𝔼[X[1]] 附近。我们的直觉告诉我们,这不是偶然;这一现象对于广泛的随机变量都成立。

让我剧透一下:这确实是这样,我们现在就来看。

20.5.3 …以及其余部分

这次,设 X[1],X[2],… 是一列独立同分布(i.i.d.)的随机变量。不是掷硬币,不是掷骰子,而是任何分布。我们看到样本平均值 X[n] 似乎收敛于 X[i]-s 的联合期望值:

′′- ′′ Xn → 𝔼[X1]

注意引号:X[n] 不是一个数字,而是一个随机变量。因此,我们还不能谈论收敛。

从数学上精确来说,我们之前看到的是,当 n 足够大时,样本平均值 X[n] 很不可能远离联合期望值 μ = 𝔼[X[1]];也就是说,

lim[n→∞] P(|X[n] − μ| > 𝜖) = 0 (20.2)

对所有 𝜖/span>0\ 都成立。

极限(20.2)现在似乎很难证明,即使是在简单的掷硬币的情况下。在那里,X[n] ∼1n 二项分布(n,p),因此

 ⌊n(p+ 𝜖)⌋ ( ) -- ∑ n k n−k P(|Xn − μ | >𝜖) = 1 − k p (1 − p) , k=⌊n(p− 𝜖)⌋

其中符号 ⌊x⌋ 表示小于 x 的最大整数。这看起来一点也不友好。(我把验证留作练习。)

因此,我们的计划如下。

  1. 找一种估计 P(jX[n]−μj/span>𝜖) 的方法,使其与 X[i]-s 的分布无关。

  2. 使用上界估计来证明 lim[n→∞]P(jX[n] −μj/span>𝜖) = 0。

我们开始吧。

20.5.4 大数法则的弱法则

首先是上界估计。有两个一般不等式可以帮助我们处理 P(jX[n] −μj ≥𝜖)。

定理 134. (马尔可夫不等式)

设 (Ω,Σ,P) 是一个概率空间,且 X : Ω → [0,∞) 是一个非负随机变量。那么

 𝔼[X ] P (X ≥ t) ≤ ----- t

对于任意 t ∈ (0,∞),此不等式都成立。

证明:我们需要分离离散情况和连续情况。证明几乎完全相同,所以我只会做离散情况,连续情况留给你作为练习来验证你的理解。

设 X : Ω → {x[1],x[2],…} 是一个离散型随机变量(其中 x[k] ≥ 0 对所有 k 成立),且 t ∈ (0,∞) 是一个任意的正实数。

那么

𝔼[X]

= ∑ [k=1]^∞x [k]P(X = x[k])

= ∑ [k:x[k]/span>tx[k]P(X = x[k]) + ∑ [k:x[k]≥t]x[k]P(X = x[k]),]

其中和 ∑ [k:x[k]/span>t 仅仅计算了 x[k]/span>t 的 k-s,类似地,∑ [k] : x[k] ≥t 仅计算了 x[k] ≥t 的 k-s。

由于假设 x[k]-s 为非负数,因此我们可以通过忽略其中一个来从下方估计 𝔼[X]。因此,

 ∑ ∑ 𝔼[X ] = xkP (X = xk )+ xkP (X = xk) k:x < k:x ≥t ∑k k ≥ xkP (X = xk ) k:xk≥t ∑ ≥ t P (X = xk ) k:xk≥t = tP(X ≥ t),

从中可以得到马尔可夫不等式

P (X ≥ t) ≤ 𝔼[X-] t

如下。

大数法则仅一步之遥,接近于马尔可夫不等式。最后这一步非常有用,值得成为一个独立的定理。让我们来看看著名的切比雪夫不等式。

定理 135.(切比雪夫不等式)

设 (Ω, Σ, P) 为一个概率空间,X : Ω →ℝ 是一个具有有限方差 σ² = Var[X] 和期望值 𝔼[X] = μ 的随机变量。

然后

 σ2 P(|X − μ| ≥ t) ≤ -t2-

对所有 t ∈ (0,∞) 成立。

证明。由于 |X − μ| 是一个非负随机变量,我们可以应用定理 134 得到

P (|X − μ| ≥ t) = P(|X − μ|2 ≥ t2) 2 ≤ 𝔼[|X--−-μ|-]. t2

然而,由于 𝔼[|X − μ|²] = Var[X] = σ²,我们有

 2 2 P(|X − μ | ≥ t) ≤ 𝔼-[|X-−2μ-|] = σ2 t t

这就是我们需要证明的。

通过这些内容,我们准备好精确地表述和证明大数法则。经过所有这些准备,(弱)大数法则只是一步之遥。以下是它的完整形式。

定理 136.(弱大数法则)

设 X[1], X[2], … 为一列独立同分布的随机变量,其期望值 μ = 𝔼[X[1]] 和方差 σ² = Var[X[1]],且

-- X1 + ⋅⋅⋅+ Xn Xn = ------------- n

设它们的样本平均值为。然后

lim P (|X- − μ| ≥ 𝜀) = 0 n→ ∞ n

对任何 𝜀/span>0\ 成立。

证明。由于 X[i] 是独立的,样本平均值的方差为

 -- [ X1 + ⋅⋅⋅+ Xn ] Var[Xn ] = Var ------------- n = -1-Var[X1 + ⋅⋅⋅+ Xn] n2 = -1-(Var[X ]+ ⋅⋅⋅+ Var[X ]) n2 1 n n σ2 σ2 = -n2- = n-.

现在,通过使用定理 135 中的切比雪夫不等式,我们可以得到

 -- -- Var[Xn-] -σ2- P (|Xn − μ | ≥ 𝜀) ≤ 𝜀2 = n 𝜀2.

因此,

0 ≤ lim P(|Xn − μ | ≥ 𝜀) n→ ∞ -σ2- ≤ nl→im∞ n𝜀2 = 0,

因此

 -- nli→m∞ P(|Xn − μ | ≥ 𝜀) = 0,

这就是我们需要证明的。

定理 136 不是关于样本平均值的全部内容。还有更强的结果,表明样本平均值确实以概率 1 收敛到均值。

20.5.5 强大数法则

为什么定理 136 被称为“弱”大数法则?想想这个声明

lim[n→∞] P(|X[n] − μ| ≥ 𝜀) = 0 (20.3)

稍等片刻。对于给定的 ω ∈ Ω,这并没有告诉我们具体样本平均值的收敛情况。

X- (ω) = X1(ω-)+-⋅⋅⋅+-Xn-(ω-), n n

它只是告诉我们,在概率意义上,X[n] 集中在联合期望值 μ 周围。从某种意义上讲,(20.3) 是

P( lim X- = μ ) = 1, n→ ∞ n

因此,这就是所谓的弱大数法则。

我们是否有比定理 136 更强的结果?是的,有。

定理 137.(强大数法则)

设 X[1],X[2],…是独立同分布的随机变量序列,具有有限的期望值μ = 𝔼[X[1]]和方差σ² = Var[X[1]],并且设

-- X + ⋅⋅⋅+ X Xn = -1---------n- n

设它们是样本均值。那么

P( lim Xn = μ ) = 1. n→ ∞

我们不会证明这一点,只需要知道样本均值会以概率 1 收敛到均值。

注 21.(随机变量的收敛性)

我们在大数法则的弱法则和强法则中看到的现象并非仅限于样本均值。在其他情况下也可以观察到类似的现象,因此,这些类型的收敛性有其各自的精确定义。

如果 X[1],X[2],…是一个随机变量序列,我们称

(a) 如果 X[n]在概率上收敛到 X,则

 lim P(|Xn − X | ≥ 𝜀) = 0 n→ ∞

对所有𝜀/span>0. 概率收敛表示为 X[n]−P→X。

(b) 如果 X[n]几乎确定地收敛到 X,则

P( lim Xn = X ) = 1 n→ ∞

成立。几乎确定的收敛表示为 X[n]−a−.→ s.X。

因此,大数法则的弱法则和强法则指出,在某些情况下,样本均值在概率上和几乎确定地都收敛到期望值。

20.6 信息论

如果你已经在实践中训练过机器学习模型,可能已经熟悉均方误差。

 n 1-∑ 2 n MSE (x,y) = n (f(xi)− yi), x, y ∈ ℝ , i=1

其中,f : ℝ^n →ℝ表示我们的模型,x ∈ℝ^n 是一个一维观测向量,y ∈ℝ^n 是真实值。学完期望值后,这个求和式应该是熟悉的:如果我们假设一个概率视角,并让 X 和 Y 是描述数据的随机变量,那么均方误差可以表示为期望值。

 [ ] MSE (x, y) = 𝔼 (f(X )− Y )2.

然而,均方误差并不适用于分类问题。例如,如果任务是分类一张图像的物体,输出将是每个样本的离散概率分布。在这种情况下,我们可以使用所谓的交叉熵,其定义为

 n H [p,q] = − ∑ p logq i i i=1

其中,p ∈ℝ^n 表示单个数据样本的类别标签的独热编码向量,q ∈ℝ^n 是类别标签预测,形成一个概率分布。(独热编码是将一个有限的类别标签集合,如{a,b,c},表示为零一向量的过程,例如

a ← → (1,0,0), b ← → (0,1,0), c ← → (0,0,1).

我们这样做是因为处理向量和矩阵比处理字符串更容易。)

并不令人惊讶,H[p,q]也是一个期望值,但它远不止如此:它量化了分布 q 与真实分布 q 之间的信息内容。

但在数学意义上,什么是信息?让我们深入探讨。

20.6.1 猜数字

让我们从一个简单的游戏开始。我已经想好了一个介于 0 到 7 之间的整数,你的任务是通过是非问题找出这个数字。

图片

图 20.7:我在想哪个数字?

一种可能的策略是逐一猜测数字。换句话说,你的问题顺序是:

  • 它是 0 吗?

  • 它是 1 吗?

  • .. .

  • 它是 7 吗?

尽管这个策略有效,但并不是最有效的。为什么?考虑一下平均问题数。设随机变量 X 表示我选择的数字。由于 X 是均匀分布的——也就是说,P(X = k) = 1/8,对于所有 k = 0,…,7——那么恰好问 k 个问题的概率为

P(#questions = k) = P(X = k − 1) = 1 8

同样。因此,所需的问题数也在 {1,…,8} 上均匀分布,因此

 ∑8 𝔼[#questions] = kP (#questions = k ) k=1 8 = 1-∑ k 8 k=1 = 1-8⋅9-= 9-, 8 2 2

我们已经使用了 ∑ [k=1]^n = n(n+1) 2

我们能做得比这个更好吗?可以。在之前的顺序策略中,每个问题命中目标的机会较小,大部分情况下只会排除一个潜在的候选项。很容易看出,最好的方法是每个问题都能将搜索空间减半。

比如,我想到的数字是 2。通过问“这个数字大于 3 吗?”,答案可以排除四个候选项。

图片

图 20.8:问题后搜索空间是比 3 大的数字吗?

每个后续的问题都会将剩余的可能性减半。以 X = 2 为例,三个问题如下:

  • X ≥ 4 吗?(否)

  • X ≥ 2 吗?(是)

  • X ≥ 3 吗?(否)

这就是所谓的二分查找,如图 20.9 所示。

图片

图 20.9:通过二分查找得出答案

如果我们将三个连续问题的答案(X ≥ 4, X ≥ 2, X ≥ 3)写成一个二进制序列,我们得到 010。如果这看起来很熟悉,那并非偶然:010 是二进制的 2。事实上,所有答案都可以用其二进制形式进行编码:

0 = 0002, 1 = 0012, 2 = 0102, 3 = 0112 4 = 1002, 5 = 1012, 6 = 1102, 7 = 1112.

因此,我们可以重新表述我们的三个问题:

  1. 1 是 X 的第一个二进制位吗?

  2. 1 是 X 的第二个二进制位吗?

  3. 1 是 X 的第三个二进制位吗?

如上例所示,猜数字等同于找出待猜数字的二进制表示。每一位代表一位信息。(在这种情况下,表示就是实际的二进制形式。)二进制编码有一个额外的好处:我们不再需要顺序地提问,可以同时问多个问题。从现在起,我们将不再讨论问题,而是讨论二进制表示(编码)及其位。

请注意,每个 X 的结果所需的位数是相同的。因此,这种策略始终需要三个位,因此它们的平均数量也是三:

 7 7 𝔼 [#bits] = ∑ 3⋅P (X = k ) = ∑ 3⋅ 1-= 3. 8 k=0 k=0

我们能做得比平均三次提问更好吗?不能。我邀请你提出你的论据,但我们稍后会看到这一点。

上述的三号数字来自哪里?一般来说,如果我们有 2^k 个可能的选择,那么 log [2]2^k = k 个问题就足够找到答案。(因为每个问题都会将可能答案的范围减半。)换句话说,我们有

 ∑7 𝔼[#bits] = P (X = k )log2 23 k=0 7 = ∑ P (X = k )log P(X = k)−1. 2 k=0

因此,log [2]P(X = k)^(−1) 的值是我们编码中表示 k 所需要的比特数。换句话说,

𝔼[#bits] = 𝔼[log2P (X = k)−1].

让我们提前一点:这就是著名的随机变量 X 的熵,而 log [2]P(X = k)^(−1) 是所谓的事件 X = k 的信息量。

然而,到此为止,这些概念还很不清楚。log [2]P(X = k)^(−1) 与信息有什么关系?为什么我们不能用比 log [2]P(X = k)^(−1) 更好的方式来表示 k 的比特数?我们很快就会看到答案。

20.6.2 猜数字 2:电气波古鲁

让我们再玩一次猜数字的游戏,但这次有点不同。我现在从 {0,1,2} 中选了一个数字,你得猜是哪一个。难点是,我选择 0 的概率是其他数字的两倍。

在概率论中,如果 X 表示我选的数字,那么 P(X = 0) = 1∕2,而 P(X = 1) = P(X = 2) = 1∕4。

最佳策略是什么?有两个关键事实需要记住:

  • 好的问题会将搜索空间减少一半,

  • 提问相当于找到这些结果的二进制编码。

然而,由于我们在寻找平均最优的编码,不能简单地理解为每个数字将搜索空间减少一半。这种理解应当是概率性的。所以,如果 0 的确是更有可能的,那么通过

0 ∼ 0, 1 ∼ 01, 2 ∼ 10,

平均比特数是

𝔼[#bits] = P (X = 0) ⋅1+ P (X = 1) ⋅2+ P (X = 2) ⋅2 = P (X = 0) log22 + P (X = 1)log24 + P (X = 2)log24 2 ∑ −1 = P(X = k)log2P (X = k) k=0 = 3-. 2

再一次,我们走到了熟悉的对数公式。我们离理解这个神秘量 log [2]P(X = k)^(−1) 更近一步了。它越小,我们需要的问题就越多;同样地,我们在编码中表示 k 所需的比特数也越多,以避免信息丢失。

那么,这些神秘的量到底是什么呢?

20.6.3 信息与熵

现在是时候对这个问题进行一般化了。假设我们的随机变量 X 从集合 {1,2,…,N} 中取一个数,每个数的概率是 p[k] = P(X = k)。在反复观察 X 后,我们的观察的平均信息量是多少?

根据我们所学到的,我们正在寻找的量是

 ∑N 𝔼 [I (X )] = − p logp , k=1 k k

其中 I : ℕ → ℝ表示信息 I(k) = −log p[k]。之前,我们已经看到两个特殊情况,其中 I(k)是猜测 k 所需的平均问题数。(等价地,信息是使用最优编码时 k 的平均比特数。)

那么一般来说,信息是什么?

让我们把 I 视为概率的未知函数:I(x) = f(P(X = x))。那么 f 可能是什么?有两个关键性质将引导我们找到答案。首先,事件越可能发生,信息含量就越少。(回想一下之前的例子,最可能的结果需要最少的比特数进行二进制表示。)

其次,作为概率的函数,信息是可加的:f(pq) = f(p) + f(q)。为什么?假设我独立地选择了两个数字,现在你需要猜测这两个数字。你可以顺序进行,首先应用最优策略猜测第一个,然后猜测第二个。

用数学语言来说,f(p)是

  • 连续的,

  • 严格递增,即对于任何 p > q,有 f(p) > f(q),

  • 且可加性,即对于任何 p, q,有 f(pq) = f(p) + f(q)。

我会省略数学细节,但通过一点微积分魔法,我们可以自信地得出唯一的选项是 f(p) = −log [a]p,其中 a > 1。表面上看,信息似乎依赖于基数,但实际上

log x = logax-, b logab

基数的选择仅会影响信息和熵的乘法尺度因子。因此,使用自然对数是最简单的选择。

所以,这里终于给出了正式定义。

定义 96.(信息)

设 X 为具有概率质量函数{P(X = x[k])}[k]的离散随机变量。

事件 X = x[k]的信息定义为

I(xk) := − logP (X = xk ) = logP (X = xk )− 1.

(注意,当没有指定对数的基数时,我们使用自然对数 e。)为了强调信息依赖于 X,我们有时会明确表示其关系为 IX。

凭借信息的概念,我们准备好定义熵,即每次观察的平均信息量。这个量以 Claude Shannon 的名字命名,他在其史诗论文《通信的数学理论》中奠定了信息理论的基础。

定义 97.(香农熵)

设 X 为具有概率质量函数{P(X = x[k])}[k]的离散随机变量。

X 的熵定义为

H[X ] := 𝔼[I(X )] ∑∞ = − P(X = xk)logP (X = xk). k=1

尽管 H[X]被称为香农熵,但我们将简单地称之为熵,除非需要明确区分。

我们可以注意到的第一件事是 H[X] ≥ 0。这一点在下面的命题中有所体现。

命题 7.(熵的非负性)

设 X 为任意离散随机变量。那么 H[X] ≥ 0。

证明。根据定义,

 ∑ H [X ] = P(X = xk)logP (X = xk)−1. k

首先,假设对于所有的 k,P(X = x[k])≠0。那么,由于 0 ≤ P(X = x[k]) ≤ 1,信息是非负的:log P(X = x[k])^(−1) ≥ 0。因此,定义和中的所有项都是非负的,H[X]也是非负的。

如果 P(X = x[k]) = 0 对于某些 k,那么,由于 lim[x→0+]xlog x = 0,表达式 0 ⋅ log 0 被视为 0。因此,H[X]仍然是非负的。

实际上计算熵是困难的,因为我们需要评估包含对数的求和。然而,仍然有几个特殊情况为熵的概念提供了有价值的启示。让我们来看一下!

示例 1. 离散均匀分布。(见第 19.2.4 节对离散均匀分布的定义。)设 X ∼ Uniform({1,…,n})。则

 ∑n H [X ] = − -1log 1- k=1n n ∑n = 1logn k=1 n = log n.

到目前为止,我们已经对熵有了直观的理解,即每次观测的平均信息量。猜猜看:在所有集中于{1,2,…,n}的分布中,均匀分布的熵如何与其他分布相比?是高于还是低于平均水平?是最小的还是最大的?

我们将在本章末尾揭示答案,但在继续下一个示例之前,花一分钟思考这个问题。

示例 2. 单点分布。(见第 19.2.5 节对单点分布的定义)设 X ∼δ(a)。则

H [X ] = − 1 ⋅log 1 = 0.

换句话说,由于事件 X = a 是确定的,观察 X 时没有获得任何信息。现在回想一下之前的例子。由于 X ∼δ(k)集中于{1,2,…,n},对于所有 k = 1,2,…,n,再思考一下之前的问题。

让我们在下一个示例中看到部分答案。

示例 3. 伯努利分布。(见第 19.2.1 节对伯努利分布的定义)。设 X ∼ Bernoulli(p)。则,很容易看出

H [X ] = − plogp − (1− p)log(1− p).

哪个 p 值最大化熵?为了找到 H[X]的极大值,我们可以求导。(回想一下,导数和二阶导数如何用于优化,正如定理 87 所述。)

因此,设 f(p) = H[X] = −plog p − (1 −p)log(1 −p)。则,

f′(p) = − logp + log(1− p) = log 1-−-p, p f′′(p) = − 1-−--1--. p 1 − p

通过解 f^′(p) = 0,我们得到 p = 1∕2,这是 f(p)的唯一潜在极值点。由于 f^(′′)(1∕2) = −4/span>0,我们可以看到 p = 1∕2 确实是一个局部最大值。让我们绘制 f(p)图像,以获得直观的确认。

def bernoulli_entropy(p): 
    return -p*np.log(p) - (1 - p)*np.log(1 - p) 

X = np.linspace(0.001, 0.999, 100) 
y = bernoulli_entropy(X) 
with plt.style.context(’seaborn-v0_8’): 
    plt.figure(figsize=(8, 8)) 
    plt.xlabel("/span>p 
    plt.ylabel("/span>H[X]" 
    plt.title("/span>The entropy of Bernoulli(p) 
    plt.plot(X, y) 
    plt.show()

PIC

图 20.10:伯努利分布的熵

对于 p = 1∕2,即当伯努利分布的熵达到最大值时,我们得到一个在二元集合{0,1}上的均匀分布。另一方面,当 p = 0 或 p = 1 时,熵最小,伯努利分布则为单点分布。

由于 {0,1} 上的每个随机变量都是伯努利分布的,我们似乎已经有了部分答案:均匀分布最大化熵,而单点分布最小化熵。

如下定理所示,这在一般情况下也是成立的。

定理 138.(均匀分布与最大熵)

设 E = {x[1],…,x[n]} 是一个有限集,且设 X : Ω →E 为一个取值于 E 的随机变量。那么,

H [X] ≤ H [Uniform (E)],

并且 H[X] = H[Uniform(E)] 当且仅当 X 在 E 上是均匀分布的。

我们在这里不会展示这个,但有许多证明可以参考。例如,Bishop 的经典著作《模式识别与机器学习》使用拉格朗日乘数法来显式地找到多变量函数 f(p[1],…,p[n]) = −∑ [k=1]^np[k] log p[k] 的最大值;有兴趣的话可以查看其中的详细内容。

如果我们不将离散型随机变量限制在有限集上,怎么办?在这种情况下,Shannon 熵没有上限。在本章的问题集中,你将看到几何分布的熵是

H [Geo (p)] = − plogp-+-(1−-p)-log(1-−-p). p

很容易看出 lim[p→0]H[Geo(p)] = ∞。我们来绘制一下图像!

def geom_entropy(p): 
    return -(p*np.log(p) + (1 - p)*np.log(1 - p))/p 

X = np.linspace(1e-16, 1-1e-16, 1000) 
y = geom_entropy(X) 

with plt.style.context(’seaborn-v0_8’): 
    plt.figure(figsize=(8, 8)) 
    plt.xlabel("/span>p 
    plt.ylabel("/span>H[X]" 
    plt.title("/span>The entropy of Geo(p) 
    plt.plot(X, y) 
    plt.show()

PIC

图 20.11:几何分布的熵

因此,Shannon 熵可以取任何非负值。

20.6.4 微分熵

到目前为止,我们只为离散型随机变量定义了熵。

这是否也适用于连续型随机变量?是的。公式 𝔼 − log f[X] 可以直接应用于连续型随机变量,得到所谓的微分熵。这里是其正式定义。

定义 98.(微分熵)

设 X 为一个连续型随机变量。X 的微分熵通过以下公式定义:

 ∫ ∞ H [X ] := − f (x )log f (x)dx, −∞ X X

其中 f[X] 表示 X 的概率密度函数。

现在来了一个惊喜。我们能从 Shannon 熵推导出这个公式吗?我们将像在定义连续型随机变量的期望值时那样处理这个问题:用离散型随机变量来逼近连续型随机变量,然后看看 Shannon 熵收敛到哪里。

因此,设 X : Ω →ℝ 为连续型随机变量,设 [a,b] ⊆ℝ 为一个(较大的)区间,使得 P(X∈∕[a,b]) 极小。我们将 [a,b] 分割为 n 等份,方法如下:

 k(b− a) xk = a+ -------, k = 0,1,...,n, n

并通过以下方式定义逼近的随机变量 X^((n)):

![ ( (n) |{ xk if x ∈ (xk −1,xk] for some k = 1,2,...,n, X (ω ) := | ( 0 otherwise. ](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/math-ml/img/file2020.png)

这样,X^((n)) 的熵由以下公式给出:

 (n) ∑n (n) (n) H [X ] = − P (X = xk)logP (X = xk). k=1

然而,由于我们定义了 X^((n)),

 ∫ (n) xk P(X = xk) = P(xk− 1 <X ≤ Xk ) = x fX (x)dx, k−1

其中 f[X]是 X 的密度函数。现在,定积分的均值定理(定理 93)表明,存在ξ[k] ∈ [x[k−1],x[k]],使得

∫ xk f (ξ ) fX (x)dx = (xk − xk− 1)fX (ξk) = -X---k-, xk−1 n

因此,总结来说,

 (n) fX-(ξk) P (X = xk ) = n .

(回想一下,x[0]/span>x[1]/span>…/span>x[n]的划分是等距的,x[k] −x[k−1] = 1∕n。)

现在,使用 P(X^((n)) = x[k]) = fX(ξk) n,我们可以得到

 ∑n H[X (n)] = − P (X (n) = xk )log P(X (n) = xk) k=1 n = − ∑ fX-(ξk)-log fX-(ξk)- n n k=n1 n = − ∑ fX-(ξk)-log f (ξ )+ log n∑ fX(ξk). n X k n k=1 k=1

这两个项都是黎曼和,近似函数内部的积分。如果 n 很大,并且区间[a,b]足够大,那么

 n ∫ − ∑ fX-(ξk)logf (ξ ) ≈ − ∞ f (x)logf (x)dx = h[X ], n X k −∞ X X k=1

并且

 n ∫ ∞ ∑ fX-(ξk)-≈ f (x)dx = 1, n − ∞ X k=1

这意味着

H [X (n)] ≈ h [X ]+ logn.

这很令人惊讶,因为人们通常会期望 H[X^((n))]会收敛到 h(X)。但事实并非如此。

 lim (H [X (n)]− log n) = h[X ] n→ ∞

保持。

现在是示例时间。

示例 1. 均匀分布。(见 19.3.4 节中的均匀分布定义。)设 X∼Uniform(a,b)。那么,

 ∫ b -1--- --1-- h[X ] = − b− a log b− a dx a = log(b− a),

这与离散均匀分布的情况相似。然而,有一个显著的区别:当 b−a/span>1 时,h(X)是负的。这与香农熵形成鲜明对比,后者始终是非负的。

示例 2. 正态分布。(见 19.3.6 节中的正态分布定义。)设 X∼𝒩(μ,σ²)。那么,

 ∫ ∞ 1 (x−μ)2 ( 1 (x−μ)2) h[X ] = − -√---e− 2σ2 log -√----e− 2σ2 dx − ∞ σ 2π∫ σ 2π ∫ ( √ ---) ∞ --1---− (x−2σμ2)2- ∞ (x−-μ-)2---1---− (x−2μσ)22 = log σ 2π −∞ σ√ 2πe dx+ − ∞ 2σ2 σ√ 2πe dx ◟--------◝◜--------◞ ◟------------◝◜------------◞ =1 = 21σ2Var[X ]= 12 1 ( 2 ) = 2- 1+ log(σ 2π ) .

根据σ的值,这里 h[X]的值也可能是负的。

之前我们已经看到,对于给定有限集合上的离散分布,均匀分布最大化熵,正如定理 138 所述。

连续分布的定理 138 的类似物是什么?大胆猜一下。如果我们设 X 为任何连续分布,那么,正如我们已经看到的,

h[Uniform (a,b)] = log(b− a),

其值可以是任意实数。与离散情形类似,我们必须做一些限制;这次,我们将固定方差。结果如下。

定理 139.(最大化微分熵)

设 X 是一个方差为σ²的连续随机变量。那么

h[X ] ≤ h [𝒩 (0,σ2)],

并且 h[X] = h[𝒩(0,σ²)] 当且仅当 X ∼𝒩(μ,σ²)。

我们不打算证明这个。你可以参考比 ISHOP 的《模式识别与机器学习》获取更多细节。

20.7 最大似然估计

我是简单想法的传播者。随时可以打断我,但无论我在哪个领域,我总是能找到一小套极其简单的想法,使整个工作得以运转。(虽然你不可能打断我,因为这是一本书。笑话是对你的!)

让我给你举个我脑海中的具体例子。你认为是什么推动了深度学习的崛起,包括具有数十亿参数的神经网络?有三个像 ABC 一样简单的想法:

  • 你可以通过反向梯度优化损失函数(无论参数的数量是多少),

  • 你可以通过巧妙应用链式法则和矩阵乘法来高效地计算梯度,

  • 并且我们可以在 GPU 上以极快的速度进行矩阵运算。

当然,在这些想法的基础上建立了一个庞大的工作体系,但这三点构成了今天机器学习的基础。最终,这些使你能够与大型语言模型对话。让你的车在城市中自动巡航,同时你阅读报纸。预测巨大的氨基酸链的准确形状,这些氨基酸链构成了每一个生物体。(包括你。)

梯度下降、反向传播和高效的线性代数属于隐喻中机器学习硬币的实用面。如果我们构建一个参数化模型,可以投入一些极其强大的工具来进行处理。

但是我们的模型从哪里来?

正如我所说,存在一小套关键的想法,它们起到了至关重要的作用。我们即将遇到其中之一:最大似然估计。

20.7.1 概率建模入门

作为一个自封为简单思想的传播者,我将从一个简单的例子开始,来说明一个简单的想法。

拿起一枚硬币,投掷几次并记录每次的结果。问题再次简单:正面的概率是多少?我们不能立即假设 p = 1∕2,也就是说,硬币是公平的。例如,硬币的一面可能被涂上铅,从而产生偏差。为了弄清楚这一点,我们来做一些统计。(卷起袖子,丢下手套。)

从数学角度来看,我们可以用伯努利分布来建模硬币投掷(第 19.2.1 节):

P(X = 1) = p, P(X = 0) = 1 − p,

其中

  • X 是表示单次投掷结果的随机变量,

  • X = 1 表示正面,X = 0 表示反面,

  • 并且 p ∈ [0,1] 是正面的概率。

这只是模型。我们的目标是估计参数 p,这正是我们统计学可以处理的。

将硬币投掷 n 次得到零一序列 x[1], x[2], …, x[n],其中每个 x[i] 都是伯努利分布的随机变量 X[i] ∼ 伯努利(p) 的一个实现,且彼此独立。

正如我们在讨论大数法则时所看到的(定理 137),一个自然的想法是通过计算样本均值来估计 p,而这恰好是 X 的期望值。为了超越经验估计,我们可以利用这个机会,因为我们现在有一个概率模型。

关键问题是:哪个参数 p 最有可能生成我们的样本?

在概率的语言中,这个问题通过最大化似然函数来回答

 n ∏ LLH (p;x1,...,xn) = P(Xi = xi | p), i=1

其中 P(X[i] = x[i]∣p)表示在固定参数 p 下观察到 x[i]的概率。LLH(p;x[1],…,x[n])越大,参数 p 越有可能。换句话说,我们对 p 的估计将是:

ˆp = argmaxp ∈[0,1]LLH (p;x1,...,xn).

让我们来求解它。

在我们的具体情况下,P(X[i] = x[i]∣p) 可以写成:

 (| {p if xi = 1, P(Xi = xi | p) = | (1 − p if xi = 0.

代数不喜欢 if-else 类型的函数,因此,通过一个巧妙的数学技巧,我们将 P(X[i] = x[i]∣p)写成:

 x 1−x P(Xi = xi | p) = p i(1− p ) i,

使得似然函数为:

 ∏n LLH (p;x1,...,xn) = pxi(1 − p)1−xi. i=1

(我们通常会写 LLH(p)来简化符号复杂性。)

这仍然不容易优化,因为它是由指数函数的乘积组成的。所以,这里有另一个数学技巧:取对数,将乘积转换为和。

由于对数是递增的,它不会改变最优解,所以我们可以继续:

 ∏n log LLH (p) = log pxi(1− p )1−xi i=1 ∑n [ ] = log pxi(1 − p)1−xi i=1 ∑n [ ] = logpxi + log(1− p)1−xi i=1 ∑n ∑n = logp xi + log(1 − p) (1− xi). i=1 i=1

相信我,这样好多了。根据二阶导数检验(定理 87),我们可以通过:

  1. ddp log LLH(p) = 0 来找到临界点 p̂,

  2. 然后证明 p̂是最大值,因为 -d2- dp2 log LLH(p)/span>0。

让我们开始吧。

由于 ddp log p = 1pddp- log(1 −p) = −11−p,我们有:

d 1 ∑n 1 ∑n --logLLH (p;x1,...,xn) = -- xi − ----- (1− xi). dp p i=1 1− p i=1

ddp log LLH(p;x[1],…,x[n]) = 0 得到一个解

 1 ∑n ˆp = -- xi. n i=1

(拿起一支笔和纸,自己计算一下答案。)关于二阶导数,我们有:

d2 1 ∑n 1 ∑n --2 logLLH (p) = − -2 xi −-------2 (1− xi), dp p i=1 (1 − p) i=1

它是均匀负的。因此,p̂ = 1n ∑ [i=1]^nx[i] 确实是一个(局部)最大值。耶!

在这种情况下,最大似然估计与样本均值相同。相信我,这是一个罕见的例外。把它看作是对样本均值的验证:我们通过不同的思路获得了相同的估计值,所以它一定是正确的。

20.7.2 建模高度

让我们继续进行另一个例子。抛硬币的例子展示了离散情况。现在是进入连续领域的时候了!

这一次,我们正在测量一个高中班级的身高,并希望建立一个概率模型。一个自然的想法是假设身高来自于正态分布 X ∼𝒩(μ,σ²)。 (查看第 19.3.6 节了解正态分布。)

我们的工作是估计期望值 μ 和方差 σ²。来吧,最大似然!

为了使问题在数学上精确,我们有测量值 x[1],…,x[n],它们来自独立同分布的随机变量 X[i] ∼𝒩(μ,σ²)。然而,出现了一个问题:因为我们的随机变量是连续的,

 ∏n P (X1 = x1,...,Xn = xn | μ, σ2) = P (Xi = xi | μ, σ2) = 0. i=1

(因为所有项的乘积为零。)那么我们该如何定义似然函数呢?别担心:虽然我们没有质量函数,但我们有密度!因此,由以下定义似然函数:

 ∏n LLH (μ, σ;x1,...,xn) = fX (xi) i=1 i n 2 = ∏ -√1--e− (xi2−σμ2), i=1 σ 2π

其中 fX[i] 是 X[i] 的概率密度函数。

让我们来最大化它。思路是类似的:取对数,找到临界点,然后使用二阶导数检验。开始吧:

 1 1 ∑n 2 log LLH (μ,σ) = nlog -√----− σ2- (xi − μ) . σ 2π i=1

从现在起,照常进行。导数如下:

 ∑n ∂--log LLH (μ,σ) = -2- (xi − μ), ∂μ σ2 i=1 ∑n ∂--log LLH (μ,σ) = − n-− -2- (xi − μ)2. ∂σ σ σ3 i=1

通过一点数字运算(你应该尝试自己完成),我们得到 ∂μ log LLH(μ,σ) = 0 这意味着

 -1∑n μ = n xi, i=1

-∂ ∂σ log LLH(μ,σ) 这意味着

 ∑n σ = -1 (xi − μ )2. n i=1

我们在这里不做二阶导数检验,但相信我:这是一个最大值,结果给出我们估计值

 n ˆμ = -1∑ x, n i=1 i n ˆσ = -1∑ (x − μˆ)2. n i i=1

再次提到,样本均值和方差。可以这样理解:默认使用样本均值和方差是最简单的方法,但即便是像最大似然估计这样聪明的方法,也会把它们作为参数估计值。

在详细解答上述两个例子后,我们已经准备好抽象掉细节,引入一般问题。

20.7.3 一般方法

我们已经看过最大似然估计的工作原理。现在,是时候构建抽象的数学框架了。

定义 99.(似然函数)

设 P[𝜃]是由参数𝜃 ∈ℝ^k 参数化的概率分布,且 x[1],…,x[n] ∈ ℝ^d 是该概率分布的独立实现。(即样本来自独立同分布的随机变量 X[1],…,X[n],其分布由 P[𝜃]给出。)

给定样本 x[1],…,x[n],𝜃的似然函数定义为:

(a)

 ∏n LLH (𝜃;x1,...,xn) := P 𝜃(Xi = xi) i=1

如果 P[𝜃]是离散的,并且

(b)

 ∏n LLH (𝜃;x1,...,xn) := f𝜃(xi) i=1

如果 P[𝜃]是连续的,其中 f[𝜃]是 P[𝜃]的概率密度函数。

我们已经看到了两个似然函数的示例:对于伯努利分布 Bernoulli(p),给定:

 ∏n LLH (p;x1,...,xn) = pxi(1 − p)1−xi, i=1

对于正态分布𝒩(μ,σ),给定:

 n 2 LLH (μ, σ;x ,...,x ) = ∏ -√1--e− (xi2−σμ2). 1 n i=1 σ 2π

从直观上讲,似然函数 LLH(𝜃;x[1],…,x[n])表示当参数𝜃为真时,我们观察到的 x[1],…,x[n]的概率。最大似然估计是通过最大化此概率得到的参数ˆ𝜃;即在这个参数下,观察结果最可能发生。

定义 100. (最大似然估计)

设 P[𝜃]是由参数𝜃 ∈ℝ^k 参数化的概率分布,且 x[1],…,x[n] ∈ ℝ^d 是该概率分布的独立实现。

𝜃的最大似然估计由以下公式给出:

ˆ𝜃 = argmax 𝜃∈ℝkLLH (𝜃;x1,...,xn ).

在这两个示例中,我们使用了对数运算将乘积转化为和。用一次是技巧;用(至少)两次就是方法。这是正式定义。

定义 101. (对数似然函数)

设 P[𝜃]是由参数𝜃 ∈ℝ^k 参数化的概率分布,且 x[1],…,x[n] ∈ ℝ^d 是该概率分布的独立实现。

给定样本 x[1],…,x[n],𝜃的对数似然函数定义为:

logLLH (𝜃;x1,...,xn),

其中 LLH(𝜃;x[1],…,x[n])是似然函数。

在经典统计学中,最大似然估计是通过以下方法进行的:

  1. 从数学家的帽子里抽出一个参数化的概率模型,

  2. 通过调整(对数)似然函数直到获得一个解析上可处理的形式,

  3. 通过求解∇LLH = 0(或∇log LLH = 0)来获得参数估计。

统计学在特定情况下可以非常强大,但我们得面对现实:上述方法有不少缺点。首先,构建一个可处理的概率模型是一个具有挑战性的任务,且容易受到专家固有偏见的影响。(我将建模过程间接地比作从帽子里抽出一只兔子,绝非偶然。)此外,模型越复杂,似然函数也越复杂。这反过来增加了我们优化问题的复杂度。

那我们为什么要花这么多页来学习这种古老的技巧呢?

因为这个思想在机器学习中至关重要,我们将通过一步步突破它的障碍,最终达到(或接近)最先进的水平。建模难吗?我们来构造一个拥有数十亿参数的函数来完成这个任务。优化计算密集吗?不用担心,我们有集群的 GPU 可以使用。

20.7.4 德国坦克问题

再举一个例子,来自二战时期。假设你是一个盟军情报官员,负责估算德军装甲师的规模。(也就是,猜测坦克的数量。)

那时候没有卫星图像可供参考,因此除了一个小小的信息来源——敌方摧毁坦克的序列号,我们几乎没有其他可以依赖的资料。我们能从这些信息中做些什么呢?

在没有详细了解制造过程的情况下,我们可以假设这些坦克在出厂时按顺序编号。我们也不知道这些坦克是如何在战场之间分配的。

这两块知识(或者更精确地说,缺乏知识)转化为一个简单的概率模型:遇到敌方坦克就像从均匀分布 Uniform(N)中抽取样本,其中 N 是坦克的总数。因此,如果 x[1],…,x[n]是摧毁坦克的序列号,我们可以使用最大似然法来估计 N。

让我们开始吧。离散均匀分布 Uniform(N)的似然函数由以下公式给出:

 ∏n LLH (N ) = P (Xi = xi), i=1

其中,概率 P(X[i] = x[i])有一个相当独特的形式:

 ( |{ -1 P (X = x ) = N if xi ∈ {1,...,N }, i i |( 0 otherwise.

记住,x[1],…,x[n] ≤N(因为没有观察到的序列号会大于坦克的总数),我们有

 ( |{ -1- LLH (N ) = Nn if N <max {x1,...,xn}, |( 0 otherwise.

想一想:N 越大,LLH(N)就越小。因此,最大似然估计是最小的可能选择。

Nˆ= max {x1,...,xn }.

换句话说,我们对坦克数量的猜测就是我们遇到的最大序列号。

你怎么看这个估计?我不骗你,我自己并不特别喜欢。德国坦克问题凸显了建模假设在统计学中的重要性。最终的估计ˆN是我们选择 Uniform(N)的结果。机器学习中的常见智慧是“垃圾进,垃圾出”。建模也是如此。

20.8 小结

在这一章中,我们了解了期望值的概念。从数学上讲,期望值的定义是

 ∑ 𝔼[X ] = xkP (X = xk) k

对于离散随机变量和

 ∫ ∞ 𝔼[X ] = xfX(x)dx −∞

对于连续型随机变量。这些公式虽然可能涉及无限和与积分,但其基本含义很简单:𝔼[X] 代表 X 的平均结果,按其底层概率分布加权。

根据大数法则,期望值也描述了一个长期的平均值:如果独立同分布的随机变量 X[1], X[2], … 描述了一个重复实验的结果——比如说,在扑克中下注——那么样本平均值将收敛到联合期望值,即,

 1 ∑n lim -- Xi = 𝔼[X1 ] n→ ∞ n i=1

以概率 1 成立。从某种意义上说,大数法则让你能够瞥见未来,看看如果你做出相同的选择会发生什么。在扑克游戏中,如果你只做期望值为正的下注,长期下来你将会赢。

在机器学习中,LLN 同样起着至关重要的作用。查看均方误差

 -1∑n 2 n MSE (x,y ) = n (f(xi)− yi) , x,y ∈ ℝ i=1

再次强调。如果样本数量(n)达到百万级,计算这个总和的梯度就变得不可行。然而,均方误差是预测误差的样本平均值;因此,采样较少的样本就足够了。这就是随机梯度的核心原理,它使得大规模机器学习变得可行。

本章结束了我们的旅程。然而,还有很多东西可以学习;我可能会一直写这本书,直到时间的尽头。不幸的是,我们必须在某个地方停下。现在,与其总结书中的所有内容,不如谈谈最重要的信息:学习永无止境。

这是一个螺旋式上升的过程,你不断从更高的视角遇见熟悉的景象。如果你继续前行,你会明白我在说什么。

如果你过着充满智力挑战的生活,你也会发现知识就像是保持一打漏水的杯子满满的水。如果你的注意力从其中一个杯子移开,它会比你想象的更快地空掉。换句话说,如果你不使用它,你会失去它。这是完全正常的。好消息是,如果你已经打下了坚实的基础,重新填充杯子是很快就能做到的。有时,只是快速浏览一下你很久以前读过的一本书的页面就能解决问题。

这就是我知道这不是告别的原因。如果你觉得这本书有用,并继续深入机器学习的世界,我们将以概率 1 再次相遇。你只需要继续前行。

20.9 问题

问题 1. 设 X,Y : Ω →ℝ 为两个随机变量。

(a) 证明如果 X ≥ 0,那么 𝔼[X] ≥ 0。

(b) 证明如果 X ≥ Y,那么 𝔼[X] ≥ 𝔼[Y]。

问题 2. 设 X : Ω →ℝ 为一个随机变量。证明如果 Var[X] = 0,那么 X 仅取一个值。(即,X(Ω) = {X(ω) : ω ∈ Ω} 只有一个元素。)

问题 3. 设 X ∼ Geo(p) 为一个几何分布(见 19.2.3 节)的离散型随机变量。证明:

H [X] = − plogp-+-(1−-p)log(1−-p)-. p

提示:对于任意 q ∈ (0,1),∑ [k=1]^∞ kq^(k−1) = (1 − q)^(−2)。

问题 4:设 X ∼ exp(λ) 为一个指数分布的连续随机变量。证明

h [X ] = 1 − logλ.

问题 5:求指数分布的 λ 参数的最大似然估计。

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者聊天,等等。扫描二维码或访问链接加入社区。packt.link/math

PIC

第二十六章

参考文献

  1. Jaynes, E. T. (2003). 《概率论:科学的逻辑》。剑桥大学出版社,剑桥。

  2. SciPy 文档。 docs.scipy.org/doc/scipy/

  3. Shannon, C. E. (1948). 《通信的数学理论》。The Bell System Technical Journal, 27, 379–423。

  4. Bishop, C. M. (2006). 《模式识别与机器学习》(第 4 卷)。Springer。

第五部分

附录

本部分包含以下章节:

  • 附录 A,这只是逻辑

  • 附录 B,数学的结构

  • 附录 C,集合论基础

  • 附录 D,复数

附录 A

这只是逻辑

逻辑规则对数学的重要性,就如同结构规则对建筑学的重要性。—— 伯特兰·罗素

“数学是一种语言,”我的一位教授常常这么说。“学习数学从建立基本词汇开始。”

他忘记补充的一点是,数学是思维的语言。我经常被问到这个问题:做软件工程师/数据科学家/随机技术专业人员需要学数学吗?我的回答很简单。如果你在职业生涯中需要经常解决问题,那么数学对你是极其有益的。你不一定要有效地思考,但你会受益更多。

数学的学习曲线是陡峭的。你自己也经历过,这种困难可能让你无法熟悉其基础。我有个好消息:如果我们将学习数学视为学习一门外语,我们可以先从建立基本词汇开始,而不是直接深入到诗歌和小说中。正如我的教授所建议的那样。

A.1 数学逻辑 101

逻辑和清晰的思维是数学的基础。但那是什么呢?你会如何解释“逻辑”是什么?

我们的思维过程通过数学逻辑这一领域被形式化。在逻辑中,我们处理命题,也就是那些要么为真要么为假的陈述。“外面在下雨。” “人行道是湿的。”这两个都是有效的命题。

为了能够有效地推理命题,我们通常用罗马大写字母表示它们,比如

A = ”外面在下雨。” B = ”人行道是湿的。”

每个命题都有一个对应的真值,要么为真,要么为假。它们通常用 1 和 0 来表示。尽管这看起来没什么大不了的,但找出真值可能是非常困难的。想想这个命题

A = “如果一个算法的解可以在多项式时间内验证,那么它也可以在多项式时间内求解。”

这是著名的 P = NP 猜想,数学中最长期未解决的问题之一。这个命题容易理解,但解决这个问题(也就是找到对应命题的真值)一直令最聪明的大脑也束手无策。

从本质上讲,我们整个科学知识体系都包含在我们已经确定真值的命题中。那么,如何在实践中做到这一点呢?

A.2 逻辑连接词

就命题本身而言,它们不足以提供有效的推理框架。数学(以及现代科学的整个体系)是由逻辑连接词构成的小型构件形成的复杂命题的集合。每个连接词接受一个或多个命题,并转变它们的真值。

“如果外面在下雨,那么人行道就会湿。”这是两个命题通过蕴含连接词结合在一起的例子。蕴含连接词共有四种基本类型:否定、析取、联结和蕴含。我们将逐一详细讨论每一种。

否定将命题的真值翻转为相反的值。它用数学符号¬表示:如果 A 是一个命题,那么¬A 就是它的否定。连接词通过真值表来定义,真值表列举了给定输入时结果表达式的所有可能真值。用文字书写,这看起来可能很复杂,所以这里提供¬的真值表来说明这一概念。

| | | |A |¬A | |--|----| |0 | 1 | |1 | 0 | | | | |

在自然语言中表达命题时,否定通常用“not”来表示。例如,“屏幕是黑色”命题的否定是“屏幕不是黑色”。(而不是“屏幕是白色”。)

逻辑与(conjunction)等同于语法中的“和”连接词,用符号∧表示。命题 A ∧B 只有在 A 和 B 都为真时才为真。例如,当我们说“桌子已摆好,食物也准备好了”时,意味着两个部分都为真。以下是真值表:

| | | | |A-|B--|A-∧-B-| |0 | 0 | 0 | | | | | |0 | 1 | 0 | | | | | |1 | 0 | 0 | |1 | 1 | 1 | | |

析取在英语中被称为“or”,用符号∨表示。命题 A ∨B 在其中一个为真时为真:

| | | | |A-|B--|A-∨-B-| | | | | |0 | 0 | 0 | |0 | 1 | 1 | | | | | |1 | 0 | 1 | |1 | 1 | 1 | | | | |

析取是包容性的,不像我们在自然语言中常用的排他性“或”。当你说“我正在坐火车或开车旅行”时,不能两者都为真。析取连接词不是排他性的。

最后,蕴含连接词(→)形式化了从前提 A 推导出结论 B 的过程:“如果 A,那么 B。”

蕴含仅在前提为真而结论为假的情况下为假;否则为真。

| | | | |A |B |A → B | |---|--|--------| | 0 |0 | 1 | | 0 |1 | 1 | | | | | | 1 |0 | 0 | | 1 |1 | 1 | | | | |

一个例子是笛卡尔的名言:“我思故我在。”将其翻译为形式逻辑语言,就是

”我思故我在” → ”我存在”。

形式为“如果 A,那么 B”的句子称为条件句。这不仅仅是哲学问题。科学是由这样的命题组成的:“如果 X 是一个封闭系统,那么 X 的熵不能减小。”(这是热力学第二定律所述的内容。)

我们的大部分科学知识由 A →B 命题组成,科学研究等同于追求蕴含的真值。在实际问题中,我们依赖于定理(即蕴含),这些定理将我们的前提转化为结论。

A.3 命题演算

如果你觉得连接词类似于算术运算,你是对的。连接词生成命题。因此,连接词可以再次应用,形成复杂的表达式,如 ¬(A ∨B) ∧C。构建这样的表达式和推理论证称为命题演算。

就像算术运算一样,由命题和连接词组成的表达式也有恒等式。想想著名的代数恒等式

(a+ b)(a − b) = a² − b²,

这是最常用的符号表达式之一。这样的恒等式意味着我们可以用一种形式写出另一种形式。

在数学逻辑中,我们称这些为逻辑等价性。

定义 102. (逻辑等价性)

如果命题 P 和 Q 总是有相同的真值,则它们在逻辑上等价。

如果 P 和 Q 在逻辑上等价,我们写作

P ≡ Q.

为了给你一个例子,来看一下我们的第一个定理,它建立了合取连接词的逻辑等价性。

定理 140. (合取的性质)

设 A、B 和 C 为任意命题。那么,

(a) (A ∧B) ∧C ≡A ∧ (B ∧C) (结合律)

(b) A ∧B ≡B ∧A (交换律)

(c) A ∨ (B ∧C) ≡ (A ∨B) ∧ (A ∨C) (分配律)

(d) A ∧A ≡A (幂等律)

证明。通过绘制真值表来展示这些性质。我们将为 (a) 做这一步,而其余部分留给你作为练习。(我强烈建议你做这件事,因为自己完成任务是一个很好的学习机会。)

对于结合性性质,庞大的真值表

| | | | | | | | |A--|B-|C--|A-∧-B-|B--∧C--|(A-∧-B)-∧-C-|A-∧-(B-∧-C)-| |0 |0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | |0 |0 | 1 | 0 | 0 | 0 | 0 | |0 |1 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | |0 |1 | 1 | 0 | 1 | 0 | 0 | |1 |0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | |1 |0 | 1 | 0 | 0 | 0 | 0 | |1 |1 | 0 | 1 | 0 | 0 | 0 | | | | | | | | | |1 |1 | 1 | 1 | 1 | 1 | 1 | | |

提供了一个证明。

有几条备注需要说明。首先,我们应该从左到右阅读真值表的列。严格来说,我们可以省略 A∧B 和 B∧C 的列。然而,包含这些列能帮助思维更清晰。

其次,由于结合律,我们可以自由地写 A ∧B ∧C,因为运算顺序无关紧要。

最后,注意我们的第一个定理是一个前提和一个结论,通过蕴含连接词连接起来。如果我们用

P = “A, B, C 是命题,” Q = “ (A ∧ B )∧ C ≡ A ∧ (B ∧ C )。”

那么我们定理的第一部分就是命题 P →Q,我们已经通过列出真值表证明了它是正确的。这展示了我们在此构建的命题演算的巨大力量。

定理 140 有一个关于析取的类似命题。为了完整起见,下面陈述了该命题,但证明留给你作为练习。

定理 141. (析取的性质)

设 A、B 和 C 为任意命题。那么,

(a) (A ∨B) ∨C ≡A ∨ (B ∨C) (结合律)

(b) A ∨B ≡B ∨A (交换律)

(c) A ∧ (B ∨C) ≡ (A ∧B) ∨ (A ∧C) (分配律)

(d) A ∨A ≡A (幂等性)

就像算术运算一样,逻辑连接词也有优先级顺序:¬,∧,∨,→。这意味着,例如,(¬A) ∧B) ∨C 可以写成 ¬A ∧ (B ∨C)。

在我们的命题演算中,最重要的规则之一就是德摩根定律,它描述了合取和析取在否定下的行为。

定理 142. (德摩根定律)

设 A 和 B 为两个任意命题。那么,

(a) ¬(A ∧B) ≡¬A ∨¬B

(b) ¬(A ∨B) ≡¬A ∧¬B

保持成立。

证明。像往常一样,我们可以通过列出两个真值表来证明德摩根定律

| | | | | | | | |A |B | ¬A |¬B | A ∧B |¬(A ∧ B ) |¬A ∨ ¬B | |---|--|----|----|-------|----------|---------| | 0 |0 | 1 | 1 | 0 | 1 | 1 | | 0 |1 | 1 | 0 | 0 | 1 | 1 | | | | | | | | | | 1 |0 | 0 | 1 | 0 | 1 | 1 | | 1 |1 | 0 | 0 | 1 | 0 | 0 | | | | |

并且

| | | | | | | | |A--|B-|-¬A-|¬B--|-A-∨B--|¬(A-∨-B-)-|¬A-∧-¬B--| | 0 |0 | 1 | 1 | 0 | 1 | 1 | | | | | | | | | | 0 |1 | 1 | 0 | 1 | 0 | 0 | | 1 |0 | 0 | 1 | 1 | 0 | 0 | | | | | | | | | | 1 |1 | 0 | 0 | 1 | 0 | 0 | | |

来验证我们的论断。

我们到目前为止建立的命题演算是思维的数学形式化。然而,还有一个东西缺失:推理,或者正如维基百科所说的,“推理是从前提的真实性中得出结论的心理过程。”这一点通过著名的假言推理规则得到了体现。

定理 143. (假言推理)

设 A 和 B 为两个命题。如果 A 和 A →B 都成立,那么 B 也成立。

证明。让我们再看一下 → 的真值表:

| | | | |A--|B-|A--→-B--| | 0 |0 | 1 | | | | | | 0 |1 | 1 | | 1 |0 | 0 | | | | | | 1 |1 | 1 | | |

通过查看其行,我们可以看到,当 A 为真且 A →B 成立时,B 也为真,正如假言推理原则所示。

由于模态假言推理听起来非常抽象,这里有一个具体的例子。从常识上讲,我们知道“如果下雨,那么人行道是湿的”这个命题是成立的。如果我们从屋顶窗户观察到确实在下雨,我们就可以毫不犹豫地得出人行道是湿的结论,即使我们没有亲眼看到它。

在符号表示中,我们可以写成

A → B, A ⊢ B,

其中,推导符号 ⊢ 实质上可以理解为“证明”。因此,假言推理表明 A →B 和 A 证明了 B。

假言推理就是我们如何使用定理。它始终在背后支持着我们。

注 22. (反转蕴含)

这是指出最常见的逻辑谬误之一——反转蕴含关系的绝佳机会。当讨论某个话题时,参与者经常会使用错误的论点

A → B, B ⊢ A。

当然,这是不成立的。例如,考虑我们最喜欢的例子:

A = "外面在下雨",B = "人行道是湿的。"

显然,A → B 成立,但 B → A 不成立。湿滑的人行道有其他原因。例如,有人不小心把一桶水洒在上面。

A.4 变量和谓词

所以,数学是关于命题、蕴涵以及它们的真值的。我们已经看到,可以使用我们的命题演算来构造命题并推理一些相当复杂的表达式。然而,到目前为止,我们建立的语言并不适用于带有变量的命题。

例如,想想这句话

x 是一个非负实数。

因为真值依赖于 x,这并不是一个结构良好的命题。

带有变量的句子叫做谓词,我们通过强调它们对变量的依赖来表示它们;例如,

P (x) : x ≥ 0,

或者

Q (x, y) : x + y 是偶数。

每个谓词都有一个域,从中可以获取其变量。你可以将谓词 P(x) 视为一个函数,它将其域映射到集合 {0, 1},表示它的真值。(严格来说,当我们定义我们形式语言的基础时,并没有可用的函数作为工具。然而,我们不是哲学家或集合论专家,所以我们不需要关心这些细节。)

谓词定义了真集,即谓词为真的域的子集。从形式上讲,它们表示为

{xD : P(x)}, (A.1)

其中 P(x) 是一个具有定义域 D 的谓词。

翻译成英文为(A.1):“对于 D 中的所有元素 x,P(x) 为真。”

虽然我们之前没有讨论过集合,但如果你有计算机科学背景,真集可能显得很熟悉。例如,如果你曾使用过 Python 编程语言,你可能见过像这样的表达式

s = {x for x in range(1, 100) if x % 5 == 0}

一直如此。这些叫做概念化,它们受到所谓的集合构造符号的启发,如(A.1)。

A.5 存在量化和全称量化

谓词是正式化数学思维的一大进步,但我们还没有完全到达目标。举一个机器学习的例子,我们来谈谈如何找到损失函数的最小值(也就是训练模型)。

如果对于其定义域 D 中的所有其他 y,f(x) ≤ f(y) 成立,则称点 x 为函数 f(x) 的全局最小值。例如,点 x = 0 是函数 f(x) = x² 的一个最小值。

我们如何在我们的形式化语言中表达这个呢?首先,我们可以说

对于所有 y ∈ D,f (x) ≤ f(y) 为真,

其中我们固定 f(x) = x² 和 x = 0。这个句子有两个部分:对于所有 y ∈ D,以及 f(x) ≤ f(y) 为真。第二部分是一个谓词:

P (y) : f (x) ≤ f (y),

其中 y ∈ ℝ。

第二部分似乎是新的,因为我们在我们的形式语言中从未见过“对于所有”的词语。它们表达了一种关于何时谓词 P(y) 为真的量化方式。

在数学逻辑中,我们需要的两个量词是:全称量词“对所有”,用符号 ∀ 表示,以及存在量词“存在”,用符号 ∃ 表示。

例如,考虑句子“我的所有朋友都是数学家。”通过定义集合 F 为我的朋友集合,并将这个定义域上的谓词表示为

M (x) : x 是数学家,

我们可以将我们的句子形式化为

∀x ∈ F,M (x).

记住,谓词 M(x) 的定义域是 F。我们本可以省略这一点,但这样写更易于理解。

类似地,“我至少有一个朋友是数学家”可以翻译为

∃x ∈ F,M (x).

当量词后面有更复杂的命题时,我们用括号标记它的范围:

∀x ∈ F,(A (x) → (B(x) ∧C (x))).

注意到,由于 (∀x ∈F,M(x)) 和 (∃x ∈F,M(x)) 只有一个真值,它们是命题,而不是谓词!因此,量词将谓词转化为命题。像其他命题一样,逻辑联接词可以应用于它们。

在所有运算中,否定是最有趣的。为了看清楚为什么,假设我们考虑之前的例子:“我的所有朋友都是数学家。”起初,你可能会说它的否定是“我的朋友都不是数学家”,但这并不正确。想一想:我可以有数学家的朋友,只要不是所有朋友都是数学家。所以,

¬(“我的所有朋友都是数学家”) ≡ “我至少有一个不是数学家的朋友。”

换句话说(或者应该说是符号),我们有

¬(∀x ∈ F,M (x )) ≡ ∃x ∈ F,¬M (x).

也就是说,粗略地说,∀ 的否定是 ∃,而 ∃ 的否定是 ∀。

A.6 问题

问题 1. 使用真值表证明

(a) A ∨¬A 为真,

(b) 和 A ∧¬A 为假。

换句话说,A ∨¬A 是一个重言式,而 A ∧¬A 是一个矛盾式。(我们称总为真的表达式为重言式,总为假的表达式为矛盾式。)

问题 2. 定义异或运算 XOR,用 ⊕ 表示,真值表如下

| | | | |A-|B--|A-⊕-B--| |0 |0 | 0 | | | | | |0 |1 | 1 | | | | | |1 |0 | 1 | |1 |1 | 0 | | | | |

证明

(a) A ⊕B ≡ (¬A ∧B) ∨ (A ∧¬B)

(b) 和 A ⊕B ≡ (¬A ∨¬B) ∧ (A ∨B)

保持成立。

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者聊天,等等。扫描二维码或访问链接加入社区。packt.link/math

图片

附录 B

数学的结构

我们已经走过了很长的路程:我们研究了命题、逻辑联结词、谓词、量词和所有形式逻辑。这是为了能够谈论数学。然而,最终,我们希望做的是数学。

作为唯一的精确科学,数学建立在定义、定理和证明的基础上。我们精确地定义对象,对它们提出猜想,然后用数学上正确的论证证明这些猜想。你可以把数学看作是由命题、蕴涵和 modus ponens 组成的庞大建筑。如果一个定理失败,那么所有依赖于它的其他定理也都会失败。

在其他科学领域,操作方式是假设、实验和验证。然而,在数学中,实验并不足够。例如,考虑著名的费马数,即形如 F[n] := 2(2n) + 1 的数。费马曾猜测它们都是素数,因为 F[0]、F[1]、F[2]、F[3] 和 F[4] 都是素数。

在某些科学领域,五次肯定的“实验”可能足以接受假设为真。然而在数学中却不行。1732 年,欧拉证明了 F[5] = 4,294,967,297 不是素数,因为 4,294,967,297 = 641 × 6,700,417。(想象一下在 18 世纪计算这个数字,计算机时代还远未到来。)

到目前为止,我们在讨论数学逻辑时已经看到了若干定义、定理,甚至证明。现在是时候放大镜下审视它们,看看它们究竟是什么!

B.1 什么是定义?

模糊性是自然语言的缺点。例如,如何定义“热”这一概念?经过几次尝试,你会很快发现没有两个人对其有相同的定义。

在数学中,没有模糊性存在的余地。每个对象和每个属性必须被精确定义。最好通过一个好的例子来理解,而不是进行哲学思考。

定义 103.(约数)

设 b ∈ ℤ 是一个整数。我们说 a ∈ ℤ 是 b 的约数,当且仅当存在一个整数 k ∈ ℤ,使得 b = ka。

“a 是 b 的约数”这一属性用 a∣b 表示。

例如,2∣10 和 5∣10,但 7 ∤ 10。(交叉符号表示所述属性的否定。)

用我们正式语言来表达,“a 是 b 的约数”的定义可以写作:

ab :∃ k ∈ ℤ,b = k**a。(B.1)

不要让 a∣b 的符号迷惑了你;这实际上是一个伪装的谓词。我们本可以用其他方式表示 a∣b,例如:

divisor(a,b) : ∃k ∈ ℤ, b = ka.

尽管每一个数学定义都可以形式化,但我们更倾向于使用自然语言,因为它更容易理解。(至少对人类来说是这样,对计算机来说则不然。)

就像积木一样,定义是层层叠加的。

(如果你有敏锐的细节观察力,你会注意到,即使是定义 103 也建立在其他概念之上,比如数字、乘法和等式。我们没有精确定义这些概念,只是假设它们是存在的。由于我们的目标不是从零开始重建数学,因此我们会对此略过不提。)

再次强调,最好在这里看一个例子。让我们来看一下偶数和奇数!

定义 104. (偶数和奇数)

设 n ∈ℤ 是一个整数。我们说 n 是偶数,当且仅当 2∣n。

反过来,我们说 n 是奇数,当且仅当 2 ∤ n。(符号 a ∤ b 表示“a 不是 b 的约数”的否定。)

再一次,用我们正式的语言。对于整数 n ∈ℤ,谓词

even(n) : 2 | n

并且

odd(n) : 2 ∤ n

表示与定义 104 相同。

这些例子并不太吸引人,所以让我们看看更有趣的内容!

定义 105. (质数)

设 p ∈ℕ 是一个正整数。我们说 p 是质数,如果

(a)p/span>1,

(b)如果 a∣p,则 a = 1 或 a = p。

换句话说,质数除了它们自己之外没有其他整数约数。前几个质数是 2、3、5、7、11、13、17 等等。非质数整数称为合成数。

PIC

图 B.1:谓词逻辑中质数定义的分解

质数的定义可以写成:

P (p) : (p >1)∧ (∀a ∈ ℤ,(a | p → ((a = 1) ∨ (a = p))))。

这看起来可能很复杂,但我们可以将其分解成几个部分,如图 B.1 所示。

质数在我们日常生活中起着至关重要的作用!例如,许多主流的加密方法使用大质数来加密和解密信息。没有它们,你就无法安全地发起金融交易。

它们的有用性通过它们的各种性质得到保证,这些性质以定理的形式被建立起来。我们很快就会看到其中的一些,但首先,让我们谈谈定理到底是什么。

B.2 什么是定理?

所以,定义本质上是一个谓词,它的真值集由我们关心的对象组成。数学的核心就是发现涉及这些对象的真命题,通常以 A → B 的形式。考虑以下定理。

定理 144. (凸函数的全局最小值存在性)

f : [0,1] → ℝ 是一个函数。如果 f 是连续的,则存在一个 x∗ ,使得 f x∗ 处达到最小值,且该点属于区间 [0,1]

(即,对于所有 x ∈ [0,1] ,我们有 f(x∗) ≤ f(x)。)

如果你对连续性和最小值的概念不熟悉,不用担心;这不是重点。关键是,定理 144 可以写成:

∀f ∈ F,(C(f) → M(f))

其中,F 表示所有函数的集合 [0,1] →ℝ,谓词 C(f) 和 M(f) 定义如下:

 C (f ) : f 在 [0,1] 上是连续的, ∗ ∗ M (f ) : ∃x ,∀x ∈ [0,1],f (x ) ≤ f(x).

注意定理的结构:“设 x ∈A。如果 B(x),则 C(x)。” 在第一句中,我们确定了谓词 A(x) 和 B(x) 的定义域,并且在条件句“如果 B(x),则 C(x)”前放置了一个全称量词。

B.3 什么是证明?

现在我们理解了什么是定理,是时候看看证明了。我们刚刚看到,定理是正确的命题。证明是建立命题真理的推理过程。让我们来看一个例子,而不是像哲学家那样讨论!

定理 144 的证明我们还无法完成,因此让我们看看一个更简单的例子:偶数之和。

定理 145. (偶数之和)

设 n,m ∈ℤ 为两个整数。如果 n 和 m 都是偶数,那么 n + m 也是偶数。

证明。由于 n 是偶数,2∣n。根据定义 103,这意味着存在一个整数 k ∈ℤ,使得 n = 2k。

类似地,由于 m 也是偶数,存在一个整数 l ∈ ℤ,使得 m = 2l。将两者相加,我们得到

n + m = 2k + 2l = 2(k + l),

给定 n + m 确实是偶数。

如果仔细阅读上面的证明,你可能会注意到它是一系列的蕴含和模态推理。正是这两者构成了我们推理技能的支柱。所证明的结论已经铁定。

理解什么是证明是数学中最大的技能差距之一。如果你没有马上理解,别担心;这是一个深奥的概念。你最终会习惯证明的。

B.4 等价命题

数学的基本构建块是形式为 A →B 的命题;至少,这是我在本章中强调的内容。

我没有说得很准确。命题 A →B 翻译为“如果 A,那么 B”,但有时,我们知道得更多。通常,A 和 B 具有相同的真值。在自然语言中,我们通过说“当且仅当 B 时 A”来表达这一点。(尽管这种情况比简单的条件句要少得多。)

在逻辑中,我们用双条件连接词 ↔︎ 来表示这种关系,其定义为

A ↔ B ≡ (A → B) ∧ (B → A ).

“当且仅当”类型的定理称为等价命题,它们在数学中起着至关重要的作用。在证明等价命题时,我们必须同时证明 A →B 和 B →A。

为了看到一个例子,让我们回到初等几何学。正如你可能在高中学到的那样,我们可以用由两个实数构成的元组来描述平面上的几何对象。通过这种方式,几何性质可以转化为分析性质,并且我们通常可以通过简单的计算证明困难的定理。

例如,让我们讨论正交性,这是数学中最重要的概念之一。这里是平面上两个向量的正交性定义。

定义 106. (正交性)

设 a 和 b 是平面上的两个非零向量。如果它们夹角为 π∕2,则称 a 和 b 互相正交。

正交性用 ⊥ 符号表示;也就是说,a ⊥ b 意味着 a 和 b 是正交的。

为了简便起见,我们总是假设夹角在 0 和 π 之间。(π 弧度等于 180 度,但我们总是使用弧度。)

然而,测量两个任意向量之间的夹角并不像听起来那么简单。我们需要一个可操作的公式,这就是点积的用武之地。

定义 107. (平面向量的点积)

设 a = (a[1], a[2]) 和 b = (b[1], b[2]) 是平面上的两个向量。它们的点积 a ⋅ b 定义为

a ⋅ b := |a||b|cosα,

其中,α 是两个向量之间的夹角,|⋅| 表示向量的大小。

点积提供了一个关于正交性的等价定义,以“当且仅当”定理的形式表达。

定理 146\。

设 a = (a[1], a[2]) 和 b = (b[1], b[2]) 是平面上的两个非零向量。那么,当且仅当 a ⋅ b = 0 时,a 和 b 是正交的。

让我们来看一下这个等价关系的证明!

证明。我们需要证明两个命题:

(a) a ⊥ b ⇒ a ⋅ b = 0,

(b) a ⋅ b = 0 ⇒ a ⊥ b。

我们从 (a) 开始。如果 a ⊥ b,那么它们之间的夹角 α 等于 π∕2。于是,

 π- a⋅b = |a||b |cos2 = |a||b |0 = 0,

这正是我们需要展示的内容。

为了证明 (b),我们必须注意到,由于 a 和 b 都是非零向量,它们的大小 |a|、|b| 也都是非零的。因此,

a ⋅ b = |a ||b|cosα = 0

只有当 cosα = 0 时,这个式子才成立。反过来,这意味着 α = π∕2;也就是说,a ⊥ b。(回想一下,我们假设夹角 α 在 0 和 π 之间。)

所以,我们已经知道了关于定理和证明的一切。那么,如何在实际中找到证明呢?让我们来看看基本的技巧。

B.5 证明技巧

这没有捷径可走:证明定理是困难的。有些最聪明的人花了几十年,有些猜想在一个世纪后仍未解决。(也就是说,它们既未被证明也未被反驳。)

一些基本而强大的工具可以帮助人们克服困难。接下来,我们将看看三种最重要的工具:数学归纳法、反证法和对立命题原则。

B.5.1 数学归纳法证明

你如何爬一段楼梯?很简单。你先爬第一步,然后爬下一步,依此类推。

你可能会感到惊讶,但这正是我们在数学中经常使用的东西。

让我们通过一个例子来阐明这一点。

定理 147. (自然数之和)

设 n ∈ℕ 为任意整数。那么,

1 + 2 + ⋯ + n = n(n + 1) 2 (B.2)

成立。

证明。对于 n = 1 ,情况很明确:(B.2) 的左侧计算结果为 1,而右侧是

1(1-+-1) 2 = 1.

因此,我们的命题对于 n = 1 成立,这叫做基本情况。

这里就是关键步骤,即归纳步骤。我们假设 (B.2) 对给定的 n 成立;也就是说,我们有

1+ 2 + ⋅⋅⋅+ n = n(n-+-1). 2

这就是所谓的归纳假设。使用这个假设,我们将证明 (B.2) 对 n+1 也成立。换句话说,我们的目标是证明:

 (n-+-1)(n-+-2) 1+ 2 + ⋅⋅⋅+ n+ (n + 1) = 2 .

根据我们的归纳假设,我们有:

 [ ] 1 + 2+ ⋅⋅⋅+ n + (n + 1) = 1+ 2 + ⋅⋅⋅+ n + (n + 1) n(n + 1) = --------+ (n + 1). 2

继续计算,我们得到:

n(n + 1) n(n + 1) 2(n+ 1) --------+ (n + 1) = --------+ -------- 2 2 2 = n(n-+-1)+-2(n-+-1) 2 = (n-+-1)(n-+-2), 2

这就是我们需要证明的。

总结一下发生了什么,我们用谓词来表示方程 (B.2):

S(n) : 1+ 2 + ⋅⋅⋅+ n = n(n-+-1). 2

归纳证明分为两个主要步骤。首先,我们证明基本情况 S(1) 是成立的。然后,我们证明对于任意 n,蕴含式 S(n) → S(n + 1) 成立。从归纳步骤出发,这意味着 S(n) 对所有 n 都成立:这一系列的蕴含

S (1) → S (2), S (2) → S (3), S (3) → S (4), ...

结合 S(1) 和强大的模态推理(定理 143)得出 S(n) 为真。我们首先完成了第一步 S(1),然后证明我们可以从任意位置进行下一步。

归纳法并不容易理解,因此这里提供另一个例子。(它比之前的稍微复杂一些。)跟随证明过程,看看你是否能识别出归纳法的痕迹。

定理 148.(数论的基本定理)

设 n ∈ ℤ 是一个整数,并假设 n > 1。那么,n 可以唯一地表示为素数的乘积;即,存在素数 p[1], p[2], ..., p[l] 和指数 k[1], k[2], ..., k[l],使得:

n = p[1]^(k[1]) p[2]^(k[2]) ⋯ p[l]^(k[l])。(B.3)

此外,这个表示是唯一的。

例如,24 = 2³3,且 24 不能写成其他素数的乘积。

自然语言的表达掩盖了它的本质,但事实上,定理 148 可以转化为以下句子:

 [ k1 k2 kl ] ∀n ∈ ℤ (n >1) → (∃p1,...,pl,k1,...,kl ∈ ℤ,(∀i,pi 是素数 )∧(n = p1 p2 ...pl )) .

为了简单起见,我们只证明素因数分解的存在性,而不证明唯一性。

证明。(存在性)对于 n = 2,定理显然成立,因为 2 本身就是一个素数。

现在,设 n > 2,并假设 (B.3) 对所有小于或等于 n 的整数 m 都成立。(这是我们的归纳假设。)

我们的目标是证明 (B.3) 对 n + 1 也成立。

有两种可能性:要么 n + 1 是一个素数,要么是一个合成数。如果它是素数,那么我们已经完成了,因为 n + 1 本身就是 (B.3) 的形式。否则,如果 n + 1 是一个合成数,我们可以找到一个不是 1 或 n + 1 的约数:

n + 1 = ab

对于某些 a,b ∈ ℤ。由于 a,b ≤ n,我们可以应用归纳假设!展开来说,这意味着我们可以把它们写成

a = pα1...pαl, 1 l b = qβ11...qβmm,

其中 p[i],q[i] 是质数,α[i],β[i] 是指数。因此,

n + 1 = ab α α β = p11...pll q11 ...qβmm ,

这只是(B.3),不过符号多了一些。

数学中的归纳法就像是一把电动工具。它非常强大,只要适用,几乎总能完成任务。

B.5.2 反证法证明

有时候,通过假设结论为假,然后推导出矛盾,证明定理会更容易。

再次,最好看一个快速的例子。让我们重新回顾一下我们亲爱的老朋友——质数。

定理 149\。

有无限多个质数。

证明。假设质数是有限个:p[1],p[2],…,p[n]。

整数 p[1]p[2]…p[n] + 1 是质数吗?如果 p[1],p[2],…,p[n] 是所有质数,那么只需检查是否

pi ∤ p1p2...pn + 1.

这确实成立,因为根据定义,p[1]p[2]…p[n] + 1 = p[i]k + 1,其中 k 只是除 p[i] 之外的质数的乘积。

由于没有 p[i] 是 p[1]p[2]…p[n] + 1 的约数,它必须是一个质数。我们发现了一个新的质数,它不在我们的列表中!这意味着我们的假设(即质数是有限个)导致了矛盾。

因此,质数必须是无限多个。

如果你眼光敏锐,你可能已经注意到,上面的例子并不是 A →B 的形式;它只是一个简单的命题:

A = ”有无限多个质数。”

在这些情况下,证明 ¬A 为假会得到所需的结论。然而,这种技巧对于 A →B 风格的命题同样适用。(顺便说一句,定理 148 的存在部分也可以通过反证法证明;我将这个留给你作为练习。)

B.5.3 逆命题

我们将要学习的最后一个技巧是逆命题,这是一个巧妙的方法,它给经典的 A →B 风格的思维方式带来了变化。

我们应该更好地了解蕴含连接词,看看它是什么。事实证明,A →B 可以通过否定和析取来表示。

定理 150\。

设 A 和 B 为两个命题。那么,

A → B ≡ ¬A ∨ B.

证明。真值表

| | | | | |A |B |¬A |¬A ∨ B | |--|---|----|--------| |0 |0 | 1 | 1 | | | | | | |0 |1 | 1 | 1 | |1 |0 | 0 | 0 | | | | | | |1 |1 | 0 | 1 | | |

提供了一个证明。

为什么这与我们有关呢?很简单。看一下以下的推论。

推论 4\。 (逆命题原理)

设 A 和 B 为两个命题。那么,

A → B ≡ ¬B → ¬A.

证明。定理 150 说明

A → B ≡ ¬A ∨ B ≡ B ∨ ¬A ≡ ¬B → ¬A,

这就是我们需要证明的内容。

这里有一个简单的整数命题,给你一个数学例子。

定理 151\。

n ∈ ℤ 表示一个整数。如果2 ∤ n ,则4 ∤ n

证明。我们应该通过对立命题来证明这一点。因此,假设 4∣n。这意味着

n = 4k

对于某个整数 k ∈ℤ。然而,这意味着

n = 2(2k),

这表明 2∣n。由于对立命题的原理,(4∣n)→(2∣n)与(2∤ n)→(4∤ n)在逻辑上是等价的,这就是我们需要证明的内容。

对立命题不仅在数学中有用,它还是一种非常有价值的思维工具。我们来考虑一个反复出现的命题:“如果外面下雨,那么人行道是湿的。”我们知道这是对的,但这也意味着“如果人行道不湿,那么外面就没有下雨”(因为如果下雨的话,人行道应该是湿的)。

你每天都会不自觉地进行这类推理。现在你有了一个名字,可以开始有意识地应用这个模式了。

加入我们的 Discord 社区

与其他用户、机器学习专家和作者本人一起阅读这本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者交流,还有更多内容。扫描二维码或访问链接加入社区。packt.link/math

PIC

附录 C

集合论基础

换句话说,一般的集合理论其实非常基础,但如果你想成为一名数学家,你需要了解它,这里有它;读一读,吸收它,然后忘掉它。——保罗·哈尔莫斯

虽然保罗·哈尔莫斯很久以前说过上述话,但它依然非常准确。只有一个部分已经过时:集合论不仅对数学家必要,对于计算机科学家、数据科学家和软件工程师同样重要。

你可能以前听说过或学习过集合论。可能很难理解它为何对机器学习如此重要,但相信我,集合论是数学的基础。深层次来看,一切都是集合,或者是集合之间的函数。(正如我们在第九章看到的,甚至函数也可以定义为集合。)

可以将集合论与机器学习的关系比作语法与诗歌的关系。要写出优美的诗歌,一个人需要熟悉语言的规则。例如,数据点在向量空间中表示为向量,通常是通过集合的笛卡尔积构造的。(如果你不熟悉笛卡尔积,不用担心,我们很快会讲到的。)或者,要真正理解概率论,你需要熟悉事件空间,它是一些在特定操作下封闭的集合系统。

那么,集合到底是什么呢?

C.1 什么是集合?

从表面上看,集合只是一个事物的集合。我们通过列举其元素来定义集合,例如

S = {红色,绿色,♡}.

如果两个集合具有相同的元素,则这两个集合相等。给定任何元素,我们总是能判断它是否是某个集合的成员。当 A 的每个元素也是 B 的元素时,我们说 A 是 B 的子集,或者用符号表示为,

A ⊆ B.

如果 A ⊆B 且 A≠B,我们说 A 是 B 的真子集,并写作 A ⊂B。如果我们有一个集合,我们可以通过指定所有元素满足的某个属性来定义子集,例如,

偶数 = {n ∈ ℤ : n%2 = 0}.

(%表示取模运算符。)这种方法被称为集合构造符号,如果你熟悉 Python 编程语言,你可以看到它启发了列表推导式。在那里,人们会写出类似这样的代码:

even_numbers = {n for n in range(10) if n%2 == 0} 

print(even_numbers)
{0, 2, 4, 6, 8}

我们甚至可以将集合描述为其他集合的集合,比如,A 的所有子集的集合。这称为幂集,是一个如此重要的概念,值得为其单独定义。

定义 108. (幂集)

设 A 为任意集合。由以下定义的集合

2A := {B : B ⊆ A},

包含 A 的所有子集的集合称为 A 的幂集。

∅和 A 都是幂集 2^A 的元素。

不幸的是,定义集合为元素的集合并不可行。没有更多的条件,它可能导致悖论,正如著名的罗素悖论所示。(我们将在本章后面讨论这个问题。)为了避免深入集合论的困境,我们接受集合有某种正式的定义,这个定义埋藏在数学的千页大书中。与其担心这些,不如专注于我们可以用集合做些什么。

C.2 集合运算

仅用这两种方法(列出成员或使用集合构造符号)描述更复杂的集合是非常困难的。为了简化工作,我们定义了集合上的运算。

C.2.1 并集、交集、差集

最基本的运算是并集、交集和差集。你可能对这些比较熟悉,因为它们在高中时就经常遇到。即使你已经熟悉了它们,接下来还是看看正式的定义吧。

定义 109.(集合运算)

设 A 和 B 为两个集合。我们定义

(a) 它们的并集由 A ∪B := {x : x ∈A 或 x ∈B} 定义,

(b) 它们的交集由 A ∩B := {x : x ∈A 且 x ∈B} 定义,

(c) 它们的差集由 A ∖B := {x : x ∈A 且 x∈∕B} 定义。

我们可以通过文氏图轻松地可视化这些运算,如下所示。

PIC

图 C.1:通过文氏图可视化的集合运算

我们也可以用简单的英语表达集合运算。例如,A∪B 表示“A 或 B”。类似地,A ∩B 表示“A 且 B”,而 A ∖B 表示“A 但不是 B”。在谈到概率时,这些表达将有助于将事件转化为集合论的语言。

这些集合运算也有许多愉快的性质。

例如,它们在括号方面表现良好。

定理 152\。

设 A、B 和 C 为三个集合。并集操作是

(a) 结合性,即 A ∪ (B ∪C) = (A ∪B) ∪C,

(b) 交换性,即 A ∪B = B ∪A。

此外,交集运算也是结合性和交换性的。最后,

(c) 并集对交集具有分配性,即 A∪(B∩C) = (A ∪B) ∩ (A ∪C),

(d) 交集对并集具有分配性,即 A ∩ (B ∪C) = (A ∩B) ∪ (A ∩C)。

并集和交集可以为任意多个操作数定义。也就是说,如果 A[1]、A[2]、…、A[n]是集合,

A1 ∪⋅⋅⋅∪ An := (A1 ∪ ⋅⋅⋅∪ An−1) ∪An,

交集的情况类似。请注意,这是一种递归定义!由于结合性,括号的顺序无关紧要。

结合性和交换性可能看起来既抽象又微不足道。然而,并非所有运算都是如此,因此强调这些概念是值得的。如果你感兴趣,非交换性运算就在我们眼前,一个简单的例子是字符串连接。

a = /span>string/span> 
b = /span>concatenation/span> 
a + b == b + a
False

C.2.2 德·摩根定律

其中一个基本规则描述了集合差、并集和交集在集合运算中的共同行为,这些被称为德·摩根定律。

定理 153.(德摩根定律)

设 A、B 和 C 为三个集合。那么

(a) A ∖ (B ∪C) = (A ∖B) ∩ (A ∖C),

(b) A ∖ (B ∩C) = (A ∖B) ∪ (A ∖C)。

证明。为了简单起见,我们将通过维恩图来证明这一点。虽然画图并不是一种“正式”的数学证明,但这不是问题。我们在这里是为了理解事物,而不是陷入哲学讨论。

这里是该图示。

PIC

图 C.2:德摩根定律,用维恩图表示

基于此,你可以轻松看到(a)和(b)。

注意,德摩根定律可以推广到任何数量的集合。因此,对于任何Γ索引集,

A ∖ (∩γ∈Γ B γ) = ∪ γ∈Γ (A ∖B γ), A ∖ (∪γ∈Γ B γ) = ∩ γ∈Γ (A ∖B γ).

C.3 笛卡尔积

构造新集合的最基本方法之一就是笛卡尔积。

定义 110.(笛卡尔积)

设 A 和 B 为两个集合。它们的笛卡尔积 A ×B 定义为

A × B := {(a,b) : a ∈ A and b ∈ B }.

积的元素称为元组。注意,这个操作既不是结合的也不是交换的!

为了证明这一点,考虑到,例如,

{1} × {2} ⁄= {2}× {1}

以及

({1} × {2})× {3} ⁄= {1} × ({2}× {3}).

任意数量集合的笛卡尔积通过递归定义进行定义,就像我们处理并集和交集时一样。因此,如果 A[1]、A[2]、…、A[n]是集合,那么

A1 × ⋅⋅⋅× An := (A1 × ⋅⋅⋅× An− 1)× An.

在这里,元素是由多个元组组成的元组,为了避免写太多括号,我们可以将其简化为(a[1],…,a[n])。当操作数相同的时候,我们通常写 A^n 来代替 A ×⋅⋅⋅×A。

最常见的例子之一是笛卡尔平面,你可能之前见过。

PIC

图 C.3:笛卡尔平面

为了给出一个与机器学习相关的例子,看看数据通常是如何呈现的。我们将重点关注著名的鸢尾花数据集(scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html),它是! ℝ4 的一个子集。在这个数据集中,四个轴分别表示花萼长度、花萼宽度、花瓣长度和花瓣宽度。

PIC

图 C.4:鸢尾花数据集中的花萼宽度与花萼长度的散点图。来源:scikit-learn 文档

正如示例所示,笛卡尔积很有用,因为它将相关信息结合成一个数学结构。这是数学中的一个重复模式:通过更简单的构建块构建复杂的事物,并通过将结果转化为另一个构建块来抽象化细节。(就像创建复杂软件一样。)

到目前为止,我们已经看到了集合的愉快一面。然而,在数学家们首次尝试形式化集合论时,它们却带来了相当大的头疼。现在我们准备好看看原因了。

C.4 集合的基数

在集合论中,“一个集合有多少个元素?”是一个自然的问题。你可能没想到的是,这样一个看似简单的问题会把我们带入一个如此深邃的洞穴!

很快,你就会明白为什么了。“集合的大小”通过基数的概念来形式化,记作 jAj;也就是说,把集合 A 放在绝对值符号中。直观上,jAj 看起来很清晰,但让我向你保证,它并不是那么简单。

这就是数学中真正让人大吃一惊的地方。当然,对于像 {4,6,42} 这样的有限集合,我们可以自信地声称 j{4,6,42}j = 3,但 jℤj、jℚj、jℝj,甚至 jℝ²j 呢?

看这个:jℤj = jℚj,但 jℤj ≠ jℝj,且 jℝj = jℝ²j。换句话说:

  • 整数的数量与有理数的数量是“相同的”。

  • 但实数比整数“更多”。

  • 以及实数线上的“点的数量”与平面上的一样多。

看吧,我告诉过你,基数是疯狂的开始。(“相同数量”和“更多”的引号是因为我们还没有定义这些术语,至少在数学意义上没有定义。)

与数学中的多个概念一样,我们不会直接定义基数。相反,我们将定义如何比较集合的基数,然后固定一些特殊集合作为比较的基准。

定义 111. (基数比较)

设 A 和 B 是两个任意的集合。

(a) 如果存在从 A 到 B 的双射函数,我们就说 jAj = jBj。

(b) 如果存在从 A 到 B 的单射函数,我们就说 jAj ≤ jBj。

(c) 如果存在从 A 到 B 的单射但不是满射的函数,我们就说 jAj/span> jBj。

让我们通过几个例子来解析这个问题。

示例 1. 设 A = {1,2,3} 和 B = {−3.2,12.452,−5.82}。那么 jAj = jBj,因为 f : A → B,

f(1) = − 5.83, f(2) = − 3.2, f(3) = 12.452

是一个双射。

示例 2. 设 A = {0,1} 和 B = {2,3,4}。那么 jAj/span> jBj,因为 f : A → B,

f(0) = 2, f(1) = 3

是单射,但不是满射。

那些更有趣的集合呢,比如 ℕ、ℤ、ℚ、ℝ?这就是事情变得奇怪的地方。

以下结果如此重要,以至于我们将其作为定理陈述。

定理 154. (自然数集合是无限的)

设 n ∈ ℕ 为任意自然数,并定义集合 A = {1,2,…,n}。那么 jAj/span> ℕ。

证明。证明很简单,因为很容易看出,函数 f : A → ℕ,由 f(a) = a 定义,是单射但不是满射。

换句话说,ℕ 不是一个有限集合!自然数集合是我们第一个无限集合的例子,定义了可数集合的概念。

定义 112. (可数集合)

A 是一个任意集合。如果 |A | ≤ ℕ ,那么 A 称为可数集合。

我们可以通过列举元素来证明一个集合是可数的,因为每个列举都定义了一个单射映射。例如,ℤ是可数的,因为函数 f : ℕ → ℤ定义了

 ( ||| 0 if n = 0, |{ f (n ) = k if n = 2k for some k, |||| ( − k if n = 2k + 1 for some k

是一个双射。换句话说,序列

0, 1, -1, 2, -2, 3, -3, ...

是ℤ的列举。更多示例,请参见第十章。

关于可数性,有两个重要的结果:可数集合的并集和笛卡尔积仍然是可数的。

定理 155. (可数集合的并集和笛卡尔积)

令 A[1], A[2], …是可数集。

(a) A[1] × A[2]是可数的。

(b) ∪[n=1]^∞A[n]是可数的。

在无限基数中,有两个我们常遇到的:可数无限和连续统。可数无限是自然数集合的基数,记作

|ℤ| = ℵ0,

其中,ℵ是希伯来字母“阿列夫”。另一方面,连续统是实数集合的基数,记作

|ℝ| = c.

C.5 拉塞尔悖论(可选)

让我们回到我之前提到的一个观点:天真地将集合定义为事物的集合是行不通的。接下来,我们将看到为什么。准备好迎接一些令人费解的数学吧。

这是一个谜语。一位理发师是“为所有不剃自己胡子的人刮胡子,并且仅为他们刮胡子。”那么,这位理发师自己剃胡子吗?没有正确的答案:无论是“是”还是“否”,定义都暗示着不然。这就是著名的理发师悖论。它不仅仅是一个有趣的小故事;它是一个动摇数学基础的悖论。

如我们所见,集合可以由集合组成。例如,{ℕ, ℤ, ℝ}是最常用的数集的集合。我们也可以定义所有集合的集合,我们用Ω来表示它。

有了这个,我们可以使用集合构造符号来描述以下集合的集合:

S := {A ∈ Ω : A ∕∈ A }.

用简单的话来说,S 是一个包含所有不属于自身的集合的集合。尽管这很奇怪,但看起来有效。我们使用了属性“A ∕∈ A ”来过滤所有集合的集合。问题出在哪里?

首先,我们无法决定 S 是否是 S 的元素。如果 S ∈ S,那么根据定义,S∕∈S。另一方面,如果 S∕∈S,那么根据定义,S ∈ S。这确实非常奇怪。

我们可以通过分解集合构造符号来诊断问题。一般来说,它可以写作

x ∈ A : T(x),

其中 A 是某个集合,T(x)是一个属性,即关于 x 的真或假的陈述。

在定义{A ∈ Ω : A∈∕A}中,我们的抽象属性通过以下方式定义

 ( |{ true if A ∕∈ A, T(A ) := |( false otherwise.

这是完全有效的,所以问题必须出现在另一个部分:集合Ω。事实证明,所有集合的集合并不是一个集合。因此,将集合定义为事物的集合是不够的。由于集合是数学的基础,这一发现给 19 世纪末到 20 世纪初的数学发展带来了巨大的阻碍,解决这个问题花费了许多年和无数聪明的头脑。

幸运的是,作为机器学习从业者,我们不需要关心像集合论公理这样的低级细节。对我们来说,知道某个地方存在坚实的基础就足够了。(希望如此。)

加入我们的 Discord 社区

与其他用户、机器学习专家以及作者本身一起阅读本书。提出问题,为其他读者提供解决方案,通过“问我任何问题”环节与作者互动,还有更多。扫描二维码或访问链接加入社区。packt.link/math

图片

附录 D

复数

学习是一个向上的螺旋。根据我们在学习旅程中的位置,我们会不断回顾过去的知识,并从不同的角度去审视它。

复数是需要不断复习的一个主题,理解它们使我们重新评估某些我们习以为常的知识。例如,你可能曾被教导过 −1 没有平方根。然而,在熟悉了复数之后,你会发现其实有两个平方根;它们都是复数。

这里的主要例子是二次方程:

x2 + 1 = 0.

为了证明它在实数范围内没有解(或者说没有根),我们可以检查判别式 b² − 4ac = −4 小于 0,但我们也可以简单地绘制 x² + 1 的图像。

import numpy as np 
import matplotlib.pyplot as plt 

X = np.linspace(-3, 3, 1000) 
Y = X**2+1
with plt.style.context("/span>seaborn-v0_8": 
    plt.figure() 
    plt.axhline(0, color=’black’, linewidth=1) 
    plt.axvline(0, color=’black’, linewidth=1) 
    plt.plot(X, Y) 
    plt.xlim([-3, 3]) 
    plt.ylim([-5, 10]) 
    plt.xlabel("/span>x 
    plt.ylabel("/span>y 
    plt.title("/span>The graph of x² + 1" 
    plt.show()

PIC

图 D.1: x² + 1 的图像

这看似不是什么大问题,但具有实数系数的多项式方程如果没有实数解,那将是一个巨大的问题。许多重要的量都是通过多项式的根来描述的,比如矩阵的特征值。

那么,我们该如何解决这个问题呢?

在大多数情况下,我们可以将二次多项式分解为两个线性项的积。例如,

x2 − 1 = (x − 1)(x + 1),

这揭示了方程 x² − 1 = 0 的解(在这个例子中,解为 x = 1 和 x = −1)。

为了因式分解多项式 x² − 1,我们使用了恒等式:

x2 − a2 = (x − a)(x + a).

那么我们能怎么做呢?对于 x² + 1,我们可以稍微发挥一点创造力。我们可以将我们的多项式写作:

x2 + 1 = x2 − (− 1),

其中原则上我们可以使用前面的恒等式。然而,有一个问题:没有实数满足 a² = −1。

经过几个世纪,数学家们最终得出了这个解决方案,这其实很简单:我们假设有这样的一个数。在数学中,最具创造性的规则滥用往往能够带来最大的回报。所以,假设我们的虚数 i 满足:

i2 = − 1.

这样,我们得到:

 2 2 2 x + 1 = x − i = (x − i)(x + i).

换句话说,著名的二次方程 x² + 1 = 0 的解是 x = i 和 x = −i。这个 i 被称为虚数,它的发现开启了复数的世界。

那么,这些奇异的对象到底是什么?

D.1 复数的定义

让我们直接进入定义。

定义 113. (复数)

形如以下的数集:

z = a + bi, a,b ∈ ℝ,

满足 i² = −1 的数,被称为复数。我们称 a 为实部,b 为虚部,记作:

Re (z) = a, Im (z) = b.

如果 a + bi 和 c + di 是两个复数,那么我们定义加法和乘法为:

(a)

(a + bi) + (c + di) := (a+ c) + (b + d)i,

(b)

(a + bi)(c + di) := (ac − bd)+ (ad + bc)i.

复数集用 ℂ 表示。因此,我们写作:

ℂ := {a + bi : a,b ∈ ℝ}.

根据定义,加法很简单。然而,乘法看起来有些复杂。为了理解为什么这样定义,像处理两个多项式一样逐项相乘。

复数的一个重要特性是它们的模,换句话说,就是它们与 0 的距离。

定义 114.(复数的模)

设 z = a + bi 为复数。它的模定义为

 ∘ --2---2 |z| := a + b .

此外,每个复数都有一个共轭复数,正如我们稍后将看到的,它对应于关于实轴的镜像。

定义 115.(复数的共轭)

设 z = a + bi 为复数。它的共轭复数 z 定义为

z- := a − bi.

注意到 zz = |z|²。

除了代数表示法 z = a + bi,复数还有丰富的几何解释,正如我们接下来要看到的那样。

D.2 几何表示法

我们可以用不同于定义中的方式来表示复数。如果你仔细想一想,每个复数 z = a + bi 可以看作有序对(a,b)。这些可以在笛卡尔平面上作为向量来可视化。

图片

图 D.2:复数作为笛卡尔平面上的向量

复数 z = a + bi 的模|z| = √ ------- a2 + b2表示从原点到向量(a,b)的长度,而共轭复数 z = a−bi 则对应于将该点关于实轴进行反射。

这种几何视角为我们提供了一种新的代数方式来表示复数。

为了理解原因,回顾一下单位圆和平面上三角函数的关系。

图片

图 D.3:复数的几何表示

这意味着每个模为 1 的复数都可以写成 cos(φ) + isin(φ)的形式。从几何表示中,我们可以看到每个复数都由其模|z|和角度φ唯一确定。所以,我们可以将所有复数写成所谓的极坐标形式

![ ( ) z = r cos(φ )+ isin (φ ) , r ∈ 0,∞ ), φ ∈ [0,2π), 其中 r = |z|被称为模,而φ被称为相位或辐角。这种几何表示法也能帮助我们更好地理解乘法。为了更清楚地理解这一点,我们先做一些代数运算!当我们乘以复数时 ( ) z1 = r1 cos(φ1 )+ isin(φ1) , ( ) z2 = r2 cos(φ2 )+ isin(φ2)

合起来,我们得到

 [( ) z1z2 = r1r2 cos(φ1 )cos(φ2 )− sin (φ1 )sin(φ2) ( )] + i cos(φ1)sin (φ2 )+ cos(φ2)sin (φ1 ) .

你能识别出实部和虚部吗?这正是著名的三角函数加法公式。有了这些公式,我们得到了

z1z2 = r1r2(cos(φ1 + φ2 )+ isin (φ1 + φ2)).

这揭示了代数定义中不明确的许多内容。最重要的是,

  1. 乘积的模是各自模的乘积,

  2. 并且积的辐角是各个辐角的和。(模是模数的复数形式。)

换句话说,复平面中的乘法等同于一个缩放和旋转。突然间,像 i² = −1 这样的恒等式变得更加有意义:由于 i 的辐角是 π∕2(或 90 度),将其逆时针旋转 π∕2 就得到 −1。

那么,为什么我们喜欢实数呢?因为所有的多项式方程在实数范围内都有解。让我们看看!

D.3 代数基本定理

还记得我们的动机例子吗?我们引入了虚数 i 使得方程 x² + 1 = 0 有解。事实证明,复数为任何多项式方程提供了解。

让我们介绍一组具有复系数的多项式:

 { } ∑ n k ℂ [x ] := ckx : ck ∈ ℂ, n ∈ ℕ0 . k=0

类似地,ℝ[x]、ℚ[x]、ℤ[x] 和 ℕ[x] 也可以被定义。多项式的次数(或简称 deg p)是 x 的最高幂。(例如,−3x⁸ + πx 的次数为 8。)

对于给定的多项式 p(x),方程 p(x) = 0 的解称为根。从代数角度看,我们希望对于特定数集上的一组多项式,每个多项式在该数集上都有根。正如我们所看到的,这在 ℝ[x] 中不成立,因为 x² + 1 = 0 没有实数解。

然而,这对于 ℂ[x] 来说有所不同,正如代数基本定理所述。

定理 156.(代数基本定理)

每个非恒定多项式 p(x) ∈ ℂ[x](即次数 deg p ≥ 1 的多项式)至少在 ℂ 中有一个根。

虽然看起来很容易证明,但我向你保证,这并不简单。原始的代数证明非常长且复杂,尽管有更简短的版本,但它需要高级的数学分析工具。

我们可以进一步理解代数基本定理。如果 p(x) 是一个次数至少为 1 的多项式,且 x[1] 是它的根,那么

 p(x) q(x) =------ x − x1

是一个次数为 deg q = deg p − 1 的多项式。如果 q 不是恒定的,代数基本定理再次保证 q 也在 ℂ 中有一个根。最终,定理的反复应用导致 p(x) 可以写成

 p(x) = (x − x1)(x − x2) ...(x − xn),

其中 x[1], x[2], …, x[n] 是 p 的根。它们中的某些可以相同,一个给定的 x[i] 在其根中出现的次数称为其代数重数。

定义 116.(根的代数重数)

令 p(x) ∈ ℂ[x],并假设

 ∏l p(x) = (x − xi)pi, xi ⁄= xj for i ⁄= j. i=1

整数 p[i] 称为根 x[i] 的代数重数。

代数基本定理之所以被称为基本定理,是有原因的。例如,这就是为什么矩阵有特征值的原因,正如我们在第六章中学到的。

D.4 为什么复数很重要?

初看之下,复数在机器学习中似乎不那么重要。让我向你保证,事实并非如此:它们是绝对必不可少的。在本节中,我们将简要展望一下复数所能实现的功能。

例如,复数最重要的应用之一就是矩阵的特征值-特征向量对。对于给定的矩阵 A,有一些特殊的数字λ,称为特征值,以及相应的向量 v,称为特征向量,使得 Av = λv 成立。在线性变换的语言中,这意味着在λ的特征向量所张成的线性子空间中,变换 A 仅仅是一个拉伸操作。

这些工具非常强大,因为在某些条件下,特征值允许我们简化矩阵。

某些实数方阵可以写成以下形式

 ( ) λ1 0 ... 0 | | −1 || 0 λ2 ... 0 || A = U ΣU , Σ = || .. .. ... .. || ( . . . ) 0 0 ... λn

是由 A 的特征值组成的对角矩阵。猜猜是什么保证了特征值的存在:代数基本定理。

通过从实数到复数的转换,我们获得了更大的自由度和更强大的工具集。其中一个工具就是著名的傅里叶变换。为了给你一个实际的例子,让我们谈谈音频。音频数据以函数 f : ℝ →ℝ的形式出现,将时间映射到信号强度。然而,在信号处理领域,将信号理解为频率是非常重要的。每个声音都是不同频率的正弦波的叠加,量化每个频率的贡献可以揭示信号的许多信息。

import matplotlib.pyplot as plt 
import numpy as np 

def sin(freq, x): 
    return np.sin(freq*x) 

X = np.linspace(0, 2*np.pi, 1000) 
freqs = [1, 2, 3, 4, 5, 6, 7, 8, 9] 
y = {freq: sin(freq, X) for freq in freqs} 

with plt.style.context("/span>seaborn-v0_8-white: 
    plt.figure(figsize=(10, 10), dpi=100) 
    for i, freq in enumerate(freqs): 
        plt.subplot(3, 3, i+1) 
        plt.plot(X, y[freq]) 
        plt.title(f/span>frequency = {freq}" 
    plt.show()

图片

图 D.4:傅里叶变换

问题是,傅里叶变换是一个复数积分。也就是说,f 的变换是由下式定义的

 ∫ ∞ ( ) ˆf(ξ) := f (t) cos(2πtξ) − isin(2πtξ) dt. −∞

没有复数,我们无法使用这个工具。

尽管我们不会详细讨论傅里叶变换,但它们在概率论中是不可或缺的工具。应用于概率分布时,它们的收敛性特性可以轻松地研究。例如,一些版本的中心极限定理(en.wikipedia.org/wiki/Central_limit_theorem)就是通过这种方式证明的。

加入我们在 Discord 上的社区

与其他用户、机器学习专家以及作者本人一起阅读本书。提问、为其他读者提供解决方案、通过问我任何问题环节与作者互动,更多内容等你来发现。扫描二维码或访问链接加入社区。packt.link/math

图片

posted @ 2025-07-19 15:46  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报