神经网络之误差反向传播法

1、计算图:将计算过程用数据结构图表示,通过多个节点和边表示,节点用O表示,O中是计算内容,将计算的中间结果写在箭头上方,表示各个节点的计算结果从左向右传递。用计算图解题的流程即:①构建计算图;②在计算图上从左向右进行计算(正向传播,从计算图出发点到结束点的传播,反向传播正好相反)。

2、计算图的优点
①计算图可以集中精力于局部计算,从而简化问题,无论全局计算多么复杂,各个步骤所要做的就是对象节点的局部计算,通过传递它的计算结果,可以获得全局的复杂计算结果。
②可以通过反向传播高效计算导数,反向传播使用与正方向相反的箭头(粗线表示),传递“局部导数”,将导数值写在箭头下方,并且计算途中求得的导数结果可以被共享,从而可以高效地计算多个导数。

3、链式法则:如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的导数的乘积表示,这是关于复合函数的导数的性质。以复合函数z = (x+y)^2为例,由式子z = t^2,t = x+y构成。
在这里插入图片描述
反向传播的顺序是,先将节点的输入信号乘以节点的局部导数(偏导数),然后再传递给下一个节点,这个过程也基于链式法则。
在这里插入图片描述
在这里插入图片描述
4、反向传播的结构

  • 加法节点的反向传播——以z=x+y为对象观察反向传播,将上游传来的导数乘以1传向下游,因为加法节点的反向传播只乘以1,所以输入值原封不动流向下一个节点。
    在这里插入图片描述
    在这里插入图片描述

  • 乘法节点的反向传播——y以z=xy为对象观察反向传播,乘法的反向传播会将上游的值乘以正向传播时输入信号的翻转值(一种翻转关系),然后传递给下游,这个过程要保存正向传播的输入信号。
    在这里插入图片描述
    在这里插入图片描述

5、乘法层的实现:层的实现中有两个共通的方法(接口)forward()和backward(),分别对应正向传播和反向传播,*_ init _()*中初始化实例变量x和y,用于保存正向传播时的输入值,forward()接收x和y两个参数,将它们相乘后输出,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

6、加法层的实现:不需要特意进行初始化,_ init _()中什么也不运行,加法层的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

7、ReLU层的实现:激活函数ReLU由下式表示,并可求出y关于x的导数,如果正向传播时所输入的x>0,则反向传播会将上游的值原封不动传给下游;如果正向传播时输入的x≤0,则反向传播中传递给下游的信号将停在此处。
在这里插入图片描述

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

其中,实例变量mask是由True/False构成的Numpy数组,将输入元素中≤0的值的索引保存为True,反向传播中会使用正向传播时保存的mask,将从上游传来的dout的mask中元素为True的地方设为0。

8、Sigmoid层的实现:除了“x”和“+”节点,还有新节点“exp”和“/”节点,①“/”节点表示y=1/x,导数可以解析性地表示为-1/(x^2)=-(y**2),反向传播时会将上游值乘以该值后再传给下游;②“+”节点将上游值原封不动传给下游;③“exp”节点表示y=exp(x),导数仍是自身;④“x”节点将正向传播值翻转后做乘法运算,即-1。通过对节点进行集约化,可以无须在意Sigmoid层中琐碎的细节,只需要注意它的输入和输出。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Sigmoid:
    def __init__(self):
        self.out = None
    def forward(self,x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        
        return out
    def backward(self,dout):
        dx = dout * (1.0 - self.out) * self.out
        
        return dx

9、Affine层的实现:神经网络的正向传播中进行的矩阵乘积运算在几何学领域被称为“仿射变换”,仿射变换的处理过程即Affine层的实现过程。比如正向传播为了计算加权信号总和,使用了矩阵的乘积运算,Y = np.dot(X,W) + B,在反向传播时以矩阵为对象,按矩阵各个元素进行计算,步骤和以标量为对象的计算图相同。
在这里插入图片描述
批版本的Affine层:输入X的形状由(1,2)变为(N,2),在正向传播时,偏置会被加到X▪W的各个数据上,比如N=2时偏置会被分别加到这两个数据计算结果上;反向传播时,各个数据的反向传播值需要汇总为偏置元素,使用np.sum()函数(axis=0指定0维)指定对应轴方向上的元素进行求和。
在这里插入图片描述

class Affine:
    def __init__(self,W,b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None
    def forward(self,x):
        self.x = x
        out = np.dot(x,self.W) + self.b

        return out
    def backward(self,dout):
        dx = np.dot(dout,self.W.T)
        self.dW = np.dot(self.x.T,dout)
        self.db = np.sum(dout,axis=0)

        return dx

10、Softmax-with-Loss层:Softmax会将输入值正规化之后再输出(将输出值的和调整为1),考虑到也包含作为损失函数的交叉熵误差(cross entropy error),所以称为Softmax-with-Loss层。
在这里插入图片描述
上述计算图可以进行简化,softmax函数记为Softmax层,交叉熵误差记为Cross Entropy Error层,假设要进行3类分类,从前面层接收3个输入,Softmax层将这3个输入(a1,a2,a3)正规化,输出(y1,y2,y3),Cross Entropy Error层接收(y1,y2,y3)和监督标签(t1,t2,t3),从这些数据中输出损失L。反向传播时Softmax层得到的(y1-t1,y2-t2,y3-t3)是该层输出和监督标签的差分,神经网络的反向传播会把这个差分表示的误差传递给前面的层,这是神经网络学习的重要性质,因为神经网络的学习目的就是通过调整权重参数,使神经网络的输出接近监督标签(y与t)。
在这里插入图片描述

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None
    def forward(self,x,t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y,self.t)
        
        return self.loss
    def backward(self,dout = 1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        #传递给前面层的是单个数据的误差
        
        return dx

11、误差反向传播法的实现
①前提:神经网络中有合适的权重和偏置,调整权重和偏置以便拟合训练的过程称为学习;
②mini-batch:从训练数据中随机选择一部分数据;
③计算梯度:计算损失函数关于各个权重参数的梯度;
④更新参数:将权重参数沿着梯度方向进行微小更新;
⑤重复上述步骤。
误差反向传播法主要在③中得以运用,和需要花费较多时间的数值微分不同,误差反向传播法可以快速高效地计算梯度,通过使用层,获得识别结果的处理(predict())和计算梯度的处理(gradient())只需要通过层之间的传递就能实现。在确认误差反向传播法的实现是否正确时需要使用数值微分,将两者的结果进行比较的操作称为梯度确认

class TwoLayerNet:
    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        #初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size,hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size,output_size)
        self.params['b2'] = np.zeros(output_size)
        #生成层
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
        self.lastLayer = SoftmaxWithLoss()
        
    def predict(self,x):
        for layer in self.layers.values():
            x = layer.forward(x)
        return x
        
    def loss(self,x,t):
        y = self.predict(x)
        return self.lastLayer.forward(y,t)

    def accuracy(self,x,t):
        y = self.predict(x)
        y = np.argmax(y,axis=1)
        if t.ndim != 1:
            t = np.argmax(t,axis=1)
        accuracy = np.sum(y==t) / float(x.shape[0])
        return accuracy

    def numerical_gradient(self,x,t):
        loss_W = lambda W: self.loss(x,t)
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        return grads

    def gradient(self,x,t):
        #forward
        self.loss(x,t)
        #backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        #设定
        grads = {}
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db
        return grads

将神经网络的层保存为OrderDict有序字典,自身可以记住向字典里添加元素的顺序,正向传播只需按照添加元素的顺序调用各层的forward()方法,反向传播则按照相反的顺序调用各层。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值