简介:Diabetes-Predictor 是一份面向数据科学初学者与医疗健康分析爱好者的机器学习实战资源,围绕 Pima Indians 糖尿病数据集,完整演示从数据预处理、探索性分析到建模评估的预测流程,帮助读者理解如何用分类模型评估疾病风险。压缩包共 4 个文件,约 322KB,包含 1 个 ipynb 交互式笔记本、1 个 py 脚本、1 个 joblib 训练模型和 1 个 md 说明文档,覆盖代码复现、脚本调用与模型加载等用途。目前已有 236 人学习下载。资源价值在于提供可直接运行的端到端案例:笔记本中呈现缺失值处理、特征工程、多模型对比与交叉验证等关键环节,py 脚本便于快速执行,joblib 文件省去重新训练成本,md 文档则梳理项目结构与思路,适合作为课程作业、入门练手或医疗预测类项目的参考模板。
1. 一个糖尿病预测项目,为什么值得你花半小时跑一遍
Diabetes-Predictor 这个名字听起来像医学课题,实际上它是一份用 Jupyter Notebook 写成的二分类机器学习实战代码。数据集是经典的 Pima Indians Diabetes 结构,8 个特征、1 个标签,任务是根据血糖、血压、BMI 等指标判断是否患病。它适合两类人:刚学完 sklearn 想找一个完整流程练手的新手,以及需要快速搭一个基线模型再迭代的开发者。整个项目没有花哨的深度学习,逻辑回归、随机森林、XGBoost 挨个上,配合混淆矩阵和 ROC 曲线做评估。你拿到手就能在本地跑通,改几行参数就能看到指标变化,这种即时反馈对建立工程直觉特别有用。
2. 项目结构与技术选型:Notebook 里到底装了什么
2.1 目录布局与文件职责
拿到源码包,第一件事是看目录。Diabetes-Predictor 的结构很典型,没有过度工程化:
Diabetes-Predictor/ ├── data/ │ └── diabetes.csv # 原始数据集,8特征+1标签 ├── notebooks/ │ └── diabetes_predictor.ipynb # 主分析流程 ├── models/ │ └── best_model.pkl # 训练后持久化的模型 ├── requirements.txt # 依赖清单 └── README.mddata/放原始 CSV,notebooks/是核心工作区,models/存训练好的模型文件。这种布局的好处是数据和代码分离,你换一份同结构的 CSV 就能复用整个流程。常见做法是把特征工程和模型训练拆成两个 Notebook,但这个项目合在一起,对初学者更友好,从上到下执行一遍就能看到完整链路。
2.2 技术栈与依赖版本
核心依赖就四个:pandas做数据加载和清洗,scikit-learn负责预处理、模型训练和评估,matplotlib+seaborn画图,xgboost作为集成模型的补充。requirements.txt里通常锁定版本,避免因为 sklearn 版本差异导致train_test_split的stratify参数行为不一致。
# 建议用虚拟环境隔离,避免和系统包冲突 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt这里有个细节:如果你用的是 Apple Silicon 芯片,xgboost 的安装可能需要额外指定pip install xgboost --no-binary :all:,否则会报架构不匹配。这不是项目本身的问题,是环境差异,提前知道能省半小时排查。
2.3 数据加载与探索性分析
Notebook 的前几个 cell 做的是标准 EDA。先读数据,再看分布,最后检查缺失值。Pima 数据集有个坑:很多字段用 0 表示缺失,比如血糖为 0 在生理上不可能。项目里通常会把这类 0 值替换成 NaN 再填充。
import pandas as pd import numpy as np # 加载数据,列名按数据集惯例命名 columns = ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age', 'Outcome'] df = pd.read_csv('data/diabetes.csv', names=columns) # 这些字段的 0 值在医学上无意义,视为缺失 zero_as_missing = ['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI'] df[zero_as_missing] = df[zero_as_missing].replace(0, np.nan) # 用中位数填充,比均值更抗异常值 df.fillna(df.median(), inplace=True) print(df.describe())这段代码的关键在replace(0, np.nan)和fillna(median)。参数inplace=True表示原地修改,省内存。中位数填充是常见做法,因为血糖和胰岛素分布右偏,均值会被极端值拉高。如果你换成均值,模型召回率可能掉两三个百分点,这是实际跑一遍就能观察到的差异。
3. 核心模块拆解:从特征工程到模型评估的完整链路
3.1 特征标准化与数据划分
原始特征的量纲差异很大:Age最大 81,DiabetesPedigreeFunction最大 2.42。逻辑回归和 KNN 对尺度敏感,必须标准化。项目里用StandardScaler做 Z-score 归一化,然后按 8:2 划分训练集和测试集。
from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split X = df.drop('Outcome', axis=1) y = df['Outcome'] # 先划分再标准化,避免数据泄露 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 注意:测试集用 transform 而非 fit_transform这里最容易翻车的地方是数据泄露:如果在划分前就对全量数据做fit_transform,测试集的统计信息会渗入训练过程,评估指标虚高。stratify=y保证训练集和测试集的标签比例一致,对不平衡数据尤其重要。random_state=42固定随机种子,保证你每次跑的结果可复现。
3.2 多模型对比与交叉验证
项目不会只跑一个模型就下结论。Notebook 里通常定义一个模型字典,循环训练并输出交叉验证分数:
from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score from xgboost import XGBClassifier models = { 'LogisticRegression': LogisticRegression(max_iter=1000), 'RandomForest': RandomForestClassifier(n_estimators=100, random_state=42), 'XGBoost': XGBClassifier(use_label_encoder=False, eval_metric='logloss') } for name, model in models.items(): scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring='roc_auc') print(f'{name}: AUC={scores.mean():.4f} (+/- {scores.std():.4f})')cross_val_score的cv=5表示 5 折交叉验证,scoring='roc_auc'用 AUC 而非准确率,因为糖尿病数据正负样本比例大约 65:35,准确率有欺骗性。max_iter=1000是给逻辑回归的迭代上限,默认 100 在未标准化数据上经常不收敛。XGBoost 的use_label_encoder=False是较新版本的参数,老版本不需要,报错时删掉即可。
3.3 模型评估与可视化
训练完不是看一个数字就结束。项目会输出混淆矩阵、分类报告和 ROC 曲线:
from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc import matplotlib.pyplot as plt best_model = models['RandomForest'].fit(X_train_scaled, y_train) y_pred = best_model.predict(X_test_scaled) y_prob = best_model.predict_proba(X_test_scaled)[:, 1] print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred)) fpr, tpr, _ = roc_curve(y_test, y_prob) plt.plot(fpr, tpr, label=f'AUC = {auc(fpr, tpr):.3f}') plt.plot([0, 1], [0, 1], 'k--') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.legend() plt.show()predict_proba返回的是概率,取第二列作为正类概率。混淆矩阵让你看到假阳性和假阴性各有多少——在医疗场景里,假阴性(漏诊)比假阳性更严重,所以召回率比精确率更值得关注。ROC 曲线的对角线是随机猜测的基线,曲线越靠左上越好。这些图在 Notebook 里直接渲染,改一个模型名字就能重新出图,迭代成本极低。
3.4 模型持久化与复用
训练完的模型用joblib存到models/目录,下次直接加载,不用重新训练:
import joblib joblib.dump(best_model, 'models/best_model.pkl') joblib.dump(scaler, 'models/scaler.pkl') # 标准化器也要存,推理时保持一致 # 加载时 loaded_model = joblib.load('models/best_model.pkl') loaded_scaler = joblib.load('models/scaler.pkl')注意标准化器必须和模型一起保存。很多人只存模型,推理时忘了对新数据做同样的标准化,结果预测全错。这是血泪经验,别问我是怎么知道的。
4. 避坑与常见问题:跑不起来时先看这几条
4.1 缺失值处理不当导致模型偏差
现象:模型准确率看着不错,但混淆矩阵里假阴性特别多。原因:直接用 0 值训练,模型把“血糖为 0”当成一个有效特征学进去了。解决:在 EDA 阶段就把生理上不可能的 0 值替换为 NaN,再用中位数或 KNN 填充。检查方法是df[df['Glucose'] == 0].shape[0],如果大于 0 就说明没处理干净。
4.2 标准化顺序错误引发数据泄露
现象:交叉验证分数很高,但换一批新数据预测效果骤降。原因:在train_test_split之前就做了fit_transform,测试集信息泄露到训练过程。解决:严格遵循“先划分、再 fit 训练集、最后 transform 测试集”的顺序。记住fit_transform只能出现在训练集上,测试集永远只用transform。
4.3 版本不兼容导致 XGBoost 报错
现象:XGBClassifier初始化时报TypeError: __init__() got an unexpected keyword argument 'use_label_encoder'。原因:xgboost 2.0 之后移除了这个参数。解决:删掉use_label_encoder=False,只保留eval_metric='logloss'。或者用pip install xgboost==1.7.6降级到兼容版本。这类报错信息很明确,顺着改就行,不用慌。
4.4 随机种子未固定导致结果不可复现
现象:每次重新运行 Notebook,AUC 都在变,没法对比模型优劣。原因:train_test_split和模型初始化都没设random_state。解决:在所有涉及随机性的地方统一加random_state=42,包括train_test_split、RandomForestClassifier、cross_val_score的cv参数(用StratifiedKFold时也要设)。固定种子后,同一份代码跑一百遍结果都一样,调参才有意义。
4.5 Notebook 执行顺序混乱导致变量未定义
现象:单独运行某个 cell 报NameError: name 'X_train_scaled' is not defined。原因:跳过了前面的 cell,或者改了代码后没按顺序重跑。解决:改完代码后点Kernel -> Restart & Run All,从头到尾执行一遍。Notebook 的隐式状态是双刃剑,方便探索但容易埋雷。养成改完就全量重跑的习惯,能避免很多玄学问题。
5. 二次开发与改造思路:从 Demo 到可用工具的几步
5.1 特征工程升级
当前项目只做了中位数填充和标准化。想提升效果,可以加特征交叉,比如Glucose * BMI作为新特征,或者用PolynomialFeatures生成二次项。另一个方向是分箱:把Age按 10 岁一段离散化,有时能捕捉非线性关系。改完后用同一套交叉验证对比,AUC 提升超过 0.01 才值得保留,否则容易过拟合。
5.2 模型调参与集成
随机森林的n_estimators、max_depth、min_samples_split都可以用GridSearchCV搜。但别一上来就大网格,先粗后细:
from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [50, 100, 200], 'max_depth': [None, 5, 10], 'min_samples_split': [2, 5] } grid = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=5, scoring='roc_auc', n_jobs=-1) grid.fit(X_train_scaled, y_train) print(grid.best_params_, grid.best_score_)n_jobs=-1用满所有 CPU 核心,但注意内存占用。搜完把最优参数写回模型字典,再跑一次评估。如果多个模型 AUC 接近,可以用VotingClassifier做软投票集成,通常比单模型稳。
5.3 封装成可调用的预测函数
Notebook 适合探索,但真要给别人用,得封装成函数或脚本:
def predict_diabetes(features: dict) -> dict: """输入原始特征字典,返回预测标签和概率""" model = joblib.load('models/best_model.pkl') scaler = joblib.load('models/scaler.pkl') input_df = pd.DataFrame([features]) input_scaled = scaler.transform(input_df) prob = model.predict_proba(input_scaled)[0, 1] return {'prediction': int(prob > 0.5), 'probability': round(prob, 4)}这个函数可以直接被 Flask 或 FastAPI 调用,变成 HTTP 接口。注意输入特征的顺序必须和训练时一致,否则标准化会错位。我一般会在函数里加一个assert list(features.keys()) == expected_columns做校验,防止调用方传错字段。
5.4 验证改造效果的方法
每次改完代码,别只看一个 AUC 数字。固定测试集,对比改造前后的混淆矩阵和分类报告,重点看召回率和 F1 的变化。如果召回率提升但精确率下降,要判断业务上哪个更重要。医疗筛查场景宁可误报也不能漏诊,所以召回率优先。另外,用cross_validate返回多个指标,一次性看全貌:
from sklearn.model_selection import cross_validate scoring = ['accuracy', 'precision', 'recall', 'f1', 'roc_auc'] results = cross_validate(best_model, X_train_scaled, y_train, cv=5, scoring=scoring) for metric in scoring: print(f'{metric}: {results["test_" + metric].mean():.4f}')从那以后我每次改完模型,都强制走一遍“固定种子 → 全量重跑 → 多指标对比”的流程,再也没出现过“改完反而更差但不知道差在哪”的情况。希望这份拆解能帮你少走点弯路,把 Diabetes-Predictor 跑起来、改起来。
本文还有配套的精品资源,点击获取