哈喽,我是cos大壮 !~
今儿和一位同学聊到了SVM,那么,这篇文章详细的再和大家聊聊这个比较基础的算法~
支持向量机 (SVM) 是一种用于分类和回归的机器学习算法。虽然名字听起来复杂,但通过一个简单的例子来解释就容易理解多了。
比如,你是学校的篮球队教练,现在要从班里挑选哪些同学适合打篮球。
你有两类同学:一类是「打篮球的」,另一类是「不打篮球的」。 你的目标是根据每个同学的身高和臂展,找出适合打篮球的同学。
这样大家可以画图看看:
你可以把每个同学的身高和臂展当作坐标,画在一个二维平面上。 「打篮球的」同学可以用红色点表示,「不打篮球的」同学用蓝色点表示。 你的任务是找一条线,把这两类同学分开。
文末可取本文PDF版本~
SVM怎么工作?
支持向量机会尝试找到一条最佳的线,不仅把两类同学分开,还希望这条线与两类同学的点之间有尽量大的距离。这样,当你遇到一个新同学时,只需要看他是在这条线的哪一侧,就可以判断他是否适合打篮球。
详细步骤:
画线:SVM 试图在「打篮球的」同学和「不打篮球的」同学之间画一条线。这条线叫做「决策边界」。
支持向量:有些同学站得离这条线非常近,他们的位置会影响这条线的走向。SVM 会重点关注这些离线最近的点,这些点就是「支持向量」。线的位置和方向是由这些支持向量决定的。
最大化间隔:SVM 不仅画线,还会确保线和最靠近的同学之间的距离(也就是「间隔」)最大。这能让分类更加稳定和准确。
总的来说,支持向量机的任务就是找到一条最佳的线,把「打篮球的」和「不打篮球的」同学分开,并且确保这条线离两类人都尽量远。这样,未来如果来了新同学,你可以很容易根据他的位置判断他是否适合打篮球。
有这些基础的解释后,再和大家详细聊聊其原理和案例。
支持向量机公式推理
支持向量机(SVM)的核心思想是通过找到一条「最佳分割线」来将不同类别的数据点分开,这条分割线不仅将两类数据分开,还尽量让两类数据离这条线的距离最大。
我们通过详细推导 SVM 的公式来解释其中的数学原理。
1. 超平面定义
假设数据点有两类,分别为 (正类)和 (负类)。我们需要找到一个分割这两类数据的超平面:
其中:
是超平面的法向量,决定超平面的方向; 是偏置,决定超平面到原点的距离; 是数据点的特征向量。
2. 分类条件
假设对于正类的点 ,我们希望满足:
对于负类的点 ,希望满足:
因此,分类的条件可以写成:
这个不等式就是支持向量机的分类条件。
3. 最大化间隔
支持向量机的关键是要最大化间隔,即使得正类和负类的点离超平面的距离尽可能大。间隔的公式为:
因此,我们的目标是最大化间隔,即最小化 。综合分类条件和最大化间隔,SVM 的优化问题可以写为:
同时满足约束条件:
4. 拉格朗日对偶问题
为了求解这个优化问题,使用拉格朗日乘子法。定义拉格朗日函数:
其中, 是拉格朗日乘子。通过对 和 求偏导,并将其设为 0,可以得到对偶问题的公式:
约束条件为:
通过求解这个对偶问题,找到最优的 后,超平面可以表示为:
完整案例
这里我们基于一个Kaggle数据集来展示如何使用支持向量机进行分类,同时通过多个图形来进行数据分析和结果可视化。
数据集选择
我们可以选择Kaggle上的Breast Cancer Wisconsin (Diagnostic) Data Set,这是一个非常经典的二分类数据集,适合用于支持向量机的演示。
数据集获取:后台回复「数据集」即可获取~
分析步骤
数据预处理:首先加载数据并进行基本的清理和预处理。 数据可视化:通过多个图形来分析特征之间的关系以及数据的分布。 支持向量机实现:根据推导的公式,手动实现支持向量机的分类过程。 结果分析和可视化:通过图形展示分类结果、支持向量、决策边界等。
这里,咱们手动实现一下SVM,大家利用上面原理和代码进行理解~
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# 加载Kaggle数据集(乳腺癌数据)
data = pd.read_csv('data.csv')
data = data.dropna(axis=1) # 删除有缺失值的列
data['diagnosis'] = data['diagnosis'].map({'M': 1, 'B': -1}) # 将分类标签转换为 +1 和 -1
# 提取特征和标签
X = data[['radius_mean', 'texture_mean']].values # 简化为2维特征
y = data['diagnosis'].values
# 特征之间的散点图
plt.figure(figsize=(8,6))
plt.scatter(X[y == 1][:, 0], X[y == 1][:, 1], color='r', label='Malignant')
plt.scatter(X[y == -1][:, 0], X[y == -1][:, 1], color='b', label='Benign')
plt.title('Scatter plot of Features')
plt.xlabel('Radius Mean')
plt.ylabel('Texture Mean')
plt.legend()
plt.show()
# 每个特征的直方图
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.hist(data['radius_mean'], bins=20, color='c', edgecolor='k')
plt.title('Histogram of Radius Mean')
plt.xlabel('Radius Mean')
plt.ylabel('Frequency')
plt.subplot(1, 2, 2)
plt.hist(data['texture_mean'], bins=20, color='m', edgecolor='k')
plt.title('Histogram of Texture Mean')
plt.xlabel('Texture Mean')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()
# SVM实现
class SVM:
def __init__(self, learning_rate=0.001, lambda_param=0.01, n_iters=1000):
self.lr = learning_rate
self.lambda_param = lambda_param
self.n_iters = n_iters
self.w = None
self.b = None
def fit(self, X, y):
n_samples, n_features = X.shape
y_ = np.where(y <= 0, -1, 1)
self.w = np.zeros(n_features)
self.b = 0
for _ in range(self.n_iters):
for idx, x_i in enumerate(X):
condition = y_[idx] * (np.dot(x_i, self.w) - self.b) >= 1
if condition:
self.w -= self.lr * (2 * self.lambda_param * self.w)
else:
self.w -= self.lr * (2 * self.lambda_param * self.w - np.dot(x_i, y_[idx]))
self.b -= self.lr * y_[idx]
def predict(self, X):
approx = np.dot(X, self.w) - self.b
return np.sign(approx)
# 训练模型
svm = SVM()
svm.fit(X, y)
# SVM分类结果的决策边界图
def plot_decision_boundary(X, y, model):
def get_hyperplane_value(x, w, b, offset):
return (-w[0] * x + b + offset) / w[1]
plt.figure(figsize=(8,6))
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)
x0_1 = np.amin(X[:, 0])
x0_2 = np.amax(X[:, 0])
x1_1 = get_hyperplane_value(x0_1, model.w, model.b, 0)
x1_2 = get_hyperplane_value(x0_2, model.w, model.b, 0)
x1_1_m = get_hyperplane_value(x0_1, model.w, model.b, -1)
x1_2_m = get_hyperplane_value(x0_2, model.w, model.b, -1)
x1_1_p = get_hyperplane_value(x0_1, model.w, model.b, 1)
x1_2_p = get_hyperplane_value(x0_2, model.w, model.b, 1)
plt.plot([x0_1, x0_2], [x1_1, x1_2], "k--")
plt.plot([x0_1, x0_2], [x1_1_m, x1_2_m], "r--")
plt.plot([x0_1, x0_2], [x1_1_p, x1_2_p], "b--")
plt.title('SVM Decision Boundary')
plt.xlabel('Radius Mean')
plt.ylabel('Texture Mean')
plt.show()
plot_decision_boundary(X, y, svm)
# 标注支持向量
def plot_support_vectors(X, y, model):
# 找到支持向量
decision_function = np.dot(X, model.w) - model.b
support_vector_indices = np.where(np.abs(decision_function) < 1 + 1e-5)[0]
plt.figure(figsize=(8,6))
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y, label="Data Points")
# 标注支持向量
plt.scatter(X[support_vector_indices][:, 0], X[support_vector_indices][:, 1],
color='y', edgecolor='k', s=200, marker='o', label='Support Vectors')
plt.title('Support Vectors Visualization')
plt.xlabel('Radius Mean')
plt.ylabel('Texture Mean')
plt.legend()
plt.show()
plot_support_vectors(X, y, svm)
图形1:特征之间的散点图展示了不同类别数据的分布,红色表示恶性肿瘤,蓝色表示良性肿瘤。这个图形帮助我们了解两类数据在特征空间中的分布情况。
图形2:特征直方图分别展示了 radius_mean
和 texture_mean
特征的分布情况。这个图形展示了每个特征的取值范围和频率,帮助我们了解特征的分布是否有偏差。
图形3:SVM 分类结果的决策边界图展示了支持向量机对数据的分类效果。黑色的虚线是决策边界,两侧的红色和蓝色虚线分别代表支持向量间隔。这个图形清晰地展示了分类的边界和不同类别数据的分布。
图形4:支持向量可视化图标注了哪些数据点是支持向量(用黄色标记)。这些点是支持向量机分类决策中最重要的点,定义了超平面的位置和方向。
最后
大家有问题可以直接在评论区留言即可~
喜欢本文的朋友可以收藏、点赞、转发起来!
推荐阅读
原创、超强、精华合集 100个超强机器学习算法模型汇总 机器学习全路线 机器学习各个算法的优缺点 7大方面,30个最强数据集 6大部分,20 个机器学习算法全面汇总 铁汁,都到这了,别忘记点赞呀~