反向自动微分

反向自动微分是 TensorFlow 采取的方案。它首先前馈遍历计算图(即,从输入到输出),计算出每个节点的值。然后进行第二次遍历,这次是反向遍历(即,从输出到输入),计算出所有的偏导数。图 D-3 展示了第二次遍历的过程。在第一次遍历过程中,所有节点值已被计算,输入是 x=3, y=4。你可以在每个节点底部右方看到这些值(例如,x \times x = 9)。节点已被标号,从 n_1n_7。输出节点是 n_7: f(3, 4) = n_7 = 42

D-3

这个计算关于每个连续节点的偏导数的思想逐渐地从上到下遍历图,直到到达变量节点。为实现这个,反向自动微分强烈依赖于链式法则,如公式 D-4 所示。

E_D-4

由于 n_7 是输出节点,即 f= n_7,所以 \frac{\partial f}{\partial n_7} = 1

接着到了图的 n_5 节点:当 n_5 变化时,f 会变化多少?答案是 \frac{\partial f}{\partial n_5} = \frac{\partial f}{\partial n_7} \times \frac{\partial n_7}{\partial n_5}。我们已经知道 \frac{\partial f}{\partial n_7} = 1,因此我们只需要知道 \frac{\partial n_7}{\partial n_5} 就行。因为 n_7n_5 + n_6 的和,因此可得到 \frac{\partial n_7}{\partial n_5} = 1,因此 \frac{\partial f}{\partial n_5}=1 \times 1 = 1

现在前进到 n_4:当 n_4 变化时,f 会变化多少?答案是 \frac{\partial f}{\partial n_4} = \frac{\partial f}{\partial n_5} \times \frac{\partial n_5}{\partial n_4}。由于 n_5 = n_4 \times n_2,我们可得到 \frac{\partial n_5}{\partial n_4} = n_2,所以 \frac{\partial f}{\partial n_4}= 1 \times n_2 = 4

这个遍历过程一直持续,此时我们达到图的底部。这时我们已经得到了所有偏导数在点 x=3, y=4 处的值。在这个例子里,我们得到 \frac{\partial f}{\partial x} = 24, \frac{\partial f}{\partial y} = 10。听起来很美妙!

反向自动微分是非常强大且准确的技术,尤其是当有很多输入参数和极少输出时,因为它只要求一次前馈传递加上一次反向传递,就可计算所有输出关于所有输入的偏导数。最重要的是,它可以处理任意代码定义的函数。它也可以处理那些不完全可微的函数,只要 你要求他计算的偏导数在该点处是可微的。

如果你在 TensorFlow 中实现了新算子,你想使它与现有的自动微分相兼容,那你需要提供函数,该函数用于构建一个子图,来计算关于新算子输入的偏导数。例如,假设你实现了一个计算其输入的平方的函数,平方算子 f(x)= x ^2,在这个例子中你需要提供相应的导函数 f^{'}(x)= 2x 。注意这个导函数不计算一个数值结果,而是用于构建子图,该子图后续将计算偏导结果。这是非常有用的,因为这意味着你可以计算梯度的梯度(为了计算二阶导数,或者甚至更高阶的导数)。