深度学习入门-误差反向传播法

深度学习入门-误差反向传播法

​ 反向传播法在神经网络中是用于计算权重参数针对于损失函数的梯度的方法,该方法比数值微分法求梯度更加的高校。

反向传播法的思想

image-20220725181013871

​ 如上图所示,未加粗的箭头为正向传播,加粗的箭头为反向传播。计算图的反向传播从右到左传播信号。反向传播的计算顺序 是,先将节点的输入信号乘以节点的局部导数(偏导数),然后再传递给下一 个节点。

​ 例如,在计算**2这个节点时的输入是 ∂ z ∂ z \dfrac{\partial z}{\partial z} zz将其乘以局部导数 ∂ z ∂ x \dfrac{\partial z}{\partial x} xz(因为正向传播时输入是t、输出是z)然后传给下一个节点。

简单层的实现

乘法层的实现

乘法层的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
        dy = dout * self.x
        
        return dx,dy

利用MulLayer实现买苹果的例子

image-20220630172423664

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

apple = 100
apple_num = 2
tax = 1.1  #消费税

mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

#正向传播
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

print(price)


#反向传播
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_number = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_number, dtax)

加法层的实现

加法层的AddLayer类

class AddLayer:
    def __init__(self):
        pass
    
    def forward(self, x, y):
        out = x + y
        return out
    
    def reward(self, dout):
        dx = dout * 1
        dy = dout *1
        return dx, dy

利用MulLayer和AddLayer类实现买苹果和橘子的例子

image-20220630174424375

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
    
class AddLayer:
    def __init__(self):
        pass
    
    def forward(self, x, y):
        out = x + y
        return out
    
    def reward(self, dout):
        dx = dout * 1
        dy = dout *1
        return dx, dy



apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1  #消费税

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

#正向传播
apple_price = mul_apple_layer.forward(apple, apple_num)
orange_price = mul_orange_layer.forward(orange, orange_num)
apple_orange_price = add_apple_orange_layer.forward(apple_price, orange_price)
price = mul_tax_layer.forward(apple_orange_price, tax)

print(price)


#反向传播
dprice = 1
dapple_orange_price, dtax = mul_tax_layer.backward(dprice)
dapple_price, dorange_price = add_apple_orange_layer.reward(dapple_orange_price)
dapple_num, dapple = mul_apple_layer.backward(dapple_price)
dorange_num, dorange = mul_orange_layer.backward(dorange_price)

print(dapple_orange_price, dtax, dapple_price, dorange_price, dapple_num, dapple, dorange_num, dorange)

激活函数层的实现

ReLU层

激活函数ReLU的形式如下所示

image-20220701094401656

通过上式,可求出y关于x的倒数

image-20220701094447639

ReLu层的计算图如下

image-20220701094851990

代码实现

class ReLU:
    def __init__(self):
        self.mask = None
    
    def forword(self, x):
        self.mask = (x <= 0) 
        '''
        变量mask是由True/False构成的NumPy数
        组,它会把正向传播时的输入x的元素中小于
        等于0的地方保存为True,其他地方(大于0的
        元素)保存为False。
        '''
        out = x.cpoy()
        out[self.mask] = 0
        return out
    
    def backreward(self, dout):
        dout[self.mask] = 0
        dx = dout
        return dx

Sigmoid层

sigmoid函数式如下

image-20220701101558776

正向传播的计算图

image-20220701101629775

反向传播的计算图

image-20220701101919507

image-20220701101942499

因此,Sigmoid层的反向传播,只根据正向传播的输出 就能计算出来。

image-20220701102031225

代码实现

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 

Affine/Softmax层的实现

Affine层

神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”。因此,这里将进行仿射变换的处理实现为“Affine层”。

Affine层的式子表示

image-20220702171651412

image-20220702171706031

批版本的Affine层的反向传播结果

image-20220702172447571

反向传播求dB的时候,各个数据的反向传播的值需要汇总为偏置的元素。

image-20220702181352418

代码实现

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

Softmax-with-Loss 层

softmax函数

softmax函数 会将输入值正规化之后再输出

image-20220703105424856

神经网络中进行的处理有推理和学习两个阶段。神经网 络的推理通常不使用 Softmax层。比如,用图 5-28的网络进行推理时, 会将最后一个 Affine层的输出作为识别结果。神经网络中未被正规 化的输出结果有时 被称为“得分”。也就是说,当神经网络的推理只需要给出一个答案 的情况下,因为此时只对得分最大值感兴趣,所以不需要 Softmax层。 不过,神经网络的学习阶段则需要 Softmax层。

交叉熵误差(cross entropy error)

image-20220703105401937

Softmax-with-Loss层(Softmax函数和交叉熵误差)的计算图如下

image-20220703105711796

对于上图的推导,需要注意的是,如果一个值在正向传播时分出了若干个,则在进行反向传播求解时也要先将这若干个反向传播的结果相加,再去求该节点的值。

图中的log是按照ln算的(不理解为什么????)

简易版的Softmax-with-Loss层的计算图

image-20220703110124163

图中要注意的是反向传播的结果。Softmax层的反向传播得到了 (y1 − t1, y2 − t2, y3 − t3)这样“漂亮”的结果。由于(y1, y2, y3)是Softmax层的 输出,(t1, t2, t3)是监督数据,所以(y1 − t1, y2 − t2, y3 − t3)是Softmax层的输出和教师标签的差分。神经网络的反向传播会把这个差分表示的误差传递给 前面的层,这是神经网络学习中的重要性质。

Softmax-with-Loss层的代码实现

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 backreward(self, dout = 1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        #反向传播时,将要传播的值除以批的大小(batch_size)后,传递给前面的层的是单个数据的误差。
 
        return dx

误差反向传播法的实现

神经网络学习的全貌图

前提

神经网络中有合适的权重和偏置,调整权重和偏置以便拟合训练数据的 过程称为学习。神经网络的学习分为下面4个步骤。

步骤1(mini-batch)

从训练数据中随机选择一部分数据。

步骤2(计算梯度)

计算损失函数关于各个权重参数的梯度。

可以用数值微分法或反向传播法

步骤3(更新参数)

将权重参数沿梯度方向进行微小的更新。

步骤4(重复)

重复步骤1、步骤2、步骤3。

TowLayerNet网络的实现

import sys,os
sys.path.append(os.pardir)
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collection import OrdereDict

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.rand(input_size,hidden_size)
        self.params['b1'] = weight_init_std * np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.rand(hidden_size,output_size)
        self.params['b2'] = np.zeros(output_size)
        
        self.layers = OrdereDict()
        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)
        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

​ 将神经网络的层保存为 OrderedDict这一点非常重要。OrderedDict是有序字典,“有序”是指它可以 记住向字典里添加元素的顺序。因此,神经网络的正向传播只需按照添加元 素的顺序调用各层的forward()方法就可以完成处理,而反向传播只需要按 照相反的顺序调用各层即可。因为Affine层和ReLU层的内部会正确处理正 向传播和反向传播,所以这里要做的事情仅仅是以正确的顺序连接各层,再 按顺序(或者逆序)调用各层。

误差反向传播法实例(手写数字识别)

import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list = []
train_acc_list = []
test_acc_list = []
iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    # 通过误差反向传播法求梯度
    grad = network.gradient(x_batch, t_batch)
    # 更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
        
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print(train_acc, test_acc)

小结

image-20220725180028546

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值