支持向量机是一种二分类模型,目标是寻找一个标准(称为超平面)对样本数据进行分割,分割的原则是确保分类最优化(类别之间的间隔最大),当数据集较小时,使用支持向量机进行分类非常有效。支持向量机是最好的现成分类器之一,这里是指分类器不加修改就可以使用的。
在对原始数据分类的过程中,可能无法使用线性方法实现分割,支持向量机在分类时,把无法线性分割的数据映射到高维空间,然后再高维空间找到分类最优的线性分类器。
又来了。。简单介绍一下基本原理
再上图中,用于划分不同类别的直线就是分类器。构造分类器时,找到最优分类器就非常重要。
上图中,右上和右下的分类器都偏向了某一个分类,左下的分类器实现了均分,左下的分类器尽量让两个分类离自己一样远,这样就为每个分类都预留了等量的扩展空间,即使有新的靠近边界的点进来,也可以按照位置划分到对应的分类内。
在已有数据中,找到离分类器最近的点,确保他们离分类器尽可能的远。 这里,离分类器最近的点到分类器的距离称为间隔,我们希望间隔尽可能的大,这样分类器在处理数据时,就会更加准确**。离分类器最近的哪些点就叫支持向量**,这些支持向量决定了分类器所在的位置。
例如:豌豆和小米混起来,如果分开他们直线肯定就不行,可以使用直径合适的筛子。这个筛子就是映射操作,可以将豌豆小米有效的分离。支持向量机处理数据时,如果在低纬空间内无法完成分类,就会自动将数据映射到高维空间,使之变成线性可分的。简单来说就是对当前数据进行函数映射操作。
支持向量机是由支持向量和机器构成:
支持向量机 就是一种基于关键点的分类算法。。
使用支持向量机模块时,需要先使用 svm = cv2.ml.SVM_create()
生成后续训练的空分类器。获取空训练器后,针对该模型使用 训练结果= svm.train(训练数据,训练数据排列格式,训练数据的标签)
对训练数据进行训练。
完成分类的训练后,使用 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.]]
预测一个离散值时,做的工作就是分类。当我们要预测一个连续值时,做的工作就是回归。
机器学习模型还可以将训练集中的数据划分为若干个组,每个组称为一个簇。这些自动形成的簇,可能对应不同的潜在概念,例如,运动员面子可以划分为篮球苗子,长跑苗子等。这种学习方式就是 聚类。特点就是学习过程中不需要用标签对训练样本进行标注。也就是说,学习过程可以根据现有训练集自动完成分类。
根据训练数据是否有标签,将机器学习划分为监督学习和无监督学习。前面的K 近邻,支持向量机都是监督学习,提供有标签的数据给算法学习,然后对数据分类,而聚类是无监督学习,不知道分类标签,直接对数据分类。
例如,100个豆子,已知其中40个绿豆,40个大豆,将剩下的20个分类就是监督学习,如果我们仅知道这些豆子由两个品种,但不知道具体是什么品种,此时可以根据豆子的大小,颜色属性,或根据两者组合属性,将其划分为两个类型,这个过程中没有使用已知标签,也同样完成了分类,这时的分类就是一种无监督学习。
6粒豆子混在一起,不知道豆子类别,按照大小分为两类。测得这些豆子直径大小,1,2,3,10,20,30.标记为 A ,B ,C, D, E, F, 进行分类操作。
经过一次后还是这个结果,我们就认为分组完成,将直径小的一组称为小豆子,另一组称为大豆子。实际处理中可能需要进行多轮的迭代才能实现数据的收敛,分类不在变化。
K均值聚类是一种将数据划分为 k 个簇的简单的聚类算法,该算法不断提取当前分类的中心点,并最终在分类稳定时完成聚类。本质来看就是一种迭代算法。
retval, bestLabels, centers=cv2.kmeans(data, K, bestLabels, criteria, attempts, flags)
# 随机生成两组数组
# 生成 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()
# 读取待处理图像
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()