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

使用 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 模型,可以尝试以下方法:

超参数调优:

使用 GridSearchCV 搜索最优的 C 和 gamma 值:

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_)

处理多类别:

扩展到三类别分类(包括 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)

处理非线性复杂数据:

尝试生成更复杂的合成数据集(如 make_moons 或 make_circles)测试 RBF 核的优势:

from sklearn.datasets import make_moons

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)

特征选择:

使用所有四个特征(萼片长度、宽度,花瓣长度、宽度),并通过特征重要性分析(如基于线性核的系数)选择关键特征。

7. 常见问题与解答

Q1:为什么需要标准化数据?

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

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

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

Q3:C 参数的作用是什么?

C 控制误分类的惩罚力度。C 越大,模型越严格(减少误分类),但可能过拟合;C 越小,模型越宽松(允许更多误分类),但可能欠拟合。

Q4:如何处理不平衡数据集?

使用 class_weight='balanced' 参数,让 SVM 自动调整类别权重,或者通过过采样/欠采样平衡数据。

8. 总结

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

相关推荐

遗迹2【饰品全收集-耶莎篇】

遗迹2【饰品全收集-耶莎篇】

📅 07-14 👁️ 7097
中国最北盛产荔枝的地方,带你去看莆田的城市绿肺,绶溪公园
2的大写怎么写

2的大写怎么写

📅 09-19 👁️ 9750