支持向量机:寻找最优决策边界的艺术
· 22 min read
"在机器学习的众多算法中,支持向量机就像一位精密的几何学家,总是能找到分离数据的最优边界。" —— 2016年在老虎致远深入研究SVM时的感悟
开篇:一个分类问题的思考
想象你是一位城市规划师,需要在两个敌对社区之间建造一条隔离带。你的目标是:
- 完全分离两个社区
- 隔离带尽可能宽,以减少冲突
- 对未来扩张有良好的泛化能力
这个看似简单的问题,恰好就是支持向量机(SVM)要解决的核心问题:在高维空间中找到最优的分类超平面。
从感知机到支持向量机:历史的演进
感知机的局限性
1957年,Frank Rosenblatt提出了感知机算法,它能找到一个分离线性可分数据的超平面。但问题是:线性可分的数据有无数条分离线,哪一条最好?
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.svm import SVC
from sklearn.linear_model import Perceptron
from sklearn.model_selection import train_test_split
import seaborn as sns
class SVMVisualizationDemo:
def __init__(self):
np.random.seed(42)
self.colors = ['red', 'blue', 'green', 'orange', 'purple']
def compare_decision_boundaries(self):
"""对比不同算法的决策边界"""
# 创建线性可分的2D数据
X, y = make_classification(n_samples=100, n_features=2, n_redundant=0,
n_informative=2, n_clusters_per_class=1,
random_state=42, class_sep=1.5)
# 只取一部分数据,确保线性可分
X = X[:60]
y = y[:60]
# 训练不同的分类器
models = {
'感知机': Perceptron(random_state=42, max_iter=1000),
'SVM': SVC(kernel='linear', C=1.0, random_state=42)
}
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 绘制原始数据
axes[0].scatter(X[y==0, 0], X[y==0, 1], c='red', marker='o', s=50, alpha=0.7, label='类别 0')
axes[0].scatter(X[y==1, 0], X[y==1, 1], c='blue', marker='s', s=50, alpha=0.7, label='类别 1')
axes[0].set_title('原始数据分布', fontsize=14)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[0].set_xlabel('特征 1')
axes[0].set_ylabel('特征 2')
# 创建网格用于绘制决策边界
h = 0.02
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.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
for i, (name, model) in enumerate(models.items()):
ax = axes[i + 1]
# 训练模型
model.fit(X, y)
# 绘制决策边界
if hasattr(model, "decision_function"):
Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
else:
Z = model.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
# 绘制等高线
contour = ax.contour(xx, yy, Z, levels=[0], colors='black', linestyles='--', linewidths=2)
# 如果是SVM,绘制支持向量和边距
if name == 'SVM':
# 绘制边距线
margin_contour = ax.contour(xx, yy, Z, levels=[-1, 1], colors='gray',
linestyles=':', linewidths=1, alpha=0.7)
ax.clabel(margin_contour, inline=True, fontsize=8, fmt='margin')
# 高亮支持向量
support_vectors = model.support_vectors_
ax.scatter(support_vectors[:, 0], support_vectors[:, 1],
s=200, facecolors='none', edgecolors='black', linewidths=2,
label='支持向量')
# 绘制数据点
ax.scatter(X[y==0, 0], X[y==0, 1], c='red', marker='o', s=50, alpha=0.7, label='类别 0')
ax.scatter(X[y==1, 0], X[y==1, 1], c='blue', marker='s', s=50, alpha=0.7, label='类别 1')
ax.set_title(f'{name} 决策边界', fontsize=14)
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlabel('特征 1')
ax.set_ylabel('特征 2')
plt.tight_layout()
plt.show()
# 分析支持向量
svm_model = models['SVM']
print("=== SVM 分析 ===")
print(f"支持向量数量: {len(svm_model.support_vectors_)}")
print(f"总数据点数量: {len(X)}")
print(f"支持向量比例: {len(svm_model.support_vectors_)/len(X):.2%}")
print(f"决策函数系数: {svm_model.coef_}")
print(f"偏置项: {svm_model.intercept_}")
return models
# 演示不同决策边界
demo = SVMVisualizationDemo()
models = demo.compare_decision_boundaries()
SVM的核心思想:最大间隔
1963年,Vladimir Vapnik和Alexey Chervonenkis提出了最大间隔的概念。SVM不仅要找到一个分离超平面,还要找到使类别间距离最大化的超平面。