5.4 简单层的实现
本节将用 Python 实现前面的购买苹果的例子。这里,我们把要实现的计算图的乘法节点称为“乘法层”(MulLayer),加法节点称为“加法层”(AddLayer)。
下一节,我们将把构建神经网络的“层”实现为一个类。这里所说的“层”是神经网络中功能的单位。比如,负责 sigmoid 函数的
Sigmoid、负责矩阵乘积的Affine等,都以层为单位进行实现。因此,这里也以层为单位来实现乘法节点和加法节点。
5.4.1 乘法层的实现
层的实现中有两个共通的方法(接口)forward() 和 backward()。forward() 对应正向传播,backward() 对应反向传播。
现在来实现乘法层。乘法层作为 MulLayer 类,其实现过程如下所示(源代码在 ch05/layer_naive.py 中)。
class MulLayer:def __init__(self):self.x = Noneself.y = Nonedef forward(self, x, y):self.x = xself.y = yout = x * yreturn outdef backward(self, dout):dx = dout * self.y # 翻转x和ydy = dout * self.xreturn dx, dy
__init__() 中会初始化实例变量 x 和 y,它们用于保存正向传播时的输入值。forward() 接收 x 和 y 两个参数,将它们相乘后输出。backward() 将从上游传来的导数(dout)乘以正向传播的翻转值,然后传给下游。
上面就是 MulLayer 的实现。现在我们使用 MulLayer 实现前面的购买苹果的例子(2 个苹果和消费税)。上一节中我们使用计算图的正向传播和反向传播,像图 5-16 这样进行了计算。

图 5-16 购买 2 个苹果
使用这个乘法层的话,图 5-16 的正向传播可以像下面这样实现(源代码在 ch05/buy_apple.py 中)。
apple = 100apple_num = 2tax = 1.1# layermul_apple_layer = MulLayer()mul_tax_layer = MulLayer()# forwardapple_price = mul_apple_layer.forward(apple, apple_num)price = mul_tax_layer.forward(apple_price, tax)print(price) # 220
此外,关于各个变量的导数可由 backward() 求出。
# backwarddprice = 1dapple_price, dtax = mul_tax_layer.backward(dprice)dapple, dapple_num = mul_apple_layer.backward(dapple_price)print(dapple, dapple_num, dtax) # 2.2 110 200
这里,调用 backward() 的顺序与调用 forward() 的顺序相反。此外,要注意 backward() 的参数中需要输入“关于正向传播时的输出变量的导数”。比如,mul_apple_layer 乘法层在正向传播时会输出 apple_price,在反向传播时,则会将 apple_price 的导数 dapple_price 设为参数。另外,这个程序的运行结果和图 5-16 是一致的。
5.4.2 加法层的实现
接下来,我们实现加法节点的加法层,如下所示。
class AddLayer:def __init__(self):passdef forward(self, x, y):out = x + yreturn outdef backward(self, dout):dx = dout * 1dy = dout * 1return dx, dy
加法层不需要特意进行初始化,所以 __init__() 中什么也不运行(pass 语句表示“什么也不运行”)。加法层的 forward() 接收 x 和 y 两个参数,将它们相加后输出。backward() 将上游传来的导数(dout)原封不动地传递给下游。
现在,我们使用加法层和乘法层,实现图 5-17 所示的购买 2 个苹果和 3 个橘子的例子。

图 5-17 购买 2 个苹果和 3 个橘子
用 Python 实现图 5-17 的计算图的过程如下所示(源代码在 ch05/buy_apple_orange.py 中)。
apple = 100apple_num = 2orange = 150orange_num = 3tax = 1.1# layermul_apple_layer = MulLayer()mul_orange_layer = MulLayer()add_apple_orange_layer = AddLayer()mul_tax_layer = MulLayer()# forwardapple_price = mul_apple_layer.forward(apple, apple_num) #(1)orange_price = mul_orange_layer.forward(orange, orange_num) #(2)all_price = add_apple_orange_layer.forward(apple_price, orange_price) #(3)price = mul_tax_layer.forward(all_price, tax) #(4)# backwarddprice = 1dall_price, dtax = mul_tax_layer.backward(dprice) #(4)dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price) #(3)dorange, dorange_num = mul_orange_layer.backward(dorange_price) #(2)dapple, dapple_num = mul_apple_layer.backward(dapple_price) #(1)print(price) # 715print(dapple_num, dapple, dorange, dorange_num, dtax) # 110 2.2 3.3 165 650
这个实现稍微有一点长,但是每一条命令都很简单。首先,生成必要的层,以合适的顺序调用正向传播的 forward() 方法。然后,用与正向传播相反的顺序调用反向传播的 backward() 方法,就可以求出想要的导数。
综上,计算图中层的实现(这里是加法层和乘法层)非常简单,使用这些层可以进行复杂的导数计算。下面,我们来实现神经网络中使用的层。
