通过编写一个能够识别小鸟图片的程序来认识深度学习和卷积神经网络。
是不是早就厌倦了在阅读完一大段相关资料之后,对深度学习依然是一头雾水的状态?那就来改变这种状态吧!
现在,谷歌能够仅根据你的描述,让你在自己的相册中轻松地找到你想要的那一张,甚至都不需要你手动给照片加上标签!这是怎么做到的呢?
在前面两个部分中我们都提到了,这份教程指南是针对所有对“机器学习”感兴趣,却不知从何下手的朋友。由于是面向大众,所以这份指南有概括性和不尽完整之处。但我们还是希望能借此激发大众对“机器学习”的兴趣,让更多人认识、了解“机器学习”。
要是你还没阅读过这份教程指南的前面两部分,赶紧去看看吧!
运用深度学习进行对象识别
相信你一定看过上面这个非常有名的《xkcd》网络漫画。
漫画的创作就是基于这样一种理念——让任何一个三岁小孩都能认出照片中的小鸟;然而,如何让一台计算机识别出不同的物体,五十多年来却一直困扰着我们最优秀的计算机科学家。
在过去的几年时间里,我们终于找到了一个相当不错的方法来进行物体识别。这种方法是运用“深度卷积神经网络”的。这个词听起来像是出自William Gibson的科幻小说中的一串文字,晦涩难懂;但是如果我们把它一个个分解来看,就容易多了。
那么我们就正式开工吧!——编写一个能够识别小鸟图片的程序。
l 从简单的入手
在正式学习识别小鸟的图片之前,我们先来学习识别一个相对简单的对象——手写的数字“8”。
在这份指南的第二部分,我们已经学习了神经网络是如何通过把许多简单神经元链接起来处理复杂问题的。我们是创造了一个小的神经网络,这个网络能根据房间数量、占地面积以及房子所处位置和周边环境等条件来估算一间房子的价格。
同时我们知道,机器学习的原理其实就是把同一个泛型算法应用到不同的数据中去解决不同的问题,所以我们可以先对同一个神经网络进行修正和调整,让它能够识别所有手写文本。但是为了简化工作,我们先来尝试识别一个手写的数字“8”
只有当你拥有数据,尤其是大量的数据,机器学习才会充分发挥其作用。因此,为了开展我们的工作,我们需要很多很多手写的数字“8”。幸运的是,研究者们为了这一目的,已经建立了一个手写数字的MNIST数据集。MNIST数据集提供了60000张手写数字的图像,每一张的尺寸都是18*18,下面是数据集中部分手写数字“8”的图像:
MNIST数据集中的部分手写“8”
仔细想想,其实所有数据都只是数字而已。
我们在第二部分建立的神经网络仅仅接收了三个数字作为数据输入,例如“3”个房间,“2000”平方等等。但是现在我们想要用我们的神经网络进行图像处理,那么究竟如何将图像,而不光是数字,输入进神经网络呢?
答案其实相当简单。一个神经网络是接收数字作为数据输入的,对一台计算机来说,一个图像其实就是一个表示图片中每个像素深度的数字方阵:
为了把图像作为数据输入至我们的神经网络中,我们只要简单地把这个18*18的像素图看作是由324个数字组成的数字集。
为了加工处理这324个数字输入,我们只要把我们的神经网络扩大至能容纳324个节点输入就好了:
注意!我们的神经网络现在有了两个结果输出,而不是之前的一个结果了。第一个输出的结果将预测了这张图像是手写数字“8”的可能性;而第二个输出的结果将预测这张图像不是数字“8”的可能性。有了我们目标识别对象的单独输出结果以后,我们就能运用一个神经网络来将同类型的对象归类成组。
这一次,我们建立的神经网络比上一次的要大得多(这次的输入容纳量达到了324,而上一次的仅为3!),但是任何一台现代计算机都能在眨眼间处理有着上百节点的神经网络,甚至在你的手机上也能运作得很好。
那么剩下的工作就是用大量数字“8”的图像和非数字“8”的图像来训练这个神经网络,让它学会把两者区分。当我们输入了一个数字“8”的图像后,我们告诉它这个图像是数字“8”的可能性是100%,不是数字“8”的可能性是0%。对相反的图像也是同样的做法。
以下是我们部分的检测数据:
嗯……这真是很棒的检测数据!
在短短的几分钟之内,我们用一台笔记本电脑就能完成对这种神经网络的训练。训练完毕以后,我们就能拥有一个能够精确识别数字“8”手写图像的神经网络了。那么,接下来就能说:欢迎来到这个20世纪八十年代后期的图像识别的世界!
l 隧道视野
不得不说这真的非常巧妙,只要简简单单地把图像像素输入进一个神经网络,就能建立图像识别了。机器学习真神奇,对吧?
嗯,当然它也并不是那么简单的。
首先,好的方面是我们的数字“8”“识别器”确实能够很好地识别简单的图像,但前提是数字必须是处在图像的中间:
坏消息是:一旦图片里的手写数字不是正好位于图片的中央时,我们的识别器就完全搞砸了——即使是一点点的位置变化,也足以让我们的神经网络犯错。
这是因为我们的神经网络只看过了“8”在图片中央的训练数据,它完全不知道不在图片中央的“8”是什么样子的。所以,它只学会了一种模式——处于图片中央的“8”。
我们都知道,在应用环境下,我们的神经网络看到的数据并不可能都这么的有规可循,它们通常是复杂多变的。所以,我们需要想出一种办法,让我们的神经网络能够识别出不仅仅是在图片中央,而且是在图片任意位置的“8”。
穷举法#1:用一个滑动窗口搜索整张图片:
既然我们的小程序已经能很好地识别出处于图片中央的“8”了,那么我们能不能简单地,一个个部分地扫描整张图片,直到我们找到包含“8”的部分,或者整张图片都被扫描完呢?
这个方法叫做滑动窗口,是一个对这个问题的暴力解决方式。在某些特定的情况下,它的表现非常不错,但是它非常低效。如果用这种方法,我们就需要一次次地检测同样一张图片,以找出处于图片不同位置和不同大小的“8”。
当然,我们有更好的办法!
穷举法#2:更多的数据和更深的网络:
在前面章节,在训练我们的神经网络时,我们只给它看了“8”处于图片正中央的图片。那如果我们用更多样的数据(“8”处于不同的位置和大小)来训练它,那会怎么样呢?
事实上,我们不需要获取新的训练数据。我们只需要写一个简单的脚本,让它生成把不同大小的“8”放到图片的不同位置就可以了,如下图所示:
根据已有的训练图片创建出不同版本的训练图片,这样,我们就创造了“合成训练数据”。这是一个非常有用的办法!
通过这个办法,我们能够轻易地拥有源源不断的训练数据。数据越多,意味着我们的神经网络需要处理的问题就越难。但是没关系,这个问题我们可以通过扩大网络来解决,同时网络本身也能够从中掌握更多、更复杂的模式。
为了扩大我们的网络,我们只需要一层一层地堆积节点
我们把这个称为一个“深度神经网络”,因为相比较传统的神经网络,它有更多的层。
这种想法早在二十世纪六十年代末期就已经出现了,但是直至最近几年之前,因为训练这么大型的神经网络计算量太大,过于缓慢了,所以这个技术一直没有被广泛应用。然而,一旦我们研究出如何使用3D显卡(用于快速计算矩阵乘法),而不是使用普通的计算机处理器,那么使用这种大型神经网络的想法就会迅速变得实用、可行。事实上,利用与你用来玩“看守人”完全相同的NVIDIA公司的GeForce GTX 1080显卡,我们就能够以惊人的速度完成对神经网络的训练。
但是,尽管我们可以扩大我们的神经网络,而且可以用3D显卡快速地对其进行训练;我们仍然没有找到一个万全的解决办法。用我们的神经网络处理图像的时候我们需要考虑得更细致、周全。
试想一下,把处于图片顶部的“8”和处于图片底部的“8”当成是两个不同的对象,然后训练我们的网络去分别识别它们,这种做法是毫无实际意义的。
所以,我们应该要找出一种办法,让这个神经网络更加聪明,聪明到不需要我们额外的训练,它就能自己理解一个数字“8”无论被置于图片的任何方位都是同一个对象。幸运的是,确实是存在这样一种解决办法的。
l 卷积就是我们想要的答案:
作为人类,你能够非常感官地知道图片是有一个层次或概念的结构的。我们来看这张图片:
免费献上我儿子的照片
作为人类,你应该能
立刻分辨出这张照片的层次结构:
· 照片中的地面是由草和水泥覆盖的;
· 照片中有个小孩;
· 这个小孩坐在一只橡胶弹跳马上;
· 这个弹跳马在草坪上。
最重要的是,不论孩子处在什么表面之上,我们都能够识别出这个孩子。我们不需要仅仅因为“孩子”这个对象所有可能处在的、不同的平面,再费心去分别研究每个平面上“孩子”的“构造”。
但是现在,我们的神经网络还做不到这一点。它会将处在图片不同位置的数字“8”看作是完全不一样的对象,它还不能够理解在图片中移动一个对象的位置并不代表创造了另一个新的对象,这就意味着它需要根据每个对象可能所处的不同位置,重新分别研究出不同的识别方式。这种办法是不科学的。
我们需要给我们的神经网络传输“平移不变性”的观念,让它知道,不论出现在图片的什么位置,“8”就是“8”。
接下来,我们将使用到一种被称作“卷积”的处理方法来给我们的神经网络输出这种观念。“卷积”这一概念,一部分是受到计算机科学的启发,还有一部分是受生物学启发(有些疯狂地科学家用一个奇怪的探针戳猫的脑袋,希望研究出猫的大脑是如何进行图像处理的)。
l “卷积”的工作过程
之前,我们把所有图像当做网格数字方阵输入至我们的神经网络中。这次不同,我们将利用“一个对象无论处在图片的什么位置都是同一个对象”这一观念干一些比之前更聪明的事情。
下面就是具体的操作步骤和流程——
步骤一:把图像分解成相互重叠的图像块
类似于我们的滑动窗口搜索。我们首先把一个滑动窗口放置于完整的原始图片上,然后把每一张输出结果按照一个个小瓷砖状的、独立图片的形式保存下来。
完成这个步骤以后,我们就成功将我们的原始图片转换成77个大小一致的小方块图片了
步骤二:把各个图片小方块分别输入至一个个小的神经网络
之前,我们是将一整个图像输入至一个大的神经网络来识别数字“8”的。这一步与之前的操作一样,但是我们会分别单独输入所有的小方块图片来进行识别。
重复这一操作77次,一次输入一个方块图片
但是,这里的操作有一个很大的不同:我们将对这个处理小方块图片的神经网络保持相同的权重。换句话说,我们会对每一个小的方块图片都以完全一样的方式进行操作和处理。如果在操作过程中,任何小方块出现了任何不同的效果,我们将会对这个方块进行标记。
步骤三:把每一个方块操作的输出结果都保存到一个新的数组
我们不希望丢失原始的图片方块的排列方式,因此,我们把每一个图片方块的处理结果都保存下来了,并且按照原始图片的安排将其排列成一个网格。就像这样:
简单来说,整个过程就是我们从一个大的图像入手,最后我们得到一个相对小的集合,这个集合记录着我们原始图片中最有趣的部分。
步骤四:降采样
步骤三的结果是一个显示出原始图片最有趣的部分的数组,但是这个数组相当大:
为了缩减这个数组的尺寸,我们需要用一个叫做“最大池化”的算法来缩减其样本。
暂时不管最大的数字,我们先看这个集合的每一个2*2的小方块:
这一步骤的原理是:如果我们在构成2*2方块的四个输入方块中的任何一个发现有趣部分,我们就只会单独保留这个有趣的部分。这样一来,我们就能在缩减这个数组的尺寸的同时,保留最重要的部分。
最后一步:做出预测
到现在为止,我们已经把一个巨大的图像缩减到了一个相对较小的数组了。
这个数组其实就是一堆数字,所以我们可以把这个小的数组作为训练数据,输入至另一个神经网络。这个最终的神经网络将决定这个图像是否相匹配。为了把这一步与“卷积”步骤区分开来,我们称之为“全连接网络”。
所以,从头至尾,我们的五个步骤的操作流水线就是这样的:
添加更多步骤
我们的图像处理流水线包含了一系列的步骤:“卷积”、“最大池化”,再到最后的一个“全连接网络”
在解决现实问题的时候,这些步骤可以根据你的需要多次组合和叠加。你能有两个、三个,甚至是十个卷积层,你能在任何你想要缩减你的数据(降采样)的地方进行最大池化。
基本原理就是从一张大的图像入手,然后一步一步地、不断地把它压缩,直到你最后只得到一个单独的输出结果。卷积步骤越多,你的网络就能够识别越复杂的图像特征。
举个例子。第一个卷积步骤是学习识别锋利边缘的;第二个卷积步骤就能运用其识别锋利边缘的知识来进一步识别鸟嘴;同样的,第三个卷积步骤就可以运用其识别鸟嘴的知识来识别一只完整的鸟,以此类推。
以下是一个更加具有现实意义的深度卷积网络(类似于你在科研论文上看到的网络图)
在这个案例中,他们以一个224*224像素的图片开始入手,重复操作卷积步骤和最大池化各两次,再操作卷积步骤三次,最大池化一次,最后输入两个全连接层。这个最终输出结果就是这个图片被分解成的1000种类型的其中一个。
l 建立一个正确的网络
那么你怎么知道究竟需要组合、重复哪些步骤来让你的图像分类器表现最好呢?
说实话,你需要做大量的实验和测试才能回答这个问题。你可能需要测试100个网络,才能找到你的待解决问题的最优结构和参数。机器学习是一门需要不断实验和试错的科学。
l 建立我们的“小鸟分类器”
现在我们终于了解了如何编写一个能够判断一幅图是否为小鸟的程序了
像往常一样,我们需要一些数据来开始我们的工作。这个免费的CIFAR10 数据库包含了六千张鸟的图片和52000张其他图片,但是为了得到更多的数据,我们同时添加了Caltech-UCSD -200–2011鸟类数据集中的12000张鸟的图片。
下图是从我们组合数据库中选出的部分鸟的图片
下面这张图则是52000张“非鸟”图片中的一部分:
这个数据集确实能够沿着我们的目标正常工作,但是72000张低分辨率的图像相对于现实世界的应用仍然是远远不够的。如果你想要取得“谷歌水平”的成绩,那么你需要数百万张高清大图。在机器学习中,拥有大量的数据几乎比拥有一个好的算法还要重要。现在你知道为什么谷歌会愿意向你提供无限量的照片储存空间了吧?他们想得到你的图片数据,大量大量的数据。
为了建立我们的分类器,我们将使用TFLearn。TFLearn是谷歌的TensorFlow深度学习框架(这个框架有简易的API)的一个封装程序。它能让我们仅需要写几行代码就能定义我们的卷积神经网络 。
以下就是用于定义和测试网络的代码:
如果你是用一个有足够显存的好显卡(例如Nvidia GeForce GTX 980 Ti显卡卡或者是更好的)来训练,那么你就能在一小时之内就可以完成训练。如果你用的是一个普通的中央处理器,那花费的时间可能就会长一点。
恭喜你!我们的程序现在可以识别小鸟的图像了!
l 网络测试检验
既然我们已经有了一个经过训练了的神经网络,我们就来用用它!下面就是一个简单的脚本,这个脚本以一个图像文件作为输入,并且可以判断图像是否为鸟。
但是为了检验我们的网络的有效性,我们需要用大量的图像对其进行测试。我之前创建的数据集中有15000张图像作为校验集,当我用这个网络运行这15000张图像的时候,它的预估正确率达到了95%。
95%正确率,这数字看起来还不错吧?但是,还是要看情况而定。
95%的精确度到底有多精确?
我们的网络的精确度是达到了95%的,但是“魔鬼总是隐藏在细节里”,这95%能有各种不同的意义。
举个例子来说,假如我们的训练数据图像中,5%是鸟,剩下的95%不是鸟,会发生什么情况呢?一个程序作出“非鸟”的预测是95%准确的,但是它同样是100%无用的,因为图像不是我们需要的。
我们更需要关注的是其中的数字,而不仅仅是整体的准确度。判断一个分类系统的好坏在于,我们需要更关注这个系统是如何失误的,而不仅仅是它失误次数的百分比值。
与其考虑我们的预测是正确还是错误,我们还是直接把它分解为四个独立的类别——
- 第一,这里是我们的网络正确识别出的鸟的图片,我们把它称为“真正例”。
哇!我们的网络能成功识别出大量的不同类的鸟!
- 第二,下图是我们的网络正确识别出的“非鸟”的图片,我们把这些称作“真反例”。
用了我们的检验数据集中的15000张图片后,下面是我们的预估在各个分类的统计次数:
为什么我们要把我们的预测结果分解成如此呢?因为不是所有的失误都是以相同的方式产生的,不同的失误有着不同的失误原因。
想象一下,假设我们正在编写一个程序,这个程序要从心脏磁共振图像中侦探出癌症。如果我们正在监测癌症,我们宁愿得到一个“假正例”结果,而不愿意得到一个“假反例”结果。“假反例”将是最糟糕的结果,就是程序告知一个人他们没有患癌症,但实际上他们患上了癌症。
与其仅关注总精确度,我们不如计算精确度和召回指标。精确度和召回指标能把我们完成情况的好坏清晰地展现给我们。
上面的图向我们反映了我们预测图像为“鸟”的正确率达到了97%!但是这同时也反映了我们在数据集中仅找到了真实的鸟的90%。换句话说就是,虽然我们可能没法找到每一只鸟,但是一旦找到一只鸟,我们就能相当确定那就是一只鸟!
这一步将通向哪里?
现在既然你已经知道了深度卷积网络的基本原理了,那你就能尝试TFLearn中的一些例子,然后跟不同的深度学习工程师联合放手去干啦。TFLearn还有内置数据集,因此你甚至不需要用你自己的图片。
现在你也已经了解了如何分枝以及如何学习机器学习的其他领域,那么接下来何不学习一下如何运用算法来训练计算机玩Atari游戏呢?
注:本文由「图普科技」编译,您可以关注微信公众号tuputech,获得最新、最好的人工智能资讯。