



在上一步骤中, Variable 类可以作为一个“箱子”使用了。不过,现在的它还只是一个“普通箱子”,我们需要一个机制把这个“普通箱子”变为“魔法箱子”。要实现这一点,关键就在于函数。本步骤的主题是函数。
什么是函数?函数是“定义一个变量与另一个变量之间的对应关系的规则”。这个说法有些生硬,我们来看一个具体的例子。有一个计算平方的函数
。这时设
,那么变量
和
之间的关系就由函数
决定。换言之,“
是
的平方”的关系是由函数
决定的。
从这个例子可以看出,函数具有定义变量之间对应关系的作用。 图2-1 是变量与函数的关系示意图。
图2-1 变量和函数的关系示意图
图2-1
直观地展示了变量
和
与函数
之间的关系。这种用节点和箭头来表示计算的图叫作计算图。本书用圆框表示变量,用方框表示函数。
提到图,大家可能会想到柱状图、饼状图等。不过在计算机科学领域,图是由节点和边组成的数据结构。
下面我们从编程的角度思考
图2-1
表示的函数。具体来说,就是假设变量
和
是之前实现的
Variable
实例,然后以
Function
类的形式实现可以处理它们的函数
。这里有两点需要注意。
在满足这两点的基础上,将 Function 类按以下方式实现。
class Function:
def__call__(self, input):
x=input.data # 取出数据
y=x ** 2 # 实际的计算
output=Variable(y) # 作为Variable返回
return output
上面的代码实现了 __call__ 方法。 __call__ 方法接收 input 参数,这里假定传来的 input 是 Variable 实例。因此,实际数据存放在 input.data 中。取出数据后,方法会执行相应的计算(在这个例子中是求平方),然后将结果放到 Variable “箱子”里并返回。
__call__
方法是一个特殊的Python方法。定义了这个方法后,当
f=Function()
时,就可以通过编写
f(...)
来调用
__call__
方法了。
现在我们来实际使用一下 Function 类。这里将 Variable 实例的 x 输入 Function 实例的 f 中。
x=Variable(np.array(10)) f=Function() y=f(x) print(type(y)) # 使用type(),获取对象的类型 print(y.data)
<class '__main__.Variable'> 100
上面的代码把 Variable 和 Function 结合起来使用了。从运行结果可以看出, y 的类型是 Variable ,其数据存储在 y.data 中。
这里实现的 Function 类是一个“对输入值求平方”的具体函数。因此,将其命名为 Square 这样的具体名称比较合适。此外,今后我们还将增加各种函数(如 Sin 函数和 Exp 函数等)。考虑到这一点,最好把 Function 类作为基类来实现,并在这个类的内部实现所有DeZero函数都有的功能。这里,我们重新设计DeZero的函数,以满足以下两点要求。
为了满足这两点,我们将 Function 类按下面的方式实现。
steps/step02.py
class Function:
def__call__(self, input):
x=input.data
y=self.forward(x) # 具体的计算在forward方法中进行
output=Variable(y)
return output
def forward(self, x):
raise NotImplementedError()
本节实现了两个方法: __call__ 和 forward 。 __call__ 方法执行两项任务:从 Variable 中取出数据和将计算结果保存到 Variable 中。其中具体的计算是通过调用 forward 方法完成的。 forward 方法的实现会在继承类中完成。
Function
类的
forward
方法会抛出一个异常,目的是告诉使用了
Function
类的
forward
方法的人,这个方法应该通过继承来实现。
下面实现一个继承自 Function 类并对输入值进行平方的类。这个类的名字是 Square ,代码如下所示。
steps/step02.py
class Square(Function):
def forward(self, x):
return x ** 2
Square 类继承自 Function 类,所以也继承了 __call__ 方法。因此,我们只需在 forward 方法中编写具体的计算代码,就能完成 Square 类的实现。接下来使用 Square 类对 Variable 进行如下处理。
steps/step02.py
x=Variable(np.array(10)) f=Square() y=f(x) print(type(y)) print(y.data)
<class '__main__.Variable'> 100
可以看出,得到的结果和之前的一样。这样我们就完成了 步骤2 ,实现了 Variable 和 Function 类的基本功能。
我们暂时只考虑
Function
的输入和输出仅有一个变量的情况。从
步骤11
开始,我们将扩展DeZero,以支持多个变量。