ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

糖尿病预测系统实战:从数据清洗到模型部署的机器学习全流程

糖尿病预测系统实战:从数据清洗到模型部署的机器学习全流程 简介这是一份面向计算机相关专业学生与教师的机器学习实战项目源码以糖尿病预测为应用场景适合作为课程设计、期末大作业或毕设参考也可用于项目立项演示与二次开发练习。资源包共46个文件约55KB以xml配置、properties属性、java源码、jsp页面、scala脚本及css样式等为主涵盖后端逻辑、前端展示与项目配置等模块结构完整、层次清晰。项目源自大三课程设计经导师指导与评审获得高分通过代码功能均已验证可稳定运行。目前已有359人学习下载具备较高的借鉴价值读者可借此理解机器学习预测流程与工程化组织方式学习java与scala混合开发、jsp页面交互及配置文件管理并在此基础上替换数据集或调整算法DIY出其他预测功能。需注意解压后项目名与路径避免使用中文建议重命名为英文再运行以免出现解析错误。1. 糖尿病预测系统从课程设计到能跑通的机器学习项目很多同学做课程设计时选题第一反应是「糖尿病预测系统」因为数据集干净、二分类任务直观、指标好解释。但真正动手才发现从拿到一份 CSV 到跑出一个能写进报告的结果中间隔着特征量纲、类别不平衡、模型选型、评估口径四道坎。这个标题对应的就是一套完整的、基于传统机器学习模型的糖尿病预测系统源代码通常包含数据预处理、特征工程、模型训练、评估可视化几个模块。它适合两类人一是正在做机器学习课程设计、需要一份能跑通且讲得清的项目骨架二是刚入门机器学习、想找一个二分类任务把 sklearn 全流程走一遍的初学者。下面我按实际落地顺序把这件事拆开讲清楚。2. 数据准备与特征工程糖尿病预测系统的地基怎么打2.1 先搞清楚数据集长什么样糖尿病预测最常用的公开数据集是 Pima Indians Diabetes Dataset包含 768 条样本、8 个特征、1 个标签。特征分别是怀孕次数、口服葡萄糖耐量试验血糖值、舒张压、三头肌皮褶厚度、血清胰岛素、BMI、糖尿病家族史函数、年龄标签是是否患病。这个数据集有几个典型问题部分特征的 0 值其实是缺失值比如血糖、血压、皮褶厚度、胰岛素、BMI 都不应该为 0正负样本比例大约是 1:1.87存在一定不平衡各特征量纲差异大年龄是几十胰岛素是几百。拿到数据第一步不是急着建模而是先做描述性统计把这些问题暴露出来。import pandas as pd import numpy as np # 列名按数据集惯例命名 columns [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age, Outcome] df pd.read_csv(diabetes.csv, namescolumns) # 查看基本信息和缺失情况 print(df.shape) print(df.describe()) # 统计各特征中 0 值的数量判断哪些是伪缺失 zero_cols [Glucose, BloodPressure, SkinThickness, Insulin, BMI] for col in zero_cols: print(col, (df[col] 0).sum())这段代码先加载数据并做描述性统计重点看describe()输出的最小值。如果某个医学上不可能为 0 的特征最小值是 0那基本可以判定存在伪缺失。参数上namescolumns是因为原始文件可能没有表头需要手动指定如果你的 CSV 自带表头去掉这个参数即可。2.2 缺失值处理别直接 drop也别无脑填均值确认伪缺失后处理方式直接影响模型效果。直接删行会损失样本尤其胰岛素缺失比例较高时填均值会压缩方差让模型学不到真实分布。我一般按缺失比例分情况处理缺失比例低于 5% 的用中位数填充缺失比例较高的考虑用 KNN 插补或者保留缺失指示特征。from sklearn.impute import KNNImputer # 把 0 替换为 NaN df[zero_cols] df[zero_cols].replace(0, np.nan) # 对缺失比例中等的特征用 KNN 插补 imputer KNNImputer(n_neighbors5) df[zero_cols] imputer.fit_transform(df[zero_cols]) # 检查插补后是否还有缺失 print(df.isnull().sum())KNNImputer的核心参数是n_neighbors默认 5样本量小的时候可以调到 3 到 7 之间。它的逻辑是找特征空间中最近的 K 个样本用它们的均值填充。注意插补必须在划分训练集之前还是之后这里有个容易翻车的点如果先插补再划分测试集的信息会泄露到训练过程。正确做法是先划分再在训练集上 fit然后 transform 测试集。上面为了演示方便先整体插补实际项目里要调整顺序。2.3 特征标准化与不平衡处理糖尿病数据集各特征量纲差异明显逻辑回归、SVM、KNN 这类基于距离或梯度的模型对量纲敏感必须做标准化。树模型如随机森林、XGBoost 对量纲不敏感但标准化也不会带来负面影响。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split X df.drop(Outcome, axis1) y df[Outcome] # 先划分再标准化避免数据泄露 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保证训练集和测试集的类别比例一致样本量不大时这个参数很重要。random_state42是为了结果可复现课程设计报告里写清楚随机种子是基本素养。类别不平衡方面Pima 数据集的比例还不算极端可以先不处理看模型表现。如果召回率明显偏低再考虑 SMOTE 过采样或调整class_weight参数。我一般先用class_weightbalanced试一下成本低且不容易引入合成样本的偏差。3. 模型选型与训练逻辑回归、随机森林还是 XGBoost3.1 先跑一个基线模型别一上来就调参很多同学拿到数据直接上 XGBoost 调参结果跑了半天不知道问题出在哪。正确顺序是先跑一个逻辑回归基线看数据本身能到什么水平再逐步换复杂模型。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, recall_score, roc_auc_score # 基线逻辑回归 lr LogisticRegression(class_weightbalanced, max_iter1000, random_state42) lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict(X_test_scaled) y_prob_lr lr.predict_proba(X_test_scaled)[:, 1] print(LR Accuracy:, accuracy_score(y_test, y_pred_lr)) print(LR Recall:, recall_score(y_test, y_pred_lr)) print(LR AUC:, roc_auc_score(y_test, y_prob_lr))max_iter1000是因为标准化后的数据逻辑回归通常收敛较快但默认 100 有时不够会报收敛警告。class_weightbalanced让模型自动按类别频率调整权重对召回率有提升。评估指标上糖尿病预测场景下召回率比准确率更重要因为漏诊的代价高于误诊。AUC 则衡量模型整体排序能力不受阈值影响。3.2 随机森林与梯度提升的对比基线跑完后换树模型看提升空间。随机森林和 XGBoost 是课程设计里最常用的两个前者好解释、不容易过拟合后者精度高但参数多。from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier # 随机森林 rf RandomForestClassifier(n_estimators200, max_depth6, class_weightbalanced, random_state42) rf.fit(X_train_scaled, y_train) y_prob_rf rf.predict_proba(X_test_scaled)[:, 1] # XGBoost xgb XGBClassifier(n_estimators200, max_depth4, learning_rate0.05, scale_pos_weight1.87, eval_metriclogloss, random_state42) xgb.fit(X_train_scaled, y_train) y_prob_xgb xgb.predict_proba(X_test_scaled)[:, 1] for name, prob in [(RF, y_prob_rf), (XGB, y_prob_xgb)]: print(name, AUC:, roc_auc_score(y_test, prob))随机森林的n_estimators一般设 100 到 500max_depth控制树深防止过拟合样本量小时 4 到 8 比较合适。XGBoost 的scale_pos_weight设为负正样本比例这里约 1.87作用是平衡类别。learning_rate设 0.05 配合 200 棵树是精度和训练速度的折中。注意 XGBoost 在 sklearn 接口下eval_metric要显式指定否则会有警告。3.3 交叉验证与超参数搜索单次划分的结果波动大课程设计里最好用交叉验证给出更稳定的评估。网格搜索虽然慢但胜在直观。from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [100, 200, 300], max_depth: [4, 6, 8], min_samples_split: [2, 5, 10] } grid GridSearchCV(RandomForestClassifier(class_weightbalanced, random_state42), param_grid, cv5, scoringroc_auc, n_jobs-1) grid.fit(X_train_scaled, y_train) print(Best params:, grid.best_params_) print(Best AUC:, grid.best_score_)cv5是五折交叉验证样本量 768 时每折约 150 条足够稳定。scoringroc_auc指定优化目标比准确率更适合不平衡场景。n_jobs-1用满 CPU 核心加速。搜索空间别设太大3×3×3 共 27 组每组 5 折总共 135 次训练普通笔记本几分钟能跑完。4. 评估与可视化课程设计报告里怎么把结果讲清楚4.1 混淆矩阵和分类报告是基本盘准确率单独看没有意义尤其是不平衡数据。混淆矩阵能看出模型在正负样本上分别错在哪分类报告给出精确率、召回率、F1。from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns import matplotlib.pyplot as plt y_pred_best grid.predict(X_test_scaled) cm confusion_matrix(y_test, y_pred_best) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[No Diabetes, Diabetes], yticklabels[No Diabetes, Diabetes]) plt.xlabel(Predicted) plt.ylabel(Actual) plt.title(Confusion Matrix) plt.show() print(classification_report(y_test, y_pred_best, target_names[No Diabetes, Diabetes]))混淆矩阵的四个格子分别是真负、假正、假负、真正。糖尿病场景下假负就是漏诊要重点关注这个数字。分类报告里的 recall 对正类就是召回率f1-score 是精确率和召回率的调和平均报告里建议三个指标都列出来。4.2 ROC 曲线和特征重要性ROC 曲线是课程设计里最常放的图AUC 值直接写进结论。特征重要性则能解释模型学到了什么增加报告的说服力。from sklearn.metrics import roc_curve import numpy as np # 多模型 ROC 对比 plt.figure(figsize(8, 6)) for name, prob in [(Logistic, y_prob_lr), (RF, y_prob_rf), (XGB, y_prob_xgb)]: fpr, tpr, _ roc_curve(y_test, prob) auc roc_auc_score(y_test, prob) plt.plot(fpr, tpr, labelf{name} (AUC{auc:.3f})) plt.plot([0, 1], [0, 1], k--) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.title(ROC Curve Comparison) plt.show() # 随机森林特征重要性 importances rf.feature_importances_ for col, imp in sorted(zip(X.columns, importances), keylambda x: -x[1]): print(f{col}: {imp:.4f})ROC 曲线越靠近左上角越好对角线是随机猜测。多模型画在一张图上对比报告里一眼能看出哪个模型更优。特征重要性排序通常 Glucose、BMI、Age 排在前列这和医学常识一致写进报告里可以作为模型合理性的佐证。5. 避坑与排查糖尿病预测系统课程设计里最容易翻车的五件事5.1 先插补再划分测试集信息泄露现象交叉验证 AUC 很高但换一批数据测试就崩。原因在划分训练测试集之前做了插补或标准化测试集的统计信息参与了训练。解决所有 fit 操作只在训练集上做测试集只 transform。用 Pipeline 可以强制这个顺序。5.2 把 0 当成真实值直接训练现象模型对胰岛素、皮褶厚度等特征学到的权重异常。原因这些特征里的 0 是缺失不是真实测量值直接喂给模型会引入噪声。解决先 replace(0, np.nan)再插补。判断标准是医学常识血糖为 0 不可能存活。5.3 只看准确率忽略召回率现象模型准确率 78%看起来不错但漏诊率很高。原因数据不平衡时模型倾向于预测多数类。解决评估时看 recall、F1、AUC训练时用 class_weight 或 scale_pos_weight 平衡。课程设计报告里要解释为什么选这些指标。5.4 标准化对象搞反现象树模型结果正常逻辑回归结果很差。原因逻辑回归对量纲敏感如果忘了标准化或者标准化对象搞错系数会失真。解决逻辑回归、SVM、KNN 必须标准化树模型可以不标准化但标准化也无害。统一用 Pipeline 管理。5.5 随机种子不固定结果无法复现现象每次跑代码结果都不一样报告里的数字对不上。原因train_test_split、模型初始化、交叉验证划分都有随机性。解决所有涉及随机的函数都设 random_state并在报告里注明。课程设计答辩时被问到复现这是基本要求。6. 把项目跑出课程设计高分三个进阶技巧第一个技巧是用 Pipeline 把预处理和模型串起来这样交叉验证时不会泄露代码也更干净。下面是一个完整示例from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()), (clf, RandomForestClassifier(n_estimators200, max_depth6, class_weightbalanced, random_state42)) ]) pipe.fit(X_train, y_train) print(Pipeline AUC:, roc_auc_score(y_test, pipe.predict_proba(X_test)[:, 1]))Pipeline 的好处是 fit 只在训练数据上进行predict 时自动复用训练集的统计量从机制上杜绝泄露。参数上SimpleImputer(strategymedian)比均值更抗异常值适合医学数据。第二个技巧是给模型加 SHAP 解释。课程设计里如果能展示单个样本的特征贡献答辩时会很加分。import shap explainer shap.TreeExplainer(rf) shap_values explainer.shap_values(X_test_scaled) shap.summary_plot(shap_values[1], X_test_scaled, feature_namesX.columns)SHAP 的TreeExplainer对树模型有精确解速度快。shap_values[1]取正类的贡献summary_plot 展示全局特征重要性方向。注意 SHAP 值解释的是模型输出不是因果报告里措辞要准确。第三个技巧是保存模型和标准化器方便部署演示。import joblib joblib.dump(pipe, diabetes_pipeline.pkl) # 加载时 loaded joblib.load(diabetes_pipeline.pkl) print(loaded.predict(X_test.iloc[:5]))joblib比 pickle 更适合存 numpy 数组Pipeline 整体保存后加载即可预测不需要重新 fit。课程设计答辩时现场演示预测一条新数据比只放静态图更有说服力。我自己做这类项目最大的教训是别在模型调参上花太多时间数据清洗和评估口径才是决定结果可信度的关键。Pima 数据集就 768 条模型再复杂也容易过拟合把预处理做扎实、把指标解释清楚比堆模型更能拿高分。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进