自动微分不同于符号微分和数值微分。符号微分面临着将计算机程序转换为单一数学表达式的困难,并且可能导致代码效率低下。数值微分(有限差分法)会在离散化过程和取消过程中引入舍入误差。这两种经典方法在计算更高导数时都存在问题,复杂性和误差会增加。最后,这两种经典方法在计算函数对许多输入的偏导数时都很慢,而这是基于梯度的优化算法所需要的。自动微分解决了所有这些问题。
block-beta
columns 5
A["f(x){...};"] space:3 B["df(x){...};"]
A--"自动微分"-->B
space:5
C space:3 D
C["y=f(x)"]--"符号微分"-->D["y'=f'(x)"]
C--"人工"-->A
D-->B
自动微分与符号微分的关系
符号微分是我们将要解开的梯度计算的下一种方法。这是一个系统的过程,将由算术运算和符号组成的表达式转换为表示其导数的表达式。这是通过将微积分的导数规则(例如求和规则)应用于闭式表达式来实现的。
实际上,符号微分是计算机手动推导表达式导数的方式。例如下面的两个函数 f 和 g,我们可以使用微积分导出其导数的表达式。
$$ \begin{gathered} g(x)=\cos (x)+2 x-e^x \\ f(g)=4 g^2 \end{gathered} $$
$$ \begin{aligned} &f(g(x))=4\left(\cos (x)+2 x-e^x\right)^2\qquad(4) \end{aligned} $$
$$ \frac{d f}{d x}=\frac{d f}{d g} \cdot \frac{d g}{d x}=8\left(\cos (x)+2 x-e^x\right) \cdot\left(-\sin (x)+2-e^x\right)\qquad(5) $$
要找到 $f(g(x))$ 输入的导数,我们只需将其插入上面的转换表达式中并对其求值即可。在实践中,我们以编程方式实现这个过程,并且所表示的变量将不仅仅是标量(例如向量、矩阵或张量)。下面是我们如何符号微分等式4得到等式 5 。
from sympy import symbols, cos, exp, diff
x = symbols("x")
fog = 4 * (cos(x) + 2 * x - exp(x)) ** 2
dfdx = diff(fog, x)
print(dfdx)
输出
4*(2*x - exp(x) + cos(x))*(-2*exp(x) - 2*sin(x) + 4)
这解决了数值微分中出现的数值不准确和不稳定的问题,因为我们有一个可以直接计算函数梯度的表达式。不过,我们仍面临限制其优化神经网络可行性的问题。
我们在符号微分中看到的主要问题是表达式膨胀。表达式膨胀导致导数表达式通过变换呈指数增长,这是系统地将导数规则应用于原始表达式的惩罚。以下面的乘法规则为例。
$$ \frac{d}{d x} f(x) g(x)=f^{\prime}(x) g(x)+g^{\prime}(x) f(x) $$
导数表达式不仅在术语上有所增长,而且在计算上也有所增长。这甚至没有考虑到 $f$ 或 $g$ 本身可以是复杂的函数 - 可能会增加更多的表达式膨胀。
当我们导出 $\frac{d f}{d x}$ 时,我们看到了一些表达式膨胀,这是一个相对简单的函数。现在想象一下,尝试对许多可能一遍又一遍地应用导数规则的复合函数执行相同的操作,对于神经网络代表许多复杂的复合函数,是极其不切实际的。
$$ \begin{gathered} f(x)=\frac{e^{w x+b}+e^{-(w x+b)}}{e^{w x+b}-e^{-(w x+b)}} \\ \frac{\partial f}{\partial w}=\frac{\left(-x e^{-b-w x}-x e^{b+w x}\right)\left(e^{-b-w x}+e^{b+w x}\right)}{\left(-e^{-b-w x}+e^{b+w x}\right)^2}+\frac{-x e^{-b-w x}+x e^{b+w x}}{-e^{-b-w x}+e^{b+w x}} \end{gathered} $$