大家好,今儿和大家分享的是关于 SVM 的内容~
支持向量机(SVM, Support Vector Machine)是一个分类算法,可以帮助我们把数据分成不同的类别。
下面呢,我会用一个简单的例子一步步解释什么是支持向量机,以及它是如何工作的。
需要本文PDF的朋友,可以文末获取~
假设的场景
我们有一群小动物,比如猫和狗,现在我们需要训练一个模型,让它能区分这些动物的照片。我们的任务就是让 SVM 帮我们画一条“线”,把猫和狗正确分开。
怎么分开猫和狗?
首先,我们可以把每只动物的照片转化为一些数据特征,比如:
猫可能更轻一点,狗比较重(可以是“体重”)。 猫的耳朵比较尖,狗的耳朵可能更圆(可以是“耳朵形状”)。
我们把这些特征画在一个二维平面上,每个点代表一只动物。假设猫的点在左边,狗的点在右边,那么我们现在就想找到一条线,把猫和狗分开,这就是 SVM 要做的事情。
1. 什么是支持向量?
在二维平面上,SVM 不仅仅是找一条能分开猫和狗的线,而是要找出最优的线。所谓“最优”,是指这条线离两类(猫和狗)的数据点尽量远。支持向量,就是离那条分割线最近的点,这些点决定了分割线的位置。
2. 什么是间隔?
SVM 会尽可能让线两侧的间隔(Margin)最大化。间隔就是从分割线到最接近的猫或狗的点的距离。如果我们找到的线让猫和狗都能分开,而且这条线到最靠近它的猫和狗的距离是最大的,那这条线就是我们要找的“最优分割线”。
具体步骤总结:
把数据点画在平面上(如体重和耳朵形状)。 找到一条线,分开猫和狗,并且让这条线尽可能远离两类动物。 支持向量就是离线最近的那些点,它们帮助决定了这条线的位置。 最大化间隔,确保我们找的线是最稳健的(即不容易受新数据影响)。
举个简单的例子:
想象一下你在沙滩上看到猫的脚印和狗的脚印。猫的脚印比较轻、比较小,狗的脚印比较重、比较大。你想用一根棍子(分割线)把这些脚印分开。SVM 会帮你找出一根棍子,这根棍子不仅能把脚印分得最清楚,还能离猫脚印和狗脚印都尽量远一些,这样即使来了一些新的脚印,你也能更好地判断它们是猫还是狗留下的。
SVM 的应用场景
SVM 常用于:
图像分类(比如猫狗分类) 文本分类(比如垃圾邮件分类) 生物信息学(比如基因数据分类)
这样,SVM 就是一个非常实用的工具,能帮我们在各种复杂的场景下进行分类工作。
只要记住 SVM 就是在帮我们找到一条线来分开不同的东西,而且它会尽可能让这条线分得稳妥,这样就容易理解了!
我们接下来会详细介绍支持向量机(SVM)的公式推导,然后基于这些公式,创建一个完整的案例,手动实现SVM。
基本原理
SVM 的目标是找到一条超平面,能够将两类数据点分开,同时最大化两类之间的间隔。我们来推导 SVM 的基本公式。
线性可分的情况
我们有一个训练数据集 ,其中 是 d 维特征向量, 是标签(我们假设只有两类)。SVM 的目标是找到一个分割超平面:
其中, 是权重向量, 是偏置项。
对于每一个数据点 ,我们希望:
这保证了数据点 被正确分类,且远离超平面的间隔至少为 1。
最大化间隔
间隔定义为超平面到最近数据点的距离。间隔的大小可以写作:
SVM 的目标是最大化间隔,这等价于最小化 ,因此我们可以得到如下优化问题:
同时满足约束条件:
这是一个凸优化问题,可以通过拉格朗日乘子法来解决。
拉格朗日函数
为了将约束条件加入到优化目标中,我们引入拉格朗日乘子 :
我们对 和 求导,得到拉格朗日对偶问题:
约束条件是:
通过求解这个问题,我们可以得到 ,从而计算出 和 。
完整案例
这里,我们手动实现支持向量机,并使用虚拟数据集进行分类。在代码中,我们将用拉格朗日乘子法求解 SVM。
import numpy as np
import matplotlib.pyplot as plt
# 生成虚拟数据集
np.random.seed(1)
n_points = 1000
X_pos = np.random.randn(n_points, 2) + [2, 2] # 正类 (+1)
X_neg = np.random.randn(n_points, 2) + [-2, -2] # 负类 (-1)
# 合并数据
X = np.vstack((X_pos, X_neg))
y = np.hstack((np.ones(n_points), -1 * np.ones(n_points)))
# 画出初始数据分布
plt.figure(figsize=(8, 6))
plt.scatter(X_pos[:, 0], X_pos[:, 1], color='red', label='Positive (+1)')
plt.scatter(X_neg[:, 0], X_neg[:, 1], color='blue', label='Negative (-1)')
plt.title("Initial Data Distribution")
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
# 定义线性核函数
def linear_kernel(x1, x2):
return np.dot(x1, x2)
# 构建SVM的优化目标函数(拉格朗日乘子法)
def fit_svm(X, y, C=1.0):
n_samples, n_features = X.shape
K = np.zeros((n_samples, n_samples))
for i in range(n_samples):
for j in range(n_samples):
K[i, j] = linear_kernel(X[i], X[j])
P = np.outer(y, y) * K
q = -np.ones(n_samples)
G = np.vstack((-np.eye(n_samples), np.eye(n_samples)))
h = np.hstack((np.zeros(n_samples), C * np.ones(n_samples)))
A = y.reshape(1, -1)
b = np.zeros(1)
# 使用cvxopt求解器求解二次优化问题
from cvxopt import matrix, solvers
P = matrix(P)
q = matrix(q)
G = matrix(G)
h = matrix(h)
A = matrix(A, (1, n_samples), 'd')
b = matrix(b)
solvers.options['show_progress'] = False
solution = solvers.qp(P, q, G, h, A, b)
alphas = np.ravel(solution['x'])
# 计算权重向量w
w = np.sum(alphas[:, None] * y[:, None] * X, axis=0)
# 计算偏置b
sv = (alphas > 1e-5)
b = np.mean(y[sv] - np.dot(X[sv], w))
return w, b, alphas
# 训练SVM
w, b, alphas = fit_svm(X, y)
# 画出支持向量
plt.figure(figsize=(8, 6))
plt.scatter(X_pos[:, 0], X_pos[:, 1], color='red', label='Positive (+1)')
plt.scatter(X_neg[:, 0], X_neg[:, 1], color='blue', label='Negative (-1)')
plt.scatter(X[alphas > 1e-5][:, 0], X[alphas > 1e-5][:, 1], s=100, facecolors='none', edgecolors='yellow',
label='Support Vectors')
# 画出分割超平面
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100), np.linspace(y_min, y_max, 100))
Z = np.dot(np.c_[xx.ravel(), yy.ravel()], w) + b
Z = Z.reshape(xx.shape)
plt.contour(xx, yy, Z, levels=[-1, 0, 1], linestyles=['--', '-', '--'], colors='k')
plt.fill_between(xx[0], y_min, y_max, where=Z[0] > 0, color='red', alpha=0.1)
plt.fill_between(xx[0], y_min, y_max, where=Z[0] < 0, color='blue', alpha=0.1)
plt.title("SVM Decision Boundary and Support Vectors")
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
# 画出拉格朗日乘子的分布
plt.figure(figsize=(8, 6))
plt.plot(np.arange(len(alphas)), alphas, 'ro', label='Lagrange Multipliers')
plt.title("Distribution of Lagrange Multipliers")
plt.xlabel('Data Index')
plt.ylabel('Alpha Value')
plt.legend()
plt.show()
# 分类结果的3D图
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X_pos[:, 0], X_pos[:, 1], alphas[:n_points], color='red', label='Positive (+1)')
ax.scatter(X_neg[:, 0], X_neg[:, 1], alphas[n_points:], color='blue', label='Negative (-1)')
ax.set_title("3D View of Data with Lagrange Multipliers")
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
ax.set_zlabel('Alpha')
ax.legend()
plt.show()
初始数据分布图:展示了正类(红色)和负类(蓝色)数据点的分布情况。
支持向量与分割平面图:展示了通过SVM找到的分割超平面(黑色线),同时标出了支持向量(黄色边框的数据点)。
拉格朗日乘子分布图:展示了每个数据点对应的拉格朗日乘子的值,支持向量的乘子值大于0。
3D图形:通过3D图展示了数据点的特征1、特征2和对应的拉格朗日乘子的分布。
这些图形帮助我们可视化 SVM 是如何工作的,并且展示了支持向量、分类边界和拉格朗日乘子的关系。