附录 A. 自动微分
本附录解释了 PyTorch 的自动微分 (autodiff) 功能是如何工作的,以及它与其他解决方案的比较。
假设你定义了一个函数f(x,y) =x2y+y+ 2,你需要它的偏导数∂f/∂x和∂f/∂y,通常是为了执行梯度下降(或其他优化算法)。主要选项有手动微分、有限差分逼近、正向模式自动衍射和反向模式自动衍射。PyTorch 实现了反向模式 autodiff,但要完全理解它,最好先看看其他选项。因此,让我们从手动微分开始,逐一了解它们。
手动微分
计算导数的第一种方法是拿起笔和纸,利用微积分知识推导出相应的方程。对于刚刚定义的函数f(x,y) 来说,这并不难,只需使用五条规则即可:
-
常数的导数为 0。
-
λx的导数是λ(其中λ是常数)。
-
xλ的导数是λxλ - 1,因此x2的导数是2x。
-
函数之和的导数是这些函数的导数之和。
-
函数λ次的导数是其导数的λ次。
根据这些规则,您可以推导出方程 A-1。
公式 A-1. f(x,y) 的偏导数
对于更复杂的函数,这种方法会变得非常乏味,而且还有可能出错。幸运的是,我们还有其他选择。让我们来看看有限差分近似法。
有限差分近似法
回顾 ,函数h(x) 在某点x0的导数h′(x0) 是函数在该点的斜率。更准确地说,导数的定义是,当x无限接近x0时,经过该点x0和函数上另一点x的直线斜率的极限(见公式 A-2)。
公式 A-2. 函数h(x) 在x0点的导数定义
因此,如果我们想计算f(x,y) 在x= 3 和y= 4 时对x的偏导数,可以计算f(3 +ε, 4) -f(3,4) 并将结果除以ε,同时使用一个很小的ε 值。下面的代码正是这样做的:
deff(x,y):returnx**2*y+y+2defderivative(f,x,y,x_eps,y_eps):return(f(x+x_eps,y+y_eps)-f(x,y))/(x_eps+y_eps)df_dx=derivative(f,3,4,0.00001,0)df_dy=derivative(f,3,4,0,0.00001)
不幸的是,结果并不精确(对于更复杂的函数,情况会更糟)。正确的结果分别是 24 和 10,但我们得到的却是
>>>df_dx24.000039999805264>>>df_dy10.000000000331966
请注意,要计算两个偏导数,我们至少要调用f() 三次(前面的代码中调用了四次,但可以优化)。如果有 1000 个参数,我们至少需要调用 1001 次f() 。在处理大型神经网络时,这使得有限差分近似的效率太低。
不过,这种方法的实现非常简单,是检查其他方法是否正确实现的绝佳工具。例如,如果它与你手动导出的函数不一致,那么你的函数很可能存在错误。
到目前为止,我们已经考虑了两种计算梯度的方法:手动微分法和有限差分近似法。遗憾的是,对于训练大规模神经网络来说,这两种方法都存在致命缺陷。因此,让我们从正向模式开始,转向自动梯度计算。 ...
Become an O’Reilly member and get unlimited access to this title plus top books and audiobooks from O’Reilly and nearly 200 top publishers, thousands of courses curated by job role, 150+ live events each month,
and much more.
Read now
Unlock full access