淘先锋技术网

首页 1 2 3 4 5 6 7

反向传播算法应用领域

在简单的神经网络中,反向传播算法可以理解为最优化损失函数过程,是求解每个参与运算的参数(权值,偏置)的梯度的方法。
在前馈神经网络中,反向传播从求解损失函数偏导过程中,逐步反向求解每一层的参数梯度。
在卷积神经网络中,反向传播可以求解全连接层的参数梯度。
在循环神经网络中,反向传播算法可以求解每一个时刻t或者状态t的参数梯度。

在本文中主要由前馈神经网络来剖析Bp算法。

前馈神经网络

  • BP算法的核心思想
    使用梯度下降来搜索可能的权向量的假设空间,以找到最佳的拟合样例的权向量。具体而言,即利用损失函数,每次向损失函数负梯度方向移动,直到损失函数取得最小值。
  • 前馈神经网络基本结构
    主要包括输入层、隐藏层、输出层,其中隐藏层可以有多个。

算法推导

在此从单隐藏层到多隐藏层进行举例讲解。

单个隐藏层

在这里插入图片描述
1、初始化网络中的权值和偏置项: w ( 0 ) , b 1 ( 0 ) , v ( 0 ) , b 2 ( 0 ) w^{\left( 0 \right)},b_{1}^{^{\left( 0 \right)}},v^{\left( 0 \right)},b_{2}^{^{\left( 0 \right)}} w(0),b1(0),v(0),b2(0)
2、前向传播,得到输出的损失期望值,一般情况下损失函数是所有损失和的期望:
L ( θ ) = 1 n ∑ i = 1 n ( y i − y i ^ ) 2 = n = 2 L ( θ ) = 1 2 ∑ i = 1 2 ( y i − y i ^ ) 2 = 1 2 ( y − y ^ ) T ( y − y ^ ) L\left( \theta \right) =\frac{1}{n}\sum_{i=1}^n{\left( y_i-\widehat{y_i} \right)}^2\xlongequal{n=2}L\left( \theta \right) =\frac{1}{2}\sum_{i=1}^2{\left( y_i-\widehat{y_i} \right)}^2=\frac{1}{2}\left( y-\widehat{y} \right) ^T\left( y-\widehat{y} \right) L(θ)=n1i=1n(yiyi )2n=2 L(θ)=21i=12(yiyi )2=21(yy )T(yy )
θ \theta θ表示参数集合, y y y表示真实值, y ^ \widehat{y} y 表示预测值。
在这里插入图片描述
3、根据损失函数,由链式法则逐步反向计算输出层的误差项和隐藏层的误差项:
在这里插入图片描述
ϕ ( ⋅ ) \phi \left( \cdot \right) ϕ()表示对激活函数的导函数。
4、更新神经网路中的权值和偏置项
梯度下降 { 输出层参数更新: v ( k ) = v ( k − 1 ) − η ∇ ( k ) v = v ( k − 1 ) − η ∂ E ∂ v , b 2 ( k ) = b 2 ( k − 1 ) − η ∂ E ∂ b 2 隐藏层参数更新: w ( k ) = w ( k − 1 ) − η ∇ ( k ) w = w ( k − 1 ) − η ∂ E ∂ v , b 1 ( k ) = b 1 ( k − 1 ) − η ∂ E ∂ b 1 梯度下降\left\{ \begin{array}{l} \text{输出层参数更新:}v^{\left( k \right)}=v^{\left( k-1 \right)}-\eta \nabla _{\left( k \right)}v=v^{\left( k-1 \right)}-\eta \frac{\partial E}{\partial v}\text{,}b_{2}^{\left( k \right)}=b_{2}^{\left( k-1 \right)}-\eta \frac{\partial E}{\partial b_2}\\ \text{隐藏层参数更新:}w^{\left( k \right)}=w^{\left( k-1 \right)}-\eta \nabla _{\left( k \right)}w=w^{\left( k-1 \right)}-\eta \frac{\partial E}{\partial v}\text{,}b_{1}^{\left( k \right)}=b_{1}^{\left( k-1 \right)}-\eta \frac{\partial E}{\partial b_1}\\ \end{array} \right. 梯度下降{输出层参数更新:v(k)=v(k1)η(k)v=v(k1)ηvEb2(k)=b2(k1)ηb2E隐藏层参数更新:w(k)=w(k1)η(k)w=w(k1)ηvEb1(k)=b1(k1)ηb1E
将步骤3中计算得到的各个值代入上式中即可迭代权值或偏置。其中 k k k表示迭代次数, η \eta η表示学习率。

多个隐藏层

在这里插入图片描述

注意上图中 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L的写法,除了输入层不做讨论,在后面的隐藏层和输出层在进行梯度反向传播时都有一定性的规律:接受前一层的输出作为输入,经过层内激活函数作用后进行输出作为下一层的输入。不过第一隐藏层和后面的层有所不同,是针对权值计算梯度。

梯度消失与梯度爆炸

发生原因

目前优化神经网络的方法都是基于BP,即根据损失函数计算的误差通过梯度反向传播的方式,指导深度网络权值的更新。其中将误差从末层往前传递的过程需要链式法则的帮助,因此反向传播算法可以说是梯度下降在链式法则中的应用。

多个隐藏层中损失函数对 w 1 w_1 w1的偏导为:
∂ L ∂ w 1 = ∂ L ∂ y ^ [ ∂ y ^ ∂ n e t 4 ∂ n e t 4 ∂ h 3 ] [ ∂ h 3 ∂ n e t 3 ∂ n e t 3 ∂ h 2 ] [ ∂ h 2 ∂ n e t 2 ∂ n e t 2 ∂ h 1 ] [ ∂ h 1 ∂ n e t 1 ∂ n e t 1 ∂ w 1 ] \frac{\partial L}{\partial w_1}=\frac{\partial L}{\partial \widehat{y}}\left[ \frac{\partial \widehat{y}}{\partial net_4}\frac{\partial net_4}{\partial h_3} \right] \left[ \frac{\partial h_3}{\partial net_3}\frac{\partial net_3}{\partial h_2} \right] \left[ \frac{\partial h_2}{\partial net_2}\frac{\partial net_2}{\partial h_1} \right] \left[ \frac{\partial h_1}{\partial net_1}\frac{\partial net_1}{\partial w_1} \right] w1L=y L[net4y h3net4][net3h3h2net3][net2h2h1net2][net1h1w1net1]
可以转化为
在这里插入图片描述

一般情况下一个网络的激活函数都相等,那么有 ϕ 4 ( ⋅ ) = ϕ 3 ( ⋅ ) = ϕ 2 ( ⋅ ) = ϕ 1 ( ⋅ ) \phi _4\left( \cdot \right) =\phi _3\left( \cdot \right) =\phi _2\left( \cdot \right) =\phi _1\left( \cdot \right) ϕ4()=ϕ3()=ϕ2()=ϕ1()
由于链式法则是一个连乘的形式,当层数越深(越多)时,梯度函数中 ϕ ( ⋅ ) \phi \left( \cdot \right) ϕ()连乘的数量越多,梯度将以指数形式传播。

  • ϕ ( ⋅ ) < 1 \phi \left( \cdot \right)<1 ϕ()<1,随着层数的增大 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L可能趋近于0而出现梯度消失的问题;
  • ϕ ( ⋅ ) > 1 \phi \left( \cdot \right)>1 ϕ()>1,随着层数的增大 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L可能趋近于 ∞ \infty 而可能出现梯度爆炸的问题。

梯度消失问题和梯度爆炸问题一般随着网络层数的增加会变得越来越明显。在根据损失函数计算的误差通过梯度反向传播的方式对深度网络权值进行更新时,得到的梯度值接近0或特别大。

直接原因

  • 梯度消失
    1、隐藏层的层数过多
    2、采用了不合适的激活函数(更容易产生梯度消失,但是也有可能产生梯度爆炸)

  • 梯度爆炸
    1、隐藏层的层数过多
    2、采用了不合适的激活函数
    3、权重的初始化值过大

根本原因

(1)隐藏层的层数过多
从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层由于梯度反向传播经过的层数过多造成其学习的速度很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多。因此,梯度消失、爆炸,其根本原因在于在于反向传播算法的不足。
多个隐藏层的网络可能比单个隐藏层的更新速度慢了几个个数量级
(2)激活函数
BP算法基于梯度下降方法,以目标的负梯度方向对参数进行调整,其中计算梯度包含了是对激活函数进行求导。例如在Sigmoid函数中,如果输入远离1,那么梯度趋近于0。
(3)初始权重过大
当网络初始权重比较大的情况下,当反向传播的链式相乘时,前面的网络层比后面的网络层梯度变化更快,最终的求出的梯度更新将以指数形式增加,将会导致梯度爆炸。所以,在一般的神经网络中,权重的初始化一般都利用高斯分布(正态分布)随机产生权重值。

解决方案

梯度消失和梯度爆炸问题都是因为网络太深层数过多,网络权值更新不稳定造成的,本质上是因为梯度反向传播中的连乘效应。对于更普遍的梯度消失问题,可以考虑一下以下方案解决:
(1)用ReLU、Leaky-ReLU、P-ReLU、R-ReLU、Maxout等替代sigmoid函数。
思想也很简单,如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了,每层的网络都可以得到相同的更新速度。
(2)用Batch Normalization。
那么反向传播过程中权重的大小影响了梯度消失和爆炸,BN方法对每一层的输出规范为均值和方差一致的,即移动均值改变方差,消除权重带来的放大缩小的影响,进而解决梯度消失和爆炸的问题。
或者可以理解为BN将输出从饱和区拉到了非饱和区(比如Sigmoid函数)。
(3)LSTM的结构设计也可以改善RNN中的梯度消失问题。
在RNN网络结构中,由于使用Logistic或者Tanh函数,所以很容易导致梯度消失的问题,即在相隔很远的时刻时,前者对后者的影响几乎不存在了,LSTM的机制正是为了解决这种长期依赖问题。
(4)ResNet残差网络
相比较以往的网络结构只是简单的堆叠,残差中有很多跨层连接结构,这样的结构在反向传播时具有很大的好处。
(5)预训练加微调
其基本思想是每次训练一层隐节点,训练时将上一层隐节点的输出作为输入,而本层隐节点的输出作为下一层隐节点的输入,此过程是逐层预训练;
在预训练完成后,再对整个网络进行“微调”。此思想相当于是先寻找局部最优,然后整合起来寻找全局最优。
(6)梯度剪切
梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内,这可以防止梯度爆炸。

代码演示

import math
import numpy as np
from numpy import *

# 激活函数
def sigmoids(z):
    a = []
    for each in z:
        b = 1 / (1 + math.exp(-each[0]))
        a.append(b)
    return a

# 前向传播,返回预测值
def forwordmd(X, W, V, B1, B2):
    # 隐藏层输入输出
    net1 = W.T * X + B1
    H = matrix(sigmoids(np.array(net1))).T

    # 输出层输入输出
    net2 = V.T * H + B2
    pred_y = matrix(sigmoids(np.array(net2))).T
    return pred_y, H, net1, net2

# 反向传播,更新权重
def Bpaugorith(Y, pred_y, H, V, aph, W):
    Errorter = 0.5 * (Y - pred_y).T * (Y - pred_y)  # 给出误差公式
    # 计算输出单元的误差项
    a1 = multiply(pred_y - Y, pred_y)  # 矩阵对应元素相乘,即逐元素相乘
    a2 = multiply(a1, 1 - pred_y)
    Verror = H * a2.T

    # 计算隐藏单元的误差项
    Werror = X * (multiply(multiply(H, 1 - H), (V * a2))).T

    # 更新权重
    Vupdate = V - aph * Verror
    Wupdate = W - aph * Werror
    return Vupdate, Wupdate, Errorter


# **********主程序部分,此处设定了步骤1中的参数初始化和输入值及输出值的真实值,及步骤5中设置迭代次数和设置阈值停止迭代的代码*********
if __name__ == '__main__':
    X = matrix([0.05, 0.10]).T
    Y = matrix([0.01, 0.99]).T
    # 给出初始权重
    W = matrix([[0.15, 0.20], [0.25, 0.30]])
    B1 = matrix([0.1, 0.1]).T
    V = matrix([[0.40, 0.45], [0.50, 0.55]])
    B2 = matrix([0.2, 0.2]).T
    # ***********初始权重亦可随机生成***********
    # 随机生成参数
    # np.random.seed(0)
    # W = matrix(np.random.normal(0,1,[2,2]))
    # B1 = matrix(np.random.normal(0, 1, [1, 2]))
    # V = matrix(np.random.normal(0, 1, [2, 2]))
    # B2 = matrix(np.random.normal(0, 1, [1, 2]))

    aph = 0.5  # 学习率
    # 迭代10次
    n = 10000  # 迭代次数
    for i in range(n):
        # 激活前向算法
        pred_y, H, net1, net2 = forwordmd(X, W, V, B1, B2)  # 得到预测值和隐藏层值
        # 更新权重
        Vupdate, Wupdate, Errorvalue = Bpaugorith(Y, pred_y, H, V, aph, W)  # 得到更新的权重
        W, V = Wupdate, Vupdate

    print('损失函数e:%.2f' % e)
    print('预测值:')
    print(pred_y)
    print('更新的权重V:')
    print(Vupdate)
    print('更新的权重W:')
    print(Wupdate)
    print('损失值:')
    print(Errorvalue)

参考文档:
https://blog.csdn.net/kundezui/article/details/126895955
https://blog.csdn.net/weixin_39441762/article/details/80446692