机器学习入门(11)— 反向传播的加法节点、乘法节点、加法层代码实现、乘法层代码实现

1. 加法节点

z = x + y 为对象,观察它的反向传播。z = x + y 的导数可由下式(解析性地)计算出来。
图 5-5
计算图如图 5-9 中,反向传播将从上游传过来的导数(本例中是 ∂ L ∂ z \frac{\partial L}{\partial z} zL)乘以1,然后传向下游。也就是说,因为加法节点的反向传播只乘以1,所以输入的值会原封不动地流向下一个节点。

图5-9
另外,本例中把从上游传过来的导数的值设为 ∂ L ∂ z \frac{\partial L}{\partial z} zL。这是因为,如图 5-10 所示,我们假定了一个最终输出值为 L 的大型计算图。z = x + y 的计算位于这个大型计算图的某个地方,从上游会传来 ∂ L ∂ z \frac{\partial L}{\partial z} zL 的值,并向下游传递 ∂ L ∂ x \frac{\partial L}{\partial x} xL ∂ L ∂ y \frac{\partial L}{\partial y} yL

图5-10

2. 乘法节点反向传播

z = xy 为对象。这个式子的导数用式(5.6)表示。
式 5-6
计算图如图5-12
图 5-12
乘法的反向传播会将上游的值乘以正向传播时的输入信号的“翻转值”后传递给下游。翻转值表示一种翻转关系,如图 5-12 所示,正向传播时信号是 x 的话,反向传播时则是 y ;正向传播时信号是 y 的话,反向传播时则是 x

乘法节点例子
加法的反向传播只是将上游的值传给下游,并不需要正向传播的输入信号。但是乘法的反向传播需要正向传播时的输入信号值。因此,实现乘法节点的反向传播时,要保存正向传播的输入信号。

3. 实例

问题1: 小明在超市买了2 个100 日元一个的苹果,消费税是10%,请计算支付金额。

对于问题 1 反向传播的例子:
图 5-14
如前所述,乘法节点的反向传播会将输入信号翻转后传给下游。

从图 5-14 的结果可知,苹果的价格的导数是 2.2,苹果的个数的导数是 110,消费税的导数是 200。这可以解释为,如果消费税和苹果的价格增加相同的值,则消费税将对最终价格产生200 倍大小的影响,苹果的价格将产生2.2 倍大小的影响。不过,因为这个例子中消费税和苹果的价格的量纲不同,所以才形成了这样的结果(消费税的1 是100%,苹果的价格的1 是1 日元)。

4. 乘法层代码实现

层的实现中有两个共通的方法(接口)forward()backward()

  • forward() 对应正向传播;
  • backward() 对应反向传播;

示例代码如下:

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y                
        out = x * y

        return out

    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy
  • __init__() 中会初始化实例变量 xy ,它们用于保存正向传播时的输入值。
  • forward() 接收 xy 两个参数,将它们相乘后输出。
  • backward() 将从上游传来的导数(dout)乘以正向传播的翻转值,然后传给下游。

下图为购买两个苹果的计算图
图 5-16
示例代码

apple = 100
apple_num = 2
tax = 1.1

mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

# backward
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print("price:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dTax:", dtax)

输出结果:

price: 220
dApple: 2.2
dApple_num: 110
dTax: 200

调用 backward() 的顺序与调用 forward() 的顺序相反。此外,要注意 backward() 的参数中需要输入“关于正向传播时的输出变量的导数”。

比如,mul_apple_layer 乘法层在正向传播时会输出 apple_price ,在反向传播时,则会将 apple_price 的导数 dapple_price 设为参数。

另外,这个程序的运行结果和图5-16 是一致的。

5. 加法层代码实现

加法层代码类实现如下:

class AddLayer:
    def __init__(self):
        pass

    def forward(self, x, y):
        out = x + y

        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1

        return dx, dy

加法层不需要特意进行初始化,所以 __init__() 中什么也不运行。加法层的 forward() 接收 xy 两个参数,将它们相加后输出。backward() 将上游传来的导数(dout )原封不动地传递给下游。

购买 2 个苹果和 3 个橘子的计算图:
图 5-17
示例代码:

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward
apple_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)

# backward
dprice = 1
dall_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:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dOrange:", dorange)
print("dOrange_num:", int(dorange_num))
print("dTax:", dtax)

输出结果:

price: 715
dApple: 2.2
dApple_num: 110
dOrange: 3.3000000000000003
dOrange_num: 165
dTax: 650

首先,生成必要的层,以合适的顺序调用正向传播的 forward() 方法。然后,用与正向传播相反的顺序调用反向传播的 backward() 方法,就可以求出想要的导数。

参考:《深度学习入门:基于Python的理论与实现》

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值