一、支持向量机的概述
(一)基本概念
支持向量机(Support Vector Machine,SVM)是一种用于分类和回归的监督学习算法。SVM的目标是找到一个最优边界,将不同类别的数据点分开。在SVM中,我们把数据点看做是n维空间中的点,每个点有n个特征。
SVM的基本概念:
决策边界:在SVM中,我们通过找到一个超平面(或者说是一个线性决策边界),将不同类别的数据点分开。决策边界可以是直线、曲线或者高维空间中的超平面等形式。
支持向量:支持向量是离决策边界最近的样本点,它们对于决策边界的位置起着关键作用。SVM算法的核心思想就是寻找最优的决策边界和支持向量。
间隔:在SVM中,我们希望找到的决策边界能够在两个类别之间留出一个尽可能大的间隔,即最小化所有数据点与决策边界之间的距离。
核函数:SVM可以使用核函数将数据点映射到高维空间中,从而解决非线性可分的问题。常见的核函数包括线性核、多项式核和径向基函数(RBF)核等。
正则化:SVM在求解最优决策边界时,通常会添加一个正则化项,以避免过拟合的问题。
总之,SVM是一种强大的机器学习算法,广泛应用于分类、回归、异常检测等问题中。它的目标是找到一个最优边界,将不同类别的数据点分开,并且在处理非线性可分的问题时,使用核函数将数据点映射到高维空间中。
(二)硬间隔、软间隔和非线性SVM
- 硬间隔(Hard Margin)SVM: 硬间隔SVM是指在数据完全线性可分的情况下,通过寻找一个能够将不同类别样本完全分开的线性决策边界。它的目标是最大化间隔,使得所有数据点都位于决策边界之外,并且不存在任何分类错误。然而,硬间隔SVM对于数据中的噪声和异常点非常敏感,因为它要求所有数据点都必须被正确分类。
- 软间隔(Soft Margin)SVM: 软间隔SVM是在数据存在一定程度的重叠或噪声的情况下使用的。它允许一些样本点位于决策边界错误的一侧,以获得更好的泛化能力。软间隔SVM引入了松弛变量,它允许一些样本点位于决策边界的边缘区域或错误的一侧。目标是找到一个最优的决策边界,同时最小化松弛变量的数量和控制间隔的大小。
- 非线性SVM(Nonlinear SVM): 非线性SVM用于处理数据不是线性可分的情况。它通过引入核函数来将数据映射到高维空间中,使得在高维空间中的数据变得线性可分。常用的核函数包括线性核、多项式核和径向基函数(RBF)核等。非线性SVM可以将数据从原始特征空间映射到更高维的特征空间,并在该空间中寻找一个线性决策边界。
(三)算法思想
找到集合边缘上的若干数据(称为支持向量(Support Vector)),用这些点找出一个平面(称为决策面),使得支持向量到该平面的距离最大。
二、支持向量机求解
(一)基础知识
(1)任意超平面均可用下面的线性方程来表示:
(2)二维空间点 (𝑥, 𝑦)到直线 𝐴𝑥 + 𝐵𝑦 + 𝐶 =0 的距离公式是:
推广到 n 维空间,点到超平面的距离为:
其中,
(二)计算求解
(1)如上图,根据定义我们可以知道,支持向量到超平面的距离为 d ,则其他点到超平面的距离均大于 d 。
对于样本点( x i , y i ),若,则
;若
,则
于是有
暂且令 d=1(之所以令它等于 1,是为了方便推导和优化,且这样做对目标函数的优化没有影响,可以缩放变换),则上式可合并为
两个边界平面的间隔为:
欲找到最大间隔来划分超平面,也就是要找到满足约束的 ω 和 b ,使 γ 最大。即
这等价于最小化,则上式改写为
(2)用拉格朗日乘子法和KKT条件求解最优值:
该问题的拉格朗日函数可写为:
求导
解得
再代入,得
用 SMO 算法求解出 α i ,最终得到超平面
(三)线性支持向量机(软间隔支持向量机)与松弛变量
在实际应用中,我们常常遇到不可完全线性可分的情况,即存在一些噪声或异常点使得无法找到一个完美的线性划分。为了解决这个问题,引入了软间隔支持向量机(Soft Margin Support Vector Machine)。
软间隔支持向量机允许一些样本点位于错误一侧,通过引入松弛变量来实现。松弛变量允许一些样本点跨越决策边界,但会受到惩罚。目标是在最大化间隔的同时,尽量减小误分类的错误。
具体来说,软间隔支持向量机的目标函数由两部分组成:一部分是最小化模型参数的平方范数,使得超平面尽可能拟合训练数据;另一部分是最小化所有样本点的松弛变量与惩罚因子的乘积之和,使得错误分类的样本点尽可能少。
通过调节惩罚因子的大小,可以控制模型对错误分类的容忍程度。当惩罚因子趋近于无穷大时,软间隔支持向量机退化为硬间隔支持向量机,即要求所有样本点都被正确分类。而当惩罚因子较小时,模型会更容忍错误分类,允许一些样本点位于决策边界的错误一侧。
软间隔支持向量机在处理线性不可分问题时具有一定的鲁棒性,并且可以通过调节惩罚因子来平衡模型的复杂度和容错能力。然而,在数据噪声较大或异常点较多的情况下,仍然可能导致模型的过拟合或欠拟合问题,需要结合其他方法进行处理。
(四)线性不可分支持向量机
1、核函数
我们假设训练样本是线性可分的,即存在一个划分超平面能将训练样本正确分类。然而在现实任务中,原始的样本空间内,也许并不存在一个能正确划分两类样本的超平面。比如下面"异或"问题就不是线性可分的:
这时可将样本从原始空间映射到更高维的特征空间,使得样本在这个特征空间内线性可分。例如在上图,若将原始的二维空间映射到一个合适的三维空间,就能找到一个合适的划分超平面。对于任意维度,只要原始样本空间为有限维,那么就一定有一个更高维特征空间使样本可分。
令 Φ(x)表示将 x 映射后的特征向量,于是,在特征空间中划分超平面所对应的模型可表示为:
同理,与上面得式子类似,就是将单个x换为Φ(x)
其对偶问题则是
这里涉及了,这是样本xi和yi映射到特征空间之后得内积,如果遇到了高维空间,那么这个不是那么的好算的,这里就用到了核函数,也就是{\color{Red} k(x_{i},y_{i})=\Phi (x_{i})^{T}\Phi (y_{i}){\color{Red} }}
最终的式子为:
这里便显示出了模型最优解可通过训练样本的核函数展开,这一展式亦称"支持向量展式 "。但问题是xi的映射是什么样的,我们这里还是不能确定Φ(.)形式,我们继续向下看。这里给出一个定理:
只要一个对称函数所对应的核矩阵半正定,它就能作为核函数使用.事实上,对于一个半正定核矩阵,总能找到一个与之对应的映射。换言之,任何一个核函数都隐式地定义了一个称为"再生核希尔伯特空间" (Reproducing Kernel Hilbert Space ,简称 RKHS) 的特征空间.
我们希望样本在特征空间内线性可分,因此特征空间的好坏对支持向量机的性能至关重要。需注意的是,在不知道特征映射的形式时,我们并不知道什么样的核函数是合适的,而核函数也仅是隐式地定义了这个特征空间。于是,"核函数选择"成为支持向量机的最大变数。如果核函数选择不合适,则意味着将样本映射到了一个不太合适的特征空间,它的性能不佳。
在这里,列出了几种常用的核函数。此处之外,函数的组合也是核函数
2、SVM 使用准则
a.SVM 的超参数
𝛾越大,支持向量越少,𝛾值越小,支持向量越多。
C = 1 / λ C=1/\lambdaC=1/λ较大时,相当于𝜆较小,可能会导致过拟合,高方差;
C = 1 / λ C=1/\lambdaC=1/λ较小时,相当于𝜆较大,可能会导致低拟合,高偏差;
𝜎较大时,可能会导致低方差,高偏差;
𝜎较小时,可能会导致低偏差,高方差。
b.SVM 使用准则
n为特征数,m为训练样本数。
如果相较于 m 而言,n 要大许多,即训练集数据量不够支持我们训练一个复杂的非线性模型,我们选用逻辑回归模型或者不带核函数的支持向量机。
如果 n 较小,而且 m 大小中等,例如 n 在 1-1000 之间,而 m 在 10-10000 之间,使用高斯核函数的支持向量机。
如果 n 较小,而 m 较大,例如 n 在 1-1000 之间,而 m 大于 50000,则使用支持向量机会非常慢,解决方案是创造、增加更多的特征,然后使用逻辑回归或不带核函数的支持向量机。
三、Support Vertor Machine 代码实战
(一)支持向量机效果展示
import warnings
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
import os
%matplotlib inline
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
warnings.filterwarnings('ignore')
from sklearn.svm import SVC
from sklearn import datasets
iris = datasets.load_iris()
X = iris['data'][:, (2, 3)]
y = iris['target']
setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]
svm_clf = SVC(kernel='linear', C=float('inf'))
svm_clf.fit(X, y)
# 一般的模型
x0 = np.linspace(0, 5.5, 200)
pred_1 = 5*x0-20
pred_2 = x0 - 1.8
pred_3 = 0.1*x0+0.5
def plot_svc_decision_boundary(svm_clf, xmin, xmax, sv=True):
w = svm_clf.coef_[0]
b = svm_clf.intercept_[0]
x0 = np.linspace(xmin, xmax, 200)
decision_boundary = -w[0]/w[1] * x0 - b/w[1]
margin = 1/w[1]
gutter_up = decision_boundary + margin
gutter_down = decision_boundary - margin
if sv:
svs = svm_clf.support_vectors_
plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')
plt.plot(x0, decision_boundary, 'k-', linewidth=2)
plt.plot(x0, gutter_up, 'k--', linewidth=2)
plt.plot(x0, gutter_down, 'k--', linewidth=2)
plt.figure(figsize=(14, 4))
plt.subplot(121)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'bs')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'ys')
plt.plot(x0, pred_1, 'g--', linewidth=2)
plt.plot(x0, pred_2, 'm-', linewidth=2)
plt.plot(x0, pred_3, 'r-', linewidth=2)
plt.axis([0, 5.5, 0, 2])
plt.subplot(122)
plot_svc_decision_boundary(svm_clf, 0, 5.5)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'bs')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'ys')
plt.axis([0, 5.5, 0, 2])
结论:左图为一般线性模型的分类效果,右图为SVM的分类效果,可见SVM的分类效果更好
(二)软间隔的作用展示
from sklearn.datasets import make_blobs
# 绘图函数
def plot_svc_decision_function(model, ax=None, plot_support=True):
"""Plot the decision function for a 2D SVC"""
if ax is None:
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# create grid to evaluate model
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)
# plot decision boundary and margins
ax.contour(X, Y, P, colors='k',
levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
# plot support vectors
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)
# 构造数据
X, y = make_blobs(n_samples=100, centers=2,
random_state=0, cluster_std=0.8)
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
for axi, C in zip(ax, [10.0, 0.1]):
model = SVC(kernel='linear', C=C).fit(X, y)
axi.scatter(model.support_vectors_[:, 0],
model.support_vectors_[:, 1],
s=250, lw=1, facecolors='#AFAAAA',alpha=0.5)
axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(model, axi)
axi.set_title('C = {0:.1f}'.format(C), size=14)
- 当C趋近于无穷大时:意味着分类严格不能有错误
- 当C趋近于很小的时:意味着可以有更大的错误容忍