首页 > 代码库 > 神经网络的初识
神经网络的初识
序:
本系列是以NeuralNetwork and Deep Learning书为基础,加上自己的见解写得,第一次写系列,不好之处,请指出哦!接下来我们会先对神经网络有一个介绍,好让大家明白神经网络是一个什么东西。为了更好的进行学习,在后面会以识别数字为引导,让我们循序渐进学习吧!
先说些有的没的吧!有时候你会不会觉得人的视觉系统在这个世界上,是多么伟大的杰作,请看下面的图:
当我们看到这些数字的时候,我们马上便可以知道这些数字是什么,为什么我们可以这么快的就能够了解呢,难道我们天生就能识别数字吗?我想答案是no。我们大脑的神经网络是一个非常复杂的系统(拜托,这可是用了几十亿年进化得来的产物呐!),在这样伟大神奇的系统的帮助下,我们得以很快的识别上面的数字。接下来,我们遇到的问题是,如何让计算机来识别这些数字。我们不是一直提倡数字化,信息化吗?识别这些数字的功效我想大家都能想到!那么,如何做呢?也许你会说,上面的数字比较简单啦,不就几张图而已嘛!那么我会说,同学,请看下面的图:
惊倒了吧!这么多,有时候,其实有一些数字写得比较草的时候,人也不是马上可以识别这些数字的,不过我们暂时忽略连人都不能识别的数字吧!
那么,我们该如何开始呢!嘿嘿,我们的主角—神经网络登场啦!我想大家一定曾经或多或少的听过一些关于神经网络的介绍。但我们在简单介绍一下吧!首先,神经网络是机器学习中一个手段啦,机器学习嘛,了解一点的人知道,就是输入数据,训练下,得出一个网络图,然后输入数据就会给出输出了。像我们小时候,老师告诉我们这个字母是A,B,C…然后,我们渐渐的懂得了这些字母,当再给我们一些字母的时候,我们就会明白这些字母的含义。
Perceptrons:
什么是神经网络,让我们先来介绍一下什么是Perceptrons,是一种人工神经元(嗯哼,翻译的好挫有没有,见谅下哈,如果有更好的翻译请告诉我)。Perceptrons是1950s和1960s,由研究人员 Warren McCulloch 和 Walter Pitts提出,被Frank Rosenblatt发展。而今天,有更多的神经元被提出,比如sigmoid神经元(我们后面会有介绍),这些神经元在很多模型中已经相当普遍了。总之就是很火啦,不懂的话就会显得out了啦,所以要懂点了啦!而Perceptrons是一个比较基础的,所以我们从基础介绍起嘛!
好了,废话太多了,进入正题,我想你应该大致学过点生物学,对于神经细胞的工作方式有一点了解,神经元之间传递信号的方式就是前面的神经元向后面的神经元释放一些信号,后面的神经元处理下,再传递给下一个,我说的好像太简单了,让我们看看我们的人工神经元的工作方式。Perceptrons是如何工作的呢?
Perceptrons能够处理一些二进制的输入,然后给你一个单一的二进制输出。
还是用图片说话比较好,从图中看得比较清楚了吧,x1,x2和x3输入,然后得出一个输出。那么输出如何得到呢?这个人Rosenblatt 提出了一个简单的规则去计算输出。他给每一个输入都给一个权重,w1,w2,…,权重可以用来表示输入的重要性,(其实直白点,就是,嗯哼,这个输入不错,给它定个0.8吧,诶哟,这个也不错,定个0.7吧,下个次点,定个0.1吧),这些值会对输出结果产生影响,权重越大,对结果的影响越大(女:“我和你妈掉进水里,你救哪个?” 男:“那得看你们的权重” 开个玩笑啦,嘿嘿)。那么输出是如何得到的呢?可以这样得到,设定一个值threshhold,然后计算∑jwjxj,如果结果比设定值大,输出1;否则输出0。
公式表达的应该挺清楚的了。接下来,让我们用一个不错的模型来看看,这个神经元怎么用。
假设有一个露天演唱会,你其实内心还是比较想去的,可是有一些因素你需要考率下,比如:1那天的天气怎么样? 2你的女朋友或者男朋友是否愿意陪你去? 3那个门票费用是多少?
让我们分别用x1,x2,x3来进行表示,x1=1表示天气好,x1=0表示天气糟糕;x2=1,表示你的异性朋友(同性也可以的)愿意陪你去,x2=0表示不愿意;x3=1表示门票费用低,x3=0表示门票费用高。
好了,定完输入的意义,下面就是你自己对各个因素的重视程度来设定了。比如你对天气很看重,你可以设定w1=6,你对第二和第三个输入不太看重,那么w2=2和w3=3,如上面说的,权重就是你对这些输入的重视程度,越大表示越重视。最后,我们需要设定一个threshhold,可以设定为5,但这个比较均衡设定,你也可以设定为3,表示你很希望参加这个演唱会。最终的输出的意思就是1表示去,0表示不去。好了,我们用Perceptrons描述了参加演唱会这个活动。
看下面的一幅图:
和上面我们描述的是不是有一些不太一样,中间多了一些神经元,其实,比较复杂的神经网络就是层次比较多,我们把最左边的叫做输入层,中间的叫做隐含层,最右边的叫做输出层。后面我们会见到更多的复杂的神经网络.
刚刚上面的输出函数中有一个threshhold值,我们在这里做一个稍微的修改,编程下面的。
和公式(1)对比一下我们看到,就是把threshhold移到左边了,这里的b=-threshhold,你可以称为bias(不会翻译还是省省好了,大家记住英文,更原味点)。
看到这里,你会不会觉得上面的神经元有点死板诶,值还要自己设定,多麻烦啊,神经元应该自己可以设定啊,嗯哼,有这个想法就对了,神经网络就是应该自己更新。下面我们来看一看另一个神经元,这个会自己更新的哦!
Sigmoid neurons:
假定我们需要一个能够自己进行学习的神经元,上面的显然不能够达到我们的要求嘛,那么我们该怎么办呢?比如我们的输入是一个扫描的图像矩阵,并且上面都是人写的数字呢?我们遇到这样的情况该怎么办呢?
看一下下面的图:
改图想要表达的意思是,如果在各输入权重或者bias有一点改变,这些改变导致输出结果output改变了一点,那么我们可以利用这些信息对权重和bias进行修改,让网络变得更符合我们想要的。假设我们的神经网络模型误把“8”读成了“9”,对weight和bias进行修改,让它能够正确的读数字。
但是如果输出只是0和1的话,那么我们做的一点改变或许并不会对结果产生多大的影响,就是说比较难让结果从0跳变成1,即使我们修改了神经网络模型,让它能够正确的读“9”了,也许中间改变的weights和bias是巨大的,这样的话,对于其它的数字,可能会遇上相同的问题,导致最后模型变来变去,而且每次变化都不小。也许你觉得不麻烦是不是,可是聪明的你,请仔细想想,如果做这些改变本身就需要消耗时间和资源,那么巨大的改变是不是相当消耗资源和时间的事,我们还是不要这样折腾电脑了吧,因为我们可以用更好的模型来进行表示。
回到这个图,我们做一些改变吧,同样是应对一些输入,我们得到一个输出,但是这次我们的输出不再是0和1这么简单了,我们让输出介于0和1之间的任意值,我们引进一个函数, σ(w?x+b)这个引进是什么呢,我们把σ看成是一个sigmoid 函数,这个函数的定义如下:
更确切的说,我们对于输入X1,X2…,,和w1,w2,…,和b,输出是:
也许,你会说一句,我去,这个函数是干嘛的,sigmoid函数其实主要的功能,在我看来,是一种将一个在- ∞ 至 + ∞之间的函数转换为(0,1)区间的函数。并且性质单调的,就是当z越大,则σ(z)越大,反之越小。例如,当z趋近于+∞时,σ(z)趋近于1;当z趋近于- ∞时,σ(z)趋近于0。若还是不清楚可以看下图σ(z)的形状。
现在有了sigmoid函数,我们可以对w和b进行细微的变动,而这些小变动一般会导致output的变化(想想现在函数已经连续了)。用Δoutput来表示output的变化,用Δwj表示在weights的变化,Δb表示在bias的变化,根据微积分,有一个公式
?output/?wj 和 ?output/?b 是偏导数,如果对偏导数不是太了解,不用紧张,记住以下这里表达了,Δoutput是由Δwj和Δb变化导致的。其实翻翻微积分的一些书,这里的东东都能够看懂的,因为微积分是很多东西的基础嘛,还是要好好学滴,我们还是假定大家有对高数的了解,所以后面有关数学方面的东东就不再扯太多了。
现在我们的输出已经是(0,1)区间内的一个实数了,不再是简单的0或1了。在原来的时候,我们判断一个数字图像是否是“9”,输出的是0表示不是“9”,“1”表示是“9”,现在可以设置一个值,比如0.5,当输出>0.5,判断是“9”;反之相反。讲了重要的sigmoid neuron了,下面就要开始设计神经网络了。
神经网络的结构
下面我们开始介绍神经网络,并且以数字识别举例。(准备好咯,比较好玩的东西来咯!)
先看下面的这幅图:
上面我们介绍过,最左边的是输入层,里面是输入神经元;最右边是输出层,里面是输出神经元;中间的是隐含层。当然,我们可以有很多中间隐含层。看下图:
输入层和输出层的设计是比较直接的,例如,我们想要判定一个手写字符是否是“9”,我们就将改图作为输入,图片一般可以转换为灰度矩阵,用灰度矩阵作为输入处理更方便些。少点其它的判断。当然针对一些其它的判断,有时候还是用原图比较好。这些东西都需要具体情况具体分析。假设图片是64 by 64 的图片,可以有4,096=64×64个输入神经元,输出可以就是一个,就想前面说过的,输出少于0.5判断不是“9”,大于等于“0.5”判断是。
中间层设计方式除了像上面比较直接的方式外,其实还有很多其它方法可以对中间层进行设计,一些启发式方法可以用来权衡中间层数和训练时间,这里不再仔细详述。
直至现在,我们讨论的神经网络,都是前一层的结果作为下一层的输入这种方式存在,这种网络我们称为前馈网络(feedforward neural networks),这样的网络不会有环的存在。还有一些网络,比如回归神经网络(recurrent neural networks),这样的结构内部可能存在环,感兴趣的可以去差一些资料(资料链接)。
一个简单是识别手写数字的神经网络
现在我们进行一个还不错的课题,手写数字的识别,这是一个已经被研究得很成熟的方向,识别率已经非常高的了。
看上面的图,我们一般把手写数字的识别分成两部分,一部分是数字的分割,再一部分就是数字的识别了。关于第一部分,有很多有效的方式可以用来分割。与其研究第一个问题,不如把重点放在更困难,更有意思的第二部分。
下面可能是我们会用到的神经网络的结构图。
一般我们的输入数据是28×28的像素图,所以我们用784=28×28 neurons作为输入层,每一个输入是灰度矩阵值,介于(0,1)之间,越小颜色越淡,反之越深。
第二层是隐含层,有15个神经元,这些值是可以自己设置试试设置多少精度最高的。
最后一层是输出层,有10个神经元,每个输出0或1,为什么不是4个,(2^4=16,所以若按照二进制编码,4个输出神经元就ok了),这个问题可以作为一个有意思的思考题,不了解的可以在评论里面留个言,比如邮箱,我可以发给你答案。当然最好自己先思考下哈!
现在假设我们已经有了训练数据。用x表示输入,每一个输入数据是一个28×28=784-维的向量,在向量中的每一个值代表灰度值。我们用y=y(x)表示预期的输出。y是一个10维的向量,如果对于一个输入数据x,它对应的是一个6,那么我们的y=(0,0,0,0,0,0,1,0,0,0)T;
接下来看下面的一个函数:
w表示神经网络中所有的权重,b表示所有的bias,a是神经网络中对应x的输出, y(x)是预期的输出。所以输出a依赖于w,b,x。那么C(w,b)表示的是什么呢?就是神经网络的输出和对应数据预计输出的差的平方和,为什么要用平方和呢?其实当然,这里的定义都是人定的,你要是觉得不爽,用其他的衡量也是ok的,不过这里用平方和还是比较好滴。想想,现在会怎么样,C(w,b)越大,表示我们设计的神经网络输出和实际输出差距越大,C的值越小,则差距越小。“那么我们的目标是什么呢?”“没有误差(蛀牙!嘿嘿)”,只要C(w,b)近于0,那么我们建立的神经网络结构就是一个优秀的模型。
好了,现在问题就归结为如何把C(w,b)变得小点。而这个就是Gradient Descent该做的事情了。
Gradient Descent:
接触过机器学习的人,对于GradientDescent一定不会陌生的吧。如果你觉得对这个算法已经没有问题了,那么可以跳过这段了,下面的介绍主要参考这里(LeftNotEasy的介绍文章),不过请大家注意一下,里面的任何公式,只是限于这个Gradient Descent内部介绍用的,后面我们使用Gradient Descent方法时,用的参数名字不一定相同,但是在后面我会标注一下。(主要是单独写一个关于Gradient介绍的内容比较麻烦,偷懒下,借鉴一下,而且这篇写得很好)
Gradient Descent 在神经网络中的运用:
其实我们使用GradientDescent的目的就是找到w和b,根据Gradient Descent方法,我们得出下面的递推规则。
这里的η就是上面GradientDescent中的步长,根据这个规则我们不断更新w和b,最终趋向于使C最小。附加下面的图有利于空间想象一下。
这样,我们就把Gradient Descent加进神经网络结构中。并且根据上面的推理规则,我们可以不断让结构中的w和b值更加准确,最终达到一个不错的效果。
stochastic gradient descent:
现在我们虽然可以进行递推了,不过,你会发现,有一个很大的问题没有解决。就是如果训练数据非常多,我们对每个训练数据都要进行一次Gradient Descent,显然会消耗大量的时间,学习的速度回非常非常慢。
为了克服这个问题,可以用stochasticgradient descent方法。随机地从训练数据中挑选一部分数据,如m个,进行标记X1,X2,…Xm,用这一小撮来表示大数据。
?Cx表示表示对于某个训练数据X的C(w,b),上面可以看到,用m个来表示n个。
同时,我们可以对w和b的更新方式,进一步改进:
当然,这里要不要m,还是你说了算,可以自己根据实际训练情况,确定最佳的步长。一般最佳的步长都是通过大量的测试得出来的一个依据。
好了,大致的介绍内容就是这些了!
实现:
看了这么多,如果你想要动手实践一下,ok,鼓励啊,数据可以在 MNIST下载,里面的数据都是收集到的专门用来训练的数据。
当然,针对上面所说的, Michael Nielsen 也有一个实现,在他的github上(链接),python实现,因为程序需要里面需要安装numpy,scikit-learn,scipy,三个python库,如果你是windows 64位,我在网上没有找到官方的scipy windows 64位的,不过在这里找到了http://blog.csdn.net/zhonghuan1992/article/details/32912557,当然,这里有很多python的包,上面三个都有,虽然是非官方的,但是还是挺健康的。