使用 Scikit-learn 训练支持向量机 (SVM) 分类器:从理论到实践

在这里插入图片描述

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,qt,python等,具备多种混合语言开发能力。撰写博客分享知识,致力于帮助编程爱好者共同进步。欢迎关注、交流及合作,提供技术支持与解决方案。\n技术合作请加本人wx(注明来自csdn):xt20160813

在这里插入图片描述

使用 Scikit-learn 训练支持向量机 (SVM) 分类器:从理论到实践

支持向量机(Support Vector Machine, SVM)是一种强大的监督学习算法,广泛用于分类和回归任务,尤其在处理高维数据集和非线性可分问题时表现出色。本文将详细介绍如何使用 Python 的 Scikit-learn 库训练一个 SVM 分类器,涵盖理论背景、数据集准备、模型训练、评估和可视化等步骤,适合初学者和有一定经验的开发者实践操作。


1. SVM 理论简介

SVM 的核心目标是找到一个最优的超平面,将不同类别的数据点分隔开,同时最大化分类边界的间隔(margin)。以下是关键概念:

  • 超平面:在二维空间中是一条直线,在三维空间中是一个平面,在高维空间中是超平面,用于分隔不同类别。
  • 支持向量:距离超平面最近的数据点,这些点决定了超平面的位置。
  • 最大间隔:SVM 优化目标是使超平面到最近的支持向量的距离(间隔)最大化。
  • 核函数:当数据非线性可分时,SVM 通过核函数(如线性核、RBF 核)将数据映射到高维空间,使其线性可分。
  • 软间隔与正则化:引入惩罚参数 C 来处理噪声数据,C 值越大,模型越倾向于减少误分类,但可能过拟合。

本文将使用 Scikit-learn 的 SVC 类(支持向量分类器)实现一个二分类任务,并通过可视化帮助理解分类边界。


2. 任务描述与环境准备

任务

我们将使用 Scikit-learn 的内置 iris 数据集(鸢尾花数据集),从中选择两个类别(Setosa 和 Versicolor)以及两个特征(萼片长度和宽度),训练一个 SVM 分类器,预测花的类别,并可视化分类边界。

环境要求

确保安装以下 Python 库:

  • Scikit-learn:用于机器学习模型和数据处理
  • NumPy:用于数组操作
  • Matplotlib:用于可视化
  • Pandas:用于数据处理(可选)

安装命令:

pip install scikit-learn numpy matplotlib pandas

运行环境

  • Python 3.7+
  • Jupyter Notebook 或任意 Python IDE(如 VS Code、PyCharm)

3. 实现步骤

以下是训练 SVM 分类器的完整步骤,每个步骤都包含详细说明和代码。

步骤 1:导入库和加载数据集

我们使用 iris 数据集,提取两个类别和两个特征,简化问题以便可视化。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data[iris.target != 2, :2]  # 仅选择前两个类别(Setosa 和 Versicolor)和前两个特征(萼片长度、宽度)
y = iris.target[iris.target != 2]    # 对应的标签(0: Setosa, 1: Versicolor)

# 查看数据形状
print(f"特征矩阵形状: {X.shape}")
print(f"标签向量形状: {y.shape}")

注释

  • iris.data 包含 150 个样本的 4 个特征,我们只取前两个特征(萼片长度和宽度)。
  • iris.target 包含类别标签(0, 1, 2),我们只取 0 和 1(排除 Virginica 类别)。
  • 结果:X 是 (100, 2) 的特征矩阵,y 是 (100,) 的标签向量。

步骤 2:数据预处理

SVM 对特征的尺度敏感,因此需要标准化数据(均值为 0,方差为 1)。此外,我们将数据集拆分为训练集和测试集。

# 拆分训练集和测试集(80% 训练,20% 测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化特征
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)  # 计算训练集均值和方差,并标准化
X_test = scaler.transform(X_test)       # 使用训练集的均值和方差标准化测试集

print("训练集形状:", X_train.shape)
print("测试集形状:", X_test.shape)

注释

  • train_test_split:随机划分数据,random_state=42 确保结果可重复。
  • StandardScaler:标准化特征,使每个特征的均值为 0,方差为 1。
  • 结果:训练集 80 个样本,测试集 20 个样本。

步骤 3:训练 SVM 模型

我们使用 Scikit-learn 的 SVC 类,尝试线性核和 RBF 核,比较效果。

# 训练线性核 SVM
svm_linear = SVC(kernel='linear', C=1.0, random_state=42)
svm_linear.fit(X_train, y_train)

# 训练 RBF 核 SVM
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42)
svm_rbf.fit(X_train, y_train)

print("线性核 SVM 训练完成")
print("RBF 核 SVM 训练完成")

注释

  • kernel='linear':使用线性核,适合线性可分数据。
  • kernel='rbf':使用径向基函数核,适合非线性数据。
  • C=1.0:惩罚参数,控制误分类的容忍度。
  • gamma='scale':RBF 核的核宽度参数,scale 是 Scikit-learn 的默认值(1 / (n_features * X.var()))。
  • fit:训练模型,拟合训练数据。

步骤 4:模型评估

使用测试集评估模型的准确率,并输出支持向量的数量。

from sklearn.metrics import accuracy_score

# 预测测试集
y_pred_linear = svm_linear.predict(X_test)
y_pred_rbf = svm_rbf.predict(X_test)

# 计算准确率
accuracy_linear = accuracy_score(y_test, y_pred_linear)
accuracy_rbf = accuracy_score(y_test, y_pred_rbf)

print(f"线性核 SVM 测试集准确率: {accuracy_linear:.2f}")
print(f"RBF 核 SVM 测试集准确率: {accuracy_rbf:.2f}")

# 输出支持向量数量
print(f"线性核 SVM 支持向量数量: {svm_linear.n_support_}")
print(f"RBF 核 SVM 支持向量数量: {svm_rbf.n_support_}")

注释

  • accuracy_score:计算分类准确率(正确预测的样本数 / 总样本数)。
  • n_support_:返回每个类别的支持向量数量,反映模型复杂度。

步骤 5:可视化分类边界

为了直观理解 SVM 的分类效果,我们绘制训练数据的散点图和分类决策边界。

# 定义绘制决策边界的函数
def plot_decision_boundary(model, X, y, title):
    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))
    
    # 预测网格点
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    # 绘制决策边界和数据点
    plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.3)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm, edgecolors='k')
    plt.xlabel('萼片长度 (标准化)')
    plt.ylabel('萼片宽度 (标准化)')
    plt.title(title)
    plt.show()

# 可视化线性核和 RBF 核的决策边界
plot_decision_boundary(svm_linear, X_train, y_train, '线性核 SVM 分类边界')
plot_decision_boundary(svm_rbf, X_train, y_train, 'RBF 核 SVM 分类边界')

注释

  • meshgrid:生成二维网格,用于绘制决策边界。
  • contourf:绘制分类区域,颜色表示不同类别。
  • scatter:绘制训练数据点,颜色表示真实类别。
  • 结果:两张图分别展示线性核和 RBF 核的分类边界,线性核边界为直线,RBF 核边界为曲线。

4. 完整代码

以下是完整的可运行代码,整合了所有步骤:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

# 加载数据集
iris = datasets.load_iris()
X = iris.data[iris.target != 2, :2]  # 仅选择前两个类别和前两个特征
y = iris.target[iris.target != 2]

# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化特征
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 训练 SVM 模型
svm_linear = SVC(kernel='linear', C=1.0, random_state=42)
svm_linear.fit(X_train, y_train)
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42)
svm_rbf.fit(X_train, y_train)

# 评估模型
y_pred_linear = svm_linear.predict(X_test)
y_pred_rbf = svm_rbf.predict(X_test)
print(f"线性核 SVM 测试集准确率: {accuracy_score(y_test, y_pred_linear):.2f}")
print(f"RBF 核 SVM 测试集准确率: {accuracy_score(y_test, y_pred_rbf):.2f}")
print(f"线性核 SVM 支持向量数量: {svm_linear.n_support_}")
print(f"RBF 核 SVM 支持向量数量: {svm_rbf.n_support_}")

# 绘制决策边界
def plot_decision_boundary(model, X, y, title):
    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))
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.3)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm, edgecolors='k')
    plt.xlabel('萼片长度 (标准化)')
    plt.ylabel('萼片宽度 (标准化)')
    plt.title(title)
    plt.show()

plot_decision_boundary(svm_linear, X_train, y_train, '线性核 SVM 分类边界')
plot_decision_boundary(svm_rbf, X_train, y_train, 'RBF 核 SVM 分类边界')

5. 结果分析

运行代码后,你将得到以下输出(具体值可能因随机拆分略有不同):

  • 准确率:线性核和 RBF 核的测试集准确率通常接近 1.0(100%),因为鸢尾花数据集的这两个类别线性可分。
  • 支持向量数量:线性核的支持向量较少(通常 3-5 个),RBF 核的支持向量较多(通常 10-20 个),反映 RBF 核模型更复杂。
  • 可视化
    • 线性核:分类边界是一条直线,清晰分隔两类数据。
    • RBF 核:分类边界是曲线,适应性更强,但在简单数据上可能无明显优势。

6. 进阶实践建议

如果你想进一步优化或扩展这个 SVM 模型,可以尝试以下方法:

  1. 超参数调优
    使用 GridSearchCV 搜索最优的 Cgamma 值:

    from sklearn.model_selection import GridSearchCV
    param_grid = {'C': [0.1, 1, 10], 'gamma': ['scale', 0.1, 1], 'kernel': ['linear', 'rbf']}
    grid = GridSearchCV(SVC(), param_grid, cv=5)
    grid.fit(X_train, y_train)
    print("最佳参数:", grid.best_params_)
    
  2. 处理多类别
    扩展到三类别分类(包括 Virginica),Scikit-learn 默认使用 “one-vs-rest” 策略:

    X = iris.data[:, :2]  # 所有类别
    y = iris.target
    svm = SVC(kernel='rbf', C=1.0, random_state=42)
    svm.fit(X_train, y_train)
    
  3. 处理非线性复杂数据
    尝试生成更复杂的合成数据集(如 make_moonsmake_circles)测试 RBF 核的优势:

    from sklearn.datasets import make_moons
    X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
    
  4. 特征选择
    使用所有四个特征(萼片长度、宽度,花瓣长度、宽度),并通过特征重要性分析(如基于线性核的系数)选择关键特征。


7. 常见问题与解答

Q1:为什么需要标准化数据?
SVM 通过距离计算优化超平面,特征尺度差异大会导致模型偏向某一特征。标准化确保所有特征贡献均衡。

Q2:线性核和 RBF 核如何选择?

  • 线性核适合线性可分数据,计算成本低。
  • RBF 核适合非线性数据,但需要调参(如 gamma),可能过拟合。

Q3:C 参数的作用是什么?
C 控制误分类的惩罚力度。C 越大,模型越严格(减少误分类),但可能过拟合;C 越小,模型越宽松(允许更多误分类),但可能欠拟合。

Q4:如何处理不平衡数据集?
使用 class_weight='balanced' 参数,让 SVM 自动调整类别权重,或者通过过采样/欠采样平衡数据。


8. 总结

通过本文,你学习了如何使用 Scikit-learn 训练一个 SVM 分类器,涵盖了数据预处理、模型训练、评估和可视化等完整流程。我们以鸢尾花数据集为例,展示了线性核和 RBF 核的分类效果,并提供了可直接运行的代码。SVM 是一个强大且灵活的算法,适合多种分类任务。通过进一步调参和扩展,你可以将其应用于更复杂的实际问题。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿享天开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值