资讯详情

机器学习糖尿病预测实战:从数据预处理到模型对比与部署

📅 2026/10/9 21:35:32 | 华诺云谱 👁 阅读
机器学习糖尿病预测实战:从数据预处理到模型对比与部署
简介基于机器学习实现的糖尿病预测系统毕业设计源码配有简要文档说明面向计算机、人工智能、自动化、电子信息等专业学生适合作为毕设、课设、课程作业或项目前期演示的起点。压缩包共22个文件主体为Java与JSP两种代码文件Java负责业务逻辑与模型调用JSP负责页面展示XML和properties文件用于系统配置Scala文件承担辅助数据处理CSS文件控制界面样式Idea模块文件标识工程结构。整个压缩包仅24KB是一个结构清晰的轻量级Maven工程。目前已有199人浏览学习。项目覆盖从输入特征、机器学习预测到结果页面反馈的完整链路可帮助读者快速理解糖尿病预测应用的工程实现附带的文档说明便于入手且可在现有代码上替换算法、增加特征或完善界面对机器学习入门和Java Web整合实践都有参考价值也是一份简洁的毕设示例代码包。1. 机器学习预测糖尿病为什么体检指标正常风险评分仍然偏高体检报告上写着空腹血糖 5.2mmol/L医生说不高可机器学习模型给出的糖尿病风险却是 73%。这不是模型在瞎猜而是它把怀孕次数、BMI、血压、胰岛素、年龄等多项指标放在一起做联合判断看到的是“整个人”而不是某一项指标。所谓基于机器学习实现的糖尿病预测系统本质上是一个二分类模型读入体检常见的几项指标输出患病概率和风险等级解决的是单指标无法捕捉联合风险的问题。它的源码和文档说明通常覆盖数据预处理、特征分析、模型训练、评估和预测五段。这个项目很适合两类人一类是刚学完分类算法、想做完整实战的初学者另一类是打算把风险预测能力集成进健康管理工具里的从业者。下文就按照真实项目的推进顺序来先立数据再讲特征跑五个模型做对比把最常见的坑排掉最后封装成一个能直接用的预测函数。2. 数据集与任务定义认识 8 个特征和 1 个标签做机器学习项目的第一步不是写模型而是把数据和任务彻底搞懂。这个糖尿病预测系统使用的公开数据集来自早期的糖尿病流行病学调查共 768 条样本每条样本记录了一位女性个体的体检指标和最终是否患病。字段数量少、标签明确、没有复杂的时间序列关系非常适合用来完整走一遍分类任务流程也适合作为课程设计或简历项目的载体。2.1 字段含义与数据类型数据集的 9 列分别是Pregnancies怀孕次数、Glucose口服葡萄糖耐量试验 2 小时后的血浆糖浓度、BloodPressure舒张压、SkinThickness三头肌皮褶厚度、Insulin2 小时血清胰岛素、BMI身体质量指数、DiabetesPedigreeFunction糖尿病遗传谱系函数衡量家族病史的影响程度、Age年龄以及标签列 Outcome0 表示未患病1 表示患病。字段类型取值范围参考说明Pregnanciesint0-17怀孕次数与妊娠期糖尿病史相关Glucoseint0-199血糖0 值需要警惕缺失BloodPressureint0-122舒张压0 值需要警惕缺失SkinThicknessint0-99皮脂厚度0 值需要警惕缺失Insulinint0-846血清胰岛素0 值需要警惕缺失BMIfloat0-67.1体重除以身高的平方DiabetesPedigreeFunctionfloat0.078-2.42家族遗传影响系数Ageint21-81年龄Outcomeint0 / 1标签0 未患病1 患病加载并查看结构与分布是编码的第一步import pandas as pd df pd.read_csv(diabetes.csv) print(df.info()) print(df[Outcome].value_counts())代码里的 df.info() 用来看每一列是否有空值、数据类型是否合理value_counts() 则统计正负样本的数量。如果发现 Outcome 的两类数量差距很大后面就不能只用准确率来评估。这个数据集里共有 268 条正样本和 500 条负样本负样本占 65.1%这意味着一个什么都不做的“全部预测为不患病”模型也能拿到 65.1% 的准确率。2.2 数据不平衡为什么准确率不是唯一指标数据不平衡直接影响两个决策一是划分训练集和测试集时要用分层抽样二是评估模型时要把召回率放在重要位置。糖尿病患者被漏诊的成本远高于把健康人误判为高风险所以模型宁可把一部分人“误报”到高风险里也不能大面积漏掉真正的病人。准确率在这个场景下会掩盖严重问题。from sklearn.model_selection import train_test_split feature_cols [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age] X df[feature_cols] y df[Outcome] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(y_train.value_counts(normalizeTrue))train_test_split 里的 stratifyy 就是分层抽样它保证切分后的训练集和测试集保持与原始数据相同的正负比例避免随机切分把某一类全部切进测试集。random_state 固定为 42 是为了让结果可复现换一台机器或换一次运行得到同样的划分这是做对比实验的基本要求。在动手建模之前还可以先看一下正负样本在各个特征上的均值差异这能帮你判断哪些特征大概率有效。按 Outcome 分组计算 Glucose、BMI、Age 的均值通常会发现患病组的血糖和 BMI 明显偏高这个观察结果和后续模型输出的特征重要性往往能对上。3. 数据预处理与特征工程标准化、缺失值修正和相关性筛选预处理在糖尿病预测这类“字段少、噪声多”的项目里对最终效果的影响经常大过模型选型本身。这个数据集最出名的问题不是缺行而是把缺失值用 0 记录了。Glucose、BloodPressure、SkinThickness、Insulin、BMI 这五列的 0 值在医学上都不合理——一个活人的血压可能是 0 吗所以必须把这批 0 当作缺失值处理否则模型会学到“血糖越低越健康”这类荒谬的规律。3.1 缺失值陷阱0 值并不总是有效数值先统计每列有多少个 0 值再决定填充策略这是最稳妥的做法。import numpy as np zero_cols [Glucose, BloodPressure, SkinThickness, Insulin, BMI] for col in zero_cols: zero_count (df[col] 0).sum() print(f{col}: {zero_count} 个 0 值) # 用分组中位数填充按 Outcome 分组因为病患与非病患的生理指标中位数不同 for col in zero_cols: df[col] df[col].replace(0, np.nan) df[col] df[col].fillna(df.groupby(Outcome)[col].transform(median))replace(0, np.nan) 先把 0 转成真正的空值然后按 Outcome 分组计算中位数填充。为什么不统一用均值因为患病组的血糖中位数可能接近 143未患病组接近 117用全量均值填会把两组数据向中间拉降低特征区分度。分组中位数是常见做法代码中的 transform(median) 会返回与原始行数相同的填充值序列避免索引错位。3.2 标准化与训练/测试切分顺序标准化是另一个容易翻车的地方。逻辑回归通过梯度下降求解特征量纲差异过大会拖慢收敛KNN 直接依赖欧氏距离量纲大的特征比如 Age 会压过量纲小的特征比如 BMI。树模型不受标准化影响但如果代码里统一处理就统一对全部模型使用标准化后的数据能保持对比公平。这里有一个必须记住的顺序先切分训练集和测试集再对训练集计算标准化参数然后把这个参数应用到测试集。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意第三行用的是 transform 而不是 fit_transform。fit_transform 会在测试集上重新计算均值和方差这等于让测试集的信息提前参与训练属于数据泄漏。正确逻辑是scaler 先在训练集上学习均值和标准差然后用同一组参数去变换测试集这样才能模拟“新数据进来时”的真实情况。3.3 特征相关性观察与取舍先用相关系数看每个特征与标签的线性关系强度给特征工程一个起点。corr_series df.corr()[Outcome].abs().sort_values(ascendingFalse) print(corr_series)通常 Glucose 排在第一位相关性接近 0.47随后是 BMI、Age、DiabetesPedigreeFunction。SkinThickness 和 Pregnancies 的相关性偏低但不建议直接删掉因为树模型可以捕捉到低线性相关的非线性组合。一种更严谨的做法是做一个特征敏感性对比分别用全部特征和去掉两个低相关特征训练随机森林比较交叉验证得分如果分数基本持平再考虑精简特征。特征筛选不是越少越好这个数据集总共只有 8 个特征保留全部往往比大胆删减更稳健。4. 五类模型对比实现从逻辑回归到 XGBoost模型对比是这个系统里最有说服力的一部分。这里选五类代表模型逻辑回归、KNN、决策树、随机森林、XGBoost。选型理由很简单逻辑回归给出可解释的概率基线KNN 代表距离类方法决策树是树模型的起点随机森林体现集成带来的稳定性XGBoost 是结构化数据竞赛中的常用选择。把五个模型在同一个训练集和测试集上跑完对比准确率、召回率、F1 和 AUC就能确定哪个模型作为最终预测系统的主模型。4.1 基线模型逻辑回归逻辑回归在这个二分类任务里不是用来冲击最高分的而是用来做一个“只要不犯错就能拿 75% 左右”的基线。它对特征做了线性加权再用 sigmoid 函数映射成概率好处是系数可以直接解释——比如 Glucose 的系数为正意味着血糖越高风险越大。from sklearn.linear_model import LogisticRegression lr LogisticRegression(max_iter1000, C1.0, random_state42) lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict(X_test_scaled) y_proba_lr lr.predict_proba(X_test_scaled)[:, 1] coef_df pd.DataFrame({ 特征: feature_cols, 系数: lr.coef_[0] }).sort_values(系数, ascendingFalse) print(coef_df)max_iter 设置为 1000 是因为数据经过标准化后梯度下降需要更多迭代才能收敛默认值 100 有时会报警告。C 是正则化强度的倒数C 越小正则化越强如果发现训练集和测试集差距过大可以把 C 下调到 0.1 再观察。系数表输出后你会看到逻辑回归认为最重要的特征基本与相关性分析一致这就是它作为基线模型的价值用最简单的方式验证前面的特征分析没有白做。4.2 距离类模型KNNKNN 的思路是近朱者赤找 K 个最相似的样本按它们的标签投票决定风险。它没有任何训练过程真正的计算发生在预测时所以要关注 K 的取值和距离度量。K 太小容易过拟合K 太大则会把远处不相似的样本也拉进来。在这个数据集上K 取 7 到 11 之间通常比较稳。from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score for k in range(3, 21, 2): knn KNeighborsClassifier(n_neighborsk, weightsdistance) scores cross_val_score(knn, X_train_scaled, y_train, cv5, scoringroc_auc) print(fk{k}, AUC{scores.mean():.4f})这段代码用五折交叉验证在训练集内部挑选 K而不是直接在测试集上试。交叉验证把训练集切成五份轮流拿四份训练、一份验证最后取平均分。weightsdistance 让距离更近的邻居拥有更高投票权重通常比等权投票更平滑。注意测试集全程不能参与这个循环否则选出来的 K 会对测试集过拟合出现“调参一时爽上线火葬场”的局面。4.3 树模型与集成决策树、随机森林、XGBoost决策树的优势是自动捕捉特征之间的交互关系比如“血糖偏高且年龄偏大”这类组合条件。但单棵决策树很容易把训练集背下来所以要把 max_depth 和 min_samples_leaf 限制住。随机森林通过多棵树并行投票来降低单棵树的方差是树模型的稳定版。XGBoost 则是走了另一条路用加法模型逐步拟合残差在小数据集上表现非常好代价是调参空间更大。from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.metrics import accuracy_score, recall_score, f1_score, roc_auc_score models { 决策树: DecisionTreeClassifier(max_depth4, min_samples_leaf5, random_state42), 随机森林: RandomForestClassifier(n_estimators200, max_featuressqrt, min_samples_leaf3, random_state42), XGBoost: XGBClassifier(n_estimators200, max_depth3, learning_rate0.05, eval_metriclogloss, random_state42) } for name, model in models.items(): model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) y_proba model.predict_proba(X_test_scaled)[:, 1] print(name, { 准确率: round(accuracy_score(y_test, y_pred), 4), 召回率: round(recall_score(y_test, y_pred), 4), F1: round(f1_score(y_test, y_pred), 4), AUC: round(roc_auc_score(y_test, y_proba), 4) })这段代码把三个树模型一次跑完。参数是经验起步值决策树 max_depth4 加 min_samples_leaf5强制每片叶子至少 5 个样本防止学到单样本特例随机森林 n_estimators200 足够稳定max_featuressqrt 让每棵树只看随机的部分特征增加树的多样性XGBoost 的 learning_rate0.05 配合 n_estimators200用小步长走更多轮次比大步长更不容易过拟合。eval_metriclogloss 只是让 XGBoost 不再输出版本兼容警告不影响模型本身。4.4 评审指标先看召回率和 AUC模型评估阶段我一般把四个指标放进一张对比表里模型准确率召回率F1AUC逻辑回归0.770.700.690.83KNN0.760.660.670.82决策树0.740.710.680.80随机森林0.780.740.720.84XGBoost0.790.780.740.86上面的数值是典型结果示意不同随机种子下会有一定浮动但排序规律通常不变。读表先后看召回率决策树和 XGBoost 的召回率高说明漏诊率低。再看 AUC它衡量模型把随机正样本排在随机负样本前面的概率0.86 在医疗风险筛查场景里已经不错。准确率在类别不平衡时只能作参考真正决定这个系统能不能用的是召回率和 AUC 的组合。5. 糖尿病预测系统的常见坑与排查5 个容易翻车的细节做这个项目的人不少但能一次性跑出稳定结果的不多因为坑基本都埋在预处理和评估环节。下面五条是我反复见到的踩坑记录每条都按“现象 → 原因 → 解决”来写你在复现时如果遇到类似问题可以直接对照排查。5.1 现象KNN 准确率很低只有 60% 左右原因没有做标准化直接拿原始特征算欧氏距离。Age 的取值范围是 21-81BMI 只有 18-40距离计算被 Age 这类数值大的特征主导胰岛素、血糖反而贡献不了多少。KNN 是最依赖特征尺度的一类模型它对量纲的敏感度远高于逻辑回归。解决在使用 KNN 之前必须对全部特征做标准化。注意标准化要在切分训练集和测试集之后做并且用同一个 scaler。具体写法见 3.2 节的代码先 fit 训练集再 transform 测试集顺序不能反过来。5.2 现象模型在测试集上准确率非常高接近 98%原因数据泄漏。常见错误是先对全量数据做标准化再去切分训练集和测试集。这样 StandardScaler 已经用全部数据的统计量计算了均值和方差测试集的信息从预处理这一步就流进了训练数据。另一个常见变体是直接用全量数据训练模型再用同一批数据测试也会得到虚高的分数。解决严格遵循“切分在前预处理参数拟合在后”的顺序。更省心的做法是使用 sklearn 的 Pipeline把标准化和模型打包成一条流水线Pipeline 会在内部自动对训练集调用 fit对测试集只调用 transform从机制上杜绝这类泄漏。5.3 现象模型准确率 72%但把所有样本都预测成了不患病原因测试集中 65% 的样本本来就是负样本模型发现只要全输出 0 就能拿到较高准确率于是偷懒走捷径。如果只看准确率你会觉得模型“还不错”但它实际一个病人都没识别出来。尤其是逻辑回归在特征区分度不足时容易把所有样本的概率都压在 0.5 以下。解决评估阶段同时看混淆矩阵、召回率和 AUC不要只看准确率。如果发生全预测为 0 的现象优先检查正负样本比例然后考虑调整分类阈值。默认阈值 0.5 只适合类别均衡的场景糖尿病筛查里可以基于 ROC 曲线找到最佳阈值常见方法是遍历所有可能阈值、选出召回率和误报率最均衡的那个点。5.4 现象网格搜索选出极端参数比如 max_depth30 或 n_neighbors1原因网格搜索的范围设置不合理或者评分指标选错了。只用准确率做评分时模型倾向于把特征学到极致在训练集上表现满分、在测试集上下滑严重。调参到后期会有一点“玄学”味道同一个参数在不同随机种子下结果差异很大本质上是陷入了对训练集的过度拟合。解决评分指标优先选 roc_auc并且必须带交叉验证。比如下面的写法让网格搜索每次都是五折交叉验证后再比较分数单次训练的偶然性就被平均掉了。from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier param_grid { max_depth: [3, 5, 7], min_samples_leaf: [3, 5, 10], n_estimators: [100, 200] } grid GridSearchCV( RandomForestClassifier(random_state42), param_grid, scoringroc_auc, cv5 ) grid.fit(X_train_scaled, y_train) print(grid.best_params_)max_depth 从 3 开始而不是从 1 或 30 开始深度太浅模型学不到交互深度太深必然过拟合。min_samples_leaf 用 3、5、10 这样的步长让叶子不要太小。n_estimators 到 200 已经足够继续加大只会增加训练时间不会带来明显收益。最终选择标准是交叉验证得分不是 grid.best_score_ 本身而是看这个参数组合在测试集上的真实召回率和 AUC。5.5 现象文档里写的参数和代码里不一致复现结果对不上原因写文档时复制的是早期参数后来调参改了代码但没同步更新文档。这类问题很常见因为项目开发时文档总是在最后才补而补文档的人可能已经不记得最终版本用了哪一组参数。某同学就吃过这个亏文档写 max_depth5代码里实际是 7两个人按文档各自复现结果差了两个百分点谁也不敢确定哪个是对的。解决把文档当成代码的一部分在 README 里记录最终使用的参数表、运行环境和复现命令同时把环境依赖文件放进项目目录。更保险的做法是在文档开头放一个“复现结果”表格包含模型、参数、随机种子、评估指标四个字段每次改代码就同步改这张表确保一个项目只对应一组可复现的结果。6. 把模型用起来导出、单条预测与系统文档训练和评估只是前半程让这个系统真正被使用还需要把模型落地成可调用的接口并且给项目补一份像样的文档说明。代码写得再漂亮三个月后或者换一个人来看都要能快速上手这才符合标题里“源代码文档说明”的价值。6.1 用 joblib 导出模型与单条预测函数训练完成后把最优模型和标准化器一起保存到磁盘后续预测不需要重新训练。常见做法是用 joblib 而不是 pickle因为 joblib 对 numpy 数组的序列化效率更高。import joblib import numpy as np joblib.dump(best_model, diabetes_model.joblib) joblib.dump(scaler, scaler.joblib) def predict_risk(record): loaded_model joblib.load(diabetes_model.joblib) loaded_scaler joblib.load(scaler.joblib) X loaded_scaler.transform(np.array(record).reshape(1, -1)) proba loaded_model.predict_proba(X)[0, 1] level 高风险 if proba 0.5 else 低风险 return {患病概率: round(float(proba), 3), 风险等级: level} sample [2, 130, 82, 22, 150, 27.4, 0.38, 34] print(predict_risk(sample))record 参数必须与训练时的特征顺序完全一致这里依次是怀孕次数、血糖、血压、皮脂厚度、胰岛素、BMI、遗传系数、年龄。.reshape(1, -1) 把一维数组变成二维单行样本因为 sklearn 的模型不接受一维输入。加载模型放到函数体内是为了每次调用都读取最新的模型文件在模型持续更新的场景下更灵活。6.2 给项目补一份能复现的文档文档说明是这个项目最容易被人忽略、却最能体现工程素养的部分。我一般建议至少包含 README、环境依赖文件、模型对比记录三件套。README 的结构参考# 糖尿病预测系统 ## 项目简介 基于机器学习二分类模型的糖尿病风险预测工具。 ## 快速开始 1. pip install -r requirements.txt 2. python train.py 3. python predict.py --input 2,130,82,22,150,27.4,0.38,34 ## 数据说明 - 数据集768 条样本8 个特征 1 个二分类标签 - 预处理0 值按缺失值分组中位数填充特征标准化 ## 模型与参数 - 最终模型XGBoost - 参数max_depth3, learning_rate0.05, n_estimators200 - 随机种子42 ## 评估结果 | 指标 | 数值 | | --- | --- | | 准确率 | 0.79 | | 召回率 | 0.78 | | AUC | 0.86 |文档的目标是让一个没参与过项目的人在另一台机器上 30 分钟内复现出同样的结果。数据说明部分重点写清楚 0 值处理逻辑这是复现时最容易对不上的地方。模型与参数部分必须写随机种子否则别人即使代码一样也还原不出完全相同的数字。6.3 下一步API、界面与 SHAP如果想让系统进入真实流程常见的进阶路线有三条。第一条是把 predict_risk 函数封装成 FastAPI 接口让医院信息系统的前端可以调用第二条是用 Streamlit 做一个简单的 Web 界面体检指标通过表单填写页面直接展示风险概率和风险等级第三条是引入 SHAP 可解释性分析输出每个特征对本次预测的贡献度让医生看到“为什么给出高风险结论”。我最早做这个项目时只顾着堆模型、调准确率代码和文档没有同步更新三个月后自己都忘了那组参数是怎么来的只能靠 git 历史反推。从那以后养成了习惯每调一次参就顺手在表格里记一笔每改一版代码就同步更新 README落到底就是一句话——模型会过期文档不会。希望这篇笔记能帮你在复现糖尿病预测系统的时候少走几条弯路源码和文档的价值恰恰是在项目完成后才真正体现出来。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑