如何用Python和深度神经网络识别图像?

只须要10几行Python代码,你就能构建本身的机器视觉模型,快速准确识别海量图片。快来试试吧!html

视觉

进化的做用,让人类对图像的处理很是高效。python

这里,我给你展现一张照片。git

若是我这样问你:github

你可否分辨出图片中哪一个是猫,哪一个是狗?bash

你可能当即会以为本身遭受到了莫大的侮辱。而且大声质问我:你以为我智商有问题吗?!服务器

息怒。微信

换一个问法:网络

你可否把本身分辨猫狗图片的方法,描述成严格的规则,教给计算机,以便让它替咱们人类分辨成千上万张图片呢?app

对大多数人来讲,此时感觉到的,就不是羞辱,而是压力了。框架

若是你是个有毅力的人,可能会尝试各类判别标准:图片某个位置的像素颜色、某个局部的边缘形状、某个水平位置的连续颜色长度……

你把这些描述告诉计算机,它果真就能够判断出左边的猫和右边的狗了。

问题是,计算机真的会分辨猫狗图片了吗?

我又拿出一张照片给你。

你会发现,几乎全部的规则定义,都须要改写。

当机器好不容易能够用近似投机取巧的方法正确分辨了这两张图片里面的动物时,我又拿出来一张新图片……

几个小时之后,你决定放弃

别气馁。

你遭遇到的,并非新问题。就连大法官,也有过一样的烦恼。

1964年,美国最高法院的大法官Potter Stewart在“Jacobellis v. Ohio”一案中,曾经就某部电影中出现的某种具体图像分类问题,说过一句名言“我不许备就其概念给出简短而明确的定义……可是,我看见的时候天然会知道”(I know it when I see it)。

原文以下:

I shall not today attempt further to define the kinds of material I understand to be embraced within that shorthand description (“hard-core pornography”), and perhaps I could never succeed in intelligibly doing so. But I know it when I see it, and the motion picture involved in this case is not that.

考虑到精神文明建设的须要,这一段就不翻译了。

人类无法把图片分辨的规则详细、具体而准确地描述给计算机,是否是意味着计算机不能辨识图片呢?

固然不是。

2017年12月份的《科学美国人》杂志,就把“视觉人工智能”(AI that sees like humans)定义为2017年新兴技术之一。

你早已据说过自动驾驶汽车的神奇吧?没有机器对图像的辨识,能作到吗?

你的好友可能(不止一次)给你演示如何用新买的iPhone X作面部识别解锁了吧?没有机器对图像的辨识,能作到吗?

医学领域里,计算机对于科学影像(如X光片)的分析能力,已经超过有多年从业经验的医生了。没有机器对图像的辨识,能作到吗?

你可能一会儿以为有些迷茫了——这难道是奇迹?

不是。

计算机所作的,是学习

经过学习足够数量的样本,机器能够从数据中本身构建模型。其中,可能涉及大量的判断准则。可是,人类不须要告诉机器任何一条。它是彻底本身领悟和掌握的。

你可能会以为很兴奋。

那么,下面我来告诉你一个更令你兴奋的消息——你本身也能很轻易地构建图片分类系统!

不信?请跟着我下面的介绍,来试试看。

数据

我们就不辨识猫和狗了,这个问题有点不够新鲜。

我们来分辨机器猫,好很差?

对,我说的就是哆啦a梦。

把它和谁进行区分呢?

一开始我想找霸王龙,后来以为这样简直是做弊,由于他俩长得实在差异太大

既然哆啦a梦是机器人,我们就另外找个机器人来区分吧。

一提到机器人,我马上就想起来了它。

对,机器人瓦力(WALLE)。

我给你准备好了119张哆啦a梦的照片,和80张瓦力的照片。图片已经上传到了这个Github项目

请点击这个连接,下载压缩包。而后在本地解压。做为我们的演示目录

解压后,你会看到目录下有个image文件夹,其中包含两个子目录,分别是doraemon和walle。

打开其中doraemon的目录,咱们看看都有哪些图片。

能够看到,哆啦a梦的图片真是五花八门。各类场景、背景颜色、表情、动做、角度……不一而足。

这些图片,大小不一,长宽比例也各不相同。

咱们再来看看瓦力,也是相似的情况。

数据已经有了,下面咱们来准备一下环境配置。

环境

咱们使用Python集成运行环境Anaconda。

请到这个网址 下载最新版的Anaconda。下拉页面,找到下载位置。根据你目前使用的系统,网站会自动推荐给你适合的版本下载。我使用的是macOS,下载文件格式为pkg。

下载页面区左侧是Python 3.6版,右侧是2.7版。请选择2.7版本。

双击下载后的pkg文件,根据中文提示一步步安装便可。

安装好Anaconda后,咱们须要安装TuriCreate。

请到你的“终端”(Linux, macOS)或者“命令提示符”(Windows)下面,进入我们刚刚下载解压后的样例目录。

执行如下命令,咱们来建立一个Anaconda虚拟环境,名字叫作turi。

conda create -n turi python=2.7 anaconda
复制代码

而后,咱们激活turi虚拟环境。

source activate turi
复制代码

在这个环境中,咱们安装最新版的TuriCreate。

pip install -U turicreate
复制代码

安装完毕后,执行:

jupyter notebook
复制代码

这样就进入到了Jupyter笔记本环境。咱们新建一个Python 2笔记本。

这样就出现了一个空白笔记本。

点击左上角笔记本名称,修改成有意义的笔记本名“demo-python-image-classification”。

准备工做完毕,下面咱们就能够开始编写程序了。

代码

首先,咱们读入TuriCreate软件包。它是苹果并购来的机器学习框架,为开发者提供很是简便的数据分析与人工智能接口。

import turicreate as tc
复制代码

咱们指定图像所在的文件夹image。

img_folder = 'image'
复制代码

前面介绍了,image下,有哆啦a梦和瓦力这两个文件夹。注意若是未来你须要辨别其余的图片(例如猫和狗),请把不一样类别的图片也在image中分别存入不一样的文件夹,这些文件夹的名称就是图片的类别名(cat和dog)。

而后,咱们让TuriCreate读取全部的图像文件,而且存储到data数据框。

data = tc.image_analysis.load_images(img_folder, with_path=True)
复制代码

这里可能会有错误信息。

Unsupported image format. Supported formats are JPEG and PNG    file: /Users/wsy/Dropbox/var/wsywork/learn/demo-workshops/demo-python-image-classification/image/walle/.DS_Store
复制代码

本例中提示,有几个.DS_Store文件,TuriCreate不认识,没法看成图片来读取。

这些.DS_Store文件,是苹果macOS系统建立的隐藏文件,用来保存目录的自定义属性,例如图标位置或背景颜色。

咱们忽略这些信息便可。

下面,咱们来看看,data数据框里面都有什么。

data
复制代码

能够看到,data包含两列信息,第一列是图片的地址,第二列是图片的长宽描述。

由于咱们使用了119张哆啦a梦图片,80张瓦力图片,因此总共的数据量是199条。数据读取完整性验证经过。

下面,咱们须要让TuriCreate了解不一样图片的标记(label)信息。也就是,一张图片究竟是哆啦a梦,仍是瓦力呢?

这就是为何一开始,你就得把不一样的图片分类保存到不一样的文件夹下面。

此时,咱们利用文件夹名称,来给图片打标记。

data['label'] = data['path'].apply(lambda path: 'doraemon' if 'doraemon' in path else 'walle')
复制代码

这条语句,把doraemon目录下的图片,在data数据框里打标记为doraemon。反之就都视为瓦力(walle)。

咱们来看看标记以后的data数据框。

data
复制代码

能够看到,数据的条目数量(行数)是一致的,只是多出来了一个标记列(label),说明图片的类别。

咱们把数据存储一下。

data.save('doraemon-walle.sframe')
复制代码

这个存储动做,让咱们保存到目前的数据处理结果。以后的分析,只须要读入这个sframe文件就能够了,不须要从头去跟文件夹打交道了。

从这个例子里,你可能看不出什么优点。可是想象一下,若是你的图片有好几个G,甚至几个T,每次作分析处理,都从头读取文件和打标记,就会很是耗时。

咱们深刻探索一下数据框。

TuriCreate提供了很是方便的explore()函数,帮助咱们直观探索数据框信息。

data.explore()
复制代码

这时候,TuriCreate会弹出一个页面,给咱们展现数据框里面的内容。

原先打印data数据框,咱们只能看到图片的尺寸,此时却能够浏览图片的内容。

若是你以为图片过小,不要紧。把鼠标悬停在某张缩略图上面,就能够看到大图。

数据框探索完毕。咱们回到notebook下面,继续写代码。

这里咱们让TuriCreate把data数据框分为训练集合和测试集合。

train_data, test_data = data.random_split(0.8, seed=2)
复制代码

训练集合是用来让机器进行观察学习的。电脑会利用训练集合的数据本身创建模型。可是模型的效果(例如分类的准确程度)如何?咱们须要用测试集来进行验证测试。

这就如同老师不该该把考试题目都拿来给学生作做业和练习同样。只有考学生没见过的题,才能区分学生是掌握了正确的解题方法,仍是死记硬背了做业答案。

咱们让TuriCreate把80%的数据分给了训练集,把剩余20%的数据拿到一边,等待测试。这里我设定了随机种子取值为2,这是为了保证数据拆分的一致性。以便重复验证咱们的结果。

好了,下面咱们让机器开始观察学习训练集中的每个数据,而且尝试本身创建模型。

下面代码第一次执行的时候,须要等候一段时间。由于TuriCreate须要从苹果开发者官网上下载一些数据。这些数据大概100M左右。

须要的时长,依你和苹果服务器的链接速度而异。反正在我这儿,下载挺慢的。

好在只有第一次须要下载。以后的重复执行,会跳过下载步骤。

model = tc.image_classifier.create(train_data, target='label')
复制代码

下载完毕后,你会看到TuriCreate的训练信息。

Resizing images...
Performing feature extraction on resized images...
Completed 168/168
PROGRESS: Creating a validation set from 5 percent of training data. This may take a while.
          You can set ``validation_set=None`` to disable validation tracking.
复制代码

你会发现,TuriCreateh会帮助你把图片进行尺寸变换,而且自动抓取图片的特征。而后它会从训练集里面抽取5%的数据做为验证集,不断迭代寻找最优的参数配置,达到最佳模型。

这里可能会有一些警告信息,忽略就能够了。

当你看到下列信息的时候,意味着训练工做已经顺利完成了。

能够看到,几个轮次下来,不管是训练的准确度,仍是验证的准确度,都已经很是高了。

下面,咱们用得到的图片分类模型,来对测试集作预测。

predictions = model.predict(test_data)
复制代码

咱们把预测的结果(一系列图片对应的标记序列)存入了predictions变量。

而后,咱们让TuriCreate告诉咱们,在测试集上,咱们的模型表现如何。

先别急着往下看,猜猜结果正确率大概是多少?从0到1之间,猜想一个数字。

猜完后,请继续。

metrics = model.evaluate(test_data)
print(metrics['accuracy'])
复制代码

这就是正确率的结果:

0.967741935484
复制代码

我第一次看见的时候,震惊不已。

咱们只用了100多个数据作了训练,竟然就能在测试集(机器没有见过的图片数据)上,得到如此高的辨识准确度。

为了验证这不是准确率计算部分代码的失误,咱们来实际看看预测结果。

predictions
复制代码

这是打印出的预测标记序列:

dtype: str
Rows: 31
['doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'walle', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'walle']
复制代码

再看看实际的标签。

test_data['label']
复制代码

这是实际标记序列:

dtype: str
Rows: 31
['doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'walle', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'walle']
复制代码

咱们查找一下,到底哪些图片预测失误了。

你固然能够一个个对比着检查。可是若是你的测试集有成千上万的数据,这样作效率就会很低。

咱们分析的方法,是首先找出预测标记序列(predictions)和原始标记序列(test_data['label'])之间有哪些不一致,而后在测试数据集里展现这些不一致的位置。

test_data[test_data['label'] != predictions]
复制代码

咱们发现,在31个测试数据中,只有1处标记预测发生了失误。原始的标记是瓦力,咱们的模型预测结果是哆啦a梦。

咱们得到这个数据点对应的原始文件路径。

wrong_pred_img_path = test_data[predictions != test_data['label']][0]['path']
复制代码

而后,咱们把图像读取到img变量。

img = tc.Image(wrong_pred_img_path)
复制代码

用TuriCreate提供的show()函数,咱们查看一下这张图片的内容。

img.show()
复制代码

由于深度学习的一个问题在于模型过于复杂,因此咱们没法精确判别机器是怎么错误辨识这张图的。可是咱们不难发现这张图片有些特征——除了瓦力之外,还有另一个机器人。

若是你看过这部电影,应该知道两个机器人之间的关系。这里咱们按下不表。问题在于,这个右上方的机器人圆头圆脑,看上去与棱角分明的瓦力差异很大。可是,别忘了,哆啦a梦也是圆头圆脑的。

原理

按照上面一节的代码执行后,你应该已经了解如何构建本身的图片分类系统了。在没有任何原理知识的状况下,你研制的这个模型已经作得很是棒了。不是吗?

若是你对原理不感兴趣,请跳过这一部分,看“小结”。

若是你对知识喜欢刨根问底,那我们来说讲原理。

虽然不过写了10几行代码,可是你构建的模型却足够复杂和高大上。它就是传说中的卷积神经网络(Convolutional Neural Network, CNN)。

它是深度机器学习模型的一种。最为简单的卷积神经网络大概长这个样子:

最左边的,是输入层。也就是我们输入的图片。本例中,是哆啦a梦和瓦力。

在计算机里,图片是按照不一样颜色(RGB,即Red, Green, Blue)分层存储的。就像下面这个例子。

根据分辨率不一样,电脑会把每一层的图片存成某种大小的矩阵。对应某个行列位置,存的就是个数字而已。

这就是为何,在运行代码的时候,你会发现TuriCreate首先作的,就是从新设置图片的大小。由于若是输入图片大小各异的话,下面步骤没法进行。

有了输入数据,就顺序进入下一层,也就是卷积层(Convolutional Layer)。

卷积层听起来彷佛很神秘和复杂。可是原理很是简单。它是由若干个过滤器组成的。每一个过滤器就是一个小矩阵。

使用的时候,在输入数据上,移动这个小矩阵,跟原先与矩阵重叠的位置上的数字作乘法后加在一块儿。这样原先的一个矩阵,就变成了“卷积”以后的一个数字。

下面这张动图,很形象地为你解释了这一过程。

这个过程,就是不断从一个矩阵上去寻找某种特征。这种特征多是某个边缘的形状之类。

再下一层,叫作“池化层”(Pooling Layer)。这个翻译简直让人无语。我以为翻译成“汇总层”或者“采样层”都要好许多。下文中,咱们称其为“采样层”。

采样的目的,是避免让机器认为“必须在左上角的方格位置,有一个尖尖的边缘”。实际上,在一张图片里,咱们要识别的对象可能发生位移。所以咱们须要用汇总采样的方式模糊某个特征的位置,将其从“某个具体的点”,扩展成“某个区域”。

若是这样说,让你以为不够直观,请参考下面这张动图。

这里使用的是“最大值采样”(Max-Pooling)。以原先的2x2范围做为一个分块,从中找到最大值,记录在新的结果矩阵里。

一个有用的规律是,随着层数不断向右推动,通常结果图像(其实正规地说,应该叫作矩阵)会变得愈来愈小,可是层数会变得愈来愈多。

只有这样,咱们才能把图片中的规律信息抽取出来,而且尽可能掌握足够多的模式。

若是你仍是以为不过瘾,请访问这个网站

它为你生动解析了卷积神经网络中,各个层次上到底发生了什么。

左上角是用户输入位置。请利用鼠标,手写一个数字(0-9)。写得难看一些也没有关系。

我输入了一个7。

观察输出结果,模型正确判断第一选择为7,第二可能性为3。回答正确。

让咱们观察模型建构的细节。

咱们把鼠标挪到第一个卷积层。停在任意一个像素上。电脑就告诉咱们这个点是从上一层图形中哪几个像素,通过特征检测(feature detection)得来的。

同理,在第一个Max pooling层上悬停,电脑也能够可视化展现给咱们,该像素是从哪几个像素区块里抽样得到的。

这个网站,值得你花时间多玩儿一下子。它能够帮助你理解卷积神经网络的内涵。

回顾咱们的示例图:

下一层叫作全链接层(Fully Connected Layer),它其实就是把上一层输出的若干个矩阵所有压缩到一维,变成一个长长的输出结果。

以后是输出层,对应的结果就是咱们须要让机器掌握的分类。

若是只看最后两层,你会很容易把它跟以前学过的深度神经网络(Deep Neural Network, DNN)联系起来。

既然咱们已经有了深度神经网络,为何还要如此费力去使用卷积层和采样层,致使模型如此复杂呢?

这里出于两个考虑:

首先是计算量。图片数据的输入量通常比较大,若是咱们直接用若干深度神经层将其链接到输出层,则每一层的输入输出数量都很庞大,总计算量是难以想像的。

其次是模式特征的抓取。即使是使用很是庞大的计算量,深度神经网络对于图片模式的识别效果也未必尽如人意。由于它学习了太多噪声。而卷积层和采样层的引入,能够有效过滤掉噪声,突出图片中的模式对训练结果的影响。

你可能会想,我们只编写了10几行代码而已,使用的卷积神经网络必定跟上图差很少,只有四、5层的样子吧?

不是这样的,你用的层数,有足足50层呢!

它的学名,叫作Resnet-50,是微软的研发成果,曾经在2015年,赢得过ILSRVC比赛。在ImageNet数据集上,它的分类辨识效果,已经超越人类

我把对应论文的地址附在这里,若是你有兴趣,能够参考。

请看上图中最下面的那一个,就是它的大略样子。

足够深度,足够复杂吧。

若是你以前对深度神经网络有一些了解,必定会更加以为难以想象。这么多层,这么少的训练数据量,怎么能得到如此好的测试结果呢?而若是要得到好的训练效果,大量图片的训练过程,岂不是应该花很长时间吗?

没错,若是你本身从头搭建一个Resnet-50,而且在ImageNet数据集上作训练,那么即使你有很好的硬件设备(GPU),也须要很长时间。

若是你在本身的笔记本上训练……算了吧。

那么,TuriCreate难道真的是个奇迹?既不须要花费长时间训练,又只须要小样本,就能得到高水平的分类效果?

不,数据科学里没有什么奇迹。

究竟是什么缘由致使这种看似神奇的效果呢?这个问题留做思考题,请善用搜索引擎和问答网站,来帮助本身寻找答案。

小结

经过本文,你已掌握了如下内容:

  • 如何在Anaconda虚拟环境下,安装苹果公司的机器学习框架TuriCreate。
  • 如何在TuriCreate中读入文件夹中的图片数据。而且利用文件夹的名称,给图片打上标记。
  • 如何在TuriCreate中训练深度神经网络,以分辨图片。
  • 如何利用测试数据集,检验图片分类的效果。而且找出分类错误的图片。
  • 卷积神经网络(Convolutional Neural Network, CNN)的基本构成和工做原理。

可是因为篇幅所限,咱们没有说起或深刻解释如下问题:

  • 如何批量获取训练与测试图片数据。
  • 如何利用预处理功能,转换TuriCreate不能识别的图片格式。
  • 如何从头搭建一个卷积神经网络(Convolutional Neural Network, CNN),对于模型的层次和参数作到彻底掌控。
  • 如何既不须要花费长时间训练,又只须要小样本,就能得到高水平的分类效果(提示关键词:迁移学习,transfer learning)。

请你在实践中,思考上述问题。欢迎留言和发送邮件,与我交流你的思考所得。

讨论

你以前作过图片分类任务吗?你是如何处理的?用到了哪些好用的工具?比起我们的方法,有什么优缺点?欢迎留言,把你的经验和思考分享给你们,咱们一块儿交流讨论。

喜欢请点赞。还能够微信关注和置顶个人公众号“玉树芝兰”(nkwangshuyi)

若是你对数据科学感兴趣,不妨阅读个人系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

相关文章
相关标签/搜索