对数几率回归期末大作业:西瓜与鸢尾花识别分类完整可复现方案

📅 发布时间:2026/10/12 15:18:14
对数几率回归期末大作业:西瓜与鸢尾花识别分类完整可复现方案
简介一套基于对数几率回归模型完成西瓜与鸢尾花识别分类的期末大作业资源面向计算机、人工智能、数据科学等专业的学生和教师适合课程设计、期末项目或机器学习入门参考。压缩包共三十个文件以六个Python源码、四个交互式分析笔记、两个项目说明文档和两个实验实践报告为主体并附有必要的工程配置与缓存文件整体大小仅544KB目录安排清晰便于按需取用。已有296人学习浏览适合需要对照参考的学生快速理解模型训练流程。通过该资源可同时获得可直接运行的分类模型脚本、逐步骤演示数据预处理与结果可视化的Notebook、完整的对数几率回归实践报告以及注释详细的模型实现既能帮助理解算法原理与作业报告撰写思路也可迁移到其他二分类场景做二次开发。1. 对数几率回归期末大作业西瓜与鸢尾花识别分类的完整可复现方案期末要交课程设计翻遍开源项目却找不到一份能直接看懂、能跑通、能写进报告里的对率回归代码这种焦虑我太熟悉了。这份期末大作业资源正是冲着这个痛点来的基于对数几率回归模型把西瓜数据集二分类和鸢尾花数据集多分类两条完整链路都做了出来Python 源码带详细注释外加一份可以直接改的作业报告文档。适合计科、人工智能、数据科学、物联网方向的学生拿来交课程设计或期末大作业也适合刚接触逻辑回归的从业者快速摸清模型落地套路——不需要你在 sklearn 里调一行 API 就算完事而是把从数据加载、模型定义到训练评估的每一步都摊开给你看。我拆完这份资源后最大的感受是它的注释密度和报告结构明显是按「老师会逐行看代码」的标准写的。2. 对率回归的建模原理与源码实现先看懂 LogisticModel.py再谈跑通2.1 为什么课程设计选对数几率回归而不是 SVM 或神经网络课程设计的选型逻辑和工业项目不一样老师要看到你对模型原理的理解而不是看你调包调得多顺。对数几率回归在这类作业里的优势很突出——它介于传统线性模型和神经网络之间既有明确的数学推导空间sigmoid 映射、极大似然估计、梯度下降代码量又足够短适合在源码里逐行注释。这份资源里的 LogisticModel.py 写的就是纯 Python 实现不依赖 sklearn 的现成分类器。核心思路是先构造线性加权求和 z w^T x b再套 sigmoid 函数把输出压到 (0,1) 区间得到样本属于正类的概率。训练时用交叉熵作为损失函数对 w 和 b 求梯度沿负梯度方向迭代更新。我一般会跟学生强调一个点对率回归的损失函数不选均方误差是因为 sigmoid 输出经过 MSE 后是非凸函数梯度下降容易卡在局部极值而交叉熵在对数空间里是凸的理论上能收敛到全局最优。作业报告里如果能写出这一层对比老师一眼就能看出你不是背公式。2.2 LogisticModel 的核心代码拆解训练循环与参数更新拿资源里的模型实现来说几个关键方法配合得非常典型。核心训练代码如下import numpy as np class LogisticModel: def __init__(self, lr0.01, max_iter1000): self.lr lr self.max_iter max_iter self.w None self.b None def sigmoid(self, z): # 数值稳定性处理z过大时exp(z)会溢出 return 1.0 / (1.0 np.exp(-np.clip(z, -500, 500))) def loss(self, X, y): z np.dot(X, self.w) self.b p self.sigmoid(z) # 交叉熵损失加1e-8防止log(0) return -np.mean(y * np.log(p 1e-8) (1 - y) * np.log(1 - p 1e-8)) def fit(self, X, y): n_samples, n_features X.shape self.w np.zeros(n_features) self.b 0.0 for i in range(self.max_iter): z np.dot(X, self.w) self.b p self.sigmoid(z) dw np.dot(X.T, (p - y)) / n_samples db np.sum(p - y) / n_samples self.w - self.lr * dw self.b - self.lr * db if i % 200 0: print(fIter {i}, loss: {self.loss(X, y):.4f}) def predict(self, X, threshold0.5): p self.sigmoid(np.dot(X, self.w) self.b) return (p threshold).astype(int)代码逻辑的关键在 fit 方法里的这行梯度计算dw np.dot(X.T, (p - y)) / n_samplesp 是模型预测概率y 是真实标签(p - y) 就是残差。这条式子的来源是交叉熵对 w 求导后正好得到 X^T 乘以残差的均值——不复杂但手推一遍再写进报告很有说服力。参数方面需要注意的是 lr学习率和 max_iter最大迭代轮数。资源里默认 lr0.01对于标准化后的特征比较稳妥如果数据量小0.1 也能跑但会出现 loss 震荡具体我在后面避坑章节会展开。2.3 数据预处理在模型里的权重不标准化梯度下降会教做人这可能是整份作业里最容易翻车的一步。西瓜数据集的特征里有「含糖率」「密度」这种连续值量纲不同鸢尾花的四个特征里花瓣长度和花萼宽度的数值范围差距也很大。如果把原始值直接送进模型梯度方向会被大数值特征主导收敛速度极慢。我通常建议用均值方差标准化def standardize(X): mean np.mean(X, axis0) std np.std(X, axis0) return (X - mean) / std参数说明axis0 表示按列特征维度计算均值和标准差每一列特征独立标准化。这样处理之后所有特征都落在均值 0、方差 1 的分布里梯度下降各方向尺度一致收敛快得多。资源里两个脚本的数据加载部分是否做了这一步下载后需要确认——如果发现 loss 不降或预测全为同一类第一反应就应该是检查预处理。3. 西瓜数据集二分类实战从数据文件到混淆矩阵的完整落地3.1 西瓜数据集的特征构成与标签切分逻辑西瓜数据集是周志华《机器学习》里的经典案例特征是「色泽」「根蒂」「敲声」「纹理」「脐部」「触感」这些离散属性加上「密度」「含糖率」两个连续属性。期末作业里通常有两种处理方式一是把离散属性做数值编码二是只取连续特征做二分类。这份资源更偏向第二种逻辑更干净。数据加载代码通常长这样import pandas as pd from sklearn.model_selection import train_test_split data pd.read_csv(watermelon.csv) X data[[density, sugar_ratio]].values # 密度、含糖率 y (data[label] good).astype(int) # 好瓜归为正类 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 )train_test_split 的参数说明test_size0.3 表示拿出 30% 的样本做测试random_state42 固定随机种子保证每次运行划分一致——这一点对于期末作业很重要不然老师复现时每次准确率都不一样显得不严谨。资源里的 WaterRegress.py 核心就是这段逻辑加上 LogisticModel 的 fit/predict 调用整套流程在几十行代码内完成。3.2 训练后必做的三件事决策边界可视化、准确率、逐类对比这份资源的价值在于它不只是「训练完输出一个准确率」。对率回归模型最大的可视化优势是决策边界是一条直线可以直观画出来。常见做法是生成网格点把每个点的预测概率画成背景色import matplotlib.pyplot as plt x_min, x_max X[:, 0].min() - 0.1, X[:, 0].max() 0.1 y_min, y_max X[:, 1].min() - 0.1, X[:, 1].max() 0.1 xx, yy np.meshgrid(np.linspace(x_min, x_max, 100), np.linspace(y_min, y_max, 100)) Z model.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha0.6) plt.scatter(X[y0][:, 0], X[y0][:, 1], cblue, labelbad) plt.scatter(X[y1][:, 0], X[y1][:, 1], cred, labelgood) plt.legend() plt.show()np.c_ 的作用是把两个一维数组按列拼接成网格坐标矩阵尺寸是 (10000, 2)一次性预测 10000 个点的类别画出的边界非常平滑。这个可视化图放进作业报告比单纯贴一行 accuracy 得分要有力得多——老师能看到模型学到的分界长什么样也方便分析误判样本在边界哪一侧。除了准确率还建议算混淆矩阵。因为西瓜数据集样本量很小一共 17 条左右很可能出现测试集只有五六个样本的情况单一准确率参考意义有限。混淆矩阵能看出模型是偏向把坏瓜判成好瓜还是反过来。4. 鸢尾花多分类扩展一对多策略与数据加载的细节处理4.1 鸢尾花三分类为什么不能用原始的二分类实现鸢尾花数据集包含 Setosa、Versicolor、Virginica 三个品种每类 50 条样本特征为花萼长度、花萼宽度、花瓣长度、花瓣宽度。问题随之而来LogisticModel 的 sigmoid 输出是一个二分类概率处理不了三个类别。这个资源里 IrisRegress.py 的常见实现方式是一对多One-vs-Rest为每个类别训练一个二分类器第 k 个分类器把类别 k 当正类其余全当负类。预测时把样本分别丢进三个分类器取输出概率最大的那个作为最终类别。核心伪代码如下models [] for cls in range(3): y_binary (y cls).astype(int) model LogisticModel(lr0.05, max_iter1000) model.fit(X_train, y_binary) models.append(model) def predict_multi(X): probs np.array([m.sigmoid(np.dot(X, m.w) m.b) for m in models]) return probs.T.argmax(axis1)参数说明probs.T 将形状从 (3, n_samples) 转成 (n_samples, 3)每一行对应一个样本在三个分类器下的概率argmax(axis1) 取每行最大值的下标即为预测类别。这样改动很小但把二分类模型强行延伸到了多分类场景。报告中如果能提到「对率回归天然是二分类模型多分类需要 OvR 策略」是一个加分点。4.2 数据集的自动加载与乱序风险鸢尾花数据集在 sklearn 里可以直接加载from sklearn.datasets import load_iris iris load_iris() X, y iris.data, iris.target print(iris.DESCR) # 打印数据集说明load_iris 返回的 Bunch 对象包含 data、target、feature_names、target_names 等字段。iris.data 是 150x4 的特征矩阵iris.target 是 0、1、2 三类标签。但注意一点数据集本身是按类别顺序排列的——前 50 条全是 Setosa中间 50 条全是 Versicolor最后 50 条是 Virginica。如果不打乱就直接切分训练集测试集极端情况下测试集可能只包含某一类模型没见过的类别完全无法预测。因此训练前必须先手动打乱import numpy as np idx np.random.permutation(len(X)) X_shuffled, y_shuffled X[idx], y[idx]np.random.permutation 返回打乱后的索引数组再用它同时重排 X 和 y能保证特征和标签的对应关系不被破坏。或者直接用 train_test_split它内部会先打乱再切分random_state 参数控制随机性。资源里的 IrisRegress.py 如果走了 sklearn 的 train_test_split就不存在这个问题如果是手动切分这条坑必须避开。4.3 多分类评估准确率不足以说明问题时的补充指标鸢尾花数据类间存在一定重叠Versicolor 和 Virginica 在特征空间里有交叉所以即便模型收敛得很好也总有几个样本会被分错。只看 accuracy 会漏掉信息。建议补充每个类别的精确率Precision和召回率Recall可以用 sklearn 一行搞定from sklearn.metrics import classification_report print(classification_report(y_test, y_pred, target_namesiris.target_names))输出会是一个按类别划分的表格包含 precision、recall、f1-score、support 四列。如果某个类别的 recall 明显偏低说明模型更倾向于把它判成别的类此时再回看决策边界或特征分布能找到原因。作业报告里放这个表既能凑篇幅又比只写一个分数更有技术含量。5. 作业报告的写作组织把代码里的决策翻译成老师想看的推导过程5.1 报告结构建议从背景到实验一气呵成资源里附带的对率回归算法实践报告.doc是这份资源的加分项。因为期末大作业通常要求「代码 报告」双份交付很多学生代码跑通了报告却凑不满页数。看了资源里的报告框架大致可以归纳出老师想看的五段式结构问题定义数据集引出分类任务、模型原理sigmoid 推导与损失函数选择、实验设计特征预处理、训练测试切分、超参数设置、结果分析准确率、边界图、混淆矩阵、讨论误分原因与改进方向。写报告时一个常见错误是事无巨细地贴代码把整份 .py 文件复制进去老师看着也累。正确做法是只摘关键片段并配上文字说明比如「模型采用批量梯度下降更新参数学习率设置为 0.01迭代 1000 轮后损失收敛至 0.XX」把数值填进句子里比单独一张图有效。5.2 报告中必须出现的数学推导与手写公式对率回归的作业报告如果只有代码没有公式推导等于把最核心的得分点丢了。至少需要覆盖三处第一sigmoid 函数的定义式p 1 / (1 e^(-z))其中 z w^T x b第二交叉熵损失与极大似然估计的关系。一般写作对率回归用极大似然法估计参数单个样本的似然为 p^y (1-p)^(1-y)取负对数后得到交叉熵损失等价于最小化 -[y ln p (1-y) ln(1-p)]。第三梯度下降更新式w : w - lr * (1/N) * Σ x_i (p_i - y_i) b : b - lr * (1/N) * Σ (p_i - y_i)如果报告里能把这三组公式写出来并配上代码对应位置老师会认为你真正理解了自己提交的东西。我在批改课程设计时最明显的感受是能写出公式的学生和只会调包的学生分数差距比想象中大得多。5.3 实验对比部分单模型跑通不够加一组对照更出彩如果你想让报告看起来不只完成了基本要求可以加一组对照实验用 sklearn 自带的 LogisticRegression 跑同一份数据和手写实现对比准确率。通常两者差距不大但报告中可以借此讨论手写实现与库实现的一致性和差异——比如 sklearn 默认用了 L2 正则化而手写版没有这会导致在特征数多时出现过拟合倾向。这一段写出来报告的「分析」属性立刻提升一个层次。6. 避坑指南对率回归期末大作业最常见的五个翻车现场6.1 现象loss 一直在下降但准确率纹丝不动原因正负样本比例失衡或预测概率集中在 0.5 附近模型其实没有学到有效特征。西瓜数据集样本太少Epoch 跑完 w 和 b 的变化本来就有限。解决先看训练集和测试集的预测概率分布。如果大部分概率在 0.4~0.6 之间说明特征对标签的区分度不够回去检查是否做了标准化如果特征已经标准化考虑增大学习率或增加迭代轮数。6.2 现象训练时报错提示 np.dot 维度不匹配原因数据加载时 X 的形状是 (n_samples,) 而非 (n_samples, n_features)活生生少了一维。比如只取了单列特征没有用 .values.reshape(-1, 1) 整理成二维。解决在训练前打印 X.shape 确认形状。如果 X 是一维数组就用X X.reshape(-1, 1)补成列向量。词二维矩阵每一行是一个样本、每一列是一个特征这是所有矩阵运算的前提。6.3 现象loss 输出为 nan 或 inf原因sigmoid 中 exp(-z) 溢出或交叉熵里 log(0)。z 绝对值大于 700 时 exp 函数直接返回 inf1/inf 变 0后续计算全部崩掉。解决代码里用 np.clip(z, -500, 500) 把 z 限制在安全范围内log 里加 1e-8 防零。手写实现时这两处防护不能省资源里的 LogisticModel.py 已经处理过但如果你改成自己的代码务必保留这两行。6.4 现象鸢尾花测试集准确率低但训练集准确率很高原因数据没打乱train_test_split 切出来的训练集和测试集类别分布不一致另一个常见原因是多分类的 OvR 实现里pandas 或 numpy 索引对不上导致标签错位。解决用from sklearn.utils import shuffle打乱或者确保 random_state 固定后重新切分。多分类模型逐个训练后把每个模型对测试集的预测概率拼成矩阵检查 argmax 的结果和 y_test 的类别编码是否一致。6.5 现象作业报告里没有可视化图老师追问数据分布原因偷懒只写了 loss 曲线没有画决策边界、类别散点图或混淆矩阵。解决matplotlib 画三张图——第一张训练损失随迭代次数变化第二张西瓜数据集决策边界第三张鸢尾花测试集混淆矩阵热力图。三张图放进报告实验部分立刻充实。图像保存用plt.savefig(result.png, dpi150)笔记型或脚本型提交都能直接用。7. 从二分类到多分类的进阶验证用学习率与迭代轮次判断模型收敛质量拿到这份资源后不要急着跑完就算交差。我自己每次拆训练代码都会先把训练过程记录下来画出一条 loss 曲线——这一步能直接告诉你模型到底学得怎么样。把 LogisticModel.fit 里每轮 loss 存到一个列表里结束后就哭着画图losses [] for i in range(model.max_iter): # 模拟一轮训练 losses.append(model.loss(X_train, y_train)) plt.plot(losses) plt.xlabel(Iteration) plt.ylabel(Loss) plt.title(Training Loss Curve) plt.show()这条曲线至少有三种值得关注的形态。第一种loss 快速下降后趋于平缓这是健康的收敛说明学习率和迭代次数匹配第二种loss 震荡不降说明学习率偏大需要调小第三种loss 持续缓慢下降没有平台期训练轮数不够加大 max_iter 再跑。我通常会把 lr 从 0.1 往下按 0.05、0.01、0.005 三档扫一遍。0.1 在西瓜或鸢尾花这种小规模数据上多半会震荡0.01 最稳0.005 收敛稍慢但结果更平滑。这个「扫学习率」的过程写进报告就是一次标准的超参数对比实验——老师会看到你不是随便设了一个值就跑。最后说个习惯我每次复现这类课程设计代码都会在预测前打印出前五个样本的原始概率值。如果模型对两个类输出概率是 0.98 和 0.76对比一目了然如果全是 0.51、0.49 之类的分数说明模型还很「犹豫」这时候准确率再高也不可信。从那以后我每次跑完训练都强制走一遍「打印概率分布 → 画 loss 曲线 → 固定 random_state 重新训练 → 对照两次结果」这个流程防止哪次随机划分碰巧分到一个特别容易的测试集给自己造成模型很强的错觉。希望帮到你。本文还有配套的精品资源点击获取