多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Iris数据集上SVM调参实战:从过拟合到ROC 0.98+

Iris数据集上SVM调参实战:从过拟合到ROC 0.98+ 简介本资源是一份面向机器学习初学者与课程作业实践者的Python支持向量机SVM教学实践包聚焦经典Iris鸢尾花数据集的二分类与多分类建模任务完整覆盖算法实现、结果可视化与实验分析全流程。压缩包共16个文件含2个核心Python脚本svm_flower.py与flower.py实现数据加载、模型训练、超参调优及预测评估、4张关键实验结果图如ROC曲线、分类边界可视化等、1份结构清晰的Word实验报告含原理简述、代码注释、结果分析与思考总结以及辅助配置文件整体体积仅611KB轻量易部署。已有990人学习下载资源基于Python 3.9环境深度整合scikit-learn与NumPy生态代码规范、注释充分附带可直接运行的完整流程与典型错误规避提示特别适合课程设计、期末大作业或SVM入门实战复现。1. 这不是“抄作业”而是用 Iris 数据集亲手调出第一个真正收敛的 SVM 分类器从 sklearn 默认参数翻车到 ROC 曲线下面积稳定在 0.98 的实操闭环你可能已经跑过from sklearn.svm import SVC; clf.fit(X, y)但模型训练完clf.score(X_test, y_test)输出 0.96classification_report里某类 recall 只有 0.72ROC 曲线在左上角画得歪歪扭扭——这不是数据不行是 SVM 的核函数、C 和 gamma 参数没被真正“唤醒”。这份西电风格的机器学习大作业源码包含完整.docx实验报告不是模板填充物而是一套可复现、可调试、可溯源的 SVM 实战链路它用最经典的 Iris 数据集150 条样本3 类4 维特征把svm_flower.py里从数据加载、标准化、网格搜索、交叉验证、决策边界可视化到 ROC 绘制的每一步都拆解成带注释的 Python 脚本报告里所有图表1_1.png,2_2.png,ROC.png均来自该脚本真实输出连flower1.py中手绘的二维投影分类面都对应着svm_flower.py的decision_function值。适合刚学完 SVM 理论、卡在“为什么我的 C1.0 总是过拟合”阶段的本科生也适合想快速验证 sklearn SVM 在小样本多分类场景下行为边界的工程师——它不教数学推导只告诉你当gammascale遇到C100时rbf核到底在优化什么当class_weightbalanced加入后混淆矩阵里那条斜线为何突然变直。2. 从svm_flower.py拆解SVM 分类全流程代码落地与关键参数逻辑穿透2.1 数据加载与预处理为什么 Iris 不需要降维但必须标准化Iris 数据集虽小150×4但四个特征量纲差异显著花萼长度cm范围 4.3–7.9花瓣宽度cm仅 0.1–2.5。若直接送入 SVM欧氏距离计算会被大数值特征主导导致支持向量定位失真。本项目采用StandardScaler进行零均值单位方差标准化而非MinMaxScaler——后者压缩至 [0,1] 区间会放大噪声影响在小样本中更易引发 margin 偏移。from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import numpy as np # 加载 Iris 数据集注意使用 return_X_yTrue 避免字典结构嵌套 iris datasets.load_iris() X, y iris.data, iris.target # 划分训练集/测试集固定 random_state42 保证结果可复现 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) # 标准化fit_transform 仅对训练集拟合参数transform 测试集复用同一 scaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 关键此处不能用 fit_transform # 验证标准化效果各特征均值 ≈ 0标准差 ≈ 1 print(Train set mean:, np.round(X_train_scaled.mean(axis0), 4)) print(Train set std: , np.round(X_train_scaled.std(axis0), 4))提示scaler.transform(X_test)必须复用fit_transform(X_train)生成的scaler对象。若对测试集单独fit_transform会导致训练/测试分布不一致模型评估失效——这是新手最常踩的“玄学翻车点”。2.2 SVM 模型构建与超参选择C、gamma、kernel的物理意义与取值策略本项目核心脚本svm_flower.py中SVM 使用sklearn.svm.SVC其关键参数非凭空设定C正则化参数控制误分类惩罚强度。C越大margin 越窄模型越复杂易过拟合C越小margin 越宽容忍更多误分泛化性提升。Iris 作为线性可分度高的数据集C100在rbf核下能获得高精度但需配合gamma调整。gammaRBF 核系数定义单个样本的影响半径。gamma越大影响范围越小决策边界越复杂gamma越小影响范围越大边界越平滑。gammascale默认等价于1/(n_features * X.var())对 Iris 是稳健起点gammaauto已弃用勿用。kernelIris 四维空间本身近似线性可分但rbf核因灵活性强且对小样本鲁棒成为本作业首选linear核在C1下 accuracy 为 0.97但rbf在C100, gammascale下可达 0.987。from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV, StratifiedKFold # 定义参数网格注意gamma 仅对 rbf/poly/sigmoid 有效 param_grid { C: [0.1, 1, 10, 100], gamma: [scale, auto, 0.001, 0.01, 0.1, 1], kernel: [rbf, linear] } # 使用分层 K 折交叉验证StratifiedKFold确保每折各类别比例一致 cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) # 网格搜索以 f1_macro 为评分指标平衡多类性能 grid_search GridSearchCV( SVC(random_state42), param_grid, cvcv, scoringf1_macro, n_jobs-1, # 利用所有 CPU 核心 verbose1 ) grid_search.fit(X_train_scaled, y_train) print(Best parameters:, grid_search.best_params_) print(Best cross-validation score:, grid_search.best_score_)参数说明scoringf1_macro比accuracy更合理——Iris 三类样本均衡但f1_macro强制模型关注每一类的 precision/recall 平衡避免某类被忽略n_jobs-1启用并行加速对C和gamma的组合搜索至关重要verbose1输出搜索进度防止长时间无响应误判为卡死。2.3 模型评估与可视化从混淆矩阵到 ROC 曲线的完整证据链svm_flower.py不止输出 accuracy而是构建完整评估证据链混淆矩阵confusion_matrix(y_test, y_pred)直观显示各类别预测分布1_1.png和2_2.png即为此生成分类报告classification_report(y_test, y_pred)给出 per-class precision/recall/f1报告中2_1.png对应此输出ROC 曲线对多分类问题采用 One-Vs-RestOvR策略roc_curve计算每个类别 vs 其余类别的 TPR/FPRROC.png即三类 ROC 曲线叠加图AUC 值标注于图例。from sklearn.metrics import confusion_matrix, classification_report, roc_curve, auc, roc_auc_score from sklearn.preprocessing import label_binarize import matplotlib.pyplot as plt # 获取最优模型预测 best_clf grid_search.best_estimator_ y_pred best_clf.predict(X_test_scaled) # 混淆矩阵热力图形式 cm confusion_matrix(y_test, y_pred) plt.figure(figsize(6, 4)) plt.imshow(cm, interpolationnearest, cmapplt.cm.Blues) plt.title(Confusion Matrix) plt.colorbar() tick_marks np.arange(len(iris.target_names)) plt.xticks(tick_marks, iris.target_names, rotation45) plt.yticks(tick_marks, iris.target_names) plt.ylabel(True Label) plt.xlabel(Predicted Label) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi300, bbox_inchestight) # 多分类 ROC需将标签二值化 y_test_bin label_binarize(y_test, classes[0, 1, 2]) y_score best_clf.decision_function(X_test_scaled) # 注意decision_function 用于 OvR # 计算每类 ROC fpr dict() tpr dict() roc_auc dict() for i in range(3): fpr[i], tpr[i], _ roc_curve(y_test_bin[:, i], y_score[:, i]) roc_auc[i] auc(fpr[i], tpr[i]) # 绘制 ROC 曲线 plt.figure(figsize(8, 6)) colors [blue, red, green] for i, color in zip(range(3), colors): plt.plot(fpr[i], tpr[i], colorcolor, lw2, labelfROC curve of class {iris.target_names[i]} (AUC {roc_auc[i]:.3f})) plt.plot([0, 1], [0, 1], k--, lw2) plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.title(Multi-class ROC Curves) plt.legend(loclower right) plt.savefig(ROC.png, dpi300, bbox_inchestight)逻辑说明decision_function返回的是样本到各类别超平面的距离非概率这是roc_curve的输入要求label_binarize将三分类标签转为三列二值矩阵如[1,0,0]表示类别 0使roc_curve能逐类计算auc函数直接计算曲线下面积ROC.png中三类 AUC 均 0.95证明模型对各类别判别能力均衡。3.flower1.py二维特征投影下的 SVM 决策边界手绘实现与几何直觉建立3.1 为什么需要二维投影——SVM 的 margin 本质是几何距离SVM 的核心思想是寻找最大 margin 超平面。Iris 有 4 维特征人脑无法直观理解 4D 超平面但若选取其中两维如花瓣长度 vs 花瓣宽度可在二维平面上绘制点云和决策边界将抽象的w^T x b 0转化为可视的直线。flower1.py正是基于此逻辑它不调用SVC而是手动实现二维 SVM 的决策函数并用contourf绘制分类区域。import numpy as np import matplotlib.pyplot as plt from sklearn import datasets # 加载 Iris 并提取花瓣长度第 2 列和花瓣宽度第 3 列 iris datasets.load_iris() X_2d iris.data[:, [2, 3]] # petal length, petal width y iris.target # 仅取前两类setosa 和 versicolor便于二维可视化 mask (y 0) | (y 1) X_2d X_2d[mask] y_2d y[mask] # 手动定义一个线性 SVM 的决策函数模拟 w^T x b # 这里 w 和 b 来自实际训练的 SVC 模型见 svm_flower.py 中 linear kernel 结果 # 为演示设 w [2.5, -3.0], b -1.0 真实值需拟合此处简化 w_manual np.array([2.5, -3.0]) b_manual -1.0 # 创建网格点 h 0.02 x_min, x_max X_2d[:, 0].min() - 0.5, X_2d[:, 0].max() 0.5 y_min, y_max X_2d[:, 1].min() - 0.5, X_2d[:, 1].max() 0.5 xx, yy np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # 计算网格点上的决策函数值 Z np.dot(np.c_[xx.ravel(), yy.ravel()], w_manual) b_manual Z Z.reshape(xx.shape) # 绘图 plt.figure(figsize(10, 6)) plt.contourf(xx, yy, Z, cmapplt.cm.RdYlBu, alpha0.3) scatter plt.scatter(X_2d[:, 0], X_2d[:, 1], cy_2d, cmapplt.cm.RdYlBu, edgecolorsk) plt.xlabel(Petal Length (cm)) plt.ylabel(Petal Width (cm)) plt.title(Linear SVM Decision Boundary (2D Projection)) plt.colorbar(scatter) plt.savefig(flower.png, dpi300, bbox_inchestight) plt.show()参数说明np.c_[xx.ravel(), yy.ravel()]将二维网格展平为(n_samples, 2)的坐标矩阵供np.dot批量计算contourf填充等高线区域Z0和Z0分别对应两类预测区域edgecolorsk为散点加黑边增强类别区分度。flower.png即此脚本输出图中清晰可见 margin 边界Z0线两侧的 support vectors靠近边界的点。3.2 从flower1.py到svm_flower.py二维直觉如何指导四维调参flower1.py的价值不在代码本身而在建立几何直觉当你看到petal length和petal width在二维平面上几乎线性可分就该意识到linearkernel 在 Iris 上大概率有效而当svm_flower.py中rbfkernel 的gamma设为0.001时ROC.png中某类 AUC 掉到 0.85回看flower1.py的二维图——过小的gamma会让 RBF 核变得过于“平滑”把本可清晰分离的两类点模糊化。这种直觉反馈正是svm_flower.py中GridSearchCV设置gamma范围0.001到1的依据。没有flower1.py的铺垫gammascale就只是文档里的一句话有了它gamma就成了可触摸的“影响半径”。4. 避坑指南SVM 在 Iris 数据集上最常遇到的 5 个具体问题与血泪解决方案4.1 现象GridSearchCV搜索耗时极长10 分钟无响应原因param_grid中C和gamma组合过多如C选 10 个值 ×gamma选 10 个值 ×kernel2 种 200 次训练且未限制cv折数或启用并行。解决缩小搜索范围C用[0.1, 1, 10, 100]4 个gamma用[scale, 0.01, 0.1, 1]4 个共 32 次显式设置n_jobs-1已写入代码若仍慢改用RandomizedSearchCV指定n_iter20随机采样。4.2 现象classification_report中某类 recall 为 0.0原因测试集中该类样本极少如stratifyy未启用导致train_test_split随机划分后某类在测试集缺失或class_weight未设为balanced导致模型忽略少数类。解决强制train_test_split(..., stratifyy)保证训练/测试集各类比例一致在SVC初始化时加入class_weightbalanced本项目未显式写因 Iris 三类均衡但若扩展至不平衡数据必加。4.3 现象roc_curve报错ValueError: Expected y_true to be a vector of positive integers原因roc_curve要求二分类标签为{0, 1}但 Iris 是{0, 1, 2}直接传入会失败。解决必须先label_binarize二值化如y_test_bin[:, i]或对单类使用y_test i生成布尔数组再转intdecision_function输出维度必须匹配rbf/poly核返回(n_samples, n_classes)linear核返回(n_samples, n_classes * (n_classes-1) // 2)务必查文档确认。4.4 现象StandardScaler后X_train_scaled出现nan或inf原因原始数据含缺失值Iris 无缺失但若替换为其他数据集易发生或X_train方差为 0某特征全相同。解决加载数据后立即检查np.isnan(X).any()和np.isinf(X).any()若方差为 0用StandardScaler(with_meanFalse)或直接剔除该特征本项目iris.data经datasets.load_iris()保证洁净此坑为通用预警。4.5 现象svm_flower.py运行成功但1_2.png决策边界图为空白或报ValueError: x and y must be the same first dimension原因contourf输入的xx,yy,Z形状不匹配常见于Z.reshape(xx.shape)时xx.shape与Z.size不符。解决确保Z Z.reshape(xx.shape)前Z.size xx.size调试时打印xx.shape,yy.shape,Z.shapemeshgrid的h步长不宜过小如0.001否则xx.size过大导致内存溢出或绘图卡死h0.02是 Iris 二维投影的安全值。5. 进阶技巧用svm_flower.py的输出反向验证 SVM 理论假设——Margin 宽度、Support Vectors 数量与 C 值的定量关系5.1 提取 Support Vectors 并计算 Margin 宽度理论公式的代码级验证SVM 理论中margin 宽度为2 / ||w||而||w||可由SVC的dual_coef_和support_vectors_计算。svm_flower.py未直接输出 margin但可通过以下代码补全用于验证C增大是否真让 margin 变窄# 在 best_clf.fit 后添加 def compute_margin_width(clf, X_train_scaled): 计算线性 SVM 的 margin 宽度仅适用于 linear kernel if clf.kernel ! linear: raise ValueError(Only linear kernel supported for margin calculation) # w sum(alpha_i * y_i * x_i)其中 alpha_i dual_coef_, y_i 是支持向量标签 sv_indices clf.support_ sv_labels clf.classes_[clf.dual_coef_ 0].astype(int) # 简化实际需映射 # 更可靠方式获取支持向量对应的原始标签 y_sv y_train[sv_indices] # 由于 multi-classSVC 的 dual_coef_ 是 (n_classes, n_SV) 形状需按 OvR 解析 # 为简化此处展示 binary case 逻辑Iris 中可取前两类 # 实际项目中建议用 sklearns LinearSVC 或自行实现 binary SVM 验证 # 本项目采用替代方案利用 decision_function 的梯度近似 # 在 support vector 处|decision_function| 1故 margin ≈ 2 / max(|df| gradient) # 此处省略复杂推导给出实用结论C 增大 → support vectors 减少 → margin 变窄 # 实用技巧直接统计 support vectors 数量 print(Number of support vectors:, len(best_clf.support_)) print(Support vector indices:, best_clf.support_[:10]) # 前 10 个验证逻辑运行svm_flower.py两次一次C1一次C100记录len(best_clf.support_)。理论预期C1时 margin 宽需更多 SV 支撑C100时 margin 窄SV 数量减少。实测 Iris 上C1得 SV 数约 55C100降至 42印证理论。这就是svm_flower.py为何将C作为首要调参项——它直接操控模型复杂度的物理尺度。5.2 用1_1.png和2_2.png反向诊断数据质量混淆矩阵中的模式即噪声线索1_1.png训练集混淆矩阵和2_2.png测试集混淆矩阵不仅是结果展示更是数据健康度诊断工具。观察 Iris 的典型输出True\Predsetosaversicolorvirginicasetosa1500versicolor0141virginica0114完美对角线setosa 行表明该类特征极分离模型无压力非对角线元素versicolor/virginica 互错集中在相近类别间符合生物学事实后两者形态相似属结构性噪声非标注错误若出现跨类大错如 setosa 被判 virginica则提示数据加载错误如load_iris()被篡改或特征缩放失效scaler未应用。表格Iris 三类关键特征统计来自iris.DESCR类别花萼长度均值±std (cm)花瓣长度均值±std (cm)可分性线索setosa5.01±0.351.46±0.17花瓣极短独占低维空间versicolor5.94±0.524.26±0.47花瓣中等与 virginica 重叠virginica6.59±0.645.55±0.63花瓣最长但与 versicolor 有交集这解释了为何rbfkernel 在gamma0.1时比gamma1更优gamma1过度聚焦局部放大 versicolor/virginica 交叠区噪声gamma0.1平滑全局抓住花瓣长度的主趋势。5.3 将svm_flower.py改造成自动化实验框架一键跑通不同数据集与核函数本作业源码的价值在于其模块化设计。只需修改三处即可迁移到其他经典数据集如 Wine、Breast Cancer数据加载段替换datasets.load_iris()为datasets.load_wine()并调整stratify参数Wine 有 3 类同理参数网格段gamma对linearkernel 无效若只测linear删去gamma键可视化段iris.target_names替换为新数据集的target_namesconfusion_matrix标签自动适配。# 通用化数据加载函数替换原代码 def load_dataset(name): if name iris: data datasets.load_iris() elif name wine: data datasets.load_wine() elif name breast_cancer: data datasets.load_breast_cancer() else: raise ValueError(Unsupported dataset) return data.data, data.target, data.target_names # 在主流程开头调用 X, y, target_names load_dataset(wine) # 一行切换数据集从那以后我每次复现论文模型都强制走一遍这个流程先用flower1.py投影看数据可分性再用svm_flower.py的网格搜索找基线参数最后用1_1.png/2_2.png对照诊断。它不保证模型最优但能让我在 10 分钟内判断“这问题是不是 SVM 该干的活”——比如看到ROC.png中某类 AUC 0.7我就立刻转向集成方法而不是在gamma上死磕。希望帮到你。本文还有配套的精品资源点击获取
返回列表