YAOTU INSIGHTS

糖尿病预测机器学习项目实战:从Notebook到可部署模型

糖尿病预测机器学习项目实战:从Notebook到可部署模型
简介Diabetes-Predictor 是一份面向数据科学初学者与机器学习实践者的糖尿病风险预测项目资源基于 Pima Indians Diabetes 等公开生理指标数据集完整演示从数据预处理、探索性分析到建模评估的全流程。压缩包共 4 个文件约 322KB包含 1 个 Jupyter Notebook、1 个 Python 脚本、1 个 joblib 模型文件与 1 个 Markdown 说明文档分别对应交互式分析、可复用代码、已训练模型与项目说明便于直接运行与二次修改。已有 236 人学习下载。读者可借此掌握缺失值处理、特征工程、逻辑回归与随机森林等模型训练、交叉验证调参及准确率、F1、AUC 等指标评估方法并理解特征重要性与模型解释思路适合作为课程作业、入门练手或公共卫生风险预测的参考案例。1. 一个糖尿病预测项目为什么值得你花半小时跑一遍Diabetes-Predictor 这个项目名字直白得有点不像话。它就是一个用 Jupyter Notebook 写的糖尿病风险预测模型输入几个体检指标输出一个概率值。你可能会想这种项目 GitHub 上一抓一大把有什么好拆的。但真正跑过十几个类似项目之后你会发现大部分所谓的“机器学习实战”要么是调包三行代码糊弄事要么是数据清洗写得像黑匣子你根本不知道中间发生了什么。Diabetes-Predictor 不一样的地方在于它把整个流程摊开在 Notebook 里从数据加载、缺失值处理、特征工程到模型训练和评估每一步都有可执行的代码块和输出结果。对于刚接触机器学习的新手这是一个能让你看清全貌的模板对于需要快速搭原型的熟手这是一个可以直接改数据源和特征列的脚手架。它不追求 SOTA 指标追求的是流程完整和逻辑透明。2. 项目架构拆解Notebook 里的机器学习流水线2.1 目录结构与文件职责拿到源码包之后先别急着点运行。花两分钟看一眼目录结构能省掉后面很多“文件找不到”的玄学问题。Diabetes-Predictor 的典型结构如下Diabetes-Predictor/ ├── data/ │ └── diabetes.csv # 原始数据集通常是 Pima Indians Diabetes 格式 ├── notebooks/ │ └── diabetes_predictor.ipynb # 主 Notebook包含完整流程 ├── models/ │ └── trained_model.pkl # 训练好的模型序列化文件运行后生成 ├── requirements.txt # Python 依赖清单 └── README.md # 项目说明data/目录放原始 CSV这是整个项目的输入源头。notebooks/是核心工作区所有代码都在.ipynb里。models/目录一开始可能是空的运行完训练代码后才会生成.pkl文件。requirements.txt决定了你环境里需要装哪些包常见的是pandas、numpy、scikit-learn、matplotlib、seaborn和joblib。如果你用 Anaconda大部分包已经预装了但版本号最好对一下避免sklearn新旧版本 API 差异导致报错。2.2 数据加载与探索性分析Notebook 的第一个代码块通常是导入库和加载数据。这里有一个容易被忽略的细节数据文件的路径。如果你在notebooks/目录下启动 Jupyter相对路径应该是../data/diabetes.csv而不是data/diabetes.csv。很多新手在这里翻车报FileNotFoundError。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, confusion_matrix, classification_report # 加载数据注意路径根据 Notebook 所在位置调整 df pd.read_csv(../data/diabetes.csv) # 查看前五行确认列名和数据类型 print(df.head()) # 查看基本统计信息重点关注均值、标准差和分位数 print(df.describe()) # 检查缺失值这个数据集通常用 0 表示缺失需要特别处理 print(df.isnull().sum())这段代码的逻辑很清晰先看数据长什么样再看数值分布最后确认有没有空值。df.head()让你确认列名是否和预期一致比如Glucose、BloodPressure、BMI、Age这些。df.describe()会暴露一个问题某些特征的均值明显偏低比如Glucose的最小值是 0这在生理上不可能。所以下一步必须处理这些伪装成 0 的缺失值。2.3 缺失值处理与特征工程Pima 数据集有个经典陷阱Glucose、BloodPressure、SkinThickness、Insulin、BMI这几个列里0 值其实是缺失值。直接删掉会损失大量样本用均值填充又太粗暴。常见做法是先用中位数填充再根据分布做微调。# 将生理指标中不可能的 0 值替换为 NaN cols_with_zero [Glucose, BloodPressure, SkinThickness, Insulin, BMI] df[cols_with_zero] df[cols_with_zero].replace(0, np.nan) # 用中位数填充缺失值中位数比均值更抗异常值 for col in cols_with_zero: median_val df[col].median() df[col].fillna(median_val, inplaceTrue) # 确认填充后没有剩余缺失值 print(df.isnull().sum()) # 特征标准化逻辑回归和 SVM 对尺度敏感 scaler StandardScaler() feature_cols [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age] df[feature_cols] scaler.fit_transform(df[feature_cols])这里有几个参数需要留意。replace(0, np.nan)是把所有指定列的 0 替换成 NaN方便后续用fillna统一处理。中位数填充的好处是不受极端值影响比如Insulin的分布非常偏斜均值会被拉高。标准化用StandardScaler它把每个特征变成均值 0、方差 1 的分布。注意fit_transform是在整个数据集上做的严格来说应该只在训练集上 fit但作为教学项目这样写更容易理解。如果你要用于实际预测记得把 scaler 也保存下来否则新数据的标准化基准会不一致。2.4 模型训练与评估模型部分通常选逻辑回归作为基线因为它的可解释性强系数能直接反映特征重要性。训练过程就是一行fit但评估才是重点。# 划分训练集和测试集stratify 保证正负样本比例一致 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 ) # 训练逻辑回归模型max_iter 调大避免收敛警告 model LogisticRegression(max_iter1000, random_state42) model.fit(X_train, y_train) # 预测并评估 y_pred model.predict(X_test) print(Accuracy:, accuracy_score(y_test, y_pred)) print(\nConfusion Matrix:\n, confusion_matrix(y_test, y_pred)) print(\nClassification Report:\n, classification_report(y_test, y_pred))train_test_split里的stratifyy很关键。糖尿病数据集中正负样本比例大概是一比一但如果不加 stratify随机划分可能导致训练集和测试集的阳性比例差异较大评估结果就不稳定。max_iter1000是因为默认的 100 次迭代在标准化后的数据上可能不够会报ConvergenceWarning。评估部分不要只看 accuracyconfusion_matrix和classification_report能告诉你模型在阳性样本上的召回率和精确率这在医疗场景下比整体准确率重要得多。3. 本地运行实操从零把 Notebook 跑起来3.1 环境准备与依赖安装不管你用 Windows、macOS 还是 Linux第一步都是确认 Python 版本。这个项目一般在 Python 3.8 到 3.11 之间都能跑太老的 3.6 可能遇到sklearn版本不兼容。推荐用虚拟环境避免污染全局包。# 创建虚拟环境名字随便取这里叫 diabetes_env python -m venv diabetes_env # 激活虚拟环境 # Windows: diabetes_env\Scripts\activate # macOS/Linux: source diabetes_env/bin/activate # 安装依赖如果 requirements.txt 存在 pip install -r requirements.txt # 如果没有 requirements.txt手动装核心包 pip install pandas numpy scikit-learn matplotlib seaborn jupyter joblib虚拟环境的好处是你在这个项目里装的包不会影响其他项目。requirements.txt里通常会锁定版本号比如scikit-learn1.3.0这样能保证你跑出来的结果和作者一致。如果没锁版本直接装最新版也行但要注意sklearn在 1.0 之后有些 API 变了比如LogisticRegression的multi_class参数默认值调整过不过这个项目是二分类影响不大。3.2 启动 Jupyter 并逐块执行依赖装好之后在项目根目录下启动 Jupyter Notebookjupyter notebook浏览器会自动打开一个页面显示当前目录的文件列表。点进notebooks/diabetes_predictor.ipynb你会看到一个个代码块。建议从第一个块开始按Shift Enter逐块执行。不要一上来就点Run All因为中间某个块报错的话后面的块会跟着出问题排查起来很麻烦。逐块执行的好处是你能看到每一步的输出。比如加载数据后打印df.head()确认列名和数值处理缺失值后打印df.isnull().sum()确认全是 0训练完模型后打印评估指标看看准确率是否在合理范围。如果某个块报错错误信息会直接显示在下方根据提示改就行。3.3 常见运行错误与排查即使步骤都对还是可能遇到一些报错。这里列几个高频问题问题一ModuleNotFoundError: No module named sklearn原因是你没有在虚拟环境里装 scikit-learn或者 Jupyter 用的内核不是当前虚拟环境。解决办法是在虚拟环境里执行pip install scikit-learn然后确认 Jupyter 内核选的是diabetes_env。可以在 Notebook 里运行import sys; print(sys.executable)查看当前 Python 路径。问题二FileNotFoundError: [Errno 2] No such file or directory: ../data/diabetes.csv原因是相对路径不对。Jupyter 的工作目录是你启动它时所在的目录。如果你在项目根目录启动Notebook 在notebooks/下那么../data/是对的。如果你在notebooks/目录下启动路径应该是data/diabetes.csv。最稳妥的办法是用绝对路径或者用os.path动态拼接。问题三ConvergenceWarning: lbfgs failed to converge原因是逻辑回归的迭代次数不够。解决办法是把max_iter调大比如max_iter2000。如果调大还不行检查一下特征是否标准化了未标准化的数据会让梯度下降收敛很慢。问题四模型准确率只有 65% 左右这个数据集的基线准确率大概在 75% 到 78% 之间。如果低于 70%检查一下缺失值处理是否正确特别是Glucose和BMI这两列。另外确认Outcome列没有被打乱stratifyy是否加了。4. 避坑与常见问题那些文档没写但一定会遇到的事4.1 数据泄漏标准化做在了划分之前这是新手最容易犯的错误。上面的示例代码里StandardScaler是在整个数据集上fit_transform的然后才划分训练集和测试集。严格来说这会导致数据泄漏测试集的统计信息均值和方差参与了训练。正确的做法是先划分再在训练集上fit然后transform测试集。# 正确做法先划分再标准化 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) # 注意这里是 transform不是 fit_transform现象是模型在测试集上表现异常好但换一批新数据就崩了。原因是测试集的分布信息泄漏到了训练过程中。解决办法就是上面这段代码把fit和transform分开。4.2 中位数填充的边界情况用中位数填充缺失值本身没问题但如果某一列缺失比例超过 50%中位数填充会引入大量偏差。比如Insulin列在某些版本的数据集中缺失率接近 50%这时候更好的做法是删掉这一列或者用模型预测填充。检查缺失比例的方法# 计算每列的缺失比例 missing_ratio df.isnull().sum() / len(df) print(missing_ratio[missing_ratio 0])如果某列缺失比例超过 40%建议在特征工程阶段直接 drop 掉或者用 KNN 填充。KNN 填充的逻辑是找相似的样本用它们的值来补比中位数更合理但计算量更大。4.3 类别不平衡的隐性影响虽然 Pima 数据集的类别比例大致均衡但如果你换了自己的数据阳性样本可能只占 10%。这时候 accuracy 会骗人全预测为阴性也能有 90% 准确率。解决办法是用class_weightbalanced让模型自动调整权重或者用 SMOTE 过采样。在LogisticRegression里加一个参数就行model LogisticRegression(max_iter1000, class_weightbalanced, random_state42)现象是模型在少数类上的召回率极低但整体准确率看起来不错。原因是模型倾向于预测多数类。加上class_weightbalanced后模型会给少数类更高的惩罚权重召回率会明显提升。4.4 Notebook 状态混乱执行顺序依赖Jupyter Notebook 的一个坑是代码块的执行顺序可以和书写顺序不一致。你可能先跑了后面的块再跑前面的块导致变量状态混乱。比如你先训练了模型然后回头改了特征列但没有重新训练评估结果就是错的。解决办法是每次改完代码从第一个块重新Run All。或者在开发阶段用Restart Run All清空所有变量重新执行。这个习惯能帮你省掉很多“为什么结果不对”的排查时间。5. 二次开发改造从 Demo 到可用工具的几步5.1 把 Notebook 拆成可复用的 Python 脚本Notebook 适合探索不适合部署。如果你想把这个模型集成到 Web 服务或者定时任务里第一步是把核心逻辑抽成.py文件。常见做法是拆成三个模块data_loader.py负责加载和清洗数据trainer.py负责训练和保存模型predictor.py负责加载模型并对外提供预测接口。# predictor.py 示例 import joblib import numpy as np class DiabetesPredictor: def __init__(self, model_pathmodels/trained_model.pkl, scaler_pathmodels/scaler.pkl): self.model joblib.load(model_path) self.scaler joblib.load(scaler_path) def predict(self, features): # features 是一个二维数组形状 (n_samples, 8) features_scaled self.scaler.transform(features) proba self.model.predict_proba(features_scaled)[:, 1] return proba # 使用示例 predictor DiabetesPredictor() sample np.array([[2, 120, 70, 25, 100, 28.5, 0.5, 35]]) print(predictor.predict(sample))这样拆完之后你可以在 Flask 或 FastAPI 里调用DiabetesPredictor传入前端表单数据返回风险概率。注意保存模型时要把 scaler 一起保存否则新数据的标准化基准不一致预测结果会偏得离谱。5.2 特征工程升级从 8 个特征到更多衍生变量原始数据只有 8 个特征信息量有限。你可以尝试构造一些衍生特征比如 BMI 和 Age 的交互项、Glucose 和 Insulin 的比值、Pregnancies 和 Age 的比值。这些在医学上有一定解释性可能提升模型表现。# 构造衍生特征 df[BMI_Age] df[BMI] * df[Age] df[Glucose_Insulin_Ratio] df[Glucose] / (df[Insulin] 1) # 加 1 避免除零 df[Pregnancies_Age_Ratio] df[Pregnancies] / (df[Age] 1) # 重新训练并对比效果 feature_cols_v2 feature_cols [BMI_Age, Glucose_Insulin_Ratio, Pregnancies_Age_Ratio]加完之后重新跑一遍训练流程对比classification_report里的 F1 分数。如果提升不明显说明这些衍生特征没有带来额外信息可以不加。特征工程不是越多越好冗余特征会增加过拟合风险。5.3 模型替换与超参数调优逻辑回归是基线你可以换成随机森林、XGBoost 或者 LightGBM。换模型的时候注意树模型不需要标准化所以StandardScaler那一步可以跳过。超参数调优用GridSearchCV或者RandomizedSearchCV不要手动一个个试。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [100, 200, 300], max_depth: [5, 10, None], min_samples_split: [2, 5, 10] } rf RandomForestClassifier(random_state42) grid_search GridSearchCV(rf, param_grid, cv5, scoringf1, n_jobs-1) grid_search.fit(X_train, y_train) print(Best params:, grid_search.best_params_) print(Best F1:, grid_search.best_score_)scoringf1是因为医疗场景下我们更关心阳性样本的识别能力而不是整体准确率。cv5是五折交叉验证n_jobs-1表示用所有 CPU 核心并行计算。调参之后把最优模型保存下来替换掉原来的逻辑回归。5.4 一个我踩过的坑模型保存与版本兼容有一次我把训练好的模型用joblib.dump保存过了两周换了个环境加载报了一堆AttributeError。原因是sklearn版本变了旧版本保存的模型在新版本里反序列化失败。从那以后我每次保存模型都会在文件名里带上版本号比如model_sklearn_1.3.0.pkl并且在requirements.txt里锁定版本。另外joblib比pickle更适合保存 numpy 数组大的模型速度更快文件更小。希望这些改造思路能帮到你。拿到源码之后先跑通再拆解最后按自己的数据改一遍比只看不练强得多。本文还有配套的精品资源点击获取