跟我学Python图像处理丨图像分类原理与案例
阅读原文时间:2023年07月08日阅读:2

摘要:本篇文章将分享图像分类原理,并介绍基于KNN、朴素贝叶斯算法的图像分类案例。

本文分享自华为云社区《[Python图像处理] 二十六.图像分类原理及基于KNN、朴素贝叶斯算法的图像分类案例丨【百变AI秀】》,作者:eastmount 。

图像分类(Image Classification)是对图像内容进行分类的问题,它利用计算机对图像进行定量分析,把图像或图像中的区域划分为若干个类别,以代替人的视觉判断。图像分类的传统方法是特征描述及检测,这类传统方法可能对于一些简单的图像分类是有效的,但由于实际情况非常复杂,传统的分类方法不堪重负。现在,广泛使用机器学习和深度学习的方法来处理图像分类问题,其主要任务是给定一堆输入图片,将其指派到一个已知的混合类别中的某个标签。

在下图中,图像分类模型将获取单个图像,并将为4个标签{cat,dog,hat,mug},分别对应概率{0.6, 0.3, 0.05, 0.05},其中0.6表示图像标签为猫的概率,其余类比。该图像被表示为一个三维数组。在这个例子中,猫的图像宽度为248像素,高度为400像素,并具有红绿蓝三个颜色通道(通常称为RGB)。因此,图像由248×400×3个数字组成或总共297600个数字,每个数字是一个从0(黑色)到255(白色)的整数。图像分类的任务是将这接近30万个数字变成一个单一的标签,如“猫(cat)”。

那么,如何编写一个图像分类的算法呢?又怎么从众多图像中识别出猫呢?这里所采取的方法和教育小孩看图识物类似,给出很多图像数据,让模型不断去学习每个类的特征。在训练之前,首先需要对训练集的图像进行分类标注,如图所示,包括cat、dog、mug和hat四类。在实际工程中,可能有成千上万类别的物体,每个类别都会有上百万张图像。

图像分类是输入一堆图像的像素值数组,然后给它分配一个分类标签,通过训练学习来建立算法模型,接着使用该模型进行图像分类预测,具体流程如下:

  • 输入: 输入包含N个图像的集合,每个图像的标签是K种分类标签中的一种,这个集合称为训练集。
  • 学习: 第二步任务是使用训练集来学习每个类的特征,构建训练分类器或者分类模型。
  • 评价: 通过分类器来预测新输入图像的分类标签,并以此来评价分类器的质量。通过分类器预测的标签和图像真正的分类标签对比,从而评价分类算法的好坏。如果分类器预测的分类标签和图像真正的分类标签一致,表示预测正确,否则预测错误。

常见的分类算法包括朴素贝叶斯分类器、决策树、K最近邻分类算法、支持向量机、神经网络和基于规则的分类算法等,同时还有用于组合单一类方法的集成学习算法,如Bagging和Boosting等。

1.朴素贝叶斯分类算法

朴素贝叶斯分类(Naive Bayes Classifier)发源于古典数学理论,利用Bayes定理来预测一个未知类别的样本属于各个类别的可能性,选择其中可能性最大的一个类别作为该样本的最终类别。在朴素贝叶斯分类模型中,它将为每一个类别的特征向量建立服从正态分布的函数,给定训练数据,算法将会估计每一个类别的向量均值和方差矩阵,然后根据这些进行预测。

朴素贝叶斯分类模型的正式定义如下:

该算法的特点为:如果没有很多数据,该模型会比很多复杂的模型获得更好的性能,因为复杂的模型用了太多假设,以致产生欠拟合。

2.KNN分类算法

K最近邻分类(K-Nearest Neighbor Classifier)算法是一种基于实例的分类方法,是数据挖掘分类技术中最简单常用的方法之一。该算法的核心思想如下:一个样本x与样本集中的k个最相邻的样本中的大多数属于某一个类别yLabel,那么该样本x也属于类别yLabel,并具有这个类别样本的特性。

简而言之,一个样本与数据集中的k个最相邻样本中的大多数的类别相同。由其思想可以看出,KNN是通过测量不同特征值之间的距离进行分类,而且在决策样本类别时,只参考样本周围k个“邻居”样本的所属类别。因此比较适合处理样本集存在较多重叠的场景,主要用于预测分析、文本分类、降维等处理。

该算法在建立训练集时,就要确定训练数据及其对应的类别标签;然后把待分类的测试数据与训练集数据依次进行特征比较,从训练集中挑选出最相近的k个数据,这k个数据中投票最多的分类,即为新样本的类别。KNN分类算法的流程描述为如下图所示。

该算法的特点为:简单有效,但因为需要存储所有的训练集,占用很大内存,速度相对较慢,使用该方法前通常训练集需要进行降维处理。

3.SVM分类算法

支持向量机(Support Vector Machine)是数学家Vapnik等人根据统计学习理论提出的一种新的学习方法,其基本模型定义为特征空间上间隔最大的线性分类器,其学习策略是间隔最大化,最终转换为一个凸二次规划问题的求解。SVM分类算法基于核函数把特征向量映射到高维空间,建立一个线性判别函数,解最优在某种意义上是两类中距离分割面最近的特征向量和分割面的距离最大化。离分割面最近的特征向量被称为“支持向量”,即其它向量不影响分割面。图像分类中的SVM如下图所示,将图像划分为不同类别。

下面的例子可以让读者对SVM快速建立一个认知。给定训练样本,支持向量机建立一个超平面作为决策曲面,使得正例和反例的隔离边界最大化。决策曲面的构建过程如下所示:

  • 在下图中,想象红球和蓝球为球台上的桌球,首先需要找到一条曲线将蓝球和红球分开,于是得到一条黑色的曲线。

  • 为了使黑色曲线离任意的蓝球和红球距离最大化,我们需要找到一条最优的曲线,如下图所示。

  • 假设这些球不是在球桌上,而是抛在空中,但仍然需要将红球和蓝球分开,这时就需要一个曲面,而且该曲面仍然满足所有任意红球和蓝球的间距最大化,如图16-7所示。离这个曲面最近的红色球和蓝色球就被称为“支持向量(Support Vector)”。

该算法的特点为:当数据集比较小的时候,支持向量机的效果非常好。同时,SVM分类算法较好地解决了非线性、高维数、局部极小点等问题,维数大于样本数时仍然有效。

4.随机森林分类算法

随机森林(Random Forest)是用随机的方式建立一个森林,在森林里有很多决策树的组成,并且每一棵决策树之间是没有关联的。当有一个新样本出现的时候,通过森林中的每一棵决策树分别进行判断,看看这个样本属于哪一类,然后用投票的方式,决定哪一类被选择的多,并作为最终的分类结果。

随机森林中的每一个决策树“种植”和“生长”主要包括以下四个步骤:

  • 假设训练集中的样本个数为N,通过有重置的重复多次抽样获取这N个样本,抽样结果将作为生成决策树的训练集;
  • 如果有M个输入变量,每个节点都将随机选择m(m<M)个特定的变量,然后运用这m个变量来确定最佳的分裂点。在决策树的生成过程中,m值是保持不变的;
  • 每棵决策树都最大可能地进行生长而不进行剪枝;
  • 通过对所有的决策树进行加来预测新的数据(在分类时采用多数投票,在回归时采用平均)。

该算法的特点为:在分类和回归分析中都表现良好;对高维数据的处理能力强,可以处理成千上万的输入变量,也是一个非常不错的降维方法;能够输出特征的重要程度,能有效地处理缺省值。

5.神经网络分类算法

神经网络(Neural Network)是对非线性可分数据的分类方法,通常包括输入层、隐藏层和输出层。其中,与输入直接相连的称为隐藏层(Hidden Layer),与输出直接相连的称为输出层(Output Layer)。神经网络算法的特点是有比较多的局部最优值,可通过多次随机设定初始值并运行梯度下降算法获得最优值。图像分类中使用最广泛的是BP神经网络和CNN神经网络。

BP神经网络

BP神经网络是一种多层的前馈神经网络,其主要的特点为:信号是前向传播的,而误差是反向传播的。BP神经网络的过程主要分为两个阶段,第一阶段是信号的前向传播,从输入层经过隐含层,最后到达输出层;第二阶段是误差的反向传播,从输出层到隐含层,最后到输入层,依次调节隐含层到输出层的权重和偏置,输入层到隐含层的权重和偏置,具体结构如下图所示。

神经网络的基本组成单元是神经元。神经元的通用模型如图所示,其中常用的激活函数有阈值函数、Sigmoid函数和双曲正切函数等。

CNN卷积神经网络

卷积神经网络(Convolutional Neural Networks)是一类包含卷积计算且具有深度结构的前馈神经网络,是深度学习的代表算法之一。卷积神经网络的研究始于二十世纪80至90年代,时间延迟网络和LeNet-5是最早出现的卷积神经网络。在二十一世纪后,随着深度学习理论的提出和数值计算设备的改进,卷积神经网络得到了快速发展,并被大量应用于计算机视觉、自然语言处理等领域。

1.KNN算法

K最近邻分类(K-Nearest Neighbor Classifier)算法是一种基于实例的分类方法,是数据挖掘分类技术中最简单常用的方法之一。该算法的核心思想是从训练样本中寻找所有训练样本X中与测试样本距离(欧氏距离)最近的前K个样本(作为相似度),再选择与待分类样本距离最小的K个样本作为X的K个最邻近,并检测这K个样本大部分属于哪一类样本,则认为这个测试样本类别属于这一类样本。

假设现在需要判断下图中的圆形图案属于三角形还是正方形类别,采用KNN算法分析如下:

  • 当K=3时,图中第一个圈包含了三个图形,其中三角形2个,正方形一个,该圆的则分类结果为三角形。
  • 当K=5时,第二个圈中包含了5个图形,三角形2个,正方形3个,则以3:2的投票结果预测圆为正方形类标。设置不同的K值,可能预测得到不同的结果。

简而言之,一个样本与数据集中的k个最相邻样本中的大多数的类别相同。由其思想可以看出,KNN是通过测量不同特征值之间的距离进行分类,而且在决策样本类别时,只参考样本周围k个“邻居”样本的所属类别。因此比较适合处理样本集存在较多重叠的场景,主要用于预测分析、文本分类、降维等处理。

KNN在Sklearn机器学习包中,实现的类是neighbors.KNeighborsClassifier,简称KNN算法。构造方法为:

KNeighborsClassifier(algorithm='ball_tree',
leaf_size=30,
metric='minkowski',
metric_params=None,
n_jobs=1,
n_neighbors=3,
p=2,
weights='uniform')

KNeighborsClassifier可以设置3种算法:brute、kd_tree、ball_tree,设置K值参数为n_neighbors=3。调用方法如下:

  • from sklearn.neighbors import KNeighborsClassifier
  • knn = KNeighborsClassifier(n_neighbors=3, algorithm=“ball_tree”)

它包括两个步骤:

  • 训练:nbrs.fit(data, target)
  • 预测:pre = clf.predict(data)

2.数据集

该部分主要使用Scikit-Learn包进行Python图像分类处理。Scikit-Learn扩展包是用于Python数据挖掘和数据分析的经典、实用扩展包,通常缩写为Sklearn。Scikit-Learn中的机器学习模型是非常丰富的,包括线性回归、决策树、SVM、KMeans、KNN、PCA等等,用户可以根据具体分析问题的类型选择该扩展包的合适模型,从而进行数据分析,其安装过程主要通过“pip install scikit-learn”实现。

实验所采用的数据集为Sort_1000pics数据集,该数据集包含了1000张图片,总共分为10大类,分别是人(第0类)、沙滩(第1类)、建筑(第2类)、大卡车(第3类)、恐龙(第4类)、大象(第5类)、花朵(第6类)、马(第7类)、山峰(第8类)和食品(第9类),每类100张。如图所示。

接着将所有各类图像按照对应的类标划分至“0”至“9”命名的文件夹中,如图所示,每个文件夹中均包含了100张图像,对应同一类别。

比如,文件夹名称为“6”中包含了100张花的图像,如下图所示。

3.KNN图像分类

下面是调用KNN算法进行图像分类的完整代码,它将1000张图像按照训练集为70%,测试集为30%的比例随机划分,再获取每张图像的像素直方图,根据像素的特征分布情况进行图像分类分析。KNeighborsClassifier()核心代码如下:

  • from sklearn.neighbors import KNeighborsClassifier
  • clf = KNeighborsClassifier(n_neighbors=11).fit(XX_train, y_train)
  • predictions_labels = clf.predict(XX_test)

完整代码及注释如下:

# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
#----------------------------------------------------------------------------------

第一步 切分训练集和测试集

#----------------------------------------------------------------------------------
X = [] #定义图像名称
Y = [] #定义图像分类类标
Z = [] #定义图像像素
for i in range(0, 10):
#遍历文件夹,读取图片
for f in os.listdir("photo/%s" % i):
#获取图像名称
X.append("photo//" +str(i) + "//" + str(f))
#获取图像类标即为文件夹名称
Y.append(i)
X = np.array(X)
Y = np.array(Y)
#随机率为100% 选取其中的30%作为测试集
X_train, X_test, y_train, y_test = train_test_split(X, Y,
test_size=0.3, random_state=1)
print len(X_train), len(X_test), len(y_train), len(y_test)
#----------------------------------------------------------------------------------

第二步 图像读取及转换为像素直方图

#----------------------------------------------------------------------------------
#训练集
XX_train = []
for i in X_train:
#读取图像
#print i
image = cv2.imread(i)
#图像像素大小一致
img = cv2.resize(image, (256,256),
interpolation=cv2.INTER_CUBIC)
#计算图像直方图并存储至X数组
hist = cv2.calcHist([img], [0,1], None,
[256,256], [0.0,255.0,0.0,255.0])
XX_train.append(((hist/255).flatten()))
#测试集
XX_test = []
for i in X_test:
#读取图像
#print i
image = cv2.imread(i)
#图像像素大小一致
img = cv2.resize(image, (256,256),
interpolation=cv2.INTER_CUBIC)
#计算图像直方图并存储至X数组
hist = cv2.calcHist([img], [0,1], None,
[256,256], [0.0,255.0,0.0,255.0])
XX_test.append(((hist/255).flatten()))
#----------------------------------------------------------------------------------

第三步 基于KNN的图像分类处理

#----------------------------------------------------------------------------------
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=11).fit(XX_train, y_train)
predictions_labels = clf.predict(XX_test)
print u'预测结果:'
print predictions_labels
print u'算法评价:'
print (classification_report(y_test, predictions_labels))
#输出前10张图片及预测结果
k = 0
while k<10:
#读取图像
print X_test[k]
image = cv2.imread(X_test[k])
print predictions_labels[k]
#显示图像
cv2.imshow("img", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
k = k + 1

代码中对预测集的前十张图像进行了显示,其中“818.jpg”图像如图所示,其分类预测的类标结果为“8”,表示第8类山峰,预测结果正确。

下图展示了“452.jpg”图像,其分类预测的类标结果为“4”,表示第4类恐龙,预测结果正确。

下图展示了“507.jpg”图像,其分类预测的类标结果为“7”,错误地预测为第7类恐龙,其真实结果应该是第5类大象。

使用KNN算法进行图像分类实验,最后算法评价的准确率(Precision)、召回率(Recall)和F值(F1-score)如图所示,其中平均准确率为0.64,平均召回率为0.55,平均F值为0.50,其结果不是非常理想。那么,如果采用CNN卷积神经网络进行分类,通过不断学习细节是否能提高准确度呢?

下面是调用朴素贝叶斯算法进行图像分类的完整代码,调用sklearn.naive_bayes中的BernoulliNB()函数进行实验。它将1000张图像按照训练集为70%,测试集为30%的比例随机划分,再获取每张图像的像素直方图,根据像素的特征分布情况进行图像分类分析。

# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
#----------------------------------------------------------------------------------

第一步 切分训练集和测试集

#----------------------------------------------------------------------------------
X = [] #定义图像名称
Y = [] #定义图像分类类标
Z = [] #定义图像像素
for i in range(0, 10):
#遍历文件夹,读取图片
for f in os.listdir("photo/%s" % i):
#获取图像名称
X.append("photo//" +str(i) + "//" + str(f))
#获取图像类标即为文件夹名称
Y.append(i)
X = np.array(X)
Y = np.array(Y)
#随机率为100% 选取其中的30%作为测试集
X_train, X_test, y_train, y_test = train_test_split(X, Y,
test_size=0.3, random_state=1)
print len(X_train), len(X_test), len(y_train), len(y_test)
#----------------------------------------------------------------------------------

第二步 图像读取及转换为像素直方图

#----------------------------------------------------------------------------------
#训练集
XX_train = []
for i in X_train:
#读取图像
#print i
image = cv2.imread(i)
#图像像素大小一致
img = cv2.resize(image, (256,256),
interpolation=cv2.INTER_CUBIC)
#计算图像直方图并存储至X数组
hist = cv2.calcHist([img], [0,1], None,
[256,256], [0.0,255.0,0.0,255.0])
XX_train.append(((hist/255).flatten()))
#测试集
XX_test = []
for i in X_test:
#读取图像
#print i
image = cv2.imread(i)
#图像像素大小一致
img = cv2.resize(image, (256,256),
interpolation=cv2.INTER_CUBIC)
#计算图像直方图并存储至X数组
hist = cv2.calcHist([img], [0,1], None,
[256,256], [0.0,255.0,0.0,255.0])
XX_test.append(((hist/255).flatten()))
#----------------------------------------------------------------------------------

第三步 基于朴素贝叶斯的图像分类处理

#----------------------------------------------------------------------------------
from sklearn.naive_bayes import BernoulliNB
clf = BernoulliNB().fit(XX_train, y_train)
predictions_labels = clf.predict(XX_test)
print u'预测结果:'
print predictions_labels
print u'算法评价:'
print (classification_report(y_test, predictions_labels))
#输出前10张图片及预测结果
k = 0
while k<10:
#读取图像
print X_test[k]
image = cv2.imread(X_test[k])
print predictions_labels[k]
#显示图像
cv2.imshow("img", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
k = k + 1

代码中对预测集的前十张图像进行了显示,其中“368.jpg”图像如下图所示,其分类预测的类标结果为“3”,表示第3类大卡车,预测结果正确。

下图展示了“452.jpg”图像,其分类预测的类标结果为“4”,表示第4类恐龙,预测结果正确。

使用朴素贝叶斯算法进行图像分类实验,最后预测的结果及算法评价准确率(Precision)、召回率(Recall)和F值(F1-score)如图所示。

本篇文章主要讲解Python环境下的图像分类算法,首先普及了常见的分类算法,包括朴素贝叶斯、KNN、SVM、随机森林、神经网络等,接着通过朴素贝叶斯和KNN分别实现了1000张图像的图像分类实验,希望对读者有一定帮助,也希望这些知识点为读者从事Python图像处理相关项目实践或科学研究提供一定基础。

[1]冈萨雷斯著. 数字图像处理(第3版)[M]. 北京:电子工业出版社,2013.
[2]杨秀璋, 颜娜. Python网络数据爬取及分析从入门到精通(分析篇)[M]. 北京:北京航天航空大学出版社, 2018.
[3]gzq0723. 干货——图像分类(上)[EB/OL]. (2018-08-28). https://blog.csdn.net/gzq0723/
[4]article/details/82185832.
[5]sinat_34430765. OpenCV分类器学习心得[EB/OL]. (2016-08-03). https://blog.csdn.net/sinat_34430765/article/details/52103189.
[6]baidu_28342107. 机器学习之贝叶斯算法图像分类[EB/OL]. (2018-10-10). https://blog.csdn.net/baidu_28342107/article/details/82999249.
[7]baidu_28342107. 机器学习之KNN算法实现图像分类[EB/OL]. (2018-09-28). https://blog.csdn.net/baidu_28342107/article/details/82870436.
[8]normol. svm实现图片分类(python)[EB/OL]. (2018-11-19). https://blog.csdn.net/normol/article/details/84230890.
[9]wfjiang. SVM-支持向量机原理详解与实践[EB/OL]. (2017-03-14). https://www.cnblogs.com/spoorer/p/6551220.html.
[10]快乐的小飞熊. 随机森林原理[EB/OL]. (2017-03-05). https://www.jianshu.com/p/57e862d695f2.
[11]烨枫_邱. 深入理解BP神经网络[EB/OL]. (2018-06-01). https://www.jianshu.com/p/6ab6f53874f7.
[12]smilejiasmile. 卷积神经网络(CNN)及其实践[EB/OL]. (2018-06-20). https://blog.csdn.net/smilejiasmile/article/details/80752889.
[13] 誓天断发. 机器学习之BP神经网络算法实现图像分类[EB/OL]. (2018-10-23). https://blog.csdn.net/baidu_28342107/article/details/83307633.
[14] 杨秀璋. [Python人工智能] 十.Tensorflow+Opencv实现CNN自定义图像分类案例及与机器学习KNN图像分类算法对比[EB/OL]. https://blog.csdn.net/Eastmount/article/details/103757386.

点击关注,第一时间了解华为云新鲜技术~