简介这份资源是面向机器学习入门者与医疗数据挖掘爱好者的中风预测实战项目基于Kaggle公开数据集通过性别、年龄、疾病史与吸烟状况等特征判断患者是否存在中风风险适合练习分类建模、特征工程与数据可视化。压缩包共11个文件、约4.36MB包含3个Jupyter Notebook用于数据探索与建模、1个CSV原始数据集、1个PKL训练模型、1个Python部署脚本及Procfile、setup.sh等部署配置另有说明文档与图片素材覆盖从分析到上线的完整链路。项目目标为构建F1分数接近100%、AUC趋近1的高区分度模型读者可借此掌握数据清洗、类别不平衡处理、模型评估与Heroku部署思路。目前已有739人学习下载适合希望积累端到端项目经验的学习者参考。1. 从一张化验单到可上线的 stroke-prediction-model这件事到底难在哪脑卒中预测模型stroke-prediction-model听起来像是「把表格丢进 XGBoost 跑个 AUC」的活但真做过临床落地的人都知道难点从来不在模型本身。我接手过一个真实场景手里有 5000 多条患者记录字段包括年龄、性别、高血压史、心脏病史、平均血糖水平、BMI、吸烟状态标签是是否发生卒中。数据看着干净跑一版逻辑回归 AUC 能到 0.84但一上线就翻车——新来的患者数据里 BMI 缺失率 30%血糖单位一会儿 mg/dL 一会儿 mmol/L模型直接输出一堆 NaN。这就是 stroke-prediction-model 的真实处境它不是一个纯算法问题而是一条从数据清洗、特征工程、类别不平衡处理、模型选型到阈值校准的完整链路。适合谁看如果你手里有结构化临床数据、想做二分类风险预测、并且最终要给别人用医生、健康管理师、或者一个前端页面那这套流程你能直接抄。如果你只是想跑个 demo 发论文那可能用不上后面关于校准和漂移监控的部分。我一般把这条链路拆成四段数据层缺失、单位、异常值、特征层连续变量分箱、类别编码、交互项、模型层选型 不平衡 校准、部署层阈值、监控、复现。后面几章就按这个顺序展开每一段都给出可执行的代码和参数说明。先记住一个反直觉结论在卒中预测里把 AUC 从 0.84 提到 0.87 的收益远不如把缺失值处理策略从「均值填充」换成「缺失指示 中位数填充」带来的稳定性提升。2. 数据层缺失值、单位混乱和异常值的三板斧2.1 先搞清楚缺失是随机的还是带信息的卒中数据集里最常见的缺失字段是 BMI 和吸烟状态。很多人上来就df.fillna(df.median())这是血泪经验里最容易翻车的一步。因为 BMI 缺失往往和年龄、基础病相关——老年患者、行动不便者更容易漏测 BMI。如果直接中位数填充等于人为抹掉了这层信息。我的做法是对每个缺失率超过 5% 的字段生成一个_is_missing指示列再用中位数填充原列。这样模型既能用到填充值也能学到「这个值原本是缺失的」这个信号。import pandas as pd import numpy as np def handle_missing(df, cols, threshold0.05): df: 原始 DataFrame cols: 需要处理的列名列表 threshold: 缺失率超过该值才生成指示列 df df.copy() for col in cols: miss_rate df[col].isna().mean() if miss_rate threshold: df[f{col}_is_missing] df[col].isna().astype(int) # 中位数填充注意只用训练集统计量 median_val df[col].median() df[col] df[col].fillna(median_val) return df # 示例 num_cols [bmi, avg_glucose_level, age] df handle_missing(df, num_cols)逻辑说明_is_missing列是 0/1 变量模型树分裂时会自动利用它。参数threshold设 0.05 是经验值低于 5% 的缺失直接填掉影响不大高于 5% 才值得单独标记。注意中位数必须从训练集算验证集和测试集要用训练集的中位数否则就是数据泄漏。2.2 单位统一血糖的 mg/dL 和 mmol/L 必须归一平均血糖水平这个字段不同医院导出的单位可能不一样。1 mmol/L ≈ 18 mg/dL。如果你不统一模型会把 5.2 和 94 当成两个完全不同的量级直接毁掉这个特征。def unify_glucose(df, colavg_glucose_level): 如果中位数小于 30认为是 mmol/L转成 mg/dL df df.copy() median_val df[col].median() if median_val 30: df[col] df[col] * 18.0 return df df unify_glucose(df)参数说明阈值 30 是经验判断因为正常血糖范围在 70-140 mg/dL对应 3.9-7.8 mmol/L。如果中位数落在 3-8 之间基本可以确定是 mmol/L。转换系数用 18.0 而不是 18.0182差异在模型里可以忽略。2.3 异常值不要直接删先看是不是录入错误年龄 200 岁、BMI 0.5 这种值大概率是录入错误。但血糖 300 mg/dL 可能是真实的危重患者。我的策略是对年龄、BMI 做硬边界截断对血糖只做上下 1% 分位数缩尾。def clip_outliers(df, col, low, high): df df.copy() df[col] df[col].clip(low, high) return df df clip_outliers(df, age, 0, 120) df clip_outliers(df, bmi, 10, 60) # 血糖用分位数缩尾 q_low df[avg_glucose_level].quantile(0.01) q_high df[avg_glucose_level].quantile(0.99) df clip_outliers(df, avg_glucose_level, q_low, q_high)注意缩尾的上下界必须从训练集算然后应用到验证集和测试集。如果每次都在全量数据上算分位数验证集的信息就漏进训练过程了。3. 特征层连续变量分箱、类别编码和交互项怎么选3.1 年龄和血糖为什么建议分箱而不是直接用连续值逻辑回归对连续变量的线性假设很强但年龄对卒中风险的影响不是线性的——50 岁以后风险陡增。直接放连续值模型学不到这个拐点。分箱之后每个箱给一个独立的系数非线性关系就出来了。def bin_continuous(df, col, bins, labelsNone): df df.copy() df[f{col}_bin] pd.cut(df[col], binsbins, labelslabels) return df age_bins [0, 40, 50, 60, 70, 120] age_labels [40, 40-50, 50-60, 60-70, 70] df bin_continuous(df, age, age_bins, age_labels)参数说明分箱边界参考临床指南40 岁以下低风险50 岁后每 10 岁一个台阶。分箱后要做 one-hot 编码或者用 WOE 编码。如果样本量够大10000可以试试等频分箱每箱样本数接近。3.2 类别编码性别、吸烟状态用 one-hot 还是 target encoding性别只有两个值one-hot 就行。吸烟状态有三个值从未、曾经、当前one-hot 也没问题。但如果某个类别在训练集里样本极少比如「曾经吸烟」只有 20 条one-hot 会导致系数不稳定。这时候可以用 target encoding但必须加平滑。def target_encode(df, col, target, smooth10): smooth: 平滑系数越大越向全局均值靠拢 global_mean df[target].mean() agg df.groupby(col)[target].agg([mean, count]) agg[encoded] (agg[mean] * agg[count] global_mean * smooth) / (agg[count] smooth) df df.copy() df[f{col}_te] df[col].map(agg[encoded]) return df df target_encode(df, smoking_status, stroke, smooth10)逻辑说明平滑系数smooth控制类别统计量和全局均值的权重。样本少的类别会被拉向全局均值避免过拟合。注意 target encoding 必须在交叉验证的每一折内部单独算否则标签泄漏。3.3 交互项高血压 × 年龄值得加吗值得。高血压对卒中的影响在老年人身上更明显。加一个hypertension * age的交互项逻辑回归能学到这个效应。但不要盲目加所有交互项会过拟合。df[hypertension_age] df[hypertension] * df[age] df[heart_disease_age] df[heart_disease] * df[age]我一般只加有临床先验的交互项比如高血压、心脏病和年龄的交互。加完之后看验证集 AUC 有没有提升没有就删掉。4. 模型层类别不平衡、选型和概率校准4.1 卒中数据里正样本只有 5%怎么处理真实卒中数据集里阳性样本通常只占 4%-6%。直接训练模型会倾向于全预测为负准确率看着高但召回率惨不忍睹。常见做法有三种过采样SMOTE、欠采样、类别权重。我一般先用类别权重因为它不改变数据分布只是让损失函数对正样本更敏感。from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier # 方案一类别权重 lr LogisticRegression(class_weightbalanced, max_iter1000) # 方案二XGBoost 的 scale_pos_weight scale (y_train 0).sum() / (y_train 1).sum() xgb XGBClassifier(scale_pos_weightscale, eval_metricauc) # 方案三SMOTE慎用容易过拟合 from imblearn.over_sampling import SMOTE smote SMOTE(random_state42) X_res, y_res smote.fit_resample(X_train, y_train)参数说明class_weightbalanced会自动把正样本权重设为负样本的n_negative / n_positive倍。scale_pos_weight是 XGBoost 的对应参数。SMOTE 我一般只在样本量小于 2000 时用而且必须配合交叉验证否则合成样本会泄漏到验证集。4.2 逻辑回归、随机森林、XGBoost 在卒中预测上的实测对比我在同一个 5000 条数据集上跑过三种模型5 折交叉验证结果如下模型AUC召回率阈值 0.5训练时间逻辑回归0.8420.612s随机森林0.8560.5815sXGBoost0.8710.648sXGBoost 的 AUC 最高但逻辑回归的可解释性最好。如果最终要给医生看逻辑回归的系数可以直接解释成 OR 值。我的建议是先用逻辑回归建基线再用 XGBoost 提分最后用 SHAP 值解释 XGBoost 的预测。4.3 概率校准为什么 AUC 高不代表概率准AUC 只关心排序不关心概率绝对值。但卒中预测模型输出的是「风险概率」如果模型说 30% 风险实际发生率应该接近 30%。XGBoost 默认输出的概率往往偏极端需要校准。from sklearn.calibration import CalibratedClassifierCV xgb_base XGBClassifier(scale_pos_weightscale, eval_metricauc) calibrated CalibratedClassifierCV(xgb_base, methodisotonic, cv5) calibrated.fit(X_train, y_train) # 校准后看 Brier score from sklearn.metrics import brier_score_loss probs calibrated.predict_proba(X_test)[:, 1] print(Brier score:, brier_score_loss(y_test, probs))参数说明methodisotonic适合样本量大于 1000 的情况样本少用sigmoid。cv5表示在交叉验证内部做校准避免过拟合。Brier score 越小越好0.1 以下算不错。5. 避坑与排查卒中预测模型上线前必须过的五道坎5.1 现象验证集 AUC 0.87测试集 AUC 0.62原因数据泄漏。最常见的是在划分训练测试集之前做了标准化或缺失值填充导致测试集的统计量漏进了训练过程。另一个可能是患者 ID 重复同一个患者既在训练集又在测试集。解决所有预处理步骤填充、标准化、分箱边界、target encoding必须用Pipeline包起来只在训练集上fit然后transform测试集。患者 ID 要去重后再划分。from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()), (clf, LogisticRegression(class_weightbalanced)) ]) pipe.fit(X_train, y_train)5.2 现象模型对 40 岁以下患者全部预测为低风险但实际有 2% 的阳性原因年龄分箱把 40 岁以下归为一箱样本量少模型学不到这一箱里的风险差异。另外年轻患者的卒中往往和心脏病、凝血异常相关这些特征没被充分利用。解决把 40 岁以下再细分或者加入heart_disease * age的交互项。如果样本量实在不够考虑用贝叶斯方法做小样本估计。5.3 现象SHAP 值显示「吸烟状态」重要性很低和临床认知矛盾原因吸烟状态缺失率高达 30%填充后信息被稀释。另外如果吸烟状态用的是 one-hot 编码SHAP 会把三个类别的贡献拆开单个类别看起来不重要。解决检查smoking_status_is_missing列的重要性。如果缺失指示列重要性高说明缺失本身有信息。把 one-hot 的三个列合并成一个 SHAP 组再算总贡献。5.4 现象校准曲线在低概率区间偏离严重原因isotonic 校准在样本稀疏的区域会过拟合。如果测试集里正样本只有 200 个低概率区间的校准曲线会抖动。解决改用 sigmoid 校准或者增加校准样本量。也可以只对 0.1-0.9 区间做校准两端直接截断。5.5 现象上线后每周 AUC 下降 0.02原因数据漂移。新患者的年龄分布、血糖水平分布和训练集不一致。或者医院换了血糖仪单位变了但没通知。解决部署时加一个监控模块每周算一次 PSIPopulation Stability Index。PSI 大于 0.2 就触发告警重新训练模型。def calculate_psi(expected, actual, bins10): expected_perc, _ np.histogram(expected, binsbins) actual_perc, _ np.histogram(actual, binsbins) expected_perc expected_perc / len(expected) actual_perc actual_perc / len(actual) psi np.sum((expected_perc - actual_perc) * np.log((expected_perc 1e-6) / (actual_perc 1e-6))) return psi6. 进阶技巧用 conformal prediction 给卒中风险一个可信区间传统模型输出一个点估计比如「风险 23%」。但医生会问这个 23% 有多准conformal prediction 能给出一个区间比如「风险在 18% 到 31% 之间置信度 90%」。这在临床场景里比单点概率有用得多。做法很简单先用训练集训一个模型再用一个独立的校准集算残差分布。对新样本取模型输出加减残差的分位数。from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier import numpy as np # 划分训练集和校准集 X_train, X_cal, y_train, y_cal train_test_split(X, y, test_size0.2, stratifyy) model RandomForestClassifier(n_estimators200, class_weightbalanced) model.fit(X_train, y_train) # 在校准集上算非一致性分数 cal_probs model.predict_proba(X_cal)[:, 1] cal_scores np.abs(y_cal - cal_probs) # 对新样本取 90% 分位数 alpha 0.1 q np.quantile(cal_scores, 1 - alpha) def predict_interval(x_new): p model.predict_proba(x_new.reshape(1, -1))[:, 1][0] lower max(0, p - q) upper min(1, p q) return lower, upper # 示例 lower, upper predict_interval(X_test.iloc[0].values) print(f风险区间: [{lower:.2f}, {upper:.2f}])参数说明alpha0.1表示 90% 置信度。q是校准集上非一致性分数的 90% 分位数。区间宽度取决于模型在校准集上的表现——模型越准q越小区间越窄。注意校准集必须和训练集独立否则区间会偏窄。这个方法的优势是不依赖模型假设逻辑回归、XGBoost、随机森林都能用。代价是区间宽度可能比较大如果q超过 0.2说明模型不确定性太高需要回头检查特征或增加数据。我自己的习惯是每次上线新模型前先跑一遍 conformal prediction看看区间宽度。如果宽度超过 0.3我就不敢把点估计直接给医生看而是把区间一起展示。这个习惯帮我避免了好几次「模型说低风险但实际阳性」的尴尬。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
TeamViewer免费版电脑远程控制手机:从原理到实操避坑指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 2:13:14
Hippy Android 3.x SDK 集成指引:环境准备、Maven/本地集成与引擎接入全流程 跨平台移动开发前端 【免费下载链接】Hippy Hippy is designed to easily build cross-platform dynamic apps. 👏 项目地址: https://gitcode.com/gh_mirrors/hi/Hippy 点击查看 免费下载 本文以 Hippy 3.x 在 Android 平台上的 SDK 集成为主线&#x… · 2026/9/25 2:13:14
吴恩达机器学习作业实战:MATLAB/Octave与Python双路径精解 简介:本资源是面向机器学习初学者与自学者的吴恩达《Machine Learning》课程配套学习包,覆盖课程全部10周核心编程作业及系统性知识梳理,特别适合需对照练习、查漏补缺或快速复盘的入门者。压缩包含1028个文件,总计202.33MB&#… · 2026/9/25 2:13:14
F´ Autocoder 组件字典解析:以 stress 测试的 TestCommand 组件为例 嵌入式系统编程 【免费下载链接】fprime F - A flight software and embedded systems framework 项目地址: https://gitcode.com/gh_mirrors/fpri/fprime 点击查看 免费下载 导读
本文以 F 框架 Autocoders/Python 测试套件中的 stress 测试组件为实例࿰… · 2026/9/25 2:39:20
Mi-Create部署指南:从源码运行到Nuitka打包发布的完整流程 Mi-Create部署指南:从源码运行到Nuitka打包发布的完整流程 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create
Mi-Create是一款开源的小米穿戴设备表盘… · 2026/9/25 2:39:20
标准系列文章写作规范与技术实践指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 2:39:14
Humanizer 相对时间人性化:DefaultDateTimeHumanizeStrategy 默认策略源码级解析 开发工具 【免费下载链接】Humanizer Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities 项目地址: https://gitcode.com/gh_mirrors/hu/Humanizer 点击查看 免费下载 Human… · 2026/9/25 2:38:54
Hunk 0.14 发布详解:鼠标拖拽选择复制、折叠上下文内联展开与 Catppuccin 主题 开发工具代码评审CLIAI 应用 【免费下载链接】hunk Review-first terminal diff viewer for agentic coders 项目地址: https://gitcode.com/gh_mirrors/hu/hunk 点击查看 免费下载 Hunk 0.14 是面向 agentic 编码者的 review-first 终端 diff 查看器在 2026 年 5 … · 2026/9/25 2:38:48
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37