这一小章节将主要介绍神经网络的主要模型以及神经网络参数优化算法,在下一章节将结合代码进行解析说明。文章所有内容来源于http://neuralnetworksanddeeplearning.com/index.html 个人感觉是对神经网络阐述很详细很细致的一篇文章,建议仔细阅读揣摩。
1.感知机
要想了解神经网络的模型,首先引入一个更为简单的模型-感知机。感知机的基本模型是:输入若干变量数据
x1,x2...xn
,感知机根据相应判别条件输出0或者1(输出结果只有两种可能性:0或者1)
输出结果是0或者1是由下面的条件进行判断的,
wj 是权重系数。直白来说,感知机可以理解为权衡不同系数从而给出决策的模型。当然,感知机可以更为复杂:可以包含更多层次,每一层又可以包含更多单元(如下图所示)。
为了下面更好的引入神经网络的概念,我们将感知机的表达式稍微做些改变:1. 将
∑jwjxj
改写成矩阵表达形式,即
w⋅x
;2. 将阈值
threshold
用变量
b
进行表示,那么最终的感知机模型可以被改写为,
我们称 w 矩阵为weight参数,称
图中的感知机bias设为3,weight为 [−2,−2] 。分析可知:当 x1,x2 分别为00时,感知机输出结果为1;当 x1,x2 分别为01或者10时,感知机输出结果为1;当 x1,x2 分别为11时,感知机输出结果为0。所以该感知机模拟的是NAND操作。
现在考虑使用上述感知机来进行handwriting digit recognition任务,即希望对于如下的手写字母,通过感知机来判断每一个数字的正确归类(如下面图片中的数字分别为 5 0 4 1 9 2)。
我们考虑,假设模型最开始是不具备识别能力的,但是我们希望可以通过一点点地调节参数weight和bias来使得感知机的输出结果不断地逼近真实数字标签。但是感知机模型会存在一个问题:对参数的局部调整都有可能导致模型输出从0变为1或从1变为0这样的突变,在这种情况下,很难用迭代方式优化参数来获得符合我们需求的模型。其实归根结底,是感知机的输出不平滑连续导致的。由此引入了神经网络模型。
神经网络模型中采用sigmoid函数对
w⋅x+b
进行处理,即
σ(w⋅x+b)
,那么sigmoid函数到底有怎么样的形式?输出结果又是如何呢?
sigmoid函数的基本表达形式是,
我们再来看sigmoid函数的基本形状,
从图中可以看出sigmoid函数的一条重要性质:当变量
z
无限趋近于正无穷时,sigmoid函数的输出就无限趋近于+1,而当变量
2.神经网络的模型结构
神经网络模拟的是人类神经元之间的信息处理传递过程,下面这张图描述了一个基本的神经网络模型结构,
如图,最左侧具有6个神经元,称为input layer;最右侧只有一个输出神经元,称为output layer;中间还有两层,分别包含4个和3个神经元,位于中间的所有层(除去输入层和输出层之外)称为hidden layers。
对于我们最开始的digit recognition问题,应如何设计神经网络结构呢?可以先考虑简单的情况:判断一张图片上的数字是否为9。我们可以将图片统一为28*28的灰度图,那么每一张输入图片就对应于一个28*28=784的像素矩阵。设置神经网络的输入层包含784个神经元,依次吸收784个像素值;而对于输出层而言,只需要设置一个输出神经元,如果当前模型的输出结果大于0.5,那么我们则认为其上的数字为9,反之,我们认为其上的数字不为9。类推至更复杂的情况,如果期望识别每一张图片上对应的真实数字属于0~9中的哪一个,那么输入层及预处理不变,而调整输出层为10个神经元,分别对应于0~9的10个数字。如果输出层上的哪一个神经元的输出值最大,则可以认为其对应的数字就是该神经网络识别的结果数字。
3.神经网络的数学表示&前向传播
首先介绍几个符号:
wljk 表示连接第 l−1 层的第 k 个神经元和第l 层的第 j 个神经元之间的weight参数,尤其需要注意的是下标j 和 k 的顺序!blj 表示第 l 层的第j 个神经元的bias参数,注意的是,对于第一层作为输入,我们不设置bias参数。- 设
alj=σ(∑kwljkal−1k+blj)
,同时也可以改写为矩阵表达式
al=σ(wlal−1+bl)
。如果第
l−1
层有
nl−1
个神经元,第
l
层有
nl 个神经元的话,那么矩阵 wl 的大小为 nl∗nl−1 ,矩阵 al−1 的大小为 nl−1∗1 ,矩阵 al 的大小为 nl∗1 。 - 设 zl=wlal−1+bl−1 。也就是说 al=σ(zl)
从上面的符号推导公式可以看出,在神经网络中,后一层的神经元的值会根据前一层神经元的改变而改变,这种由前向后依次计算每个神经元值的传导过程称为神经网络的前向传播过程。
4.神经网络-梯度优化
对于digit recognition任务,经过前面的介绍,我们已经知道如何设计神经网络结构,以及如何用数学符号和数学表达式对神经网络进行表示,但是这样的一个神经网络又是如何来完成识别任务的呢?对于上面设计好的神经网络结构(输入784个神经元,中间若干隐含层,输出10个神经元),假设存在这样的一组神经网络参数 w 和
b ,对于任意给定的输入,都能正确识别出对应的数字是多少,那么我们就可以认为当前的这个模型可以完成digit识别任务。那么这样一组合理的参数 w,b 又是如何确定的,才能达到我们的期望目标呢?又该如何衡量某个参数 w,b 是否足够好呢?所以当前问题的关键一是如确定好的参数 w,b ,以及如何衡量参数 w,b 是好的。实际上,神经网络依赖于大量的训练数据,在训练过程中,会不断优化调整参数,使得更多的训练数据的输出值与其真实的数字标签相同。这里我们使用MNIST数据集进行训练测试(http://yann.lecun.com/exdb/mnist/),对于每一张画有数字的图像,按照统的一规格预处理为28*28大小,那么所有的784个灰度像素值依次输入神经网络的784个输入层神经元,输出层设置为10个输出神经元,依次对应于0~9的10个数字。我们用变量 x 表示输入的784维向量,用
y=y(x) 表示对应的10维期望输出,比例,对于某个输入 x ,其真实的标签为6,那么对应的y 为 y(x)=(0,0,0,0,0,0,1,0,0,0)T 。为了衡量当前网络的识别能力,定义cost function,【注意,其实有多种定义cost function的方法,下面的这种形式其实是有一定的问题的,这里先不提具体的在哪里,只是从这种简单的cost function开始来说明神经网络算法问题。具体的关于cost function的问题将在第三小节进行讨论】
C(w,b)=12n∑x(||y(x)−a||2)
其中, w 表示weight参数,b 表示bias参数, n 表示训练集中所有数据的个数,a 表示对于输入 x ,通过神经网络后的输出为a , y(x) 是数据 x 的实际标签。如果C(w,b) 的值越小,则说明越多的训练数据被正确归类,或者说明当前的网络的正确识别能力越强。为了获得适当的参数 w,b 使得 C(w,b) 尽可能地小,可以通过梯度下降算法来优化求解。首先我们先简化问题,假设 C 是关于单变量
v 的函数,即 C(v) 。梯度下降求最优参数 v 的基本思想就是从一个随机起点开始,沿着梯度的反方向前进一小步,更新v ,这样不断依次地更新参数,直至使得cost function足够小为止。 v 的更新方程如下,其中η 是步长系数。
v→v−η∂C∂v而在神经网络中存在两组参数 w,b ,同样可以按照相同的方式来迭代优化,使cost function不断减小,
w→w−η∂C∂w
b→b−η∂C∂b但是这里会有个小问题,我们回过头来看原本的cost function C=12n∑x(||y(x)−a||2) ,它是由所有训练数据的cost之和构成的。那么在计算梯度 ∇C 时,同样要基于所有训练数据来进行计算,实际上有, ∇C=1n∑x∇Cx , ∇Cx 是训练数据 x 对应的梯度值。那么问题就来了,当训练集中的数据庞大时,就势必会影响到模型的整体训练效率。所以由此引出随机梯度下降的概念(stochastic gradient descent)。
随机梯度下降
上面我们已经了知道如何设计神经网络来匹配digit recognition的任务,也知道可以使用梯度下降优化的方法来不断迭代地更新神经网络的参数,使得训练数据集中更多的数据输出尽可能地贴近真实的数字标签,这样训练之后的网络就可以完成我们的期望目标。但是梯度下降法受到训练数据集数量的限制,容易存在效率问题,所以引入随机梯度下降的概念来改善这个问题。
随机梯度下降的思想也很简单,既然使用所有的训练集计算梯度会耗费较多的时间,那么我们考虑在所有训练集上随机选取一个小的子集上进行训练,可以认为在这个子集上训练的平均误差与在整个训练集上的训练误差是比较接近的,而又由于训练数据的减少,就可以明显减少模型整体的训练时间。基于这样的想法,我们将所有的训练数据分为若干个子集X1,X2,...,Xm ,称之为mini-batch。对于子集 Xj ,有
∑mj=1∇CXjm≈∑x∇Cxn=∇C所以 wk,bl 的更新公式可以重新写为,
wk←wk−ηm∑j∂CXj∂wk
bl←bl−ηm∑j∂CXj∂bl上述公式的求和是针对当前mini-batch中的所有训练数据进行的,然后再随机选择下一个mini-batch,直至将训练集中的所有训练数据遍历完为止,这样的一个过程称为一个epoch。结束一个epoch后,可以继续进行下一个epoch。epoch的次数以及mini-batch的大小需要单独作为参数设置。
5.神经网络-后向传播
前面我们已经介绍了可以使用梯度下降法来优化求解网络参数 w,b ,使得整体的cost function尽可能的小。但是梯度下降公式中还有一个量我们还不知道怎么求解,那就是cost function关于参数 w,b 的梯度值,接下来,我们将引入向后传播的思想来快速求解参数的梯度值。向后传播求梯度值关键有四个公式,为了引出这四个公式首先举一个小例子:
假设在第 l 的第
j 个神经元有一个小恶魔,每次输入向这个神经元的值 zlj 都会被这个小恶魔改动添加一个波动值 Δzlj ,那么该神经元的激活值将变为 σ(zlj+Δzlj) ,经过神经网络后续的传播分散,最终会导致整体的cost function增加 ∂C∂zljΔzlj 。现在假设这个小恶魔是一个好的角色,它希望通过添加一个扰动值来使得最终的cost值变得更小。如果当前时刻的 ∂C∂zlj 的值较大( ∂C∂zlj 的值大说明通过更新优化 zlj 的值可以进一步的使得值 C 变小),那么这个小恶魔就可以通过赋值Δzlj 使得其符号与 ∂C∂zlj 相反,使得cost的增加量 ∂C∂zljΔzlj 为负,从而进一步降低cost的值。相反,如果 ∂C∂zlj 较小或趋近于0,那么这个小恶魔是很难通过调节 Δzlj 来使得cost值降低。通过上面这个例子可以看出, ∂C∂zlj 可以间接反映cost值的大小,或者说可以反映整个神经网络error的大小,基于这样的结论,我们定义第 l 层的第
j 个神经元的error项为,
δlj≡∂C∂zlj
δl 表示第 l 层的error项矩阵,在向后传播算法中,会提供方案计算每一层的error项,进而关联至偏导项∂C∂wljk 和 ∂C∂blj ,至于如何计算error以及如何关联至偏导项,我们接下来引入四个关键公式,并依次证明四个公式的正确性。这四个公式将是向后传播的关键!公式1:如何计算输出层的error项?
δLj=∂C∂aLjσ′(zLj)证明:根据定义有 δLj=∂C∂zLj ,而 aLj=σ(zLj) ,所以根据链式法则有,
δLj=∂C∂aLj∂aLj∂zLj=∂C∂aLjσ′(zLj)
证毕!讨论: ∂C∂aLj 依赖于cost function的具体形式,如果我们按照前面使用的 C=12n∑j(yj−aj)2 来作为cost function,那么 ∂C∂aLj=(aj−yj) 。另外, σ 的导数公式也很特别, σ′(zLj)=σ(zLj)(1−σ(zLj))
上述公式写成矩阵表达式为: δL=(aL−y)⊙σ′(zL)
公式2:error项的向后传播演算
δl=((wl+1)Tδl+1)⊙σ′(zl)
其中, (wl+1)T 是第 l+1 层的权重矩阵。证明:
δlj=∂C∂zlj=∑k∂C∂zl+1k∂zl+1k∂zlj=∑k∂zl+1k∂zljδl+1k而又因为,
zl+1k=∑jwl+1kjalj+bl+1k=∑jwl+1kjσ(zlj)+bl+1k
所以,
∂zl+1k∂zlj=wl+1kjσ′(zlj)
所以有,
δlj=∑kwl+1kjδl+1kσ′(zlj)
证毕!讨论:由上面的公式可以发现,当前层的误差error可以通过后一层的误差error传播而来,那么我们就可以从最后一层的误差 δL 开始逐层向前依次计算每一层的error项 δl 。
公式3:关于bias的偏导计算
神经网络关于bias的偏导计算公式是,
∂C∂blj=δlj证明:
通过链式法则改写上述公式为,
∂C∂blj=∂C∂zlj∂zlj∂blj
而因为,
zlj=∑kwljkal−1k+blj
所以,
∂zlj∂blj=1
所以,
∂C∂blj=∂C∂zlj=δlj
证毕!讨论:从上面的公式很明显可以看出,cost方程关于bias的偏导其实就是error项!这离我们的目标只差最后一步了!
公式4:关于weight的偏导计算
神经网络关于weight的偏导计算公式是,
∂C∂wljk=al−1kδlj证明:
∂C∂wljk=∂C∂zlj∂zlj∂wljk
而,
zlj=∑kwljkal−1k+blj
所以,
∂zlj∂wljk=al−1k
所以,
∂C∂wljk=∂C∂zljal−1k=al−1kδlj
证毕!讨论:从公式中可以看出,当前层关于weight的偏导,可以通过当前层的误差项和前一层的神经元激活项的乘积得到。
以上四个公式介绍了如何求每一层的误差项 δl ,以及如何由 δl 求cost function关于bias和weight的偏导项!那么整个算法就被打通了!
6.神经网络优化算法
采用随机梯度下降法优化神经网络参数,每次随机取 m <script type="math/tex" id="MathJax-Element-146">m</script>个训练数据作为mini-batch。整个算法优化流程为(当然实际中还需要在外层添加循环遍历多个mini-batch,以及更外层多个epoch循环),
好啦,现在已经将整个神经网络最基本的内容介绍结束了,在下一章节将会列举一小段实现神经网络优化算法的代码进一步来说明!
- 设
alj=σ(∑kwljkal−1k+blj)
,同时也可以改写为矩阵表达式
al=σ(wlal−1+bl)
。如果第
l−1
层有
nl−1
个神经元,第
l
层有