3382 字
17 分钟
OpenCV:机器学习

1. 机器学习#

1.1 介绍#

机器学习算法从数据中产生模型,在面对新的情况时,模型就会为我们提供预测结果

1.2 训练#

从数据中学得模型的过程称为 学习 (learning)或者 训练 (training)。在训练过程中所使用的数据称为 训练数据 ,其中的每个样本称为训练样本训练样本所组成的集合称为 训练集

1.3 标签#

​ 如果希望获取一个模型,除了有数据,还需要给样本贴上对应的标签(label),通常将拥有了标签的样本称为样例

1.4 测试#

​ 学得模型后,为了测试模型的效果,还要对其进行测试被测试的样本称为 测试样本

输入测试样本时,并不提供测试样本的目标类别,而是由模型决定样本属于哪个类别。

1.5 计算精确度#

比较测试样本预测的标签与实际样本标签之间的差别,就可以计算出模型的精确度

2. K近邻算法#

2.1 本质#

对指定对象根据已知特征值分类。比如,看到一对父子,一般情况下,通过判断他们的年龄,就能够马上分辨出哪位是父亲,哪位是儿子,这是通过年龄属性的特征值来划分对象的。

​ 为了提高算法的可靠性在实施时会取k个近邻点,这k个点中属于哪一类的较多,然后将当前待识别点划分为哪一类。为了方便判断, k值通常取奇数

2.2 基本思想(举例说明)#

​ 已知某知名双胞胎艺人AB长得很像,如果要判断一张图像T上的人物到底是A还是B, 采用K近邻算法实现的具体步骤如下:

  1. 收集数据:收集艺人A艺人B的照片各100张。
  2. 选取特征确定几个用来识别人物的重要特征使用这些特征来标注A和B的照片
    • 例如,选取4个特征,每张照片可以表示为 [156, 34, 890, 457] 这样的形式。
    • 这样,可以获得A的100张照片的数据集FAB的100张照片的数据集FB。每个数据集集合中各有 100 个这样的特征值。
  3. 计算特征计算待识别图像T的特征使用特征值表示图像T
    • 例如,图像T的特征值可能为 [257, 896, 236, 639]
  4. 计算距离计算图像T的特征值与FA、FB中各特征值之间的距离
    • 曼哈顿距离:用绝对值之和表示距离。
    • 欧氏距离:用平方和的平方根表示距离。
  5. 决策寻找产生其中 k个最短距离 的样本点,统计k个样本点中属于FA和FB的样本点个数,属于哪个数据集的样本点多,就将T确定为哪个艺人的图像
    • 例如,找到11个最近的点,在这11个点中,属于FA的样本点有7个,属于FB的样本点有4个,那么就确定这张图像T上的艺人为 A;反之,如果这11个点中,有6个样本点属于FB,有5个样本点属于FA,那么就确定这张图像T上的艺人为B

2.3 使用#

  • 通过函数cv2.ml.KNearest_create()用来创建K近邻算法对象
  • 通过函数obj.train()用来训练K近邻算法数据
    • 函数原型obj.train( trainData, cv2.ml.ROW_SAMPLE, trainLable )
    • 参数说明
      • trainData训练数据
      • cv2.ml.ROW_SAMPLE每一行是一个样本
      • trainLable训练数据标签
  • 通过函数obj.findNearest()用来推理K近邻算法数据
    • 函数原型ret, results, neighbours, dist = obj.findNearest( testData, K )
    • 参数说明
      • testData测试数据
      • K数量,通常为奇数,表示寻找K个近邻
      • results数据点的类别
      • neighboursK近邻数据点的类别
      • dist与K近邻数据点的距离

2.4 示例#

import cv2
import numpy as np
import matplotlib.pyplot as plt
######### 数据量 #########
num_train = 20
num_test = 4
######### 准备训练数据 #########
# 制作训练数据
rand1 = np.random.randint(0, 30, (num_train, 2)).astype(np.float32) # rand1数据位于(0, 30)
rand2 = np.random.randint(70, 100, (num_train, 2)).astype(np.float32) # rand2数据位于(70, 100)
trainData = np.vstack((rand1, rand2)) # 将rand1和rand2拼接为训练数据
# 制作标签
r1Label = np.zeros((num_train, 1)).astype(np.float32) # r1Label对应rand1的标签,类型0
r2Label = np.ones((num_train, 1)).astype(np.float32) # r2Label对应rand2的标签,类型1
trainLable = np.vstack((r1Label, r2Label))
# 显示数据
# 绿色:数据类别0
# 蓝色:数据类别1
g = trainData[trainLable.ravel() == 0]
plt.scatter(g[:,0], g[:,1], 80, 'g', 'o')
b = trainData[trainLable.ravel() == 1]
plt.scatter(b[:,0], b[:,1], 80, 'b', 's')
######### 训练模型 #########
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, trainLable)
######### 准备测试数据 #########
# 制作测试数据
test = np.random.randint(40, 60, (num_test, 2)).astype(np.float32) # test数据位于(0, 100)
# 显示数据
# 红色:测试
plt.scatter(test[:, 0], test[:, 1], 80, 'r', '*')
######### 推理 #########
# 推理:使用 K 近邻算法分类
K = 7
ret, results, neighbours, dist = knn.findNearest(test, K)
# 推理结果
for n in range(num_test):
print("第%s个点的类别为:" % (n+1), results[n][0])
print("距离该点最近的%s个邻居的类别分别为:" % K, neighbours[n])
print("距离该点最近的%s个邻居的距离分别为:" % K, dist[n])
plt.show()
第1个点的类别为: 1.0
距离该点最近的7个邻居的类别分别为: [1. 1. 1. 1. 1. 1. 1.]
距离该点最近的7个邻居的距离分别为: [ 953. 954. 1201. 1280. 1445. 1549. 1585.]
第2个点的类别为: 1.0
距离该点最近的7个邻居的类别分别为: [1. 1. 0. 1. 0. 1. 0.]
距离该点最近的7个邻居的距离分别为: [1154. 1213. 1460. 1513. 1640. 1696. 1768.]
第3个点的类别为: 0.0
距离该点最近的7个邻居的类别分别为: [0. 0. 0. 0. 0. 0. 0.]
距离该点最近的7个邻居的距离分别为: [ 841. 965. 1069. 1073. 1096. 1252. 1341.]
第4个点的类别为: 0.0
距离该点最近的7个邻居的类别分别为: [1. 1. 0. 1. 0. 0. 0.]
距离该点最近的7个邻居的距离分别为: [1225. 1300. 1429. 1586. 1657. 1805. 1825.]

K近邻算法

3. K均值聚类#

3.1 介绍#

​ 机器学习模型可以将训练集中的数据划分为若干个组,每个组被称为一个,这种学习方式被称为聚类。它的重要特点是在学习过程中不需要用标签对训练样本进行标注,也就是说,学习过程能够根据现有训练集自动完成聚类

根据训练数据是否有标签,我们可以将学习划分为 有监督学习无监督学习K近邻算法、支持向量机都是有监督学习,提供有标签的数据给算法学习,然后对数据分类聚类无监督学习事先并不知道分类标签是什么,直接对数据分类

​ 聚类能够将具有相似属性的对象划分到同一个簇中。聚类方法能够应用于所有对象,簇内的对象越相似,聚类算法的效果越好

3.2 基本步骤#

K均值聚类一种将输入数据划分为k个簇的聚类算法。这个算法不断提取当前分类的中心点,并最终在分类稳定时完成聚类。因此,K均值聚类本质上是一种迭代算法

K均值聚类算法的基本步骤如下:

  1. 随机选取k个点作为分类的中心点
    • 可以是原始数据中的点,也可以是原始数据中不存在的数据点
  2. 将每个数据点放到距离它最近的中心点所在的类中
    • 具体计算距离时,可以根据需要采用不同形式的距离度量方法
  3. 重新计算各个分类的数据点的平均值,将平均值作为新的分类中心点
  4. 重复步骤2和步骤3,直到分类稳定

3.3 使用#

  • 通过函数cv2.kmeans()用来实现K均值聚类
  • 函数原型val, bestLabels, centers=cv2.kmeans( data, K, bestLabels, criteria, attempts, flags )
  • 参数说明
    • data输入的待处理数据集合
    • K分类的数目。
    • bestLabels计算之后各个数据点的最终分类标签。实际调用时,默认设置为None
    • criteria算法迭代的终止条件。当达到最大循环数目或者指定的精度阈值时,算法停止迭代。参数由3个子参数构成,分别为typemax_itereps
      • type:表示终止的类型
        • cv2.TERM_CRITERIA_EPS精度满足eps时, 停止迭代
        • cv2.TERM_CRITERIA_MAX_ITER迭代次数超过阈值max_iter时,停止迭代
        • cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER上述两个条件中的任意一个满足时,停止迭代
      • max_iter最大迭代次数
      • eps精确度的阈值
    • attempts:让算法使用不同的初始值进行attempts次的尝试。
    • flags选择初始中心点的方法。主要有以下 3 种:
      • cv2.KMEANS_RANDOM_CENTERS随机选取中心点。
      • cv2.KMEANS_PP_CENTERS基于中心化算法选取中心点。
      • cv2.KMEANS_USE_INITIAL_LABELS使用用户输入的数据作为第一次分类中心点。
    • val距离值,返回每个点到相应中心点距离的平方和
    • centers每个分类的中心点数据

3.4 示例#

一维数据#

import cv2
import numpy as np
import matplotlib.pyplot as plt
######### 数据量 #########
Amount = 100
Group = 2
######### 准备数据 #########
rand1 = np.random.randint(0, 40, Amount).astype(np.float32) # rand1数据位于(0, 50)
rand2 = np.random.randint(50, 100, Amount).astype(np.float32) # rand2数据位于(50, 100)
data = np.hstack((rand1, rand2)) # 拼接数据
####### 实施K均值聚类 #######
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
retval, labels, centers = cv2.kmeans(data, Group, None, criteria, 10, flags)
# print("retval:", retval)
# print("labels:", labels)
print("centers:", centers)
####### 获取分类结果 #######
label0 = data[labels.ravel() == 0]
label1 = data[labels.ravel() == 1]
######### 显示数据 #########
plt.plot(label0, 'go')
plt.plot(label1, 'bs')
plt.scatter(0, centers[0], 200, 'r', '*')
plt.scatter(0, centers[1], 200, 'r', '*')
plt.show()
centers: [[22.16],
[75.29]]

K均值聚类_1D

二维数据#

import cv2
import numpy as np
import matplotlib.pyplot as plt
######### 数据量 #########
Amount = 100
Group = 2
######### 准备数据 #########
rand1 = np.random.randint(0, 50, (Amount, 2)).astype(np.float32) # rand1数据位于(0, 50)
rand2 = np.random.randint(50, 100, (Amount, 2)).astype(np.float32) # rand2数据位于(50, 100)
data = np.vstack((rand1, rand2)) # 拼接数据
####### 实施K均值聚类 #######
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
retval, labels, centers = cv2.kmeans(data, Group, None, criteria, 10, flags)
# print("retval:", retval)
# print("labels:", labels)
print("centers:", centers)
####### 获取分类结果 #######
label0 = data[labels.ravel() == 0]
label1 = data[labels.ravel() == 1]
######### 显示数据 #########
plt.scatter(label0[:, 0], label0[:, 1], 50, 'g', 'o')
plt.scatter(label1[:, 0], label1[:, 1], 50, 'b', 's')
plt.scatter(centers[0, 0], centers[0, 1], 200, 'r', '*')
plt.scatter(centers[1, 0], centers[1, 1], 200, 'r', '*')
plt.show()
centers: [[23.91, 27.21],
[74.909996, 73.78]]

K均值聚类_2D

4. 支持向量机#

4.1 介绍#

支持向量机( Support Vector Machine, SVM)是一种 二分类 模型,目标是寻找一个超平面对样本数据进行分割,分割的原则是确保分类类别之间的间隔最大。当数据集较小时,使用支持向量机进行分类非常有效。

不同分类器的间隔情况

  • 支持向量机就是 在已有数据中,找到离分类器最近的点,确保它们离分类器尽可能地远

  • 离分类器最近的点到分类器的距离称为间隔。目标是希望间隔尽可能地大,这样分类器在处理数据时就会更加准确

  • 离分类器最近的那些点称为支持向量,它们决定了分类器所在的位置

​ 在对原始数据分类的过程中,可能无法使用线性方法实现分割。支持向量机在分类时,可以把无法线性分割的数据映射到高维空间,然后再在高维空间找到分类最优的线性分类器

升维处理

4.2 使用#

  • 通过函数cv2.ml.SVM_create()用来创建支持向量机模块对象
  • 通过函数obj.train()用来训练支持向量机分类器
    • 函数原型obj.train( trainData, cv2.ml.ROW_SAMPLE, trainLable )
    • 参数说明
      • trainData训练数据
      • cv2.ml.ROW_SAMPLE每一行是一个样本
      • trainLable训练数据标签
  • 通过函数obj.predict()用来预测测试数据的类别
    • 函数原型ret, results = obj.predict( testData )
    • 参数说明
      • testData测试数据
      • results数据点的类别

4.3 示例#

import cv2
import numpy as np
import matplotlib.pyplot as plt
######### 数据量 #########
num_train = 50
num_test = 5
######### 准备训练数据 #########
# 制作训练数据
rand1 = np.random.randint(0, 50, (num_train, 2)).astype(np.float32) # rand1数据位于(0, 30)
rand2 = np.random.randint(50, 100, (num_train, 2)).astype(np.float32) # rand2数据位于(70, 100)
trainData = np.vstack((rand1, rand2)) # 将rand1和rand2拼接为训练数据
# 制作标签
r1Label = np.zeros((num_train, 1)).astype(np.int32) # r1Label对应rand1的标签,类型0
r2Label = np.ones((num_train, 1)).astype(np.int32) # r2Label对应rand2的标签,类型1
trainLable = np.vstack((r1Label, r2Label))
# 显示数据
# 红色:数据类别0
# 绿色:数据类别1
g = trainData[trainLable.ravel() == 0]
plt.scatter(g[:,0], g[:,1], 30, 'r', 'o')
b = trainData[trainLable.ravel() == 1]
plt.scatter(b[:,0], b[:,1], 30, 'g', 's')
# ######### 训练模型 #########
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setC(0.01)
svm.train(trainData, cv2.ml.ROW_SAMPLE, trainLable)
# ######### 准备测试数据 #########
# 制作测试数据
test = np.random.randint(0, 100, (num_test, 2)).astype(np.float32) # test数据位于(0, 100)
# 显示数据
# 红色:测试
plt.scatter(test[:, 0], test[:, 1], 80, 'm', '*')
######### 推理 #########
# 推理:使用SVM预测分类
(ret, results) = svm.predict(test)
# 推理结果
for n in range(num_test):
print("第%s个点的数据为:(%d, %d),类别为:%d" %
((n+1), test[n][0], test[n][1], results[n][0]))
plt.show()
第1个点的数据为:(84, 88),类别为:1
第2个点的数据为:(72, 80),类别为:1
第3个点的数据为:(5, 73),类别为:0
第4个点的数据为:(79, 96),类别为:1
第5个点的数据为:(80, 19),类别为:1

支持向量机

封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00