首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

人工神经网络——手写数字识别

今天我们来讲一个超级牛逼的话题——人工智能。这里面学问挺大的,我就不打算深入追究了。(不然这辈子也别想更新了!(雾))我就简单介绍一下,人工神经网络到底是什么,以及我做完这个程序的感想。感谢、花絮什么的就放最后了。

识别数字

这有一个“3”,一个28像素*28像素的灰色的模糊的“3”。但是你能把它“认”出来这是“3”,而不是别的数字。

这里还有很多“3”,这些图片对应的每个像素点的值都大相径庭,但是你还是能把它认出来。如果我来叫你写个程序,把一张图所有像素点的灰度值作为输入,算出这张图写的是几,那就有点困难了。

我们现在要用神经网络的方法来完成这个任务。这看起来是想用计算机来模拟生物的神经网络。不过在看完后面的介绍之后,你会觉得它只是借用了一下生物方面的词汇。神经网络这个词在此之前就已经有过了,为了消除歧义,我们这里将要搭建的其实是“人工神经网络”简称ANN(Artificial neural network)。

神经网络的结构

来看一张神经网络的示意图,这个图是由一些神经元(结点)和一些连接箭头组成。神经元按层排布,分为输入神经元(输入层),中间神经元(隐含层),输出神经元(输出层)。数据从输入层通过连接箭头传向隐含层,最终到达输出层。说白了这不就是一个函数吗?输入神经元作为自变量,输出神经元作为函数值。

图片来自百度

这个图只是展示了最基本的内容,实际的网络图肯定更复杂。要注意到,隐含层可以不止这一层,它里面可以包含很多层。每个神经元里都存储着一个0到1的实数,叫作激活值。我们先在输入神经元里给定激活值,激活值经过运算,得到的数值传给下一层神经元,看起来就像在模仿神经网络的运行。最终数值会传向输出层,得到输出结果。

图片来自百度

那对于我们这个具体的问题,识别数字,我们可以把28*28这784个像素的灰度值作为输入(0就是黑色,1就是白色,之间的数就是不同程度的灰色),那就有784个输入神经元;对于输出,我用了10个神经元来分别代表0到9这10个数字,我规定这10个神经元的激活值中最大的那一个神经元代表的数字就是识别的结果。

图片来源:油管频道 3Blue1Brown

隐含层的确定

我决定用两个隐含层,每个隐含层有16个神经元。用两个隐含层的原因是这样的。假设我们想识别9,那就先识别出9上面的圈和下面的一弯。也就是把数字拆分成一个一个的笔画,把这些笔画的特征或者模式(pattern)识别出来。我假设隐含层的第一层到第二层就是识别出这些特征,第二层到输出层就是通过这些特征来判断最终的数字。但是你想一想,就单单识别一个圈、一个弯就很难了吧。于是就有了隐含层的第一层,把每个笔画拆成更小的部分,先识别更小的部分就更简单一些了吧。这就是为什么有两个隐含层的原因,把问题拆成一小部分一小部分地解决。我们只是猜测它会这样“想”,至于它到底会怎么做、到底会学到什么,我们也不清楚。但是为什么是16个神经元呢?因为那是一个整数。

权重、偏置、σ函数

接下来就是神奇的数据传递过程了。当我们给定好输入神经元的激活值后,下一层神经元的激活值如何确定?举个例子,第一个隐含层的第一个神经元的激活值这样算,每一个输入端神经元的激活值乘以权重(weight)再求和。

记每个输入端神经元的激活值为ai, (i=1, 2, ..., 784),每个激活值对应的权重为wi, (i=1, 2, ..., 784),那么就会得到a1*w1+a2*w2+...+ a784*w784

权重的作用是这样的。假设第一个神经元识别的是这样一条短边,那么,这条边上对应像素的权重应该为正;当然要说明这是一条边,还得让周围的权重为负;除此之外,我们还要将得到的值减去偏置量(bias),表明这条边的值要“正”到一定程度才能真正“激活”这个神经元。

假设我要识别“7”上面的短边,那么绿色部分的权重就应该为正,红的为负。

图片来源:油管频道 3Blue1Brown

对于这一层所有的神经元都按照这样(加权求和减偏置)的方法运算。上一层每个神经元都对下一层的神经元的激活值有贡献(都有不同的权重)。人工神经网络的结构图上,每个箭头都有一个权重,将指出神经元的激活值乘以这个权重并加在被指到的神经元上。

不过你并不能保证这样算出来的值一定在0到1。你需要一个(非线性)函数把在(-∞,+∞)的数映射到[0,1],这样的函数很好找,而且不止一个。那我就随便选了个这样的函数σ(x)=1/(1+e-x),叫σ函数。当x趋于-∞时,σ(x)趋于0;当x趋于+∞时,σ(x)趋于1。

如此一来,我们就能在输出端得到结果了。为了明确地表示出输入与输出之间的数学关系,现在有请我们的数学好伙伴“矩阵”出场。 我们把某一层的每个神经元激活值做成一个列向量[a(0)i],下一层也做成一个列向量[a(1)j],偏置量也是一个列向量[bj]。权重变成了一个大矩阵[wi,j]。于是相邻两层激活值的关系a(1)=σ(w·a(0)-b)

是不是很简单?写成矩阵才好编程嘛。首先,我们画一个28px*28px的灰度图像,把每个像素的值输进输入神经元a(0),(注:图像每个像素的灰度值范围是0到255的整数,我们要变成0到1之间的实数再输进去),经过层层的数据传递a(k+1)=σ(w(k)·a(k)-b(k)),得到输出向量a(n)。于是,作出判断。

代价函数(cost function)

这个神经网络相当于是一个复杂无比的函数,它有784个输入量,10个输出量,里面还有众多的(784*16 +16*16 +16*10 +16 +16 +10 =13002个)权重与偏置这样的参数。如何确定所有的权重与偏置是个很大的问题。我们不可能去手动调整这些参数,而是要通过“训练”让它自动地调整。给电脑一些训练样本,告诉它每张图写的是几,去“教”它。

你可以在这个网站里下载数字识别的训练样本。下面的链接你点不动的,微信不让加外链。

http://yann.lecun.com/exdb/mnist/

我们一开始也不知道这些权重与偏置应该是多少,不如先随机地给个数值吧。如果我们拿现在这个没有经过训练的神经网络,去识别数字,它只会随机的做出一个判断,猜对的概率很低。判断这个神经网络的好坏,需要用到代价函数(cost function)或者叫损失函数、误差函数。用输出向量减去我们期望输出的向量,得到的向量的模的平方作为代价函数。如果我们给的图像是一个“9”,我们希望最终输出的向量应该是[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]T,而实际的输出可能是[0.2, 0.3, 0.1, 0.6, 0.3, 0.4, 0.7, 0.1, 0.9, 0.5]T那么此时的代价(误差)就是2.31。

拿不同的图片能算出不同的代价。代价越大说明识别得很辣鸡,代价越小越趋于0就说明识别得很准确。事实上代价函数也是个复杂到爆的函数。它把所有的13002个权重与偏置作为输入量。输出量只有一个数(也就是代价)。可供调节的参量是所有的训练样本。“训练”的目标就是要在所有的训练样本下,找到代价函数的最小值点。想一想有哪些方法可以找到多元函数的最小值点(最小值太难找到了,找到极小值就不错了)。

梯度下降法

非常天真的办法是,对函数求导,找到导数为0的点。求导倒是能求(也不容易),找0点几乎是不可能的。目前有效的方法(之一)是梯度下降法。一个函数在某个点的梯度告诉了我们函数值增长最快的方向以及大小。对于一个13002元函数来讲,不太好想象。如果是个一元函数,它的梯度就是导数,如果导数大于0自变量就应该向左移动一小步,反之朝右移动,移动的幅度跟梯度的大小成正比,防止走过头。这样函数值就会滑向极小值。

对于我们这个例子而言,给定一个训练样本和当前的13002个权重与偏置,我就能算出此时的梯度(一个有13002个元素的列向量),从而得到这13002个权重与偏置的应该变化的量。对每一个训练样本,我们都能算出一个变化量,取平均值就能得到在所有训练样本下让总体代价下降的权重与偏置的变化量,从而得到新的权重与偏置。反复进行这样的操作,我们不断地得到更好的权重与偏置。这样我们就能找到代价函数的极小值。

不过,每次都要遍历庞大的训练样本,计算量未免有点大。我们可以采取一些手段,来加快每迈进一步的速度。把整个样本随机打乱成分成一小部分一小部分的。比如每一小部分有100个样本(叫迷你样本)。把迷你样本的梯度当做整体样本的梯度,第一步用第一个迷你样本,第二步就用下一个迷你样本,这样每算一步就很快。但由于有近似,我们没有按照梯度下降最快的那个方向,就像是弯弯曲曲的下山坡,但是每走一步走得快。

当训练的差不多的时候,也就是至少样本里的数字基本能识别成功了之后。基本上就要大功告成了。这时拿一组新的测试样本来,测试一下识别的效果,如果正确率很高,就很好很完美。要是识别率很低,那就要找找原因了。

于是,我这个入门(hello world)级别的神经网络介绍就到此为止了。

我的程序

我编程序的时候没有想到用网上的数据库,所有的数字全是我自己手写的,写了300个,由于数量极少,我的每个迷你样本就一个样本,所以训练出来的效果不是很好,不过大多数还是能认出来的。

我用的gamemaker8做了一个图像显示的工具,用它来手写图像并转化成训练数据以及最终检测训练效果用。训练的程序是用fortran语言写的。

求梯度这部分有点复杂,把代码贴出来给大家参考参考。

编好程序,训练完后,就可以愉快地玩耍了。不过看它识别错误的那些例子还是很有趣的,就很迷。

最后,我们比较好奇的是,它真的是按照“识别短边、识别笔画、拼成数字”的方法来识别的吗?

像素越亮代表权重越大,不过看上面的图好像也看不出什么名堂来。至少它肯定不是按照我们以为的方法来识别的。

微信号unidentified2016

微信公众号unidentified2016原创文章。除注明来源的图片以外,其余均为自制图片。

本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

花絮:

最后要非常感谢一下油管的3Blue1Brown频道(B站上也有它的官方频道)。视频做的非常好,让我非常直观初步地了解了神经网络。我就是按照视频里面的方法,把程序编出来了。在此非常感谢。

其实这个程序早在半年前就做好了,一直拖到现在,哈哈哈。希望继续努力吧,好久不发图文消息都不知道该怎么写了,感觉自己还是要提高一下自己的写作水平。

感谢大家一直以来的关注。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180506G16MFH00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券