机器学习之支持向量机SVM详解

摘要: 支持向量机(Support Vector Machine,SVM)是一种经典的监督学习算法,主要用于分类和回归任务。SVM的核心思想是在特征空间中寻找一个最大间隔的分类超平面,使得两个不同类别的样本点之间的间隔最大化,从而获得更强的泛化能力。当数据线性不可分时,SVM通过核函数将数据映射到高维空间,在高维空间中寻找最优分类超平面。本文详细介绍了SVM的原理、核函数机制、对偶问题与SMO算法,并提供了完整的Python代码示例,涵盖线性SVM与RBF核SVM对比、不同核函数性能对比、网格搜索调优以及决策边界可视化,帮助读者从理论到实践全面掌握SVM。

关键词: 支持向量机;SVM;核函数;最大间隔分类器;SMO算法;scikit-learn


1. 引言

支持向量机由Vladimir N. Vapnik和Alexey Ya. Chervonenkis于1963年提出,是机器学习领域最具影响力的算法之一。SVM在文本分类、图像识别、生物信息学、药物发现等众多领域都有着广泛应用。其之所以备受青睐,是因为它具有以下突出优点:泛化能力强 ,理论基础扎实(VC维理论、结构风险最小化);在小样本、高维数据上表现优异;通过核函数可以高效处理非线性问题。

本文将系统讲解SVM的理论基础,包括最大间隔分类器、核函数机制、对偶问题与SMO算法,并结合scikit-learn库给出完整的Python实战代码。


2. SVM原理

2.1 最大间隔分类器

SVM的基本思想可以从二维空间中的二分类问题直观理解。假设有两类样本点,SVM的目标是找到一条分类直线(或高维空间中的分类超平面),使得两类样本分别位于直线的两侧,并且距离直线最近的点到直线的距离最大。这个距离最大化的分类边界,就是SVM所寻找的最优解。

为什么间隔最大化的分类器具有更强的泛化能力?直观上理解:如果分类边界与各类样本都保持较大的距离,那么当新样本出现少量扰动时,被误分类的概率就会大大降低。从统计学习理论的角度,最大间隔分类器实际上是在控制模型的复杂度,使得模型在训练集上的表现与泛化能力之间取得最佳平衡。

2.2 决策边界与支持向量

在SVM中,分类超平面可以用以下方程表示:

复制代码
w·x + b = 0

其中 w 是权重向量(垂直于超平面),b 是偏置项。对于任意一个样本点 x_i,其到超平面的几何间隔为:

复制代码
γ_i = y_i (w·x_i + b) / ||w||

其中 y_i ∈ {+1, -1} 是样本的真实标签。

支持向量(Support Vectors) 是那些恰好落在最大间隔边界上的样本点。这些点在数学上满足:

复制代码
y_i (w·x_i + b) = 1

换句话说,只有支持向量对分类超平面的确定起作用,其他所有样本点(位于间隔带之外)对超平面没有任何影响。这使得SVM具有稀疏性------训练完成后,大部分训练样本可以被丢弃,分类决策仅依赖于少数支持向量。

2.3 硬间隔与软间隔

硬间隔SVM(Hard Margin SVM) 要求所有样本点都严格位于间隔带之外,即所有样本都满足 y_i (w·x_i + b) ≥ 1。硬间隔SVM只适用于线性可分的数据,即存在一个超平面能够完美分开两类样本。

然而,现实世界中的数据往往不是线性可分的,或者为了追求更强的泛化能力,需要容忍少量样本违反间隔约束。为此,软间隔SVM(Soft Margin SVM) 引入了松弛变量(slack variable) ξ_i,对每个样本允许一定程度的违规:

复制代码
y_i (w·x_i + b) ≥ 1 - ξ_i
ξ_i ≥ 0

软间隔SVM的优化目标变为:

复制代码
min (1/2)||w||² + C · Σ ξ_i

其中 C 是一个正则化参数,控制对违规样本的惩罚力度:

  • C 值很大:近似硬间隔,对违规样本惩罚很重,模型趋近于线性可分的情况,容易过拟合

  • C 值很小:允许更多违规样本,间隔更宽,模型更"软",容易欠拟合

复制代码
# 软间隔SVM示意代码
from sklearn.svm import SVC
import numpy as np
​
# 创建软间隔SVM分类器(C=1.0为默认值)
# 当数据近似线性可分时,C值的选择会影响模型的泛化能力
svm_soft = SVC(kernel='linear', C=1.0)

3. 核函数

3.1 为什么需要核函数

现实中的数据往往不是线性可分的,例如XOR问题(两类样本呈对角线分布)就无法用一条直线分开。核函数(Kernel Function)的核心思想是:将原始特征空间通过一个非线性映射 Φ 变换到更高维甚至无限维的空间,在那个空间中数据变得线性可分

核函数的数学定义为:对于映射 Φ,存在函数 K(x, z) 使得

复制代码
K(x, z) = Φ(x) · Φ(z)

K(x, z) 等于两个样本在高维空间中映射向量的点积。核函数的优势在于:我们无需显式计算高维映射 Φ,直接在原始空间通过核函数计算高维空间中的内积,这被称为"核技巧"(Kernel Trick)。

3.2 常见核函数

3.2.1 线性核(Linear Kernel)
复制代码
K(x, z) = x · z

线性核是最简单的核函数,本质上是不做任何变换,适用于数据本身就已经线性可分或特征维度非常高的场景(如文本分类)。

复制代码
# 线性核SVM代码示例
from sklearn.svm import SVC
​
# 线性核(等价于不引入核技巧)
svm_linear = SVC(kernel='linear', C=1.0)
3.2.2 多项式核(Polynomial Kernel)
复制代码
K(x, z) = (γ · x · z + r)^d

多项式核将数据映射到 d 维多项式空间。其中:

  • γ(gamma):控制单个样本的影响范围

  • r(coef0):独立项系数

  • d(degree):多项式的度数

复制代码
# 多项式核SVM代码示例
from sklearn.svm import SVC
​
# 多项式核,度数=3,gamma=1,coef0=0
svm_poly = SVC(kernel='poly', degree=3, gamma=1.0, coef0=0, C=1.0)
3.2.3 径向基核函数(RBF/Gaussian Kernel)
复制代码
K(x, z) = exp(-γ · ||x - z||²)

RBF核(也称高斯核)是应用最广泛的核函数。它将每个样本映射到无穷维空间,具有局部特性------只有距离接近的样本才会有较大的核函数值。

参数 γ 控制高斯核的"宽度":

  • γ 很大:每个样本的影响范围很小,模型复杂度高,容易过拟合

  • γ 很小:每个样本的影响范围很大,模型趋近于线性

复制代码
# RBF核SVM代码示例
from sklearn.svm import SVC
​
# RBF核(最常用),gamma='scale'为默认值
svm_rbf = SVC(kernel='rbf', gamma='scale', C=1.0)
3.2.4 Sigmoid核
复制代码
K(x, z) = tanh(γ · x · z + r)

Sigmoid核来源于神经网络中的激活函数,又称双曲正切核。使用Sigmoid核的SVM等价于一个两层的感知机。

复制代码
# Sigmoid核SVM代码示例
from sklearn.svm import SVC
​
# Sigmoid核
svm_sigmoid = SVC(kernel='sigmoid', gamma='scale', coef0=0, C=1.0)

3.3 核函数选择指南

核函数 适用场景 参数复杂度 备注
线性核 高维稀疏数据(如文本分类)、样本数远大于特征数 首选,尤其推荐用于文本
RBF核 通用场景,特征维度不太高,样本数适中 默认首选,效果好
多项式核 需要考虑特征交互的场景 高(3个参数) degree过大会导致数值不稳定
Sigmoid核 近似模拟神经网络 性能通常不如RBF核

经验法则:首先尝试线性核(尤其是文本数据)。如果线性核效果不好,再用RBF核。对于图像等结构化数据,可以尝试多项式核。RBF核的参数通常通过网格搜索来优化。


4. 对偶问题与SMO算法

4.1 拉格朗日乘数法

SVM的优化问题属于约束优化问题。原始优化目标为:

复制代码
min_{w, b, ξ}  (1/2)||w||² + C · Σ ξ_i
s.t.  y_i(w·x_i + b) ≥ 1 - ξ_i,  ξ_i ≥ 0

通过拉格朗日乘数法引入乘子 αi ≥ 0 和 β i ≥ 0,可将约束问题转化为无约束的拉格朗日函数,然后求偏导并令其为零,得到原问题的对偶问题

复制代码
max_α  Σ α_i - (1/2) Σ Σ α_i α_j y_i y_j K(x_i, x_j)
s.t.  0 ≤ α_i ≤ C
       Σ α_i y_i = 0

可以看到,对偶问题只涉及样本之间的核函数值 K(x_i, x_j),而不显式需要特征维数,这正是核技巧得以实施的关键。

4.2 KKT条件

KKT(Karush-Kuhn-Tucker)条件是约束优化问题的必要条件,在SVM中有如下形式:

  • α_i = 0:样本位于间隔带之外(被正确分类,且远离超平面)

  • 0 < α_i < C:样本恰好是支持向量(位于间隔边界上)

  • α_i = C:样本是软间隔支持向量(位于间隔带内部或被错误分类)

通过KKT条件,我们可以检查SVM模型的解是否满足最优性条件。在实际训练中,如果某个样本不满足KKT条件,说明模型尚未收敛或存在问题。

4.3 SMO算法

SMO(Sequential Minimal Optimization)是目前求解SVM对偶问题最流行的算法,由John Platt于1998年提出。其核心思想是:每次选择两个拉格朗日乘子 αi 和 αj 进行联合优化,因为当只选一个乘子时,会违反等式约束 Σ α_i y_i = 0。

SMO算法的步骤简述如下:

  1. 初始化所有 α_i = 0

  2. 启发式选择两个违反KKT条件的乘子 αi 和 αj

  3. 固定其他乘子,求解关于 αi 和 αj 的二次优化问题(解析解)

  4. 更新 αi 和 αj

  5. 重复步骤2-4,直到所有乘子满足KKT条件

SMO算法的优势在于:每个子问题的求解是解析的(非常快),且不需要存储整个核矩阵,从而能够处理大规模训练数据。

复制代码
# SMO算法由scikit-learn内部实现,以下代码展示如何使用
from sklearn.svm import SVC
​
# scikit-learn的SVC默认使用SMO算法(libsvm实现)
# 当样本量非常大时(>10万),可以考虑使用LinearSVC(基于坐标下降法)
svm = SVC(kernel='rbf', C=1.0, gamma='scale')
svm.fit(X_train, y_train)  # 内部自动调用SMO算法求解

5. 使用场景

5.1 文本分类

SVM在文本分类领域表现出色,尤其是在垃圾邮件过滤、新闻主题分类、情感分析等任务中。文本数据通常是高维稀疏的向量(如TF-IDF特征),而SVM的最大间隔特性使其在高维空间中具有很强的泛化能力,且计算效率高。

复制代码
# 文本分类场景示意
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
​
# 构建文本分类Pipeline
text_clf = Pipeline([
    ('tfidf', TfidfVectorizer(max_features=5000)),  # 将文本转为TF-IDF向量
    ('svm', SVC(kernel='linear', C=1.0))            # 线性SVM适合高维稀疏文本数据
])
​
# 训练文本分类器
text_clf.fit(X_train_texts, y_train_labels)

5.2 人脸识别

人脸识别任务通常涉及图像特征的提取与比对。早期的人脸识别系统(如著名的ORL人脸数据集实验)常用SVM结合HOG(方向梯度直方图)等特征描述子。SVM在小样本场景下的优异性能使其在人脸识别领域得到广泛应用。

5.3 药物分类

在生物信息学和药物发现领域,SVM被用于预测分子的生物活性、药物-靶点相互作用等。例如,给定一个分子的化学结构特征,SVM可以预测该分子是否具有特定的药理活性。这一应用场景体现了SVM在小样本、高维特征环境下的独特优势。

5.4 异常检测

SVM的另一种重要应用是单类分类(One-Class Classification)------训练时仅使用正常样本,目标是识别与正常模式显著不同的异常点。One-Class SVM通过找到包含正常数据的最小超球体(或最大间隔超平面),将偏离该区域的新样本判定为异常。这在网络入侵检测、信用卡欺诈检测、工业故障检测等场景中应用广泛。

复制代码
# One-Class SVM异常检测示例
from sklearn.svm import OneClassSVM
​
# 仅用正常样本训练
oc_svm = OneClassSVM(kernel='rbf', gamma='scale', nu=0.1)
oc_svm.fit(X_normal)
​
# 预测:1表示正常,-1表示异常
predictions = oc_svm.predict(X_test)

6. 实战代码

6.1 环境准备

本文所有代码均基于Python 3.x,使用以下主要依赖库:

复制代码
scikit-learn >= 1.0
numpy
matplotlib

安装方式:

复制代码
pip install scikit-learn numpy matplotlib

6.2 线性SVM vs RBF核SVM对比

本节通过一个二维数据集,直观展示线性SVM与RBF核SVM在处理非线性数据时的差异。

复制代码
"""
线性SVM vs RBF核SVM对比
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
​
# ========== 1. 生成非线性数据集(双月牙形状)==========
# make_moons生成的数据是线性不可分的,适合测试核函数效果
X, y = make_moons(n_samples=200, noise=0.15, random_state=42)
​
# 将标签转换为 +1 和 -1(SVM的标准格式)
y = 2 * y - 1
​
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)
​
print(f"训练集样本数: {len(X_train)}, 测试集样本数: {len(X_test)}")
​
# ========== 2. 训练线性SVM ==========
svm_linear = SVC(kernel='linear', C=1.0)
svm_linear.fit(X_train, y_train)
y_pred_linear = svm_linear.predict(X_test)
acc_linear = accuracy_score(y_test, y_pred_linear)
​
print(f"\n=== 线性SVM ===")
print(f"准确率: {acc_linear:.4f}")
print(f"支持向量数量: {len(svm_linear.support_vectors_)}")
​
# ========== 3. 训练RBF核SVM ==========
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale')
svm_rbf.fit(X_train, y_train)
y_pred_rbf = svm_rbf.predict(X_test)
acc_rbf = accuracy_score(y_test, y_pred_rbf)
​
print(f"\n=== RBF核SVM ===")
print(f"准确率: {acc_rbf:.4f}")
print(f"支持向量数量: {len(svm_rbf.support_vectors_)}")
​
# ========== 4. 可视化对比 ==========
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
​
def plot_decision_boundary(ax, clf, X, y, title):
    """绘制决策边界和支持向量"""
    h = 0.02  # 网格步长
    x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
    y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    ax.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.RdBu)
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.RdBu, edgecolors='k', s=20)
    # 标注支持向量
    ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
               s=100, facecolors='none', edgecolors='yellow', linewidths=1.5,
               label=f'Support Vectors ({len(clf.support_vectors_)})')
    ax.set_title(title)
    ax.legend()
​
plot_decision_boundary(axes[0], svm_linear, X, y,
                       f'Linear SVM (Acc={acc_linear:.2f})')
plot_decision_boundary(axes[1], svm_rbf, X, y,
                       f'RBF SVM (Acc={acc_rbf:.2f})')
​
plt.tight_layout()
plt.savefig('svm_linear_vs_rbf.png', dpi=150)
plt.show()
print("\n图片已保存为 svm_linear_vs_rbf.png")

运行上述代码可以看到:线性SVM在双月牙数据集上只能得到一条直线分割两类,准确率较低;而RBF核SVM通过非线性决策边界,能够完美分开两类数据,准确率接近100%。黄色圆圈标记的点即为支持向量,可以看到SVM的稀疏性------只有少数几个点决定了分类边界。

6.3 不同核函数对比(鸢尾花数据集)

本节使用经典的鸢尾花(Iris)数据集,对比四种核函数在多分类任务上的性能。

复制代码
"""
不同核函数在鸢尾花数据集上的性能对比
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
​
# ========== 1. 加载数据 ==========
iris = load_iris()
X, y = iris.data, iris.target
​
# 取二分类子任务(Setosa vs Versicolour)便于可视化
X_binary = X[:100]
y_binary = y[:100]
​
# 使用全部3类进行核函数对比
print("=== 鸢尾花数据集(3类)核函数对比 ===\n")
​
# ========== 2. 定义不同核函数的SVM ==========
kernels = {
    'Linear': SVC(kernel='linear', C=1.0),
    'RBF': SVC(kernel='rbf', gamma='scale', C=1.0),
    'Polynomial (d=3)': SVC(kernel='poly', degree=3, gamma='scale', C=1.0),
    'Sigmoid': SVC(kernel='sigmoid', gamma='scale', C=1.0, coef0=0),
}
​
# ========== 3. 使用5折交叉验证评估各核函数 ==========
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
​
results = {}
for name, clf in kernels.items():
    # 使用Pipeline确保数据标准化
    pipeline = Pipeline([
        ('scaler', StandardScaler()),
        ('svm', clf)
    ])
    scores = cross_val_score(pipeline, X, y, cv=cv, scoring='accuracy')
    results[name] = {
        'mean': scores.mean(),
        'std': scores.std(),
        'scores': scores
    }
    print(f"{name:20s} | 准确率: {scores.mean():.4f} ± {scores.std():.4f} | "
          f"各折: {[f'{s:.3f}' for s in scores]}")
​
# ========== 4. 可视化对比 ==========
fig, ax = plt.subplots(figsize=(10, 6))
names = list(results.keys())
means = [results[n]['mean'] for n in names]
stds = [results[n]['std'] for n in names]
​
colors = ['#3498db', '#e74c3c', '#2ecc71', '#9b59b6']
bars = ax.bar(names, means, yerr=stds, capsize=5, color=colors, alpha=0.8)
​
for bar, mean in zip(bars, means):
    ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.01,
            f'{mean:.3f}', ha='center', va='bottom', fontsize=11, fontweight='bold')
​
ax.set_ylim(0, 1.1)
ax.set_ylabel('交叉验证准确率', fontsize=12)
ax.set_title('不同核函数在鸢尾花数据集上的性能对比(5折交叉验证)', fontsize=13)
ax.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('svm_kernel_comparison.png', dpi=150)
plt.show()

该实验揭示了不同核函数在相同数据集上的性能差异。通常RBF核和线性核在该数据集上表现最好,多项式核次之,Sigmoid核通常最差。这也印证了核函数选择的经验法则。

6.4 使用网格搜索调优C和gamma

RBF核SVM的两个关键超参数是 C (正则化强度)和 gamma(RBF核的宽度参数)。本节展示如何使用网格搜索(GridSearchCV)找到最优超参数组合。

复制代码
"""
RBF核SVM超参数网格搜索调优
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
​
# ========== 1. 加载数据 ==========
iris = load_iris()
X, y = iris.data, iris.target
​
# 使用二分类任务:Virginica vs Non-Virginica
X_binary = X[y != 0]  # 去掉Setosa类
y_binary = y[y != 0]
y_binary = (y_binary == 2).astype(int)  # Virginica为1,其余为0
​
X_train, X_test, y_train, y_test = train_test_split(
    X_binary, y_binary, test_size=0.3, random_state=42, stratify=y_binary
)
​
print(f"训练集: {len(X_train)} 样本, 测试集: {len(X_test)} 样本")
​
# ========== 2. 定义超参数搜索空间 ==========
# C: 正则化参数,越大模型越复杂
# gamma: RBF核参数,控制单个样本的影响范围
param_grid = {
    'svm__C': [0.01, 0.1, 1, 10, 100],
    'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 'scale', 'auto'],
}
​
# ========== 3. 创建Pipeline并执行网格搜索 ==========
pipeline = Pipeline([
    ('scaler', StandardScaler()),      # 数据标准化对SVM很重要
    ('svm', SVC(kernel='rbf'))
])
​
# GridSearchCV会做5折交叉验证,找出最优超参数
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring='accuracy',
    refit=True,        # 用最优参数在全部训练数据上重新训练
    verbose=1,
    n_jobs=-1          # 使用所有CPU核心加速
)
​
print("\n开始网格搜索(请稍候)...")
grid_search.fit(X_train, y_train)
​
# ========== 4. 输出最优结果 ==========
print(f"\n最优超参数: {grid_search.best_params_}")
print(f"交叉验证最优准确率: {grid_search.best_score_:.4f}")
​
# 在测试集上评估
best_model = grid_search.best_estimator_
test_acc = best_model.score(X_test, y_test)
print(f"测试集准确率: {test_acc:.4f}")
​
# ========== 5. 可视化网格搜索结果(热力图) ==========
# 提取'C'和'gamma'的搜索结果用于可视化
C_values = [0.01, 0.1, 1, 10, 100]
gamma_values = [0.001, 0.01, 0.1, 1, 10]  # 排除'scale'和'auto'便于可视化
​
# 重新构建不含scale/auto的搜索空间并训练
scores_matrix = np.zeros((len(gamma_values), len(C_values)))
​
for i, gamma in enumerate(gamma_values):
    for j, C in enumerate(C_values):
        pipeline_temp = Pipeline([
            ('scaler', StandardScaler()),
            ('svm', SVC(kernel='rbf', C=C, gamma=gamma))
        ])
        pipeline_temp.fit(X_train, y_train)
        scores_matrix[i, j] = pipeline_temp.score(X_test, y_test)
​
# 绘制热力图
fig, ax = plt.subplots(figsize=(10, 8))
im = ax.imshow(scores_matrix, cmap='YlOrRd', aspect='auto')
​
# 设置坐标轴
ax.set_xticks(np.arange(len(C_values)))
ax.set_yticks(np.arange(len(gamma_values)))
ax.set_xticklabels(C_values)
ax.set_yticklabels(gamma_values)
ax.set_xlabel('C(正则化参数)', fontsize=12)
ax.set_ylabel('gamma(RBF核宽度)', fontsize=12)
ax.set_title('RBF核SVM超参数网格搜索热力图(测试集准确率)', fontsize=13)
​
# 在热力图上标注数值
for i in range(len(gamma_values)):
    for j in range(len(C_values)):
        text = ax.text(j, i, f'{scores_matrix[i, j]:.3f}',
                       ha='center', va='center', color='black', fontsize=10)
​
# 添加颜色条
cbar = ax.figure.colorbar(im, ax=ax)
cbar.ax.set_ylabel('准确率', rotation=-90, va='bottom', fontsize=11)
​
plt.tight_layout()
plt.savefig('svm_gridsearch_heatmap.png', dpi=150)
plt.show()
print("\n热力图已保存为 svm_gridsearch_heatmap.png")

网格搜索的结果会清晰地显示:过大的C值或gamma值都会导致过拟合,而适中的参数组合能够获得最佳的泛化性能。热力图直观展示了C和gamma的交互效应。

6.5 SVM决策边界可视化

本节通过一个综合示例,展示如何绘制包含决策边界、间隔带和支持向量的完整可视化图。

复制代码
"""
SVM决策边界完整可视化
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
​
# ========== 1. 生成线性可分的二维数据 ==========
# make_blobs生成多个高斯分布的簇
X, y = make_blobs(n_samples=150, centers=2, cluster_std=1.2,
                  random_state=42)
y = 2 * y - 1  # 转换为 +1 / -1
​
# ========== 2. 训练SVM ==========
svm = SVC(kernel='linear', C=1.0)
svm.fit(X, y)
​
# ========== 3. 获取模型参数 ===========
w = svm.coef_[0]          # 权重向量
b = svm.intercept_[0]     # 偏置
support_vectors = svm.support_vectors_  # 支持向量
​
# 计算决策边界的直线方程: w[0]*x + w[1]*y + b = 0
# 即 y = (-w[0]/w[1])*x - b/w[1]
slope = -w[0] / w[1]
x_line = np.linspace(X[:, 0].min() - 1, X[:, 0].max() + 1, 100)
decision_boundary = slope * x_line - b / w[1]
​
# 间隔边界: w·x + b = ±1
margin_boundary_pos = slope * x_line - (b - 1) / w[1]
margin_boundary_neg = slope * x_line - (b + 1) / w[1]
​
# ========== 4. 绘制完整可视化 ===========
fig, ax = plt.subplots(figsize=(12, 8))
​
# 绘制样本点(按类别着色)
colors = ['#3498db', '#e74c3c']
markers = ['o', 's']
for cls in [-1, 1]:
    mask = y == cls
    ax.scatter(X[mask, 0], X[mask, 1], c=colors[cls], marker=markers[cls],
               s=80, label=f'类别 {cls}', edgecolors='k', alpha=0.8)
​
# 绘制决策边界(实线)
ax.plot(x_line, decision_boundary, 'k-', linewidth=2.5,
        label='决策边界 (w·x + b = 0)')
​
# 绘制间隔边界(虚线)
ax.plot(x_line, margin_boundary_pos, 'k--', linewidth=1.5, alpha=0.7,
        label='间隔边界 (w·x + b = +1)')
ax.plot(x_line, margin_boundary_neg, 'k--', linewidth=1.5, alpha=0.7,
        label='间隔边界 (w·x + b = -1)')
​
# 绘制支持向量(用黄色圆圈标记)
ax.scatter(support_vectors[:, 0], support_vectors[:, 1],
           s=250, facecolors='none', edgecolors='#f39c12',
           linewidths=2.5, label=f'支持向量 (n={len(support_vectors)})')
​
# 填充间隔带
ax.fill_between(x_line, margin_boundary_neg, margin_boundary_pos,
                alpha=0.1, color='gray')
​
# 在图的角落添加几何间隔公式
ax.text(0.02, 0.98, f'几何间隔 γ = {2/np.linalg.norm(w):.4f}',
        transform=ax.transAxes, fontsize=11, verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
​
ax.set_xlabel('特征 1', fontsize=12)
ax.set_ylabel('特征 2', fontsize=12)
ax.set_title('SVM决策边界与间隔带可视化\n'
             f'权重向量 w = [{w[0]:.4f}, {w[1]:.4f}], 偏置 b = {b:.4f}',
             fontsize=13)
ax.legend(loc='lower right', fontsize=10)
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('svm_decision_boundary.png', dpi=150)
plt.show()
​
print(f"\n关键参数:")
print(f"  权重向量 w: [{w[0]:.4f}, {w[1]:.4f}]")
print(f"  偏置 b: {b:.4f}")
print(f"  几何间隔: {2/np.linalg.norm(w):.4f}")
print(f"  支持向量数量: {len(support_vectors)}")
print(f"  支持向量:\n{support_vectors}")
print("\n图片已保存为 svm_decision_boundary.png")

运行以上代码,你可以看到完整的SVM分类可视化图:黑色实线是决策边界,两条黑色虚线是间隔边界,黄色圆圈标记的是支持向量,灰色半透明区域是分类间隔带。图中还标注了权重向量和几何间隔的值,帮助理解SVM的几何意义。


7. 总结

支持向量机是机器学习领域不可替代的经典算法。本文系统梳理了SVM的核心知识点:

  1. 原理层面:SVM以最大间隔为优化目标,通过支持向量确定分类边界,具有良好的泛化理论基础

  2. 核函数层面:通过核技巧,SVM可以将线性不可分问题转化为高维空间中的线性可分问题。RBF核是最通用的选择,线性核适合高维稀疏数据

  3. 求解算法层面:SMO算法高效地求解SVM的对偶优化问题,使得SVM能够处理大规模数据

  4. 实践层面:scikit-learn提供了简洁易用的SVM API,但超参数(C、gamma)的选择至关重要,网格搜索是调参的常用方法

SVM虽然训练复杂度较高,不适合超大规模数据,但其在中小规模数据集上的优异性能和可解释性,使其在工业界和学术界始终占据重要地位。建议读者在理解原理后,多动手跑实验,体会不同核函数和超参数对模型行为的深刻影响。


相关推荐
沪漂阿龙1 小时前
面试题:深度学习基础概念是什么?与机器学习区别、神经网络结构、核心特点一文讲透
深度学习·神经网络·机器学习
he___H1 小时前
子串----
java·数据结构·算法·leetcode
05候补工程师2 小时前
【ROS 2 避坑指南】从 SLAM 实时建图到 Nav2 导航算法深度调优全过程
算法·ubuntu·机器人
Dlrb12112 小时前
C语言-函数传参
c语言·数据结构·算法
洛水水9 小时前
【力扣100题】18.随机链表的复制
算法·leetcode·链表
南宫萧幕10 小时前
规则基 EMS 仿真实战:SOC 区间划分与 Simulink 闭环建模全解
算法·matlab·控制
多加点辣也没关系10 小时前
数据结构与算法|第二十三章:高级数据结构
数据结构·算法
沪漂阿龙11 小时前
AI大模型面试题:线性回归是什么?最小二乘法、平方误差、正规方程、Ridge、Lasso 一文讲透
人工智能·机器学习·线性回归·最小二乘法
hoiii18712 小时前
孤立森林 (Isolation Forest) 快速异常检测系统
算法