Machine Learning With Python 简明教程

Support Vector Machine (SVM)

Introduction to SVM

支持向量机 (SVM) 功能强大且灵活,是监督机器学习算法,可同时用于分类和回归。但一般而言,它们用于分类问题。SVm 于 1960 年代首次推出,但后来在 1990 年得到了改进。与其他机器学习算法相比,SVM 具有独特的实现方式。近年来,由于其处理多个连续变量和分类变量的能力而备受青睐。

Working of SVM

SVM 模型本质上是对多维空间中超平面中不同类别的表示。SVM 会以迭代方式生成超平面,以便最大限度地减少错误。SVM 的目标是将数据集划分为不同的类别,以找到最大边缘超平面 (MMH)。

margin

SVM 中的重要概念包括以下几个 −

  1. Support Vectors - 最接近超平面的数据点称为支持向量。将利用这些数据点定义分隔线。

  2. Hyperplane − 如以上图表所示,它是一个决策平面或空间,由一组具有不同类别的对象划分。

  3. Margin - 它可以定义为不同类最近数据点上的两条线之间的间隙。它可以计算为从该线到支持向量的垂直距离。大间隔被认为是好间隔,小间隔被认为是坏间隔。

SVM 的主要目标是将数据集划分为不同类别,以找到最大边缘超平面 (MMH),可以分两步完成 −

  1. 首先,SVM 将以迭代方式生成超平面,以最佳方式区分类别。

  2. 然后,它将选择正确划分类别的超平面。

Implementing SVM in Python

我们在 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.samples_generator 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 个簇的样本集后,输出如下 −

map

我们知道 SVM 支持判别分类。它通过在二维情况下简单地找到一条线或在多维情况下找到一个流形来区分不同的类。在上述数据集中实现 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);

输出如下 −

cross

从上述输出中我们可以看到,有三个不同的分隔符完美地区分了上述样本。

如前所述,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);
green

从输出中的上述图像中,我们很容易观察到判别分类器中的“边缘”。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);
yellow

我们可以从上述输出中观察到,一个 SVM 分类器适合具有裕度的数据,即虚线和支持向量,这是该拟合的关键元素,触及虚线。这些支持向量点存储在分类器的 support_vectors_ 属性中,如下所示——

model.support_vectors_

输出如下 −

array([[0.5323772 , 3.31338909],
   [2.11114739, 3.57660449],
   [1.46870582, 1.86947425]])

SVM Kernels

在实践中,SVM 算法使用将输入数据空间转换为所需形式的核来实现。SVM 使用称为核技巧的技术,其中核采用低维输入空间并将其转换为更高维的空间。简单来说,核通过向其中添加更多维度将不可分离的问题转换为可分离问题。它使 SVM 功能更强大、更灵活、更准确。以下是 SVM 使用的一些内核类型——

Linear Kernel

它可用作任意两个观测之间的点积。线性核的公式如下——

k(x,xi) = sum(x*xi)

从上述公式中,我们可以看到两个向量(例如 𝑥 和 𝑥𝑖)之间的乘积是输入值每对乘积的总和。

Polynomial Kernel

它是线性核的更通用形式,并且区分曲线或非线性输入空间。以下是多项式核的公式——

K(x, xi) = 1 + sum(x * xi)^d

其中 d 是多项式的度数,我们需要在学习算法中手动指定它。

Radial Basis Function (RBF) Kernel

RBF 核主要用于 SVM 分类,它将输入空间映射到无限维空间。以下公式在数学上对它进行了说明——

K(x,xi) = exp(-gamma * sumx – xi^2

在此,gamma 范围从 0 到 1。我们需要在学习算法中手动指定它。gamma 一个好的默认值为 0.1。

由于我们为线性可分离数据实现了 SVM,所以我们可以使用 Python 针对不可线性分离的数据实现它。可以通过使用核函数来实现这一点。

Example

下面是使用核函数创建 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')

Output

Text(0.5, 1.0, 'Support Vector Classifier with linear kernel')
curve

对于使用 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')

Output

Text(0.5, 1.0, 'Support Vector Classifier with rbf kernel')
classifier

我们将 gamma 值设置为“自动”,但是你也可以提供 0 到 1 之间的值。

Pros and Cons of SVM Classifiers

Pros of SVM classifiers

SVM 分类器提供极高的准确度,并且在高维度空间中表现良好。SVM 分类器基本上使用训练点的子集,因此实际上使用非常少的内存。

Cons of SVM classifiers

它们的训练时间很长,因此在实践中不适用于大型数据集。另一个缺点是 SVM 分类器与重叠类别不匹配。