33.1_求二阶导数
33.1 求二阶导数
现在试着求二阶导数,这里以步骤29中介绍的 为对象。在使用DeZero的情况下,我们可以将代码编写成下面这样(下面的代码其实存在一个问题)。
import numpy as np
fromdezero import Variable
deff(x): y=x\*\*4-2\*x\*\*2 returny
x=Variable(np.array(2.0)) y=f(x)
y Backwardcreate_graph=True) print(x.grad)
gx=x.grad
gx.Backward()
print(x.grad)运行结果
variable(24.0)
variable(68.0)首先通过y(reversecreate_graph=True)进行第1次反向传播。代码中指定create_graph=True,为反向传播的计算创建一个计算图。接着,程序对反向传播的计算图再次进行反向传播。由于这里求的是x的二阶导数,所以使用了gx=x.grad来取出y对x的导数。之后从这个gx导数再次进行反向传播,这样就可以求出gx对x的导数,也就是二阶导数了。
执行上面的代码,得到的一阶导数是24.0,二阶导数是68.0。根据式子 可知, 时的一阶导数为24,这与代码的运行结果一致;根据二阶导数的式子 可知, 时的二阶导数为44。遗憾的是,这与代码的运行结果不同。
68这个错误的运行结果是一阶导数(24)加上二阶导数(44)所得出的值。也就是说,新的反向传播是在Variable的导数保留了上次结果的状态下进行的,所以新的导数值中加上了上次的结果。解决这个问题的方法是在执行新的计算之前重置Variable的导数。

回顾一下,DeZero中有一个反向传播的参数,即x.backup(ret_grad=False)中的retain_grad。这个retain_grad是在步骤18中引入的,当它为False(默认值)时,中间计算的变量的导数(梯度)会被自动重置。此时只有终端变量,即用户赋值的变量持有导数。在上面的计算中,调用x.backup()后,只有x会保存它的导数。
基于以上内容,我们再来求解前面的问题。代码如下所示。
$\mathbf{x} =$ Variable(np.array(2.0))
y $=$ f(x)
y.backupcreate_graph $\equiv$ True)
print(x.grad)gx = x.grad
x.cleargrad()
gx_backward()
print(x.grad)运行结果
variable(24.0) variable(44.0)与之前不同的地方是在调用gx.backup()之前添加了x.cleargrad(),这将重置 的导数。由此便能正确进行反向传播了。实际运行上面的代码,得到的二阶导数的结果为44.0,这与通过式子计算的结果一致。