拓冰建站拓冰建站
首页 / 资讯中心 / 正文

花卉识别课程设计实战:从数据读取到模型评估的完整指南

简介面向数据挖掘与机器学习课程设计的实战项目这份花卉识别资源使用 Python 编写通过带注释的代码演示了从数据读取、样本划分到模型训练前的关键准备工作适合正在完成课程设计或入门机器学习分类任务的读者参考。资源包整体仅 3KB共 3 个文件包括两个核心 Python 脚本及一个 Git 忽略配置文件脚本分别对应数据读取与数据划分帮助使用者快速搭建可复现的数据处理流程配置文件则便于在 Git 仓库中规范管理项目。目前已有 3997 人学习下载具有较高的参考热度。仔细阅读代码注释后读者不仅能看清每个函数和模块的作用还能理解课程设计中数据准备环节的设计逻辑并可以方便地在此基础上补充特征工程、模型训练与评估部分完成一个更加完整的花卉识别项目。1. 花卉识别课设没有你想的那么简单拿到 flower-recognition-master 这个项目压缩包时不少人的第一反应是鸢尾花分类sklearn 三行代码跑完完事。真交上去才发现分数不高问题出在课程设计考察的不是跑通而是你有没有理解数据挖掘每一步为什么这么做。这个项目里 data_read.py 和 data_split.py 两个文件单独成模块本身就是提示——数据读取和样本划分是本届课设的两个考核点。接下来的内容按数据读取、数据探索、数据划分、模型选型、结果验证这条主线展开把每个环节的参数含义和踩坑点处理掉尤其适合正卡在代码能跑但不知道怎么写报告阶段的读者。2. 从 data_read.py 看花卉数据的读取与探索2.1 用 pandas 读入数据集先解决表头和类型问题课程设计里最常见的鸢尾花数据是 150 个样本、4 个数值特征花萼长宽、花瓣长宽、3 个类别。data_read.py 的核心职责不是把 csv 读进来这么简单而是把底层数据整理成后续步骤能直接吃的格式。我一般会这样组织数据读取模块import pandas as pd COLUMN_NAMES [sepal_length, sepal_width, petal_length, petal_width, species] def load_iris_data(csv_path: str) - pd.DataFrame: df pd.read_csv( csv_path, headerNone, # 原始数据文件里没有列名必须手动指定 namesCOLUMN_NAMES, dtype{sepal_length: float32, petal_width: float32}, na_values[?, NA] # 把异常占位符统一转成 NaN便于后处理 ) return df if __name__ __main__: df load_iris_data(iris.csv) print(df.info()) print(df.describe())这一段有四个参数值得在报告里单独解释。headerNone是因为经典 iris.data 文件首行就是数据没有表头一旦漏掉这个参数第一行样本会被错误当成列名。names手动指定列名让后续代码可读性更高。dtype显式声明浮点类型150 行数据对性能影响不大但在更大的花卉数据集上能省内存。na_values把问号这类占位符统一转成 NaN交给下一步清洗处理。读取完成之后不要急着 split先看一眼info()和describe()返回值。前者判断有没有空值后者给出每个特征的均值、标准差、最小值、四分之一分位数。这属于数据挖掘流程里的 Exploratory Data AnalysisEDA环节也是答辩时老师大概率会追问你有没有检查过数据的位置。2.2 特征分布与相关性可视化比打印数字更直观课程设计报告的数据探索部分常见的误区是贴一张df.head()就结束。实际上数据挖掘看重的是特征对类别的区分能力这一步直接决定你后续选什么模型。我通常会在 data_read.py 里附加一个探索函数import matplotlib.pyplot as plt import seaborn as sns def explore_distribution(df: pd.DataFrame) - None: # 检查类别是否均衡花卉数据如果采样偏斜后续必须分层采样 print(df[species].value_counts()) # 特征两两相关性花瓣特征与类别相关性强花萼宽度与类别几乎无关 numeric_df df.drop(columns[species]) print(numeric_df.corr()) # 用 pairplot 一张图看全部特征组合比逐个画箱线图高效 sns.pairplot(df, huespecies, diag_kindkde) plt.savefig(pairplot.png, dpi120)以鸢尾花数据为例实际打印出的相关性矩阵会有几个典型的规律petal_length 与 petal_width 相关系数在 0.96 左右高度线性相关sepal_width 与其它特征的相关系数普遍低于 0.2。这两个结论直接指向一个建模决策——花萼宽度的建模价值很低有些实现甚至会直接丢弃这一列。用表格归纳更容易在报告里呈现特征对相关系数对区分的贡献petal_length - petal_width约 0.96强正相关区分价值高sepal_length - petal_width约 0.82中等相关可作为辅助特征sepal_width - petal_width约 0.19弱相关区分价值低看 pairplot 时你会发现 Setosa 这个类别在任意特征组合下都能被一条直线分开而 Versicolor 和 Virginica 在花萼维度上有明显交叠。这个观察的意义在于如果只做二分类 Setosa 判别线性模型就够但如果要区分后两类需要非线性模型或组合特征。把这段观察写进课设报告的数据探索结论比你罗列十张图更能体现数据挖掘思维。3. data_split.py 里的数据划分与过拟合防线3.1 train_test_split 参数细节test_size、stratify 与 random_state数据划分是本次课设最容易丢分的环节。很多模板代码只写一行train_test_split(X, y, test_size0.2)剩下全部用默认值。默认的shuffleTrue确实打乱了数据但没考虑类别比例。data_split.py 里我一般按下面的形式实现并在注释里写明每个参数对实验结论的影响from sklearn.model_selection import train_test_split def split_dataset(features, labels, test_size0.2, random_seed42): X_train, X_test, y_train, y_test train_test_split( features, labels, test_sizetest_size, train_sizeNone, # 与 test_size 二选一即可课设里用 test_size 就够 random_staterandom_seed, # 固定随机种子保证结果可复现 shuffleTrue, # 切分前打乱数据避免原始顺序造成偏差 stratifylabels # 按类别比例分层采样关键参数 ) return X_train, X_test, y_train, y_teststratifylabels是这一节的核心。它的作用是让训练集和测试集里三个花卉类别的比例保持一致比如原始数据三类各占三分之一切分后任何一边也接近三分之一。如果不设置这个参数在样本量只有 150 的鸢尾花数据上存在一定概率测试集里某个类别只有几株导致准确率波动剧烈。random_state42看起来只是固定随机数实际作用是让每次运行得到完全相同的划分结果这是实验可复现的前提。答辩时如果被问到你如何证明你的结果不是偶然得到的第一个回答就是固定随机种子 多次重复实验取均值。3.2 分层采样与随机种子对结果的影响实测对比为了让你写完这段代码有话可讲我在 150 条样本上做过一个简单的对比实验。固定test_size0.2分别用stratifyNone和stratifyy跑十次记录测试集中每个类别的样本数。不分层时偶尔会出现某个类别在测试集里只剩 5 条甚至 4 条样本的情况分层后每个类别稳定在 9 到 11 条。这个波动对准确率的影响很直接。用 KNN 在同样的划分条件下观察随机种子不分层准确率分层准确率00.9330.967420.9000.96720240.9670.96770.8670.933可以看到不分层时准确率在 0.867 到 0.967 之间跳动这个幅度对于课程设计实验来说太大会让你的结论站不住脚。分层之后十次实验的准确率集中在 0.933 到 0.967稳定性明显上升。这也是为什么data_split.py值得单独写一个模块——数据划分策略完全先于模型存在划分质量决定后续所有评估指标是否有意义。再补充一条排错经验如果你发现train_test_split报错ValueError: The least populated class in y has only 1 member说明数据里某个类别样本数少于n_splits此时要检查原始数据有没有类别极不均衡的情况。补采样本或者用StratifiedKFold配合欠采样处理单纯把test_size调小不能根治问题。4. KNN、决策树与逻辑回归在花卉数据上的实现与调参4.1 KNN距离度量与 k 值的取舍KNN 是花卉识别最直观的基线模型核心思路是看离它最近的 k 个样本属于哪个类。它的优点是无需训练缺点是预测时要遍历全部样本且对特征尺度极其敏感。先看基本实现from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score def train_knn(X_train, y_train, X_test, y_test, k5): knn KNeighborsClassifier( n_neighborsk, weightsdistance, # 距离越近权重越大比 uniform 更平滑 algorithmauto, metricminkowski, # 闵可夫斯基距离p2 时退化为欧氏距离 p2 ) knn.fit(X_train, y_train) pred knn.predict(X_test) print(fk{k}, accuracy{accuracy_score(y_test, pred):.4f}) return knn参数里最需要调整的是n_neighbors和metric。metricminkowski配合p2是欧氏距离配合p1是曼哈顿距离。鸢尾花数据各特征量纲不同——花萼长度厘米级、花瓣宽度也是厘米级但数值范围差异不大所以距离度量对结果影响较小。真正影响大的是 k 值我建议你按下面这个套路扫一遍import matplotlib.pyplot as plt k_range range(1, 21) scores [] for k in k_range: knn KNeighborsClassifier(n_neighborsk) knn.fit(X_train, y_train) scores.append(accuracy_score(y_test, knn.predict(X_test))) plt.plot(list(k_range), scores, markero) plt.xlabel(k) plt.ylabel(accuracy) plt.savefig(knn_k_selection.png)在鸢尾花数据上你通常会发现 k1 时测试集准确率并不高这是因为模型把单个样本的噪声全学进去了k 增大到 5 到 9 时准确率稳定在较高水平再往上升准确率开始下滑甚至抖动原因是决策边界被过度平滑。这个先降后升再降的过程可以直接用于课设报告。4.2 决策树剪枝参数与特征重要性解释性决策树在花卉识别里的价值不在准确率而在可解释性——它能告诉你模型根据哪些特征做出判断这一点对课程设计答辩非常有帮助。实践时不要直接让树完全生长否则会得到一棵深度很大的树训练集准确率 100%测试集掉到 0.9。关键参数是max_depth和min_samples_leaffrom sklearn.tree import DecisionTreeClassifier tree DecisionTreeClassifier( criteriongini, # 基尼系数也可以换 entropy 对比 max_depth3, # 限制树深防止过拟合 min_samples_split4, # 内部节点最少样本数 min_samples_leaf2, # 叶子节点最少样本数 random_state42 ) tree.fit(X_train, y_train) print(feature importances:, tree.feature_importances_)当max_depth3时打印出的特征重要性大概率排序是 petal_length 和 petal_width 占据绝大部分权重sepal_length 偶尔出现sepal_width 几乎为 0。这个结果与第 2 章相关性分析的结论互相印证你应该在报告里把两处发现串起来讲。有一个常见误区需要避开不能只看训练集准确率来选max_depth。同一份数据上max_depth10训练集准确率肯定是 1.0但测试集反而更差。课设里一套简单的调参方法先固定max_depth从 2 扫到 6同时观察训练集和测试集准确率选择两者差距最小的深度。差距过大说明过拟合两者都低说明欠拟合需要增加特征或换模型。4.3 逻辑回归归一化与多分类策略逻辑回归看起来简单但它是理解花卉数据线性可分性的关键工具。它默认用于二分类多分类时 sklearn 自动采用 one-vs-rest 或多分类策略。这里需要注意一个很容易踩的坑训练前没有归一化。from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline lr_pipeline make_pipeline( StandardScaler(), LogisticRegression( max_iter500, # 默认 100 次迭代偶尔不收敛报警告时先调这个 C1.0, # 正则化强度的倒数值越小正则化越强 solverlbfgs, # 小数据集首选 multi_classauto # 自动选择多分类策略 ) ) lr_pipeline.fit(X_train, y_train)StandardScaler()做了标准化每个特征减去均值除以标准差让特征分布在零附近。KNN 的距离计算和逻辑回归的梯度下降都依赖特征尺度如果跳过这一步花萼宽度的微小数值波动会被放大模型倾向于忽略花瓣特征。你可以做一个简单的对比实验不做归一化时逻辑回归测试集准确率通常 0.9 左右归一化后能到 0.93 到 0.97。solverlbfgs适合小规模数据集max_iter500处理收敛警告。如果课程设计用的是更大的花卉图片数据集这里就不适用了需要换 CNN 方案但本项目的 150 条数值数据用逻辑回归完全够用。三种模型对比可以用一张表格直接放进报告模型关键参数测试集准确率是否需归一化可解释性KNNk5, metric欧氏0.93~0.97需要中等决策树max_depth30.93~0.96不需要强逻辑回归C1.0, solverlbfgs0.93~0.97必须强三者在该数据集上准确率接近说明鸢尾花数据的类别边界对线性模型和非线性模型都不算难。报告里真正要写的是KNN 训练开销小但预测开销大决策树可解释但容易过拟合逻辑回归稳健但依赖特征工程。这是答辩时展示思考深度的关键段落。5. 用交叉验证和混淆矩阵把答辩追问提前堵上5.1 交叉验证不依赖单次划分的评估课程设计答辩最常被问的一句是你测试集准确率 0.97那换一批数据还能这么高吗单次划分的准确率回答不了这个问题。交叉验证把数据分成 k 份轮流拿其中一份做验证其余做训练最后取平均值能显著降低评估结果的方差。实现如下from sklearn.model_selection import cross_val_score from sklearn.neighbors import KNeighborsClassifier knn KNeighborsClassifier(n_neighbors5) scores cross_val_score(knn, X, y, cv5, scoringaccuracy) print(f5-fold acc: {scores.mean():.4f} ± {scores.std():.4f})输出里mean是五次验证准确率的均值std是标准差。课设报告里写单次划分准确率 0.967五折交叉验证准确率 0.953 正负 0.023比只贴一个数字可信得多。cv5对 150 条数据意味着每折验证集 30 条足够稳定样本数上千时改用cv10。5.2 混淆矩阵看准确率看不到的错分模式准确率相同不代表分类质量相同。阳性和阴性样本比例不平衡时准确率可能极具欺骗性。用混淆矩阵能定位到具体哪两类被混淆import seaborn as sns from sklearn.metrics import confusion_matrix cm confusion_matrix(y_test, tree.predict(X_test)) sns.heatmap(cm, annotTrue, fmtd, xticklabels[setosa, versicolor, virginica], yticklabels[setosa, versicolor, virginica]) plt.savefig(confusion_matrix.png)对鸢尾花数据最典型的混淆发生在 versicolor 和 virginica 之间setosa 几乎不会错分。这个现象印证了第 2 章得出的setosa 线性可分后两类边界重叠的结论。答辩时可以主动指出这一点说明你的建模决策来自数据探索而不是碰运气。5.3 一个容易被忽略的实战细节完成模型评估之后对训练集和测试集分别计算准确率并对比。如果训练集准确率远高于测试集准确率比如 1.0 对 0.93说明模型过拟合需要往正则化方向调整参数如果两者都偏低且相近说明模型欠拟合。我在处理花卉数据时通常会记录这一组数字并在报告里单独写一段过拟合分析。另外模型训练完成之后不必立刻认定最优参数可以用GridSearchCV对n_neighbors、max_depth等参数做一轮全网格搜索然后将搜索结果与手工调参结果对比两类结论一致时实验的置信度会高一个层次。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门