图 7-6 中引入了两个中间变量 c 和 $d ,$ ,要求 $\cdot \frac { \partial e } { \partial a }$ 和 $\frac { \partial e } { \partial b }$ ,假设输入的样例数据为 $a = 2 , \ b = 1$ ,传统正向模式的做法是根据偏导数的定义,先求出各个层级各个节点的偏导关系,如图 7-7 所示。
根据求导的链式法则,我们可以很容易知道 a 和 b 是如何影响最终结果 e的,即它们的偏导数计算过程如下:
$$
\frac { \partial e } { \partial a } = \frac { \partial e } { \partial c } \cdot \frac { \partial c } { \partial a }
$$
以及
$$
\frac { \partial e } { \partial b } = \frac { \partial e } { \partial c } \cdot \frac { \partial c } { \partial b } + \frac { \partial e } { \partial d } \cdot \frac { \partial d } { \partial b }
$$
对比计算图的结构和这两个式子,不难发现规律:计算 $\cdot \frac { \partial e } { \partial a }$ 的值,等价于计算图中从 a 到 $e$ 的路径( $( \ a \ \to c \ \to e )$ 上经过的偏导值的乘积,而计算 $\frac { \partial e } { \partial b }$ 的值就等价于计算图中从 $b$ 到 $e$ 的两条不同路径经过的偏导值乘积之和 $\ : \ " \ : b c \ :$ $e ^ { \prime \prime } + \mathrm { \Large ~ ~ } b d e ^ { \prime \prime } ,$ )。这一点并非巧合,通过计算图做自动微分其实就是对计算图路径上的偏导值求和,这是计算机求导的算法原理。
一般性地描述这个过程:对顶层节点 $p$ 和底层节点 $q$ ,要求导数 $\cdot { \frac { \partial p } { \partial q } }$ ,便需找到从节点 $q$ 到节点 $p$ 的所有路径,并且对每条路径求得该路径上的所有偏导数,把它们相乘,再将所有路径的乘积累加起来,就能得到 $\frac { \partial p } { \partial q }$ 的值。这种导数计算方法应用在许多问题中的效果都不错,但应用在神经网络上就有一个明显的缺陷:运算冗余,计算图中大量的节点被重复访问了。比如在我们的例子里,计算图的两条路径“ $\mathbf { \Phi } ^ { \cdot } a \mathbf { \Phi } \xrightarrow { } c \mathbf { \Phi } \xrightarrow { \cdot > } e ^ { \mathbf { \Phi } ^ { \prime \prime } }$ 和 $^ { } b c e ^ { \ ' }$ 都经过了公共的路径 $^ { 6 6 } c > e ^ { \prime \prime }$ 。对于例子中的计算图而言这点冗余算不了什么,但对于权值数量动辄百万乃至上亿的神经网络来说,这样的重复路径的排列组合会出现数百亿、数千亿条,所面临的计算量是完全不能承受的。
254
第三部分 第三波高潮