您好,欢迎访问三七文档
NeuralNetworkforRecognitionofHandwrittenDigits中文翻译一个在修改过的手写数字库MNIST上精度达到99.26%的卷积神经网络迷若烟雨翻译2014年12月26日目录•简介•一些神经网络的理论•前向传播•激活函数•反向传播•二次导数•卷积神经网络的结构•通用的描述•构建神经网络的代码•手写数据库MNIST•测试/示例程序的架构•使用示例程序•神经网络的可视化•训练视图和控制神经网络•测试神经网络•训练神经网络•使训练更快的一些把戏•使用伪汉森的二次反向传播•同时反向和正向传播•跳过反向传播中的小误差•训练神经网络中的一些经验•结果•引用•版权和版本信息简介本篇文章详细梳理了用于手写数字识别的人工神经网络的历史。尽管这儿给出了一些神经网络的理论,但是如果你已经理解了一些神经网络的基本概念,比如说神经元、层、权重、反向传播等将会更好。这儿描述的神经网络不是用于通用目的的神经网络,它也不属于那些神经网络基准的类型。相反,我们集中于用于特定目的(识别手写数字)的一个特定的神经网络(5层的卷积神经网络)。用神经网络来识别手写数字并不是一个新奇的想法。这儿介绍的架构灵感来自于两个不同的作者。第一位作者是LeCun,独立发现反向传播的作者。LeCun博士为神经网络作出了大量的研究贡献。特别地,你应该看一下他的LearningandVisualPerception,在那里使用了动画来展示他研究的结果。MNIST数据集就是由他收集的。我使用了他的两个工作作为基础的源材料,我也十分推荐你去读一下他的其他的著作(在他的网站上有)。和其他的神经网络的文章不同,LeCun博士的工作理论性并不是很强,对数学要求也不高,它们很容易就能读懂,并且提供了一些灵感和解释。他的文章和著作可以在这儿找到。这儿是我以来的两个工作:•Y.LeCun,L.Bottou,Y.Bengio,andP.Haffner,Gradient-BasedLearningAppliedtoDocumentRecognition,ProceedingsoftheIEEE,vol.86,no.11,pp.2278-2324,Nov.1998.[46pages]•Y.LeCun,L.Bottou,G.Orr,andK.Muller,EfficientBackProp,inNeuralNetworks:Tricksofthetrade,(G.OrrandMullerK.,eds.),1998.[44pages]第二个作者是PatriceSimard博士,他是LeCun博士在AT&T实验室时的一个之前的合作者。Simard博士现在是微软DocumentProcessingandUnderstanding研究组的研究员。他的文章和著作可以在这儿找到,下面是我以来的工作:•PatriceY.Simard,DaveSteinkraus,JohnPlatt,BestPracticesforConvolutionalNeuralNetworksAppliedtoVisualDocumentAnalysis,InternationalConferenceonDocumentAnalysisandRecognition(ICDAR),IEEEComputerSociety,LosAlamitos,pp.958-962,2003.本文工作的一个目标是重现LeCun博士的精度,他训练神经网络达到了achieve99.18%!,也就是仅有0.82%的错误率。这个错误率是我工作的一个基准。在简介的最后,我无不高兴的表达我对代码的赞美,它几乎是一个工业级的应用。刚开始时,源代码灵活易读并且很容易去更改。随着事情的发展,代码变得丑陋起来,我开始把代码写的简洁起来,有时候使得代码难以理解。为了把他们合并起来,我尝试了不同的主意,一些可以但另一些不行。随着我把那些失败的主意去除,我并没有及时更改那些更新日志,因此有一些悬空的分子和死亡的结束。我也试过不发布源代码,但是我非常讨厌我所读过的那些文章:它们都没有公布源码。因此,为了不至于惴惴不安,也为了这些代码不招来指责,我重写了这些代码,这儿展示的就是它们。神经网络的一些理论本文不是神经网络的导读教程,带式为了理解代码以及里面使用的一些变量,看一下神经网络的基础知识很有必要。下面的讨论并不通用。它只考虑了5层的前向神经网络,也就是说网络有多层构成,每层仅向后面一层输出,并且仅接受前一层作为输入。换句话说,神经元不会垮层传播。考虑一个由多层组成的网络,并且每层有多个神经元。假设第n层中的一个命名为第i个神经元。这个神经元从前一层的输出获得输入,加上值为1的偏置。我使用变量x来代替那些神经元的输出。第i个神经元对每个输入都有一个权重来把输入叠加。为了表示激活阈值,我使用了变量y。激活函数是一个被称为sigmoid的函数,一个S型的函数,因为它的目的是把所有的输出的范围局限到-1到1之间,因此给了网络一些处理非线性函数的能力。下面是部分神经网络的一张图,记住集中于第n层的第i个神经元。这些变量的含义如下所示:第n层的第i个神经元的输出第n-1层的第j个神经元的输出第n-1层的第k个神经元的输出第n-1层的第i个神经元到第n层的第j个神经元的权重第n-1层的第i个神经元到第n层的第k个神经元的权重通用的前向传播的等式,F()是激活函数.我们稍后会详细的讨论激活函数那么怎么把这些转换层c++类的代码呢?在我看来,上图中标明一个神经网络是由4个不同的类组成:层、层中的神经元、从一层到另一层神经元之间的连接,连接的权重。这4个类反映在了类中,并且还有第5个类神经网络本身—它作为其他所有物体的一个容器,并且是和外部世界交互的接口。注意这些代码包含了带量的std::vector,还有部分的std::vectordouble://simplifiedview:somemembershavebeenomitted,//andsomesignatureshavebeenaltered//有用的定义typedefstd::vectorNNLayer*VectorLayers;typedefstd::vectorNNWeight*VectorWeights;typedefstd::vectorNNNeuron*VectorNeurons;typedefstd::vectorNNConnectionVectorConnections;//神经网络类classNeuralNetwork{public:NeuralNetwork();virtual~NeuralNetwork();voidCalculate(double*inputVector,UINTiCount,double*outputVector=NULL,UINToCount=0);voidBackpropagate(double*actualOutput,double*desiredOutput,UINTcount);VectorLayersm_Layers;};//层类classNNLayer{public:NNLayer(LPCTSTRstr,NNLayer*pPrev=NULL);virtual~NNLayer();voidCalculate();voidBackpropagate(std::vectordouble&dErr_wrt_dXn/*in*/,std::vectordouble&dErr_wrt_dXnm1/*out*/,doubleetaLearningRate);NNLayer*m_pPrevLayer;VectorNeuronsm_Neurons;VectorWeightsm_Weights;};//神经元类classNNNeuron{public:NNNeuron(LPCTSTRstr);virtual~NNNeuron();voidAddConnection(UINTiNeuron,UINTiWeight);voidAddConnection(NNConnectionconst&conn);doubleoutput;VectorConnectionsm_Connections;};//连接类classNNConnection{public:NNConnection(UINTneuron=ULONG_MAX,UINTweight=ULONG_MAX);virtual~NNConnection();UINTNeuronIndex;UINTWeightIndex;};//权重类classNNWeight{public:NNWeight(LPCTSTRstr,doubleval=0.0);virtual~NNWeight();doublevalue;};从上面你能够看出,神经网络类保存了一个指向层类列表的指针。没有有关添加一层的操作,简单的使用std::vector::push_back()就可以。神经网络类还提供了两个主要的对外接口,名字为前向传播,用了计算以及一个反向传播用类训练网络。每个层存储了一个指向前一层的指针,因此它知道从哪儿找到它的输入。另外它存储了一个指向神经元类列表的指针。一个有关类结构的真实问题是为什么把连接和权重类分开?如上图所示,每个连接有一个权重,为什么不把他们放在同一个类里呢?答案是连接间会共享权重。因此,尽管每层有数以百计的神经元,但仅有几十个需要共享的权重。通过把连接和权重类分开,更易于阅读。前向传播前向传播是对每个神经元基于它们前面的输入计算它们的输出。在代码中,这个过程是通过调用NeuralNetwork::Calculate().NeuralNetwork::Calculate()直接设置输入层的值,然后在后面的层迭代进行,调用每层的NNLayer::Calculate()函数。这导致了从输入层逐层向前传播直到输出层。不一定非得顺序进行传播,但这是最直接的方式。下面是简化的代码,使用C风格的doubles数组来表示神经网络的输入,并且存储在C风格的doubles数组输出中。//简化的代码voidNeuralNetwork::Calculate(double*inputVector,UINTiCount,double*outputVector/*=NULL*/,UINToCount/*=0*/){VectorLayers::iteratorlit=m_Layers.begin();VectorNeurons::iteratornit;//firstlayerisinputlayer:directly//setoutputsofallofitsneurons//tothegiveninputvectorif(litm_Layers.end()){nit=(*lit)-m_Neurons.begin();intcount=0;ASSERT(iCount==(*lit)-m_Neurons.size());//thereshouldbeexactlyoneneuronperinputwhile((nit(*lit)-m_Neurons.end())&&(countiCount)){(*nit)-output=inputVector[count];nit++;count++;}}//iteratethroughremaininglayers,//callingtheirCalculate()functionsfor(lit++;litm_Layers.end();lit++){(*lit)-Calculate();}//loadupoutputvectorwithresultsif(outputVector!=NULL){lit=m_Layers.end();lit--;nit=(*lit)-m_Neurons.begin();for(intii=0;iioCount;++ii){outputV
本文标题:Neural-Network-for-Recognition-of-Handwritten-Digi
链接地址:https://www.777doc.com/doc-4759527 .html