购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

步骤6
手动进行反向传播

上一个步骤介绍了反向传播的机制。本步骤将扩展现有的 Variable 类和 Function 类,实现通过反向传播来求导的功能。首先是 Variable 类。

6.1 Variable类的功能扩展

下面实现支持反向传播的 Variable 类。为此,我们要扩展 Variable 类,除普通值( data )之外,增加与之对应的导数值( grad )。阴影部分是新增加的代码。

上面的代码在类中增加了一个新的实例变量 grad 。实例变量 data grad 都被设置为NumPy的多维数组( ndarray )。另外, grad 被初始化为 None ,我们要在通过反向传播实际计算导数时将其设置为求出的值。

梯度(gradient)是对包含多个变量的向量和矩阵等求导的导数。因此 Variable 类中增加了一个名为 grad 的变量, grad 是gradient的缩写。

6.2 Function类的功能扩展

接下来是 Function 类。在前面的步骤中, Function 类实现了进行普通计算的正向传播( forward 方法)的功能。在此基础上,我们新增以下两个功能。

下面的代码实现了这两个功能。

在上面的代码中, __call__ 方法将输入的 input 设置为实例变量。这样一来,当调用 backward 方法时,向函数输入的 Variable 实例就可以作为 self.input 使用。

6.3 Square类和Exp类的功能扩展

接下来实现具体函数的反向传播( backward )。首先从计算平方的 Square 类开始。由于 的导数是 ,所以这个类可以按照如下方式实现。

steps/step06.py

class Square(Function):
    def forward(self, x):
        y=x ** 2
        return y

    def backward(self, gy):
        x=self.input.data
        gx=2 * x * gy
        return gx

上面的代码增加了用于反向传播的 backward 方法。这个方法的参数 gy 是一个 ndarray 实例,它是从输出传播而来的导数。 backward 返回的结果是通过这个参数传播来的导数和“ 的导数”的乘积。这个返回结果会进一步向输入方向传播。

接下来是计算 Exp 类。由于 ,所以这个类可以按下面的方式实现。

steps/step06.py

class Exp(Function):
    def forward(self, x):
        y=np.exp(x)
        return y

    def backward(self, gy):
        x=self.input.data
        gx=np.exp(x) * gy
        return gx

6.4 反向传播的实现

这样就做好准备工作了。下面我们尝试通过反向传播对 图6-1 的计算求导。

图6-1 进行反向传播的复合函数

首先编写 图6-1 所示的正向传播的代码。

steps/step06.py

A=Square()
B=Exp()
C=Square()

x=Variable(np.array(0.5))
a=A(x)
b=B(a)
y=C(b)

接着通过反向传播计算 y 的导数。为此,我们需要按照与正向传播相反的顺序调用各函数的 backward 方法。 图6-2 是这个反向传播计算的计算图。

图6-2 反向传播的计算图

图6-2 可以看出各个函数的 backward 方法的调用顺序,也能看出应该将 backward 方法的结果赋给哪个变量的 grad 。下面是反向传播的实现。

steps/step06.py

y.grad=np.array(1.0)
b.grad=C.backward(y.grad)
a.grad=B.backward(b.grad)
x.grad=A.backward(a.grad)
print(x.grad)
运行结果
3.297442541400256

反向传播从 开始。因此,我们将输出 y 的导数设为 np.array(1.0) 。之后,只要按照 C B A 的顺序调用 backward 方法即可。这样就可以对各变量求出导数。

运行上面的代码后,得到的 x.grad 的结果是 3.297442541400256 。这就是 y x 的导数。顺带一提, 步骤4 的数值微分的结果是3.2974426293330694,这两个结果几乎一样。这说明反向传播的实现是正确的,更准确地说,这个实现大概率是正确的。

这样就完成了反向传播的实现。虽然我们得到了正确的运行结果,但是反向传播的顺序 C B A 是通过编码手动指定的。在下一个步骤,我们会把这项工作自动化。 JdGvoBtIlaB+ypBc8tbuHYZzfnlK/jpsSk1AhjMf5TmXgh9WedN6VbD2i6e1S37G

点击中间区域
呼出菜单
上一章
目录
下一章
×