22.5. 积分¶ 在 SageMaker Studio Lab 中打开 Notebook
微分学仅占传统微积分教学内容的一半。另一个支柱,积分学,起初看起来是一个相当不相干的问题:“这条曲线下方的面积是多少?”虽然表面上看似无关,但积分与微分通过所谓的微积分基本定理紧密地联系在一起。
在本书中讨论的机器学习层面,我们不需要对积分有深刻的理解。不过,我们将提供一个简要的介绍,为我们以后可能遇到的任何进一步应用奠定基础。
22.5.1. 几何解释¶
假设我们有一个函数 \(f(x)\)。为简单起见,我们假设 \(f(x)\) 是非负的(永远不会取小于零的值)。我们想要理解的是:\(f(x)\) 和 \(x\) 轴之间包含的面积是多少?
%matplotlib inline
import torch
from IPython import display
from mpl_toolkits import mplot3d
from d2l import torch as d2l
x = torch.arange(-2, 2, 0.01)
f = torch.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.tolist(), f.tolist())
d2l.plt.show()
%matplotlib inline
from IPython import display
from mpl_toolkits import mplot3d
from mxnet import np, npx
from d2l import mxnet as d2l
npx.set_np()
x = np.arange(-2, 2, 0.01)
f = np.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.tolist(), f.tolist())
d2l.plt.show()
[22:02:47] ../src/storage/storage.cc:196: Using Pooled (Naive) StorageManager for CPU
%matplotlib inline
import tensorflow as tf
from IPython import display
from mpl_toolkits import mplot3d
from d2l import tensorflow as d2l
x = tf.range(-2, 2, 0.01)
f = tf.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.numpy(), f.numpy())
d2l.plt.show()
在大多数情况下,这个面积是无限的或未定义的(考虑 \(f(x) = x^{2}\) 下的面积),所以人们通常会讨论一对端点之间的面积,比如 \(a\) 和 \(b\)。
x = torch.arange(-2, 2, 0.01)
f = torch.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.tolist()[50:250], f.tolist()[50:250])
d2l.plt.show()
x = np.arange(-2, 2, 0.01)
f = np.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.tolist()[50:250], f.tolist()[50:250])
d2l.plt.show()
x = tf.range(-2, 2, 0.01)
f = tf.exp(-x**2)
d2l.set_figsize()
d2l.plt.plot(x, f, color='black')
d2l.plt.fill_between(x.numpy()[50:250], f.numpy()[50:250])
d2l.plt.show()
我们将用下面的积分符号表示这个面积
内部变量是一个哑变量,就像 \(\sum\) 中求和的索引一样,所以这可以用任何我们喜欢的内部值等价地写出
有一种传统的方法来尝试理解我们如何近似这类积分:我们可以想象将 \(a\) 和 \(b\) 之间的区域切割成 \(N\) 个垂直切片。如果 \(N\) 很大,我们可以用一个矩形来近似每个切片的面积,然后将这些面积相加得到曲线下的总面积。让我们来看一个用代码实现这个过程的例子。我们将在后面的章节中看到如何得到真实值。
epsilon = 0.05
a = 0
b = 2
x = torch.arange(a, b, epsilon)
f = x / (1 + x**2)
approx = torch.sum(epsilon*f)
true = torch.log(torch.tensor([5.])) / 2
d2l.set_figsize()
d2l.plt.bar(x, f, width=epsilon, align='edge')
d2l.plt.plot(x, f, color='black')
d2l.plt.ylim([0, 1])
d2l.plt.show()
f'approximation: {approx}, truth: {true}'
'approximation: 0.7944855690002441, truth: tensor([0.8047])'
epsilon = 0.05
a = 0
b = 2
x = np.arange(a, b, epsilon)
f = x / (1 + x**2)
approx = np.sum(epsilon*f)
true = np.log(2) / 2
d2l.set_figsize()
d2l.plt.bar(x.asnumpy(), f.asnumpy(), width=epsilon, align='edge')
d2l.plt.plot(x, f, color='black')
d2l.plt.ylim([0, 1])
d2l.plt.show()
f'approximation: {approx}, truth: {true}'
'approximation: 0.7944855690002441, truth: 0.34657359027997264'
epsilon = 0.05
a = 0
b = 2
x = tf.range(a, b, epsilon)
f = x / (1 + x**2)
approx = tf.reduce_sum(epsilon*f)
true = tf.math.log(tf.constant([5.])) / 2
d2l.set_figsize()
d2l.plt.bar(x, f, width=epsilon, align='edge')
d2l.plt.plot(x, f, color='black')
d2l.plt.ylim([0, 1])
d2l.plt.show()
f'approximation: {approx}, truth: {true}'
'approximation: 0.7944855690002441, truth: [0.804719]'
问题在于,虽然可以通过数值方法做到这一点,但我们只能对最简单的函数(例如下面这个)用这种方法进行解析求解
任何稍微复杂一点的,比如我们上面代码中的例子
都超出了我们能用这种直接方法解决的范围。
我们将采取一种不同的方法。我们将直观地使用面积的概念,并学习用于求解积分的主要计算工具:微积分基本定理。这将是我们研究积分的基础。
22.5.2. 微积分基本定理¶
为了更深入地探讨积分理论,我们引入一个函数
这个函数度量了从 \(0\) 到 \(x\) 的面积,取决于我们如何改变 \(x\)。注意到这已经满足了我们的所有需求,因为
这是对以下事实的数学编码:我们可以测量到远端点的面积,然后减去到近端点的面积,如 图 22.5.1 所示。
图 22.5.1 可视化为什么我们可以将计算两点之间曲线下面积的问题,简化为计算某点左侧面积的问题。¶
因此,我们可以通过确定 \(F(x)\) 是什么来计算任何区间上的积分。
为此,让我们考虑一个实验。像我们在微积分中经常做的那样,让我们想象一下当我们把值移动一个微小的量时会发生什么。根据上面的评论,我们知道
这告诉我们,函数的变化量是一个函数在一个微小薄片下的面积。
在这一点上,我们进行一个近似。如果我们看这样一个微小的面积薄片,它看起来接近一个矩形面积,其高度是 \(f(x)\) 的值,底宽是 \(\epsilon\)。实际上,可以证明当 \(\epsilon \rightarrow 0\) 时,这个近似会越来越好。因此我们可以得出结论
然而,我们现在可以注意到:这正是我们在计算 \(F\) 的导数时期望的模式!因此我们看到了一个相当令人惊讶的事实
这就是微积分基本定理。我们可以把它写成展开形式
它把找面积这个概念(*先验地*相当困难),简化为了一个关于导数的陈述(一个我们理解得更透彻的东西)。我们必须做的最后一点评论是,这并没有确切地告诉我们 \(F(x)\) 是什么。事实上,对于任何 \(C\),\(F(x) + C\) 都有相同的导数。这是积分理论中的一个常态。幸运的是,注意到在处理定积分时,常数会消掉,因此与结果无关。
这可能听起来像是抽象的废话,但让我们花点时间来体会一下,它给了我们一个全新的视角来计算积分。我们的目标不再是做某种切分求和的过程来试图恢复面积,相反,我们只需要找到一个函数,它的导数是我们已有的函数!这太不可思议了,因为我们现在可以通过反转 22.3.2节 中的表格来列出许多相当困难的积分。例如,我们知道 \(x^{n}\) 的导数是 \(nx^{n-1}\)。因此,我们可以使用基本定理 (22.5.10) 说
同样,我们知道 \(e^{x}\) 的导数是它本身,所以这意味着
通过这种方式,我们可以自由地利用微分学的思想来发展整个积分理论。每一条积分规则都源于这一个事实。
22.5.3. 换元法¶
和微分一样,有许多规则可以使积分的计算更容易处理。事实上,微分学的每一条规则(如乘法法则、加法法则和链式法则)都有一个相应的积分规则(分别是分部积分法、积分的线性和换元法)。在本节中,我们将深入探讨可以说是列表中最重要的:换元法。
首先,假设我们有一个函数本身就是一个积分
假设我们想知道这个函数与另一个函数复合得到 \(F(u(x))\) 时,它看起来是什么样的。根据链式法则,我们知道
我们可以像上面那样,通过使用基本定理 (22.5.10) 将其转化为一个关于积分的陈述。这给出了
回想一下 \(F\) 本身是一个积分,这表明左手边可以改写为
同样,回想一下 \(F\) 是一个积分,这使我们能够使用基本定理 (22.5.10) 认识到 \(\frac{dF}{dx} = f\),因此我们可以得出结论
这就是换元法公式。
为了更直观的推导,考虑当我们对 \(f(u(x))\) 在 \(x\) 和 \(x+\epsilon\) 之间进行积分时会发生什么。对于一个小的 \(\epsilon\),这个积分大约是 \(\epsilon f(u(x))\),即相关矩形的面积。现在,让我们将其与 \(f(y)\) 从 \(u(x)\) 到 \(u(x+\epsilon)\) 的积分进行比较。我们知道 \(u(x+\epsilon) \approx u(x) + \epsilon \frac{du}{dx}(x)\),所以这个矩形的面积大约是 \(\epsilon \frac{du}{dx}(x)f(u(x))\)。因此,要使这两个矩形的面积相等,我们需要将第一个矩形乘以 \(\frac{du}{dx}(x)\),如 图 22.5.2 所示。
图 22.5.2 可视化在变量变换下单薄矩形的变换。¶
这告诉我们
这是针对单个小矩形表达的换元法公式。
如果 \(u(x)\) 和 \(f(x)\) 选择得当,这可以让我们计算极其复杂的积分。例如,如果我们甚至选择 \(f(y) = 1\) 和 \(u(x) = e^{-x^{2}}\)(这意味着 \(\frac{du}{dx}(x) = -2xe^{-x^{2}}\)),这可以表明
因此通过重新整理得到
22.5.4. 关于符号约定的评论¶
敏锐的读者会注意到上面计算中的一些奇怪之处。即,像这样的计算
可以产生负数。当考虑到面积时,看到一个负值可能会觉得奇怪,因此有必要深入了解一下约定是什么。
数学家采用有向面积的概念。这体现在两个方面。首先,如果我们考虑一个函数 \(f(x)\),它有时小于零,那么面积也将是负的。例如
同样,从右到左而非从左到右的积分也被视为负面积
标准面积(从左到右的正函数的面积)总是正的。任何通过翻转得到的东西(比如绕 \(x\) 轴翻转得到一个负数的积分,或者绕 \(y\) 轴翻转得到一个顺序错误的积分)都会产生一个负面积。而且,翻转两次会得到一对负号,它们会抵消掉,从而得到正面积
如果这个讨论听起来很熟悉,那就对了!在 22.1节 中,我们讨论了行列式如何以类似的方式表示有向面积。
22.5.5. 多重积分¶
在某些情况下,我们需要在更高维度下工作。例如,假设我们有一个两个变量的函数,比如 \(f(x, y)\),我们想知道当 \(x\) 在 \([a, b]\) 范围内,\(y\) 在 \([c, d]\) 范围内时,\(f\) 下方的体积是多少。
# Construct grid and compute function
x, y = torch.meshgrid(torch.linspace(-2, 2, 101), torch.linspace(-2, 2, 101))
z = torch.exp(- x**2 - y**2)
# Plot function
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y')
d2l.plt.xticks([-2, -1, 0, 1, 2])
d2l.plt.yticks([-2, -1, 0, 1, 2])
d2l.set_figsize()
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_zlim(0, 1)
ax.dist = 12
# Construct grid and compute function
x, y = np.meshgrid(np.linspace(-2, 2, 101), np.linspace(-2, 2, 101),
indexing='ij')
z = np.exp(- x**2 - y**2)
# Plot function
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x.asnumpy(), y.asnumpy(), z.asnumpy())
d2l.plt.xlabel('x')
d2l.plt.ylabel('y')
d2l.plt.xticks([-2, -1, 0, 1, 2])
d2l.plt.yticks([-2, -1, 0, 1, 2])
d2l.set_figsize()
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_zlim(0, 1)
ax.dist = 12
# Construct grid and compute function
x, y = tf.meshgrid(tf.linspace(-2., 2., 101), tf.linspace(-2., 2., 101))
z = tf.exp(- x**2 - y**2)
# Plot function
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y')
d2l.plt.xticks([-2, -1, 0, 1, 2])
d2l.plt.yticks([-2, -1, 0, 1, 2])
d2l.set_figsize()
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_zlim(0, 1)
ax.dist = 12
我们写成
假设我们希望计算这个积分。我的主张是,我们可以通过先迭代计算 \(x\) 的积分,然后再转移到 \(y\) 的积分来做到这一点,也就是说
让我们看看为什么会这样。
考虑上图,我们将函数分割成 \(\epsilon \times \epsilon\) 的方块,我们将用整数坐标 \(i, j\) 来索引它们。在这种情况下,我们的积分近似为
一旦我们离散化了问题,我们可以按任何我们喜欢的顺序将这些方块上的值相加,而不用担心改变值。这在 图 22.5.3 中有所说明。特别是,我们可以说
图 22.5.3 说明如何将多个方块上的和分解为先对列求和(1),然后将列的和加在一起(2)。¶
内部的和恰好是积分的离散化
最后,注意到如果我们结合这两个表达式,我们会得到
因此,把它们放在一起,我们有
请注意,一旦离散化,我们所做的只是重新排列我们添加一列数字的顺序。这可能让它看起来没什么,然而这个结果(称为富比尼定理)并不总是正确的!对于机器学习中遇到的数学类型(连续函数),不用担心,但可以构造出它失败的例子(例如,在矩形 \([0,2]\times[0,1]\) 上的函数 \(f(x, y) = xy(x^2-y^2)/(x^2+y^2)^3\))。
注意,选择先对 \(x\) 进行积分,然后再对 \(y\) 进行积分是任意的。我们同样可以选择先对 \(y\) 进行积分,然后再对 \(x\) 进行积分,得到
通常,我们会浓缩成向量表示法,并说对于 \(U = [a, b]\times [c, d]\),这就是
22.5.6. 多重积分中的换元法¶
与 (22.5.18) 中的单变量一样,在高维积分中换元的能力是一个关键工具。让我们不加推导地总结一下结果。
我们需要一个函数来重新参数化我们的积分域。我们可以将其设为 \(\phi : \mathbb{R}^n \rightarrow \mathbb{R}^n\),即任何输入 \(n\) 个实变量并返回另外 \(n\) 个变量的函数。为了保持表达式简洁,我们将假设 \(\phi\) 是单射的,也就是说它永远不会自身重叠(\(\phi(\mathbf{x}) = \phi(\mathbf{y}) \implies \mathbf{x} = \mathbf{y}\))。
在这种情况下,我们可以说
其中 \(D\phi\) 是 \(\phi\) 的雅可比矩阵,它是 \(\boldsymbol{\phi} = (\phi_1(x_1, \ldots, x_n), \ldots, \phi_n(x_1, \ldots, x_n))\) 的偏导数矩阵,
仔细观察,我们发现这与单变量链式法则 (22.5.18) 类似,只是我们用 \(\left|\det(D\phi(\mathbf{x}))\right|\) 替换了 \(\frac{du}{dx}(x)\) 这一项。让我们看看如何解释这一项。回想一下,\(\frac{du}{dx}(x)\) 这一项的存在是为了说明我们通过应用 \(u\) 拉伸了 \(x\) 轴多少。在更高维度中的相同过程是确定我们通过应用 \(\boldsymbol{\phi}\) 拉伸了一个小正方形(或小*超立方体*)的面积(或体积、或超体积)多少。如果 \(\boldsymbol{\phi}\) 是乘以一个矩阵,那么我们已经知道行列式如何给出答案。
通过一些工作,可以证明雅可比矩阵提供了对一个点处多变量函数 \(\boldsymbol{\phi}\) 的最佳矩阵近似,就像我们可以用导数和梯度来近似直线或平面一样。因此,雅可比矩阵的行列式精确地反映了我们在一个维度中识别出的缩放因子。
要填补这些细节需要一些工作,所以如果现在还不清楚也不用担心。让我们至少看一个我们稍后会用到的例子。考虑积分
直接处理这个积分不会有任何结果,但如果我们进行变量替换,我们可以取得重大进展。如果我们令 \(\boldsymbol{\phi}(r, \theta) = (r \cos(\theta), r\sin(\theta))\)(也就是说 \(x = r \cos(\theta)\), \(y = r \sin(\theta)\)),那么我们可以应用换元法公式,看到这与以下表达式相同
其中
因此,积分为
最终的等式是根据我们在 22.5.3节 中使用的相同计算得出的。
当我们在 22.6节 中研究连续随机变量时,我们会再次遇到这个积分。
22.5.7. 小结¶
积分理论使我们能够回答关于面积或体积的问题。
微积分基本定理使我们能够利用关于导数的知识,通过观察到某个点之前的面积的导数等于被积函数在该点的值来计算面积。
高维积分可以通过迭代单变量积分来计算。