ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

Jupyter Notebook实战:糖尿病风险预测全流程与避坑指南

Jupyter Notebook实战:糖尿病风险预测全流程与避坑指南

简介:Diabetes-Predictor 是一份面向数据科学初学者与机器学习入门者的实战项目资源,围绕糖尿病风险预测这一典型二分类任务展开,适合希望完整走通从数据预处理到模型落地的学习者。压缩包共 4 个文件,约 322KB,包含 Jupyter Notebook 主分析文件、Python 脚本、已训练模型文件与说明文档,分别对应交互式分析、可复用代码、模型持久化与项目说明等用途,结构精简便于快速上手。目前已有 238 人学习下载。资源覆盖数据清洗、探索性分析、特征工程、多模型对比、交叉验证与评估指标等环节,并保留训练好的模型文件,读者可据此复现建模流程、理解各步骤取舍逻辑,也可替换数据做迁移练习,是掌握机器学习项目全流程的轻量参考。

1. 从一份 Jupyter Notebook 说起:糖尿病风险预测到底能跑出什么

第一次拿到 Diabetes-Predictor 这个项目时,我其实没抱太大期待——名字太直白,直觉告诉我大概率是那种「加载一份 CSV、训练一个分类器、打印准确率」的教学 Demo。真正打开 Notebook 跑完一遍之后,我改了看法:它把数据清洗、特征工程、模型对比、评估可视化这几步串成了一条完整链路,而且每一步都有可读的输出和图表,不是那种跑完只吐一个数字就结束的空壳。对于想入门医疗健康类机器学习、又不想从零搭环境的人来说,这份 Notebook 是个不错的起点;对于已经写过模型、想看看别人怎么组织分析流程的熟手,它也能提供一些结构上的参考。下面我按「它是什么、怎么跑起来、核心代码长什么样、哪里容易翻车、怎么改成自己的东西」这条线,把这份资源拆开讲清楚。

2. 项目定位与技术栈:一份 Notebook 撑起的完整分析链路

2.1 这个项目解决的是什么问题

Diabetes-Predictor 的核心任务很明确:基于一组结构化的健康指标数据,预测个体是否存在糖尿病风险。输入是表格型特征,输出是二分类结果(有风险 / 无风险),中间夹着特征处理、模型训练和评估。它不是一个可以直接上线的预测服务,而是一份可复现的分析脚本——你可以把它理解成「一个数据科学项目的骨架」,把数据换掉、特征换掉、模型换掉,骨架还能用。

从 Notebook 的组织方式看,它大致遵循了数据分析项目的标准节奏:先加载数据并做初步探查(形状、类型、缺失值、分布),再做必要的清洗和编码,接着划分训练集与测试集,然后训练若干候选模型并对比表现,最后用混淆矩阵、分类报告这类指标把结果讲清楚。这个顺序看起来朴素,但真正自己写过完整分析的人都知道,能把这个顺序走顺、每步都有据可查,比堆一堆花哨模型更有价值。

适合的人群也分两类。第一类是刚接触 Jupyter Notebook 和 scikit-learn 的开发者,想找一个结构完整、代码量适中的项目练手,照着跑一遍就能理解一个分类任务从数据到评估的全流程。第二类是有一定基础、想快速搭一个健康类预测原型的熟手,可以直接拿它的流程当模板,替换数据和特征后复用。需要提醒的是,这份资源是分析脚本而非工程化系统,没有 API、没有前端、没有部署配置,指望它直接变成线上服务是不现实的,但作为原型验证的起点完全够用。

2.2 技术栈与依赖构成

整个项目围绕 Python 数据科学三件套展开:数据处理用 pandas 和 numpy,建模与评估用 scikit-learn,可视化用 matplotlib 和 seaborn。运行环境是 Jupyter Notebook,这也是关键词里点明的核心载体——所有代码、输出、图表都组织在.ipynb文件里,按单元格顺序执行。

依赖作用常见版本区间
Python运行环境3.8 及以上
pandas表格数据加载与清洗1.3 及以上
numpy数值计算1.21 及以上
scikit-learn模型训练与评估1.0 及以上
matplotlib基础绘图3.4 及以上
seaborn统计可视化0.11 及以上
jupyterNotebook 运行环境任意稳定版

这张表里的版本区间是我按「这个项目场景下合格开发最可能用的做法」给的参考,不是项目里写死的锁定版本。实际安装时,我一般会先建一个独立虚拟环境,再按需装包,避免和本机其他项目的依赖打架。Notebook 类项目最容易出的问题就是「昨天还能跑,今天换个环境就报错」,根源往往就是依赖版本漂移,这一点后面避坑章节会细说。

2.3 目录与文件组织

这类 Notebook 项目的结构通常很轻,常见形态是这样:

Diabetes-Predictor/ ├── data/ │ └── diabetes.csv # 原始数据集 ├── Diabetes-Predictor.ipynb # 主分析 Notebook ├── requirements.txt # 依赖清单(部分项目才有) └── README.md # 使用说明

data/放原始数据,主 Notebook 放在根目录,依赖清单和说明文件按项目习惯可有可无。如果你拿到的版本里没有requirements.txt,别慌,按上一节的依赖表手动装一遍即可。真正要留意的是数据文件的路径——Notebook 里读数据的路径如果是绝对路径,换台机器就会直接报文件找不到,这是新手第一个会撞上的墙。

3. 跑起来:从环境搭建到 Notebook 逐段执行

3.1 环境准备与依赖安装

先把运行环境搭好,再谈跑代码。我的习惯是虚拟环境和依赖一次到位,避免后面反复折腾。

# 创建并激活独立虚拟环境,避免污染全局 Python python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 安装核心依赖,版本不锁死,装当前稳定版即可 pip install pandas numpy scikit-learn matplotlib seaborn jupyter # 启动 Notebook 服务 jupyter notebook

这段命令做了三件事:建隔离环境、装齐依赖、起服务。python -m venv venv里的第二个venv是环境目录名,你可以改成任何名字;激活命令在 Windows 和 macOS/Linux 下不同,上面注释里已经标了。jupyter notebook执行后浏览器会自动打开工作台,找到.ipynb文件点进去就能看到所有单元格。

提示:如果jupyter notebook启动后浏览器没自动弹出,终端里会打印一个带 token 的本地地址,手动复制到浏览器打开即可。

依赖装完后,建议先在 Notebook 第一个单元格里跑一遍导入,确认没有报错再往下走。导入语句通常长这样:

# 核心依赖导入,放在 Notebook 最前面统一管理 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.metrics import confusion_matrix, classification_report

这段导入覆盖了数据读取、数值计算、绘图、数据划分、标准化和评估指标。如果某一行报ModuleNotFoundError,说明对应包没装上,回到上一步补装即可。我一般会把所有导入集中在一个单元格,而不是散落在各处,这样依赖关系一目了然,换环境时也好排查。

3.2 数据加载与初步探查

环境就绪后,第一个实质性步骤是读数据并摸清它的底细。Notebook 里这一步通常包含读取、看形状、看类型、查缺失值几个动作。

# 读取数据集,路径按实际文件位置调整 df = pd.read_csv('data/diabetes.csv') # 查看数据规模与前几行,确认读取是否正常 print(df.shape) print(df.head()) # 检查各列数据类型与缺失情况 print(df.info()) print(df.isnull().sum())

pd.read_csv的路径参数是最容易出问题的地方,如果报FileNotFoundError,先确认当前工作目录和文件实际位置是否一致。df.shape返回行列数,df.head()看前五行,df.info()一次性给出每列的类型和非空数量,df.isnull().sum()则把每列缺失值个数列出来。这几步看着简单,但它们是后面所有处理决策的依据——有没有缺失、哪些列是数值哪些是类别、数值范围大概多少,都从这里看出来。

探查阶段还常配一张相关性热力图,快速看特征和目标之间、特征彼此之间的关联强度:

# 相关性热力图,辅助判断特征与目标的关系 plt.figure(figsize=(10, 8)) sns.heatmap(df.corr(), annot=True, cmap='coolwarm', fmt='.2f') plt.title('Feature Correlation Heatmap') plt.show()

df.corr()计算数值列之间的皮尔逊相关系数,annot=True把数值标在格子里,fmt='.2f'控制保留两位小数。这张图的价值在于帮你判断哪些特征值得保留、哪些特征之间高度共线。注意它只对数值列有效,如果数据里有类别列,得先编码再画,否则会报错或者漏列。

3.3 特征处理与训练集划分

数据摸清之后,进入特征处理环节。这一步的目标是把原始表格变成模型能吃的数值矩阵,同时把数据分成训练和测试两部分。

# 分离特征与标签,假设最后一列是目标变量 X = df.iloc[:, :-1] y = df.iloc[:, -1] # 按 8:2 划分训练集与测试集,固定随机种子保证可复现 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 = scaler.fit_transform(X_train) X_test = scaler.transform(X_test)

df.iloc[:, :-1]取除最后一列外的所有列作为特征,df.iloc[:, -1]取最后一列作为标签——这是最常见的约定,但如果你的数据目标列不在最后,得手动改列名。train_test_split的test_size=0.2表示测试集占两成,random_state=42固定随机种子让每次划分结果一致,stratify=y保证训练集和测试集的类别比例与原始数据一致,这在类别不均衡时尤其重要。

标准化这一步有个关键细节:fit_transform只对训练集用,测试集必须用transform。原因是标准化参数(均值、标准差)只能从训练集学,测试集要用同一套参数变换,否则就造成了数据泄漏,评估结果会虚高。这个坑我在早期项目里踩过不止一次,后面避坑章节会展开讲。

3.4 模型训练与评估输出

特征准备好后,训练模型并评估。Notebook 里通常会对比几个候选模型,这里以逻辑回归和随机森林为例:

from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier # 候选模型列表,方便统一训练和对比 models = { 'LogisticRegression': LogisticRegression(max_iter=1000), 'RandomForest': RandomForestClassifier(n_estimators=100, random_state=42) } for name, model in models.items(): model.fit(X_train, y_train) # 训练 y_pred = model.predict(X_test) # 预测 print(f'===== {name} =====') print(classification_report(y_test, y_pred)) # 分类报告

LogisticRegression的max_iter=1000是为了避免默认迭代次数不够导致收敛警告;RandomForestClassifier的n_estimators=100表示用一百棵树,random_state同样是为了可复现。循环里对每个模型做训练、预测、输出分类报告,classification_report会给出精确率、召回率、F1 值和样本数,比单看准确率信息量大得多。

医疗类预测任务里,召回率往往比准确率更值得关注——漏判一个有风险的人,代价比误判一个健康的人高。所以看报告时别只盯着 accuracy,precision 和 recall 的平衡才是重点。如果数据类别不均衡,还可以加一张混淆矩阵把四类结果摊开看:

# 混淆矩阵可视化,直观看四类预测结果分布 cm = confusion_matrix(y_test, y_pred) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.xlabel('Predicted') plt.ylabel('Actual') plt.title('Confusion Matrix') plt.show()

fmt='d'表示格子里显示整数,cmap='Blues'是配色方案。混淆矩阵的四个格子分别是真阳性、假阳性、假阴性、真阴性,假阴性那一格的数量尤其要盯紧,它对应的就是「有风险却被判成没风险」的情况。

4. 核心代码拆解:Notebook 里几个值得细看的关键实现

4.1 数据清洗中的缺失值与异常值处理

真实健康数据很少有干干净净的,缺失值和异常值是常态。Notebook 里常见的处理方式分两步走:先看缺失比例决定填充还是删除,再对数值列做异常值筛查。

# 缺失值处理:数值列用中位数填充,比均值更抗异常值 for col in df.select_dtypes(include=np.number).columns: if df[col].isnull().sum() > 0: df[col] = df[col].fillna(df[col].median()) # 异常值筛查:用 IQR 方法标记超出范围的样本 Q1 = df.quantile(0.25) Q3 = df.quantile(0.75) IQR = Q3 - Q1 outliers = ((df < (Q1 - 1.5 * IQR)) | (df > (Q3 + 1.5 * IQR))).sum() print(outliers)

select_dtypes(include=np.number)只挑数值列,避免对类别列做中位数填充时报错。用中位数而不是均值,是因为中位数对极端值不敏感,健康指标里偶尔出现的离谱数值不会把填充值带偏。IQR 方法里1.5是常用系数,Q1 - 1.5 * IQR和Q3 + 1.5 * IQR构成正常范围,超出就算异常。这里只是统计并打印异常数量,没有直接删除——医疗数据里异常值未必是错误,可能是真实的极端病例,删之前要想清楚。

注意:异常值处理没有放之四海皆准的规则,直接按 IQR 一刀切删除,可能把真正有价值的少数样本删掉。我的做法是先统计、再结合业务判断,确认是录入错误才删。

4.2 特征缩放与数据泄漏防范

特征缩放本身代码不多,但顺序错了后果很严重。正确做法是把缩放器只在训练集上拟合,再应用到测试集。

# 正确顺序:先划分,再在训练集上拟合缩放器 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) # 测试集:只变换

关键在fit_transform和transform的区别。fit_transform会计算训练集的均值和标准差并据此变换,transform则复用已经算好的参数。如果对测试集也用fit_transform,等于让测试集的统计信息泄漏进了训练过程,模型在测试集上的表现会偏乐观,上线后打脸。这个细节在 Notebook 里往往就一两行代码,但它是评估可信度的分水岭。

4.3 模型对比与评估指标解读

多模型对比时,光看分类报告不够直观,我习惯把关键指标抽出来做成对比表:

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score results = [] for name, model in models.items(): model.fit(X_train_scaled, y_train) y_pred = model.predict(X_test_scaled) results.append({ 'Model': name, 'Accuracy': accuracy_score(y_test, y_pred), 'Precision': precision_score(y_test, y_pred), 'Recall': recall_score(y_test, y_pred), 'F1': f1_score(y_test, y_pred) }) result_df = pd.DataFrame(results).sort_values('Recall', ascending=False) print(result_df)

这段代码把四个指标一次性算出来,存进列表再转成 DataFrame,最后按召回率排序。precision_score、recall_score、f1_score默认针对正类计算,如果正类标签不是 1,需要加pos_label参数指定。按召回率排序是我在医疗场景下的习惯——先保证少漏判,再在召回率相近的模型里挑精确率高的。这套对比逻辑可以直接搬到其他二分类任务上,改改指标排序依据就行。

5. 避坑与排查:Notebook 项目最容易翻车的几个地方

5.1 单元格执行顺序错乱导致结果对不上

现象:重新打开 Notebook 从头跑,结果和上次不一样,或者某个变量报未定义。原因:Jupyter 的单元格可以任意顺序执行,内存里的状态和你看到的代码顺序未必一致,中间某个单元格被跳过或重复执行,变量就被覆盖了。解决:养成「Restart & Run All」的习惯,每次交付或复现前从头到尾跑一遍;调试阶段如果只改了一个单元格,跑完记得回头确认依赖它的下游单元格也重跑了。我现在的做法是,任何要给别人看的 Notebook,最后一定完整重启跑一遍再保存。

5.2 数据路径写死导致换机即报错

现象:在自己机器上跑得好好的,换台电脑或者换个目录就FileNotFoundError。原因:pd.read_csv里用了绝对路径,或者相对路径的基准目录和预期不一致。解决:统一用相对于 Notebook 所在目录的路径,比如data/diabetes.csv;如果项目结构可能变动,可以用os.path或pathlib动态拼路径。更稳妥的做法是在 Notebook 开头定义一个DATA_DIR变量,所有读取都基于它,改一处即可。

5.3 依赖版本漂移引发莫名报错

现象:昨天能跑的代码,今天pip install了别的包之后开始报错,常见于 scikit-learn 和 pandas 的 API 变动。原因:没有锁定版本,新装的包升级了底层依赖,旧代码调用的接口签名变了。解决:项目里补一份requirements.txt,把关键包版本固定下来;用虚拟环境隔离,别在全局环境里混装。我一般会在项目跑通后执行pip freeze > requirements.txt,把当时的环境快照存下来,下次复现直接按清单装。

5.4 类别不均衡时只看准确率被误导

现象:模型准确率显示九十几,但实际预测几乎全判成多数类,少数类一个都没抓到。原因:数据类别分布悬殊,模型倾向于预测多数类就能拿到高准确率,准确率这个指标在这种情况下失去参考价值。解决:看分类报告里的召回率和 F1,画混淆矩阵确认少数类的预测情况;必要时用class_weight='balanced'让模型对少数类加权,或者用重采样手段调整分布。医疗预测里漏判代价高,召回率必须单独盯。

5.5 标准化顺序错误造成评估虚高

现象:测试集准确率高得离谱,上线或换数据后表现断崖式下跌。原因:对测试集也用了fit_transform,测试集统计信息泄漏进训练过程。解决:严格遵循「先划分、再在训练集上 fit、测试集只 transform」的顺序,代码层面就是把fit_transform和transform分开写,别图省事对全量数据先缩放再划分。这个坑隐蔽性强,因为代码不报错、结果还很好看,只有对比真实表现时才暴露。

6. 二次开发:把这份 Notebook 改成自己的预测原型

6.1 换数据与换特征的改造路径

拿到这份 Notebook 想改成自己的项目,第一步是换数据。把data/下的 CSV 替换成你的数据集,然后回到数据探查单元格,重新确认列名、类型、缺失情况和目标列位置。目标列不在最后一列的话,把df.iloc[:, -1]改成按列名取,比如y = df['your_target'],特征则用X = df.drop(columns=['your_target']),这样比位置索引更稳,列顺序变了也不受影响。

换完数据后,特征工程部分要按新数据的类型调整:数值列走标准化,类别列得先做编码(独热编码或标签编码),文本列则要考虑是否值得引入。我一般会在特征处理前加一个按类型分组的步骤,把数值列和类别列分开处理,避免对类别列做标准化时报错。这一步改完,后面的训练和评估代码基本不用动,这也是这份 Notebook 结构清晰带来的好处——数据层和模型层是解耦的。

6.2 从 Notebook 到可复用脚本的迁移

Notebook 适合探索,不适合长期维护和部署。想把它变成可复用的东西,我通常做三件事:把数据加载和清洗抽成函数、把训练和评估抽成函数、把入口写成if __name__ == '__main__'。下面是一个最小化的迁移骨架:

# predictor.py —— 从 Notebook 抽出的可复用脚本骨架 import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report def load_and_clean(path): """加载数据并做基础清洗,返回特征与标签""" df = pd.read_csv(path) df = df.fillna(df.median(numeric_only=True)) X = df.drop(columns=['target']) # 按实际目标列名调整 y = df['target'] return X, y def train_and_evaluate(X, y): """划分、缩放、训练、评估一条龙""" 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 = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) model = RandomForestClassifier(n_estimators=100, random_state=42) model.fit(X_train, y_train) print(classification_report(y_test, model.predict(X_test))) return model, scaler if __name__ == '__main__': X, y = load_and_clean('data/diabetes.csv') train_and_evaluate(X, y)

这个骨架把 Notebook 里的逻辑拆成了两个职责清晰的函数:load_and_clean只管数据,train_and_evaluate只管建模评估。numeric_only=True让中位数填充只作用于数值列,避免对类别列报错。迁移时最容易漏的是缩放器——训练完要把scaler和model一起保存下来(比如用joblib),否则新数据进来没法用同一套参数变换。这一步做完,你的原型就从「只能自己看的 Notebook」变成了「别人能调用的脚本」。

6.3 验证改造是否成功的几个检查点

改完之后怎么确认没改坏?我一般走三个检查点。第一,用原始数据跑一遍,结果应该和改造前的 Notebook 基本一致,指标允许有小幅波动(随机种子和库版本可能导致),但不该出现数量级差异。第二,换一份同结构的新数据跑,确认路径、列名、类型处理都没有硬编码残留。第三,把模型和缩放器保存后再加载,用同一批测试数据预测,结果应该和保存前完全一致,这一步能抓出序列化相关的隐藏问题。

检查点验证方式通过标准
结果一致性原始数据跑改造后脚本指标与 Notebook 接近
数据可替换性换同结构新数据无路径/列名报错
模型可持久化保存后重新加载预测预测结果完全一致

这三个检查点花不了多少时间,但能挡掉大部分「改完看着能跑、实际已经坏了」的情况。我吃过亏——有次迁移脚本时忘了把缩放器一起保存,线上新数据直接拿原始量纲喂给模型,预测结果全乱,排查了半天才定位到。从那以后我每次做 Notebook 到脚本的迁移,都强制把这三个检查点走一遍,尤其是模型持久化那一步,宁可多写两行保存代码,也不留这个隐患。希望这些拆解能帮到你,把这份 Notebook 真正跑成自己的东西。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

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