LeNet - Python中的卷积神经网络

本教程将  主要面向代码,  旨在帮助您 深刻学习和卷积神经网络。因为这个意图,我  不会花不少时间讨论激活功能,池层或密集/彻底链接的层 - 未来会有  不少教程在PyImageSearch博客上将覆盖  每一个层类型/概念  在不少细节。css

再次,本教程是您  第一个端到端的例子,您能够训练一个现实的CNN(并在实际中看到它)。咱们将在本系列帖子中稍后介绍激活功能,聚集层和彻底链接层的细节(尽管您应该已经知道卷积运算的基本知识); 可是在此期间,只需跟随,享受教训,并  学习如何使用Python和Keras实现您的第一个卷积神经网络。python

MNIST数据集


图1: MNIST数字识别数据集。git

您可能已经看过MNIST数据集,不管是在PyImageSearch博客上仍是在您研究的其余地方。在任何一种状况下,我将继续查看数据集,以确保您  准确了解咱们正在使用的数据。编程

MNIST数据集能够说是计算机视觉和机器学习文献中研究最多,数据最多的数据集,它是您在深刻学习之旅中使用的出色的“第一个数据集”。windows

注意:正如咱们将会发现的那样,即便在CPU上,在这个数据集上得到> 98%的分类精度也很容易,训练时间最短。缓存

该数据集的目标是对手写数字0-9进行分类。咱们共得到了7万张图像,一般有6万张图像用于培训,10,000张用于评估; 可是,咱们能够自由分割这些数据,由于咱们认为合适。共同分拆包括标准60,000 / 10,000,75%/ 25%和66.6%/ 33.3%。我将在博客文章中使用2/3的数据进行培训和1/3的数据进行测试。markdown

每一个数字表示为  28 x 28  灰度图像(来自MNIST数据集的示例能够在上图中看到)。这些灰度像素强度是无符号整数,像素值落在[0,255]的范围内  。 全部数字都放置在  黑色背景上 ,具备浅色前景(即,数字自己)为  白色和  各类灰色。网络

值得注意的是,许多库(如scikit-learn)都有内置的帮助方法来下载MNIST数据集,将其缓存到磁盘上,而后加载它。这些帮助方法一般将每一个图像表示为  784-d向量。架构

784来自哪里?dom

简单。这只是  平坦的  28 x 28 = 784的形象。

要从784-d矢量恢复咱们的原始图像,咱们简单地将阵列重塑为  28×28 图像。

在本博客的上下文中,咱们的目标是培训LeNet,以便咱们最大限度地提升咱们的测试集的准确性。

LeNet架构


图2: LeNet架构由两组卷积,激活和合并层组成,后面是彻底链接的层,激活,另外一个彻底链接,最后是一个softmax分类器(图像源)。

LeNet架构是卷积神经网络的一个很好的“第一架构”(特别是在MNIST数据集上进行了培训时,手写数字识别的图像数据集)。

LeNet很小,易于理解 - 但足够大,能够提供有趣的结果。此外,LeNet + MNIST的组合可以在CPU上运行,使初学者可以轻松地在深度学习和卷积神经网络中迈出第一步。

在许多方面,LeNet + MNIST是“Hello,World”等同于Deep Learning的图像分类。

LeNet架构由如下层组成:

而不是解释每层的卷积过滤器数量,过滤器自己的大小以及如今彻底链接节点的数量,我将保存这个讨论,直到咱们的  “使用Python和Keras实现LeNet”  部分博客文章,其中的源代码将做为辅助解除。

同时,咱们来看看咱们的项目结构 - 一个结构,咱们将  在之后的PyImageSearch博客文章中屡次重用

注意:原来的LeNet架构使用 TANH   激活功能而不是 RELU  。咱们  在这里使用RELU的缘由是由于它有更好的分类精度,由于一些很好的,理想的属性(我将在将来的博客文章中讨论)。若是您在LeNet中进行任何其余讨论,您可能会看到他们使用 TANH,   而不是再想想。

咱们的CNN项目结构

在咱们潜入任何代码以前,咱们先来看看咱们的项目结构:

为了保持代码的组织,咱们将定义一个名为pyimagesearch的包  。在 pyimagesearch   模块中,咱们将建立一个 cnn   子模块 - 这是咱们将存储卷积神经网络实现的地方,以及与CNN相关的任何帮助实用程序。

看看cnn里面  ,你会看到 网络   子模块:这是  网络实现自己将被存储的地方。顾名思义,这一点py   文件将定义一个名为LeNet的类  ,这是咱们在Python + Keras中实际的LeNet实现。

该 lenet_mnist py   脚本将是咱们的驱动程序,用于实例化LeNet网络架构,训练模型(或加载模型,若是咱们的网络是预先训练的),而后评估MNIST数据集上的网络性能。

最后, 输出   目录将在咱们的LeNet   模型训练完成后存储 ,从而容许咱们在后续调用lenet_mnist  时对数字进行分类 py,没必要从新训练网络。

过去一年,我我的一直在使用这个项目结构(或项目结构很是类似)。 我发现它颇有条理,易于扩展 - 随着咱们用更多的网络架构和帮助功能添加到这个库中,这将在将来的博客文章中变得更加明显。

用Python和Keras实现LeNet

首先,我会假设你已经有Kerasscikit学习,和OpenCV的系统上安装(和可选,启用GPU支持)。

不然,打开 礼物py   文件并插入如下代码:

第2-7行处理从keras   库导入所需的函数/类 

所述 LeNet   类被定义在  第9行,其次是 构建   于方法  11号线每当我定义一个新的网络架构,我  老是将它放在本身的类中(主要用于命名空间和组织目的),而后建立一个  静态 构建   函数。

该 构建   方法,顾名思义,须要提供的任何参数,其在  最低限度 包括:

  • 输入图像的  宽度
  • 输入图像的  高度
  • 输入图像的  深度(即通道数)。
  • 和数量  在咱们的数据集(即类标签的惟一的号码)。

我一般还包括一个  可用于加载预训练模型的 权值路径给定这些参数, 构建   函数负责构建网络架构。

谈到构建LeNet架构时,  第13行将实例化一个 Sequential   类,咱们将用它来构建网络。

如今模型已初始化,咱们能够开始添加图层:

在  第15-19行,咱们建立了第一组 CONV RELU POOL   图层集。

咱们的 CONV   层将学习20个卷积滤波器,每一个滤波器的大小为  5 x 5该值的输入尺寸与输入图像的宽度,高度和深度相同(在本例中为MNIST数据集),因此咱们将有  28 x 28个输入,单个通道用于深度(灰度)。

而后,咱们将xy方向上应用ReLU激活功能,而后  x和  y方向上移动2 x 2的 最大值池  (假设一个2 x 2的滑动窗口,经过激活体积“滑动”,进行最大运算,同时在水平和垂直方向上采起2像素的步骤)。

注:本教程主要是基于代码的意思是你 第一次接触到实现卷积神经网络-我会去到 不少更 详细的关于卷积层,激活功能,和最大集中在将来的博客帖子层。在此期间,只需试着跟随代码。 

咱们如今准备应用咱们的第二组 CONV RELU POOL   层:

这一次,咱们将学习  50个卷积滤波器,而不是像上一个图层集中的  20个卷积滤波器

一般,在网络的更深层次上,观察到的CONV   滤波器数量的 增长

接下来,咱们来到LeNet架构的彻底链接的层(一般称为“密集”层):

在  第27行,咱们取上一个MaxPooling2D   层的输出,  并将其  平坦化为一个向量,使咱们能够应用密集/彻底链接的层。若是您有神经网络的任何先前经验,那么您将知道一个密集/彻底链接的层是网络中的“标准”类型的层,其中上一层中的每一个节点都链接到下一层的每一个节点(所以,术语“彻底链接”)。

咱们的彻底链接的层将包含500个单位(28行),咱们经过另外一个非线性ReLU激活。

第32行是  很是重要的,虽然它很容易忽视 - 这一行定义了另外一个 Dense   类,但接受一个变量(即,不是硬编码)的大小。这个大小是由    变量表示的类标签的  数量 在MNIST数据集的状况下,咱们有10个类(咱们正在尝试学习识别的十位数中的每一个一个)。

最后,咱们应用一个softmax分类器(多项式逻辑回归),它将返回  几率列表,一个用于10个类标签中的每个(第33行)。具备最大几率的类标签将被选为网络的最终分类。

咱们的最后一个代码块处理加载一个预先存在的 weightsPath   (若是这样一个文件存在)并将构造的模型返回给调用函数:

建立LeNet驱动程序脚本

如今咱们已经使用Python + Keras实现了LeNet卷积神经网络架构,如今是定义lenet_mnist的时候了 py   驱动脚本将处理:

  1. 加载MNIST数据集。
  2. 将MNIST分红培训和  测试分组  
  3. 加载和编译LeNet架构。
  4. 培训网络
  5. 可选地将序列化的网络权重保存到磁盘,以即可以重用(而没必要从新训练网络)。
  6. 显示  网络输出的可视示例,以证实咱们的实现确实正常工做。

打开你的 lenet_mnist py   文件并插入如下代码:

第2-9行处理导入咱们须要的Python包。注意咱们如何  从cnn   和 pyimagesearch网络   子模块 导入咱们的 LeNet类  。

注意:若是您跟随此博客文章并打算执行代码,  使用此帖子底部的“下载”部分。为了保持这个短短的简洁,我已经省略了 __init__ py   更新可能会抛弃新的Python开发人员。

从那里,  第12-19行解析三个可选的命令行参数,每一个参数详细以下:

  • 保存模型  :指示器变量,用于指定在培训LeNet后是否 模型保存到磁盘。
  • 负载模型  :另外一个指示器变量,此时间指定咱们是否应该 加载从磁盘预先训练的模型。
  • 权重  :在这种状况下 保存模型   提供的 路径   应该指向咱们要 保存序列化的模型。而在这状况下 负载模型   提供的 权重   应该指向预先存在的权重文件咱们的系统上的生活。

咱们如今能够加载MNIST数据集并将其分为咱们的培训和测试分裂:

第25行从磁盘加载MNIST数据集。若是这是您首次  使用“MNIST Original”   字符串调用fetch_mldata函数 ,则须要下载MNIST数据集。MNIST数据集是一个55MB的文件,因此根据你的互联网链接,这个下载可能须要几秒到几分钟的时间。

在下载MNIST数据集以后,咱们将 数据   从一组  784-d特征向量(即原始像素强度)重构为  28×28灰度图像,咱们能够经过网络(30行)。

咱们的 数据   矩阵如今具备形状 70000 28 28   ; 然而,  存在一个问题 --Keras假定咱们将为每一个图像提供  至少 1个通道,所以咱们须要在数据   阵列中添加一个额外的维度第31行)。这一行执行后,新形状 数据   矩阵将是: 70000 28 28    - ,如今适合于经过咱们的LeNet架构。

最后,  第32-33行执行训练和测试拆分,使用2/3的数据进行训练,剩下的1/3用于测试。咱们也能够将图像从  [0,255]缩小至  [ 0,1.0 ],这是一种常见的缩放技术。

下一步是处理咱们的标签,以便它们能够与分类交叉熵损失函数一块儿使用:

39和40行处理咱们的培训和测试标签(即,MNIST数据集中每一个图像的“地面真相”标签)。

因为咱们使用分类交叉熵损失函数,咱们须要应用  将整数的标签从整数转换为  向量to_categorical函数  ,其中每一个向量范围从 ]  。该函数为每一个类标签生成一个向量  ,其中正确标签的索引设置为  1,全部其余条目设置为  0。

在MNIST数据集的状况下,咱们有10个lass标签,所以每一个标签如今表示为  10-d向量。例如,考虑培训标签  “3”应用 to_categorical   函数后,咱们的向量如今看起来像:

注意,除了如今设置为1的第三个索引以外向量中的全部条目都为零  

咱们如今准备创建咱们的 LeNet   架构,可选择从磁盘加载任何预先训练的权重,而后训练咱们的网络:

咱们将使用随机梯度降低(SGD)训练咱们的网络 ,学习率为 0.01  。分类交叉熵将被用做咱们的损失函数,这是在使用具备两个以上类标签的数据集时至关标准的选择。而后咱们的模型被编译并加载到第45-48行的内存中  

在这种状况下 负载模型  不提供,咱们要培养咱们的网络(52号线)。

培训咱们的网络是经过打电话来完成的   实例化模型的拟合方法   (第54和55行)。咱们将容许咱们的网络训练  20个纪元(代表咱们的网络将“看到”每一个训练示例共20次,以学习每一个数字类的区分过滤器)。

而后咱们对测试数据进行评估(59-61行),并将结果显示给咱们的终端。

接下来,咱们检查一下咱们是否应该将网络权重序列化为文件,以便咱们运行 lenet_mnistpy   脚本  后续时间,无需从头开始从新训练网络:

咱们的最后一个代码块能够从咱们的测试集中随机选择几位数字,而后经过咱们训练有素的LeNet网络进行分类:

对于每一个随机选择的数字,咱们使用LeNet模型(第71行对图像进行分类

  咱们网络的实际 预测是经过找到具备最大几率的类标签的索引得到的  记住,咱们的网络将经过softmax函数返回一组几率,每一个类别标签一个,所以网络的实际“预测”是具备最大几率的类标签。

76-80行处理将28 x 28图像调整  到  96 x 96  像素,以便咱们能够更好地可视化,而后绘制 图像   上的 预测  。

最后,  第82-86行将结果显示在咱们的屏幕上。

用Python和Keras训练LeNet

要在MNIST数据集上训练LeNet,请确保已使用 本教程底部找到“下载”表单下载源代码  这个 zip   文件包含本教程中详细介绍的全部代码 - 此外,此代码的组织方式与上面详细描述的  相同的项目结构,  确保在系统上正常运行(若是您的环境配置正确)。

下载后 邮政编码   存档,您能够经过执行如下命令在MNIST上训练LeNet:

个人机器输出结果以下:


图3:在个人Titan X上的MNIST数据集上训练LeNet每一个时期须要大约3秒钟。通过20个时代,LeNet在培训数据上达到98.90%的分类精度,测试数据的精度达到98.49%。

在个人Titan X GPU上,每一个纪元须要大约3秒钟,容许  整个训练过程在大约60秒内完成。

只有20个时代,LeNet 在MNIST数据集上达到  98.49%的分类精度 -  在全部计算时间只有60秒的时间里,差很少!

注意:若是执行 lenet_mnist py   脚本在咱们的CPU而不是GPU,指望每一个时代的时间跳到70-90秒您仍然能够在您的CPU上训练LeNet,只须要一段时间。

用Python和Keras评估LeNet

下面我列出了LeNet + MNIST实现的几个示例评估图像:


图4:应用LeNet对MNIST数据集中的数字进行分类。

在上述图像中,咱们能够正确地将数字分类为  “6”

在这个图像中,LeNet正确地将数字识别为  “2”


图5:在Python和Keras中实现LeNet。

下面的图像是CNN滤波器学习的卷积滤波器的鲁棒性,区分性质的一个很好的例子:这个  “6”是至关扭曲的,在数字的圆形区域之间留下了不多的差距,但LeNet仍然可以正确分类数字:


图6:使用LeNet和卷积神经网络正确分类特别难读的数字。

这是另外一个图像,此次分类严重偏斜的  “1”


图7:使用卷积神经网络对偶数位进行正确分类。

最后,最后一个例子演示了分类“2”的LeNet模型  


图8:使用LeNet和Deep Learning对数字进行分类的最终示例。

Running the serialized LeNet model

After our lenet_mnist.py  script finishes executing the first time (provided you supplied both--save-model  and --weights ), you should now have a lenet_weights.hdf5  file in youroutput  directory.

Instead of re-training our network on subsequent runs of lenet_mnist.py , we can instead load these weights and use them to classify digits.

To load our pre-trained LeNet model, just execute the following command:

I’ve included a GIF animation of LeNet used to correctly classify handwritten digits below:


图9: LeNet的示例动画正确分类数字。

概要

在今天的博文中,我演示了如何使用Python编程语言和Keras库实现LeNet架构,用于深刻学习。

LeNet架构是一个伟大的  “你好,世界”网络,让你的脚深刻学习和卷积神经网络。网络自己很简单,内存占用空间小,当应用于MNIST数据集时,能够在CPU或GPU上运行,使其成为实验和学习的理想选择,尤为是在深刻学习新手时。

本教程主要以  代码为重点,所以,我须要跳过  重要的卷积神经网络概念的详细评论,例如激活层,池层和密集/彻底链接的层(不然这个帖子可能  很容易被5x为长)。

在未来的博客文章中,我会详细介绍  每一个这些图层类型 - 同时,您只需熟悉代码并尝试本身执行。若是您感受  真的很大胆,请尝试调整每一个卷积层的过滤器数量和过滤器尺寸,看看会发生什么!

不管如何,我但愿你喜欢这篇博客文章 - 我必定会在未来作更深刻的学习和图像分类工做。

相关文章
相关标签/搜索