1. 简介
支持向量机(Support Vector Machine,SVM)是一个强大的分类算法。它的基本思想是找到一个超平面,用来分割数据空间,使得两个不同分类的数据点能够被完美地分开。
在OpenCV中,我们可以使用cv2.ml.SVM类来实现支持向量机分类器,提供了许多不同的内核函数来适应不同类型的数据集,如线性、多项式和径向基函数等。
2. SVM基本原理
2.1 线性可分情况
当数据集是线性可分的时候,SVM的目标就是在不同类别之间找到一个可以割开数据集的直线或超平面。这个超平面应该是距离每个类别尽可能远的点最近的超平面。这就是所谓的“最大间隔”方法。
在SVM的基本形式中,我们将找到一个超平面的法向量w和一个截距b,使得所有数据点满足:
yi(w · xi + b) ≥ 1
其中,yi是第i个样本的标签,xi是对应的特征向量。
因为每个点到分类边界的距离为:
d = |w| / ||w||
其中|w|是w的模,||w||是w的范数,指数是2。我们注意到,向量w只有在分割超平面的方向上才有重要性,因此可以使用单位向量代替它。这意味着我们只需要将所有样本与超平面之间的距离缩小至1或更小即可。
为了解决这个问题,我们可以使用Lagrange乘子法,通过最小化函数
L(W,b,α) = 0.5||W||2 - ∑ αi[yi(W · xi + b) - 1]
其中,αi是一个Lagrange乘子,用于处理限制条件(yi(w · xi + b) ≥ 1)。通过求导并令其等于0,求解W和b得到
W = ∑ αiyixi
b = yi - w · xi
其中只有支持向量对应的αi不为0。因此,SVM只需要从数据集中挑选出这些支持向量来计算权重向量W和偏差b。
在OpenCV中,我们可以使用cv2.ml.SVM类来实现这个过程:
import cv2
import numpy as np
# 构建数据集
X = np.array([[1, 2], [4, 5], [2, 1], [3, 5], [4, 2], [2, 4]], dtype=np.float32)
Y = np.array([[-1], [1], [-1], [1], [1], [-1]], dtype=np.float32)
# 初始化分类器
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
# 训练SVM
svm.train(X, cv2.ml.ROW_SAMPLE, Y)
# 执行预测
newcomer = np.array([[4, 2]], dtype=np.float32)
response = svm.predict(newcomer)
print(response)
以上代码首先构建了一个线性可分的二分类问题的数据集。然后,初始化了一个cv2.ml.SVM类实例,将其类型设置为SVM_C_SVC,并将内核设置为线性内核。随后使用train方法进行训练,最后使用predict方法对新的数据进行分类。
2.2 非线性情况
当数据集不能被简单的超平面分割时,我们需要使用非线性内核将其从低维空间映射到高维空间。在高维空间中,我们通过超平面来分割数据。因此,在高维空间中出现的超平面是原始空间中的非线性函数。
3. OpenCV中的SVM
3.1 数据准备
在OpenCV中,我们可以使用cv2.ml.SVM_create方法创建一个SVM分类器。然后,我们需要将数据分为训练集和测试集,使用fit或train方法训练SVM,并使用predict方法进行预测。
假设我们有一个包含两个类别的数据集,具有两个特征。下面的代码演示如何将其分成训练集和测试集,以及如何使用cv2.ml.SVM_create方法创建并训练SVM。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 构建数据集
X = np.random.randn(500, 2)
Y = np.logical_xor(X[:, 0] < 0, X[:, 1] < 0)
# 数据分离
train_data, train_labels = X[:400], Y[:400]
test_data, test_labels = X[400:], Y[400:]
# 初始化SVM分类器
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_RBF)
svm.setC(10)
svm.setGamma(0.1)
# 训练SVM
svm.train(train_data, cv2.ml.ROW_SAMPLE, train_labels)
# 预测测试数据
_, result = svm.predict(test_data)
accuracy = (result == test_labels).astype(int).sum() / len(test_labels)
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap='cool')
plt.show()
以上代码首先生成一个半环形的二分类数据集。然后,将它分为训练集和测试集。在初始化SVM分类器的时候,我们使用了径向基函数作为内核,并设置C和γ的参数。在训练SVM的时候,我们使用train方法,并在predict方法中进行预测和评估。
3.2 其他参数
除了上面提到的C和γ参数之外,cv2.ml.SVM_create()还提供了许多其他参数,可以用来调整SVM的性能和精度。
一些重要的参数包括:
C:代价常数。该参数控制SVM分类器中误分类的惩罚程度。常数C越小,误分类的代价也越小,容错性更高。
gamma:与径向基函数相关。该参数控制每个数据点对分类超平面的影响程度。当gamma越大时,分类边界也会越弯曲,使得SVM过度拟合。
degree:与多项式内核相关。该参数控制多项式函数的次数。
coef0:与多项式和sigmoid内核相关。该参数控制中心位置。
class_weights:与不平衡样本相关。该参数可以给某些类别指定较高的权重,帮助训练过程更关注通常较少样本的类别。
4. 总结
SVM是一个功能强大的分类器,可以用于不同类型的数据集。OpenCV中提供了一个cv2.ml.SVM_create类,可以轻松构建和训练SVM分类器,并在新数据上进行分类。要获得更好的性能和精度,请使用不同的内核和参数进行试验。