机器学习之支持向量机实现与应用

支持向量机

从几何学上更加直观的方法进行求解,如下图所示:

pk7IHat.png

上图展示了支持向量机分类的过程。图中\(wx十b=0\)为分割直线,我们通过这条直线将数据点分开。与此同时,分割时会在直线的两边再设立两个互相平行的虚线,这两条虚线与分割直线的距离一致。这里的距离往往也被我们称之为「间隔」,而支持向量机的分割特点在于,要使得分割直线和虚线之间的间隔最大化。同时也就是两虚线之间的间隔最大化。

对于线性可分的正负样本点而言,位于\(wc十b=1\)虚线外的点就是正样本点,而位于\(wx十b=一1\)虚线外的点就是负样本点。另外,正好位于两条虚线上方的样本点就被我们称为支持向量,这也就是支持向量机的名字来源。

1
2
3
4
5
6
7
8
9
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

%matplotlib inline

x, y = make_blobs(n_samples=60, centers=2, random_state=30, cluster_std=0.8) # 生成示例数据

plt.figure(figsize=(10, 8)) # 绘图
plt.scatter(x[:, 0], x[:, 1], c=y, s=40, cmap="bwr")

make_blobs 是一个在 Python 的 sklearn.datasets 模块中定义的函数,用于生成一个二维或多维的分类数据集,这些数据集由多个圆形或球形的簇组成,通常用于测试和演示聚类算法。下面是函数 make_blobs 的参数解释:

  1. n_samples: 这是一个整数,表示每个中心点(cluster center)周围生成的样本数量。如果提供了一个元组,第一个元素表示每个簇的样本数量,第二个元素表示生成的簇的数量。

  2. centers: 这是一个整数或数组,表示簇的数量。如果是一个整数,表示生成的簇的总数;如果是一个数组,则数组的长度表示簇的数量,数组中的每个元素是一个簇的中心点坐标。

  3. random_state: 这是一个整数或 None,用于控制随机数生成器的种子,确保每次生成的数据集是相同的。如果设置为 None,则每次生成的数据集可能不同。

  4. cluster_std: 这是一个浮点数,表示簇内数据点的标凊差(或方差的标准差)。它决定了簇内数据点的分布范围,值越大,簇内的数据点分布越分散。

使用 make_blobs 函数可以生成一个具有多个簇的数据集,每个簇的数据点围绕一个中心点分布,并且具有一定的随机性。这在机器学习中,特别是在聚类算法的测试和验证中非常有用。

函数间隔与几何间隔在支持向量机中的意义

在支持向量机(Support Vector Machine, SVM)中,函数间隔和几何间隔是衡量数据点与决策边界(即分类面)之间关系的两个重要概念。下面是它们的定义和意义:

  1. 几何间隔(Geometric Margin)
    • 几何间隔是指数据点到决策边界(超平面)的最短距离。对于线性可分的情况,几何间隔是数据点到超平面的垂直距离。
    • 几何间隔是实际的物理距离,可以直观地反映数据点在空间中的位置与决策边界的关系。
    • 几何间隔越大,表示数据点与决策边界的距离越远,模型的分类能力越强,泛化能力越好。
  2. 函数间隔(Functional Margin)
    • 函数间隔是指数据点在决策函数上的值与决策边界的距离。在二元分类问题中,决策函数可以表示为 \(\( f(x) = w \cdot x + b \)\),其中 \(\( w \)\) 是权重向量,$( b ) $是偏置项。
    • 函数间隔计算的是 \(\( y_i (w \cdot x_i + b) \)\) 的值,其中 \(\( y_i \)\) 是第 \(\( i \)\) 个数据点的真实标签,\(\( x_i \)\) 是其特征向量。函数间隔的绝对值越大,表示数据点越容易被正确分类。
    • 函数间隔是相对于权重向量 \(\( w \)\) 的,因此它与模型的参数(权重和偏置)有关。

它们之间的关系和意义: - 几何间隔与函数间隔的关系:几何间隔是函数间隔的缩放版本。具体来说,函数间隔 \(\( y_i (w \cdot x_i + b) \)\) 除以权重向量的范数 \(\( \|w\| \)\) 可以得到几何间隔 \(\( \frac{y_i (w \cdot x_i + b)}{\|w\|} \)\)。 - 优化目标:在SVM的训练过程中,目标是最大化函数间隔,而不是几何间隔。这是因为函数间隔与模型的参数直接相关,而几何间隔则依赖于权重向量的范数,这使得函数间隔更容易在优化过程中控制。 - 泛化能力:函数间隔越大,意味着模型对训练数据的拟合度越高,但也可能意味着过拟合。因此,SVM通过最大化函数间隔来寻找一个平衡点,从而提高模型的泛化能力。 - 决策边界:几何间隔直接决定了决策边界的位置。支持向量是那些几何间隔为零的数据点,它们是距离决策边界最近的点,对决策边界的形成有直接影响。

总结来说,几何间隔和函数间隔在SVM中都是衡量数据点与决策边界关系的重要指标,但它们在优化和泛化能力方面的作用有所不同。SVM的训练过程主要关注最大化函数间隔,以获得更好的泛化效果。

SVM的实现

scikit-learn 中的支持向量机分类器对应的类及参数为:

1
sklearn.svm.SVC(C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape='ovr', random_state=None)

C: 支持向量机中对应的惩罚参数。 kernel: 核函数,linear, poly, rbf, sigmoid, precomputed 可选,下文详细介绍。 degree: poly 多项式核函数的指数。 tol: 收敛停止的容许值。

如下图所示,支持向量机中当训练数据集并非完全线性可分时,这样在保证每个点都被正确分开后会造成过拟合,为了解决过拟合问题,引入惩罚因子 \(\xi\) ,可以看作上面的参数 C ,允许少部分的点出错。在训练集不完全线性可分情况下,我们就要使几何间隔尽可能的大,同时使误分类点的个数尽量小,C 则是调合二者的系数。

pk7o8zD.png

当我们使用支持向量机求解这类问题时,就会把最大间隔称之为最大「软间隔」,而软间隔就意味着可以容许零星噪声数据被误分类。而上文中能将数据完美分开的最大间隔也就被称为「硬间隔」。

这里,我们还是使用上面生成的示例数据训练支持向量机模型。由于是线性可分数据,kernel 参数指定为 linear 即可

线性支持向量机

  • 数据准备
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import random

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

%matplotlib inline

# n_samples: 这是一个整数,表示要生成的样本总数。
# centers: 这是一个整数或数组,表示簇的中心点数量。如果是一个整数,make_blobs 会生成相应数量的簇,每个簇的中心点是随机选择的。
# random_state: 这是一个整数或 None,用于控制随机数生成器的行为,以确保结果的可重复性。如果设置为 None,则每次生成的数据可能不同。
# cluster_std: 这是一个浮点数,表示簇内数据点的标准差。它决定了簇内数据点的分布范围。值越大,簇内的数据点分布越分散;值越小,簇内的数据点越集中。
x, y = make_blobs(n_samples=60, centers=2, random_state=30, cluster_std=0.8) # 生成示例数据

plt.figure(figsize=(10, 8)) # 绘图
plt.scatter(x[:, 0], x[:, 1], c=y, s=40, cmap="bwr")

pk7j0FH.png

想要将两类数据分开,通过可视化我们能够看出我们有很多种直线可以选择

1
2
3
4
5
6
7
8
9
10
11
plt.figure(figsize=(10, 8))
plt.scatter(x[:, 0], x[:, 1], c=y, s=40, cmap="bwr")

# 绘制 3 条不同的分割线
x_temp = np.linspace(0, 6)
for m, b, d in [(1, -8, 0.2), (0.5, -6.5, 0.55), (-0.2, -4.25, 0.75)]:
y_temp = m * x_temp + b
plt.plot(x_temp, y_temp, "-k")
plt.fill_between(x_temp, y_temp - d, y_temp + d, color="#f3e17d", alpha=0.5)
# alpha: 这是一个介于 0 和 1 之间的浮点数,表示填充区域的不透明度。值越接近 1,填充区域越不透明;值越接近 0,填充区域越透明。
# fill_between填充区域:fill_between(x,y下限,y上限)

pk7jBYd.png

使用支持向量机可以是我们找到间距最大的那条直线

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.svm import SVC

linear_svc = SVC(kernel="linear")
linear_svc.fit(x,y)

# 对于训练完成的模型,我们可以通过 support_vectors_ 属性输出它对应的支持向量
# linear_svc.support_vectors_

# 绘制最大间隔支持向量图
plt.figure(figsize=(10, 8))
plt.scatter(x[:, 0], x[:, 1], c=y, s=40, cmap="bwr")
svc_plot(linear_svc)

pk7jsSI.png

  • 可视化支持向量机

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def svc_plot(model):
    # 获取到当前 Axes 子图数据,并为绘制分割线做准备
    ax = plt.gca()
    x = np.linspace(ax.get_xlim()[0], ax.get_xlim()[1], 50)
    y = np.linspace(ax.get_ylim()[0], ax.get_ylim()[1], 50)
    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="green", levels=[-1, 0, 1], linestyles=["--", "-", "--"])

    # 标记出支持向量的位置
    ax.scatter(
    model.support_vectors_[:, 0], model.support_vectors_[:, 1], c="green", s=100
    )

  • 加入噪点

1
2
3
4
5
6
7
8
9
# 向原数据集中加入噪声点
x = np.concatenate((x, np.array([[3, -4], [4, -3.8], [2.5, -6.3], [3.3, -5.8]])))
y = np.concatenate((y, np.array([1, 1, 0, 0])))

linear_svc.fit(x, y) # 训练

plt.figure(figsize=(10, 8))
plt.scatter(x[:, 0], x[:, 1], c=y, s=40, cmap="bwr")
svc_plot(linear_svc)

pk7j66P.png

通过改变参数C的值,有不同的效果

1
2
3
# linear_svc = SVC(kernel="linear",C=1)
# linear_svc = SVC(kernel="linear",C=10000)
# linear_svc = SVC(kernel="linear",C=1000000)

非线性支持向量机

  • 构造数据
1
2
3
4
5
6
from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
x2, y2 = make_circles(150, factor=0.5, noise=0.1, random_state=30) # 生成示例数据

plt.figure(figsize=(8, 8)) # 绘图
plt.scatter(x2[:, 0], x2[:, 1], c=y2, s=40, cmap="bwr")

pk7jWTg.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from mpl_toolkits import mplot3d
from ipywidgets import interact, fixed

def kernel_function(xi, xj):
poly = xi**2 + xj**2
return poly

r = kernel_function(x2[:, 0], x2[:, 1])
plt.figure(figsize=(10, 8))
ax = plt.subplot(projection="3d")
ax.scatter3D(x2[:, 0], x2[:, 1], r, c=y2, s=40, cmap="bwr")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("r")

pk7jhkQ.png

1
2
3
4
5
6
7
8
from sklearn.svm import SVC
rbf_svc = SVC(kernel="rbf", gamma="auto")
rbf_svc.fit(x2, y2)

plt.figure(figsize=(8, 8))
plt.scatter(x2[:, 0], x2[:, 1], c=y2, s=40, cmap="bwr")

svc_plot(rbf_svc)

pk7jopn.png

SVM的应用

支持向量机实现人像分类


机器学习之支持向量机实现与应用
https://fu-jingqi.github.io/2024/07/22/机器学习之支持向量机实现与应用/
作者
coderfjq
发布于
2024年7月22日
许可协议