葡萄酒质量检测实战:从特征工程到分类模型的机器学习完整流程
简介面向计算机、电子信息工程、数学等专业学生的Python机器学习实践项目聚焦葡萄酒质量检测场景涵盖数据预处理、特征分析与模型训练等典型环节适合作为课程设计、期末大作业或毕业设计的参考资料。压缩包为rar格式整体大小26.59MB内含项目源码、实验数据与说明文档源码结构清晰数据可直接用于模型验证说明文档有助于快速理解项目思路与代码逻辑。资源内附带的说明文档还对运行环境、数据字段与代码模块做了梳理便于读者快速上手。目前已有2696人学习下载内容实用且贴近真实任务适合具备一定Python与机器学习基础、希望参考完整项目流程来调试代码或扩展功能的读者。通过这份材料可以学习如何将分类算法应用于实际数据集并借鉴其工程化组织方式为独立完成类似检测任务提供参考。1. 葡萄酒质量检测项目到底在做什么一个能跑通全流程的机器学习样板拿到「基于Python机器学习的葡萄酒质量检测项目」这套东西大多数人第一反应是这不就是拿公开的Wine Quality数据集跑个分类模型吗确实数据集本身不稀奇但把这个项目认真做完你等于把机器学习入门到实战的一条完整链路走了一遍——从数据探查、特征工程、模型训练到结果评估每一步都有坑在等你。这也是为什么大量Python机器学习教程和课程设计都会拿它当样板数据量小、特征明确、标签清晰新手看得懂老手能玩出花。这套源码加数据加说明文档的组合适合三类人想交一份靠谱课程设计的在校生、刚入行需要练手的Python开发以及想搞懂分类模型评估指标的检测类从业者。它能回答的不只是「葡萄酒好不好喝」而是「机器学习的检测项目到底应该怎么做才规范」。2. 先弄清楚数据和任务红白葡萄酒要分开建模更要先定分类还是回归2.1 数据集长什么样字段含义、质量分布和样本量差异Wine Quality数据集是机器学习入门最常用的公开数据集之一包含了红葡萄酒和白葡萄酒两个独立文件。常见做法是红白两份数据分开建模而不是混在一起训练原因后面会细说。每个文件是CSV格式行是不同批次的葡萄酒样本列是11个理化指标加1个质量评分。这11个理化指标分别是固定酸度fixed acidity、挥发性酸度volatile acidity、柠檬酸citric acid、残糖residual sugar、氯化物chlorides、游离二氧化硫free sulfur dioxide、总二氧化硫total sulfur dioxide、密度density、pH值、硫酸盐sulphates、酒精alcohol。质量评分是一个3到8红或3到9白的整数来源是品酒师打分后的中位数不是主观单次评分。样本量方面红葡萄酒大约1600条白葡萄酒接近4900条。这个数量级对机器学习来说不算大但做课程设计或者入门实战完全够用。要注意的是两张表的特征数量一样但数据分布差很多比如白葡萄酒的残糖和二氧化硫含量明显更高。这就是为什么我拿到项目第一步不是急着训练模型而是先加载数据把两份文件的分布差异看清楚。2.2 质量检测到底是分类任务还是回归任务两种建模方式的取舍同一个数据集可以有完全不同的建模方式这是这个项目最值得想清楚的地方。如果把它当回归任务就是用11个指标去预测质量评分这个连续数值最后算均方误差MSE或者R²。如果把它当分类任务常见做法是二分类——把评分大于等于7的列为「好酒」其余为「普通酒」——或者三分类把3到4归为低、5到6归为中、7以上归为高。我一般建议项目优先做分类理由有两条。第一品酒师评分本身就是离散的整数而且高度集中在5和6分回归模型在6分附近会非常拥挤误差大且难解释第二分类模型的评估指标准确率、F1、混淆矩阵对新手更直观也更容易在说明文档里把逻辑写清楚。如果非要走回归更适合的用途是预测某个特征调整后评分的相对变化趋势而不是给出精确评分。决定任务类型要在训练之前就定下来因为它直接影响标签构造方式和评估指标。二分类是最稳的选择把质量7分及以上视为「优质」6分及以下视为「普通」问题立刻变成「这瓶酒值不值得推荐」贴合检测场景的实际语义。2.3 用pandas做第一次数据探查加载文件、看缺失值和基本统计量动手第一步先把CSV加载进来做基本检查。我习惯的流程是读取文件、查看行列数、检查缺失值、看describe统计量、确认标签分布。这五步走完这个数据集能不能用、该不该做清洗心里就有数了。import pandas as pd # 加载红白两份数据注意sep;Wine Quality数据集是分号分隔 red pd.read_csv(winequality-red.csv, sep;) white pd.read_csv(winequality-white.csv, sep;) # 基础检查形状、列名、缺失值 print(红葡萄酒形状:, red.shape) print(白葡萄酒形状:, white.shape) print(红葡萄酒缺失值:\n, red.isnull().sum().sum()) print(白葡萄酒缺失值:\n, white.isnull().sum().sum()) # 标签分布看质量评分是否均衡 print(红葡萄酒质量分布:\n, red[quality].value_counts().sort_index()) print(白葡萄酒质量分布:\n, white[quality].value_counts().sort_index())加载时最容易翻车的是分隔符。这个数据集的CSV不是逗号分隔而是分号分隔如果用默认的read_csv加载会发现所有列挤成一列后面全部代码白跑。缺失值这一项两份数据基本都是0这是好消息意味着不需要做填充处理。质量分布会看到明显的长尾红葡萄酒里5分和6分占了约75%白葡萄酒同样集中在5分和6分高分和低分样本都很少。这个不平衡直接决定了后续要不要做类别处理。3. 特征工程与训练集划分标准化、相关性筛选和类别不平衡3.1 为什么葡萄酒特征必须先做标准化量纲差异的真实影响看一下描述统计就会发现问题固定酸度动辄七八克每升游离二氧化硫是几十毫克每升酒精含量只有9到15而密度是0.99到1.00这种小数点后两位的数值。这些特征的量纲差异不是一倍两倍而是几十倍。如果不做标准化直接丢给模型逻辑回归这类基于距离和权重的模型会被量纲大的特征主导模型权重完全失去可解释性。标准化常见做法是StandardScaler把每个特征变成均值0、方差1的标准正态分布。要注意标准化是在划分训练集和测试集之后做的而且只能用在数值特征上。先把特征和标签拆开再处理特征最后才进入模型。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 特征和标签分离假设已经构造好二分类标签1代表优质酒 X red.drop(quality, axis1) y red[quality].apply(lambda x: 1 if x 7 else 0) # 先划分再标准化避免数据泄漏 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)这串代码里的关键点有两个。第一是stratifyy按标签比例分层采样保证训练集和测试集里优质酒的比例一致第二是scaler.fit_transform只用在训练集测试集只用transform绝对不能重新fit。一旦把测试集的均值方差混进标准化参数得到的结果就是数据泄漏评估分数会虚高。数据泄漏是这类检测项目里最隐蔽也最致命的错误。3.2 相关性排查哪些指标真正影响质量评分特征之间高度相关会带来多重共线性尤其在逻辑回归里会让权重估计不稳定。Wine Quality数据里最典型的例子是密度和残糖、游离二氧化硫和总二氧化硫之间相关性很强。可以画相关性热力图也可以直接打印相关系数矩阵挑出高相关特征。# 计算特征之间的相关系数找出高度相关的对 corr X.corr() high_corr_pairs [] for i, col in enumerate(corr.columns): for j in range(i 1, len(corr.columns)): if abs(corr.iloc[i, j]) 0.7: high_corr_pairs.append((corr.columns[i], corr.columns[j], round(corr.iloc[i, j], 2))) for pair in high_corr_pairs: print(pair)输出里你会看到密度和残糖的相关系数在0.5到0.7之间总二氧化硫和游离二氧化硫更是接近0.7。这个阈值取0.7是我习惯的经验值大于0.7说明两个特征携带了大量重复信息。处理方式倒不一定直接删除因为随机森林和XGBoost对多重共线性不敏感但对逻辑回归来说保留高度相关的特征会让系数解释变得别扭。我一般会在说明文档里记录这些相关对然后选择保留业务含义更明确的一个比如保留酒精和硫酸盐这种与发酵过程直接相关的指标。3.3 类别不平衡要不要处理评分集中在中段带来的模型偏置前面已经看到优质酒7分以上在红葡萄酒里只占约15%白葡萄酒里更低。这就是典型的类别不平衡。如果不处理模型为了最小化损失会倾向于把绝大多数样本预测为普通酒——准确率看着很高可能超过85%但优质酒几乎全部被漏掉。对于一个检测项目来说漏报优质酒比误报普通酒更严重因为检测的意义就是要把少数关键样本找出来。处理不平衡有几种常见做法。最简单的是改阈值模型预测概率大于0.3就算优质而不是默认的0.5。更常规的是用class_weightbalanced让少数类样本按比例获得更高权重。SMOTE过采样也常被提到但对这个数据集来说样本量本来就不大合成样本容易过拟合我不太推荐在课程设计里用。先用class_weight再看混淆矩阵判断效果这个路径最稳。4. 用scikit-learn训练基线模型逻辑回归、随机森林、XGBoost4.1 为什么这三个模型作为基线组合最有代表性这个项目选模型我一般固定三件套逻辑回归、随机森林、XGBoost。逻辑回归是线性基线跑得快结果够直观权重系数能直接解读为「酒精每提高一个标准差优质概率上升多少」随机森林是非线性树的代表能自动处理特征交互不需要太多预处理XGBoost则是梯度提升树通常在准确率上能刷新前两者的上限但也最容易过拟合。三件套的另一个好处是能覆盖「线性模型 vs 树模型」的对比维度这在说明文档里是很好的分析素材。读者做课程设计或面试讲项目时可以明确说出「逻辑回归在测试集上准确率多少随机森林提升多少XGBoost在高分样本的召回率上又强在哪里」。光是这个对比表就够撑起大半个项目的技术深度。4.2 逻辑回归训练默认参数先跑一遍看权重和基线分数先用默认参数跑一遍逻辑回归拿到基线数字再谈优化。这里要注意max_iter默认的100次迭代在标准化后的数据上经常不收敛跑出告警直接改成1000省心。模型评估我用准确率和F1同时看因为准确率在不平衡数据上会骗人F1才能反映少数类的真实表现。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, f1_score, classification_report # 逻辑回归线性基线max_iter给足 lr LogisticRegression(max_iter1000, class_weightbalanced) lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict(X_test_scaled) print(逻辑回归准确率:, accuracy_score(y_test, y_pred_lr)) print(逻辑回归F1:, f1_score(y_test, y_pred_lr)) print(classification_report(y_test, y_pred_lr))class_weightbalanced在这里作用很明显它按类别频率反向调整样本权重让优质酒的误判代价更大。跑完打印的classification_report里有precision、recall、F1重点看优质酒那一行的recall如果低于0.5说明这个模型根本没把少数类当回事。权重可以打印出来看生活coefficient绝对值最大的特征往往是酒精、挥发性酸度、硫酸盐这几个和葡萄酒工艺的经验吻合。这个可解释性是逻辑回归独有的价值树模型给不了。4.3 随机森林与XGBoost树模型的优势和过拟合风险随机森林用默认参数先跑不用急着调。它不需要标准化所以直接拿原始特征训练就可以但要注意训练集上的准确率如果接近100%而测试集只有80%出头就是过拟合的典型信号。树深限制、最小样本数这些参数可以稍后再动。from sklearn.ensemble import RandomForestClassifier # 随机森林树模型不需要标准化直接用原始特征 rf RandomForestClassifier(n_estimators200, random_state42) rf.fit(X_train, y_train) y_pred_rf rf.predict(X_test) print(随机森林准确率:, accuracy_score(y_test, y_pred_rf)) print(随机森林F1:, f1_score(y_test, y_pred_rf))n_estimators200是经验值太少容易欠拟合太多收益递减还拖慢速度。树模型在这里表现通常优于逻辑回归因为酒精、挥发酸这类特征对质量的贡献不是线性的比如酸度过低和过高都不好中间有个最佳区间决策树天然能切出这种非线性边界。但如果项目说明文档里只写了随机森林而不对比其他模型这个项目的分析深度一眼就会被看穿。XGBoost的用法稍微多两行。要注意两个参数习惯。第一eval_metric建议用logloss而不是默认的准确率因为梯度提升过程需要概率输出第二early_stopping_rounds配合验证集可以自动在过拟合前停住这是最实用的调参手段。from xgboost import XGBClassifier # XGBoost用early_stopping在过拟合前停住 xgb_model XGBClassifier( n_estimators300, max_depth5, learning_rate0.1, eval_metriclogloss, early_stopping_rounds20, random_state42 ) xgb_model.fit( X_train, y_train, eval_set[(X_test, y_test)], verboseFalse )这串代码里的eval_set用来指定早停监控的验证集early_stopping_rounds20表示连续20轮验证损失不下降就停。注意早停之后n_estimators会被实际使用的树数量覆盖打印xgb_model.best_iteration能看到训练真正用了多少棵树。XGBoost在这个数据上一般能跑到比随机森林高两三个点的F1但代价是训练时间变长且调参空间大容易在无关参数上浪费时间。4.4 评估指标怎么选准确率、F1和混淆矩阵的适用场景单看准确率会掩盖关键问题。红葡萄酒里优质样本只有15%一个「全都预测普通」的模型准确率也能有85%但完全没用。检测类项目里漏检少数类的代价远高于误检所以必须同时看F1和混淆矩阵。from sklearn.metrics import confusion_matrix # 混淆矩阵行是真实类别列是预测类别 cm confusion_matrix(y_test, y_pred_rf) print(混淆矩阵:) print(cm) # 输出格式 [[TN, FP], [FN, TP]] sensitivity cm[1, 1] / (cm[1, 1] cm[1, 0]) specificity cm[0, 0] / (cm[0, 0] cm[0, 1]) print(召回率:, round(sensitivity, 3)) print(精确率:, round(cm[1, 1] / (cm[1, 1] cm[0, 1]), 3))混淆矩阵的四格一眼就能看出模型在哪个方向上犯错。左下角的FN是「实际优质但被漏掉」的样本这个数越小越好。如果它远大于右上角的FP说明模型偏保守宁可漏检也不误报这在葡萄酒检测里未必是坏事取决于使用场景。做说明文档时把三个模型的三个混淆矩阵并排放在一起结论自然就出来了逻辑回归偏保守但可解释性强随机森林均衡XGBoost在高分段的召回率最高。5. 葡萄酒质量检测项目的避坑手册数据泄漏、过拟合和评分偏移这一章是给你留后悔药的地方。我整理了自己做这个项目踩过的坑按现象、原因、解决三步写每一条都能直接对应到代码里查。5.1 数据泄漏标准化顺序错了模型分数虚高还不自知现象标准化之后测试集准确率比训练集还高或者跟训练集几乎一样一看就觉得不对劲。原因对全量数据做了StandardScaler的fit然后再划分训练集和测试集。这样的话测试集的均值和方差已经被模型「偷看」了评估结果是骗人的。更隐蔽的变种是在交叉验证里对整个数据集做标准化每一折的验证集都泄漏了。解决永远先train_test_split再对训练集fit_transform对测试集只transform。写一个检查习惯每次划分前问自己一句scaler是fit在什么数据上的。如果要用交叉验证标准化要放进Pipeline里让每一折都重新fit这是唯一正确的做法。5.2 过拟合训练集准确率99%测试集却只有82%现象随机森林在训练集上准确率接近100%但到测试集就掉到82%。XGBoost更夸张树一加到300棵训练集几乎全对测试集纹丝不动甚至下降。原因决策树本身就是高方差模型可以无限记住训练集的噪声点。数据量只有1600条特征多噪声占比相对高树又足够深过拟合几乎是必然。解决先把max_depth限制在5到7不要放任默认值。然后看min_samples_leaf调到10到20强制每个叶子节点至少有十几个样本模型自然变「钝」。最后用early stopping这是对梯度提升最有效的一招。记住一个原则模型复杂度增加的速度必须慢于训练集拟合提升的速度。5.3 红白葡萄酒评分分布差异大混在一起训练让你的模型学偏现象把红白两份数据合到一张表里训练准确率看着不低但单独看白葡萄酒的优质酒召回率惨不忍睹。原因白葡萄酒的残糖和二氧化硫含量整体高于红葡萄酒品酒师对两种酒的评分标准也不同。混合训练相当于强迫一个模型同时拟合两种分布模型只能取中间地带两边都不讨好。解决坚持红白分开建模。说明文档里写清楚两份数据的描述统计差异分别展示各自的模型结果这个项目瞬间从「跑通代码」变成「做了对比实验」。课程设计评分和面试提问都很吃这一套。5.4 类别不平衡全部预测为普通酒准确率还行但项目毫无意义现象逻辑回归不设class_weight时预测结果几乎全是0普通酒测试集F1只有0.2但准确率还有85%。原因优质酒只占15%模型计算损失时少数类的总损失太小自然被忽略。默认的0.5决策阈值对不平衡数据太「高」模型概率输出很少能超过它。解决先加class_weightbalanced多数情况够用。如果还不够就把决策阈值下调用predict_proba取概率列大于0.3就判为优质。在说明文档里记录这个阈值调整过程这是深度的体现。不建议在这个小数据集上上SMOTE合成样本在树模型上容易引起额外过拟合。5.5 运行报错分号分隔、版本兼容和中文路径现象CSV读出来所有列挤在一起sklearn和xgboost版本不匹配报ValueErrorWindows下数据文件放在中文路径里读不了。原因Wine Quality数据是分号分隔read_csv默认逗号分隔直接读错。xgboost函数签名在0.90和1.0之后有大变化老代码直接报错。中文路径在部分Windows环境的编码处理上确实有兼容问题。解决读文件一律写sep;。环境上建议Python 3.8以上scikit-learn装1.1以上版本xgboost装1.6以上安装完跑一行import xgboost先验证。数据文件路径全部用英文目录这是最快最稳的规避方式。6. 让模型结果站得住脚交叉验证、网格调参和特征重要性6.1 交叉验证单次划分的结果不可信用5折交叉验证看稳定性单次划分测试集的结果只有一次运气成分样本量小的时候尤其明显。用交叉验证跑一遍看所有折的均值方差才算给结果上了保险。from sklearn.model_selection import cross_val_score # 5折交叉验证cv值越大越稳但越慢 scores cross_val_score(lr, X_train_scaled, y_train, cv5, scoringf1) print(每折F1:, scores) print(平均F1:, scores.mean(), 标准差:, scores.std())cv5表示5折每折用80%数据训练、20%验证。标准差如果大于0.05说明模型在不同数据子集上表现波动太大优先回去处理过拟合而不是继续调参。交叉验证的结果在说明文档里比单次划分更有说服力面试或答辩时直接拿这个数字说话。6.2 参数搜索GridSearchCV跑随机森林和XGBoost的调参方向网格搜索不需要覆盖太多参数每个模型挑两三个关键参数就好。随机森林看max_depth和min_samples_leafXGBoost看learning_rate和colsample_bytree。from sklearn.model_selection import GridSearchCV # 随机森林只搜树的深度和叶子最小样本数减少搜索空间 param_grid { max_depth: [5, 7, 10], min_samples_leaf: [5, 10, 20] } grid GridSearchCV( RandomForestClassifier(n_estimators200, random_state42), param_grid, cv5, scoringf1, n_jobs-1 ) grid.fit(X_train, y_train) print(最佳参数:, grid.best_params_) print(最佳F1:, grid.best_score_)n_jobs-1让所有CPU核心并行跑速度快不少。搜索范围宁可小也不要大参数组合从3乘3的9个起步覆盖了主要方向不会太慢。XGBoost的搜索同理但加上learning_rate和n_estimators的联动学习率小就得多加树学习率大就早停两个参数单独搜容易自相矛盾。6.3 特征重要性回答「什么决定了葡萄酒质量」这个核心问题模型调完参数之后最后一个让人信服的环节是解释特征重要性。随机森林直接有feature_importances_属性XGBoost也有。把排名前五的特征列出来对照葡萄酒工艺知识写进说明文档整个项目就从「跑了几个模型」变成了「做了一次有业务洞察的分析」。import numpy as np # 提取特征重要性按从大到小排序输出前5个最重要的特征 importance rf.feature_importances_ feature_names X.columns indices np.argsort(importance)[::-1] for i in range(5): print(f第{i1}重要: {feature_names[indices[i]]}, 重要性评分: {importance[indices[i]]:.3f})输出里酒精、挥发性酸度、硫酸盐通常会排在前面。这三个指标恰好对应葡萄酒工艺里最核心的控制项酒精是发酵产物挥发性酸度直接决定酒是否变质硫酸盐是防腐和抗氧化指标。把模型结果和这些常识对照上项目的可信度就立住了。这是我做完这个项目后最深的体会——机器学习检测的意义不在于模型本身有多花哨而在于它能不能帮你找出真正值得关注的那几个变量。希望这个项目的实践能帮到你也帮你自己建立起「先看数据、再定任务、最后才选模型」的做事习惯。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →