2.3. 自动求梯度
在深度学习中,我们经常需要对函数求梯度(gradient)。本节将介绍如何使用MXNet提供的autograd
模块来自动求梯度。如果对本节中的数学概念(如梯度)不是很熟悉,可以参阅附录中“数学基础”一节。
- In [1]:
- from mxnet import autograd, nd
2.3.1. 简单例子
我们先看一个简单例子:对函数
求关于列向量
的梯度。我们先创建变量x
,并赋初值。
- In [2]:
- x = nd.arange(4).reshape((4, 1))
- x
- Out[2]:
- [[0.]
- [1.]
- [2.]
- [3.]]
- <NDArray 4x1 @cpu(0)>
为了求有关变量x
的梯度,我们需要先调用attach_grad
函数来申请存储梯度所需要的内存。
- In [3]:
- x.attach_grad()
下面定义有关变量x
的函数。为了减少计算和内存开销,默认条件下MXNet不会记录用于求梯度的计算。我们需要调用record
函数来要求MXNet记录与求梯度有关的计算。
- In [4]:
- with autograd.record():
- y = 2 * nd.dot(x.T, x)
由于x
的形状为(4,1),y
是一个标量。接下来我们可以通过调用backward
函数自动求梯度。需要注意的是,如果y
不是一个标量,MXNet将默认先对y
中元素求和得到新的变量,再求该变量有关x
的梯度。
- In [5]:
- y.backward()
函数
关于 的梯度应为 。现在我们来验证一下求出来的梯度是正确的。
- In [6]:
- assert (x.grad - 4 * x).norm().asscalar() == 0
- x.grad
- Out[6]:
- [[ 0.]
- [ 4.]
- [ 8.]
- [12.]]
- <NDArray 4x1 @cpu(0)>
2.3.2. 训练模式和预测模式
从上面可以看出,在调用record
函数后,MXNet会记录并计算梯度。此外,默认情况下autograd
还会将运行模式从预测模式转为训练模式。这可以通过调用is_training
函数来查看。
- In [7]:
- print(autograd.is_training())
- with autograd.record():
- print(autograd.is_training())
- False
- True
在有些情况下,同一个模型在训练模式和预测模式下的行为并不相同。我们会在后面的章节详细介绍这些区别。
2.3.3. 对Python控制流求梯度
使用MXNet的一个便利之处是,即使函数的计算图包含了Python的控制流(如条件和循环控制),我们也有可能对变量求梯度。
考虑下面程序,其中包含Python的条件和循环控制。需要强调的是,这里循环(while循环)迭代的次数和条件判断(if语句)的执行都取决于输入a
的值。
- In [8]:
- def f(a):
- b = a * 2
- while b.norm().asscalar() < 1000:
- b = b * 2
- if b.sum().asscalar() > 0:
- c = b
- else:
- c = 100 * b
- return c
我们像之前一样使用record
函数记录计算,并调用backward
函数求梯度。
- In [9]:
- a = nd.random.normal(shape=1)
- a.attach_grad()
- with autograd.record():
- c = f(a)
- c.backward()
我们来分析一下上面定义的f
函数。事实上,给定任意输入a
,其输出必然是f(a) = x * a
的形式,其中标量系数x
的值取决于输入a
。由于c = f(a)
有关a
的梯度为x
,且值为c / a
,我们可以像下面这样验证对本例中控制流求梯度的结果的正确性。
- In [10]:
- a.grad == c / a
- Out[10]:
- [1.]
- <NDArray 1 @cpu(0)>
2.3.4. 小结
- MXNet提供
autograd
模块来自动化求导过程。 - MXNet的
autograd
模块可以对一般的命令式程序进行求导。 - MXNet的运行模式包括训练模式和预测模式。我们可以通过
autograd.is_training()
来判断运行模式。
2.3.5. 练习
- 在本节对控制流求梯度的例子中,把变量
a
改成一个随机向量或矩阵。此时计算结果c
不再是标量,运行结果将有何变化?该如何分析该结果? - 重新设计一个对控制流求梯度的例子。运行并分析结果。