神经网络二之神经网络反向传播原理与python编程实现

欢迎关注鄙人公众号,技术干货随时看!
神经网络二之神经网络反向传播原理与python编程实现_第1张图片

鄙人的新书《elasticsearch7完全开发指南》,欢迎订阅!

https://wenku.baidu.com/view/8ff2ce94591b6bd97f192279168884868762b8e7

《kibana权威指南》

https://wenku.baidu.com/view/24cfee1ce43a580216fc700abb68a98270feac21

视频教学地址:https://edu.csdn.net/course/detail/8574

技术交流qq群: 659201069

误差

样本数据的真实值与神经网络的输出值之间的差值称为误差,当然一般不会直接使用直接的差值,常用的有回归算法的均方差、分类的交叉熵,这方面不影响我们来讨论神经网络的反向传播原理与过程,所以不做过多讨论。

梯度下降

目前的神经网络层数可达百层以上,激活函数又是非线性的,很难用一个数学表达式来表达整个神经网络的输出,即使能够找一个数学表达式也是非常复杂,如果能够找到一个数学表达式我们就可以用求解方程的形式推导出参数矩阵W(每个一个,可以多达百个),当然这是不太可能实现的。我们看下下面的一个简单的三层神经网络:
  神经网络二之神经网络反向传播原理与python编程实现_第2张图片
  第三层第k个节点的输出表达式如下(自己可以根据前向传播原理推导下,可以参考我的上一篇文章讲的很清楚https://blog.csdn.net/zhaojianting/article/details/80541732):
神经网络二之神经网络反向传播原理与python编程实现_第3张图片
  感觉是不是太复杂了,这只是三层的,按照目前百层网络,输出如果用一个表达式来表达我相信没有能看懂,或者说不可能用计算机来实现。所以后来人就改变了思路,神经网络的训练(或者说优化)就是找到各层参数矩阵让样本的经过神经网络的输出尽可能得接近真实值。这样问题就转变成了差误最小化问题(理想误差为0)。上面已经说了神经网络的输出不可能通过求解方程的方法获得,那么我们也不可能通过令误差等于0,然后求方程来获得。这样数学家(我猜得也可能是计算机专家)发明的类似于枚举的梯度下降算法(其它应该是贪心算法),梯度下降算法的原理大致是:首先随机初始化每层的参数矩阵w,然后可以算得与真实值的误差(第一次误差应该很大,当然也可能你运气好,初始化的W就达到了你的要求),因为是求最小值,我们就沿着梯度下降的方向加一个小步长(求偏导乘以一个常,在二维平面上w有可能左移或右移)△w,用新的w+△w再计算误差值,如此迭代下去就可以得到局部最优解(为什么是局总而不是全局最优,下面会讨论),看下面的一个例子假设误差y=(x-1)**2+1,图像如下:
  神经网络二之神经网络反向传播原理与python编程实现_第4张图片

上面的只有一个谷底可以接近全局最优,如果有两个(当然可是以多个)谷底的情况,这个能不能得到全局最优和初化始值及每次变动的△w(这个是学习率,也就是设定的一个常数,此处不做过多讨论)有关,第一种情部如果初始化值落在较低的谷底附近而学习率不是很大,每次变动都跳不出谷底就可以快速接近全局最优(下图左),第二种情况如果初始化值落在较高的谷底附近而学习率不是很大,每次变动都跳不出谷底,那只能得到局部最优(下图中),第三种情况如果初始化值落在较低的谷底附近而学习率较大,其中有一次变动跳出了较高的谷底,那么也是可以接近全局最优(下图右)
  神经网络二之神经网络反向传播原理与python编程实现_第5张图片

误差的反向传播

上面讨论的根据误差,用梯度下降的方法也只能适应于更新一个参数的值,多层神经网络只有最后一层有误差,中间隐藏层的w参数矩阵如卫生间更新呢,这就是要讨论的重点,误差的反向传播,慢慢来不要急,我们先看下面最简单的一个神经网络(先不考虑激活函数,其原理是一样的):
  神经网络二之神经网络反向传播原理与python编程实现_第6张图片
  根据上面的原理我们很容易求很每次更新的△w=L(E/x) 其中E是样本产生的误差,L是上面提到的学习率。下面看稍微复杂点的情况:

神经网络二之神经网络反向传播原理与python编程实现_第7张图片

我们上面只有一个误差E,怎么根据公式△w=L(E/x) 更新两个参数呢,如果都采用E来计算,显然不合理,一般的做法的就是按照W1和W2的比例分隔误差(这是误差反向传播的第一步),如下图所示:
  神经网络二之神经网络反向传播原理与python编程实现_第8张图片
  这样就有:
  △w1=L(E1/x1)
  △w2=L(E2/x2)

下面我们看多层神经网络误差的反向传播过程:
神经网络二之神经网络反向传播原理与python编程实现_第9张图片
  根据上面的讨论我们很容易得到隐藏层到输出层的参数矩阵
神经网络二之神经网络反向传播原理与python编程实现_第10张图片
  要想计算输入层到到隐藏层的误差我们需要隐藏层的输出误差,但隐藏层实际上是没有输出误差的,这就需要我们造出来,也就是从输出层先分隔再重组,计算公式如下(一看就能明白)
神经网络二之神经网络反向传播原理与python编程实现_第11张图片
  这样我们就得了隐藏层的误差,接下来我们就可以按照上面介绍计算输入层到隐藏层的参数更新步长了:
神经网络二之神经网络反向传播原理与python编程实现_第12张图片
  上面就是神经网络模型优化的核心思想,反向传播,也就是误差的反向传播来更新各个参数。根据上面的反向传播原理,无论神经网络有多少层,每层有多少个结点都是适用的。

向矩阵运算的转化(将计算矢量化)

还是利用上面的神经网络举例,但计算方式可以推广到多层神经网络:

第一步:误差矢量化
神经网络二之神经网络反向传播原理与python编程实现_第13张图片
  第二步:隐藏层误差矢量化

自己按照矩阵乘法算一下,正好可以得到下面的一个1行2列的矩阵(就是这么巧合,如果没有巧合百层神经网也就不会出现了)
  神经网络二之神经网络反向传播原理与python编程实现_第14张图片
  到现在我们可以根据上面的公式计算隐藏层的误差的,但是上面的表达式可以进一步简化(能把复杂问题简单化的都是创举,在工作中遇到过不少装B货,明明很简单的几行代码就可以解决的问题非要写得上成百上千行结果BUG也是成百上千。鄙人追求的目标就是用最单的方式解决复杂的问题,因为越简单越容易受控,自然而然就越稳定,扯远了…………这年头生活压力都不小,很少有机会发发牢骚!),其它我们分析下分母只是起到了归一化的作用我们完全可以舍弃,最后就变成了如下的形式
  神经网络二之神经网络反向传播原理与python编程实现_第15张图片
 最终隐藏层的误差表达式:
 神经网络二之神经网络反向传播原理与python编程实现_第16张图片
 到目前为止我们已经完成了一大半,我们已经把输出层误差、参数、隐藏层误差实现了矢量化,但是我们还没有完成最重要最核心的目标就是求得各个参数矩阵的更新步长:△W

第三步:偏导公式推导,完成最终求解这里写图片描述,这里W是大代表的是第j层和k层的参数矩阵,我们上面的推导为了彻底讲解原理,都是用的三层神经网络,而且每个节点的输出没有应用激活函数。从现在开始我们的推导基于多层,带激活函数的神经网络,最后形成的公式对所有的全连接神经网络都适用。
  对只有一层且只有一个神经元的网络,我们有△w=L(E/x) ,这里L是设置的学习率,而E/x是误差函数对w的导数,现在我们就自然而然的推广到一般情况(这里我们的误差采用输出结点误差的平方和,其它的也是一样的),我们先只考虑输出层的公式推导:
  神经网络二之神经网络反向传播原理与python编程实现_第17张图片
  我们的偏导就可以写出来了:
  神经网络二之神经网络反向传播原理与python编程实现_第18张图片
  这个是输出层各个节点的误码差的平方各。这个表达式是需要求和的,我们能不能简化下呢,我们再看下神经网络的结构图,节点k的输出这里写图片描述只取决于连接到这个节点的权重,也就是只取决于这里写图片描述,不依赖于这里写图片描述,因为b和k之间没有连接,因此b与k无关联,这里写图片描述是输出层第b个节点的连接权重。这样我们就可以舍弃除了这里写图片描述所连接的这里写图片描述外的所有其它的这里写图片描述,于是我们得到了下面的公式,也就是根本不用求和:
这里写图片描述
  我们利用微积分进行分解(这个如果你不懂我也没办法,当然也不需要懂,因为根本不用推导,我们只是看懂原理使用罢了):
  这里写图片描述
  我们再把E的表达式这里写图片描述代入上式可以得到:
  这里写图片描述
  根据前向传播原理,我们把这里写图片描述代入上式可以得到:
  神经网络二之神经网络反向传播原理与python编程实现_第19张图片
  现在只剩下一个sigmoid微分函数,这个函数的微分我们自己是推不出来的,可以说现在常用的激活函数我们基本上也都是求不微分的(因为一个微分函数可能是一个数学家一辈子的成果),但是数学家们帮我们推导好的,我们直接查表拿来用就是了:
  ,这里写图片描述
  于是我们得到下面的公式:
这里写图片描述
  别忘了我们要求的是斜率,最前面的2是可以去除的,因为我们自己会设置一个学习率的,于是得到下面的结果:
这里写图片描述
  上面的公式可以直接用来更新隐藏层到输出层的参数,那么前面的参数怎么更新呢?其实很简单,我们只要把输出和参数换成前一层的就可以的,也就是上面所讨论的反向传播,把误差就成隐藏层误差就可以了,现在要把这个计算过程转化为矩阵运算,推导过程和上面的误差传播是一样的,最后可以得到如一下矩阵运算公式:
  
  神经网络二之神经网络反向传播原理与python编程实现_第20张图片
  到此就全部讲解了神经网张的反向传播过程,神经网络训练的过程就是通多次迭代更新参数的过程。下面我们看下ptyhon实现反向传的代码:

    # 神经网络模型训练
    def train(self, inputs_list, targets_list):

        #注意维度,输入是[[1],[2]],还是[[1,2]]参数矩的写法也是不一亲的,
        #这个可以自己定,只要保证输放和参数矩阵的维度对应即可,一般的写法是第二种
        inputs = numpy.array(inputs_list, ndmin=2).T
        targets = numpy.array(targets_list, ndmin=2).T

        # 计算隐藏层输出
        hidden_inputs = numpy.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)#激活函数,现在用的较多的是relu

        # 隐藏层到输出层的传播
        final_inputs = numpy.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)

        # 下面两行就是计算隐藏层的误差,也就是上面讨论的误差的反向传播
        output_errors = targets - final_outputs
        hidden_errors = numpy.dot(self.who.T, output_errors)

        # 下面两步是最重要的也就是实现我们上面推导的最后的公式,自己对比下,正好是对应的
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)),
                                        numpy.transpose(hidden_outputs))

        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
                                        numpy.transpose(inputs))

        pass

你可能感兴趣的:(算法)