回想一下2.4 节,计算导数是我们将用于训练深度网络的所有优化算法中的关键步骤。虽然计算很简单,但手工计算可能很乏味且容易出错,而且这个问题只会随着我们的模型变得更加复杂而增长。
幸运的是,所有现代深度学习框架都通过提供自动微分(通常简称为 autograd )来解决我们的工作。当我们通过每个连续的函数传递数据时,该框架会构建一个计算图来跟踪每个值如何依赖于其他值。为了计算导数,自动微分通过应用链式法则通过该图向后工作。以这种方式应用链式法则的计算算法称为反向传播。
虽然 autograd 库在过去十年中成为热门话题,但它们的历史悠久。事实上,对 autograd 的最早引用可以追溯到半个多世纪以前(Wengert,1964 年)。现代反向传播背后的核心思想可以追溯到 1980 年的一篇博士论文 ( Speelpenning, 1980 ),并在 80 年代后期得到进一步发展 ( Griewank, 1989 )。虽然反向传播已成为计算梯度的默认方法,但它并不是唯一的选择。例如,Julia 编程语言采用前向传播 (Revels等人,2016 年). 在探索方法之前,我们先来掌握autograd这个包。
2.5.1. 一个简单的函数
假设我们有兴趣区分函数 y=2x⊤x关于列向量x. 首先,我们分配x
一个初始值。
tensor([0., 1., 2., 3.])
在我们计算梯度之前y关于 x,我们需要一个地方来存放它。通常,我们避免每次求导时都分配新内存,因为深度学习需要针对相同参数连续计算导数数千或数百万次,并且我们可能会面临内存耗尽的风险。请注意,标量值函数相对于向量的梯度x是向量值的并且具有相同的形状x.
array([0., 1., 2., 3.])
Before we calculate the gradient of y with respect to x, we need a place to store it. In general, we avoid allocating new memory every time we take a derivative because deep learning requires successively computing derivatives with respect to the same parameters thousands or millions of times, and we might risk running out of memory. Note that the gradient of a scalar-valued function with respect to a vector x is vector-valued and has the same shape as x.
array([0., 0., 0., 0.])
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
Array([0., 1., 2., 3.], dtype=float32)
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([0., 1., 2., 3.], dtype=float32)>
Before we calculate the gradient of y with respect to x, we need a place to store it. In general, we avoid allocating new memory every time we take a derivative because deep learning requires successively computing derivatives with respect to the same parameters thousands or millions of times, and we might risk running out of memory. Note that the gradient of a scalar-valued function with respect to a vector x is vector-valued and has the same shape as x.
我们现在计算我们的函数x
并将结果分配给y
。
tensor(28., grad_fn=<MulBackward0>)
我们现在可以通过调用它的方法来获取y
关于的梯度。接下来,我们可以通过的 属性访问渐变。x
backward
x
grad
tensor([ 0., 4., 8., 12.])
array(28.)
We can now take the gradient of y
with respect to x
by calling its backward
method. Next, we can access the gradient via x
’s grad
attribute.
[09:38:36] src/base.cc:49: GPU context requested, but no GPUs found.
array([ 0., 4., 8., 12.])
Array(28., dtype=float32)
We can now take the gradient of y
with respect to x
by passing through the grad
transform.
Array([ 0., 4., 8., 12.], dtype=float32)
<tf.Tensor: shape=(), dtype=float32, numpy=28.0>
We can now calculate the gradient of y
with respect to x
by calling the gradient
method.
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([