本文适用于已经对感知机、神经网络有初步了解,但上手比较困难,愿意推导公式,更深入了解神经网络的朋友。

引言

前馈神经网络是所有神经网络中最简单,也是最有效的一种。从单个神经元的角度看,不过就是设计权值与偏置的简单感知机运算。但到底网络是如何实现的?特别是back propagation的原理?我相信刚刚入门的很多朋友与我一样有很多疑惑,但又不想总是调包,那么我们就慢慢推导公式吧。

需要准备

  1. 良好的线性代数知识,特别是矩阵的运算。
  2. 微积分知识,特别是偏导和链式公式的应用。
  3. 基本的python技巧。

符号说明

wljkwjklw_{jk}^{l}:表示从(l−1)(l−1)(l-1)层的第kkk个神经元到第l" role="presentation" style="position: relative;">lll层的第jjj个神经元的连接上的权重。虽然从直观上不太好理解为什么要这样表示(通常应该表示为wkjl" role="presentation" style="position: relative;">wlkjwkjlw_{kj}^{l}),但请先接受这种写法。可以对相邻两层的所有权重用矩阵的形式表示为wlwlw^l。

σσ\sigma :表示激活函数,本文都使用Sigmoid function。

bljbjlb_{j}^{l}:表示第lll层j" role="presentation" style="position: relative;">jjj神经元的偏置,可以对同一层的神经元表示为blblb^l,记为偏置向量。

aljajla_{j}^l:表示第lll层j" role="presentation" style="position: relative;">jjj神经元的激活值,可以对同一层的神经元表示为alala^l,记为激活向量。
由BP神经网络的定义可得:al=σ(wlal−1+bl)al=σ(wlal−1+bl)a^l = \sigma (w^la^{l-1}+b^l)。

zlzlz^l:表示带权输入,zl=wlal−1+blal=σ(zl)zl=wlal−1+blal=σ(zl)z^l = w^la^{l-1}+b^l\quad a^l = \sigma(z^l)。

CCC :表示代价函数,定义为C=12n∑||y(x)−aL(x)||2" role="presentation" style="position: relative;">C=12n∑||y(x)−aL(x)||2C=12n∑||y(x)−aL(x)||2C = \frac {1}{2n} \sum ||y(x) - a^L(x)||^2,其中y(x)y(x)y(x)表示每个样本的真实输出,LLL表示神经网络的总层数。

代价函数

BP神经网络的向前传播很简单,就使用之前提到的矩阵形式就可以计算,当我们初始化所有权重和偏置时,得到的结果输出与目标输出肯定有较大差距,我们使用代价函数来度量这种差距。定义如下:

C=12n∑||y(x)−aL(x)||2" role="presentation">C=12n∑||y(x)−aL(x)||2C=12n∑||y(x)−aL(x)||2

C = \frac {1}{2n} \sum ||y(x) - a^L(x)||^2

那么,当输入和输出固定时,CCC就是关于w和b" role="presentation" style="position: relative;">w和bw和bw和b的函数,我们需要对其进行求偏导,以此来更新代价函数。

我们需要对代价函数进行如下定义(假设):

  1. 代价函数可以写成一个在每个训练样本xxx上的代价函数Cx" role="presentation" style="position: relative;">CxCxC_x的均值C=1n∑xCxC=1n∑xCxC = \frac{1}{n}\sum _xC_x。
  2. 将CCC仅看成输出激活值aL" role="presentation" style="position: relative;">aLaLa^L的函数。

以下公式,不加说明,CCC都指特定的Cx" role="presentation" style="position: relative;">CxCxC_x。

反向传播的四个方程

反向传播其实就是对权重和偏置变化影响函数过程的理解。最终就是需要计算∂C∂wljk和∂C∂blj∂C∂wjkl和∂C∂bjl\frac{\partial C}{\partial w_{jk}^{l}}和 \frac{\partial C}{\partial b_{j}^{l}}。

我们首先定义一个中间量δlj=∂C∂zljδjl=∂C∂zjl\delta_j^l = \frac{\partial C}{\partial z_{j}^{l}} ,表示为第l层第jl层第jl层第j个神经元的误差,然后将δljδjl\delta_j^l 关联到∂C∂wljk和∂C∂blj∂C∂wjkl和∂C∂bjl\frac{\partial C}{\partial w_{jk}^{l}}和 \frac{\partial C}{\partial b_{j}^{l}}。

这里可能会感到疑惑,为什么会定义这样一个误差呢? 我们想象,当在一个神经元的带权输入上增加一个很小的变化ΔzljΔzjl\Delta z_j^l,使得神经元输出由σ(zlj)σ(zjl)\sigma (z_j^l)变为σ(zlj+Δzlj)σ(zjl+Δzjl)\sigma (z_j^l + \Delta z_j^l),那么这个变化将会向网络后面的层进行传播,最终导致整个代价产生∂C∂zljΔzlj∂C∂zjlΔzjl\frac{\partial C}{\partial z_{j}^{l}}\Delta z_j^l的变化。因此,这里有一种启发式的认识,∂C∂zlj∂C∂zjl\frac{\partial C}{\partial z_{j}^{l}}是神经元误差的度量:

δlj=∂C∂zljδjl=∂C∂zjl

\delta_j^l = \frac{\partial C}{\partial z_{j}^{l}}

在给出方程严谨的证明前,我们不妨从直观上看一下这些方程,这有助于我们的进一步理解。

  • 输出层误差方程:

    • δLj=∂C∂aLjσ′(zLj)δjL=∂C∂ajLσ′(zjL)

      \delta _j^L =\frac{\partial C}{\partial a_j^L} {\sigma}' (z^L_j)

    • 右边的第一个项∂C∂aLj∂C∂ajL\frac{\partial C}{\partial a_j^L}表示代价随着第jjj个输出激活值的变化而变化的速度。第二项刻画了在zjl" role="presentation" style="position: relative;">zljzjlz_j^l处激活函数σσ\sigma 变化的速度,可以理解为ΔaljΔajl\Delta a_j^l。
    • 注意到这个式子的每一部分都是很好计算的。我们如果已知了一个代价函数和激活函数,那么在前向传播中就可以算得每一个δLjδjL\delta _j^L。
    • 用矩阵的形式表示第一个式子则更加简单和美妙,注意⊙⊙\odot表示矩阵对应元素相乘:
    • δL=∇aC⊙σ′(zL)δL=∇aC⊙σ′(zL)

      \delta ^L = \nabla_aC\odot {\sigma}' (z^L)

  • 使用下一层的误差来表示当前层的误差:
    • δl=((wl+1)Tδl+1)⊙σ′(zl)δl=((wl+1)Tδl+1)⊙σ′(zl)

      \delta^l = ((w^{l+1})^T\delta^{l+1})\odot {\sigma}'(z^l)

    • 当我们知道l+1l+1l+1层的误差δl+1δl+1\delta^{l+1},当我们应用转置的权重矩阵(wl+1)T(wl+1)T(w^{l+1})^T,我们可以凭直觉理解为它是沿着网络反向移动误差,给我们度量在lll层输出误差的计算方法
    • 然后,使用hadamard乘积运算,让误差通过l" role="presentation" style="position: relative;">lll层的激活函数反向传递回来并给出在第lll层带权输入的误差δ" role="presentation" style="position: relative;">δδ\delta。
      • 通过组合前两个公式,我们可以计算出任意一层的带权输入误差。
    • 代价函数关于网络中任意偏置的改变率:

      • ∂C∂bij=δlj∂C∂bji=δjl

        \frac{\partial C}{\partial b_j^i} = \delta _j^l

      • 通过这个方程我们发现,我们需要计算的∂C∂bij∂C∂bji\frac{\partial C}{\partial b_j^i} 与δljδjl\delta _j^l完全一致。
    • 代价函数关于任何一个权重的改变率:
      • ∂C∂wijk=al−1kδlj∂C∂wjki=akl−1δjl

        \frac{\partial C}{\partial w_{jk}^i} = a_k^{l-1}\delta_j^l

      • 这告诉我们如何求∂C∂wijk∂C∂wjki\frac{\partial C}{\partial w_{jk}^i} 。其中al−1k和δljakl−1和δjla_k^{l-1}和\delta_j^l我们都已经知道如何计算了,便于理解,我们可以将其化简为:
      • ∂C∂w=ainδout∂C∂w=ainδout

        \frac{\partial C}{\partial w} = a_{in} \delta_{out}

      • 我们发现,当激活值ainaina_{in}很小时,∂C∂w∂C∂w\frac{\partial C}{\partial w} 也会变得很小。这时候,我们就说权重缓慢学习,表示在进行梯度下降时,这个权重不会改变太多。

通过之前的式子,我们可以发现,如果输入神经元激活值很低,或者输出神经元已经饱和了,权重会学习的非常缓慢。这可以帮助我们选择激活函数。例如,我们可以选择一个不是sigmoid函数的激活函数,使得σ′σ′{\sigma}'总是证书,不会趋近于0,这样会防止原始的S型神经元饱和时学习速率下降的情况。

四个基本方程的推导

总结下来一共有四个重要公式:

  1. δL=∇aC⊙σ′(zL)δL=∇aC⊙σ′(zL)\boldsymbol {\delta ^L = \nabla_aC\odot {\sigma}' (z^L)}

    • ∵δLj=∂C∂zLj∵δjL=∂C∂zjL

      \because \delta_j^L = \frac{\partial C}{\partial z_{j}^{L}}

    • ∴δLj=∑k∂C∂aLk∂aLk∂zLj=∂C∂aLj∂aLj∂zLj=∂C∂aLjσ′(zLj)∴δjL=∑k∂C∂akL∂akL∂zjL=∂C∂ajL∂ajL∂zjL=∂C∂ajLσ′(zjL)

      \therefore \delta_j^L =\sum\limits _k \frac{\partial C}{\partial a_k^L} \frac {\partial a_k^L}{\partial z_{j}^{L}} = \frac{\partial C}{\partial a_j^L} \frac {\partial a_j^L}{\partial z_{j}^{L}} = \frac{\partial C}{\partial a_j^L} {\sigma}' (z^L_j)

  2. δl=((wl+1)Tδl+1)⊙σ′(zl)δl=((wl+1)Tδl+1)⊙σ′(zl)\boldsymbol {\delta^l = ((w^{l+1})^T\delta^{l+1})\odot {\sigma}'(z^l)}
    • ∵δlj=∂C∂zlj=∑k∂C∂zl+1k∂zl+1k∂zlj,表示这一层的神经元对下一层都有影响∵δjl=∂C∂zjl=∑k∂C∂zkl+1∂zkl+1∂zjl,表示这一层的神经元对下一层都有影响

      \because \delta_j^l = \frac{\partial C}{\partial z_{j}^{l}} = \sum\limits _k \frac{\partial C}{\partial z_{k}^{l+1}} \frac{\partial z_k^{l+1}}{\partial z_{j}^{l}},表示这一层的神经元对下一层都有影响

    • ∴δlj=∑kδl+1k∂zl+1k∂zlj∴δjl=∑kδkl+1∂zkl+1∂zjl

      \therefore \delta_j^l = \sum\limits _k\delta_k^{l+1} \frac{\partial z_k^{l+1}}{\partial z_{j}^{l}}

    • ∵zl+1k=∑jwl+1kjσ(zlj)+bl+1k∵zkl+1=∑jwkjl+1σ(zjl)+bkl+1

      \because z_k^{l+1} =\sum\limits_j w_{kj}^{l+1}\sigma(z_j^l) + b_k^{l+1}

    • ∴∂zl+1k∂zlj=wl+1kjσ′(zlj)∴∂zkl+1∂zjl=wkjl+1σ′(zjl)

      \therefore \frac {\partial z_k^{l+1}}{\partial z_j^l} = w_{kj}^{l+1}{\sigma}'(z_j^l)

    • 带入可得:δlj=∑kδl+1kwl+1kjσ′(zlj)带入可得:δjl=∑kδkl+1wkjl+1σ′(zjl)

      带入可得:\delta_j^l = \sum\limits _k\delta_k^{l+1} w_{kj}^{l+1}{\sigma}'(z_j^l)

  3. ∂C∂bij=δlj∂C∂bji=δjl\boldsymbol {\frac{\partial C}{\partial b_j^i} = \delta _j^l}
    • ∵blk=zlk−∑jwlkjσ(zl−1j)∵bkl=zkl−∑jwkjlσ(zjl−1)

      \because b_k^l = z_k^l - \sum\limits _j w_{kj}^l\sigma(z_j^{l-1})

    • ∴δlj=∂C∂zlj=∂C∂blj∂blj∂zlj=∂C∂blj∴δjl=∂C∂zjl=∂C∂bjl∂bjl∂zjl=∂C∂bjl

      \therefore \delta_j^l = \frac{\partial C}{\partial z_{j}^{l}} = \frac{\partial C}{\partial b_j^l} \frac{\partial b_j^l}{\partial z_j^l} = \frac{\partial C}{\partial b_j^l}

  4. ∂C∂wijk=al−1kδlj∂C∂wjki=akl−1δjl\boldsymbol {\frac{\partial C}{\partial w_{jk}^i} = a_k^{l-1}\delta_j^l}
    • ∵zlj=∑kwljkal−1k+blj∵zjl=∑kwjklakl−1+bjl

      \because z_j^l = \sum\limits _k w_{jk}^l a_k^{l-1} + b_j^l

    • ∴∂zlj∂wljk=al−1k⇒∂C∂zlj∂zlj∂wljk=al−1k∂C∂zlj∴∂zjl∂wjkl=akl−1⇒∂C∂zjl∂zjl∂wjkl=akl−1∂C∂zjl

      \therefore \frac {\partial z_j^l}{\partial w_{jk}^l} = a_k^{l-1} \Rightarrow \frac {\partial C}{\partial z_j^l} \frac {\partial z_j^l}{\partial w_{jk}^l} = a_k^{l-1} \frac {\partial C}{\partial z_{j}^l}

    • ∴∂C∂wijk=al−1kσlj∴∂C∂wjki=akl−1σjl

      \therefore \frac{\partial C}{\partial w_{jk}^i} = a_k^{l-1}\sigma _j^l

首先我们可以通过第一个公式算出δLδL\delta ^L,然后利用第二个公式的递推关系可以算出所有的δδ\delta,这样,我们就可以很轻松的算出我们想要的每一个∂C∂bij以及∂C∂wijk∂C∂bji以及∂C∂wjki\frac{\partial C}{\partial b_j^i} 以及\frac{\partial C}{\partial w_{jk}^i}。

在反向传播中,为了减少计算量,很常见的方法是使用随机梯度下降。思想也很简单,每一个样本都需要进行参与求导实在是计算量太大,但我们可以只去一小部分来进行更新权重,多算几次取平均。

总结

我们使用Mini-batch BGD来进行BP神经网络训练,具体步骤为:

  1. 输入训练样本集合
  2. 对每个训练样本xxx:设置对应的输入激活ax1" role="presentation" style="position: relative;">a1xax1a_x^{1},并进行:
    • 前向传播:对每个l=2,3,4...,Ll=2,3,4...,Ll = 2,3,4...,L,计算zlxzxlz_x^{l}
    • 输出误差σlx=∇aCx⊙σ′(zLx)σxl=∇aCx⊙σ′(zxL)\sigma _x^{l } = \nabla_aCx\odot {\sigma}' (z_x^L)
    • 反向转播误差:对每个l=L−1,L−2,...,2l=L−1,L−2,...,2l = L-1,L-2,...,2,计算δlx=((wl+1)Tδl+1x)⊙σ′(zlx)δxl=((wl+1)Tδxl+1)⊙σ′(zxl)\delta_x^l = ((w^{l+1})^T\delta_x^{l+1})\odot {\sigma}'(z_x^l)
  3. 梯度下降:根据wl→wl−ηm∑xδlx(al−1x)Twl→wl−ηm∑xδxl(axl−1)Tw^l \rightarrow w^l - \frac {\eta}{m}\sum _x\delta_x^l(a_x^{l-1})^T和bl→bl−ηm∑xδlxbl→bl−ηm∑xδxlb^l \rightarrow b^l - \frac {\eta}{m}\sum _x\delta_x^l 更新权值和偏置。

反向传播到底在干什么?

首先,反向传播算法加速了神经网络的学习。设想,我们如果只按照最基本的思路计算权重:

∂C∂wj≈C(w+ϵej)−C(w)ϵ∂C∂wj≈C(w+ϵej)−C(w)ϵ

\frac {\partial C}{\partial w_j} \approx \frac{C(w+\epsilon e_j)- C(w)}{\epsilon }

那么,我们可以使用两个相近的权重来估计∂C∂wj∂C∂wj\frac {\partial C}{\partial w_j}。但如果我们这样做,需要对每个权重进行前向传播估算,计算量是非常大的。

反向传播聪明的地方就是它确保我们可以同时计算所有的偏导数∂C∂wj∂C∂wj\frac {\partial C}{\partial w_j}因此,比起直接计算导数,显然反向传播有着更大优势。

参考资料

neuralnetworksanddeeplearning

欢迎关注我的个人博客。

前馈神经网络原理与实现相关推荐

  1. 什么是多层前馈神经网络,多层前馈神经网络原理

    前馈神经网络.BP神经网络.卷积神经网络的区别与联系 一.计算方法不同1.前馈神经网络:一种最简单的神经网络,各神经元分层排列.每个神经元只与前一层的神经元相连.接收前一层的输出,并输出给下一层.各层 ...

  2. 《深度学习》之 前馈神经网络 原理 详解

    前馈神经网络 1. 深度学习: **定义:**深度学习是机器学习的一个研究方向,它基于一种特殊的学习机制.其特点是建立一个多层学习模型,深层级将浅层级的输出作为输入,将数据层层转化,使之越来越抽象.是 ...

  3. 前馈神经网络——深度学习之神经网络核心原理与算法

    五月两场 | NVIDIA DLI 深度学习入门课程 5月19日/5月26日一天密集式学习  快速带你入门阅读全文> 正文共7165个字,85张图,预计阅读时间35分钟. 因上几次读者反映,公式 ...

  4. 【深度学习原理第1篇】前馈神经网络,感知机,BP神经网络

    前馈神经网络(FNN) 前馈神经网路是一种单向多层的网络结构,信息从输入层开始,逐层向一个方向传递,即单向传递,一直到输出层结束.前馈的意思就是指传播方向指的是前向.前馈神经网络由三部分组成:输入层( ...

  5. 前馈神经网络FNN(原理及实现)

    一.引例 在介绍前馈神经网络之前先介绍感知机的概念: x1,x2是输入信号,y是输出信号,w1,w2是权重.输入信号被送往神经元时,会被乘以固定的权重,权重越大,对应的权重的信号的重要性越高,神经元会 ...

  6. 机器学习与高维信息检索 - Note 5 - (深度)前馈神经网络((Deep) Feedforward Neural Networks)及基于CVXOPT的相关实例

    Note 5 - (深度)前馈神经网络((Deep) Feedforward Neural Networks)及相关实例 5.1 FNN的定义和动机 粗略地说,前馈神经网络(FNN)是一种特殊的函数类 ...

  7. 基于Numpy构建全连接前馈神经网络进行手写数字识别

    文章目录 (一) 问题描述 (二) 设计简要描述 (三) 程序清单 (四) 结果分析 (五) 调试报告 (六) 实验小结 (一) 问题描述 不使用任何机器学习框架,仅仅通过Numpy库构建一个最简单的 ...

  8. 机器学习——前馈神经网络

    一.神经网络基础 1. 神经元模型 神经网络中最基本的单元是神经元模型(neuron). 细胞体分为两部分,前一部分计算总输入值(即输入信号的加权和,或者说累积电平),后一部分先计算总输入值与该神经元 ...

  9. 前馈神经网络中的前馈_前馈神经网络在基于趋势的交易中的有效性(1)

    前馈神经网络中的前馈 This is a preliminary showcase of a collaborative research by Seouk Jun Kim (Daniel) and ...

最新文章

  1. Fortinet宣布收购AccelOps
  2. MySQL SQL优化
  3. python键盘输入代码,python监控键盘输入实例代码
  4. GDB 远程调试Linux (CentOS)
  5. 公司注册资金100万欠债1千万,股东还100万,剩下的900万怎么办?
  6. 【计蒜客习题】消除字符串
  7. Android 升级到android studio 2.2项目死活run不起来
  8. 字符串分割与存入List集合
  9. 【BZOJ1857】【SCOI2010】传送带 [三分]
  10. 设置框开始隐藏状态html5,小猿圈分享HTML5中form如何关闭自动完成功能的方法
  11. [Robot Framework] SikuliLibrary的关键字执行依赖java进程,但是上次的java进程如果没有杀掉,robot framework控制台的日志出不来,怎么办?...
  12. java innodb存储引擎_InnoDB存储引擎简介
  13. scala 高阶函数学习
  14. java配置springmvc_告别XML 使用JAVA配置SpringMVC
  15. centos7 如何使用ReaR进行系统备份(如何使用NFS方法设置ReaR备份)
  16. 人体面部检测python_使用Python检测面部特征
  17. 【测绘程序设计】Excel度(°)转换度分秒(° ‘ “)模板附代码超实用版
  18. ios 打包分发全流程
  19. c#Winform自定义控件-目录
  20. 算法笔记习题 2-9小节

热门文章

  1. 时尚礼品网html代码,礼品赠送明细.html
  2. 华为P60、P60 Pro、P60 Art区别对比评测
  3. R-CNN、Fast R-CNN、Faster R-CNN
  4. 打卡第5天:安恒元旦杯-爆破鬼才续与linux shell
  5. 虚拟机双网卡设置(外网+内网)
  6. sql 四舍五入后补零
  7. 19 行代码能搭建一个女朋友?!
  8. dell服务器 指示灯_戴尔R710服务器硬盘出现告警(绿黄灯闪),解决方法
  9. NIO模型(Non Blocking IO)
  10. 2018-8-10-使用-RetroShare-分享资源