机器学习中的支持向量机(SVM)
摘要:支持向量机(SVM)是一种监督学习算法,主要用于分类任务。其核心思想是通过寻找最优超平面来最大化不同类别间的间隔距离,其中靠近超平面的关键数据点称为支持向量。SVM通过核技巧处理非线性可分数据,常用核函数包括线性核、多项式核和径向基函数(RBF)核。Python实现中可使用scikit-learn库,需注意参数调优(如正则化参数C和核参数)对模型性能的影响。SVM优势在于高维数据处理能力强、内存效率高,但存在训练耗时、不适用于大规模数据及类别重叠场景的局限性。
目录
什么是支撑矢量机(SVM)
支持向量机(SVM)是一种强大且灵活的监督机器学习算法,既可用于分类,也用于回归。但通常它们主要用于分类问题。SVM最早在20世纪60年代被引入,后来在1990年进行了完善。与其他机器学习算法相比,SVM有其独特的实现方式。如今,它们因能够处理多个连续和类别变量而极受欢迎。
特别监测仪的工作原理
SVM的目标是找到一个将数据点分为不同类别的超平面。超平面是二维空间中的一条直线、三维空间中的平面,或n维空间中的高维曲面。选择超平面的方式是为了最大化空间,即超平面与每个类别中最近数据点之间的距离。最近的数据点称为支撑向量。
超平面与数据点“x”之间的距离可以用公式 − 计算
distance = (w . x + b) / ||w||
其中“w”是权重向量,“b”是偏置项,“||w||”是权重向量的欧氏范数。权重向量“w”垂直于超平面,确定其方向,而偏置项“b”决定其位置。
最优超平面是通过求解一个优化问题来求得的,该问题是在所有数据点都正确分类的约束条件下最大化边际。换句话说,我们希望找到一个能最大化两类间距的超平面,同时确保没有数据点被错误分类。这是一个凸优化问题,可以通过二次规划求解。
如果数据点不可线性分离,我们可以使用一种称为核技巧的技术,将数据点映射到更高维空间,使其变得可分。核函数计算映射数据点之间的内积,而无需计算映射本身。这使得我们可以在高维空间中处理数据点,而无需支付映射的计算成本。
让我们通过以下图表详细理解它 −

以下是SVM中的重要概念 −
-
支持向量 − 最接近超平面的数据点称为支持向量。分隔线将借助这些数据点来定义。
-
超平面 − 如上图所示,它是一个决策平面或空间,被一组具有不同类的对象分割。
-
裕度 − 它可以定义为不同类别的近距离数据点两行之间的间隙。它可以计算为直线到支撑矢量之间的垂直距离。大边际被视为良好边际,小边际被视为坏边际。
使用 Python 实现 SVM
在 Python 中实现 SVM,我们将从以下标准库导入开始 −
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns; sns.set()
接下来,我们将从sklearn.dataset.sample_generator创建一个线性可分数据的样本数据集,用于使用SVM−进行分类。
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.50)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='summer');
生成包含100个样本和2个簇的样本集后的输出如下 −

我们知道SVM支持判别分类。对于二维,它通过简单地将类别划分为一条线,在多维情况下选择流形。在上述数据集上实现方式如下 −
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='summer')
plt.plot([0.6], [2.1], 'x', color='black', markeredgewidth=4, markersize=12)
for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
plt.plot(xfit, m * xfit + b, '-k')
plt.xlim(-1, 3.5);
输出如下 −

从上述输出中可以看到,有三种不同的分离器能够完美区分上述样本。
如前所述,SVM的主要目标是将数据集划分为多个类,以找到最大边际超平面(MMH),因此我们不再在类别之间画一条零线,而是在每条线周围画一个宽度的边距,直到最近的一点。具体步骤如下 −
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='summer')
for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
yfit = m * xfit + b
plt.plot(xfit, yfit, '-k')
plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
color='#AAAAAA', alpha=0.4)
plt.xlim(-1, 3.5);

从上述输出图中,我们可以轻松观察到判别分类器中的“边际”。SVM会选择最大化裕量的线。
接下来,我们将使用Scikit-Learn的支持向量分类器,基于这些数据训练SVM模型。这里,我们使用线性核来拟合SVM,如下 −
from sklearn.svm import SVC # "Support vector classifier"
model = SVC(kernel='linear', C=1E10)
model.fit(X, y)
输出如下 −
SVC(C=10000000000.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
kernel='linear', max_iter=-1, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False)
现在,为了更好地理解,下面将绘制二维SVC−的判定函数图。
def decision_function(model, ax=None, plot_support=True):
if ax is None:
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
为了评估模型,我们需要创建如下网格 −
x = np.linspace(xlim[0], xlim[1], 30)
y = np.linspace(ylim[0], ylim[1], 30)
Y, X = np.meshgrid(y, x)
xy = np.vstack([X.ravel(), Y.ravel()]).T
P = model.decision_function(xy).reshape(X.shape)
接下来,我们需要绘制决策边界和边际,如下 −
ax.contour(X, Y, P, colors='k',
levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
现在,同样地绘制支撑向量如下 −
if plot_support:
ax.scatter(model.support_vectors_[:, 0],
model.support_vectors_[:, 1],
s=300, linewidth=1, facecolors='none');
ax.set_xlim(xlim)
ax.set_ylim(ylim)
现在,利用该函数拟合我们的模型如下 −
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='summer')
decision_function(model);

从上述输出可以观察到,SVM分类器拟合到带有边距的数据,即虚线和支撑矢量,这些拟合的关键元素都与虚线相连。这些支持向量点存储在分类器的support_vectors_属性中,具体如下 −
model.support_vectors_
输出如下 −
array([[0.5323772 , 3.31338909], [2.11114739, 3.57660449], [1.46870582, 1.86947425]])
SVM内核
实际上,SVM算法通过核实现,将输入数据空间转换为所需形式。SVM使用一种称为核技巧的技术,核将一个低维输入空间转化为更高维空间。简单来说,核通过增加维度将不可分问题转换为可分问题。它使SVM更强大、更灵活且更准确。以下是SVM−所使用的一些核类型
线性核
它可以作为任意两个观测值之间的点积使用。线性核的公式如下 −
k(x,xi) = sum(x*xi)
从上述公式可以看出,两个向量的乘积是每对输入值相乘的和。
多项式核
它是更广义的线性核形式,区分了曲线输入空间和非线性输入空间。以下是多项式核−的公式
K(x, xi) = 1 + sum(x * xi)^d
这里 d 是多项式的次数,需要在学习算法中手动指定。
径向基函数(RBF)核
RBF核主要用于SVM分类,将输入空间映射到无限维空间中。按照公式在数学上解释 −
K(x,xi) = exp(-gamma * sum((x xi^2))
这里的伽马值范围从0到1。我们需要在学习算法中手动指定它。伽马的良好默认值是0.1。
我们为线性可分数据实现了SVM,也可以用Python实现非线性可分数据。这可以通过使用核来实现。
示例
以下是使用核创建SVM分类器的示例。我们将使用scikit-learn的虹膜数据集——
我们将从导入以下包开始 −
import pandas as pd
import numpy as np
from sklearn import svm, datasets
import matplotlib.pyplot as plt
现在,我们需要加载输入数据 −
iris = datasets.load_iris()
从该数据集中,我们取前两个特征如下 −
X = iris.data[:, :2]
y = iris.target
接下来,我们将用原始数据绘制出 SVM 边界,如下 −
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
h = (x_max / x_min)/100
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
X_plot = np.c_[xx.ravel(), yy.ravel()]
现在,我们需要给出正则化参数的值如下 −
C = 1.0
接下来,可以创建 SVM 分类器对象如下 −
Svc_classifier = svm.SVC(kernel='linear', C=C).fit(X, y)
Z = svc_classifier.predict(X_plot)
Z = Z.reshape(xx.shape)
plt.figure(figsize=(15, 5))
plt.subplot(121)
plt.contourf(xx, yy, Z, cmap=plt.cm.tab10, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.xlim(xx.min(), xx.max())
plt.title('Support Vector Classifier with linear kernel')
输出
Text(0.5, 1.0, 'Support Vector Classifier with linear kernel')

为了创建带有rbf核的SVM分类器,我们可以将核改为rbf,具体如下 −
Svc_classifier = svm.SVC(kernel='rbf', gamma ='auto',C=C).fit(X, y)
Z = svc_classifier.predict(X_plot)
Z = Z.reshape(xx.shape)
plt.figure(figsize=(15, 5))
plt.subplot(121)
plt.contourf(xx, yy, Z, cmap=plt.cm.tab10, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.xlim(xx.min(), xx.max())
plt.title('Support Vector Classifier with rbf kernel')
输出
Text(0.5, 1.0, 'Support Vector Classifier with rbf kernel')

我们把伽马值设为“自动”,但你也可以在0到1之间设置。
SVM参数调优
实际上,SVM通常需要调整参数以实现最佳性能。最重要的参数是核、正则化参数C和核特定的参数。
核参数决定了应使用的核类型。最常见的核类型包括线性核、多项式核、径向基函数(RBF)和S形核。线性核用于线性可分数据,而其他核用于非线性可分数据。
正则化参数C控制着最大化裕度与最小化分类误差之间的权衡。C值越高,分类器会以减少边缘为代价来最小化分类误差;C值越低,分类器即使误判更多,也会尽量最大化误差。
核特定的参数取决于所使用的核类型。例如,多项式核包含多项式次数和系数参数,而RBF核则有高斯函数宽度参数。
我们可以利用交叉验证来调整SVM的参数。交叉验证涉及将数据拆分为若干子集,并在每个子集上训练分类器,同时使用剩余子集进行测试。这使我们能够评估分类器在不同数据子集上的表现,并选择最佳参数集。
示例
# define the parameter grid
param_grid = {
'C': [0.1, 1, 10, 100],
'kernel': ['linear', 'poly', 'rbf', 'sigmoid'],
'degree': [2, 3, 4],
'coef0': [0.0, 0.1, 0.5],
'gamma': ['scale', 'auto']
}
# create an SVM classifier
svm = SVC()
# perform grid search to find the best set of parameters
grid_search = GridSearchCV(svm, param_grid, cv=5)
grid_search.fit(X_train, y_train)
# print the best set of parameters and their accuracy
print("Best parameters:", grid_search.best_params_)
print("Best accuracy:", grid_search.best_score_)
我们首先从scikit-learn导入GridSearchCV模块,该工具用于对一组参数进行网格搜索。我们定义一个参数网格,包含每个参数的可能值。
我们用SVC()创建SVM分类器,然后连同参数网格和交叉验证折叠次数(cv=5)一起传递给GridSearchCV。然后调用 grid_search.fit(X_train, y_train) 进行网格搜索。
网格搜索完成后,我们分别用 grid_search.best_params_ 和 grid_search.best_score_ 打印最佳参数及其准确性。
输出
执行该程序时,你会得到以下输出 −
Best parameters: {'C': 0.1, 'coef0': 0.5, 'degree': 3, 'gamma': 'scale', 'kernel': 'poly'}
Best accuracy: 0.975
这意味着网格搜索找到的最佳参数集合为:C=0.1, coef0=0.5, degree=3, gamma=scale, and kernel=poly。这组参数在训练集上的准确率为97.5%。
你现在可以用这些参数创建新的SVM分类器,并在测试集上测试其性能。
SVM分类器的优缺点
SVM分类器的优点
SVM分类器具有极高的精度,并且在高维空间中表现良好。SVM分类器基本上使用训练点的子集,因此结果中对内存的消耗非常少。
SVM分类器的缺点
它们训练时间长,因此实际上不适合处理大型数据集。另一个缺点是SVM分类器在重叠类中表现不佳。
原文地址:https://blog.csdn.net/zl811103/article/details/156572805
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!
