代码改变世界

机器学习 反向传播算法

2022-04-05 14:08  jym蒟蒻  阅读(262)  评论(0编辑  收藏  举报

使用反向传播算法计算参数的梯度并用python实现加法和乘法节点的反向传播

 

一、what is 反向传播

误差反向传播法是一种高效计算权重参数的梯度的方法。所谓的反向传播,从图上看的话,就是从右向左的传播。

举个例子,如图所示,jym买了两个100元一个的苹果,消费税是10%,最终输出支付金额,这个图就表示了一个计算的流程。正向看都能理解,就是100和2相乘得到200,作为下一个节点的入,然后200和1.1相乘得到220的支付金额。

那么,如果jym想要知道苹果价格上涨会多大程度影响最终的支付金额,就需要求支付金额关于苹果价格的偏导数。设苹果价格为x,支付金额为L,支付金额关于苹果价格的偏导数为d,d的值表示,x上涨a时支付金额会增加d*a。

图中,反向传播用与正向相反的箭头表示。它传递的数是局部导数。而且,通过图片可以了解到,苹果价格如果增加1元,那么最终支付金额将增加2.2元。

在这里插入图片描述

传递这个局部导数的原理是链式法则。表示链式法则的数学公式如下:

在这里插入图片描述

反向传播计算流程:

将节点的输入信号乘以节点输出关于输入的偏导数,然后再传到下一个节点,这里面传递的方向是和正向相反的。

把上面链式法则数学公式表示到图像上:

x是苹果j的价格,z是最终的支付金额,z对x求偏导就根据那个链式法则的流程逐步求出了。

在这里插入图片描述

这就很好,因为偏导数是按照流程规则求出来的,这样就可以用程序编程了。

二、乘法节点的反向传播

z=xy,z对x求偏导等于y,z对y求偏导等于x,那么,乘法的反向传播会将上游的值乘以正向传播时的输入信号的翻转值之后传给下游,和加法的区别就是,需要正向传播的输入信号值,编程时需要保存正向传播的输入信号。乘法节点的反向传播计算图如下图所示。

在这里插入图片描述

python实现乘法层的代码:

init函数初始化实例变量x、y,保存正向传播时的输入值。

forward接收x、y两个参数,将他们相乘后输出。

backward将上游传过来的导数dout乘正向传播的翻转值,然后传给下游

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

下面这个代码举了个例子,用代码复现了这张图片。
在这里插入图片描述

from layer_naive import *

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

三、加法节点的反向传播

z=x+y,z对x和对y求偏导都是1。也就是说,加法的反向传播只是将上游的值传给下游。加法节点的反向传播计算图如下图所示。

在这里插入图片描述

python实现加法层的代码:

forward接收x和y两个参数,将它们相加后输出。

backward将上游传来的导数原封不动传给下游。

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

四、加法层和乘法层混合应用

接下来用代码表述下面这张图。

在这里插入图片描述

from layer_naive import *

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