opencv入门:支持向量机,K均值聚类

支持向量机

支持向量机是一种二分类模型,目标是寻找一个标准(称为超平面)对样本数据进行分割,分割的原则是确保分类最优化(类别之间的间隔最大),当数据集较小时,使用支持向量机进行分类非常有效。支持向量机是最好的现成分类器之一,这里是指分类器不加修改就可以使用的。

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

理论基础

又来了。。简单介绍一下基本原理

  1. 分类
    企业通过笔试,面使的形式招聘员工,分为A级,B级,图中横坐标是笔试成绩,纵坐标是面试成绩,右上角的圆点表示A 级,左下角的就是B级。
    opencv入门:支持向量机,K均值聚类_第1张图片
    公司希望找收到的都是A 类,关键是如果根据两个成绩确定哪个员工可能是未来的A类员工。虽然可以将成绩标准定的很高,但这样会失去一些优秀员工。所以合理指定成绩标准是很重要的
    opencv入门:支持向量机,K均值聚类_第2张图片
  2. 分类器

再上图中,用于划分不同类别的直线就是分类器。构造分类器时,找到最优分类器就非常重要。

上图中,右上和右下的分类器都偏向了某一个分类,左下的分类器实现了均分,左下的分类器尽量让两个分类离自己一样远,这样就为每个分类都预留了等量的扩展空间,即使有新的靠近边界的点进来,也可以按照位置划分到对应的分类内。

在已有数据中,找到离分类器最近的点,确保他们离分类器尽可能的远。 这里,离分类器最近的点到分类器的距离称为间隔,我们希望间隔尽可能的大,这样分类器在处理数据时,就会更加准确**。离分类器最近的哪些点就叫支持向量**,这些支持向量决定了分类器所在的位置。

  1. 将不可分变为可分
    上例是非常简单的,用一条直线(线性分类器)就可以完成划分,现实中大多数问题就更加复杂,支持向量机会将不那么容易分类的数据通过函数映射变为可分类的。

例如:豌豆和小米混起来,如果分开他们直线肯定就不行,可以使用直径合适的筛子。这个筛子就是映射操作,可以将豌豆小米有效的分离。支持向量机处理数据时,如果在低纬空间内无法完成分类,就会自动将数据映射到高维空间,使之变成线性可分的。简单来说就是对当前数据进行函数映射操作。

opencv入门:支持向量机,K均值聚类_第3张图片
支持向量机可以通过核函数有效降低计算复杂度。

  1. 总结
    实际上支持向量机可以处理任何维度的数据,在不同维度下,支持向量机都会尽可能寻找类似于二维空间中的直线的线性分类器。例如三维中就会寻找一个可以划分数据的平面,更高维中,就寻找一个可以划分当前数据的超平面。一般来说,把可以被一条直线(或者超平面)分割的数据称为线性可分的数据,所以超平面是线性分类器。

支持向量机是由支持向量和机器构成:

  • 支持向量就是离分类器最近的那些点,这些点位于最大间隔上,分类仅依靠这些点,与其他点无关
  • 机器 就是分类器。。

支持向量机 就是一种基于关键点的分类算法。。

SVM 案例介绍

使用支持向量机模块时,需要先使用 svm = cv2.ml.SVM_create() 生成后续训练的空分类器。获取空训练器后,针对该模型使用 训练结果= svm.train(训练数据,训练数据排列格式,训练数据的标签) 对训练数据进行训练。

  • 训练数据:表示原始数据,用来训练分类器。
  • 排列格式,原始数据的排列形式按行排序(cv2.ml.ROW_SAMPLE,每一条训练数据占一行) ,按列排序(cv2.,ml.COL_SAMPLE,每一条数据占一列)两种形式,根据实际选择
  • 训练数据的标签,原始数据的标签
  • 训练结果

完成分类的训练后,使用 svm.predict() 函数即可完成训练好的分类器模型对测试数据进行分类(返回值,返回结果) = svm.predict(测试数据)

这就是基本使用方法,当然实际中还需要对其中的参数进行调整。例如:可以通过 setType()函数设置类别,通过setKernel()函数设置核类型,通过 setC()函数设置支持向量机的参数 C(惩罚系数,即对误差的宽容度,默认值为 0)。

举个栗子

import cv2
import numpy as np
import matplotlib.pyplot as plt
# 第 1 步 准备数据
# 表现为 A 级的员工的笔试、面试成绩
a = np.random.randint(95,100, (20, 2)).astype(np.float32)
# 表现为 B 级的员工的笔试、面试成绩
b = np.random.randint(90,95, (20, 2)).astype(np.float32)
# 合并数据
data = np.vstack((a,b))
data = np.array(data,dtype='float32')

# 第 2 步 建立分组标签,0 代表 A 级,1 代表 B 级
#aLabel 对应着 a 的标签,为类型 0-等级 A
aLabel=np.zeros((20,1))
#bLabel 对应着 b 的标签,为类型 1-等级 B
bLabel=np.ones((20,1))
# 合并标签
label = np.vstack((aLabel, bLabel))
label = np.array(label,dtype='int32')

# 第 3 步 训练
# 用 ml 机器学习模块 SVM_create() 创建 svm
svm = cv2.ml.SVM_create()
# 属性设置,直接采用默认值即可
#svm.setType(cv2.ml.SVM_C_SVC) # svm type
#svm.setKernel(cv2.ml.SVM_LINEAR) # line
#svm.setC(0.01)
# 训练
result = svm.train(data,cv2.ml.ROW_SAMPLE,label)

# 第 4 步 预测
# 生成两个随机的笔试成绩和面试成绩数据对
test = np.vstack([[98,90],[90,99]])
test = np.array(test,dtype='float32')
# 预测
(p1,p2) = svm.predict(test)

# 第 5 步 观察结果
# 可视化
plt.scatter(a[:,0], a[:,1], 80, 'g', 'o')
plt.scatter(b[:,0], b[:,1], 80, 'b', 's')
plt.scatter(test[:,0], test[:,1], 80, 'r', '*')
plt.show()
# 打印原始测试数据 test,预测结果
print(test)
print(p2)

[[98. 90.]
 [90. 99.]]
[[1.]
 [1.]]

opencv入门:支持向量机,K均值聚类_第4张图片
可看到,测试的两个都是 B 类。

K 均值聚类

预测一个离散值时,做的工作就是分类。当我们要预测一个连续值时,做的工作就是回归。

机器学习模型还可以将训练集中的数据划分为若干个组,每个组称为一个簇。这些自动形成的簇,可能对应不同的潜在概念,例如,运动员面子可以划分为篮球苗子,长跑苗子等。这种学习方式就是 聚类。特点就是学习过程中不需要用标签对训练样本进行标注。也就是说,学习过程可以根据现有训练集自动完成分类。

根据训练数据是否有标签,将机器学习划分为监督学习和无监督学习。前面的K 近邻,支持向量机都是监督学习,提供有标签的数据给算法学习,然后对数据分类,而聚类是无监督学习,不知道分类标签,直接对数据分类。

例如,100个豆子,已知其中40个绿豆,40个大豆,将剩下的20个分类就是监督学习,如果我们仅知道这些豆子由两个品种,但不知道具体是什么品种,此时可以根据豆子的大小,颜色属性,或根据两者组合属性,将其划分为两个类型,这个过程中没有使用已知标签,也同样完成了分类,这时的分类就是一种无监督学习。

理论基础

分豆子

6粒豆子混在一起,不知道豆子类别,按照大小分为两类。测得这些豆子直径大小,1,2,3,10,20,30.标记为 A ,B ,C, D, E, F, 进行分类操作。

  1. 随机选择两个参考豆子,例如选取 A, B 作为分类参考豆子
  2. 计算每粒豆子的直径与 A 和 B 的距离。距离哪个豆子近就将新豆子划分在哪个豆子所在的组。不用想第一组只有A ,剩下五个都是B 。
  3. 分别计算第一组豆子和第二组豆子的直径平均值,然后将各个豆子按照与直径平均值的距离大小分组。第一组平均值 AV1 = 1 ,第二组 AV2= (2+3+10+20+30)/5= 13
    opencv入门:支持向量机,K均值聚类_第5张图片
    划分为 AV1 组,豆子 A ,B ,C . AV2 组,豆子 D,E,F 。
  4. 重复第三步,知道分组稳定不在变化,即可确定分组完成。

经过一次后还是这个结果,我们就认为分组完成,将直径小的一组称为小豆子,另一组称为大豆子。实际处理中可能需要进行多轮的迭代才能实现数据的收敛,分类不在变化。

K 均值聚类的基本步骤

K均值聚类是一种将数据划分为 k 个簇的简单的聚类算法,该算法不断提取当前分类的中心点,并最终在分类稳定时完成聚类。本质来看就是一种迭代算法。

  1. 随机选取k 个点作为分类的中心点。也可以随机生成 K 个并不存在于原始数据中的数据点作为分类中心点。
  2. 将每个数据点放到离他们最近的中心点所在的类中,
  3. 重新计算各个分类的数据点的平均值,将该平均值作为新的分类中心点。
  4. 重复步骤2,3 知道分类稳定。

K 均值聚类模块

retval, bestLabels, centers=cv2.kmeans(data, K, bestLabels, criteria, attempts, flags)

  • data 输入的待处理数据集合,应该是 np.float32 类型,每个特征放在单独的一列中。
  • K 分出的簇的个数,就是分类的个数,一般就是 K =2 表示二分类。
  • bestLabels 表示计算后各个数据点最终分类标签(索引),实际调用时,设置为None。
  • criteria 算法迭代的终止条件。当达到最大循环数目或者指定的精度阈值时,算法停止继续分类迭代计算。这个参数由三个子参数构成:type, max_iter , eps。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 表示选择初始中心点的方法,cv2.KMEANS_RANDOM_CENTERS:随机选取中心点。cv2.KMEANS_PP_CENTERS:基于中心化算法选取中心点。cv2.KMEANS_USE_INITIAL_LABELS:使用用户输入的数据作为第一次分类中心点;如果算法需要尝试多次(attempts 值大于 1 时),后续尝试都是使用随机值或者半随机值作为第一次分类中心点。
  • retval 返回的距离值(也称为密度值或紧密度),返回各个点到相应中心距离的平方和。
  • bestLabels 各个数据点额最终分类标签(索引)
  • centers,每个分类的中心点数据。

示例

# 随机生成两组数组
# 生成 60 个值在[0,50]内的 xiaoMI 直径数据
xiaoMI = np.random.randint(0,50,60)
# 生成 60 个值在[200,250]内的 daMI 直径数据
daMI = np.random.randint(200,250,60)
# 将 xiaoMI 和 daMI 组合为 MI
MI = np.hstack((xiaoMI,daMI))         # 转换为一列
# 使用 reshape 函数将其转换为(120,1)
MI = MI.reshape((120,1))
# 将 MI 转换为 float32 类型
MI = np.float32(MI)
# 调用 kmeans 模块
# 设置参数 criteria 的值,type, max_iter , eps
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.)
# 设置参数 flags 的值
flags = cv2.KMEANS_RANDOM_CENTERS    # 随机选取中心点
# 调用函数 kmeans
retval,bestLabels,centers = cv2.kmeans(MI,2,None,criteria,10,flags)

# # 打印返回值
# print(retval)
# print(bestLabels)
# print(centers)

# 获取分类结果
XM = MI[bestLabels==0]
DM = MI[bestLabels==1]
# 绘制分类结果
# 绘制原始数据
plt.plot(XM,'ro')                      # 用不同的颜色表示分类
plt.plot(DM,'bo')
# 绘制中心点,中心点是随机的,并且并不是原数据中的点,毕竟经过多次平均怎么可能还是源数据中的。
plt.plot(centers[0],'gx')
plt.plot(centers[1],'gx')
plt.show()

opencv入门:支持向量机,K均值聚类_第6张图片

# 读取待处理图像
img = cv2.imread('5.jpg')
# 使用 reshape 将一个像素点的 RGB 值作为一个单元处理,,感觉就是将三个当作一个了,
data = img.reshape((-1,3))
# 转换为 kmeans 可以处理的类型
data = np.float32(data)
# 调用 kmeans 模块
print(data.shape)     # (353500, 3)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 2            # 决定了展示的灰度级
ret,label,center=cv2.kmeans(data,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# 转换为 uint8 数据类型,将每个像素点都赋值为当前分类的中心点像素值
# 将 center 的值转换为 uint8
center = np.uint8(center)
# 使用 center 内的值替换原像素点的值
res1 = center[label.flatten()]        # 这个索引,,试试center[[0,0]] 就知道了。。 
# 使用 reshape 调整替换后的图像
res2 = res1.reshape((img.shape))
# 显示处理结果
cv2.imshow('img',img)
cv2.imshow('res2',res2)
cv2.waitKey(0)
cv2.destroyAllWindows()

opencv入门:支持向量机,K均值聚类_第7张图片

你可能感兴趣的:(opencv从入门到放弃,opencv)