一、机器学习十大常见算法
本篇主要介绍支持向量机(Support Vector Machine,简称 SVM),是一种监督学习算法,主要用于分类和回归问题。SVM 的核心思想是找到一个最优的超平面,将不同类别的数据分开。这个超平面不仅要能够正确分类数据,还要使得两个类别之间的间隔最大化。
二、SVM 的核心原理
2.1 线性可分与最大间隔
想象一个二维平面,上面有两类数据点(用圆圈和叉号表示)。我们的任务是画一条直线把两类点分开。
最简单的做法是“随便画一条线,只要分开就行”。但 SVM 不满足于此——它要找到最优的那条线。什么样的线是最优的?SVM 认为:能让两类数据点离这条线都尽可能远的那条线,就是最优的。
这个“距离”被称为间隔(Margin)。SVM 的目标就是最大化间隔。位于间隔边界上的那些数据点,就是支持向量——它们决定了最终分类器的位置。
2.2 线性不可分与核函数
现实中的数据往往没有那么“乖巧”——很多时候,在低维空间中根本无法用一条直线(或一个平面)将两类数据完全分开。
这时,SVM 祭出了它的“秘密武器”——核函数(Kernel Function)。核函数的核心思想是:将数据从低维空间映射到高维空间,在高维空间中,原本线性不可分的数据可能就变得线性可分了。
SVM 中最常用的核函数是RBF(径向基函数)核,也就是高斯核。在sklearn的SVC中,默认使用的就是 RBF 核。
简单总结一下:
超平面:
在二维空间中,超平面是一个直线。
在三维空间中,超平面是一个平面。
在更高维空间中,超平面是一个分割空间的超平面。
支持向量:
支持向量是离超平面最近的样本点。这些支持向量对于定义超平面至关重要。
支持向量机通过最大化支持向量到超平面的距离(即最大化间隔)来选择最佳的超平面。
最大间隔:
SVM的目标是最大化分类间隔,使得分类边界尽可能远离两类数据点。这可以有效地减少模型的泛化误差。
核技巧(Kernel Trick):
对于非线性可分的数据,SVM使用核函数将数据映射到更高维的空间,在这个空间中,数据可能是线性可分的。
常用的核函数有:线性核、多项式核、径向基函数(RBF)核等。
SVM 分类流程
- 选择一个超平面:找到一个能够最大化分类边界的超平面。
- 训练支持向量:通过支持向量机算法,选择离超平面最近的样本点作为支持向量。
- 通过最大化间隔来找到最优超平面:选择一个最优超平面,使得间隔最大化。
- 使用核函数处理非线性问题:通过核函数将数据映射到高维空间来解决非线性可分问题。
三、SVM 的关键参数
在使用sklearn.svm.SVC时,有两个参数对模型性能影响最大:
3.1 惩罚系数 C
C 是正则化参数,控制着模型对训练样本分类错误的容忍程度。
C 值越大:模型对分类错误越“不能容忍”,会尽量正确分类每一个训练样本。这可能导致模型过于复杂,容易过拟合。
C 值越小:模型对分类错误越“宽容”,允许一些样本被分错,但决策边界会更平滑,泛化能力可能更好。
简单来说,C 在“训练集上的准确率”和“决策边界的平滑度”之间做权衡。
3.2 RBF 核参数 gamma
gamma 定义了单个训练样本的影响力范围。
gamma 值越小:单个样本的影响力范围越“远”,决策边界会比较平滑、简单。
gamma 值越大:单个样本的影响力范围越“近”,决策边界会更复杂,会紧密围绕训练数据。
gamma 可以理解为“模型选出的支持向量的影响半径的倒数”。gamma 太大同样容易导致过拟合。
C 和 gamma 的配合:当 C 比较大、gamma 比较小时,模型会有更多支持向量,模型更复杂,容易过拟合。
四、实战案例:智能寝室分配(数据集介绍可看往期KNN算法)
第一步:加载数据
data = np.loadtxt("datingTestSet2.txt") data = pd.DataFrame(data) x = data.iloc[:, :-1] y = data.iloc[:, -1]np.loadtxt读取文本格式的数据文件,每行是一个样本。我们将数据转换为 DataFrame,方便操作。x是前三列特征,y是最后一列标签。
第二步:数据标准化 —— 至关重要的一步
x = StandardScaler().fit_transform(x) x = pd.DataFrame(x)这是 SVM 使用中极其重要的一步! SVM 依赖于样本之间的距离计算(尤其是使用 RBF 核时)。如果不同特征的量纲差异很大,那么量级大的特征会主导距离计算,模型会忽略量级小的特征。StandardScaler将每个特征转换为均值为 0、标准差为 1 的标准正态分布,公式为:
其中 μ 是均值,σ 是标准差。标准化后,所有特征处于同一量级,模型才能公平地看待每一个特征。
第三步:划分训练集和测试集
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2, random_state=0)将数据按 8:2 的比例划分为训练集和测试集。random_state=0保证每次运行结果一致。
第四步:交叉验证 —— 找到最优参数
这是本代码的核心部分:
scores = [] C_values = [0.01, 0.1, 1, 10, 100, 1000, 10000] gamma_values = [0.01, 0.1, 1, 10, 100, 1000, 10000] for i in C_values: for j in gamma_values: model = SVC(C=i, gamma=j, random_state=0) score = cross_val_score(model, train_x, train_y, cv=10, scoring='accuracy') score_mean = sum(score) / len(score) scores.append(score_mean) best_c = C_values[np.argmax(scores) // len(gamma_values)] best_g = gamma_values[np.argmax(scores) % len(gamma_values)]网格搜索(Grid Search):我们定义了 7 个 C 值和 7 个 gamma 值,共 7×7=497×7=49 种参数组合。通过双重循环遍历所有组合,对每一种组合都训练模型并评估效果。
交叉验证(Cross Validation):对于每一种参数组合,我们不是只用一次训练/验证划分来评估,而是使用10 折交叉验证。10 折交叉验证将训练集随机分成 10 份,轮流用其中 9 份训练、1 份验证,重复 10 次后取平均准确率作为该参数组合的得分。这样评估出来的得分更加稳定可靠。
参数还原:由于我们将所有得分保存在一维列表scores中,np.argmax(scores)返回的是最大得分在一维列表中的索引。通过整除和取余运算,我们可以反推出对应的 C 和 gamma:
best_c = C_values[np.argmax(scores) // len(gamma_values)]
best_g = gamma_values[np.argmax(scores) % len(gamma_values)]
np.argmax(scores) // 7得到的是第几个 C 值,np.argmax(scores) % 7得到的是第几个 gamma 值。
第五步:使用最优参数训练最终模型
model = SVC(C=best_c, gamma=best_g, random_state=0) model.fit(train_x, train_y)用网格搜索找到的最优参数(best_c和best_g)创建 SVM 模型,并在完整的训练集上进行训练。
第六步:模型评估
train_pred = model.predict(train_x) print(metrics.classification_report(train_y, train_pred)) test_pred = model.predict(test_x) print(metrics.classification_report(test_y, test_pred))classification_report会输出精确率(Precision)、召回率(Recall)、F1-score 等详细的分类评估指标,帮助我们全面了解模型在训练集和测试集上的表现。
完整代码如下:
import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.model_selection import cross_val_score from sklearn import metrics # 1. 加载数据 data = np.loadtxt("datingTestSet2.txt") data = pd.DataFrame(data) x = data.iloc[:, :-1] # 特征 y = data.iloc[:, -1] # 标签 # 2. 数据标准化 x = StandardScaler().fit_transform(x) x = pd.DataFrame(x) # 3. 划分训练集和测试集 train_x, test_x, train_y, test_y = train_test_split( x, y, test_size=0.2, random_state=0 ) # 4. 网格搜索 + 交叉验证 寻找最优参数 scores = [] C_values = [0.01, 0.1, 1, 10, 100, 1000, 10000] gamma_values = [0.01, 0.1, 1, 10, 100, 1000, 10000] for i in C_values: for j in gamma_values: model = SVC(C=i, gamma=j, random_state=0) score = cross_val_score(model, train_x, train_y, cv=10, scoring='accuracy') score_mean = sum(score) / len(score) scores.append(score_mean) # 从一维列表中还原最优参数 best_c = C_values[np.argmax(scores) // len(gamma_values)] best_g = gamma_values[np.argmax(scores) % len(gamma_values)] print(f"最优 C: {best_c}, 最优 gamma: {best_g}") # 5. 使用最优参数训练模型 model = SVC(C=best_c, gamma=best_g, random_state=0) model.fit(train_x, train_y) # 6. 模型评估 train_pred = model.predict(train_x) print("训练集评估报告:") print(metrics.classification_report(train_y, train_pred)) test_pred = model.predict(test_x) print("测试集评估报告:") print(metrics.classification_report(test_y, test_pred))五、SVM 的使用流程总结
通过上面的实战案例,我们可以总结出使用 SVM 的详细流程:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1. 数据准备 | 加载数据,分离特征和标签 | 确保数据格式正确 |
| 2. 数据预处理 | 标准化/归一化 | SVM 依赖距离计算,必须做 |
| 3. 划分数据集 | train_test_split | 训练集用于调参,测试集用于最终评估 |
| 4. 参数调优 | 网格搜索 + 交叉验证 | 寻找最优的 C 和 gamma |
| 5. 训练模型 | 用最优参数训练 | 在完整训练集上训练 |
| 6. 模型评估 | 在测试集上评估 | 检验模型的泛化能力 |
六、SVM 的优缺点
| 优点 | 缺点 |
|---|---|
| 在高维空间中表现优异 | 对大规模数据集训练时间较长 |
| 使用核函数可以处理非线性分类 | 对参数(C 和 gamma)和核函数的选择敏感 |
| 决策边界由支持向量决定,内存效率高 | 模型可解释性较差 |
| 在样本量较小时仍有良好表现 | 对数据标准化要求较高 |
七、总结
支持向量机通过最大化分类间隔来寻找最优决策边界,借助核函数可以处理线性不可分的数据。在实际使用中,数据标准化和参数调优(C 和 gamma)是决定模型性能的关键环节。希望通过这篇文章,大家能够理解 SVM 的核心思想,并能够独立使用 sklearn 实现 SVM 分类任务。