简介这份资源是面向机器学习初学者与数据竞赛练习者的中风预测建模项目基于Kaggle公开的中风数据集通过性别、年龄、疾病史与吸烟状况等特征判断患者是否存在中风风险适合用来练习分类建模、数据可视化与模型部署的完整流程。压缩包共11个文件约4.36MB包含3个Jupyter Notebook用于数据探索与建模分析另有csv原始数据、pkl训练模型、py部署脚本、Procfile与sh启动脚本等覆盖从数据处理到Heroku部署的环节并附有说明文档与示意图。已有743人学习下载。项目以追求高F1分数与AUC为目标读者可参考其中的特征筛选、类别不平衡处理与模型评估思路并借助部署脚本了解如何将预测模型封装为可调用服务适合作为课程设计或简历项目的实践参考。1. 从一份 Notebook 说起stroke-prediction-model 到底能跑出什么打开这份stroke-prediction-model第一眼看到的就是一个.ipynb文件而不是一堆.py脚本。很多人拿到这种资源会犯嘀咕一个 Jupyter Notebook 能算“项目”吗我的判断是能而且它恰好是数据类项目最真实的形态——从读数据、清洗、特征工程、建模、评估到结论全链路都摊在一个可交互的文档里每一步的中间结果都看得见。这个项目做的事很具体基于一组包含年龄、性别、高血压、心脏病、婚姻状态、工作类型、居住类型、平均血糖水平、BMI、吸烟状态等字段的表格数据预测个体是否发生过脑卒中stroke本质是一个二分类任务标签字段通常是stroke取值 0/1。它适合两类人一类是想照着跑通一个完整机器学习流程的新手另一类是手里有类似结构化医疗/健康数据、想找个可改的基线做二次开发的熟手。Notebook 这种载体在 JupyterNotebook 生态里天然适合做探索和交付但真要落地得先把它拆明白。2. 项目结构与技术栈一个 Notebook 里藏了几层2.1 目录与文件构成这类资源通常不会给你一个复杂的工程骨架常见做法是根目录下放一个主 Notebook可能再配一个数据文件或数据下载说明。我一般会先ls一遍确认三件事Notebook 文件名、数据文件在哪、有没有requirements.txt或环境说明。典型的目录长这样stroke-prediction-model/ ├── stroke-prediction-model.ipynb # 主流程EDA → 预处理 → 建模 → 评估 ├── data/ │ └── healthcare-dataset-stroke-data.csv └── README.md # 数据来源与运行说明部分版本有如果数据文件没随包提供Notebook 里一般会有一段读取路径的代码改成本地路径即可。这里要提醒一句不要假设字段名和列顺序固定先跑一遍df.columns和df.head()再往下走这是血泪经验。2.2 技术栈与依赖从 Notebook 的 import 段基本能反推出技术栈pandas、numpy做数据处理matplotlib、seaborn做可视化scikit-learn做建模与评估可能还会用到imbalanced-learn处理类别不平衡。这套组合是数据科学项目的标准配置不需要额外造轮子。依赖安装我一般直接写一个最小集合# 建议在虚拟环境里装避免污染全局 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas numpy matplotlib seaborn scikit-learn imbalanced-learn jupyter参数说明imbalanced-learn不是必装但如果 Notebook 里出现SMOTE或RandomOverSampler就必须装jupyter用来启动 Notebook 服务。装完用jupyter notebook或jupyter lab打开主文件按单元格顺序执行。注意Notebook 的执行顺序依赖很强跳着跑很容易出现变量未定义的报错正确姿势是Kernel → Restart Run All。2.3 数据字段与任务定义这个数据集的核心字段可以整理成一张表方便改代码时对照字段含义类型预处理要点gender性别类别独热或二值编码age年龄数值标准化可选hypertension高血压二值保持 0/1heart_disease心脏病二值保持 0/1ever_married是否已婚类别二值编码work_type工作类型类别独热编码Residence_type居住类型类别二值编码avg_glucose_level平均血糖数值标准化/分箱bmi体质指数数值缺失值填充smoking_status吸烟状态类别独热注意 Unknownstroke是否卒中标签0/1严重不平衡任务定义很清晰以stroke为目标变量做二分类。真正的难点不在模型选择而在两件事——bmi的缺失值处理和正负样本极度不平衡。这两点决定了后面评估指标不能只看准确率。3. 核心流程拆解从 EDA 到模型评估的每一步3.1 数据加载与探索性分析Notebook 的前几个单元格通常是读数据和看分布。这一段看似简单但决定了后面所有决策。我一般会先确认数据规模、缺失情况和标签比例import pandas as pd import numpy as np # 读取数据路径按实际位置改 df pd.read_csv(data/healthcare-dataset-stroke-data.csv) print(df.shape) # 行数、列数 print(df.isnull().sum()) # 每列缺失值数量 print(df[stroke].value_counts(normalizeTrue)) # 标签比例 print(df.describe()) # 数值列统计逻辑说明shape让你知道样本量够不够isnull().sum()定位缺失列这个数据集里bmi通常有缺失value_counts(normalizeTrue)直接暴露不平衡程度正样本往往只占几个百分点describe()看数值范围判断要不要标准化。参数上没什么可调的关键是养成“先看再动”的习惯。如果标签比例极端后面评估就必须上召回率、F1、AUC而不是 accuracy。3.2 缺失值处理与特征编码bmi的缺失是绕不开的。常见做法有两种用中位数填充或者按分组中位数填充。我一般先用整体中位数简单可控from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 数值列与类别列分开处理 num_cols [age, avg_glucose_level, bmi] cat_cols [gender, ever_married, work_type, Residence_type, smoking_status] bin_cols [hypertension, heart_disease] num_pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), # bmi 缺失用中位数 (scaler, StandardScaler()) # 数值标准化 ]) cat_pipe Pipeline([ (encoder, OneHotEncoder(handle_unknownignore)) # 未知类别不报错 ]) preprocessor ColumnTransformer([ (num, num_pipe, num_cols), (cat, cat_pipe, cat_cols), (bin, passthrough, bin_cols) ])逻辑说明SimpleImputer(strategymedian)对偏态数值比均值稳StandardScaler对逻辑回归、SVM 这类模型有必要对树模型影响不大但无害OneHotEncoder(handle_unknownignore)是防止线上出现训练集没见过的类别时直接崩掉这个参数在二次开发时很关键。ColumnTransformer把不同列的处理串起来好处是后面可以直接塞进Pipeline避免训练和预测阶段预处理不一致——这是很多人翻车的地方。3.3 类别不平衡处理与模型训练正样本少直接训练模型会倾向于全预测为负。Notebook 里常见的处理是 SMOTE 过采样或者用class_weightbalanced。我倾向于先试class_weight因为它不改变数据分布更稳from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.pipeline import Pipeline X df.drop(columns[stroke, id], errorsignore) y df[stroke] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy # stratify 保证训练测试标签比例一致 ) # 逻辑回归基线带类别权重 lr_model Pipeline([ (prep, preprocessor), (clf, LogisticRegression(class_weightbalanced, max_iter1000)) ]) # 随机森林对照 rf_model Pipeline([ (prep, preprocessor), (clf, RandomForestClassifier(class_weightbalanced, n_estimators200, random_state42)) ]) lr_model.fit(X_train, y_train) rf_model.fit(X_train, y_train)参数说明stratifyy在不平衡数据里几乎是必加否则切出来的测试集可能正样本极少class_weightbalanced让损失函数按类别频率加权max_iter1000防止逻辑回归不收敛报警n_estimators200是随机森林的树数量越大越稳但越慢。这里用Pipeline包住预处理和模型预测时直接lr_model.predict(X_test)就行不会出现“训练时标准化了、预测时忘了”的低级错误。3.4 评估指标与结果解读不平衡二分类只看 accuracy 是自欺欺人。Notebook 里应该输出混淆矩阵、召回率、F1 和 AUCfrom sklearn.metrics import classification_report, roc_auc_score, confusion_matrix for name, model in [(LR, lr_model), (RF, rf_model)]: y_pred model.predict(X_test) y_prob model.predict_proba(X_test)[:, 1] print(name) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred, digits3)) print(AUC:, round(roc_auc_score(y_test, y_prob), 4))逻辑说明predict_proba取正类概率算 AUC比硬分类阈值更全面地反映排序能力classification_report里重点看正类的 recall 和 f1因为漏诊的代价通常更高。如果召回很低可以调阈值或者换 SMOTE 再试。这一段是整份 Notebook 的“结论区”二次开发时改模型、改特征最终都要回到这几个指标上对比。4. 避坑与常见问题跑不起来时先看这几条4.1 现象FileNotFoundError找不到 CSV原因Notebook 里的数据路径是作者本机路径或者数据文件根本没随包提供。解决先确认data/目录是否存在把pd.read_csv的路径改成相对路径如果确实没有数据文件按 README 说明获取或者用同结构的小样本先跑通流程。我一般会在读数据前加一句import os; print(os.getcwd())确认当前工作目录。4.2 现象KeyError某列不存在原因字段名大小写或拼写不一致比如Residence_type和residence_type。解决先跑df.columns.tolist()打印所有列名再对照代码里的列名逐个改。不要凭记忆写列名这是最常见的低级错误。4.3 现象模型全预测为负类recall 为 0原因正样本比例太低模型没学到正类特征。解决加class_weightbalanced或者用 SMOTE 过采样同时检查是否误把id列当特征喂进去了id是无关列必须 drop。评估时看正类 recall不要被高 accuracy 迷惑。4.4 现象OneHotEncoder报未知类别错误原因训练集和测试集类别不一致或者预测时出现新类别。解决初始化时加handle_unknownignore如果用的是旧版 scikit-learn参数名可能是handle_unknownignore但行为略有差异升级到较新版本更稳。4.5 现象Notebook 跳着执行结果对不上原因单元格之间有隐藏的变量依赖跳着跑导致用了旧变量。解决每次改完代码Kernel → Restart Run All从头跑一遍。这是 Notebook 项目的通病养成全量重跑的习惯能省很多排查时间。5. 二次开发把 Notebook 变成能复用的预测脚本5.1 从 Notebook 抽成模块Notebook 适合探索不适合部署。二次开发第一步是把预处理和模型训练抽成.py模块比如train.py和predict.py。核心是把第 3 章里的preprocessor和Pipeline原样搬过去用joblib保存整个 Pipelineimport joblib # 训练完成后保存整个 pipeline预处理和模型一起落盘 joblib.dump(rf_model, stroke_rf_pipeline.joblib) # 预测时直接加载输入原始字段即可 loaded joblib.load(stroke_rf_pipeline.joblib) proba loaded.predict_proba(new_df)[:, 1]这样做的价值在于预处理逻辑跟着模型走不会出现训练和推理不一致。参数上注意new_df的列必须和训练时一致多余列要提前 drop。5.2 改造方向与验证方法可改的点不少特征工程上可以给age分箱、给avg_glucose_level做对数变换模型上可以试 XGBoost 或 LightGBM但记得同样处理不平衡阈值上可以按业务需求调predict_proba的判定阈值而不是固定 0.5。验证方法我一般用分层 K 折交叉验证看指标均值而不是单次切分from sklearn.model_selection import StratifiedKFold, cross_val_score cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(rf_model, X, y, cvcv, scoringroc_auc) print(scores.mean(), scores.std())scoringroc_auc在不平衡场景比 accuracy 靠谱std大说明模型不稳定需要检查数据或调参。改造后一定要和基线对比同一套指标别只看单次结果就下结论。5.3 一个容易忽略的细节smoking_status里的Unknown不要当普通类别随便编码它本身可能携带信息比如不愿透露可以单独保留一类也可以合并。我踩过的坑是直接 drop 掉 Unknown 行结果样本量掉了一截指标反而虚高。从那以后我每次处理类别缺失都先统计占比再决定不拍脑袋。希望这份拆解能帮你把这个 Notebook 顺利跑起来并改成自己手里能用的预测流程。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?