学习一个“深度学习”算法(2)
在我看来这绝对是一个工程上的幸运发现。很多发明创造都是工程师偶然鼓捣出来的。喷气式发动机到底是什么原理?到现在人们也没说清楚。阿司匹林为什么有这样和那样的疗效?医学研究还得继续做实验。神经网络计算就是这么一种发明,计算机科学家发现它很有用,可是要想用严格的数学说明它为什么这么有用,那很困难。
我们要用神经网络来识别手写的阿拉伯数字。这是一个非常成熟的项目,网上有现成的数据库和好几个教程。有个迈克尔·尼尔森(Michael Nielsen)做了个深度学习在线教程值得你深入了解 [1],他只用了74行 Python 程序代码就做成了这件事。
给你几个手写阿拉伯数字,可能是信封上的邮政编码也可能是支票上的钱数,你怎么教会计算机识别这些数字呢?
学习一个“深度学习”算法(2)1. 简化
想要让计算机处理,首先你得把问题“数学化”。写在纸上的字千变万化,我们首先从中简化出来一个数学问题。我们首先用几个正方形把各个数字分开,就像下面这张图一样 ——
学习一个“深度学习”算法(2)现在问题变成给你一个包含一个手写数字的正方形区域,你能不能识别其中是个什么数字 ——
学习一个“深度学习”算法(2)再进一步,我们忽略字的颜色,降低正方形的分辨率,就考虑一个 28×28 = 784 个像素的图像。我们规定每一个像素值都是0到1之间的一个小数,代表灰度的深浅,0表示纯白色,1表示纯黑。这样一来,手写的数字“1”就变成了下面这个样子 ——
学习一个“深度学习”算法(2)[图片来自packtpub.com, The MNIST dataset。实际分辨率是28×28。]
你看这就完全是一个数学问题了。现在无非就是给你784个0-1之间的数,你能不能对这组数做一番操作,判断它们对应的是哪个阿拉伯数字。输入784个数,输出一个数。
这件事从常理来说并不是一点头绪都没有。比如任何人写数字“7”,左下角的区域应该是空白的,这就意味着784个像素点中对应正方形左下角区域那些点的数值应该是0。再比如说,写“0”的时候的中间是空的,那么对应正方形中间的那些像素点的数值应该是0。
但这个人为找规律的思路非常不可行。首先你很难想到所有的规则,更重要的是很多规则都是模糊的 —— 比如,7的左下角空白,那这个空白区域应该有多大呢?不同人的写法肯定不一样。
肯定有规律,可你说不清都是什么规律,这种问题特别适合神经网络学习。
2. 设定
我们要用的这个方法叫做“误差反向传播网络”,它最早起源于1986年发表在《自然》杂志上的一篇论文 [2],这篇论文的被引用次数已经超过了4万次,是深度学习的里程碑。
根据尼尔森的教程,我们建一个三层的神经网络,就是下面这张图 ——
学习一个“深度学习”算法(2)第一层是输入数据,图中只画了8个点,但其实上有784个数据点。第二层是隐藏层,由15个神经元组成。第三层是输出层,有10个神经元,对应0-9这10个数字。
上一讲我们说了,每个神经元都由输入、权重和偏移值参数、输出三个部分组成。隐藏层15个神经元中的每一个都要接收全部784个像素的数据输入,总共有784×15=11760个权重和15个偏移值。第三层10个神经元的每一个都要跟第二层的所有15个神经元连接,总共有150个权重和10个偏移值。这样下来,整个神经网络一共有11935个可调参数。
理想状态下,784个输入值在经过隐藏层和输出层这两层神经元的处理后,输出层的哪个神经元的输出结果最接近于1,神经网络就判断这是哪一个手写数字。当然一开始肯定做不到,我们可以干脆随机选择这11935个参数的初始值。
3. 训练
网上有个公开的现成数据库叫“MNIST”,其中包括6万个手写的数字图像,都标记了它们代表的是哪些数字 ——
学习一个“深度学习”算法(2)我们要做的是用这些图像训练神经网络,去调整好那11935个参数。我们可以用其中3万个图像训练,用剩下3万个图像检验训练效果。
这个训练调整参数的方法,就是“误差反向传播”。比如我们输入一个数字“7”的图像 ——
学习一个“深度学习”算法(2)神经网络实际收到的是784个像素值。经过两层神经元的传播计算,理想情况下,输出层的7号神经元的输出值应该最接近于1,其他的都很接近于0。可是一开始肯定不是这样,我们要用一套特定的规则去调整各个神经元的参数。
关键思想在于,参数调整有个方向,叫做“误差梯度”。比如对输出层的7号神经元来说,调整方向就是要让它的输出值变大;对其他9个神经元,调整方向则是让输出值变小。这个调整策略是看哪个输入信息对结果的影响大,对它的权重的调整就要大一点。
隐藏层神经元的参数调整更麻烦。我们必须考虑到,调整一个隐藏层的神经元,会同时影响全部10个输出层神经元的输入值。这就涉及到误差反向传递了,也就是调整前面神经元的参数,必须考虑它对后面神经元总体误差的影响。
具体的调整算法都是技术细节,我们这里关键是要理解这个设计思想:每次新的训练数据进来,就根据正确答案对参数进行一次微调,使得神经网络输出数值更接近正确答案。
这有点像是体育训练中的刻意练习。反馈是即时的,你做的结果立即就能跟正确答案比较。不要求你马上就做对,但是每次总要进步一点点。而你每一次都明确知道,身体上每一个关节的动作应该往哪个方向调整。
几万个训练图像可能会被反复使用多次,神经网络参数改过来改过去,最终将会达到稳定。你会发现,慢慢地,新图像喂进来,这11935个参数的变化越来越小、最终几乎不动了。那就是说,这个识别手写数字的神经网络,已经练成了。
事实证明这个简单网络的识别准确率能达到95%!这个结果让我们有很多感慨和问题。
4. 感慨
如果是一个数学家听说这波操作,他问的第一个问题肯定是,为什么最后居然能得到一组稳定的参数?为什么不一直都是每输入一个数,所有参数都剧烈变动呢?用数学语言来说,就是为什么这些参数能“收敛”?
这只能是因为手写数字是有规律的。所有人写数字差不多都是相似的写法,神经网络训练一段时间就见怪不怪了。如果你一会儿把这个写法叫7,一会儿把一个完全不同的写法叫7,神经网络就会无所适从。
比如说,如果我们用历史上的股票价格走向去训练一个神经网络,看看它能不能预测未来的股价,我敢打赌你将不会得到一组稳定的参数。因为股价变动几乎是没有规律的。
我们能这么做,是因为手写数字允许你这么做。
第二,虽然有规律很重要,虽然我们的神经网络已经能做出出色的判断,但是我们仍然不知道到底有哪些规律。
整个神经网络就像一个黑箱。你能看到那11935个参数的数值,但是你不知道,这些数值到底用到了哪些规律。
当神经网络判断数字“7”的时候,它利用了“7的左下角都是空白”这个规律吗?我猜如果你仔细分析神经网络的信息传递,这个规律肯定已经体现在其中了。这就是神经网络的妙处!现在你还是不能一一列举所有的规律,但是所有的规律都已经被默默包含在11935个参数之中。
会,但是说不清 —— 人脑的这个特点,就这样简单粗暴地被神经网络复制了。
第三,神经网络必须得用专门的数据喂出来。训练好之后,它就相当于长成了一个专门判断阿拉伯数字的网络。它不能判断英文字母或者别的什么东西。
当然,我们完全可以使用同样的网络设定,训练一个专门判断“零、壹、……玖”大写中文数字的神经网络 —— 但是对不起,你得先有几万个图像作为训练素材才行。深度学习的原理都是公开的,训练数据才是各家公司真正的稀缺资源,得慢慢积累。
第四,深度学习非常消耗资源。我们只用了25个神经元和28×28的低分辨率的图像,就要每次训练都调整11935个参数。应用级别的深度学习将会是海量的计算。现在最新的芯片,比如苹果的A11和A12芯片上,已经有专门的神经网络计算引擎。
为什么今天的例子只用了两层神经元,为什么不再多加一层、每层多用几个神经元呢?首先运算量会大大增加,其次更复杂的网络需要更多的训练,可能几万个图像就不够了。工程学不是从第一性原理出发的科学,只要能用就是对的 —— 到底怎么搭配是最优解,那是留给数学家的难题。
还有一点,这个方法按现代标准就算是太简单了。我们的训练素材中,每个数字都不大不小,都写在正方形区域的正中间。那要是一张很大的图片里有个很小的数字,用这个神经网络就不能识别了。
而新一代深度学习网络,哪怕是一张照片的角落里有一只猫,它都能识别出来。这就是“卷积网络”做的事,它除了一般的隐藏层,还会在中间再加入几个逻辑层……关于这个话题,咱们下一讲再说。
参考文献
[1] http://neuralnetworksanddeeplearning.com/chap1.html
[2] David E. Rumelhart, Geoffrey E. Hinton & Ronald J. Williams, Learning representations by back-propagating errors, Nature 323, 533–536 (1986).