SVM支持向量机Iris分类实战:从原理到参数调优全解析
发布时间:2026/11/11 22:43:06来源:尧图网络
简介基于Iris鸢尾花数据集的SVM分类作业项目面向机器学习初学者与课程设计学生提供完整可运行的Python源码与配套实验报告帮助理解支持向量机原理及sklearn、numpy、matplotlib的实际用法。压缩包共18个文件包含2个Python脚本如svm_flower.py、flower1.py、2份docx实验报告、7张结果示意图含ROC曲线以及项目配置文件整体仅631KB轻量易下载。项目采用Python 3.9环境通过sklearn加载经典鸢尾花样本完成SVM建模、训练与可视化报告中详细说明了实验方法与各模块功能如sklearn提供分类器与内置数据集numpy负责数组运算matplotlib绘制分类结果与ROC曲线便于对照学习。目前已有970人学习适合需要快速上手SVM分类实验、参考作业写法或完善课程项目的读者。1. 作业题为什么要选 SVMIris 分类背后的三个硬门槛机器学习作业清单里Iris 鸢尾花数据集配 SVM 支持向量机几乎是标配组合。数据集只有 150 条样本、4 个特征、3 个类别任何入门算法都能跑出 95% 以上的准确率——所以很多同学交上去的作业只是调库调参老师一眼就能看出来。真正让这份作业有价值的是它同时踩中了 SVM 的三个关键点小样本下间隔最大化理论的适用性、线性不可分时核函数的引入动机、以及 C 和 gamma 这对参数对模型行为的实际影响。把一个玩具数据集做透比跑十个数据集都管用。这篇笔记按我实际做这个作业的顺序来写原理、代码、参数、坑你能直接照着复现也能搞清楚每行代码在干什么。2. 从线性可分到 RBF 核SVM 分类 Iris 数据的原理与选型2.1 线性 SVM 在 Iris 上为什么永远做不出高分setosa 与 versicolor 的边界Iris 数据集包含三类鸢尾花setosa、versicolor、virginica每类 50 条样本特征为花萼长度、花萼宽度、花瓣长度、花瓣宽度。取其中两个特征做二维散点图会发现 setosa 和另外两类是线性可分的——任意拿花萼宽度和花瓣长度组合都能画出一条直线把它们切开。但 versicolor 和 virginica 的样本在原始特征空间里互相交叠线性 SVM 在这里的最高准确率会被锁死在 90% 上下。这个结论不是玄学而是 VC 维理论在起作用。线性 SVM 的假设空间只是原始维度里的超平面集在 4 维特征空间里它就是 4 个特征变量的线性组合。Iris 数据集刻意加入了 virginica 这类和 versicolor 边界模糊的样本就是在逼你认识到如果作业里只调LinearSVC或者SVC(kernellinear)那是在给老师递一个扣分理由。核函数的引入动机就藏在这个交叠里。核技巧不直接计算高维空间的坐标而是用核函数算两个样本在高维空间的内积让原本线性不可分的数据在高维空间里被一个超平面切开。Iris 数据里真实有用的特征是花瓣长度和花瓣宽度这两个特征在二维里已经呈现出明显的非线性边界——用 RBF 核能把这种弯曲的决策边界拟合出来。2.2 用 scikit-learn 跑通最小 SVM 分类fit/predict 的完整调用链完整代码清单里核心训练部分最小只需要下面这几行from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.metrics import accuracy_score iris datasets.load_iris() X iris.data[:, 2:] # 取花瓣长度和花瓣宽度两个特征 y iris.target X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) model SVC(kernelrbf, C1.0, gammascale, random_state42) model.fit(X_train, y_train) y_pred model.predict(X_test) print(fAccuracy: {accuracy_score(y_test, y_pred):.4f})这段代码有四个参数需要逐个交代。iris.data[:, 2:]是只取了后两个特征也就是花瓣长度和花瓣宽度——这能让后面画决策边界时不用降维也符合大多数报告里“先可视化再建模”的叙述顺序。test_size0.3是 70% 训练、30% 测试的经典划分比例对 150 条样本的数据集测试集是 45 条足够评估模型但又不至于让训练集太薄。stratifyy是做分层抽样保证训练集和测试集里三类花的比例都接近 1:1:1这个在类别均衡的小数据集上特别重要不写的话可能随机划分出某一类在测试集里只剩 8 条的极端情况。gammascale是 scikit-learn 的默认值它会根据特征数量自动计算 gamma 1 / (n_features * X.var())在 Iris 这种特征量纲差异不大的数据集上通常够用。C1.0 是默认的正则化强度这个值后面调参时要动。第一次跑通这套代码准确率应该在 0.93~0.97 之间浮动——浮动的原因就是随机划分的种子没固定这个坑第四节专门讲。3. 特征工程与数据划分训练集、验证集和测试集的正确打开方式3.1 四种特征的量纲差异为什么直接干扰 C 和 gamma 的物理意义很多新手版的 SVM 作业会把四个特征全部喂进模型然后发现准确率在 0.9 附近怎么调参都上不去。原因是花萼长度、花萼宽度的量纲是厘米级范围在 4~8而花瓣宽度最小只有 0.1。这导致特征向量的模长被前两个变量主导而分类最有效的花瓣特征反而被压小了。SVM 的间隔计算依赖样本向量的内积和模长C 是对误分类的惩罚力度gamma 控制径向基核函数的“作用半径”。当特征没有标准化时C 和 gamma 的实际效果会被大数值特征裹挟gamma 稍微调大花萼特征上的微小波动就会被放大成剧烈的边界抖动决策边界在花瓣维度上却几乎不动。所以标准化的意义不止是让梯度下降收敛更快——对 SVM 来说它直接决定 C 和 gamma 是否有可解释的物理意义。常见的做法是StandardScaler或MinMaxScaler二选一。SVM 对特征的平移和缩放都敏感但 StandardScaler 在特征本身近似高斯分布时效果更稳Iris 恰好近似这种分布。3.2 标准化、train_test_split 与三个随机种子陷阱有一个必须刻在脑子里的原则先切分再标准化。如果先对全量数据做标准化再切分测试集的均值和方差已经偷瞄了训练集的信息这叫做数据泄漏。测试集就不再是“未来的样本”评估出来的准确率是虚高的。from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # 在 Pipeline 里做标准化天然避免数据泄漏 pipeline make_pipeline( StandardScaler(), SVC(kernelrbf, C1.0, gammascale, random_state42) ) pipeline.fit(X_train, y_train) y_pred pipeline.predict(X_test) print(fAcc: {accuracy_score(y_test, y_pred):.4f})make_pipeline是 sklearn 里相当推荐的写法它把标准化和分类器绑定成一个整体fit 时在训练集上计算均值和方差predict 时用同一套参数转换测试集不需要手动在中间插入transform。我见过不少报告里写的是手动scaler.fit(X_train)、X_test_scaled scaler.transform(X_test)的两行式逻辑没错但管道式代码在报告里显得更规整也少一个忘记转换测试集的风险。随机种子陷阱有三个一个比一个隐蔽。第一train_test_split里的random_state不固定每次运行准确率都不一样报告里写的 0.9667 下次跑不出来。第二SVC 里的random_state只对概率估计和类别权重相关逻辑有影响决策边界本身是确定性的但固定它能让输出可复现。第三如果调了参或者换了特征子集random_state没变但数据集版本变了结果同样不可比——在报告里每次实验都应该记录当前用的是哪个种子。4. 参数调优C 和 gamma 怎么设网格搜索怎么用4.1 C、gamma 的作用边界与典型数值范围SVM 的 RBF 核有两个核心参数C和gamma。C 是误差项的惩罚系数越大越不能容忍训练集上的误分类容易过拟合越小越鼓励模型找更宽的间隔允许更多的训练集错误偏向欠拟合。在 Iris 上C 从 0.01 到 100 跨度三个数量级直观表现是决策边界从平滑直线变成紧贴样本的复杂曲线。gamma 是 RBF 核的带宽参数的倒数K(x, x) exp(-gamma * ||x - x||^2)。gamma 越大每个训练样本的影响半径越小决策边界会围绕每个点画圈边界极度弯曲接近逐点记忆gamma 越小影响半径越大边界越来越平直最后退化成近似线性分类器。Iris 数据集用三个特征维度和 150 个样本gamma 的合理范围大概是 0.01 到 10C 的范围大概是 0.1 到 100。如果作业里只是手写几个for循环去试参数报告里写“经过反复实验得出最优参数”老师大概率会追问你“反复了几次搜索范围怎么确定的”。正确做法是用网格搜索把参数空间穷举一遍同时用交叉验证评估每一组参数的真实表现。4.2 GridSearchCV 调参的完整代码与决策边界可视化import numpy as np from sklearn.model_selection import GridSearchCV, StratifiedKFold param_grid { svc__C: [0.1, 1, 10, 100], svc__gamma: [0.01, 0.1, 1, 10] } cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) grid GridSearchCV( pipeline, param_gridparam_grid, cvcv, scoringaccuracy, n_jobs-1 ) grid.fit(X_train, y_train) print(fBest params: {grid.best_params_}) print(fBest CV accuracy: {grid.best_score_:.4f}) print(fTest accuracy: {accuracy_score(y_test, grid.best_estimator_.predict(X_test)):.4f})param_grid里的键名带svc__前缀是因为管道里标准化器的名字是standardscaler分类器的名字是svc__用来穿透管道指定底层的参数。这是 sklearn 管道搜索的固定语法写错成C会直接报 ValueError。StratifiedKFold(n_splits5)是五折分层交叉验证每折训练 4/5、验证 1/5五轮下来每个样本都被验证过一次比单一的 holdout 验证集更抗随机波动。对于 Iris 这样的 150 条样本小数据集5 折交叉验证是标准选择。10 折会让每折训练集只有 135 条验证集 15 条评估分数方差变大网格搜索选出的参数反而可能不稳定。n_jobs-1让网格搜索用满所有 CPU 核心4×4 的网格共 16 组参数、每组 5 折 80 次训练在 Iris 上最多几秒跑完不加并行也够快但以后换大数据集就知道这个参数的好了。网格搜索最容易翻车的点是在同一个验证集上反复挑参数最后挑出来的模型对验证集有信息泄漏测试集分数会比验证分数低。所以我在作业里固定用三块数据训练集用于 fit验证集由交叉验证内部生成并用于选参测试集从头到尾只能碰一次——也就是最后评估grid.score(X_test, y_test)时。4.3 决策边界可视化画出来才能写进报告作业报告里如果只有准确率数字没有图说服力会大打折扣。画决策边界不是给代码凑篇幅它是检查过拟合最直观的手段。import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap def plot_decision_boundary(model, X, y, title): 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.linspace(x_min, x_max, 200), np.linspace(y_min, y_max, 200) ) Z model.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) colors ListedColormap([#FFAAAA, #AAFFAA, #AAAAFF]) plt.contourf(xx, yy, Z, alpha0.3, cmapcolors) scatter plt.scatter(X[:, 0], X[:, 1], cy, cmapcolors, edgecolorsk) plt.xlabel(Petal length (cm)) plt.ylabel(Petal width (cm)) plt.title(title) plt.legend(handlesscatter.legend_elements()[0], labels[setosa, versicolor, virginica]) plt.show() # 画原始特征空间未标准化的决策边界 plot_decision_boundary( grid.best_estimator_.named_steps[svc], X_train, # 注意这里是原始特征 y_train, SVM RBF Decision Boundary on Raw Features )这段代码里有个比较关键的处理grid.best_estimator_.named_steps[svc]取出的分类器而X_train是原始特征但plot_decision_boundary内部直接调model.predict没有经过scaler.transform——这样画出来的边界位置是正确的因为决策边界是 SVC 在标准化后的特征空间内计算出来的但我们把 X_train 原样喂进去等于把标准化后的坐标映射回了原始坐标。前提是训练时用的特征和这里传的特征是同一批原始值。如果你嫌麻烦也可以在函数内部加入StandardScaler的 transform。网格搜索跑完后打印出的Best params大概率落在C1和gamma0.1~1附近。如果三类的决策边界在图上看起来像三块平滑的色块拼接边界清晰且没有“岛屿”说明参数选得合理。如果边界上出现了围绕单个样本的封闭小圈那是gamma调太大、模型过拟合的信号。5. 作业提交前的排查报告里那些让老师一眼扣分的坑5.1 现象一直接四个特征训练准确率只有 0.9调参也上不去原因四个特征里花萼相关特征和花瓣相关特征的量纲与判别力差异过大SVM 的间隔被无关维度干扰。常见做法是直接用全特征建模再调参但这在 Iris 上并不合适。解决先做特征相关性分析或者直接可视化两两特征组合。Iris 数据里花瓣长度和花瓣宽度和类别相关性最高花萼宽度次之——用这两个特征建模准确率反而比四个全上更高。在报告里写清楚“保留花瓣长度和花瓣宽度、其余特征丢弃的理由”这一条就能看出有没有真正理解数据。5.2 现象二标准化前后准确率几乎不变报告里却没写这一步原因Iris 的特征量纲差异没有大到让模型完全失效的程度都在厘米级所以很多人做完标准化发现分数差不多就在报告里删掉了标准化这一步。但 SVM 的决策边界在标准化前后的形状完全不同调参的数值范围也不同——不标准化时 C100 和 C0.1 的边界变化幅度小于标准化后的变化幅度。解决用grid.best_params_反推如果最优参数落在边界上比如 C100 是最小值 0.1 的 1000 倍说明搜索范围没覆盖好。标准化后再看最优参数是否落在搜索空间的中间区域。一般正确的做法是保留标准化步骤并在报告中说明“标准化使模型对参数更敏感”这样调参的结果才有说服力。5.3 现象三报告里只写一个准确率没有交叉验证分数这是作业报告的头号扣分项。单次 train_test_split 的准确率取决于随机划分同一次实验换个random_state分数就会波动 0.03~0.05。比如设置random_state0时准确率 0.9333换成random_state42变 0.9667报告里只写 0.9667 是站不住的。解决正文里同时报告交叉验证均值±标准差和测试集分数。比如“5 折交叉验证平均准确率 0.9600±0.0327测试集上为 0.9667”这样老师看到的是模型的稳定性而不仅是一次抽签运气。5.4 现象四用了accuracy_score(y_test, model.predict(X_test))但测试集只分到 10 条样本原因train_test_split没设stratify150 条样本随机切 30% 时可能某一类在测试集中只剩 4 条整体测试集条数偏少导致分数方差大。解决固定stratifyy让每类在训练和测试中的比例一致。这行代码加上后测试集各类比例都是 0.33。5.5 现象五决策边界图片跟网格搜索的最佳参数对不上原因可视化时要么用了未标准化的特征画边界要么用了训练集之外的数据。如果代码里画的边界是SVC直接 fit 原始特征得到的结果而网格搜索的pipeline内部做了标准化两张图对应的模型就不是同一个边界形状会完全不同。解决确保可视化复用的是管道里的最终模型。把整个 pipeline 传到画图函数里在函数内部对np.c_[xx.ravel(), yy.ravel()]先调用scaler.transform再predict或者干脆画基于原始特征的模型另一种做法作为对比图并在报告里标注清楚两者区别。6. 进阶技巧从三分类到多分类策略OvO 与 OvR 的取舍sklearn 的 SVC 默认对多分类使用一对一OvO策略在训练时建立 C(3,2)3 个二分类器每个分类器只区分两类花预测时让三个分类器投票。另一种策略是一对多OvR训练 3 个分类器每个分类器区分“这一类”和“其余两类”。两者在 Iris 上准确率接近但决策边界形状和训练时间有差别。OvR 在类别数量大时训练速度更快因为每个分类器用全部样本OvO 每个分类器只用自己的两个类别的样本但分类器数量是类别数的平方量级。Iris 只有 3 个类这个区别不明显但报告里如果能写清“sklearn 默认用 OvO 且分类器数量为 3”并手动指定decision_function_shapeovr跑一次对比就能展示对 SVC 多分类机制的深入理解ovr_model SVC(kernelrbf, C1, gammascale, decision_function_shapeovr) ovo_model SVC(kernelrbf, C1, gammascale, decision_function_shapeovo) ovr_model.fit(X_train, y_train) ovo_model.fit(X_train, y_train) ovr_pred ovr_model.predict(X_test) ovo_pred ovo_model.predict(X_test) print(OvR acc:, accuracy_score(y_test, ovr_pred)) print(OvO acc:, accuracy_score(y_test, ovo_pred))decision_function_shape只影响decision_function输出的矩阵形状不影响预测标签——这是比较容易踩的一个细节但它能帮你理清多分类投票机制的组织形式。作业报告里用这个对比可以自然引到“SVM 本质上是一个二分类器多分类靠策略组合”这个核心结论。最后补一个我做这个作业养成的习惯也算血泪教训所有实验按“数据集版本 特征子集 模型参数”三个维度编上版本号每次跑的准确率、训练时间、最优参数都记录在脚本开头。否则改过特征后忘了刷新记录报告里的数字和图表对不上返工很烦。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网