深度学习入门|第5章 误差反向传播法(二)

 误差反向传播法

前言

此为本人学习《深度学习入门》的学习笔记

四、简单层的实现

本节将用 Python 实现前面的购买苹果的例子。这里,我们把要实现的计算图的乘法节点称为“乘法层”(MulLayer),加法节点称为“加法层”(AddLayer)。

1、乘法层的实现

层的实现中有两个共通的方法(接口)forward() 和backward()forward() 对应正向传播,backward() 对应反向传播。

实现乘法层。乘法层作为 MulLayer 类,其实现过程如下所示

 

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 # 翻转x和y
        dy = dout * self.x

        return dx, dy

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

使用 MulLayer 实现前面的购买苹果的例子(2 个苹果和消费税)。

 

        

                    图 5-16 购买 2 个苹果

使用这个乘法层的话,图 5-16 的正向传播可以像下面这样实现

 此外,关于各个变量的导数可由 backward() 求出。

调用 backward() 的顺序与调用 forward() 的顺序相反。此外,要注意 backward() 的参数中需要输入“关于正向传播时的输出变量的导数”。比如,mul_apple_layer 乘法层在正向传播时会输出 apple_price,在反向传播时,则会将 apple_price 的导数 dapple_price 设为参数。

2、加法层的实现

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__() 中什么也不运行(pass 语句表示“什么也不运行”)。加法层的 forward() 接收 x 和 y 两个参数,将它们相加后输出。backward() 将上游传来的导数(dout)原封不动地传递给下游。

使用加法层和乘法层,实现图 5-17 所示的购买 2 个苹果和 3 个橘子的例子。

      

              图 5-17 购买 2 个苹果和 3 个橘子

用 Python 实现图 5-17 的计算图的过程如下所示

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

五、激活函数层的实现

将计算图的思路应用到神经网络中。把构成神经网络的层实现为一个类。先来实现激活函数的 ReLU 层和 Sigmoid 层。

1、ReLU层

激活函数 ReLU(Rectified Linear Unit)由下式(5.7)表示。

通过式(5.7),可以求出 y 关于 x 的导数,如式(5.8)所示。

在式(5.8)中,如果正向传播时的输入 x 大于 0,则反向传播会将上游的值原封不动地传给下游。反过来,如果正向传播时的 x 小于等于 0,则反向传播中传给下游的信号将停在此处。用计算图表示的话,如图 5-18 所示。

实现 ReLU 层。在神经网络的层的实现中,一般假定 forward() 和backward() 的参数是 NumPy 数组。

 

  图 5-18 ReLU 层的计算图

class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx

Relu 类有实例变量 mask。这个变量 mask 是由 True/False 构成的 NumPy 数组,它会把正向传播时的输入 x 的元素中小于等于 0 的地方保存为 True,其他地方(大于 0 的元素)保存为 False

 2、Sigmoid层

实现sigmoid函数,sigmoid函数由式(5.9)表示

用计算图表示式子(5.9)的话,则如图5-19所示

图 5-19 sigmoid 层的计算图(仅正向传播)

图 5-19 中,除了“×”和“+”节点外,还出现了新的“exp”和“/”节点。“exp”节点会进行 y = exp(x) 的计算,“/”节点会进行 y=\frac{1}{x} 的计算。

如图 5-19 所示,式(5.9)的计算由局部计算的传播构成。

下面我们就来进行图 5-19 的计算图的反向传播。这里,作为总结,我们来依次看一下反向传播的流程。

步骤 1

“/”节点表示 y=\frac{1}{x},它的导数可以解析性地表示为下式。

enter image description here

根据式(5.10),反向传播时,会将上游的值乘以 -y^2(正向传播的输出的平方乘以 -1 后的值)后,再传给下游。计算图如下所示。

{95%}

步骤 2

“+”节点将上游的值原封不动地传给下游。计算图如下所示。

{95%}

步骤 3

“exp”节点表示 y = exp(x),它的导数由下式表示。

\frac{\partial y}{\partial x}=\exp(x)\quad\quad\quad\quad\quad(5.11)

计算图中,上游的值乘以正向传播时的输出(这个例子中是 exp(-x))后,再传给下游。

{95%}

步骤 4

“×”节点将正向传播时的值翻转后做乘法运算。因此,这里要乘以 -1。

{95%}

图 5-20 Sigmoid 层的计算图

 

posted @ 2019-05-12 18:54  砍柴人Ryan  阅读(1084)  评论(0编辑  收藏  举报