逻辑回归鸢尾花分类实战:从原理到实验报告全流程指南
简介机器学习课程设计与期末大作业中鸢尾花分类是逻辑回归算法的经典实战题目。这份基于Python实现的项目包面向需要提交完整方案或追求高分的本科生及自学者整体包括项目源码、实验报告和文档说明三大部分正好对应建模、分析与交付全流程。压缩包采用ZIP格式大小约192.11MB源码文件均配有详细注释实验报告覆盖数据探索、模型训练、分类评估与结论分析文档说明则提供环境配置和快速部署方法降低了上手机器学习项目的门槛。逻辑回归模型以鸢尾花的四种特征预测三个类别通过梯度下降迭代优化参数项目界面清晰、操作直观同时具备功能完善、界面美观等特点可作为课程设计或期末大作业直接改造复用。目前已有265人学习下载综合考虑代码完整度、文档配套与实操友好性是一份性价比较高的学习与作业参考资源。1. 逻辑回归做鸢尾花分类这份大作业资源到底能帮你省多少事期末交机器学习大作业最怕的不是不会写代码而是代码跑通之后说不清原理、写不出报告、答不上老师随口的追问。逻辑回归对鸢尾花Iris分类是经典到不能再经典的入门题但正因为经典老师更容易看出你是自己做的还是抄的。这份资源把源码、实验报告、文档说明并按好代码带注释适合直接拿去部署、复现也适合当作自己动手前的参考模板。拿到之后建议按本文的顺序走一遍先看清数据再理解逻辑回归在做什么然后跑代码、写报告最后用几个小技巧把结果做得更经得起追问。这样你交上去的就不只是能跑的代码而是一份能讲清楚、能应对提问的完整作业。2. 先把数据看清楚鸢尾花数据集的结构、读入方式和可视化检查鸢尾花数据集是机器学习里最常用的入门数据之一。它包含150条样本每条样本有4个特征花萼长度sepal length、花萼宽度sepal width、花瓣长度petal length、花瓣宽度petal width对应的标签是3个品种setosa、versicolor、virginica每个品种各50条。用逻辑回归做这个任务目标就是根据这4个特征判断一朵鸢尾花属于哪个品种。2.1 从 sklearn 读入数据并检查结构常见做法是直接用sklearn.datasets.load_iris()读入。这个接口返回一个Bunch对象里面包含数据、目标、特征名等字段。先看一眼数据结构再动手建模能避免很多后面才发现的低级问题。from sklearn.datasets import load_iris import pandas as pd iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) df[target] iris.target df[species] df[target].map({0: setosa, 1: versicolor, 2: virginica}) print(df.head()) print(df.groupby(species).size())这里把iris.data转成DataFrame并加上target和species两列方便按品种分组统计。map这一步是把数字标签0、1、2映射成可读的品种名后面做画图和写报告时会更直观。逻辑回归本身只需要数字标签所以你建模时用的还是iris.data和iris.target映射列只是给人看的。数据检查阶段还要留意一点4个特征的量纲是否一致。花萼长度在4.3到7.9之间花瓣宽度在0.1到2.5之间数值范围差距不小。逻辑回归的特征系数会受到特征尺度影响所以后面训练前要做标准化。2.2 画分布图判断数据是否线性可分逻辑回归本质上是一个线性分类器它找的是特征空间里的一条决策边界二维情况下是一条直线多维情况下是一个超平面。所以先画图看看数据大概的分布能提前判断线性边界够不够用。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(2, 2, figsize(10, 8)) pairs [(0, 1), (0, 2), (1, 3), (2, 3)] names iris.feature_names for ax, (i, j) in zip(axes.ravel(), pairs): for label, marker, color in [(0, o, blue), (1, s, orange), (2, ^, green)]: subset df[df[target] label] ax.scatter(subset.iloc[:, i], subset.iloc[:, j], markermarker, colorcolor, labeliris.target_names[label], s40) ax.set_xlabel(names[i]) ax.set_ylabel(names[j]) ax.legend() plt.tight_layout() plt.show()这段代码选了4组特征组合画散点图每组用不同形状和颜色区分三个品种。参数上marker控制点的形状s控制点的大小颜色直接用三个固定值确保三分类的视觉区分。画出图之后重点看有没有某一组特征组合下三个品种能被一条直线大致分开。鸢尾花数据的常见结论是用花瓣长度和花瓣宽度两个特征setosa 和另外两类就能分得很开但 versicolor 和 virginica 之间有交叠这意味着线性边界在部分样本上一定会出错模型不可能做到100%准确。这一步对写报告很有价值。你可以在实验报告里直接放其中一两张图说明“基于散点图观察花瓣长度和花瓣宽度对分类贡献最明显因此后续建模选择标准化后的全部4个特征”。这就是老师的评分点里的“数据分析”环节。2.3 划分训练集和测试集随机种子和分层采样数据准备好了下一步是把150条样本分成训练集和测试集。这里有两个参数容易翻车random_state和stratify。from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( iris.data, iris.target, test_size0.2, random_state42, stratifyiris.target ) print(f训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]}) print(训练集类别分布:, {i: sum(y_train i) for i in range(3)})test_size0.2表示取20%的样本做测试集即30条。random_state42固定随机种子保证每次运行划分结果一致这样实验报告里写的准确率是可复现的。stratifyiris.target是分层采样让训练集和测试集里三个品种的比例都接近1:1:1。如果去掉stratify在某个倒霉的随机种子下可能出现测试集里某个品种只有3条样本的情况算出来的准确率波动会很大。这里有个容易被新手的忽略的问题数据划分要在任何预处理之前完成。标准化的均值和方法只能用训练集算出再应用到测试集上防止测试集的信息泄露到训练过程里。这也是答辩时老师经常追问的一个点。3. 逻辑回归原理与代码实现损失函数、常见参数和训练流程逻辑回归虽然名字里带“回归”但它是用来做分类的。核心思路是把特征线性组合的结果送进 sigmoid 函数映射到0到1之间作为样本属于正类的概率。然后通过极大似然估计或梯度下降找到一组系数让所有训练样本的预测概率尽量贴近真实标签。3.1 二分类到三分类OvR 策略和 softmax逻辑回归原生解决二分类问题面对鸢尾花这种三分类数据有两条路一是用 OvR一对剩余策略训练三个二分类器二是直接用支持多分类的 softmax 版本。sklearn 的LogisticRegression默认在多分类时使用 OvR你把multi_class参数改成multinomial就可以切换到 softmax。对鸢尾花这种小数据两者准确率差别通常不大但实验报告里如果能写清楚你用的是哪种策略显得你是真懂。3.2 用 sklearn 训练模型并评估实际训练代码很短直接封装成一个完整流程。from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline from sklearn.metrics import accuracy_score, confusion_matrix, classification_report # 第一步标准化特征 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 第二步训练逻辑回归模型 model LogisticRegression( penaltyl2, C1.0, solverlbfgs, max_iter200, multi_classovr ) model.fit(X_train_scaled, y_train) # 第三步评估 y_pred model.predict(X_test_scaled) acc accuracy_score(y_test, y_pred) print(f测试集准确率: {acc:.4f}) print(混淆矩阵:) print(confusion_matrix(y_test, y_pred)) print(分类报告:) print(classification_report(y_test, y_pred, target_namesiris.target_names))这里的StandardScaler做标准化fit_transform在训练集上计算均值和方差并完成转换transform用训练集的参数转换测试集。逻辑回归的参数里penaltyl2表示 L2 正则化防止过拟合C是正则化强度的倒数C 越小正则化越强solverlbfgs是优化器适合小数据集max_iter200是最大迭代次数multi_classovr指定用一对剩余策略。训练完成后测试集准确率通常在90%以上。混淆矩阵能看出哪个品种容易被混。常见的结论是 setosa 100%分类正确versicolor 和 virginica 之间偶尔互换。原因前面画散点图时已经看到了这两个品种的特征分布有交叠线性边界无法完全分开。3.3 查看逻辑回归学到的系数coef_是模型的核心产出直接解释特征的重要性和方向。三个品种在 OvR 模式下会得到三组系数每一组代表“当前品种 vs 其余两个品种”的决策逻辑。coef_df pd.DataFrame( model.coef_.T, indexiris.feature_names, columns[f{name} vs rest for name in iris.target_names] ) print(coef_df.round(2))系数绝对值越大说明该特征对这个品种的判别越重要符号为正说明特征值越大越倾向判为当前品种。比如 setosa 那一列里花瓣长度和花瓣宽度的系数可能明显偏大这和散点图结论一致。这个表格放进实验报告可以直接支撑“模型可解释性分析”这一小节。3.4 自定义实现逻辑回归选做加分有些课程要求不能只调用 sklearn需要手写梯度下降实现逻辑回归。这个资源里如果源码包含手写版本建议别急着删它是答辩时最有力的加分材料。我一般会这样组织手写代码先定义 sigmoid 和损失函数再写梯度下降更新系数最后封装成类。import numpy as np def sigmoid(z): return 1 / (1 np.exp(-z)) def compute_loss(y, h): # 二分类交叉熵损失加一个极小值防止 log(0) eps 1e-15 return -np.mean(y * np.log(h eps) (1 - y) * np.log(1 - h eps)) class LogisticRegressionManual: def __init__(self, lr0.1, epochs500): self.lr lr self.epochs epochs self.theta None def fit(self, X, y): X np.hstack([np.ones((X.shape[0], 1)), X]) self.theta np.zeros(X.shape[1]) m X.shape[0] for i in range(self.epochs): h sigmoid(X self.theta) gradient (1 / m) * (X.T (h - y)) self.theta - self.lr * gradient return self def predict(self, X): X np.hstack([np.ones((X.shape[0], 1)), X]) prob sigmoid(X self.theta) return (prob 0.5).astype(int)这个版本只用于二分类所以如果要多分类训练时需要自己复制三份每份把当前品种当正类其余当负类再分别调用fit。注意lr和epochs需要手动调学习率太大会导致 loss 震荡太小则收敛慢。特征标准化在这个版本里更加关键因为手写的梯度下降对特征尺度很敏感不标准化很容易发散了。4. 把实验报告写到位大作业评分点拆解与可复现结果组织源码能跑只是及格线实验报告才是拿高分和拉开差距的地方。访谈中多位老师的一致说法是实验报告看的是“能否讲清楚一个问题从数据到结论的完整链路”。下面拆一下通常的评分点和对应的写作策略。4.1 实验目的与问题描述怎么写实验目的不需要长篇大论。控制在300字以内说清楚三点数据是什么、任务是什么、用什么方法。比如“本实验使用鸢尾花数据集包含150条样本、4个特征、3个品种目标是基于4个特征利用逻辑回归算法实现品种分类并评估模型性能”。这属于“问题定义”环节占评分点中“明确问题”的小分。4.2 数据分析与预处理段落的评分重点这一段是拉开差距的地方。不要只写“我调用了 StandardScaler”。尝试按这个顺序组织先放 train_test_split 的代码说明随机种子和分层采样的理由再放散点图说明观察到的特征可分性最后写标准化操作强调它在逻辑回归中的必要性。整体上交代了“为什么这样处理”而不是只报流水账。4.3 模型训练与评估贴结果并做简要解释模型训练部分给出测试集准确率、混淆矩阵、分类报告每样都要跟一两句解读。混淆矩阵是整个报告里最值得写的对象它比准确率更有信息量。比如“setosa 全部正确分类但 versicolor 有两条被分成了 virginica说明这两类在特征空间有重叠区域线性模型难以再优化”。4.4 可视化结果决策边界和损失曲线如果源码里包含损失曲线或决策边界绘制的代码报告里放图会非常加分。损失曲线展示训练过程收敛情况决策边界直观展示模型学到的分类规则。下面放一段画二分类决策边界的通用代码用前面训练好的模型加画网格点。import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap # 只取两个特征方便可视化 X_vis X_train_scaled[:, 2:] X_test_vis X_test_scaled[:, 2:] model_vis LogisticRegression(C1.0, max_iter200) model_vis.fit(X_vis, y_train) x_min, x_max X_vis[:, 0].min() - 0.5, X_vis[:, 0].max() 0.5 y_min, y_max X_vis[:, 1].min() - 0.5, X_vis[:, 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 model_vis.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha0.3, cmapListedColormap([blue, orange, green])) scatter plt.scatter(X_vis[:, 0], X_vis[:, 1], cy_train, cmapListedColormap([blue, orange, green]), edgecolork, s40) cbar plt.colorbar(scatter) plt.xlabel(petal length (scaled)) plt.ylabel(petal width (scaled)) plt.title(Logistic Regression Decision Boundary) plt.show()这里只取花瓣长度和花瓣宽度两个标准化后的特征因为前面散点图显示这对特征区分度最高。np.meshgrid生成整个平面的网格点model_vis.predict对每个网格点预测品种预测结果用于contourf画背景色。图中的三个色块就是模型学到的三个区域散点越接近色块中心模型对那个位置的分类越有信心。写报告时可以把这张图与全特征模型的准确率做对比解释“只用两个特征时准确率略降但可视化程度更高”的 trade-off。4.5 实验小结客观记录错误分布小结不要写成“模型表现很好”。更客观的写法是列出错误类型、错误来自哪些样本分析是否需要换模型或加特征。比如“从混淆矩阵看模型主要混淆 versicolor 和 virginica若想进一步提升可考虑 KNN 或决策树这类非线性模型”。这种写法让老师看到你有分析问题、对比方案的能力而不是只会跑通。5. 避坑指南逻辑回归鸢尾花分类中的常见翻车现场逻辑回归看代码很简单实际跑起来翻车点不少。下面梳理几个我在复现这类项目时常遇到的实际问题。5.1 报错 ConvergenceWarninglbfgs 没有收敛现象训练时输出ConvergenceWarning: lbfgs failed to converge模型准确率偏低有时还会出现全预测成同一类的极端情况。原因max_iter太小或者特征没有标准化。lbfgs 是拟牛顿法虽然比梯度下降快但一样有迭代上限。如果特征量纲差异大loss 曲面更“狭长”收敛变慢。解决先加max_iter比如max_iter500或者max_iter1000另外一定要做StandardScaler。如果标准化后还报可以把solver换成liblinear试试这个对中小数据集更稳定。经验上鸢尾花数据标准化 max_iter200通常就不报错再报就检查是不是某列特征数据读入后类型出了问题。5.2 准确率只有60%左右吓得以为是代码写错了现象测试集准确率只有0.6左右错过真因是模型几乎把所有样本都分成了样本量最多的那一类。原因没做分层采样。如果train_test_split没加stratifyiris.target测试集30条里可能只有5条 setosa、20条 versicolor、5条 virginica模型偏向学 versicolor。再加上类别不平衡准确率看似可以但其实没有泛化能力。解决train_test_split加stratifyiris.target。对150个样本的小数据分层采样意义很大能保证训练集和测试集类别比例一致。5.3 手写梯度下降 loss 越跑越大现象手写逻辑回归训练时loss 曲线不降反升或者直接变成 nan。原因学习率太大梯度下降在陡峭的 loss 曲面上来回震荡甚至一步跨过最低点。另一种情况是特征没标准化某些特征数值范围大到让梯度爆炸。解决把学习率从 0.1 降到 0.01 甚至 0.001再跑一遍看 loss。同时给手写版本也加StandardScaler。如果 loss 在某个位置变成 nan可以把批次改成小批量或者初始化参数时全零改为小随机数。通常手写版本在鸢尾花数据上0.01 学习率、500 次迭代内就能收敛得很好。5.4 测试集准确率100%别高兴太早现象训练集准确率100%测试集也是100%感觉自己“满分”了。原因鸢尾花数据本身比较简单尤其 setosa 这个类和其他两类分得非常开所以100%也不是完全不可能。但如果是用了全部4个特征、C值设得太大比如 100模型可能过拟合了训练集。150条样本少过拟合更容易发生。解决把C调小到 0.5 或 0.1再跑一次对比。另一个简单有效的方法是做交叉验证把cross_val_score的结果和单次划分的准确率对比。如果交叉验证均值明显低于单次测试集结果说明单次划分可能凑巧分到了容易的样本模型稳定性存疑。5.5 报告里的图和代码对不上现象实验报告里的散点图是原始特征代码里却先标准化再训练图上特征数值范围和图例不匹配。原因绘图代码用了标准化后的数据报告里贴的却是早先画的原始特征图或反了过来。解决写完报告记得随手把报告里每一张图对应的代码跑一遍确认数值范围一致。答辩最尴尬的不是模型效果差而是被老师发现图和代码不是一套的。6. 把结果做得更经得起追问交叉验证、混淆矩阵热力图和一份可复现的实验记录前面已经把基础流程跑通但大作业要想拿高分还得在“模型评估”和“可复现性”上多做一层。老师问到“你的准确率可靠吗”如果你能拿出交叉验证结果说服力会强很多。6.1 交叉验证看稳定性单次划分的准确率有运气成分。用cross_val_score跑5折交叉验证能给你一个更稳妥的评估。from sklearn.model_selection import cross_val_score, StratifiedKFold cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) pipe make_pipeline(StandardScaler(), LogisticRegression(C1.0, max_iter200, multi_classovr)) scores cross_val_score(pipe, iris.data, iris.target, cvcv, scoringaccuracy) print(每折准确率:, scores.round(4)) print(平均准确率: {:.4f} (±{:.4f}).format(scores.mean(), scores.std()))StratifiedKFold是分层K折保证每折的类别比例接近原始分布。shuffleTrue打乱顺序random_state42保证可复现。把StandardScaler放在Pipeline里的好处是每折交叉验证时标准化都在训练折内进行避免信息泄露。之后在两个地方用时直接把pipe拿过去不会出现“测试集用了训练集的均值”这种隐蔽错误。6.2 混淆矩阵热力图分类报告里的混淆矩阵是文本不够直观。在报告里放一张混淆矩阵热力图效果明显更好。import seaborn as sns cm confusion_matrix(y_test, y_pred) plt.figure(figsize(6, 5)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsiris.target_names, yticklabelsiris.target_names) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Confusion Matrix) plt.show()annotTrue在格子中间显示数字fmtd告诉 seaborn 按整数格式输出cmapBlues用蓝色渐变表示数值大小。对角线越接近50越理想非对角线的格子是错误样本。写报告时直接说“第2行第3列的2表明有两条真实的 versicolor 被预测成了 virginica”比“准确率100%”更有说服力也更加真实可信。6.3 让实验过程可复现固定随机状态并导出关键结果一份能得高分的实验报告必然是可以一键复现的。请把以下习惯固化成操作流程在 Python 文件或 notebook 开头固定三个随机源——numpy、sklearn、内置 random代码跑完后保存一份带输出的 notebook 或 markdown在报告末尾附上一段“运行环境说明”包括 Python 版本、sklearn 版本和关键依赖。环境说明不需要写很长的输出两三行就行。import sys import numpy as np import sklearn print(Python 版本:, sys.version.split()[0]) print(NumPy 版本:, np.__version__) print(scikit-learn 版本:, sklearn.__version__) np.random.seed(42) rng np.random.RandomState(42)实践里遇到太多因为版本差异导致结果对不上的尴尬情况。版本不同同样的train_test_split划分结果可能不一致准确率也会不同。所以固定随机种子和记录环境版本这两件事做一次成本极低但能让你的作业在任何一台机器上都能复现同样的结果。6.4 一个最后检查清单把这些事做成一个小模板每次交作业前强制过一遍代码从头到尾跑一遍没有任何报错报告里每张图都在代码里能找到对应生成函数训练集和测试集中三类样本比例相近模型参数、随机种子、数据集划分方式都被记录能完整复现对老师可能问的问题提前准备一两句回答比如“为什么选择multi_classovr而不是multinomial”“标准化做了什么”“正则化参数 C 怎么调”。经历了太多次本来模型效果不错结果交作业时发现 notebook 输出丢失、图被清空的惨痛教训后从那以后我每次提交课程设计或期末大作业前都强制走一遍上述检查清单把.py文件和带输出的.ipynb文件都保留一份。希望这些你能直接用上的整理也帮到你。本文还有配套的精品资源点击获取