深度学习微积分
微积分
在 2500 年前,古希腊人把一个多边形分成三角形,并把它们的面积相加,才找到计算多边形面积的方法。为了求出曲线形状(比如圆)的面积,古希腊人在这样的形状上刻内接多边形。内接多边形的等长边越多,就越接近圆。这个过程也被称为逼近法(method of exhaustion)。
事实上,逼近法就是积分(integral calculus)的起源。2000 多年后,微积分的另一支,微分(differential calculus)被发明出来。在微分学最重要的应用是优化问题,即考虑如何把事情做到最好。这种问题在深度学习中是无处不在的。
在深度学习中,我们”训练”模型,不断更新它们,使它们在看到越来越多的数据时变得越来越好。通常情况下,变得更好意味着最小化一个损失函数(loss function),即一个衡量”模型有多糟糕”这个问题的分数。最终,我们真正关心的是生成一个模型,它能够在从未见过的数据上表现良好。但”训练”模型只能将模型与我们实际能看到的数据相拟合。因此,我们可以将拟合模型的任务分解为两个关键问题:
| 概念 | 含义 |
|---|---|
| 优化(optimization) | 用模型拟合观测数据的过程 |
| 泛化(generalization) | 数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集本身的模型 |
为了帮助读者在后面的章节中更好地理解优化问题和方法,本节提供了一个非常简短的入门教程,帮助读者快速掌握深度学习中常用的微分知识。
导数和微分
我们首先讨论导数的计算,这是几乎所有深度学习优化算法的关键步骤。在深度学习中,我们通常选择对于模型参数可微的损失函数。简而言之,对于每个参数,如果我们把这个参数增加或减少一个无穷小的量,可以知道损失会以多快的速度增加或减少。
假设我们有一个函数 (f: \mathbb{R} \rightarrow \mathbb{R}),其输入和输出都是标量。如果 (f) 的导数存在,这个极限被定义为
[
f’(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h}.
]
如果 (f’(a)) 存在,则称 (f) 在 (a) 处是可微(differentiable)的。如果 (f) 在一个区间内的每个数上都是可微的,则此函数在此区间中是可微的。我们可以将上式中的导数 (f’(x)) 解释为 (f(x)) 相对于 (x) 的瞬时(instantaneous)变化率。所谓的瞬时变化率是基于 (x) 中的变化 (h),且 (h) 接近 (0)。
为了更好地解释导数,让我们做一个实验。定义 (u=f(x)=3x^2-4x) 如下:
1 | import numpy as np |
通过令 (x=1) 并让 (h) 接近 (0),(\frac{f(x+h)-f(x)}{h}) 的数值结果接近 (2)。虽然这个实验不是一个数学证明,但稍后会看到,当 (x=1) 时,导数 (u’) 是 (2)。
1 | def numerical_lim(f, x, h): |
输出:
1 | h=0.10000, numerical limit=2.30000 |
让我们熟悉一下导数的几个等价符号。给定 (y=f(x)),其中 (x) 和 (y) 分别是函数 (f) 的自变量和因变量。以下表达式是等价的:
[
f’(x) = y’ = \frac{dy}{dx} = \frac{df}{dx} = \frac{d}{dx} f(x) = Df(x) = D_x f(x),
]
其中符号 (\frac{d}{dx}) 和 (D) 是微分运算符,表示微分操作。我们可以使用以下规则来对常见函数求微分:
| 规则 | 公式 |
|---|---|
| 常数 | (DC = 0)((C) 是一个常数) |
| 幂律 | (Dx^n = nx^{n-1})((n) 是任意实数) |
| 指数 | (De^x = e^x) |
| 对数 | (D\ln(x) = 1/x) |
为了微分一个由一些常见函数组成的函数,下面的一些法则方便使用。假设函数 (f) 和 (g) 都是可微的,(C) 是一个常数,则:
常数相乘法则
[
\frac{d}{dx} [Cf(x)] = C \frac{d}{dx} f(x),
]
加法法则
[
\frac{d}{dx} [f(x) + g(x)] = \frac{d}{dx} f(x) + \frac{d}{dx} g(x),
]
乘法法则
[
\frac{d}{dx} [f(x)g(x)] = f(x) \frac{d}{dx} [g(x)] + g(x) \frac{d}{dx} [f(x)],
]
除法法则
[
\frac{d}{dx} \left[\frac{f(x)}{g(x)}\right] = \frac{g(x) \frac{d}{dx} [f(x)] - f(x) \frac{d}{dx} [g(x)]}{[g(x)]^2}.
]
现在我们可以应用上述几个法则来计算 (u’=f’(x)=3\frac{d}{dx}x^2-4\frac{d}{dx}x=6x-4)。令 (x=1),我们有 (u’=2):在这个实验中,数值结果接近 (2),这一点得到了在本节前面的实验的支持。当 (x=1) 时,此导数也是曲线 (u=f(x)) 切线的斜率。
为了对导数的这种解释进行可视化,我们将使用 matplotlib,这是一个 Python 中流行的绘图库。要配置 matplotlib 生成图形的属性,我们需要定义几个函数。
1 | %matplotlib inline |
现在我们可以绘制函数 (u=f(x)) 及其在 (x=1) 处的切线 (y=2x-3),其中系数 (2) 是切线的斜率。
1 | x = np.arange(0, 3, 0.1) |
偏导数
到目前为止,我们只讨论了仅含一个变量的函数的微分。在深度学习中,函数通常依赖于许多变量。因此,我们需要将微分的思想推广到多元函数(multivariate function)上。
设 (y = f(x_1, x_2, \ldots, x_n)) 是一个具有 (n) 个变量的函数。(y) 关于第 (i) 个参数 (x_i) 的偏导数(partial derivative)为:
[
\frac{\partial y}{\partial x_i} = \lim_{h \rightarrow 0} \frac{f(x_1, \ldots, x_{i-1}, x_i+h, x_{i+1}, \ldots, x_n) - f(x_1, \ldots, x_i, \ldots, x_n)}{h}.
]
为了计算 (\frac{\partial y}{\partial x_i}),我们可以简单地将 (x_1, \ldots, x_{i-1}, x_{i+1}, \ldots, x_n) 看作常数,并计算 (y) 关于 (x_i) 的导数。对于偏导数的表示,以下是等价的:
[
\frac{\partial y}{\partial x_i} = \frac{\partial f}{\partial x_i} = f_{x_i} = f_i = D_i f = D_{x_i} f.
]
梯度
我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。具体而言,设函数 (f:\mathbb{R}^n\rightarrow\mathbb{R}) 的输入是一个 (n) 维向量 (\mathbf{x}=[x_1,x_2,\ldots,x_n]^\top),并且输出是一个标量。函数 (f(\mathbf{x})) 相对于 (\mathbf{x}) 的梯度是一个包含 (n) 个偏导数的向量:
[
\nabla_{\mathbf{x}} f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_n}\bigg]^\top,
]
其中 (\nabla_{\mathbf{x}} f(\mathbf{x})) 通常在没有歧义时被 (\nabla f(\mathbf{x})) 取代。
假设 (\mathbf{x}) 为 (n) 维向量,在微分多元函数时经常使用以下规则:
- 对于所有 (\mathbf{A} \in \mathbb{R}^{m \times n}),都有 (\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top)
- 对于所有 (\mathbf{A} \in \mathbb{R}^{n \times m}),都有 (\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A})
- 对于所有 (\mathbf{A} \in \mathbb{R}^{n \times n}),都有 (\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x})
- (\nabla_{\mathbf{x}} |\mathbf{x} |^2 = \nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{x} = 2\mathbf{x})
同样,对于任何矩阵 (\mathbf{X}),都有 (\nabla_{\mathbf{X}} |\mathbf{X} |_F^2 = 2\mathbf{X})。正如我们之后将看到的,梯度对于设计深度学习中的优化算法有很大用处。
链式法则
然而,上面方法可能很难找到梯度。这是因为在深度学习中,多元函数通常是复合(composite)的,所以难以应用上述任何规则来微分这些函数。幸运的是,链式法则可以被用来微分复合函数。
让我们先考虑单变量函数。假设函数 (y=f(u)) 和 (u=g(x)) 都是可微的,根据链式法则:
[
\frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx}.
]
现在考虑一个更一般的场景,即函数具有任意数量的变量的情况。假设可微分函数 (y) 有变量 (u_1, u_2, \ldots, u_m),其中每个可微分函数 (u_i) 都有变量 (x_1, x_2, \ldots, x_n)。注意,(y) 是 (x_1, x_2, \ldots, x_n) 的函数。对于任意 (i = 1, 2, \ldots, n),链式法则给出:
[
\frac{\partial y}{\partial x_i} = \frac{\partial y}{\partial u_1} \frac{\partial u_1}{\partial x_i} + \frac{\partial y}{\partial u_2} \frac{\partial u_2}{\partial x_i} + \cdots + \frac{\partial y}{\partial u_m} \frac{\partial u_m}{\partial x_i}
]
小结
- 微分和积分是微积分的两个分支,前者可以应用于深度学习中的优化问题。
- 导数可以被解释为函数相对于其变量的瞬时变化率,它也是函数曲线的切线的斜率。
- 梯度是一个向量,其分量是多变量函数相对于其所有变量的偏导数。
- 链式法则可以用来微分复合函数。