教你用Keras和CNN建立模型识别神奇宝贝!(附代码)

2018-09-17 14:48:09 浏览数 (1)

作者:ADRIAN ROSEBROCK

翻译:张恬钰

校对:万文菁

本文8500字,建议阅读30 分钟。

本文将讲解如何用Keras和卷积神经网络(CNN)来建立模型识别神奇宝贝!

用Keras创造一个卷积神经网络来识别神奇宝贝妙蛙种子的填充玩具

简介

今天的博客内容是构建完整端对端图像分类 深度学习应用系列的第二部分。

  • 第一部分:如何(快速)建立一个深度学习的图像数据库
  • 第二部分:Keras和卷积神经网络(今天的内容)
  • 第三部分:在iOS上运行Keras模型(下周发布)

在今天博客的最后,你将会了解如何在你自己的数据库中建立、训练并评估一个卷积神经网络。

在下周的博客中,我将会向你展示如何仅用几行代码就把你训练出来的Keras模型用在智能手机的app上。

为了让这个系列轻松、愉快,我决定实现我童年的一个梦想,那就是构造一个神奇宝贝图鉴。神奇宝贝图鉴是于神奇宝贝(一部很火的动画片、电子游戏和集换卡系列)世界中的一个设备(我曾经是以及现在还是神奇宝贝的大粉丝)。

如果你对于神奇宝贝不了解,你可以把神奇宝贝图鉴想象成一个可以识别神奇宝贝(一个长得像动物、存在于在神奇宝贝世界的生物)的智能手机app。

当然你也可以换成你自己的数据,我只是觉得很有趣并且在做一件很怀旧的事情。

想要知道如何在你自己的数据库中用Keras和深度学习训练一个卷积神经网络,继续往下读就行了。

目录

1.Keras和卷积神经网络

  • 我们的深度学习数据库
  • 卷积神经网络和Keras项目的结构
  • Keras和CNN结构
  • 完成我们的CNN Keras训练脚本
  • 用Keras训练CNN
  • 创造CNN和Keras训练脚本
  • 用CNN和Keras分类图片
  • 该模型的局限性
  • 我们能否使用这个Keras深度学习模型建一个REST API?

2.总结

Keras和卷积神经网络

在上周的博文中,我们学习了如何能快速建立一个深度学习的图像数据库——我们使用了博文中的过程和代码来收集、下载和组织电脑上的图像。

既然已有下载并组织好的图像,下一步就是在数据上训练一个卷积神经网络(CNN)。

我将会在今天的博文中向你展示如何用Keras和深度学习来训练你的CNN。下周将要发布这个系列的最后一部分,将会向你展示你如何仅用几行代码将你训练好的Keras模型应用在一个智能手机(特别是iPhone)app上。

这个系列的最终目的是帮助你建立一个能够运行的深度学习app——用这个系列的文章作为发灵感启发,并且开始帮助你建立自己的深度学习应用。

现在让我们开始用Keras和深度学习训练一个CNN。

我们的深度学习数据库

图1:神奇宝贝深度学习数据库中的样本示意图。它展示了神奇宝贝的每个种类。正如我们所见,数据库的内容范围很大,包含了插画、电影/电视节目截图、模型、玩具等。

我们深度学习的数据库有1191个神奇宝贝)的图片。

我们的目标是用Keras和深度学习训练一个卷积神经网络来识别和分类这些神奇宝贝。

我们将要识别的神奇宝贝包括这几种:

  • 妙蛙种子(234张图片)
  • 小火龙(238张图片)
  • 杰尼龟(223张图片)
  • 皮卡丘(234张图片)
  • 超梦(239张图片)

每个种类的训练图片的示意图在图1中可见。

正如你所见,我们的训练图片包括:

  • 电视节目和电影中的截图
  • 集换卡
  • 模型
  • 玩具
  • 粉丝的画作和艺术表达

我们的CNN将从这些涵盖范围很广的使N一大堆图片中识别出5种神奇宝贝。——而且我们将会看到,我们可以达到97%的分类准确率!

卷积神经网络和Keras项目的结构

今天的项目有很多活动部件?。我们现在从回顾项目的目录结构开始。

有3个目录:

  • dataset:包含了5个种类,每个种类有自己单独的子目录,这使得分析种类标签较为容易。
  • examples:包含了我们将要用来测试CNN的图片。
  • pyimagesearch模块:包含了SmallerVGGNet模型种类(在这片文章的后面将会用到)。

根目录下有5个文件:

  • plot.png:训练脚本运行之后产生的的训练/测试准确率和失败率的图像
  • lb.pickle:LabelBinarizer序列化的目标文件——包含个从种类指标到种类名称的查找机制
  • pokedex.model:这是我们序列化的Keras卷积神经网络的模型文件(即权值文件)
  • train.py:我们将用这个脚本来训练我们的Keras CNN,划分准确率/失败率,然后将CNN和标签二值序列化于磁盘上。
  • classify.py:测试脚本

Keras和CNN结构

图2:一个我称为“SmallerVGGNet”的VGGNet类神经网络,它将被用于和Keras一起训练一个深度学习分类器。

我们将要使用的CNN结构是VGGNet网络的一个小而紧凑的变体。Simonyan和Zisserman在他们2014年的论文Very Deep Convolutional Networks for Large Scale Image Recognition中引入。

VGGNet类结构有这些特点:

  • 在增加深度方面只用3×3个互相交叠的卷积层。
  • 使用最大化池化来减少体积。
  • 相比于softmax分类器,优先网络最后完全连接的层。

我假设你已经安装了Keras并且在你的系统上配置好了它。如果没有,这里有一些关于深度学习构建环境配置指导的链接:

  • 使用Python为深度学习配置Ubuntu
  • 使用Python建立Ubuntu 16.04 CUDA GPU进行深度学习
  • 配置macOS以便使用Python进行深入学习

如果你想要跳过配置深度学习环境,我推荐你用以下几个云上预先配置好的实体个:

  • Amazon AMI用于使用Python进行深度学习
  • 微软的数据科学虚拟机(DSVM)用于深度学习

现在让我们开始执行更小版本的BGGNet,即SmallerVGGNet。在pyimagesearch模块中创建一个命名为smallervggnet.py的新文件,插入以下代码:

首先我们输入模块——注意到它们都来自于Keras,每个都在Deep Learning for Computer Vision with Python课程中有涉及到。

注意:在pyimagesearch中创建__init__.py文件,这样Python就知道这个文件夹是一个模块。如果你对__init__.py这个文件或者如何用他们创建模块不熟悉,不要担心,用本文最后的“下载”部分来下载我的目录结构、源代码和数据库 图片样例。

在此,我们定义SmallerVGGNet类:

我们构建的方法需要4个参数:

  • width:图像的宽度
  • height:图像的高度
  • depth:图像的深度——也叫做隧道数
  • classes:数据库中的种类数(这将会影响模型的最后一层)。

在这篇博文中,我们用到神奇宝贝的5个种类,但是如果你为每个种类下载了足够多的样例图片,你可以使用807个神奇宝贝种类!

注意:我们将会使用96*96、深度为3的输入图片。请记住这个,因为我们已经解释了当它穿过网络输入的体积的空间参数。

因为我们使用TensorFlow的编译器后端,我们用频道最后的数据顺序来安排输入形状,但是如果你想使用频道最先(Theano等),那么它将在23-25行被自动处理。

现在,我们开始在模型中添加层:

以上是我们第一个CONV=>RELU=>POOL块。

卷积层有3×3个核的32个过滤器。我们在批规范化后使用激活函数RELU。POOL层有3×3的POOL大小来减少图片的空间参数,从96×96变成32×32(我们将使用96×96×3的输入图片来训练网络)。

如你在代码中所见,我们将网络中使用丢弃这个功能。丢弃的工作机制是随机断开从当前层到下一层之间的节点作。这个在训练批中随机断开的过程能够在模型中自然引入丢弃——层中没有一个单独的节点是用于预测一个确定的类、目标、边或者角。

从这里在使用另外一个POOL层之前加入(CONV=>RELU)*2层。

堆叠多个CONV和RELU层在一起(在减少体检的空间尺寸之前),这样们能够获得更丰富的特征。

注意: 增加过滤器尺寸,从32到64。网络越深,体积的空间尺寸越小,我们就能学习更多过滤器。 我们减少最大池化尺寸,从3×3变成2×2,以保证我们不会太快地减少空间尺寸。

这一阶段再次使用丢弃段。

现添加另外一组(CONV=>RELU)*2=>POOL:

注意到我们已经将的过滤器的尺寸增加到128。节点的25%再次被丢弃以减少过度拟合。

最后,我们有一组FC=>RELU的层和一个softmax分类器:

完全连接的层由具备矫正线性单元激活器和批规范化的Dense(1024)来指定。

最后再操作一次丢弃次。这次注意到,在训练时,我们丢弃了50%的节点。一般情况下,在我们的完全连接层你将使用一个40-50%丢弃率的丢弃和一个低得多的丢弃率,通常是在之前的层10-25%丢层(如果有某个丢弃在所有层都使用)。

我们用softmax分类器来完善这个模型,该分类器可为每个种类反馈预测概率型。

在这个部分的一开始的图2是一个SmallerVGGNet前几层的网络结构的图片。如果想看完整的SmallerVGGNet的Keras CNN结构,可以进入这个链接

https://www.pyimagesearch.com/wp-content/uploads/2018/04/smallervggnet_model.png。

执行我们的CNN Keras训练脚本

既然SmallerVGGNet已经完成了,我们可以用Keras来训练卷积神经网络了。

打开一个新的文件,命名为train.py,并且加入下面的代码,我们在此加载需要的包和库。

我们将使用“Agg” matplotlib后端将图片保存在背景中。(第三行)

ImageDataGenerator类将被用于数据增加,这是一个用已经存在于我们数据库的图片并且使用随机变换(旋转、剪切等)来阐释新的训练数据的技巧。数据增加能够防止过拟合。

第七行载入了Adam优化器,一个用来训练网络的优化器。

LabelBinarizer(第九行)是一个重要的类,这个类使得我们能够:

  • 输入一系列种类的标签(如,代表了在数据库中人类可以阅读的种类标签的字符串)
  • 把种类标签转化成一个独热编码向量。
  • 让我们能够取一个Keras CNN中的整数种类标签预测,并且把它转化为一个人类可读的标签。

在PyImageSearch博客上我经常被问到如何将一个种类标签字符串转换成一个整数及其反向操作。现在你知道方法就是使用LabelBinarizer类。

train_test_split函数(第10行)用于创建训练和测试划分。同样注意到第11行中载入SmallerVGGNet——这是我们上一节中已经完成了的Keras CNN。

这个博客的读者很熟悉我自己的imutils安装包。如果你还没有安装/升级它,可以这样安装它:

如果你正在用一个虚拟的Python环境(正如在PyImageSearchg博客中我们总是做的那样),确保你在安装/升级imutils之前使用workon命令来进入虚拟环境级前。

现在,让我们分析一下命令行的语句:

为了我们的训练脚本,需要使用三个命令行语句:

  • --dataset:这是输入数据库的路径。数据库在dataset的目录中,这个目录下面有代表每个种类的子目录。在每个子目录中,有大约250个神奇宝贝的图像。请看开头的目录结构。
  • --model:这个是输出模型的路径——这个训练脚本将会训练模型并且把它输出到磁盘上。
  • --labelbin:这是输出标签二值器的路径——正如你将看到,我们从dataset目录的名字中提取种类标签并建立标签二值器。

我们还有一个可选的语句“—plot”。如果你不特别指定一个路径/文件名,那么plot.png这个文件将被放在目前的工作目录中。

你不需要修改第22-31行来提供新的文件目录。命令行语句将在runtime被执行。如果你不明白,那么请回顾我的命令行语句那篇博文。

既然我们已经写完了命令行语句,现在开始一些重要的变量:

第35-38行初始化用于训练Keras CNN的重要变量。

  • EPOCHS:我们将要训练的网络总epoch次数。(即,次网络“看见”训练例子并从中学习模式的次数)
  • INIT_LR:开始的学习率——1e-3是我们将用于训练网络的Adam优化器的初始值。
  • BS:我们将输入很多捆图片进入网络进行训练。在每个epoch有很多捆,BS值控制了捆的大小。
  • IMAGE_DIMS:我们使用输入图片的空间大小。输入图片是96×96像素,并有3个通道(红绿蓝)。我也注意到把SmallerVGGNet设计成处理96×96的图片。

我们初始化两个列表——data和labels,他们是预先处理过的图片和标签。

第46-48行得到所有图片的路径并且随机切换它们。

从这,我们循环一下每个imagePahts:

在第51行我们循环imagePaths然后加载图片(第53行),并且调整它的尺寸使之适应我们的模型(第54行)。

现在更新data和labels列表。

使用Keras img_to_array函数来将图片转换成一个兼容Keras的数组(第55行),接着把图片贴在我们的data列表上(第56行)。

对于labels列表,我们从第60行的文件路径上提取label并把它加入列表(第61行)。

为什么这个种类标签分析过程有效?

到我们有意创建了dataset目录结构来得到下以下格式式

用第60行的路径分离器,我们可以将分离路径成一个数组,然后抓取从第二个到最后一项放进列表——种类标签。

如果这个过程让你困惑,我建议你打开Python的壳文件,然后通过分离操作系统的路径分离器的路径来探索imagePath这个例子。

我们继续。一些事情在下面的代码块中发生了——更多的预处理、二值化的标签和分割数据。

我们首先把data数据转化成一个NumPy数组,然后调整像素密度到[0,1]的范围(第64行)。我们也在第65行把一个列表中的labels转换到一个NumPy数据。一个表示data矩阵的大小(以MB为单位)的信息出现了。

然后,我们用scikit-learn的LabelBinarizer二值化标签nr(第70和71行)。

随着深入学习或任何机器学习,通常的做法是进行训练和测试的划分。在第75和76行,我们创建了一个对于数据的80/20的随机分划。

接着,我们创建的图像数据放大器项目:

既然我们在处理受限的数据点(每个种类<250个图像),我们可以利用训练过程中的数据给模型带来更多图像(基于已经存在的图像)进行训练。

数据放大应该是每个深度学习从业者的必备工具。

我们在第79-81行初始化ImageDataGenerator。

从这里开始我们编译模型并且开始训练。

在第85和86行,我们初始化96×96×3输入空间大小的Keras CNN。我将再重申一遍这个问题,因为我很多次被问到这个问题——SmallerVGGNet被设计成接受96×96×3的输入图片。如果你想要用不同的空间尺寸,你也许需要:

  • 要么减少较小图片的网络深度
  • 要么给较大图片增加网络深度

不要瞎改代码。先考虑较大或较小图片的含义!

我们用到有学习速度衰变的Adam优化器(第87行),然后用多种的交叉熵编译我们的模型,因为我们有>2个种类(第88和89行)。

注意:如果只有两个类别,你应该用二元的交叉熵作为遗失函数。

从这里开始,我们使用Keras fit_generator方法来训练网络(第93-97行)。要有耐心——这会花一些时间,这取决于你用CPU还是GPU来训练。

一旦Keras CNN完成了训练,我们将会想要保存(1)模型和(2)标签二值化,因为当我们用网络测试不在训练/测试集中的图片时,我们需要从磁盘上加载它们片。

我们保存模型(第101行)和标签二值化(第105-107行),这样我们就能在之后的classify.py脚本中使用它们。

标签二值化文件包含了种类指数和人类刻度的种类标签词典。其目的是让我们不必把我们使用Keras CNN脚本中的种类标签用一个固定值代表。

最终,我们可以划分出训练和失败准确率。

我选择保存我的图片到磁盘上(第121行)而不是展示它,有两个原因:

  • 我在使用一个没有显示器的云端服务器,
  • 我想要确保我不会忘记保存这些图片。

用Keras训练CNN

现在我们准备好训练神奇宝贝图鉴CNN。

确保阅读了这篇博客的“下载”部分以下载代码 数据。

然后执行下面的语句来训练模型。确保正确提供命令行语句。

查看训练脚本的结果,我们发现Keras CNN获得:

  • 训练集上的96.84%的分类准确率
  • 测试集上的97.07%的准确率

训练失败和准确率如下:

图3:用Keras训练的神奇宝贝图鉴深度学习分类器的训练和验证失败/准确率片s

正如你在图3中所见,我训练了这个模型100遍来达到在过拟合限制下的低失败率。如果有更多的训练数据,我们就能得到更高的准确率。

创建CNN和Keras训练脚本

既然我们的CNN已经被训练了,我们需要完成一个脚本来分类不在我们训练/测试集中的图片。打开一个新的文件,命名为classify.py,并加入以下代码:

首先载入必要的包(第2-9行)。

从这里开始分析命令行语句。

我们有三个必要的命令行语句需要分析:

  • model:我们刚才训练的模型的路径。
  • labelbin:标签二值化文件的路径。
  • image:我们输入图片文件的路径。

这三个命令在第12-19行。记住,你不需要修改这些命令——我将会在下一节告诉你如何运用runtime提供的命令行语句运行这个程序在的。

接着,我们加载并预处理图片。

现在我们加载输入image(第22行),备份命名为做output,以备展示。(第23行)

然后用训练时一样的方法预处理image(第26-29行)。

这时,加载模型和标签二值化,然后分类图片:

为了分类图片,我们需要模型和标签二值化。我们在第34和35行加载这两者。

接着,我们分类图片然后创建标签(第39-41行)。

剩下的代码块是为了展示:

在第46和47行,我们从filename中提取神奇宝贝的名字并且把它和label比较。correct变量将会根据结果显示”correct”或”incorrect”。显然这两行包含了这样的假设,即你输入的文件有一个文件名是它真正的标签。

我们进行如下步骤:

  • 附加百分比的概率和”correct”/”incorrect”的文本进了类标签(第50行)。
  • 调整output图片的尺寸使得它适合屏幕(第51行)。
  • 在output图片上画标签的文字(第52和53行)。
  • 展示output图像并且等待按键来退出(第57和58行)。

用CNN和Keras分类图片

现在准备运行classify.py脚本!

确保你已经从“下载”部分(在这篇博文的下面)获得了代码 图片。

一旦你已经下载并且解压了压缩文件,就把它放入这个项目的根目录中,并且跟着我从一个小火龙的图片开始。注意到我们已经提供了三个命令行语句来执行这个脚本:

图4:用Keras和CNN正确分类了一个输入图片

现在用尊贵和凶猛的布隆巴尔口袋妖怪填充玩具来考验我们的模型。

图5:Keras深度学习图片分类器再次正确分类输入图片。

尝试一个超梦(一个基因改造过的神奇宝贝)的玩具立体模型。

图6:在CNN中使用Keras、深度学习和Python我们能够正确分类输入图片。

如果一个神奇宝贝图鉴不能识别出皮卡丘,那它有什么用呢!

图7:用Keras模型我们可以识别标志性的皮卡丘。

现在尝试可爱的杰尼龟神奇宝贝。

图8:用Keras和CNN正确分类图片。

最后,再次分类有火尾巴的小火龙。这次他很害羞并且有一部分藏在了我的显示器下面。

图9:最后一个用Keras和CNN正确分类输入图片的例子。

这些神奇宝贝中的每一个都和我的新神奇宝贝不符合。

目前,有大概807种不同种类的神奇宝贝。我们的分类器被训练用来分类5种不同的神奇宝贝(为了简单的目的)。

如果你想要训练一个分类器来识别神奇宝贝图鉴中的更多神奇宝贝,你将要更多每个种类的更多的训练图片。理想的情况下,你的目标应该是在每个你想要识别的种类里有500-1000个图片。

为了获得训练图片,我建议你使用微软必应的图片搜索API,比谷歌的图片搜索引擎更好用。

这个模型的限制

这个模型的主要限制是很少的训练数据。我测试过不同的图片,有时这个分类器是不准确额。这时,我仔细检查了输入图片 网络然后发现图片中最显著的颜色很严重地影响了分类器。

比如,一个有很多的红色或者黄色的图片很可能让它被识别成小火龙。类似的,一个有很多黄色的图片通常被识别成皮卡丘。

这一部分是因为我们的输入数据。神奇宝贝显然是虚假的并且没有它们显示中的图片(除了立体模型和毛绒玩具)。

我们的图片大多数来自于一个粉丝的绘画,或者电影/电视的截图。此外,我们每个种类只有很少的数据(225-250个图片)。

理想的情况下,在训练一个卷积神经网络时我们每个种类有至少500-1000个图片。记住这个当你处理你自己的数据时。

我们能否使用Keras深度学习模型作为一个REST API?

如果你想用这个模型(或其它深度学习模型)作为一个REST API,我写过三个博文来帮助你开始:

  • 创建一个简单的Keras 深度学习REST API
  • 一个可拓展的Keras 深度学习REST API
  • 深度学习和Keras、Redis、Flask、Apache一起使用

总结

在今天的博文中你学习了如何用Keras深度学习库构建一个卷积神经网络s。

我们数据集的收集方法在上周的博文中已经讲过了。

我们的数据集包含5种神奇宝贝的1191个图片。

用卷积神经网络和Keras,我们能获得97.07%的准确率。这是很厉害的,因为:

  • 我们数据集不大
  • 我们网络中的参数量少

原文标题: Keras and Convolutional Neural Networks (CNNs) 原文链接: https://www.pyimagesearch.com/2018/04/16/keras-and-convolutional-neural-networks-cnns/

译者简介

张恬钰,上海交通大学本科物理专业,Emory University生物统计硕士在读。以后想继续在生物统计方向深造。希望能留在美国学习和工作。希望能和广大的数据爱好者做朋友!

0 人点赞