赞
踩
人工神经网络目前仍然是一个火热的话题,许多人都对它充满了兴趣。然而,对于想了解ANN具体是怎么回事的同学来说,往往缺乏一个足够简单可视化的方法去了解神经网络的内部构造。网络上的各种文章除了数学公式以外,剩下的多是利用Tensorflow等现成的python库实现的样例,虽然实现起来简单,但是底层函数全部封装起来,十分难以理解。因此,神经网络往往在大家的心目中呈现出高深莫测的样子。然而,这篇文章的写作目的就是为了把神经网络拉下神坛,让任何对Excel有最基本了解的人也能在自己的电脑上搭建出一个能准确识别手写数字(准确率达到95%以上)的神经网络出来,同时还能对神经网络的运行底层算法有一个非常直观的了解。对于仅仅是感兴趣的初学者来说,能够非常直观地看到神经元长什么样子,能够非常清晰地看到网络的前馈计算和反向传播的参数传递过程,对于希望了解神经忘咯具体算法的研究者或爱好者来说,能够直观地了解各种不同的算法参数的区别,从而对神经网络各个超参数(hyper parameter)的选择和调整产生更深刻的理解。
本文在写作时考虑到需要照顾各个不同层次的读者,因此会力求从浅到深,按照下面的结构来编排,同时每一个章节又相对独立,如果对某些部分已经熟悉的读者,大可跳过一部分,直接阅读感兴趣的章节即可
人工神经网络是近年来非常火热的概念,作为实现人工智能的工具之一最近以来受到了非同寻常的关注和瞩目。
人工神经网络是机器学习算法的一种,而机器学习算法有被认为是实现人工智能的途径之一。我们现在看到的很多人工智能的成果,从自动下棋程序到机器视觉,自然语言处理、自动驾驶、数据挖掘,无一不是基于机器学习的算法来实现的。
机器学习算法通过程序的“学习”来提高其性能。按照机器学习的思想,为了让计算机程序能够完成一项任务T
手写识别学习问题:
任务T
性能指标P
训练经验E
人工神经网络作为机器学习算法,当然也是遵循上面的范式,不过,性能指标P
感知器模型
人脑最基本的功能性组成单元是单个的神经细胞,通常也被称为神经元,神经元在大脑内相互连接,组成一个网络状的结构,彼此之间通过微弱的脉冲电位互相通信。人们相信,通过模拟神经元的功能,并且模拟某种特定的神经元网络结构,就能模拟出(至少是部分)人脑的功能。关于神经元的更多介绍,可以看这里。
感知器(Perceptron)就是这样一个人工神经元的数学模型,它最早由McCullen和Pitts在1943年提出,因此被称为MP感知器,通过简单的数学形式描述了神经元的三大特性:单向信号传递,多输入单输出以及连接强度可变。这个数学模型是这样的:
神经元的输入
如上图中所示,一个感知器能够接受N
这个输入向量模拟了神经元的树突电信号输入。就如同人眼视网膜的光学感受器会把光线转化为电信号,亮度越高的地方电信号就越强,反之亦然,电信号通过视网膜细胞的轴突传递到下一层神经细胞的树突上。每一个神经细胞的树突能够接受若干个其他神经元的输出。同样的道理,机器视觉的输入是一幅图像,这幅图像中的每一个像素的颜色值就代表了输入的强度,一个神经元可以连接全部像素,也可以连接部分像素,把所有输入像素值表示为一个向量X
神经元的权值
人脑神经元接受到输入电位后,如果输入电位的总和大于某一个阈值,这个神经元就会被“激活”从而产生一个电脉冲,并通过它的轴突传递到下一个神经元。在感知器模型中,同样的状况也会发生:感知器在接受到输入向量X
由于树突的连接强度会影响神经元的反应,因此它可以说是神经元最重要的参数之一,在感知器模型中,这个参数被称为“权值weights”,借鉴了加权累加的概念。感知器权值的数量与输入的数量一样,都是N个:w1,w2,w3,…,wN
神经元的激活,偏置
一个神经元的激活与否除了取决于输入的电信号强度、树突连接强度之外,还取决于神经元本身的兴奋阈值,就像我们听一个笑话的时候,有些人笑点很低会立即哈哈大笑,而另一些人由于笑点太高而觉得一点都不好笑一样,只有当输入的电信号强度超过了一个神经元的“笑点”时,它才会被“激活”从而产生电信号传递给下一个神经元。为了模拟这个特性,MP感知器是这样设置的:只有当输入加权总和大于某一个实数值时,感知器的输入就为1(感知器被激活),否则就为0(不激活)。为了方便地描述这个阈值,McCullen和Pitts给感知器设定了第二个重要参数b,这个参数被称为“偏置bias”,并且规定感知器的输出Out为:
上面的式子便是经典MP感知器的数学公式了。要使这个感知器能够学习,就是通过选择合适的权值w和偏置b,使得这个感知器在特定输入的时候输出1,而在其他时候输出0。比如,我们现在有一个双输入感知器,我们只要这样选择w和b,它就能成为一个逻辑与(AND)门2:
可以验证,只有当输入x1和x2都为1时,感知器的输出才为1,同样,我们改变一下感知器的w和b值,也能让它成为一个逻辑或(OR)门3,当x1和x2都为0时,感知器输出才为0:
有意思的是,XOR函数是无法用单个感知器来表示的4,要表示XOR函数,需要两个感知器并联后与一个感知器串联形成的双层网络才能实现。感兴趣的读者可以自己试着推算一下什么样的参数组合可以表达XOR函数(本节最后有答案)
从现在的观点来看,与其他试图模拟思维逻辑的人工智能方法相比,感知器模型最大的特点就是抛弃了可理解的逻辑,把解决问题的方法最终归结为搜索和优化——按照传统的方法,解决一个问题必须首先告诉机器解决这个问题的逻辑步骤,但是感知器方法却不需要这样,你需要解决一个问题,不用告诉我逻辑步骤,只要在整个参数的可能取值范围中”搜索“到正确的取值就可以了。这种思路对于大多数模糊问题简直是天大福音:大家想想,怎么教机器通过一系列逻辑步骤去识别一个数字”2“?你可以告诉它”2像一个小鸭子“,如此等等,但是具体到真实的像素,一个手写的数字2存在着巨量的例外要素,根本无法通过逻辑步骤来描述,如下图5。而用搜索优化的思路,则意味着我们只要找到正确的函数表达形式,那么总有一组最优化的参数让这个函数给出正确的结果——至于这个函数优化后人类是否能理解,则不在我们的考虑范围之内了。
由于本质上使用感知器表达的问题可以用搜索最优值的方法来求解,因此各种最优值搜索算法都可以被用上了,但是对于一个稍微大一些的感知器来说,由于搜索的空间巨大,因此最实用的算法是一种递进算法,比如从某一个可能的解开始,采用小步递进的方法来逐步逼近最优解。一个很形象的比喻是爬山:假设我们的目标是爬到一座山的最顶峰,但是不知道山顶的具体方位,如何爬到山顶呢?我们可以从山区的任何一点作为起点,然后一步步地移动,只要通过某种方法保证我没走一步都是往海拔更高的方向走一步,那么最后就一定能够爬到山顶。这就是所谓的爬山法:
假设我们需要设计一个感知器,它的性能P取决于w和b参数的组合,需要找到一组参数wmax和bmax以使P达到最优Pmax
求解最优性能P的位置坐标(对应的参数)可以采用爬山法:假设我们只有两个参数,形成一组位置坐标,我们将性能P想象成每个位置坐标的海拔高度,那么爬山法就等效于在地图上找到山顶(P最大值Pmax所在的点)的位置坐标。如上图,在不知道山顶(感知器的最优性能表现Pmax)坐标(w和b的具体参数)的情况下,从图中的第一个任意小红点坐标(一个任意w和b参数组合)出发一步步逼近目标,每一步都往山坡最陡峭(P相对于w和b参数的变化率最大)的方向走一步,最终走到山顶的位置(找到wmax和bmax)。除了爬山法之外,我们还可以采用粒子群算法(particle swamp algorithm)遗传算法等寻优算法,同样可以达到我们的目标。
但是为了使用爬山法。MP感知器却是无法工作的,因为它缺乏一项关键的特性:可微分性。由于MP感知器的输出函数是不连续的二值函数,这样导致我们无法爬山。想象一下,我们寻找山顶的方法是依据脚下山坡的斜率,我们只管往斜度最大的方向往上爬就可以了,但是如果我们要爬的山是一座平原上的平顶山,而我们的起点恰好又选在了平坦的平原上,没有斜率的信息可以利用,我们如何找到山顶呢?如下图:
答案是将二值函数替换为一个平滑可微分的函数,就像下面的图像一样:
两座山仍然有相似的边界,但是平滑的山坡让我们终于能够利用脚下的斜率了。我们用来平滑感知器输出的函数通常是一个输出域有限,如−1<out<1的函数,同时应该单调上升并且处处可微分,这类函数画出来往往呈S形,我们把它们叫做Sigmoid函数,最典型的Sigmoid函数叫做Logistics函数,它是这样的一个函数:
Sigmoid函数之所以被选中,不仅仅因为它具备了单调上升处处可微的特点,还因为它有一个非常宝贵的性质,在下一小节中我们会重点讨论。
多层感知器
早在感知器诞生之初人们就知道,单个感知器可以表示的函数非常有限,连。然而,早已有人证明一个由足够多感知器组成的多层网络,可以在任意精度上逼近任意连续函数6
多层感知器是由感知器组成的多层网络,如下图所示:
一个典型的多层网络是由感知器组成的结构,以上图为例,这个网络输入层(Input)有三个节点,代表网络的输入参数有三个(注意输入层并没有感知器),由四个感知器组成了隐藏层(Hidden),其中每个感知器都与输入层连接,并产生一个输出,连接到输出层(Output),输出层有两个感知器,接受隐藏层的输出作为其输入,并产生两个输出,作为网络的输出。注意,图中的输出层与隐藏层并不是全联接的,上面感知器与前三个隐藏层感知器连接,而下方的感知器与下面两个隐藏层感知器连接。事实上,在一个多层网络中,网络的连接方式可以有多种多样的,除了部分连接、全联接之外,跨层连接甚至成环连接都可以。在过去的几十年里,人们研究过了许多种不同的网络结构,用于完成不同的任务。
我们现在讨论的,是最简单的全联接多层感知器网络。就像下图中一样,每一层的感知器都与前一层的所有感知器直接连接,且不存在其他多余的连接。
到了这里,我们就需要正式进入神经网络的世界,而不仅仅是单个感知器了。为了符合习惯,从这里开始我都会用“神经元”或者“单元”来指代神经网络中的感知器。
对于神经网络来说,反向传播算法可以说是最重要的一项发明。在反向传播算法发明以前,由于缺乏对多层网络的有效训练方法,而单层网络的表征能力又非常之差,导致神经网络在60~70年代经历了一次低谷,直到80年代反向传播发明之后7,神经网络才得以再次复兴
神经网络的前向传播
我们经常把神经网络叫做前馈网络(Feed Forward Network, FF),这是因为神经网络的输出计算过程是一个前向传播的过程。这个过程可以用前面的例图来形象地说明:
在前向传播的过程中,整个网络的参数是静态不变的,所有的计算从第一层输入层开始,将x1,x2,x3作为输入参数输入网络后,向前传递到隐藏层的四个神经元,每一个神经元都会把这三个输入参数作为它自己的输入,计算其输出,假设outhj代表隐藏层第i个单元的输出,这个输出的计算可以按前面介绍的感知器输出公式计算(假设这个单元是Sigmoid单元,且有N个输入)
outhj=Logistics(N∑i=1xiwij+bj)
接下来输出层的计算方式与隐藏层一样,但是两个输出单元的输入来自于隐藏层(它的前一层单元)的输出。假设outk代表输出层第k个单元的输出,隐藏层有M个单元,它的计算公式如下:
这样我们就得到了网络的输出值,输出值的数量与输出单元的数量一致,两个输出单元输出两个实数out1,out2
通过神经网络的前向传播,我们把一组输入信号(可能是图像、视频、音频、文字或者任何类型的信号,只要这些信号以一种有规律的形式编排好即可)从神经网络(由若干神经元前后连接而成)的输入层进入,产生第一层输出信号,继而传递至下一层神经元产生下一层输出信号,直到产生的信号传递到最末一层输出层,在这里产生对我们而言有意义的输出信号。这个过程被称为信号的“前向传播”Feed Forward Propagation。前向传播就是神经网络根据输入信号产生相应信号的过程,犹如我们的眼睛看到了一个人,视网膜上的视神经将视觉信号传递到我们大脑的视觉皮层区域,经过短短几十毫秒的信息传递之后,我们的大脑就能迅速判断出这个人是成龙。
用于对图像进行分类的神经网络也能实现类似的功能,比如在2012年出现的AlexNet中,它的输出层有1000个神经元,每一个神经元代表一个明确且互不重复的分类,比如,第一个神经元代表一朵花,而第二个神经元代表一只猫,以此类推。由于采用了特殊的输出函数,这1000个神经元输出的实数和永远为1,因此,我们就可以把这1000个神经元的输出理解为一种分类的置信度。比如当第一个神经元输出为0.02,第二个神经元输出为0.98时,代表神经网络认为一幅图片为一朵花的置信度为0.02,而认为它是一只猫的置信度为0.98.
通过前向传播,神经网络能够对输入信号作出反应,但是,它并不能对网络产生任何影响,无法改变神经网络内部神经元的连接强度权值,所以,前向传播是无法让神经网络产生学习能力的,如果一个神经网络错误地把一个数字3识别成了“9”,那么就算我们重复一万次地把数字3输入到神经元中进行前向传播,这个网络仍然无法学习到正确的分类值。
如果要让神经网络“学会”它还没有掌握的知识或技能,就需要反向传播算法的帮助了。
神经网络的“学习”在机器学习领域属于“有监督学习”算法。有监督学习特别像我们小时候老师教我们学习识字的过程:老师先将几个字写在黑板上,告诉我们每个字的读音,然后让我们重复,如果我们读音不正确,则反复不断地教授,慢慢地我们的大脑就能调整神经反射,到最后我们就能正确地读出这些字。
神经网络的学习也是一样:为了让神经网络“学习”到正确的知识,我们必须反复地把“正确的知识”传授给神经网络,以便它能逐渐地调整自己“大脑”中的神经元连接强度,最终“学到”正确的知识。比如,我们如果想让神经网络学习识别手写数字,就必须同时告诉它正确的答案:
因此,在“学习”的过程中,我们必须同时给网络提供两方面的输入,一方面我们需要把图像输入到网络的输入层,并且使用前向传播让网络产生输出;另一方面,我们还需要“告诉”网络正确的输出,以便网络进行自我调整。想象一下小学生的课堂授课:老师在黑板上写下了一个大大的“山”字,问小明“这个字念什么呀?”。如果小明读对了,那么老师就会夸奖一句并让小明坐下,而如果小明读错了,那么老师就会把正确的读音告诉小明,并且提醒小明记住。在训练神经网络的时候,我们也会像老师一样,让网络自行产生一个输出后,会告诉网络这个输出的正确与否。不过,与老师直接给出正确答案不同,我们会采用更加“数学”的方法——我们会告诉网络这个输出与正确答案之间的“误差值”。“误差值”可以理解为网络输出与正确答案之间相差的离谱程度——网络输出与正确答案差的越离谱,误差值就越大,相反误差值就越小。因此,误差值就是网络输出o与正确答案t之间偏差的一个函数C(o),也被称为“误差函数”,通常最常见的误差函数被称为均方差函数:
知道了网络输出的误差之后,就可以把误差用于网络的训练了。在网络的学习或训练过程中,我们是通过“反向传播算法”来实现权值的调整的
本节中的小问题的答案
表达XOR函数的双层网络8:
<未完待续>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。