ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

对数几率回归从二分类到多分类:西瓜与鸢尾花数据集的Python实战

对数几率回归从二分类到多分类:西瓜与鸢尾花数据集的Python实战 简介基于对数几率回归模型实现西瓜与鸢尾花分类识别的期末大作业资料包面向计算机、数据科学、人工智能等专业学生及教师覆盖课程设计、期末大作业与毕设参考场景。压缩包共30个文件、约544KB主要包含Python源码(.py)、Jupyter Notebook交互脚本(.ipynb)、作业报告(.doc)和项目说明(.md)并附有工程配置文件便于还原项目结构。代码已经过验证稳定可运行现有295人参与学习浏览。通过源码中的详细注释和报告可以快速理解对数几率回归原理、数据预处理、训练评估全流程同时掌握针对西瓜与鸢尾花数据集的分类实现及运用sklearn或手写模型完成任务的完整思路项目框架也方便迁移至其他分类数据集适合入门进阶和二次开发。整体目录组织清晰按模块区分源码、脚本、报告和文档能有效辅助撰写实验报告或深化对回归模型的认识。1. 用对数几率回归同时做西瓜和鸢尾花先解决二分类到多分类的问题周志华《机器学习》西瓜书的课后题和期末大作业里出现频率最高的组合就是“西瓜数据集 鸢尾花数据集”。这两个数据集看着简单但如果写代码前没想清楚一件事后面全乱套对数几率回归天然输出的是二分类概率而鸢尾花有 3 个类别。直接调用 sklearn 的LogisticRegression当然能跑出结果但你写不出报告里最重要的一段“为什么这样扩展”也会在答辩时被问到卡壳。本文按我完成同类作业的顺序拆解先讲原理再处理两个数据集的差异最后给出可运行源码、代码注释规范和报告写法适合正在写期末大作业、想拿高分而不是只求过查重的读者。2. 对数几率回归的核心原理logit 变换、损失函数与梯度的推导做大作业最容易犯的错误是一上来就写代码把原理抄到报告里充字数。答辩老师通常只问三个问题为什么用对率回归而不是线性回归、损失函数怎么来的、梯度下降朝哪个方向走。这三个问题都在这章里。2.1 为什么对率回归不是“回归”而是分类线性回归的输出值是整个实数域上的连续值它假设 y 和特征 x 是线性关系。但西瓜“好瓜/坏瓜”这个标签只有 0 和 1 两种取值直接用线性回归拟合输出的可能是 -0.3 或 1.4没有概率含义。核心技术动机是要在实数域和 (0, 1) 区间之间建立一个可导映射。常见做法是引入 logit 变换z w^T x b p 1 / (1 exp(-z))也就是把线性组合的得分 z 输入 sigmoid 函数。反过来看当 p 被解释为“好瓜”的概率时对 p 做 logit 变换得到log(p / (1 - p)) w^T x b这就是“对数几率”三个字的来源logit(p) 等于线性回归的表达式所以它在本质上仍然是线性模型只是在输出端加了非线性映射。对分类边界而言p 0.5 对应 z 0决策边界永远是平面这是它比 SVM、决策树好讲清楚的地方。2.2 交叉熵损失函数和梯度方向如果沿用线性回归的均方误差得到的损失函数关于参数是非凸的梯度下降容易停在局部最优。对率回归的约定是用最大似然估计推导交叉熵损失L(w, b) -sum[y * log(p) (1 - y) * log(1 - p)]整体除以样本数 m 得到平均损失。对参数求导后梯度是gw (1/m) * X^T (p - y) gb (1/m) * sum(p - y)这个式子漂亮的地方在于梯度等于“预测概率与真实标签的误差”乘特征。预测越不准、特征值越大参数更新幅度越大预测完全准确时梯度为 0。下面这个表是课堂上通常会列出的对比损失函数对正确分类样本的惩罚梯度是否含有 p-y 因子是否建议用于对率回归均方误差乐观样本也可能被大改否不建议交叉熵概率接近正确标签时惩罚趋近 0是建议2.3 从二分类到多分类OvR 与 softmax 的取舍鸢尾花数据集的 3 个类别是 setosa、versicolor、virginica二分类的对率回归不能直接用。作业里最常见的扩展是 OvROne vs Rest训练 3 个二分类器每个分类器负责把“某一类”和“其余所有类”区分开。预测时把样本分别送进 3 个分类器取输出概率最大的类别。sklearn 里直接在LogisticRegression里设置multi_classovr或multinomial都能跑。区别在于后者是 softmax 回归一次训练学出 K 组权重原理上等价于 K 个二分类器的组合但梯度更新同时考虑所有类别。期末作业如果只要求实现对数几率回归OvR 更容易在报告中解释因为可以给出每个二分类器的准确率。3. 西瓜与鸢尾花识别分类的数据预处理编码、缩放与训练集划分很多人拿到.zip源码后第一步就跑去跑模型完全不看数据长什么样。实际上两个数据集的属性构成差异很大不处理直接喂给对率回归结果会难看且不稳定。3.1 西瓜数据集的特征编码连续值和离散值怎么并存西瓜数据集周志华书中表 4.1 对应版本包含两类属性连续属性和离散属性。典型的离散属性是色泽、根蒂、敲声、纹理、脐部、触感典型连续属性是密度、含糖率。直接给离散属性标 0/1/2 对线性模型是一种误导因为“青绿0、乌黑1、浅白2”会隐含顺序关系。我一般先把离散属性做 one-hot连续属性原样保留。处理后每条样本变成一个固定长度的数值向量原始属性类型处理方式示例色泽离散One-Hot青绿 - [1, 0, 0]根蒂离散One-Hot蜷缩 - [1, 0, 0]纹理离散One-Hot清晰 - [1, 0]密度连续原样或缩放0.697含糖率连续原样或缩放0.460好瓜目标映射为 0/1是 - 1代码里最省事的方式是直接用 pandas 的get_dummies或者手工建立一个字典做映射。注意 one-hot 之后特征维度会膨胀到接近 20 维而西瓜数据集只有 17 条左右训练集更小对率回归很容易被稀疏特征带着走这是后面调正则化参数时要留意的。3.2 鸢尾花数据集的读取与数据划分鸢尾花数据集在 sklearn 里自带共 150 条样本、4 个连续特征、3 个类别每个类别 50 条。相比西瓜数据集它干净得不需要做太多编码工作重点在于训练集与测试集的划分策略。下面的代码直接读取并查看基本信息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 print(df.head()) print(df.groupby(target).size())这段代码先加载数据再用DataFrame包装并打印前 5 行和每个类别的样本数量。注意鸢尾花的 4 个特征量纲不同花萼长宽大致在 3.0~7.9 范围花瓣长宽大致在 1.0~6.9 范围。看起来差不多但对梯度下降的影响不一样。特征值更大的维度在计算梯度时会获得更大的更新量最终让模型偏向某个特征。所以标准流程是划分训练集和测试集再在训练集上做标准化from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X_train, X_test, y_train, y_test train_test_split( iris.data, iris.target, test_size0.2, random_state42, stratifyiris.target ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)stratifyiris.target保证三个类别在训练集和测试集中的比例与原始数据一致。标准化用fit_transform只在训练集上计算均值和方差测试集仅用transform防止信息泄漏。西瓜数据集如果做 one-hot离散列不需要标准化连续列密度和含糖率可以做同样的处理。3.3 特征缩放的必要性一个可观察的小实验如果不做标准化就把数据直接丢进手写的梯度下降常见现象是损失下降非常慢或者来回震荡。原因是梯度方向被大数值特征主导等值线变成狭长的椭圆形步长稍大就越过最优点步长太小则收敛极慢。你可以做个对照组把n_iter设为 500分别打印标准化前后前 20 轮的损失值报告的“实验分析”部分立刻有内容可写。标准化还有一个额外好处正则化项对每个特征的惩罚一致。L2 正则化默认所有特征权重共享一个惩罚系数如果特征量纲不同等价于在不重要的特征上惩罚过度在重要特征上惩罚不足。这一点在西瓜数据集上尤其明显因为 one-hot 产生的大量 0/1 特征和连续特征并存。4. 对数几率回归的 Python 源码实现从 numpy 手写到 sklearn 对照期末作业的源码通常要包含两个层次一是自己实现的模型证明你理解原理二是与成熟库的对照实验证明你的实现正确。这一章给出两者并解释每个参数的意义。4.1 用 numpy 手写一个对数几率回归类先实现最核心的 sigmoid、交叉熵损失、梯度下降三件套。这个版本只处理二分类接口模仿 sklearn方便后面替换import numpy as np def sigmoid(z): return 1.0 / (1.0 np.exp(-z)) class LogisticRegressionDIY: 二分类对数几率回归梯度下降法求解 def __init__(self, lr0.1, n_iter1000, tol1e-6): self.lr lr # 学习率 self.n_iter n_iter # 最大迭代次数 self.tol tol # 损失变化阈值 def fit(self, X, y): n_samples, n_features X.shape self.w np.zeros(n_features) self.b 0.0 self.losses [] for i in range(self.n_iter): z X self.w self.b p sigmoid(z) loss -np.mean(y * np.log(p 1e-12) (1 - y) * np.log(1 - p 1e-12)) self.losses.append(loss) dw (X.T (p - y)) / n_samples db np.mean(p - y) self.w - self.lr * dw self.b - self.lr * db if i 0 and abs(self.losses[-2] - loss) self.tol: break return self def predict_proba(self, X): return sigmoid(X self.w self.b) def predict(self, X, threshold0.5): return (self.predict_proba(X) threshold).astype(int)代码逻辑不复杂z X self.w self.b一次性算出所有样本的线性得分p是对应的概率dw和db就是上一章推导的梯度提前停止条件用相邻两轮损失的绝对差实现。注意np.log(p 1e-12)是数值保护防止p取到 0 时出现无穷大。使用这个类时有两个常见错误。第一个是忘记把 y 转为 0/1 整数西瓜数据集里如果标签是“是/否”直接送进去会报错或得到错误梯度。第二个是学习率设得太大比如lr1.0损失在前几轮会飘到几千之后np.exp溢出。对标准化后的数据集lr0.1通常是安全的起点。4.2 多分类改造OvR 手写循环处理鸢尾花三分类时我在作业里采用 OvR 策略对每个类别训练一个二分类器预测时比较三个分类器的正类概率。实现不需要改上面的类循环即可class OvRLogistic: 用多个二分类对数几率回归实现多分类 def __init__(self, lr0.1, n_iter1000): self.lr lr self.n_iter n_iter self.classifiers {} def fit(self, X, y): self.classes np.unique(y) for c in self.classes: y_binary (y c).astype(int) clf LogisticRegressionDIY(lrself.lr, n_iterself.n_iter) clf.fit(X, y_binary) self.classifiers[c] clf return self def predict(self, X): scores np.column_stack([ clf.predict_proba(X) for clf in self.classifiers.values() ]) return np.array(list(self.classifiers.keys()))[np.argmax(scores, axis1)]这里column_stack把每个分类器输出的概率按列拼接argmax选最大的下标。注意直接用概率做比较在理论上可行但如果三个分类器在不同样本上的概率尺度差异较大会更稳的做法是输出每个类的得分后再做 softmax 归一化。作业中解释 OvR 时画一张“类别 0 与其余类”的示意图就够了。4.3 sklearn 对照版参数怎么设才不算瞎调对照组的代码很短但要写清楚参数含义不然报告中“参数分析”一段无话可说from sklearn.linear_model import LogisticRegression # 对西瓜二分类 clf_watermelon LogisticRegression( C1.0, # 正则化强度的倒数越小正则化越强 solverlbfgs, # 适合中小数据集默认即此 max_iter500 ) # 对鸢尾花三分类显式指定 OvR clf_iris LogisticRegression( C1.0, solverliblinear, multi_classovr, max_iter500 )C的常见取值范围是 0.01、0.1、1、10。对西瓜数据集这种高维度小样本one-hot 之后特征数比样本数还多我建议从C0.1开始试看测试集准确率变化。solver的选择不需要过度纠结小数据集上用lbfgs或liblinear区别不大但手写梯度下降属于sgd思路如果想和三组结果立体比较可以在 sklearn 里用solversag它也是梯度法家族。两个模型的对比结果可以整理成类似下面的表数据集模型测试集准确率F1宏平均西瓜手写实现0.7860.799西瓜sklearn0.8000.812鸢尾花手写 OvR0.9000.899鸢尾花sklearn OvR0.9330.932数值会因随机种子和数据划分而变但只要手写版和 sklearn 版在同一个数量级就说明实现基本正确。如果差距超过 10 个百分点优先怀疑是否忘了标准化其次是检查梯度更新公式里的括号和除法。5. 期末作业提交前的自查报告、注释与模型验证最后一步不是写新功能而是把现有的东西打磨成可提交状态。作业报告和注释的质量往往决定了答辩时老师是追问还是放你走。5.1 作业报告的结构设计一份够用的报告不需要长篇大论建议按下述模块组织每个模块控制在一页以内问题定义两个数据集各是什么分类任务、模型原理logit 变换、损失函数、梯度推导、数据集与预处理表格展示编码前后维度变化、实验结果准确率表、训练损失曲线、结论与改进。报告里最容易出彩的地方是画训练损失曲线。在手写实现中记录self.losses用 matplotlib 画出来并对比两种学习率下的曲线。这个图能同时说明“梯度下降能收敛”和“学习率参数敏感”两个点比大段文字有效。5.2 代码注释规范和提交前检查清单注释不需要每一行都写但要在类和函数头部写明输入输出在关键公式处标注对应哪一行数学表达式。比如dw (X.T (p - y)) / n_samples上一行写“公式梯度 X^T(p-y)/m”老师扫一眼就能对上。提交前的自查我按这个顺序过代码里是否有绝对路径如C:/Users/xxx/有则改成相对路径模型训练是否固定random_state保证结果可复现两个数据集的预处理链路是否分离西瓜有没有误用鸢尾花的标准化器测试集准确率和报告中的数字是否一致避免报告写 0.9 但代码跑出 0.7最后一个自查技巧在zip重新解压到一个新目录只保留说明文档里列出的依赖包用python main.py全流程跑一遍。能在裸环境跑通才是真正的高分交付。本文还有配套的精品资源点击获取
返回列表