前馈神经网络原理与实现
本文适用于已经对感知机、神经网络有初步了解,但上手比较困难,愿意推导公式,更深入了解神经网络的朋友。
引言
前馈神经网络是所有神经网络中最简单,也是最有效的一种。从单个神经元的角度看,不过就是设计权值与偏置的简单感知机运算。但到底网络是如何实现的?特别是back propagation的原理?我相信刚刚入门的很多朋友与我一样有很多疑惑,但又不想总是调包,那么我们就慢慢推导公式吧。
需要准备
- 良好的线性代数知识,特别是矩阵的运算。
- 微积分知识,特别是偏导和链式公式的应用。
- 基本的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 = \frac {1}{2n} \sum ||y(x) - a^L(x)||^2
那么,当输入和输出固定时,CCC就是关于w和b" role="presentation" style="position: relative;">w和bw和bw和b的函数,我们需要对其进行求偏导,以此来更新代价函数。
我们需要对代价函数进行如下定义(假设):
- 代价函数可以写成一个在每个训练样本xxx上的代价函数Cx" role="presentation" style="position: relative;">CxCxC_x的均值C=1n∑xCxC=1n∑xCxC = \frac{1}{n}\sum _xC_x。
- 将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}}是神经元误差的度量:
\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型神经元饱和时学习速率下降的情况。
四个基本方程的推导
总结下来一共有四个重要公式:
- δ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)
- δ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)
- ∂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}
- ∂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神经网络训练,具体步骤为:
- 输入训练样本集合
- 对每个训练样本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)
- 梯度下降:根据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 更新权值和偏置。
反向传播到底在干什么?
首先,反向传播算法加速了神经网络的学习。设想,我们如果只按照最基本的思路计算权重:
\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
欢迎关注我的个人博客。
前馈神经网络原理与实现相关推荐
- 什么是多层前馈神经网络,多层前馈神经网络原理
前馈神经网络.BP神经网络.卷积神经网络的区别与联系 一.计算方法不同1.前馈神经网络:一种最简单的神经网络,各神经元分层排列.每个神经元只与前一层的神经元相连.接收前一层的输出,并输出给下一层.各层 ...
- 《深度学习》之 前馈神经网络 原理 详解
前馈神经网络 1. 深度学习: **定义:**深度学习是机器学习的一个研究方向,它基于一种特殊的学习机制.其特点是建立一个多层学习模型,深层级将浅层级的输出作为输入,将数据层层转化,使之越来越抽象.是 ...
- 前馈神经网络——深度学习之神经网络核心原理与算法
五月两场 | NVIDIA DLI 深度学习入门课程 5月19日/5月26日一天密集式学习 快速带你入门阅读全文> 正文共7165个字,85张图,预计阅读时间35分钟. 因上几次读者反映,公式 ...
- 【深度学习原理第1篇】前馈神经网络,感知机,BP神经网络
前馈神经网络(FNN) 前馈神经网路是一种单向多层的网络结构,信息从输入层开始,逐层向一个方向传递,即单向传递,一直到输出层结束.前馈的意思就是指传播方向指的是前向.前馈神经网络由三部分组成:输入层( ...
- 前馈神经网络FNN(原理及实现)
一.引例 在介绍前馈神经网络之前先介绍感知机的概念: x1,x2是输入信号,y是输出信号,w1,w2是权重.输入信号被送往神经元时,会被乘以固定的权重,权重越大,对应的权重的信号的重要性越高,神经元会 ...
- 机器学习与高维信息检索 - Note 5 - (深度)前馈神经网络((Deep) Feedforward Neural Networks)及基于CVXOPT的相关实例
Note 5 - (深度)前馈神经网络((Deep) Feedforward Neural Networks)及相关实例 5.1 FNN的定义和动机 粗略地说,前馈神经网络(FNN)是一种特殊的函数类 ...
- 基于Numpy构建全连接前馈神经网络进行手写数字识别
文章目录 (一) 问题描述 (二) 设计简要描述 (三) 程序清单 (四) 结果分析 (五) 调试报告 (六) 实验小结 (一) 问题描述 不使用任何机器学习框架,仅仅通过Numpy库构建一个最简单的 ...
- 机器学习——前馈神经网络
一.神经网络基础 1. 神经元模型 神经网络中最基本的单元是神经元模型(neuron). 细胞体分为两部分,前一部分计算总输入值(即输入信号的加权和,或者说累积电平),后一部分先计算总输入值与该神经元 ...
- 前馈神经网络中的前馈_前馈神经网络在基于趋势的交易中的有效性(1)
前馈神经网络中的前馈 This is a preliminary showcase of a collaborative research by Seouk Jun Kim (Daniel) and ...
最新文章
- Fortinet宣布收购AccelOps
- MySQL SQL优化
- python键盘输入代码,python监控键盘输入实例代码
- GDB 远程调试Linux (CentOS)
- 公司注册资金100万欠债1千万,股东还100万,剩下的900万怎么办?
- 【计蒜客习题】消除字符串
- Android 升级到android studio 2.2项目死活run不起来
- 字符串分割与存入List集合
- 【BZOJ1857】【SCOI2010】传送带 [三分]
- 设置框开始隐藏状态html5,小猿圈分享HTML5中form如何关闭自动完成功能的方法
- [Robot Framework] SikuliLibrary的关键字执行依赖java进程,但是上次的java进程如果没有杀掉,robot framework控制台的日志出不来,怎么办?...
- java innodb存储引擎_InnoDB存储引擎简介
- scala 高阶函数学习
- java配置springmvc_告别XML 使用JAVA配置SpringMVC
- centos7 如何使用ReaR进行系统备份(如何使用NFS方法设置ReaR备份)
- 人体面部检测python_使用Python检测面部特征
- 【测绘程序设计】Excel度(°)转换度分秒(° ‘ “)模板附代码超实用版
- ios 打包分发全流程
- c#Winform自定义控件-目录
- 算法笔记习题 2-9小节