本文主要讲解“PythonOpenCV与机器学习碰撞的实例分析”。感兴趣的朋友不妨看看。本文介绍的方法简单、快速、实用。让边肖带你学习“一个例子分析PythonOpenCV与机器学习的碰撞”!
0. 前言
机器学习是人工智能的一个子集,它为计算机和其他具有计算能力的系统提供自动预测或决策的能力。虚拟助手、车牌识别系统、智能推荐系统等机器学习应用给我们的日常生活带来了便捷的体验。机器学习的蓬勃发展得益于以下三个关键因素:1)海量数据集;2)算法的快速发展;3)计算机硬件的发展。在本文中,我们将学习OpenCV提供的常用机器学习算法和技术,用于解决计算机视觉项目中的实际问题,如分类和回归问题。
1. 机器学习简介
机器学习是利用计算机编程从历史数据中学习来预测新数据的过程。机器学习可以分为三类:——监督学习、无监督学习和半监督学习。这些技术中包含的算法如下图所示:
监督学习中使用的00-1010个样本具有相应的预期输出值(或样本标签)。因为我们知道每个训练数据的正确标签,所以监督学习可以根据预测和相应的预期输出之间的差异来校正这些预测。基于这些校准,算法可以从误差中学习来调整其内部参数,以拟合最近样本集和相应预期输出之间的函数。
监督学习问题可以进一步分为以下几类和回归:
分类:当输出变量是一个类别时,问题可以认为是一个分类问题。在分类问题中,算法将输入映射到输出标签。
回归:当输出变量为实数时,在回归问题中,算法将输入映射为连续的实数输出。
在监督学习中,需要考虑以下问题:
偏差-方差权衡:数据欠拟合的模型偏差较大,数据过拟合的模型方差较大;
偏差是学习算法中错误假设导致的误差,可以定义为模型预测值与预期正确值的差值。偏差较大的模型无法找到数据中的所有模式(欠拟合),因此不能很好地拟合训练集,也不能很好地拟合测试集。
方差被定义为算法学习错误事物的倾向,它将同时拟合数据中的真实信号和噪声。因此,具有高方差(过拟合)的模型非常适合训练集,但它不能推广到测试集,因为它学习数据中的噪声。
模型复杂度和训练数据量:模型复杂度是指机器学习算法试图达到的复杂度。模型的复杂度通常由训练数据决定:比如用少量数据训练模型,复杂度低的模型更可取,因为复杂度高的模型会导致过拟合。
输入空间的维度:在处理高维空间数据时,学习可能会非常困难,因为会有很多额外的特征会混淆学习过程,也就是所谓的维度灾难。因此,在处理高维空间数据时,常用的方法是修改学习算法,使其具有高偏差和低方差。
00-1010在无监督学习中,样本集缺少每个样本对应的输出值(样本集没有被标记、分类或归类)。无监督学习的目标是建模和推断样本集中的结构或分布。因此,在无监督学习中,该算法使用数据进行推理,并试图揭示隐藏的分布信息。聚类和降维是无监督学习中最常用的两种算法。
00-1010半监督学习可以看作是监督学习和非监督学习的折中,因为它同时使用有标签和无标签的数据进行训练。很多现实世界的机器学习问题都可以归为半监督问题,因为正确标记所有数据可能非常困难或耗时,而未标记的数据更容易收集。
1.1 监督学习
OpenCV提供cv2.kmeans()函数实现K-Means聚类算法,找到聚类中心,围绕聚类对输入样本进行分组。
K-Means聚类算法的目标是将n个样本划分(聚类)成K个聚类,其中每个样本都属于均值最近的聚类。cv2.kmeans()函数的用法如下:
Retval,bestlabels,centers=cv。kmeans (data,k,bestlabels,criteria,try,flags [,centers])数据表示用于聚类的输入数据,它是np.float32数据类型,每一列都包含一个特征。
;K 指定最后需要的簇数;算法终止标准由 criteria 参数指定,该参数设置最大迭代次数或所需精度,当满足这些标准时,算法终止。criteria 是具有三个参数 (type, max_item, epsilon) 的元组:
criteria 参数的标准示例如下:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
上述语句表示,最大迭代次数设置为 20 (max_iterm = 20),所需精度为 1.0 (epsilon = 1.0)。
attempts
参数指定使用不同的初始标签执行算法的次数。flags
参数指定初始化簇中心的方法,其可选值包括:cv2.KMEANS_RANDOM_CENTERS
每次选择随机初始化簇中心;cv2.KMEANS_PP_CENTERS
使用 Arthur 等人提出的 K-Means++
中心初始化。
cv2.kmeans()
返回以下内容:
返回值 | 解释 |
---|---|
bestLabels | 整数数组,用于存储每个样本的簇索引 |
center | 包含每个簇中心的数组 |
compactness | 每个点到其簇中心的距离平方和 |
2.1 K-Means 聚类示例
作为示例,我们将使用 K-Means 聚类算法对一组 2D 点进行聚类。这组 2D 点由 240 个点组成,使用两个特征进行了描述:
# 2D数据 data = np.float32(np.vstack((np.random.randint(0, 50, (80, 2)), np.random.randint(40, 90, (80, 2)), np.random.randint(70, 110, (80, 2))))) # 可视化 plt.scatter(data[:, 0], data[:, 1], c='c') plt.show()
如上图所示,数据将作为聚类算法的输入,每个数据点有两个特征对应于 (x, y) 坐标,例如,这些坐标可以表示 240 人人的身高和体重,而 K-Means 聚类算法用于决定衣服的尺寸(例如 K=3,则相应表示尺寸为 S、M 或 L)。
接下来,我们将数据划分为 2 个簇。第一步是定义算法终止标准,将最大迭代次数设置为 20 (max_iterm = 20),epsilon 设置为 1.0 (epsilon = 1.0):
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
然后调用 cv2.kmeans() 函数应用 K-Means 算法:
ret, label, center = cv2.kmeans(data, 2, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
由于返回值 label 存储每个样本的聚类索引,因此,我们可以根据 label 将数据拆分为不同的集群:
A = data[label.ravel() == 0] B = data[label.ravel() == 1]
最后绘制 A 和 B 以及聚类前后的数据,以便更好地理解聚类过程:
fig = plt.figure(figsize=(12, 6)) plt.suptitle("K-means clustering algorithm", fontsize=14, fontweight='bold') # 绘制原始数据 ax = plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], c='c') plt.title("data") # 绘制聚类后的数据和簇中心 ax = plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], c='b') plt.scatter(B[:, 0], B[:, 1], c='g') plt.scatter(center[:, 0], center[:, 1], s=100, c='m', marker='s') plt.title("clustered data and centroids (K = 2)") plt.show()
接下来,我们修改参数 K 进行聚类并进行相应的可视化。例如需要将数据分为三个簇,则首先应用相同的过程对数据进行聚类,只需要修改参数 (K=3) 将数据分为 3 个簇:
ret, label, center = cv2.kmeans(data, 3, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
然后,当使用标签输出分离数据时,将数据分为三组:
A = data[label.ravel() == 0] B = data[label.ravel() == 1] C = data[label.ravel() == 2]
最后一步是显示 A 、 B 和 C ,以及簇中心和训练数据:
fig = plt.figure(figsize=(12, 6)) plt.suptitle("K-means clustering algorithm", fontsize=14, fontweight='bold') # 绘制原始数据 ax = plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], c='c') plt.title("data") # 绘制聚类后的数据和簇中心 ax = plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], c='b') plt.scatter(B[:, 0], B[:, 1], c='g') plt.scatter(C[:, 0], C[:, 1], c='r') plt.scatter(center[:, 0], center[:, 1], s=100, c='m', marker='s') plt.title("clustered data and centroids (K = 3)") plt.show()
我们也可以将簇数设置为 4,观察算法运行结果:
ret, label, center = cv2.kmeans(data, 4, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
3. K最近邻
k-最近邻 (k-nearest neighbours, kNN) 是监督学习中最简单的算法之一,kNN 可用于分类和回归问题。在训练阶段,kNN 存储所有训练样本的特征向量和类别标签。在测试阶段,将未标记的向量分类为距离最近的 k 个训练样本中出现频率最高的类标签,其中 k 是用户定义的常数:
如上图所示,如果 k = 5,则绿色圆圈(未标记的测试样本)将被归类为三角形,因为离其最近的 5 个样本中有 3 个三角形但只有 1 个菱形;如果 k = 9,则绿色圆圈将被归类为菱形,因为离其最近的 9 个样本中有 5 个菱形但只有 4 个三角形。
在 OpenCV 中,使用 kNN 分类器首先需要使用 cv2.ml.KNearest_create() 创建 kNN 分类器,然后提供数据和标签以使用 train() 方法训练 kNN分类器。最后,使用 findNearest() 方法用于查找测试样本邻居,使用如下:
retval, results, neighborResponses, dist=cv2.ml_KNearest.findNearest(samples, k[, results[, neighborResponses[, dist]]])
其中,samples 是输入样本,k 设置为最近邻居的个数,results 存储每个输入样本的预测值,neighborResponses 存储对应的邻居,dist 存储输入样本到相应邻居的距离。
3.1 K最近邻示例
接下来,为了演示 kNN 算法,首先随机创建一组点并分配一个标签 (0 或 1)。标签 0 将代表红色三角形,而标签 1 将代表蓝色方块;然后,使用 kNN 算法根据 k 个最近邻对样本点进行分类。
第一步是创建具有相应标签的点集和要分类的样本点:
# 点集由50个点组成 data = np.random.randint(0, 100, (50, 2)).astype(np.float32) # 为1每个点创建标签 (0:红色, 1:蓝色) labels = np.random.randint(0, 2, (50, 1)).astype(np.float32) # 创建要分类的样本点 sample = np.random.randint(0, 100, (1, 2)).astype(np.float32)
接下来,创建 kNN 分类器,训练分类器,并找到要分类样本点的 k 个最近邻居:
# 创建 kNN 分类器 knn = cv2.ml.KNearest_create() # 训练 kNN 分类器 knn.train(data, cv2.ml.ROW_SAMPLE, labels) # 找到要分类样本点的 k 个最近邻居 k = 3 ret, results, neighbours, dist = knn.findNearest(sample, k) # 打印结果 print("result: {}".format(results)) print("neighbours: {}".format(neighbours)) print("distance: {}".format(dist)) # 可视化 fig = plt.figure(figsize=(8, 6)) red_triangles = data[labels.ravel() == 0] plt.scatter(red_triangles[:, 0], red_triangles[:, 1], 200, 'r', '^') blue_squares = data[labels.ravel() == 1] plt.scatter(blue_squares[:, 0], blue_squares[:, 1], 200, 'b', 's') plt.scatter(sample[:, 0], sample[:, 1], 200, 'g', 'o') plt.show()
获得结果如下所示:
result: [[0.]]
neighbours: [[0. 0. 1.]]
distance: [[13. 40. 65.]]
因此,绿点被归类为红色三角形,可视化效果如下所示:
4. 支持向量机
支持向量机 (Support Vector Machine, SVM) 是一种监督学习技术,它通过根据指定的类对训练数据进行最佳分离,从而在高维空间中构建一个或一组超平面。
已二维平面为例,在下图中看到,其中绿线是能够将两个类分开的最佳超平面,因为其到两个类中的最近元素的距离是最大的:
上图第一种情况下,决策边界是一条线,而在第二种情况下,决策边界是一条圆形曲线,虚线代表其他决策边界,但它们并非最好地将两个类分开的决策边界。
OpenCV 中的 SVM 实现基于 LIBSVM,使用 cv2.ml.SVM_create() 函数创建空模型,然后为模型分配主要参数:
svmType :设置 SVM 类型,可选值如下:
-
SVM_C_SVC:C CC-支持向量分类,可用于 n 分类 (n≥2) 问题
-
NU_SVC: v vv-支持向量分类
-
ONE_CLASS: 分布估计(单类 SVM)
-
EPS_SVR: ϵ \epsilonϵ-支持向量回归
-
NU_SVR: v vv-支持向量回归
kernelType :这设置了 SVM 的核类型,可选值如下:
-
LINEAR : 线性核
-
POLY :多项式核
-
RBF : Radial Basis Function (RBF),大多数情况下是不错的选择
-
SIGMOID : Sigmoid 核
-
CHI2 : 指数 Chi2 核,类似于 RBF 核
-
INTER : 直方图交集核;运行速度较快的核
degree : 核函数的 degree 参数 (用于 POLY 核)
gamma :核函数的 γ \gammaγ 参数(用于 POLY/RBF/SIGMOID/CHI2 核)
coef0 : 核函数的 coef0 参数 (用于 POLY/SIGMOID 核)
Cvalue : SVM 优化问题的 C 参数 (用于 C_SVC/EPS_SVR/NU_SVR 类型)
nu : SVM 优化问题的 v vv 参数 (用于 NU_SVC/ONE_CLASS/NU_SVR 类型)
p : SVM 优化问题的 ϵ \epsilonϵ 参数 (用于 EPS_SVR 类型)
classWeights : C_SVC 问题中的可选权重,分配给特定的类
termCrit :迭代 SVM 训练过程的终止标准
核函数选择通常取决于数据集,通常可以首先使用 RBF 核进行测试,因为该核将样本非线性地映射到更高维空间,可以方便的处理类标签和属性之间的关系是非线性的情况。
默认构造函数使用以下值初始化 SVM:
svmType: C_SVC, kernelType: RBF, degree: 0, gamma: 1, coef0: 0, C: 1, nu: 0, p: 0, classWeights: 0, termCrit: TermCriteria(MAX_ITER+EPS, 1000, FLT_EPSILON )
4.1 支持向量机示例
为了解如何在 OpenCV 中使用 SVM,首先需要创建训练数据和标签:
labels = np.array([1, 1, -1, -1, -1]) data = np.matrix([[800, 40], [850, 400], [500, 10], [550, 300], [450, 600]], dtype=np.float32)
以上代码创建了 5 个点,前 2 个点被指定为 1 类,而另外 3 个被指定为 -1 类。接下来使用 svm_init() 函数初始化 SVM 模型:
def svm_init(C=12.5, gamma=0.50625): """ 创建 SVM 模型并为其分配主要参数,返回模型 """ model = cv2.ml.SVM_create() model.setGamma(gamma) model.setC(C) model.setKernel(cv2.ml.SVM_LINEAR) model.setType(cv2.ml.SVM_C_SVC) model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6)) return model # 初始化 SVM 模型 svm_model = svm_init(C=12.5, gamma=0.50625)
创建的 SVM 核类型设置为 LINEAR,SVM 的类型设置为 C_SVC。
然后,编写 svm_train() 函数训练 SVM 模型:
def svm_train(model, samples, responses): # 使用 samples 和 responses 训练模型 model.train(samples, cv2.ml.ROW_SAMPLE, responses) return model # 训练 SVM svm_train(svm_model, data, labels)
然后创建一个图像,并绘制 SVM 响应:
def show_svm_response(model, image): colors = {1: (255, 255, 0), -1: (0, 255, 255)} for i in range(image.shape[0]): for j in range(image.shape[1]): sample = np.matrix([[j, i]], dtype=np.float32) response = svm_predict(model, sample) image[i, j] = colors[response.item(0)] cv2.circle(image, (800, 40), 10, (255, 0, 0), -1) cv2.circle(image, (850, 400), 10, (255, 0, 0), -1) cv2.circle(image, (500, 10), 10, (0, 255, 0), -1) cv2.circle(image, (550, 300), 10, (0, 255, 0), -1) cv2.circle(image, (450, 600), 10, (0, 255, 0), -1) support_vectors = model.getUncompressedSupportVectors() for i in range(support_vectors.shape[0]): cv2.circle(image, (support_vectors[i, 0], support_vectors[i, 1]), 15, (0, 0, 255), 6) # 创建图像 img_output = np.zeros((640, 1200, 3), dtype="uint8") # 显示 SVM 响应 show_svm_response(svm_model, img_output)
如上图所示,SVM 使用训练数据进行了训练,可用于对图像中所有点进行分类。SVM 将图像划分为黄色和青色区域,可以看到两个区域之间的边界对应于两个类之间的最佳间隔,因为到两个类中最近元素的距离最大,支持向量用红线边框显示。
到此,相信大家对“Python OpenCV与机器学习的碰撞举例分析”有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/134019.html