深度学习2-- 神经网络及反向传播

时间:2024-05-23 12:17:09

全连接神经网络

概念

使用非线性函数变换的多层感知机又称深度前馈网络、 深度神经网络

这种网络的目的是使得整个模型近似于某个函数ff^{*} 。在前馈神经网络中定义了一个y=f(x;θ)y = f(x;\theta),并且学习参数θ\theta的值,使得它能够到最佳的函数近似。

深度学习2-- 神经网络及反向传播

前馈神经网络结构

网络结构: 许多函数复合在一起表示,

​ 链式结构是神经网络中最经常用的结构 f(x)=f(3)(f(2)(f(1)(x)))f(x) = f^{(3)}(f^{(2)}(f^{(1)}(x)))

深度: 链的全部长度

输出层: 前馈神经网络的最后一层

隐藏层: 中间层

宽度: 隐藏层的维数决定模型的宽度

神经元(单元) : 表示一个从向量到标量的函数,持有一定的模型参数值。接受

训练数据中的标签只是指导了最后一层的输出,由学习算法来制定中间层产生的输出

为什么要使用非线性函数进行变换?

深度学习2-- 神经网络及反向传播

**函数引入了非线性的拟合能力

不加非线性**函数的多层感知器只能解决多条直线相交划分的类的问题,而且层数越多,相对复杂。相反,加入了非线性**函数的多层感知器可以解决分界线为曲线、圆等分类问题,从而能够真正解决非线性的分类问题。

深度学习2-- 神经网络及反向传播

我们通常定义: 每一个输入数据为n维向量,

wij(l)w_{ij}^{(l)} 为 第 l 层 第 i 个神经元 与 第 j 个 输入相连的权重,第 l 层中每个神经元的权重为 k 维向量, k 为 (l -1) 层神经元的数量

第 l 层中所有 p 个神经元的权重组成矩阵 WlR[p,k]W^{l} \in \mathbb{R}[p,k]

y(l))=output(l)=f(W(l)output(l1)+b)y^{(l))} = output^{(l)} = f(W^{(l)} \cdot output^{(l-1)}+b)​

反向传播算法 (Back propagation)

反向传播概念讲解

反向传播算法的核心通过比较输出 y 和期望输出t , 向输入层方向逐层计算梯度并更新权值,与前馈运算正好相反

如果训练得到的结果和期望值不一致,说明权重 w 不恰当,这里使用的调整的手法就是反向传播。

向前解释: 从输入到输出的方向

向后解释 : 从输出到输入的方向

外国文献: bottom 输入,top 输出 。 top to bottom 从输入到输出

深度学习2-- 神经网络及反向传播

推导及公式

写**函数或者代价函数的话 ,要理解反向传播的概念。

Cost(x)=12(ty)2Cost(x) = \frac{1}{2}(t -y)^{2} – 损失函数定义

σ(x)=sigmoid(x)=11+ex\sigma(x) = sigmoid(x) = \frac{1}{1+ e^{-x}} – **函数定义

hj(l)=σ(jwij(l)hj(l1)+biasi(l))h_j^{(l)}= \sigma(\sum_{j} w^{(l)}_{ij}h_{j}^{(l-1)}+bias_{i}^{(l)})​ – 经过**函数的隐层输出

logit(i)l=jwij(l)hj(l1)+biasi(l)logit_{(i)}^{l} = \sum_{j} w^{(l)}_{ij}h^{(l-1)}_{j}+bias_{i}^{(l)} – 未经过**函数的隐层计算

δj(l)=Costlogitj(l)\delta_{j} ^{(l)} = \frac{\partial Cost}{\partial logit_{j}^{(l)}} – 神经元固有的参数

Costwij(l)=δj(l)×hj(h1)\frac{\partial Cost}{\partial w_{ij}^{(l)}} = \delta_{j}^{(l)} \times h_{j}^{(h-1)}

推导出第一个重要的公式:

δi(L)=Costlogiti(L)=yCost×σ(logiti(L))\delta_{i}^{(L)} = \frac{\partial Cost}{\partial logit_{i}^{(L)}} = \triangledown_{y}Cost \times \sigma'(logit_{i}^{(L)}) (1)

计算最后一层神经元的固有参数的更新

固有参数层与层之间关系重要公式

δi(l)=jδi(l+1)wji(l+1)σ(logiti(l))\delta_{i}^{(l)} = \sum_{j}\delta_{i}^{(l+1) }w_{ji}^{(l+1)}\sigma'(logit_{i}^{(l)}) (2)

当前神经元固有函数的更新是以下两部分的乘积

1.下一层所有神经元与当前神经元和下一层神经元的权重相乘之和

2.当前神经元输出的**函数的导数

损失函数对于权重、偏置的偏导数,求取变化量的重要公式:

Costwij(l)=hj(l1)δi(l)\frac{\partial Cost}{\partial w_{ij}^{(l)}} = h_{j}^{(l-1)}\delta_{i}^{(l)} (3)

Costbiasi(l)=δi(l)\frac{\partial Cost}{\partial bias_{i}^{(l)}} = \delta_{i}^{(l)} (4)

权重、偏置的更新公式同单个神经元公式:

wnew=wold+η(ty)w,bnew=bold+η(ty)w_{new} = w_{old} + \eta(t-y)w, \quad b_{new} = b_{old} + \eta(t-y)

感知器的梯度下降和神经网络的反向传播方法的对比:

反向传播算法也是利用了梯度下降的方法,从输出层逐层向前进行权值、偏置的更新。每轮迭代每一层更新一次。感知器一轮迭代这个神经元更新一次。

神经网络的优化

梯度消失 (vanishing gradient)

使用 sigmoid 函数 时,它的导数为
σ(x)=σ(x)(1σ(x))δi(l)=jδi(l+1)wji(l+1)σ(logiti(l)) \sigma'(x) = \sigma(x)(1 - \sigma(x)) \\ \delta_{i}^{(l)} = \sum_{j}\delta_{i}^{(l+1) }w_{ji}^{(l+1)}\sigma'(logit_{i}^{(l)})
sigmoid 导数最大值为0.25,它是关于y轴对称的二次曲线 每一次传播都会是原来的 14\frac{1}{4},越靠近输入端的层上的δ\delta越小。因此,越靠近输入层的神经元对应的权重更新就会越小,很可能就几乎为0。

zig zag 现象

由于sigmoid **函数输出为正数,由之前的公式可以得到 经过**函数的隐层输出 hi>0h_{i} >0 。同一层某个神经元的δ\delta 是固定的,对于前一层连接这个神经元的权重的更新值。所以Costwij(l)=hj(l1)δi(l)\frac{\partial Cost}{\partial w_{ij}^{(l)}} = h_{j}^{(l-1)}\delta_{i}^{(l)} 总是同正,或者同负。 不可能出现某个权重增加,某个权重减小这种情况。 如果是有w11,w12w_{11},w_{12} 他们的更新的梯度Δw11,Δw12\Delta w_{11}, \Delta w_{12} 总会出现在1,3象限。

所以梯度更新有可能出现拉链状更新的情况,由下图可知。这样会极大增长优化时长。

深度学习2-- 神经网络及反向传播

神经元死亡

ReLu 函数如果落入x轴的负半轴上。神经元输出为 0。 会造成:

  1. 下一层神经元权重不会更新
  2. 由于输出是0,自身的**函数的导数也为0,这样对应层的δ\delta 也为0。对应权重更新公式可知。自身的所有权重不会得到更新。

工业经验,如果学习率设置过大。神经元死亡过多,基本就没办法挽救了。所以可以根据神经元死亡数量来调整学习率。

反向传播代码实现

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))
def sigmoidDerivationx(y):
    return y * (1 - y)


if __name__ == '__main__':
    # 初始化一些参数
    alpha = 0.5
    numIter = 1000000 #迭代次数
    w1 = [[0.15, 0.20], [0.25, 0.30]]  # Weight of input layer
    w2 = [[0.40, 0.45], [0.50, 0.55]]
    # print(np.array(w2).T)
    b1 = 0.35
    b2 = 0.60
    x = [0.05, 0.10]
    y = [0.01, 0.99]
    # 前向传播
    z1 = np.dot(w1, x) + b1     # dot函数是常规的矩阵相乘
    a1 = sigmoid(z1)

    z2 = np.dot(w2, a1) + b2
    a2 = sigmoid(z2)
    for n in range(numIter):
        # 反向传播 使用代价函数为C=1 / (2n) * sum[(y-a2)^2]
        # 分为两次
        # 一次是最后一层对前面一层的错误

        delta2 = np.multiply(-(y-a2), np.multiply(a2, 1-a2))
        # for i in range(len(w2)):
        #     print(w2[i] - alpha * delta2[i] * a1)
        #计算非最后一层的错误
        # print(delta2)
        delta1 = np.multiply(np.dot(np.array(w2).T, delta2), np.multiply(a1, 1-a1))
        # print(delta1)
        # for i in range(len(w1)):
            # print(w1[i] - alpha * delta1[i] * np.array(x))
        #更新权重
        for i in range(len(w2)):
            w2[i] = w2[i] - alpha * delta2[i] * a1
        for i in range(len(w1)):
            w1[i] = w1[i] - alpha * delta1[i] * np.array(x)
        #继续前向传播,算出误差值
        z1 = np.dot(w1, x) + b1
        a1 = sigmoid(z1)
        z2 = np.dot(w2, a1) + b2
        a2 = sigmoid(z2)
        print(str(n) + " result:" + str(a2[0]) + ", result:" +str(a2[1]))
        # print(str(n) + "  error1:" + str(y[0] - a2[0]) + ", error2:" +str(y[1] - a2[1]))

参考博客:

https://www.cnblogs.com/fuqia/p/8982405.html