ARTICLE DETAIL

资讯详情

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

基于Python机器学习的葡萄酒质量检测项目实战:从数据预处理到模型评估

基于Python机器学习的葡萄酒质量检测项目实战:从数据预处理到模型评估 简介一份面向高校计算机、电子信息、数学等专业学生的机器学习实践资料适合用于课程设计、期末大作业或毕业设计参考。项目以葡萄酒质量检测为场景覆盖数据读取、缺失值处理、特征相关性分析、标准化、模型训练与评估等完整环节代码结构清晰且保留了关键注释便于理解随机森林、逻辑回归等算法的实际调用与调参方式。压缩包内以Python源码、CSV数据集及说明文档为主整体约26.59MB下载后可快速在本地环境运行复现。目前已有2696人学习浏览资料严格按“源码数据说明文档”组织既可从零搭建一个完整的机器学习项目也可作为论文写作与答辩展示的参考蓝本。说明文档对运行环境、依赖库和操作步骤作了交代尤其适合具备一定Python基础、想要独立完成类似分析任务的读者。1. 一份葡萄酒质量检测的 .rar 里到底装了什么拿到“基于Python机器学习的葡萄酒质量检测项目源码数据说明文档.rar”先别急着解压跑代码你得先搞清楚这个项目到底在做什么。它对应的是机器学习里最经典的一类表格数据任务根据葡萄酒的理化指标比如固定酸度、挥发酸度、残糖、pH 值、酒精浓度这些可测量的数值预测一瓶酒的质量评分。数据来自 UCI 的 Wine Quality 数据集分红葡萄酒和白葡萄酒两份总共六千多条样本每条样本带 11 个特征和一个 0 到 10 分的质量标签。它不是图像识别也不涉及传感器和品酒师纯粹是“读表、练模型、看评估”的入门级实战项目。适合刚学完 Python 基础、想用 sklearn 把完整机器学习流程跑一遍的人也适合做课程设计需要一份有数据有代码有文档的源码包。它的价值在于数据干净、特征少、训练速度快同时又能踩到类别不平衡和指标选择这些真实工程坑。2. 先把数据看明白字段含义、分号分隔与标签分布2.1 红葡萄酒和白葡萄酒为什么不能一上来就合并Wine Quality 数据集的原始格式是 CSV但分隔符是分号而不是逗号这是项目里的第一个暗坑。直接用默认的 pd.read_csv() 去读所有字段都会挤在一列里后续根本没法做特征拆分。另一个更关键的坑是red 和 white 两份样本来自不同工艺白葡萄酒的残糖和总二氧化硫浓度明显高于红葡萄酒理化特征分布差异很大。如果直接把两份拼在一起训练模型会先学会“区分红白”再去学质量给你一个看起来很准但完全不可用的结果。这个场景我放到第 5 章专门讲这里先把两份数据分开读、分开看。import pandas as pd red pd.read_csv(winequality-red.csv, sep;) white pd.read_csv(winequality-white.csv, sep;) print(red.shape, white.shape) print(red.columns.tolist()) print(red[quality].dtype, white[quality].dtype)sep; 是打开这份数据的标准姿势漏掉它整个读取就废了。red 是 1599 行 12 列white 是 4898 行 12 列白葡萄酒样本接近红葡萄酒的三倍。columns 里能看到 11 个特征加 1 个 quality 标签列名和 UCI 官方的字段命名一致。quality 列是 int64 类型说明标签是离散整数评分不是连续浮点数这意味着这个字段既可以当回归目标也可以做阈值切分转成分类标签两种玩法后面会展开。下面这张表把 11 个特征的含义和单位列出来写报告和调参时都会用到字段含义常见单位fixed acidity固定酸度主要是酒石酸等不挥发酸g/dm³volatile acidity挥发酸度醋酸为主过高会有醋味g/dm³citric acid柠檬酸含量g/dm³residual sugar残糖发酵后剩下的糖分g/dm³chlorides氯化物含量和咸味相关g/dm³free sulfur dioxide游离二氧化硫起抗氧化作用mg/dm³total sulfur dioxide游离和结合态二氧化硫总量mg/dm³density密度和酒精、糖分相关g/cm³pH酸碱度0~14sulphates硫酸盐含量和发酵有关g/dm³alcohol酒精体积分数% volquality品酒师打出的质量分0~10 整数2.2 描述统计与标签分布质量分不是均匀的接下来要确认取值范围和分布。quality 的理论范围是 0 到 10但实际数据只有 3 到 9红葡萄酒集中在 5 和 6白葡萄酒集中在 6。真正意义上的“好酒”也就是 7 分以上的样本占比很低。这个分布直接决定后面用分类还是回归以及用什么评估指标。print(red[quality].value_counts().sort_index()) print(\n--- white ---) print(white[quality].value_counts().sort_index())value_counts() 没加 normalizeTrue 时输出的是绝对数量加了才能直接看出比例。红葡萄酒 7 分以上约两百多条占总样本的 13% 上下白葡萄酒更夸张好酒占比不到 9%。这种不平衡程度在二分类里不算极端但已经足够让 accuracy 变得有迷惑性。这里先给结论后面做二分类时标签切在“7 分及以上 好酒其余 普通酒”而不是用 3 到 9 做九分类。九分类的中间分数样本特征重叠严重模型很难学出边界而且每个类别的样本量太少评估指标容易变成一团乱账。2.3 缺失值与重复样本这份数据集的隐藏质量坑Wine Quality 这份经典数据没有缺失值这是它适合入门的原因之一。但重复样本是真实存在的坑。传感器重复测量、数据录入重复都可能导致同一行出现多次而这些重复行会悄悄抬高交叉验证分数因为同一条样本同时跑进了训练集和测试集模型相当于提前见过答案。print(red.isnull().sum().sum(), white.isnull().sum().sum()) print(red duplicated:, red.duplicated().sum()) print(white duplicated:, white.duplicated().sum()) red_clean red.drop_duplicates().reset_index(dropTrue) white_clean white.drop_duplicates().reset_index(dropTrue)isnull().sum().sum() 是快速看整张表有没有空值的惯用写法第一次 sum 按列求和第二次 sum 把结果汇总成一个数字。duplicated() 判断整行是否重复返回布尔序列sum 一下就得到重复行数量。两份数据都有重复行白葡萄酒尤其多建议直接 drop_duplicates()然后接 reset_index(dropTrue) 重建索引否则后面 train_test_split 和按索引取数时容易出乱子。reset_index 这个参数很多人漏掉一旦做 merge 或按位置取特征就会踩坑。提示重复样本清洗要在切分训练集和测试集之前做清洗之后再 split不然测试集里仍可能残留和训练集一模一样的样本评估结果虚高。3. 特征工程与基线模型相关性筛查和两个最小可跑模型3.1 先看特征和标签的相关性再决定要不要删特征11 个特征里常见做法是先画相关性热力图看特征之间、特征与 quality 之间的线性关系。如果两个特征强相关比如 density 和 alcohol通常保留一个解释性更强的就行如果某个特征和标签几乎没关系可以先留在模型里做对照而不是急着删。我一般先跑 corr() 再配合业务理解做判断很少一上来就无脑筛特征。import seaborn as sns import matplotlib.pyplot as plt df red_clean.copy() corr df.corr() plt.figure(figsize(10, 8)) sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm, center0) plt.show() print(corr[quality].sort_values(ascendingFalse))corr() 默认用皮尔逊相关系数适合先看线性关系。fmt.2f 让格子里的数值只显示小数点后两位不然 11×11 的格子会挤成一团。center0 让色带以 0 为中点负相关和正相关一眼能分出来。排序结果里 alcohol 通常和 quality 最正相关volatile acidity 最负相关这两个特征在所有 Wine Quality 实验里都是主角。density 和 alcohol 之间有强负相关也是预期内的因为酒精密度低于水酒精越多整体密度越低。这里要留个心眼皮尔逊相关只能捕捉线性关系树模型能学非线性交互所以相关性分析是用来理解数据、辅助选特征的不是筛选特征的唯一依据。3.2 最小基线逻辑回归和随机森林怎么选模型选型别一上来就贪复杂。逻辑回归是线性模型配合标准化之后训练快、可解释性强每个特征的系数直接对应“这个指标上升会推高还是压低好酒概率”随机森林是树模型不需要标准化能捕捉非线性关系比如“酸度适中最好过高过低都不行”这种区间效应。对这个数据集逻辑回归在二分类上通常已经能到 0.75 到 0.80 的 AUC随机森林能再高几个点但代价是训练时间和调参成本。入门建议两个都跑用同一套切分和同一套指标来对比这才是“选型”而不是“猜型”。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report X df.drop(quality, axis1) y (df[quality] 7).astype(int) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) lr LogisticRegression(max_iter1000, C1.0) lr.fit(X_train_scaled, y_train) print(classification_report(y_test, lr.predict(X_test_scaled)))train_test_split 里的 stratifyy 非常关键它让训练集和测试集里的好酒比例保持一致。没有它随机切分可能把测试集切得好酒过多或过少评估结果忽高忽低。random_state42 是约定俗成的随机种子固定下来方便别人复现你的结果。StandardScaler 的 fit_transform 只用在训练集测试集只调 transform这是一个从源头防止数据泄漏的肌肉记忆。max_iter1000 是因为 sklearn 默认 100 次迭代在逻辑回归上经常不收敛会弹 ConvergenceWarning调大只是让它正常跑完不是玄学。C1.0 是正则化强度的默认值先不动等第 4 章调参再改。3.3 评估指标别只盯 accuracy混淆矩阵和 F1 是更诚实的朋友好酒占比不到 15%意味着即使模型把所有样本都判成“普通酒”准确率也能有 85% 以上。这个数字会骗人。对质量检测场景我们真正关心的是在误判多少瓶普通酒的代价下找到了多少瓶真正的好酒。这正是精确率和召回率的权衡。精确率是“预测为好酒的里面有多少是真好酒”召回率是“真正的好酒里面有多少被找出来”。只看准确率模型完全可以啥都不学还显得很好看。from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay cm confusion_matrix(y_test, lr.predict(X_test_scaled)) ConfusionMatrixDisplay(cm, display_labels[normal, good]).plot()confusion_matrix 返回一个 2×2 矩阵第一个维度是真实标签第二个维度是预测标签。矩阵右下角是好酒被正确召回的数量左下角是把普通酒当好酒也就是假阳性右上角是把好酒漏掉也就是假阴性。不同业务目标对这四个数的权重完全不同。如果你想做一个“筛酒”工具宁可多推荐几瓶普通酒也不该漏掉好酒那就优先提高召回率后面可以往下调概率阈值。入门阶段最保险的姿势是先看 classification_report 里的 F1 分数再看 ROC-AUC最后结合业务场景调整阈值而不是被训练准确率带着走。4. 把质量分转成好酒/普通酒二分类阈值切分与模型对比4.1 三种常见的标签切法哪一种最常用标签切分是被低估的决策点切法不同任务难度完全不同。常见做法有三种第一种是 quality 7 为好酒其余为普通酒这是大多数教程和课程设计的默认切法好酒占比在 10% 上下样本量够用第二种更严格quality 4 为差酒、 7 为好酒把 5 和 6 的中间地带丢掉只保留两端做二分类数据量会掉到原来的 60% 左右第三种是直接在 3 到 9 上做回归预测具体分数。对入门项目我一般推荐第一种理由很简单它保留了全部样本好酒比例虽然低但还在可控范围而且和“品酒师打分是否及格”的业务直觉一致。df[label] (df[quality] 7).astype(int) print(df[label].value_counts(normalizeTrue))(df[quality] 7).astype(int) 把布尔值转成 0/1 整数这就是二分类的 y。value_counts(normalizeTrue) 输出比例这里能看到好酒占比。第二种两端切法在论文或严格对比中有价值但它把中间分数直接扔掉训练样本变少实际部署时 5 分酒到底算好还是坏又成了问题所以我只在实验说明时用不推荐放进正式项目。第三种回归路径不是不能做只是评估上要换成 RMSE 和 R²和分类完全两套逻辑入门阶段先别两条线同时推进。4.2 随机森林和逻辑回归的横向对比用同一份数据说话既然两个模型都要跑最省事的方法是写一个函数把训练和评估封装起来然后对比结果。这也是源码包里常见的组织方式。逻辑回归取标准化后的数据随机森林取原始数据其他超参数先全部用默认值。注意两份 classification_report 要在同一份测试集上生成否则对比没有意义。rf RandomForestClassifier(n_estimators200, random_state42, n_jobs-1) rf.fit(X_train, y_train) print(classification_report(y_test, rf.predict(X_test)))随机森林不需要标准化这来自树模型的机制每次分裂只对单个特征做排序比较特征量纲不影响分裂点选择。n_estimators200 是森林里决策树的数量不是越多越好在这个量级上 200 棵已经能稳定收敛再往上边际收益很小。n_jobs-1 表示用满所有 CPU 核训练速度快很多。对比两份报告时逻辑回归的精确率一般偏高随机森林的召回率通常更好。原因也好解释逻辑回归在类别不平衡下更容易把决策边界推到多数类一侧树模型对边界样本更敏感愿意多承担一些误报去换召回。如果随机森林效果大幅碾压逻辑回归先别急着高兴回头查一下是不是红白混训或者数据泄漏造成的这是第 5 章的重点。4.3 超参数初始值怎么给一张参数表解决“从哪开始调”调参不是拍脑袋。每个参数都该有个合理的初始范围和粗调方向。下表给出我在这类表格数据任务里的常用初始值适用于葡萄酒质量检测这种几千条样本的规模。模型参数初始值说明 / 粗调方向LogisticRegressionC1.0越小正则越强类别不平衡时试 0.1 ~ 10LogisticRegressionmax_iter1000默认 100 常不收敛调大即可LogisticRegressionsolverlbfgs小数据集默认即可不用换库RandomForestClassifiern_estimators200100 ~ 500超过 500 边际收益很小RandomForestClassifiermax_depthNone先不限制过拟合再限到 10 ~ 20RandomForestClassifiermin_samples_split2过拟合时提到 5 ~ 10能明显压方差max_depthNone 表示让树长到不能再长在小数据集上通常表现得还可以但如果训练集准确率接近 100% 而测试集明显下降就是过拟合信号把它限制到 10 到 20 是最快的修正手段。min_samples_split 控制内部节点最少要多少样本才能继续分裂提高它等于限制树长太细过拟合时优先动这个。逻辑回归的 C 值管正则化强度C 越小惩罚越强特征多但样本少的时候往小调特征少样本够的时候 C1 就够用。如果不想手调GridSearchCV 是最快的粗调方案但注意别把网格开太大参数组合会指数增长。常见做法是先拿 max_depth 和 min_samples_split 做一次小网格确认过拟合方向后再定其他参数。网格搜索跑完一定要回到测试集上重新评估一次而不是直接采用网格内的分数否则同样有数据泄漏风险。5. 避坑指南葡萄酒质量检测项目最容易翻车的 5 个地方5.1 红白混训导致的“假高分”现象把 red 和 white 直接 pd.concat 成一份训练后准确率直奔 95% 以上甚至做九分类每个分数都预测得挺准。 原因红白两份数据的理化特征分布差异太大模型学到的是“这份样本像红还是像白”而不是“这酒质量如何”。特征重要性排序里 residual sugar 和 total sulfur dioxide 冲到最前就是典型信号。 解决回到第 2 章的思路红白分开建模。如果一定要合并显式加一列 type 特征区分品种再用交叉验证观察分数是否明显回落。如果合并后的分数从 95% 掉回 80% 左右说明之前那 95% 里掺了水分模型学到了不该学的分组信息。5.2 直接用 3~9 分做九分类模型一团浆糊现象classification_report 里大部分类别的 F1 在 0.2 以下只有样本量最大的 quality5 和 6 看起来能看混淆矩阵里预测结果全挤在中间分数。 原因九分类类别太多相邻分数的样本特征重叠严重靠 11 个理化指标很难区分“5 分酒”和“6 分酒”的边界。每个类别的训练样本也被摊薄模型学不到稳定模式。 解决把任务降成二分类好酒/普通酒是最稳做法。如果业务上确实需要分数就走回归预测连续评分再按分数区间映射回等级而不是硬做九分类。这个切换能把 F1 从 0.2 拉到 0.6 以上代价只是损失一点粒度。5.3 忘记标准化直接跑逻辑回归和 SVM现象代码能跑但终端刷出 ConvergenceWarning或者 SVM 的预测结果几乎全是普通酒分类报告里正类全部阵亡。 原因fixed acidity、alcohol 和 total sulfur dioxide 的量纲差了几十倍梯度下降在未缩放的尺度上很难收敛SVM 依赖距离计算大尺度特征直接碾压小尺度特征模型等于只看酒精这一个字段。 解决训练前必须做 StandardScaler而且只对训练集 fit测试集只 transform。树模型不受影响但为了流程统一建议养成“数值特征先缩放再进模型”的固定习惯。遇到聚类或 KNN 类算法同理这些基于距离的方法对尺度极其敏感。5.4 accuracy 好看但好酒一瓶都没找出来现象打印准确率 87%检查混淆矩阵发现右下角为 0模型把所有测试样本都判成普通酒。 原因好酒占比太低全判普通酒的策略在 accuracy 上就是 87%模型完全没学到好酒的模式分类器成了摆设。 解决别只看 accuracy。分类报告里的 recall 行才是检测能力的真实体现。好酒漏检率高就提高召回率比如把 predict_proba 输出的概率阈值从 0.5 往下调让更多“半信半疑”的样本进入好酒类。漏报和误报的权衡由业务场景决定没有固定最优值。5.5 环境依赖版本不一致源码跑不起来现象按说明文档的 import 语句跑报 ModuleNotFoundError或者 AttributeError 提示某个模型对象没有某个方法。 原因sklearn 从 0.19 到 1.x 改了很多 API 名字Python 3.7 和 3.10 下部分依赖的兼容行为也不一样。很多流传的源码是按两三年前的版本写的直接拿新版环境跑必然报错。 解决先建虚拟环境再装依赖不要直接污染系统 Python。一个能稳定复现这套代码的起点组合是 Python 3.9 pandas 1.5 scikit-learn 1.2。新手最常见的卡点其实是环境面板vscode 里没选对解释器pip 装好了包但 import 不到这类问题不是代码问题。把 python 装好、解释器指到虚拟环境、依赖按版本装齐这一步能省掉后续 80% 的排错时间。6. 把训练好的模型变成一句能用的预测函数最后给一个收尾用的最小模块。训练完模型后项目源码一般会把它保存成文件再写一个加载函数供业务调用。保存用 joblib 最省事它对 sklearn 对象的兼容性比 pickle 更稳不会出现跨版本加载失败的问题。import joblib import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier # 假设 df 已清洗、X_train 已标准化演示训练 保存 加载 model RandomForestClassifier(n_estimators200, random_state42, n_jobs-1) model.fit(X_train, y_train) joblib.dump(model, wine_model.pkl) loaded_model joblib.load(wine_model.pkl) def predict_wine(features, scalerNone): x np.array(features, dtypefloat).reshape(1, -1) if scaler is not None: x scaler.transform(x) prob loaded_model.predict_proba(x)[0, 1] return good if prob 0.5 else normal, round(prob, 4) sample [7.4, 0.70, 0.00, 1.9, 0.076, 11.0, 34.0, 0.9978, 3.51, 0.56, 9.4] print(predict_wine(sample))predict_proba 返回形状为 (1, 2) 的矩阵取 [0, 1] 就是“好酒”那一类的概率。阈值 0.5 是默认分界业务上可以调整比如做高召回筛酒时把条件改成 prob 0.3代价是误报变多。scaler 参数留了个口子保存模型时如果也用了标准化记得把 scaler 一起 joblib 存下来加载后一并传入。sample 里的 11 个值依次对应 11 个特征顺序必须和训练时的列顺序一致接外部数据时这里最容易出偏差。到这一步这个项目的完整闭环就建立起来了先分红白两套数据理解分布清洗重复样本再做相关性筛查和基线模型切二分类标签横向对比最后用一个加载函数把模型暴露给外部调用。我自己做这个方向时最大的教训是没有先看标签分布就把 quality 当九分类硬训了一版准确率看着不差可每类样本一拆开就露馅。后来改成二分类、把混淆矩阵摆在桌面上所有评估才真正说得清楚。如果你也卡在“准确率挺高但不知道模型学会了什么”回到数据分布、回到混淆矩阵答案几乎都在那里。希望帮到你。本文还有配套的精品资源点击获取
返回列表