7.1_为反向传播的自动化创造条件
7.1 为反向传播的自动化创造条件
在实现反向传播的自动化之前,我们先思考一下变量和函数之间的关系。首先从函数的角度来考虑,即思考“从函数的角度如何看待变量”。从函数的角度来看,变量是以输入和输出的形式存在的。如图7-2左图所示,函数的变量包括“输入变量”(input)和“输出变量”(output)(图中的虚线表示引用)。

图7-2 从函数的角度来看其与变量的关系(左图)和从变量的角度来看其与函数的关系(右图)

那么从变量的角度来看,函数是什么样的呢?这里要强调的是变量是由函数“创造”的。也就是说,函数是变量的“父母”,是creator(创造者)。如果变量没有作为创造者的函数,我们就可以认为它是由非函数创造的,比如用户给出的变量。
下面在代码中实现图7-2所示的函数和变量之间的“连接”。我们让这个“连接”在执行普通计算(正向传播)的那一刻创建。为此,先在Variable类中添加以下代码。
steps/step07.py
class Variable: def__init__(self,data): self.data $\equiv$ data self.grad $=$ None self creator $\equiv$ None def setCreator(self,func): self creator $\equiv$ func上面的代码添加了一个名为creator的实例变量,之后添加了用于设置creator的set creator方法。下面我们在Function类中添加以下代码。
steps/step07.py
class Function: def__call__(self,input): x $=$ input.data y $=$ self.forward(x) output $=$ Variable(y) output.setCreator(self)#让输出变量保存创造者信息 self.output $\equiv$ input #也保存输出变量 returnoutput上面的代码通过正向传播的计算,创建了一个名为output的Variable实例。对于创建的output变量,代码让它保存了“我(函数本身)是创造者”的信息。这是动态建立“连接”这一机制的核心。为了兼顾下一个步骤,这里将输出
设置为实例变量output。

DeZero 的动态计算图的原理是在执行实际的计算时,在变量这个“箱子”里记录它的“连接”。Chainer 和 PyTorch 也采用了类似的机制。
这样一来,Variable和Function之间就有了“连接”,我们就可以反向遍历计算图了。具体的实现代码如下所示。
A Square()
B = Exp()
C = Square()
Variable(np.array(0.5))
a = A(x)
b = B(a)
# 反向遍历计算图的节点
assert y creator == C
assert y creator_input == b
assert y creator_inputCreator == B
assert y creator_inputCreator_input == a
assert y creator_inputCreator_inputCreator == A
assert y creator_inputCreator_inputCreator_input == x首先介绍一下assert(断言)语句,它的用法是assert...。如果这里的...不为True,就会抛出异常。因此可以使用assert语句来检查条件是否得到满足。上面的代码在运行时没有发生任何问题(没有抛出异常),这意味着assert语句的所有条件都得到了满足。
上面的代码通过Variable实例变量creator找到前一个Function,然后通过Function的实例变量input找到前一个Variable。它们的连接方式如图7-3所示。

图7-3 以y为起点反向遍历计算图
如图7-3所示,计算图是由函数和变量之间的“连接”构建而成的。更重要的是,这个“连接”是在计算实际发生的时候(数据在正向传播中流转的时候)形成的。变量和函数连接的这个特征就是Define-by-Run。换言之,“连接”是通过数据的流转建立起来的。
图7-3这种带有“连接”的数据结构叫作连接节点。节点是构成图的一个元素,连接则代表对另一个节点的引用。也就是说,我们用了一个叫作“连接节点”的数据结构来表示计算图。