一、算法介绍
支持向量机(Support Vector Machine,SVM)是一种常用的监督学习算法,用于分类和回归任务。SVM的基本原理是构建一个最优超平面来分割不同类别的数据点。
在二分类问题中,SVM试图找到一个能够将两个类别的数据点尽可能分开的超平面。超平面是一个d-1维的线性子空间,其中d是数据的特征维度。SVM的目标是找到具有最大间隔(即离超平面最近的样本点的距离最大)的超平面。
SVM的核心思想是将数据映射到高维特征空间中,通过引入核函数可以避免直接计算高维特征空间的复杂性。常用的核函数有线性核、多项式核和高斯核等。这些核函数能够将数据从原始空间映射到一个更高维度的空间,使得原本线性不可分的数据在新的空间中变得线性可分。
SVM的训练过程可以形式化为一个凸优化问题,通过最小化目标函数来确定超平面的位置和宽度。目标函数通常由数据的误分类损失和正则化项组成,正则化项是为了防止过拟合。
一旦训练完成,SVM可以用于对新样本进行分类或回归预测。对于分类问题,新样本将被映射到特征空间中,并根据其位置与超平面的关系来判断其所属类别。对于回归问题,SVM可以通过拟合一个边界带来预测。
SVM具有一些重要的特性,包括结构化风险最小化、稀疏性和鲁棒性等。它在处理小样本、高维数据和非线性问题方面表现出色,并在许多领域中得到广泛应用,如图像分类、文本分类、生物信息学和金融预测等。
二、基于最大间隔分隔数据
2.1 线性模型
在二维空间上,两类点被一条直线完全分开叫做线性可分。如下图,在二维坐标下,样本空间中找到直线, 将不同类别的样本分开。
上述将数据集分隔开来的直线称为分隔超平面,即
2.2 超平面
由于数据点都在二维平面上,所以此时分隔超平面就只是一条直线。但是,如果所给的数据 集是三维的,那么此时用来分隔数据的就是一个平面。显而易见,更高维的情况可以依此类推。如果数据集是1000维的,那么就需要一个999维的某某对象来对数据进行分隔。当数据集是N维时,需要一个N-1维的某某对象来对数据进行分隔。N-1维的该对象被称为超平面(hyperplane),也就是分类的决策边界。 分布在超平面一侧的所有数据都属于某个类别,而分布在另一侧的所有数据则属于另一个类别。
![](https://i-blog.csdnimg.cn/blog_migrate/b1ade796dab4cc6789bc6a54a3b853ac.png)
从二维扩展到多维空间中时,将 D0和 D1 完全正确地划分开的 就成了一个超平面。为了使这个超平面更具鲁棒性,我们会去找最佳超平面,以最大间隔把两类样本分开的超平面,也称之为最大间隔超平面。
如上图,有五条直线,它们都能将数据分隔开,但是其中哪一条最好呢?我们希望找到离分隔超平面最近的点,确保它们离分隔面的距离尽可能远。这里点到分隔面的距离被称为间隔(margin)。我们需要的是间隔尽可能地大,这是因为如果犯错或者在有限数据上训练分类器的话,分类器尽可能健壮。所以,应选择”正中间”的那条直线 , 容忍性好, 鲁棒性高, 泛化能力最强,选择最大化决策边界的边缘。
2.3 支持向量
如下图,支持向量(support vector)就是离分隔超平面最近的那些点。
超平面方程:
2.4 支持向量机
支持向量机(Support Vector Machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器;SVM还包括核函数,这使它成为实质上的非线性分类器。SVM有很多实现,下面介绍的是序列最小优化(Sequential Minimal Optimization,SMO)算法。 在此之后,将介绍如何使用核函数(kernel)的方式将SVM扩展到更多数据集上。
三、寻找最大间隔
3.1超平面的性质
性质一:法向量和偏置项以任意相同的倍数放缩,新表达式描述的仍然是原来的超平面。假设放缩比例为,令
后得到的超平面表达式为
,显然,这个表达式表示的仍然是原来的超平面。
性质二:样本空间中的任意一个点,到超平面的距离表达式为
性质三:超平面将维空间划分为3部分,分为是:①点
在超平面里
=0;②点
在超平面的“上方”
;③点
在超平面的“下方”
。
3.2寻找最大间隔
SVM中,分隔超平面是一个能够将正负样本恰好隔开的超平面,并且使得正样本在分隔超平面“上方”,负样本在分隔超平面”下方“。这就意味着,分隔超平面中的
需要满足以下条件:
其中为正样本点,
为负样本点,而正负样本对应的标记值为
,所以这两个条件可以改写成下面两个式子:
于是,对于线性可分的样本集:
,其中,
分类正确的超平面需要满足的条件为:
令,我们可以得到更加紧凑的表达:
"分类正确"
在SVM中,被称为样本点到超平面的函数间隔。
因此,我们可以得出结论,对于给定的线性可分的样本集合,必然存在分隔超平面可以将正负样本分开,该分类正确的超平面需要满足的条件为:样本点到超平面的函数间隔大于零。
所以,为了方便后续的计算,简化方程为:
令 和
位于决策边界上,标签分别为正、负的两个样本,考虑
到分类线的距离为:
因此,分类间隔为:
最大化间隔也就是寻找参数和
, 使得
最大,即:
四、拉格朗日乘子法
拉格朗日乘子法(Lagrange Multiplier Method)是一种用于求解约束最优化问题的数学方法。通过引入拉格朗日乘子,将约束条件融入目标函数中,从而将原始问题转化为一个无约束的优化问题。
假设我们要求解如下形式的最优化问题:
最小化(或最大化)目标函数 f(x), 满足约束条件 g(x) = 0。
其中,x 是待求解的变量。
为了使用拉格朗日乘子法求解这个问题,我们引入一个拉格朗日乘子 λ,构建拉格朗日函数:
L(x, λ) = f(x) + λg(x)。
然后,我们可以将原始问题转化为一个无约束的优化问题,即求解拉格朗日函数 L(x, λ) 的极值问题:
最小化(或最大化)L(x, λ)。
为了找到 L(x, λ) 的极值点,我们需要对 x 和 λ 分别求导,并令导数等于零:
∂L(x, λ)/∂x = 0, ∂L(x, λ)/∂λ = 0。
通过求解这个方程组,我们可以得到关于 x 和 λ 的解。其中,x 就是原始问题的最优解,而 λ 则对应着约束条件的乘子。
五、核函数
核函数(Kernel Function)是机器学习中一种重要的工具,它用于将数据从原始空间映射到一个高维特征空间,从而使得非线性可分的数据在新的特征空间中变得线性可分。
在支持向量机(SVM)等算法中,核函数的主要作用是通过计算两个样本在特征空间中的相似度,来替代显式地进行特征映射。这样可以避免了高维特征空间的计算和存储开销,同时仍然能够利用高维特征空间带来的线性可分性。
核函数的特点是只需计算原始空间中的样本之间的内积,而无需显式地计算样本在高维特征空间中的位置。常见的核函数包括线性核函数、多项式核函数、高斯径向基函数(RBF核函数)等。
以高斯径向基函数为例,其定义如下:
K(x, y) = exp(-||x-y||^2 / (2σ^2))
其中,x 和 y 是两个样本点,||x-y|| 表示欧氏距离,σ 是高斯核函数的宽度参数。高斯径向基函数可以将样本映射到无穷维的特征空间中,使得原本分布较近的样本之间的相似度更高。
六、基于SVM实现鸢尾花分类预测
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
# 导入数据,分离特征与输出
iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
target = pd.Series(iris.target)
print('特征和目标的大小', iris_df.shape, target.shape)
# 画布属性设置
mpl.rcParams['font.family'] = ['sans-serif']
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
# 散点图
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
scatter = plt.scatter(iris_df.iloc[:, 0], iris_df.iloc[:, 1], c=target, cmap='viridis')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('鸢尾花特征分析散点图')
a, b = scatter.legend_elements()
print('散点对象及其标签', a, b)
b = ['setosa', 'versicolor', 'virginica'] # 原标签0,1,2分别对应以下三种
plt.legend(a, b)
plt.subplot(1, 2, 2)
scatter = plt.scatter(iris_df.iloc[:, 2], iris_df.iloc[:, 3], c=target, cmap='viridis')
plt.xlabel('Petal length')
plt.ylabel('Petal width')
plt.title('鸢尾花特征分析散点图')
a, b = scatter.legend_elements()
print('散点对象及其标签', a, b)
b = ['setosa', 'versicolor', 'virginica'] # 原标签0,1,2分别对应以下三种
plt.legend(a, b)
plt.show()
# 划分训练和测试数据
X_train, X_test, y_train, y_test = train_test_split(iris_df, target, test_size=0.2, random_state=5)
# 线型SVM模型训练
linear_svm = SVC(kernel='linear', C=1, random_state=0)
linear_svm.fit(X_train, y_train)
# 基于RBF非线性SVM模型训练
rbf_svm = SVC(kernel='rbf', C=1, gamma=0.1, random_state=0)
rbf_svm.fit(X_train, y_train)
# 模型评估
linear_y_pred = linear_svm.predict(X_test)
linear_accuracy = accuracy_score(y_test, linear_y_pred)
rbf_y_pred = rbf_svm.predict(X_test)
rbf_accuracy = accuracy_score(y_test, rbf_y_pred)
print()
print('线性SVM模型模型评估:')
print('参数C:', linear_svm.C)
print('分类准确度accuracy: ', linear_accuracy)
print()
print('RBF非线性SVM模型评估:')
print('参数C:{:d},gamma:{:.1f}'.format(rbf_svm.C, rbf_svm.gamma))
print('分类准确度accuracy: ', rbf_accuracy)
# print('真实分类:', list(y_test))
# print('预测分类', list(linear_y_pred))
# 线型模型分类结果展示
plt.figure(figsize=(8, 8))
plt.subplot(2, 2, 1)
plt.scatter(X_test.iloc[:, 0], X_test.iloc[:, 1], c=y_test, cmap='viridis')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('真实散点图')
plt.subplot(2, 2, 2)
plt.scatter(X_test.iloc[:, 0], X_test.iloc[:, 1], c=linear_y_pred, cmap='viridis')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('预测散点图')
plt.subplot(2, 2, 3)
plt.scatter(X_test.iloc[:, 2], X_test.iloc[:, 3], c=y_test, cmap='viridis')
plt.xlabel('Petal length')
plt.ylabel('Petal width')
plt.title('真实散点图')
plt.subplot(2, 2, 4)
plt.scatter(X_test.iloc[:, 2], X_test.iloc[:, 3], c=linear_y_pred, cmap='viridis')
plt.xlabel('Petal length')
plt.ylabel('Petal width')
plt.title('预测散点图')
plt.suptitle('线性模型分类结果对照图')
plt.show()
# rbf非线型模型分类结果展示
plt.figure(figsize=(8, 8))
plt.subplot(2, 2, 1)
plt.scatter(X_test.iloc[:, 0], X_test.iloc[:, 1], c=y_test, cmap='viridis')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('真实散点图')
plt.subplot(2, 2, 2)
plt.scatter(X_test.iloc[:, 0], X_test.iloc[:, 1], c=rbf_y_pred, cmap='viridis')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('预测散点图')
plt.subplot(2, 2, 3)
plt.scatter(X_test.iloc[:, 2], X_test.iloc[:, 3], c=y_test, cmap='viridis')
plt.xlabel('Petal length')
plt.ylabel('Petal width')
plt.title('真实散点图')
plt.subplot(2, 2, 4)
plt.scatter(X_test.iloc[:, 2], X_test.iloc[:, 3], c=rbf_y_pred, cmap='viridis')
plt.xlabel('Petal length')
plt.ylabel('Petal width')
plt.title('预测散点图')
plt.suptitle('rbf非线性模型分类结果对照图')
plt.show()
# 参数调优
print()
print('模型调优')
linear_params = {'C': [0.1, 0.5, 1, 5, 10]}
linear_gridsearch = GridSearchCV(SVC(kernel='linear'), linear_params, scoring='accuracy', cv=5)
linear_gridsearch.fit(X_train, y_train)
print('线性SVM参数调优结果:', linear_gridsearch.best_params_)
linear_svm.C = linear_gridsearch.best_params_['C']
rbf_params = {'C': [0.1, 0.5, 1, 5, 10], 'gamma': [0.001, 0.01, 0.1, 1, 10]}
rbf_gridsearch = GridSearchCV(SVC(kernel='rbf'), rbf_params, cv=5)
rbf_gridsearch.fit(X_train, y_train)
print('RBF非线性SVM参数调优结果:', rbf_gridsearch.best_params_)
rbf_svm.C = rbf_gridsearch.best_params_['C']
rbf_svm.gamma = rbf_gridsearch.best_params_['gamma']
# 模型评估
linear_y_pred = linear_svm.predict(X_test)
linear_accuracy = accuracy_score(y_test, linear_y_pred)
rbf_y_pred = rbf_svm.predict(X_test)
rbf_accuracy = accuracy_score(y_test, rbf_y_pred)
print()
print('参数调优后的模型性能:')
print('C:', linear_svm.C)
print('线性SVM模型准分类确度: ', linear_accuracy)
print('C,gamma:', rbf_svm.C, rbf_svm.gamma)
print('RBF非线性SVM模型准分类确度: ', rbf_accuracy)