☰
机器学习SVM作业实战:基于Iris鸢尾花数据集的分类项目完整解析
2026/9/28 5:08:06 网站建设 项目流程

简介:这份资源面向机器学习初学者与高校学生,围绕经典Iris鸢尾花数据集完成SVM分类实验,适合作为课程作业参考或算法入门练手项目。压缩包共18个文件,约631KB,包含2个Python源码文件、2份docx实验报告、7张png结果图与若干xml配置及工程文件,源码与报告相互对应,便于对照理解建模流程。实验基于Python 3.9的IDLE环境,借助sklearn完成数据加载、模型训练与评估,用numpy处理数组运算,并通过Matplotlib绘制分类结果与ROC曲线,覆盖从数据准备到结果可视化的完整链路。目前已有970人学习下载,读者可参考其中的代码组织方式、报告撰写结构与实验结论分析,快速复现SVM分类过程,并在此基础上调整核函数与参数,加深对支持向量机原理的理解。

1. 从一份 SVM 作业说起:Iris 鸢尾花分类到底在练什么

很多人第一次接触机器学习,绕不开两个东西:一个是吴恩达机器学习作业里反复出现的 SVM,另一个是 Iris 鸢尾花数据集。这份「机器学习SVM作业基于Iris鸢尾花的数据样本实现SVM分类项目源码+报告」听起来像学生作业,但它其实是一套非常完整的分类项目最小闭环:数据加载、特征分析、模型训练、参数调优、评估可视化、报告撰写。把这套流程吃透,你换任何数据集做二分类或多分类,骨架都不用重写。

Iris 数据集只有 150 个样本、4 个特征、3 个类别,小到能在笔记本上秒级跑完,但它的价值在于「干净且可分」。用 SVM 在它上面做分类,你能清楚看到核函数、惩罚系数 C、gamma 这些参数到底怎么影响决策边界。对新手来说,这是理解 SVM 从硬间隔到软间隔、从线性到非线性的最佳沙盘;对熟手来说,这是验证自己评估流程和调参直觉的基准盘。下面我按实际做项目的顺序,把这份作业从零复现一遍,顺带把容易翻车的地方标出来。

2. 数据加载与探索:先把 Iris 的四个特征摸清楚

2.1 用 sklearn 加载 Iris 并确认数据结构

Iris 数据集在 sklearn 里自带,不需要额外下载。但很多人直接load_iris()拿到数据就扔进模型,连特征名和类别分布都没看,后面画图和分析报告时就会卡壳。我一般先做三件事:看形状、看特征名、看类别是否均衡。

from sklearn.datasets import load_iris import pandas as pd import numpy as np # 加载数据,as_frame=True 直接返回 DataFrame,省去手动拼列 iris = load_iris(as_frame=True) df = iris.frame # 特征列是 sepal length/width、petal length/width,单位 cm print("数据形状:", df.shape) print("特征名:", iris.feature_names) print("类别分布:\n", df['target'].value_counts()) # 把 target 数字映射回类别名,报告里写起来更直观 target_map = {i: name for i, name in enumerate(iris.target_names)} df['species'] = df['target'].map(target_map) print(df.head())

这段代码的关键在as_frame=True,它把特征和标签合成一个 DataFrame,列名就是sepal length (cm)这类可读名称。value_counts()确认三个类别各 50 个样本,完全均衡,所以后面不需要做重采样。参数上没什么可调的,但要注意iris.target_names返回的是 numpy 数组,映射时用enumerate转成字典更稳。

2.2 特征分布与相关性:为什么 petal 比 sepal 更管用

Iris 四个特征里,花瓣长度和花瓣宽度的区分度远高于花萼。这个结论不是背来的,画个箱线图或算一下类间均值就能看出来。做报告时把这一步写进去,比直接甩模型准确率有说服力得多。

import matplotlib.pyplot as plt import seaborn as sns # 箱线图看每个特征在三个类别下的分布差异 fig, axes = plt.subplots(2, 2, figsize=(10, 8)) for idx, feature in enumerate(iris.feature_names): ax = axes[idx // 2, idx % 2] sns.boxplot(x='species', y=feature, data=df, ax=ax) ax.set_title(feature) plt.tight_layout() plt.show() # 相关性热力图,看特征之间是否冗余 plt.figure(figsize=(6, 5)) sns.heatmap(df[iris.feature_names].corr(), annot=True, cmap='coolwarm') plt.title('Feature Correlation') plt.show()

箱线图里你会看到petal length和petal width在 setosa 和另外两类之间几乎完全分开,而 versicolor 和 virginica 在花瓣特征上有少量重叠。相关性热力图通常显示花瓣长度和花瓣宽度高度相关(0.96 左右),这意味着两者信息冗余,但 SVM 对冗余特征不敏感,所以不必强行做特征选择。如果报告里要写特征工程,可以提一句「基于箱线图观察,花瓣特征区分度更高,但保留全部四个特征对 SVM 影响不大」。

提示:画图时如果中文标题乱码,加plt.rcParams['font.sans-serif'] = ['SimHei'],但报告里建议用英文标题避免环境差异。

3. SVM 分类实现:从线性核到 RBF 核的完整训练流程

3.1 划分训练集与测试集:stratify 不能省

Iris 虽然均衡,但随机划分仍可能让某一类在测试集里偏少。用stratify=y保证训练集和测试集类别比例一致,这是分类任务的基本习惯。测试集比例我一般设 0.2 到 0.3,150 个样本用 0.2 就是 30 个测试样本,足够看出问题。

from sklearn.model_selection import train_test_split X = df[iris.feature_names].values y = df['target'].values # stratify=y 保证按类别比例分层抽样,random_state 固定便于复现 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) print("训练集:", X_train.shape, "测试集:", X_test.shape)

random_state=42是为了让每次运行结果一致,报告里的数字才能对得上。stratify=y在类别不均衡时更重要,Iris 上算是个好习惯的养成。注意X取.values变成 numpy 数组,后面 SVM 接受这种格式;如果保留 DataFrame,sklearn 也能跑,但某些旧版本会警告特征名问题。

3.2 线性核 SVM:先拿一个基线出来

不要一上来就 RBF 核调参,先用线性核跑一个基线。Iris 在花瓣特征上近似线性可分,线性核往往能到 95% 以上。有了基线,后面调 RBF 才知道提升来自哪里。

from sklearn.svm import SVC from sklearn.metrics import accuracy_score, classification_report # 线性核,C 默认 1.0,先不调 svm_linear = SVC(kernel='linear', C=1.0, random_state=42) svm_linear.fit(X_train, y_train) y_pred_linear = svm_linear.predict(X_test) print("线性核准确率:", accuracy_score(y_test, y_pred_linear)) print(classification_report(y_test, y_pred_linear, target_names=iris.target_names))

classification_report会输出每个类别的 precision、recall、f1-score。Iris 上通常 setosa 全对,versicolor 和 virginica 之间偶尔错一两个。C=1.0是默认惩罚系数,控制间隔宽度和误分类惩罚的权衡:C 越大越不容忍错分,容易过拟合;C 越小间隔越宽,可能欠拟合。线性核下 C 的影响相对温和,但报告里最好附一个 C 值对比表。

3.3 RBF 核与参数网格:gamma 和 C 怎么配

RBF 核是 SVM 在中小规模数据上的默认选择,它把样本映射到无限维空间,能处理非线性边界。但 RBF 有两个关键参数:C和gamma。gamma控制单个样本的影响范围,越大越容易过拟合,越小越平滑。我一般用GridSearchCV在训练集上交叉验证选参,而不是拿测试集反复试。

from sklearn.model_selection import GridSearchCV # 参数网格:C 和 gamma 各取几个量级 param_grid = { 'C': [0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1], 'kernel': ['rbf'] } # 5 折交叉验证,scoring 用准确率 grid = GridSearchCV(SVC(random_state=42), param_grid, cv=5, scoring='accuracy', n_jobs=-1) grid.fit(X_train, y_train) print("最佳参数:", grid.best_params_) print("交叉验证最佳得分:", grid.best_score_) # 用最佳模型在测试集上评估 best_svm = grid.best_estimator_ y_pred_best = best_svm.predict(X_test) print("测试集准确率:", accuracy_score(y_test, y_pred_best))

GridSearchCV会对每组参数做 5 折交叉验证,n_jobs=-1用满 CPU 核。Iris 数据小,16 组参数几秒就跑完。best_params_通常落在C=1或C=10、gamma=0.1附近,但不同random_state会有细微差异。注意不要用测试集去选参数,否则报告里的准确率就是「偷看」来的,严谨性会打折扣。

注意:如果gamma设得过大(比如 10),模型会把每个训练样本都当成一个簇,训练集准确率 100% 但测试集可能掉到 70% 以下,这是典型的过拟合信号。

4. 评估与可视化:让报告里的结论站得住脚

4.1 混淆矩阵与决策边界:两个必画的图

准确率只是一个数字,混淆矩阵能看出错在哪两类之间。决策边界图则直观展示 SVM 怎么切分特征空间。Iris 有四个特征,画决策边界只能选两个,通常选花瓣长度和花瓣宽度,因为这两个区分度最高。

from sklearn.metrics import confusion_matrix import seaborn as sns # 混淆矩阵 cm = confusion_matrix(y_test, y_pred_best) plt.figure(figsize=(6, 5)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=iris.target_names, yticklabels=iris.target_names) plt.xlabel('Predicted') plt.ylabel('Actual') plt.title('Confusion Matrix - RBF SVM') plt.show() # 决策边界:只用花瓣两个特征重新训练一个 SVM 用于可视化 X_2d = df[['petal length (cm)', 'petal width (cm)']].values X_train_2d, X_test_2d, y_train_2d, y_test_2d = train_test_split( X_2d, y, test_size=0.2, random_state=42, stratify=y ) svm_2d = SVC(kernel='rbf', C=grid.best_params_['C'], gamma=grid.best_params_['gamma']) svm_2d.fit(X_train_2d, y_train_2d) # 生成网格点预测类别,画填充等高线 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, 0.02), np.arange(y_min, y_max, 0.02)) Z = svm_2d.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape) plt.figure(figsize=(8, 6)) plt.contourf(xx, yy, Z, alpha=0.3, cmap='viridis') plt.scatter(X_2d[:, 0], X_2d[:, 1], c=y, cmap='viridis', edgecolors='k') plt.xlabel('Petal Length (cm)') plt.ylabel('Petal Width (cm)') plt.title('SVM Decision Boundary (RBF Kernel)') plt.show()

混淆矩阵里如果 versicolor 和 virginica 之间有 1 到 2 个错分,属于正常范围。决策边界图会显示 setosa 被完全隔离在左下角,另外两类之间有一条弯曲的边界。这张图放进报告,比单纯写「准确率 96%」更有信息量。注意可视化用的 2D 模型和之前 4D 模型不是同一个,参数虽然一样,但特征少了两个,边界形状会略有不同,报告里要说明这一点。

4.2 交叉验证曲线:C 和 gamma 各自怎么影响准确率

网格搜索给了最佳组合,但报告里最好展示单参数变化趋势。固定一个参数,扫另一个,画折线图,能看出模型对哪个参数更敏感。

# 固定 gamma=0.1,扫 C C_values = [0.01, 0.1, 1, 10, 100, 1000] train_scores, test_scores = [], [] for C in C_values: svm = SVC(kernel='rbf', C=C, gamma=0.1, random_state=42) svm.fit(X_train, y_train) train_scores.append(svm.score(X_train, y_train)) test_scores.append(svm.score(X_test, y_test)) plt.figure(figsize=(8, 5)) plt.plot(C_values, train_scores, marker='o', label='Train') plt.plot(C_values, test_scores, marker='s', label='Test') plt.xscale('log') plt.xlabel('C (log scale)') plt.ylabel('Accuracy') plt.legend() plt.title('Effect of C on SVM Accuracy (gamma=0.1)') plt.show()

通常你会看到 C 从 0.01 增到 1 时测试准确率上升,超过 10 后训练准确率接近 100% 但测试不再提升甚至下降。这就是过拟合的直观证据。gamma的扫描同理,gamma太大时测试准确率会明显下滑。报告里把这两张图放进去,调参部分就有了数据支撑,而不是一句「我用网格搜索找到了最佳参数」。

5. 避坑与排查:Iris 跑 SVM 最容易翻车的 5 个地方

5.1 现象:准确率 100%,但换个 random_state 就掉到 90%

原因:Iris 只有 150 个样本,测试集 30 个,错一个就差 3.3 个百分点。如果只跑一次就写报告,数字波动很大。解决:用cross_val_score做 10 折交叉验证,报告里写平均准确率和标准差,而不是单次测试集准确率。

from sklearn.model_selection import cross_val_score scores = cross_val_score(best_svm, X, y, cv=10, scoring='accuracy') print("10折交叉验证: %.4f ± %.4f" % (scores.mean(), scores.std()))

5.2 现象:StandardScaler 之后准确率反而降了

原因:Iris 四个特征量纲接近(都是厘米),SVM 对尺度敏感但这里差异不大。如果强行标准化,可能改变核函数对距离的感知,导致边界偏移。解决:先不标准化跑基线,如果准确率明显低于预期再试标准化。Iris 上通常不需要,但报告里可以提一句「特征量纲一致,未做标准化」。

5.3 现象:gamma 设成 'scale' 和设成具体数字结果差很多

原因:sklearn 的gamma='scale'默认是1 / (n_features * X.var()),Iris 方差小,算出来 gamma 偏大,可能过拟合。解决:显式指定 gamma 网格搜索,不要依赖默认值。报告里写清楚 gamma 的搜索范围。

5.4 现象:多分类时 SVM 内部怎么决策的搞不清

原因:SVM 原生是二分类,sklearn 用 one-vs-one 策略,3 个类别训练 3 个分类器。解决:知道这个机制后,看混淆矩阵时就能理解为什么 versicolor 和 virginica 容易混——它们在一对一投票里可能互相「抢票」。报告里可以加一句「采用 one-vs-one 多分类策略」。

5.5 现象:决策边界图上的点颜色和类别对不上

原因:plt.scatter的c=y传入的是数字标签,cmap映射后颜色顺序可能和target_names不一致。解决:用c=y配合cmap='viridis'时,在图例里手动标注 0=setosa、1=versicolor、2=virginica,或者用ListedColormap指定颜色。

提示:以上五条里,第 1 条和第 3 条最常出现在作业报告里,因为单次划分和默认 gamma 太容易让人误以为模型已经调好了。

6. 把作业变成可复用的分类模板:三个进阶习惯

做完 Iris 这份 SVM 作业,如果只交一份报告就结束,有点浪费。我后来带新人时,会让他们把代码整理成三个可复用件:一个数据加载函数、一个训练评估函数、一个参数扫描函数。这样换到 wine 数据集或乳腺癌数据集,改几行就能跑。

第一个习惯是固定随机种子并记录。random_state=42不是玄学,是为了让报告里的每个数字都能被复现。我一般会在脚本开头写SEED = 42,所有涉及随机的地方都引用它。第二个习惯是评估指标不只看准确率。Iris 均衡,准确率够用;但如果换成不均衡数据,就要看 f1-score 和 recall。classification_report每次必打,花不了几秒。第三个习惯是保存最佳模型和参数。用joblib.dump(best_svm, 'svm_iris.pkl')存下来,下次直接加载,不用重新训练。

import joblib # 保存模型和最佳参数 joblib.dump(best_svm, 'svm_iris_rbf.pkl') with open('best_params.txt', 'w') as f: f.write(str(grid.best_params_)) # 加载验证 loaded_svm = joblib.load('svm_iris_rbf.pkl') print("加载模型测试准确率:", loaded_svm.score(X_test, y_test))

这段代码里joblib比pickle更适合存 sklearn 模型,因为内部用了 numpy 数组优化。保存参数到文本文件是为了写报告时直接引用,不用翻控制台记录。

最后一个技巧是关于报告写作的:把「准确率 96%」改成「在 10 折交叉验证下平均准确率 96.7%,标准差 0.021,其中 versicolor 和 virginica 之间存在少量错分」。前者是数字,后者是结论。我吃过亏,早期报告只写准确率,被问「这个 96% 是怎么来的、稳不稳定」时答不上来。后来养成习惯,每个指标都带上下文和波动范围,报告的可信度完全不一样。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询