首页 > 技术文章 > 第7章 你的第一个分类器

paladinzxl 2018-08-23 08:27 原文

第7章 你的第一个分类器

       在过去几章,我们花费时间讨论了图像基础、学习类型、甚至是构建图像分类器时的四个步骤,但是到目前为止我们还没有构建一个真正的自己的分类器。

       我们先构建几个辅助工具,以方便从磁盘上预处理和加载图像。之后,我们讨论k-Nearest Neighbors (KNN)分类器,你将首先使用机器学习来探索图像分类器。事实上,这个算法非常简单,它根本不做任何实际的“学习”——但它仍然是一个重要的算法,以至于我们可以在以后的章节中理解神经网络是如何从数据中学习的。

       最后,我们应用KNN算法在数据集上识别不同动物的种类。

1          使用图像数据集

当使用数据集的时候,我们必须首先考虑以字节为单位的数据集的大小。数据集是否足够合适以满足机器的RAM大小?我们是否可以像加载大矩阵或数组那样加载数据集?或是数据集太大,超出了我们机器的内存,要求我们一次只能将数据集“分块”成片段并一次只加载一部分?

一些数据集较小可以直接加载进内存,而在大的数据集上工作时,就需要开发一些灵活的工具来有效的加载图像而不是造成内存崩溃。

因此,在构建图像分类器之前,花些时间来组织、预处理和加载数据集是很重要的一方面。

1.1    “Animals”数据集的介绍

 

图1 三个类别每个类别1000张图像,共3000张的数据集例子

“Animals”数据集是一个简单的示例数据集,用于展示使用简单的机器学习技术和高级的深度学习算法时如何训练图像分类器。

Animals数据集中的图像严格分成三个类别:dogs、cats、pandas,每个类别有1000张示例图像。猫和狗的图像是从kaggle的Dogs vs. Cats挑战(http://pyimg.co/ogx37))中抽取来的,而pandas则是从ImageNet数据集中抽取来的。

仅包含3000张图像,可以容易的加载进内存中,这将使训练更快、也不需要额外的编写分块加载图像的代码。深度学习模型可以快速的以CPU或GPU模式在这个数据集上工作。无论你的硬件配置如何,你都可以使用这个数据集学习机器学习和深度学习的基础。

本章的目标是使用KNN分类器仅利用原始像素强度(如不考虑特征提取)尽量对一张图像进行分类。可以看到,原始像素强度不适合于KNN算法,尽管如此,这扔是一个重要的基准实验,因为我们可以理解为什么CNNs能够在原始像素强度上获得如此高的精度,而传统的机器学习算法却不能做到这一点。

1.2    深度学习工具箱的开始

在贯穿全书中,我们将构建我们自定义的深度学习工具箱。我们将从基本的辅助函数和类开始,对图像进行预处理并加载小数据集,最终构建当前最先进的CNNs应用。

实际上,这是作者在运行深度学习实验时使用的相同的工具箱。这个工具包将逐章逐步建立起来,让您可以看到组成软件包的各个组件,最终成为一个完整的库,可以用来快速构建和训练您自己的自定义深度学习网络。

让我们开始定义我们工具箱的项目结构:

 

可看到,我们有一个单独定义的模块pyimagesearch,我们开发的所有代码都在该模块下。

为了本章的开发需要,我们定义两个子模块:

 

datasets子模块使用命名为simpledatasetloader的类开始执行,我们将使用这个类从磁盘中加载小的数据集(可以加载进主内存中),可选的功能是依据一些函数对数据集中的每个图像进行预处理,并返回:(1)图像(例如,原始像素强度);(2)与每个图像相关的类别标签。

之后,执行preprocessing模块,就像后面章节看到的,这里有一些预处理方法,我们可以应用到图像的数据集上来提升分类精确度,例如均值减法、抽样随机补丁、或简单的将图像调整到固定大小。在这个例子中,我们的SimplePreprocessor类将从磁盘加载图像、将它调整到固定大小并且忽略长宽因素(ratio aspect)。在下两部分,我将手动实现SimplePreprocessor和SimpleDatasetLoader。

1.3    基本的图像预处理器

机器学习算法如KNN、SVMs甚至是CNNs要求数据集中的所有图像有固定的特征向量大小。对于图像,这一要求意味着我们的图像必须经过预处理和缩放,以具有相同的宽度和高度。

有许多方法可以完成这种大小调整和缩放,从尊重原始图像的长宽比的更先进的方法,到忽略长宽比的简单方法,这种方法简单地压缩宽度和高度到所需的尺寸。实际上,使用哪种方法取决于变化因素的复杂性,一些可以忽略长宽比,一些则不可以。

本章中,我们构建一个基本的解决:构建一个忽略长宽比的图像预处理器。如simplepreprocessor.py中代码所示,其中这里接受一个缩放大小宽、高参数,默认使用的差值算法为cv2.INTER_AREA。这里的预处理做的工作很基本,接收输入图像,调整到固定大小,然后返回。与下一节中的图像加载器结合,可以快速的将磁盘中的原始图像调整为我们需要的分类器所需要的图像大小。

1.4    构建图像加载器

图像加载器见simpledatasetloader.py文件,初始化__init__(self, preprocessors=None)中可选的接收一个预处理器,可用于之后加载的原始图像进行预处理。注意,这里将preprocessor设定为一个列表可用于接收多个预处理器,可对原始图像进行一系列预处理操作,如调整大小、均值减法、转换到合适Keras处理的格式等等。

在load(self, pathname, verbose=-1)中,我们接收原始图像的路径,这里的路径格式为/dataset_name/class/image.jpg,即dataset_name为数据集的名字,class为数据集中类别名字,image.jpg为实际图像的名字,具体框架格式见该章示例程序。verbose用于显示加载的图像个数的一种显示控制参数,默认不打印图像加载信息。

在本书中几乎所有项目都是按照这种项目框架来组织的,数据集也是按照这种配置,强烈建议按照这种组织。

此数据集加载程序的唯一注意事项是,它假定数据集中的所有图像都可以一次装入主内存。对于不能一次加载的数据集,我们需要一种更复杂的加载方式,这将在Practioner Bundle中阐述。

现在我们理解了图像如何加载进内存,也已经对图像进行了预处理,现在就可以进入KNN分类器设计阶段了。

2          K-NN简单的分类器

KNN是目前为止最简单的机器学习和图像分类算法。实际上,它不需要学习,只是利用了特征向量之间的距离度量。简单的说,它对未知数据点的分类,通过在K个最近邻中通过投票选择出票数最多的类别作为该未知数据点的类别。为了使KNN算法发挥作用,它提出了一个基本假设,即具有相似视觉内容的图像紧密地位于一个N维空间中。

为了在KNN算法度量两个数据点的距离,我们需要选择一个距离度量或类似的函数。衡量距离的x距离如下:

 

 

最常见的欧拉距离时x=2,曼哈顿距离则x=1。实际上,在KNN中可以任意选择距离度量公式,这里我们以欧拉距离为例。

2.1    KNN工作示例

例如当选择k=3时,如图2所示,在最近的三个距离中,为2个cats和1个panda,依据投票规则,这里未知的图像分类为cats。

 

图2 k=3时KNN示例

2.2    KNN超参数

这里有两个最明显的超参数,一个是k值,当k太小,则无法有效的分类未知图像,当k太大时,则可能过于平滑结果不准确。另一个超参数则是距离度量公式,欧拉距离是最佳的吗?其它距离度量如何?

2.3    实现KNN算法

KNN算法的目标是在Animals数据集上使用原始像素强度训练一个分类器且用它对未知图像进行分类。我们使用前几章描述的4步机器学习步骤进行:

步骤一:收集数据。这里我们animals有三个类,每个类1000张图像,通过预处理将图像缩放到32*32大小,那么每张图像的维度大小为32*32*3=3072整数维度。

步骤二:划分数据集。这里将数据集按照75%训练集和25%测试集划分。

步骤三和步骤四:训练、评估KNN算法。

通过示例程序框架,我们在knn.py中编写主程序,这里首先通过argparse模块对输入参数进行格式化处理。

根据代码,我们知道通过SimpleDatasetLoader().load()函数返回的data为(3000, 32, 32, 3)的维度大小,然后我们通过data.shape[0]将数据原封不动的调整为(3000, 3072)的维度格式,这样我们根据LabelEncoder().fit_transform(labels)获得的(3000, [0…0, 1…1, 2…2])其中012为类别整数化后的表示,就可以将(data, label)调整为维度参数都对应且便于参数化处理了。在许多机器学习算法中,都假定类别标签编码为整数,因此这一步转换是很有必要的。

之后,我们使用python的train_test_split()函数对数据集data进行划分数据集,约定成俗的做法是X表示用于训练和测试的数据集表示,Y表示类别标签。因此,约定使用trainX和testX分别表示训练和测试示例,trainY和testY分别表示训练和测试标签。最后,根据示例程序中的最后部分调用KNN算法进行训练和测试。

2.4    KNN结果

在knn.py所在目录下,执行:python knn.py --dataset ../datasets/animals,将显示执行过程,且在运行结束后显示出评估结果。

其中显示数据缓存大小约为9M,是因为我们3000张图像,每张图像32*32*3大小。根据评估,我们的分类器正确率在50%左右,比我们随机猜测1/3要高一些,说明还是不错的。

但是KNN的一个主要缺陷是,它简单但是不能从数据中学习。我们的下一章将讨论参数化学习(parameterized learning)的概念,在这个概念中,我们可以从图像本身学习模式(patterns),而不是假设具有相似内容的图像将在一个N维空间中聚集在一起。

2.5    KNN的优缺点

KNN的一个主要优点是简单实施和理解。但是,它在分类阶段将花费大量时间,我们可以通过近似最近邻算法(Approximate Nearest Neighbor)来克服,但是这实际上是准确率与时间/空间的权衡。KNN与大多数机器学习算法(所有深度学习算法)相比,后者是在训练阶段花费大量时间来获得较高的准确率,而在测试分类阶段进行快速分类。

考虑到这些,为什么还要学习KNN呢?一个原因是它简单易于理解。另一个原因是它给我们一个测试基准,可以用于和神经网络和CNNs进行性能比较。

推荐阅读