天问

盘点 | 对比图像分类五大方法:KNN、SVM、BPNN、CNN和迁移学习

选自Medium

机器之心编译

参与:蒋思源、黄小天、吴攀


图像分类是人工智能领域的基本研究主题之一,研究者也已经开发了大量用于图像分类的算法。近日,Shiyu Mou 在 Medium 上发表了一篇文章,对五种用于图像分类的方法(KNN、SVM、BP 神经网络、CNN 和迁移学习)进行了实验比较,该研究的相关数据集和代码也已经被发布在了 GitHub 上。

项目地址:https://github.com/Fdevmsy/Image_Classification_with_5_methods

图像分类,顾名思义,就是为输入图像打上固定类别的标签。这是计算机视觉领域的核心问题之一。尽管听起来很简单,但图像分类有大量不同的实际应用。

传统方式:特征描述和检测

也许对一些样本任务有好处,但实际情况要复杂得多。

因此,我们并没有通过代码的形式直接指出每一类型的外观(visual appearance),而是使用机器学习——为计算机提供每一类的诸多实例,接着开发学习算法观察这些实例,并学习每一类的外观。

然而,图像分类如此复杂,以至于其处理经常用到深度学习模型,比如 CNN(卷积神经网络)。我们已经知道,我们在课堂上学习的不少算法(如 KNN、SVM)通常很擅长数据挖掘;但是对于图像分类,它们却不是最佳选择。


因此,我们将对课堂中学到的以及 CNN 和迁移学习等算法做一个对比。


目标


我们的目标是:


1. 把 KNN、SVM、BP 神经网络与业界处理图像识别问题的算法——CNN 和迁移学习——进行对比。

2. 获得深度学习经验。

3. 通过 TensorFlow 探索机器学习框架。


系统设计 & 实现细节


算法与工具


本项目使用的 5 个方法是 KNN、SVM、BP 神经网络、CNN 和迁移学习。

全项目可分为 3 类方法:


  • 第一类方法:使用 KNN、SVM、BP 神经网络这些课堂算法。这些算法强大易实现。我们主要使用 sklearn 实现这些算法。

  • 第二类方法:尽管传统的多层感知器模型已成功应用于图像识别,但由于其节点之间的全连接性,它们遭遇了维度的难题,从而不能很好地扩展到更高分辨率的图像。因此我们使用深度学习框架 TensorFlow 打造了一个 CNN。

  • 第三个方法:重新训练一个被称作 Inception V3 的预训练深度神经网络的最后一层,同样由 TensorFlow 提供。Inception V3 是为 ImageNet 大型视觉识别挑战赛训练的,使用了 2012 年的数据。这是计算机视觉的常规任务,其中模型试图把全部图像分为 1000 个类别,比如斑马、达尔阿提亚人和洗碗机。为了再训练这一预训练网络,我们要保证自己的数据集没有被预训练。

实现


第一类方法:预处理数据集,并使用 sklearn 实现 KNN、SVM、BP 神经网络。


首先,我们使用 OpenCV 包定义了 2 个不同的预处理函数:第一个是图像到特征向量,它可以重调图像大小,并把图像转化为行像素列表;第二个是提取颜色直方图,即使用 cv2.normalize 从 HSV 颜色空间提取 3D 颜色直方图,并平化(flatten)结果。


接着,建构若干个我们需要解析的参数。由于想要同时测试整个数据集和带不同数量标签的子数据集的精确度,我们构建了一个作为参数的数据集并解析进我们的程序。我们同样构建了用于 k-NN 方法的邻元素数作为解析参数。

之后,我们开始提取数据集中的每一图像特征,并将其放入数组。我们使用 cv2.imread 读取每一图像,通过从图像名称中提取字符串来拆分标签。在我们的数据集中,我们使用相同格式——类别标签. 图像序号.jpg——设置名称,因此我们可以轻易提取每张图像的分类标签。接着我们使用这两个函数提取 2 种特征并附加到数组 rawImages,而之前提取的标签附加到数组标签。


下一步是使用从 sklearn 包导入的函数 train_test_split 拆分数据集。这个集具有后缀 RI,RL 是 rawImages 和标签对的拆分结果,另一个是特征和标签对的拆分结果。我们使用 85% 的数据集作为训练集,余下的 15% 作为测试集。


最后,我们应用 KNN、SVM、BP 神经网络函数评估数据。对于 KNN 我们使用 KNeighborsClassifier,对于 SVM 我们使用 SVC,对于 BP 神经网络我们使用 MLPClassifier。

第二类方法:使用 TensorFlow 构建 CNN。TensorFlow 的全部目的在于使你打造一张计算图(使用 Python 等语言),接着在 C++ 中执行该图(在相同计算量的情况下,C++比 Python 更高效)。


TensorFlow 也可自动计算优化图变量所需的梯度,从而使模型表现更好。这是由于该图由简单的数学表达式组合而成,因此可通过导数链式法则计算全图的梯度。


一张 TensorFlow 图包含以下几个部分,每一部分将在下文详述:


  • 占位符变量,用于输入数据到图。

  • 优化向量以使卷积网络表现更好。

  • 卷积网络的数学公式。

  • 可用于指导变量优化的成本衡量标准。

  • 更新变量的优化方法。

  • CNN 架构由一堆不同的层组成,这些层通过可微分函数可把输入量转化为输出量。

因此,在我们的实现中,第一层是保存图像,接着我们使用 2 x 2 最大池化和修正线性单元(ReLU)的构建 3 个卷积层。输入是 4 维张量:


  • 图像序号。

  • 每一图像的 Y 轴。

  • 每一图像的 X 轴。

  • 每一图像的通道(channel)。


输出是另一个 4 维张量:


  • 图像序号,与输入相同。

  • 每一图像的 Y 轴。如果使用 2x2 池化,接着输入图像的高和宽除以 2。

  • 每一图像的 X 轴。同上。

  • 由卷积滤波器生成的通道。

接着,我们我们在网络末端构建了 2 个全连接层。输入是一个 2 维的形状张量 [num_images、num_inputs]。输出也是一个 2 维的形状张量 [num_images、num_outputs]


然而,为了连接卷积层和全连接层,我们需要一个平层(Flatten Layer)以把 4 维向量减少至可输入到全连接层的 2 维。


CNN 末端通常是一个 softmax 层,它可归一化来自全连接层的输出,因此每一元素被限制在 0 与 1 之间,并且所有元素总和为 1。


为了优化训练结果,我们需要一个成本衡量标准并在每次迭代中将成本降至最少。这里我们使用的成本函数是交叉熵(tf.nn.oftmax_cross_entropy_with_logits()),并在所有的图像分类中取交叉熵的平均值。优化方法是 tf.train.AdamOptimizer(),它是梯度下降的高级形式。这是一个可被调节的参数学习率。

第三种方法:再训练 Inception V3。现代目标识别模型有数以百万计的参数,并可能需要花费数周的时间才能完全训练一个模型。迁移学习是一种采用在分类数据集(如 ImageNet)中已训练的模型而快速完成这一工作的方法,因为其只需要重新训练新类别的权重就行。虽然这样的模型并没有完全训练的模型表现好,但对于许多应用来说,这是非常高效的,因为其不需要 GPU 并可以在笔记本上花半个小时就完成训练。


读者可以点击一下链接进一步了解迁移学习的训练过程:https://www.tensorflow.org/tutorials/image_retraining


首先我们需要获取预训练模型,并移除旧的顶层神经网络,然后再基于我们的数据集重新训练一个输出层。虽然猫的所有品种并没有在原始 ImageNet 数据集和全训练的模型中体现,但迁移学习的神奇之处就在于其可以利用已训练模型用来识别某些目标的底层特征,因为底层特征可以在很多不更改的情况下应用于很多识别任务。然后我们分析本地的所有图片并计算每张的瓶颈值(bottleneck values)。因为每张图片在训练过程中重复使用了多次,所以计算每个瓶颈值需要花费大量时间,但我们可以加快缓存这些瓶颈值,也就可以省去重复的计算。

该脚本将运行 4000 次训练步。每一步从训练集中随机选择 10 张图片,并从缓存中搜索其瓶颈值,然后再将它们训练最后一层以得到预测。这些预测会通过对比真实标注值而通过反向传播过程更新最后一层的权重。

实验


数据集


Oxford-IIIT Pet 数据集:http://www.robots.ox.ac.uk/~vgg/data/pets/


该数据集有 25 种狗和 12 种猫。每一种类别有 200 张相片。我们在该项目中只会使用 10 种猫。



在该项目中我们用的类别为 [斯芬克斯猫、暹罗猫、布偶猫、波斯猫、缅因猫、英国短毛猫、孟买猫、伯曼猫、孟加拉豹猫、阿比西尼亚猫]。


因此在数据集中我们总共有 2000 张图片。虽然图片的尺寸是不同的,但我们可以调整为固定的大小如 64x64 或 128x128。

预处理


在该项目中,我们主要使用 OpenCV 对图片进行预处理,如读取图片放入阵列或调整为我们需要的大小等。


提升图像训练结果的一个常用方法就是对训练输入随机进行变形、裁剪或亮度调整处理。由于采用了同一图片所有可能的变体,该方法不仅具有扩展有效训练数据大小的优点,同时还倾向帮助网络使用分类器学习处理所有在现实生活中可能出现的畸变。


具体请查看:https://github.com/aleju/imgaug.

评估


第一个方法:第一部分为预处理数据集和使用 sklearn 应用 KNN、SVM 和 BP 神经网络。


在程序中有很多参数可以调整:在 image_to_feature_vector 函数中,我们设置的图片尺寸为 128x128,我们之前也尝试过使用其他尺寸(如 8x8、 64x64、256x256)进行训练。我们发现虽然图片的尺寸越大效果越好,但大尺寸的图片同样也增加了执行时间和内存需求。因此我们最后决定使用 128x128 的图片尺寸,因为其并不太大,同时还保证了准确度。


在 extract_color_histogram 函数中,我们将每个通道的二进制值设置为 32,32,32。在先前的函数中,我们还尝试了 8, 8, 8 和 64, 64, 64。虽然更高的数值能有更优的结果,但同时也要求更长的执行时间,因此我们认为 32,32,32 是比较合适的。

对于数据集,我们训练了 3 种。第一种是有 400 张图片、2 种标注的子数据集。第二种是有 1000 张图片、5 种标注的子数据集。最后一种是有 1997 张图片、10 种标注的全数据集。我们将不同的数据集解析为程序中的参数。

在 KNeighborsClassifier 中,我们只改变近邻的数量并储存每一种数据集最优 K 值的分类结果。其他所有参数都设为默认。


在 MLPClassifier 中,我们设置每一个隐藏层有 50 个神经元。我们确实测试了多个隐藏层,但好像对最后的结果没有明显的变化。最大的迭代次数设置为 1000,并且为了确保模型能够收敛,我们容忍差设置为 1e-4。同时还需要设置 L2 罚项的参数 alpha 为默认值,随机状态为 1,求解器设置为学习速率为 0.1 的「sgd」。


在 SVC 中,最大迭代次数为 1000,类别权重设置为「balanced」。


我们程序的运行时间并不会太久,对于我们的三种数据集大概分别花 3 到 5 分钟左右。

第二种方法:使用 TensorFlow 构建 CNN


使用整个大数据集会需要很长的时间计算模型的梯度,因此我们在优化器每一次迭代中都只使用小批量的图片更新权重,批量大小一般是 32 或 64。该数据集分为包含 1600 张图片的训练集、包含 400 张图片的验证集和包含 300 张图片的测试集。


该模型同样有许多参数需要调整。


首先是学习率。优良的学习率因为其足够小而很容易令模型收敛,同时又足够大令模型的收敛速度不至于太慢。所以我们选择了 1 x 10^-4。

第二个需要调整的参数是投入到网络的图片尺寸。我们训练了 64x64 和 128x128 两种图片尺寸,结果表明尺寸越大模型精度就越高,但代价是运行时间会更长。


然后是神经网络层级数和它的形状。然而实际上由于这一方面有太多的参数可以调整,所以很难在所有的参数间找到一个最优值。


根据网上的很多资源,我们发现对于构建神经网络,参数的选择很大一部分都是根据已有的经验。


最开始,我们希望构建相当复杂的神经网络,其所采用的参数如下:


  • # Convolutional Layer 1. filter_size1 = 5 num_filters1 = 64

  • # Convolutional Layer 2. filter_size2 = 5 num_filters2 = 64

  • # Convolutional Layer 3. filter_size3 = 5 num_filters3 = 128

  • # Fully-connected layer 1. fc1_size = 256

  • # Fully-connected layer 2. fc1_size = 256

我们采用 3 个卷积层和 2 个全连接层,它们的结构都比较复杂。


然而,我们的结果是:过拟合。对于这样的复杂网络,训练精度在迭代一千次后就达到了 100%,但测试精度仅仅只有 30%。最开始,我们十分疑惑为什么模型会过拟合,然后开始随机调整参数,但这时候模型的表现却又变好了。幸好几天后我碰巧读到了 Google 在讨论深度学习的一篇文章:https://medium.com/@blaisea/physiognomys-new-clothes-f2d4b59fdd6a 该文章指出他们所主导的项目是有问题的:「一个技术性的问题是如果少于 2000 个样本,那么其是不足以训练和测试如同 AlexNet 那样的卷积神经网络而不出现过拟合情况。」所以我才意识到我们的数据集实在是太小了,而网络构架又太复杂,这才产生了过拟合现象。

我们的数据集正好包含 2000 张图片


因此,我开始减少神经网络的层级数和核函数的大小。我尝试调整了很多参数,以下是我们最后使用的神经网络架构参数:


  • # Convolutional Layer 1. filter_size1 = 5 num_filters1 = 64

  • # Convolutional Layer 2. filter_size2 = 3 num_filters2 = 64

  • # Fully-connected layer 1. fc1_size = 128

  • # Number of neurons in fully-connected layer.

  • # Fully-connected layer 2. fc2_size = 128

  • # Number of neurons in fully-connected layer.

  • # Number of color channels for the images: 1 channel for gray-scale. num_channels = 3

我们仅仅使用 2 个小型的卷积层和 2 个全连接层。训练结果并不好,在迭代 4000 次后同样出现了过拟合现象,但测试精度还是要比前面的模型高 10%。


我们仍然在寻找解决的办法,然而一个显然易见的原因是我们的数据集实在是太小了,我们也没有足够的时间做更多的改进。


作为最后的结果,我们在 5000 次迭代后大概实现了 43% 的精度,该训练花了一个半小时。实际上,我们对这一结果比较沮丧,因此我们准备使用另一标准数据集 CIFAR-10。



CIFAR-10 数据集由 60000 张 32x32 10 类彩色图片,每一个类别都有 6000 张图片。该数据集包含了 50000 张训练集和 10000 张测试集。


我们使用了和上面相同的神经网络架构,在 10 小时的训练后,我们在测试集上实现了 78% 的准确度。

第三种方法:再训练 Inception V3,我们随机选取一些图片进行训练,而另一批图片用于验证。


该模型同样有许多参数需要调整。


首先是训练步,默认值是 4000 步。我们也可以根据情况增加或减少以尽快获得一个可接受的结果。


随后是学习率,该参数控制了在训练期间更新至最后一层的量级。直观地说,如果学习速率小,那么需要更多的时间进行学习,但最终其可能收敛到更优的全局精度。训练批量大小控制了在一个训练步中检查图片的多少,又因为学习率应用于每一个批量,如果能以更大的批量获得相似的全局效果,我们需要减少它。


因为深度学习任务所需要的运行时间通常很长,所以我们并不希望模型在训练几小时后实际上表现很糟糕。所以我们需要经常获得验证精度的报告。这样我们同样可以避免过拟合。数据集的分割是将 80% 的图片投入到主要的训练中,10% 的图片作为训练期间经常进行的验证集,而剩下 10% 的图片作为最终的测试集以预测分类器在现实世界中的表现。

结果


第一类方法:预处理数据集并使用 sklearn 实现 KNN、SVM 和 BP 神经网络。


结果在下表中。由于 SVM 结果非常差,甚至低于随机猜测,我们不再展示其结果。


从结果中我们看到:


  • 在 k-NN 中,原始像素和直方图精确度是相对等同的。在 5 个标签的子数据集,直方图精确度比原始像素高一点;但是整体来讲,原始像素的结果更好。

  • 在神经网络 MLP 分类器中,原始像素精确度远低于直方图。对于整个数据集(10 个标签),原始像素精确度甚至低于随机猜测。

  • 所有这 2 个 sklearn 方法并没有良好表现,在整个数据集中(10 标签数据集)识别正确分类的精确度仅约有 24%。这些结果说明,通过 sklearn 分类图像效果欠佳,它们在使用多个类别分类复杂图像时表现并不好。但是相比于随机猜测,它们确实有提升,只是还不够。

基于以上结果,我们发现为了提升精确度,使用一些深度学习方法很必要。


第二类方法:使用 TensorFlow 构建 CNN。如上所述,由于过拟合我们不能获取好的结果。


正常情况下训练需要半个小时,然而由于结果过拟合,我们认为这一运行时间并不重要。通过和第一类方法的比较,我们看到:尽管 CNN 过拟合训练数据,我依然得到了更好的结果。


第三类方法:再训练 Inception V3


整个训练过程不超过 10 分钟。我们获得了极好的结果,并真正看到了深度学习和迁移学习的力量。


演示:


结论


基于以上比较,我们可以看到:


  • KNN、SVM 和 BP 神经网络对于某些特定图像分类任务是不够的。

  • 虽然我们会在 CNN 中过拟合,但这仍然比那些课堂方法要好。

  • 迁移学习在图像分类问题上效率很高,功能强大。它准确快速,可以在短时间内完成训练——而且不需要 GPU 的帮助。即使你只有一个很小的数据集,它也可以达到很好的效果,并且减少了过拟合的概率。


我们已经从图像分类任务中学到了很多,这类任务与课堂上的其他分类任务大不相同。数据集相对较大且稠密,需要的网络十分复杂,且大多方法依赖于 GPU 的计算能力。

经验:


  • 裁剪或重调图像,使其更小

  • 在训练的每个迭代中随机选择一个小 batch

  • 在验证集进行验证的时候随机选择一个小 batch,在训练过程中频繁记录验证分数

  • 可以使用 Image Augmentation 处理图片,增大数据集体量

  • 对于图像分类任务,我们需要比 200 x 10 的更大的数据集,CIFAR-10 数据集包含 6 万张图像。

  • 越复杂的网络需要越大的数据集进行训练

  • 小心过拟合


注: 第一种方法由 Ji Tong 实现:https://github.com/JI-tong 


参考文献


1. CS231n Convolutional Neural Networks for Visual Recognition:http://cs231n.github.io/convolutional-networks/

2. TensorFlow Convolutional Neural Networks:https://www.tensorflow.org/tutorials/deep_cnn

3. How to Retrain Inception』s Final Layer for New Categories:https://www.tensorflow.org/tutorials/image_retraining

4. k-NN classifier for image classification:http://www.pyimagesearch.com/2016/08/08/k-nn-classifier-for-image-classification/

5. Image Augmentation for Deep Learning With Keras:http://machinelearningmastery.com/image-augmentation-deep-learning-keras/

6. Convolutional Neural Network TensorFlow Tutorial:https://github.com/Hvass-Labs/TensorFlow-Tutorials/blob/master/02_Convolutional_Neural_Network.ipynb

点击阅读原文,查看全部嘉宾阵容并报名参与机器之心 GMIS 2017  ↓↓↓

博客地址:http://blog.yoqi.me/?p=3439
扫我捐助哦
喜欢 1

这篇文章还没有评论

发表评论