在这篇文章中,我将介绍与支持向量机有关的东西,我们知道支持向量机主要分两类,就是线性支持向量机 和核支持向量机这两种(当然还有其他的,如多类支持向量机、 Nu-Support Vector Regression等),因此我将介绍它俩的联系与区别,但首先将会详细介绍它俩有关的知识及python实现。
一、 线性支持向量机
1.1 概念
线性支持向量机(Linear Support Vector Machine, Linear SVM)是一种用于分类和回归分析的监督学习模型,特别适用于数据在特征空间中是线性可分的情况。
线性SVM原本可用于进行二分类任务,但如果进行"一对多"等处理后,也可进行多分类任务。
下图就是iris数据集进行SVM后的图像:
1.2 数学模型
关于线性SVM的数学模型可以分为三来来看,分别是决策函数、原问题以及对偶问题。
(1)决策函数
线性SVM的决策函数是用来预测新输入样本所属类别的函数。在求解线性SVM的对偶问题之后,我们得到一系列拉格朗日乘子 αi,这些乘子用于构建最终的决策函数。其中拉格朗日乘子是对偶问题里的,暂且不谈。
我们知道SVM是用于划分类别的,所以我们可以用一个线性组合来描述:
我们将其记为:(W,b)。这样就会诞生一个空间中任意点到超平面的距离r。
然后我们假设这个超平面能正确分类样本,即方才的线性组合>0时,有yi = +1;反之小于0时,有yi = -1。
那么我将这个线性组合重写下就是:
如果用sign函数来表示,可以是这样:
(2)原问题
首先我们知道SVM问题就是一个优化问题,旨在数据线性可分的情况下去寻找一个最优的超平面将之划分,并使间隔最大化。
其中,距离超平面最近的几个训练样本使得其决策函数成立,它们就被称为是"支持向量"。而两个异类支持向量到超平面的和就是"间隔"。
为了间隔最大化,就有了原问题,所以可以写出以下式子:
(3)对偶问题
关于刚才的问题,我们要知道如果直接就用那些式子去找最优超平面会很复杂,所以我们我们需要对偶问题来帮助我们。而在对偶问题中就需要拉格朗日乘子,关于这个乘子,我们则需要了解下拉格朗日函数。
假如我们有目标函数f(x,y),我们要找到它的极值,但同时又存在一个g(x,y)=c(c为常数)来对其进行约束的话,我们就可以构造一个新函数来解决,这个新函数就是拉格朗日函数,表达式如下:
其中的λ(lambda)就是拉格朗日乘子。
而要解决这个式子就需要让其梯度为0,如下:
然后有:
最后就可分别求得x、y、λ的值了。
那么我们就来求取在对偶条件下的线性SVM的模型:
我们已知拉格朗日函数的形式,那么我们将相应的函数带入即可。首先是目标函数,因为我们要求取一个最优超平面,所以将刚才的原问题中的模型带入到f(x,y)的位置;然后是约束函数,因为要让间隔最大化,所以需要约束条件如此确保间隔的最大化;接下来是常数C,一般在没有先验条件的前提下,我们都会使用1作为默认值。(当C越大,模型越倾向于减少分类错误,C越小,模型的泛化能力越强)
那么就有了最后的模型:
1.3 python实现
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 加载Iris数据集
iris = load_iris()
X = iris.data[:, :2] # 只取前两个特征:花萼长度和花萼宽度
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建SVM模型
svm = SVC(kernel='linear', C=1)
# 训练模型
svm.fit(X_train, y_train)
y_pre = svm.predict(X_test)
accuracy = accuracy_score(y_test, y_pre)
print("Accuracy is: {}".format(accuracy))
# 绘制决策边界
def plot_decision_boundary(model, X, y):
# 创建网格
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01),
np.arange(y_min, y_max, 0.01))
# 预测网格中的类别
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制背景色
plt.contourf(xx, yy, Z, alpha=0.8)
# 绘制训练数据点
markers = ['o', 's', '^']
for i, label in enumerate(np.unique(y)):
plt.scatter(X[y == label, 0], X[y == label, 1], marker=markers[i], label=iris.target_names[label])
# 绘制决策边界
plt.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], linestyles=['--', '-', '--'])
# 图像设置
plt.title('Iris Dataset with SVM Decision Boundary')
plt.xlabel('Sepal Length (cm)')
plt.ylabel('Sepal Width (cm)')
plt.legend()
plt.show()
# 绘制决策边界
plot_decision_boundary(svm, X, y)
二、 核支持向量机
1.1 概念
核SVM利用核函数(Kernel Function)将输入数据映射到一个更高维的空间,在这个高维空间中,原本非线性可分的数据变得线性可分。
同样的,我给出一个用iris进行SVM分类后的图像,如下图:(其代码随后给出)
1.2 数学模型
我们来看核SVM的数学模型,其基本形式与线性的十分相似,我们令Φ(x )来表示将x 映射后的特征向量,于是之前的模型就发生了改变,便成为:的形式。然后其对偶问题也就变为:
最后经过一些重写、化简等就变为:
而这里的函数κ()就是"核函数"(kernal function)。
而关于核函数其实存在多种选择,因为我自己也对其不够了解,所以只在此给出西瓜书上的常用核函数表,如下:
1.3 联系
在我们初步了解二者的概念后,我们就可以知道,其实KSVM(核SVM)就是线性SVM在原本处理不了的非线性数据上做出的一个拓展,旨在增加维度而能实现让SVM进行分类。
1.4 区别
适用范围:在线性SVM中,它适用于处理线性可分的数据集,即可以通过一个超平面(在二维空间中是一条直线,在高维空间中是一个超平面)来分隔不同类别的数据点;而KSVM中,它适用于处理非线性可分的数据集,即数据不能通过一个简单的超平面来分隔。在这种情况下,数据可能需要被映射到一个更高维的空间,才能找到一个合适的分隔超平面。
计算复杂度:线性SVM的复杂度相对较低,而KSVM因为核函数的缘故,计算复杂度较高。
实际应用:在线性可分数据集上,线性SVM通常比KSVM更快并且更简单。例如,在文本分类或一些简单的图像识别任务中,数据可能是线性可分的;在非线性可分数据集上,KSVM能够更好地捕捉数据中的复杂关系。例如,在手写数字识别、生物信息学等领域,数据通常是非线性可分的,KSVM能够提供更高的准确率。
1.5 python代码
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.svm import SVC
from mpl_toolkits.mplot3d import Axes3D
# 加载Iris数据集
iris = datasets.load_iris()
X = iris.data[:, :3] # 使用前三个特征
y = iris.target
# 使用线性核的支持向量机
svm = SVC(kernel='linear')
svm.fit(X, y)
# 创建3D图形
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d') # 使用add_subplot代替Axes3D
ax.view_init(elev=-152, azim=-26) # 设置视角
# 掩码
mask_0 = y == 0
mask_1 = y == 1
mask_2 = y == 2
# 绘制散点图
ax.scatter(X[mask_0, 0], X[mask_0, 1], X[mask_0, 2], c='b', s=60, label="Setosa")
ax.scatter(X[mask_1, 0], X[mask_1, 1], X[mask_1, 2], c='r', marker='^', s=60, label="Versicolour")
ax.scatter(X[mask_2, 0], X[mask_2, 1], X[mask_2, 2], c='g', marker='s', s=60, label="Virginica")
# 设置坐标轴标签
ax.set_xlabel("Sepal length")
ax.set_ylabel("Sepal width")
ax.set_zlabel("Petal length")
# 添加图例
ax.legend()
# 绘制决策平面
w = svm.coef_[0]
a = -w[0] / w[2]
b = -w[1] / w[2]
xx, yy = np.meshgrid(np.linspace(X[:, 0].min(), X[:, 0].max(), 50),
np.linspace(X[:, 1].min(), X[:, 1].max(), 50))
zz = (-svm.intercept_[0] - a * xx - b * yy) / w[2]
# 画出平面
surf = ax.plot_surface(xx, yy, zz, rstride=1, cstride=1, color='none',
alpha=0.3, edgecolor='darkblue')
# 显示图形
plt.show()
三、 补足
此外,我们会发现在分类时偶尔会有些数据点分类错误或是在间隔内,那么我们就可以称它为"软间隔",那么我可以在这里完整描述下关于软间隔的含义:它适用于不完全线性可分的数据集,允许一定的误差。并通过调整C来控制惩罚程度,以达到最好的泛化能力。而于此相对的,硬间隔的含义就是:适用于 完全线性可分的数据集,不允许任何训练样本出现在间隔内或是被错误地分类。
因为在现实中大多不能做到硬间隔的分类,所以我们我们需要软间隔,更需要关于它而对原有的SVM模型进行修改,比如加入损失函数,引入松弛变量等,这样就能得到常说的"软间隔SVM"。
SVM处了能处理分类问题还能进行回归,这样就是"支持向量回归"了,即Support Vector Regression。
关于SVM的东西还有很多,我将在之后继续介绍。
此上