一、支持向量机概述
1.定义
支持向量机是一种基于间隔最大化原则的监督学习模型,它通过找到数据集中的最优超平面来区分不同的类别。在二维空间中,这个超平面可以看作是一条线;在三维空间中,它是一个平面;而在更高维空间中,它是一个超平面。
2.基本原理
- SVM通过找到数据点之间的最优边界(称为超平面)来区分不同的类别。
- 这个超平面的选择使得它尽可能地远离每个类别最近的数据点,这些数据点被称为支持向量。
3.关键概念
(1)间隔(Margin):
- 数据集所有点到分隔面的最小间隔的2倍,称为分类器或数据集的间隔。SVM的目标是找到一个决策边界,使得数据间隔最大化。
(2)支持向量(Support Vectors):
- 支持向量是那些位于间隔边界上的数据点,它们直接影响超平面的位置和方向。如果移除了这些点,超平面的位置将会改变。
(3)超平面(Hyperplane):
- 在数学上,超平面是一个线性决策边界,可以表示为 w⋅x+b=0,其中 w 是权重向量,x 是特征向量,b 是偏置项。对于二维平面来说,分隔超平面就是一条直线。对于三维及三维以上的数据来说,分隔数据的是个平面,称为超平面,也就是分类的决策边界。
(4)核函数(Kernel Function):
- 当数据不是线性可分时,核函数可以用来将数据映射到一个更高维的空间,在这个空间中寻找线性可分的超平面。在原始数据中计算结果映射到高维空间中,可以在一个低维空间去完成高维度的样本的计算。
(5)软间隔(Soft Margin):
- 软间隔是SVM对间隔最大化原则的一种扩展,它允许一些数据点违反间隔边界,以处理数据中的噪声和异常值。
(6)正则化(Regularization):
- 正则化是为了防止模型过拟合,通过在目标函数中添加一个惩罚项来控制模型的复杂度。
(7)优化问题:
- SVM的训练过程可以看作是一个凸优化问题,通常使用拉格朗日乘子法来求解。
二、求解最大间隔
1.点到超平面的距离
如图,点x到平面的距离大小可以等于x与平面内一点x'的距离在法向量上的投影的绝对值。
即
又由于x'在超平面上,所以w^T*x'=-b,则上式可化为
2.寻找目标函数
(1)优化目标
数据集是(x1,y1),(x2,y2).....(xn,yn),当x为正例时样本类别y=1,当x为负例的时样本类别y=-1.
我们的决策方程是,当y(xi)>0时y=+1,当y(xi)<0时y=-1,所以yi*y(xi)必然大于0
那么上一步得到的距离公式可以去绝对值简化为
(2)目标函数
我们的需求是想要找到的这个决策面,要使得所有点中离该面最近的点能够与该面的距离越远越好
将需求转化为公式即
为了简化这个目标函数方便计算,可以通过放缩变换使得决策方程的结果|y|大于等于1
即 (之前认为必然大于0,现在严格了些)
那么
所以目标函数只需考虑:
将上式这个求极大值问题转化为求极小值问题:
约束条件:
3.拉格朗日乘子法
其中f0(x)是目标函数,fi(x)为不等式约束,hi(x)为等式约束。
由上一步我们可以看到要求解目标函数,即是在约束条件下求极值问题,那么我们可以通过拉格朗日乘子法来进行求解。
根据已知条件引入 拉格朗日乘子αi,构造拉格朗日函数:
由KKT条件对偶性质可有
对w求偏导:
对b求偏导:
将上面两个偏导得出的两个式子带入构造的拉格朗日函数
可以得到继续对α求极大值
条件:
将求极大值转化为求极小值问题:
条件:
4.求解决策方程
以此题为例进行求解。
(1)首先将样本数据带入原式中得到:
由于约束条件可将上式化简为:
(2)接着分别对α1和α2求偏导并另偏导结果等于0可得:
α1=1.5,α2=-1 (并不满足约束条件 ,所以解应在边界上)
考虑边界情况,分别令α1和α2等于0带入式子求偏导并令偏导等于0求得:
令α1=0时,得α2=-2/13 不满足约束
令α2=0时,得α1=0.25 满足约束
可得α1=0.25,α2=0,α3=0.25,所以最小值在(0.25,0,0.25)处取得。
(3)最后将α结果带入求解:
w=0.25*1*(3,3)+0.25*(-1)*(1,1)=(0.5 , 0.5)
又
则 b=1-(0.25*1*18+0.25*(-1)*6)=-2
所以平面方程为:0.5x1+0.5x2-2 (这里的x1,x2为两个特征)
三、软间隔优化
硬间隔
在硬间隔SVM中,模型试图找到一个决策边界(或超平面),使得不同类别的数据点被完全分开,并且距离这个决策边界最近的点(即支持向量)与边界的距离最大化。硬间隔SVM的目标是找到这样的一个超平面,使得所有的训练数据点都正确分类,并且没有一个数据点违反这个间隔。
软间隔
软间隔的概念放宽了硬间隔的要求。在软间隔SVM中,允许一些数据点违反间隔,即它们可以位于错误的一侧或者在间隔内部。这种违反是通过引入松弛变量来实现的,每个数据点都有一个对应的松弛变量。这些变量允许数据点在间隔内或间隔外。
有时候数据中会有一些噪音点,如果考虑它们会使得获得的决策边界效果不好,如下图:
若我们按照此前讲的方法进行求解我们会得到图中实线所表示的超平面,这个支持向量机中的间隔非常小,在之后的分类测试中不会得到很好的效果,这都是因为我们此前的方法过于严格,要求把两类点完全分开,由于左上角出现的噪音点造成得到超平面不是很好。
为了解决这个问题,引入松弛因子:
得到新的目标函数: 其中C是我们需要指定的一个参数
要求得使这个目标函数值小,那么对C的不同选择会有不同影响:
- 当C趋近于很大时:那么要偏小,意味着分类严格不能有错误
- 当C趋近于很小时:那么要偏大,意味着可以有更大的错误容忍
接着后续同此前解法相同进行求解
四、核函数
核函数 K 是一个函数,它能够计算两个数据点 x 和 y 在某个高维特征空间中的内积(点积)。通过使用核函数,SVM可以解决非线性可分问题。核函数将原始数据映射到一个更高维的空间,在这个空间中,数据可能是线性可分的。
如图,在左边的数据的无法线性可分,将其映射到高维空间中就可以通过一个平面进行划分了
其中ϕ 是一个从原始输入空间映射到高维特征空间的映射函数。
另外,在高维空间中计算内积更为复杂,核函数在原始数据中计算结果映射到高维空间中,可以在一个低维空间去完成高维度的样本的计算,大大降低了计算复杂度和存储需求。
常见的核函数:
五、利用SVM完成数据分类
1.获得训练集
(1)代码:
import numpy as np # type: ignore
import matplotlib.pyplot as plt # type: ignore
from sklearn import datasets # type: ignore
# 生成一些测试数据
X, y = datasets.make_blobs(n_samples=100, centers=2, random_state=22)
从 sklearn.datasets
模块导入 make_blobs
函数,这个函数可以生成具有特定参数的合成数据集。
- 调用
make_blobs
函数生成数据集,并将结果赋值给变量X
和y
。n_samples=100
:生成 100 个样本点。centers=2
:形成两个类别的两组。random_state=22
:设置随机状态,以确保每次运行代码时生成的数据集是相同的。这有助于代码的可重复性。
(2)运行结果:
将样本点可视化得到:
2.求解α
(1)代码实现:
#求解alpha
def solveAlpha(X, y, C, alpha):
# 设置学习率和迭代次数
learning_rate = 0.001
n_iters = 1000
# 计算内积矩阵
H = np.outer(y, y) * np.dot(X, X.T)
# 迭代更新 alpha
for _ in range(n_iters):
for i in range(len(y)):
# 计算梯度
gradient = np.dot(H[i], alpha) - 1
# 使用梯度下降更新 alpha
alpha[i] -= learning_rate * gradient
# 将 alpha 限制在 [0, C] 范围内
alpha[i] = max(0, min(C, alpha[i]))
return alpha # 返回计算得到的 alpha 数组
此前我们已经经过分析,欲通过拉格朗日乘子法来求w和b,要先构造拉格朗日函数,构造好的拉格朗日函数经过我们的化简后得到一个与拉格朗日乘子α相关的式子 :,就转换成了求使得式子值最小的α,再通过求得的α进一步去求w和b,那么这里就是通过梯度下降的方式来求最佳α。
在这里由于拉格朗日乘子要满足α>0, 且通过正则化参数C来约束阿尔法最终的取值,把α最终的值映射到0~C范围内实现正则化和软间隔的过程。
(2)运行结果:
在写一个函数来表示计算式子的值来查看每一轮计算α后目标函数的值是否朝着我们预期方向发展:
def objective_value(alpha, H):
return 0.5 * np.dot(alpha, np.dot(H, alpha)) - np.sum(alpha)
在运行下列代码:
进行测试后我们得到下列目标函数跟着变化的结果:
从上面的结果中可以看到目标函数的值在不断减少,与我们欲求的min越来越近,朝着预期方向发展,故alpha求解过程正确。
3.求解w,b
(1)代码实现:
#求解w,b
def fit(X, y,C):
# 获取样本数量和特征数量
n_samples, n_features = X.shape
# 将标签转换为双极性形式 (-1 和 1)
y = np.where(y <= 0, -1, 1)
# 初始化拉格朗日乘子
alpha = np.zeros(n_samples)
# 用于求解对偶问题的优化器 (手动实现的简单梯度下降),获取alpha
alpha=solveAlpha(X,y,C,alpha)
# 计算 w
w = np.sum(alpha[:, None] * y[:, None] * X, axis=0)
# 找出支持向量的索引(alpha > 0 的那些样本)
support_vector_indices = np.where(alpha > 0)[0]
# 计算 b,使用支持向量的平均值
b = np.mean(y[support_vector_indices] - np.dot(X[support_vector_indices], w))
return w,b
在上一步中,我们已经通过梯度下降的方法找到了较好的α值,接着我们可以通过α来计算w和b
首先是w,我们可以通过对最开始构造的拉格朗日函数求w的偏导得到
接着我们可以通过线性函数的式子来求得
alpha[:, None] * y[:, None] * X
: 这是一个元素乘法操作。alpha
和y
被扩展为二维数组后,与X
进行元素级别的乘法。None
在这里的作用是增加维度,使得alpha
和y
可以与X
在形状上兼容,从而进行元素级别的乘法。
np.mean
: 这是NumPy库中的函数,用于计算数组的平均值。在这里,它被用来计算支持向量的目标值与它们的线性组合(通过权重w
和特征X
)的差值的平均值。
(2)运行结果:
加入代码进行测试w和b的结果:
w,b=fit(X,y,1)
print(w)
print(b)
运行此段代码进行测试得到:
4.进行预测
(1)代码实现
def predict(w, b, X):
result = np.dot(X, w) - b # 计算线性输出结果
signs = [] # 用于存储预测的类别标签
for i in result:
if i > 0:
signs.append(1) # 若结果大于0,则预测为正类
else:
signs.append(-1) # 若结果小于等于0,则预测为负类
return signs
(2)运行结果
加入如下代码测试,其中测试项X_test的实际结果为负
w,b=fit(X,y,1)
X_test = np.array([[-7.86981776e+00,-2.71891455e-01]])
print(predict(w,b,X_test))
测试得到结果:
成功进行了正确分类。
5.绘制超平面
(1)代码实现:
def plot_hyperplane(X, y, w, b):
# 绘制数据点
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y, s=100, edgecolors='k', cmap='winter') # X中的每一行表示一个数据点,第一列为x坐标,第二列为y坐标
# 获取轴对象并获取该轴的x和y的界限
ax = plt.gca()
xlim = ax.get_xlim() # 获取 x 轴的范围
ylim = ax.get_ylim() # 获取 y 轴的范围
# 创建网络
xx = np.linspace(xlim[0], xlim[1], 30) # 在 x 轴范围内创建30个网格点
yy = np.linspace(ylim[0], ylim[1], 30) # 在 y 轴范围内创建30个网格点
YY, XX = np.meshgrid(yy, xx) # 生成网格点坐标矩阵
# 评估决策函数
xy = np.vstack([XX.ravel(), YY.ravel()]).T # 将网格点坐标转换为 (N, 2) 的数组形式
Z = (np.dot(xy, w) - b).reshape(XX.shape) # 使用模型预测网格点坐标的标签
# 绘制决策边界和正负区域
ax.contour(XX, YY, Z, color='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
plt.show()
(2)运行结果:
1)当C=1时:
2)当C=0.001时:
(3)结果分析:
对于1)和2),我们取了不同的参数C,最后得到的决策边界也不相同
- 在1)中我们使用了较大的正则化参数C=1,对分类要求更为严格,错误容忍更小,那么分割的数据点要更为严格的分布在边界外,允许更少量数据点违反间隔
- 在2)中我们使用了较小的正则化参数C=0.001,放宽了间隔的要求,允许更多的数据点违反间隔,就出现了2)中图所示的更多的点在边界内的现象。
六、实验总结
1.问题与解决:
问题:如何获得想要的w和b?
解决方案:观看了网上的教学视频以及搜索网络资料素材,逐渐明白了采用拉格朗日乘子法来求解 w和b。首先构造个拉格朗日函数,再通过对w 和 b 的偏导结果来简化拉格朗日函数式子,最终化为了与 \( \alpha \) 拉格朗日乘子有关的式子,最终的 w和 b 可以间接通过alpha 来求得,那么问题就转化成了求 alpha。
问题:如何更新alpha 以获得最佳的决策边界?
解决方案:我们转换简化了目标函数与alpha 有关,且是为了求使得这个目标函数值最小的alpha,此前我们有用过梯度下降法来求逻辑回归中的w,联想到这两个变量的求法类似,所以直接搬来了梯度下降方法来迭代更新 alpha的值。
问题:获得的决策边界不理想,无法实现硬间隔的效果:
alpha 的值:
解决方案:在对比自己与他人的代码,查询网络资料后发现少了正则化的过程,在拉格朗日乘子法实现 SVM 的方法中采用限制约束的方式来完成这个过程,首先拉格朗日乘子本身需要大于 0,其次我们设置一个正则化参数来进一步约束这个alpha的值来完成正则化约束,C是正则化参数,控制模型复杂度与分类误差之间的权衡。ξi 是松弛变量,用来处理不满足约束条件的样本点。就是将alpha 的值限制映射到了 0 到C 的范围内,通过调节C的值来完成软硬间隔的切换即容错率大小。当C 小时容错率大,当C 大时容错率小。
问题:如何设置合适的学习率以避免代价逐渐增大的问题?
解决方案:发现学习率对梯度下降的影响很大。开始时,我的学习率设置过大,导致代价函数逐渐增大。通过反复尝试不同的学习率,最终找到了一个较为合适的值,确保了梯度下降的稳定性。
问题:如何处理数据中的噪音点对模型性能的影响?
解决方案:引入了软间隔的概念,允许一些数据点违反间隔,并通过引入松弛变量来实现。这些变量允许数据点在间隔内或间隔外,提高了模型对噪音点的容忍度。但在设置正则化参数的时候,选择不同的参数对实验结果也有影响,需要不断地尝试不同的 C找到较为合适的超平面。
2.实验小结
理解SVM: 支持向量机是一种强大的分类算法,特别适用于解决高维空间中的分类问题。通过最大化间隔和使用核技巧,SVM能够有效地区分不同类别的数据点。
模型构建: 在构建SVM模型时,我们首先定义了模型的形式,包括线性核和非线性核。然后,我们定义了目标函数和约束条件,以实现间隔最大化。
梯度下降算法: 为了找到最优的权重向量和偏置项,我们采用了梯度下降算法。通过迭代更新参数,我们最小化了目标函数,从而获得了最佳的模型参数。
实验体会: 本次实验加深了我们对SVM的理解,特别是在处理非线性可分数据集时的应用。我们也认识到了选择合适的学习率、理解矩阵运算规则以及核函数选择的重要性。实验过程中的问题解决提高了我们的编程能力和问题解决能力,使我们更加熟悉机器学习模型的调试和优化过程。通过实践,我们学会了如何构建、训练和评估一个SVM模型,这是我们在数据科学和机器学习领域的重要一步。