简介:这份资源是面向机器学习与Web开发初学者的心脏病预测实战案例,基于逻辑回归二分类算法,结合Python建模与PHP搭建Web界面,帮助读者理解从数据处理到模型部署的完整链路。压缩包共8个文件,约7KB,包含1个Python脚本、1个CSV数据集、4个XML配置、1个iml工程文件及1个Markdown说明,覆盖模型训练、数据读取与项目配置等环节。已有245人学习下载,适合希望将算法模型集成到网页应用中的入门者参考。通过分析源码,读者可掌握逻辑回归的sigmoid概率输出、特征预处理与模型评估思路,并了解PHP如何调用Python脚本完成前后端交互,从而获得一个可复用的分类预测项目模板,为后续处理类似医疗或二分类任务提供实践基础。
1. 心脏病预测这套源码,为什么值得你花一个晚上拆完
上周有个做医疗信息化的朋友问我,手上有一批体检指标数据,想快速搭一个能跑通「输入指标 → 输出患病概率」的演示系统,问我有没有现成的参考。我翻出了这个基于 Python + PHP 实现逻辑回归二分类的心脏病预测案例源码包。它不是一个玩具 demo,而是把数据清洗、模型训练、Web 端调用整条链路都串起来了,预测准确率标称超过 84%。对于想搞明白「机器学习模型怎么塞进 Web 应用」的从业者来说,这套代码的参考价值在于它把 Python 侧的训练脚本和 PHP 侧的交互界面放在同一个工程里,你能直接看到前后端是怎么通过进程调用或 HTTP 请求打通的。适合谁?一是刚学完逻辑回归理论、想找个完整项目练手的新手;二是手里有类似二分类需求、想快速验证技术路线的工程师。它解决的核心问题是:让你不用从零搭架子,直接在一个可运行的工程里理解模型部署的完整流程。
2. 拆包先看目录:code、data、analysis 三块怎么分工
2.1 目录结构与文件职责
解压之后,根目录下最值得关注的是code、data、analysis三个文件夹,外加一个README.md。data目录里放的是heart.csv,这是整个项目的血液——一份包含患者医疗指标和是否患病标签的表格数据。analysis目录下是Analyze.py,负责数据探索和预处理分析。code目录则是核心,Python 训练脚本和 PHP 界面代码都在这里。.idea目录是 IntelliJ 或 PyCharm 的项目配置,DataMiningHomework.iml、modules.xml、vcs.xml、misc.xml、deployment.xml这些是 IDE 自动生成的工程文件,不影响运行,但说明作者是在 JetBrains 系 IDE 里开发的。
| 路径 | 类型 | 作用 |
|---|---|---|
data/heart.csv | 数据文件 | 心脏病预测原始数据集,含特征列与标签列 |
analysis/Analyze.py | Python 脚本 | 数据探索、分布统计、相关性分析 |
code/ | 代码目录 | 模型训练脚本与 PHP Web 界面 |
README.md | 文档 | 项目说明与运行指引 |
.idea/ | 配置目录 | IDE 工程配置,可忽略 |
2.2 先跑 Analyze.py 摸清数据底细
在动模型之前,我习惯先把数据摸一遍。analysis/Analyze.py就是干这个的。你可以在项目根目录下用命令行执行:
python analysis/Analyze.py这个脚本通常会输出几类信息:各特征的统计量(均值、标准差、分位数)、标签列的分布比例、特征之间的相关性矩阵。为什么要先跑它?因为逻辑回归对特征的尺度和缺失值比较敏感,如果某个特征的量纲和其他特征差了几个数量级,梯度下降会收敛得很慢甚至震荡。通过分析脚本,你能提前发现哪些列需要做标准化,哪些列存在缺失需要填充。
常见做法是:对于连续型特征(如年龄、血压、胆固醇),用StandardScaler做零均值单位方差标准化;对于类别型特征(如性别、胸痛类型),做独热编码。Analyze.py的输出能帮你判断哪些列是连续的、哪些是离散的。如果脚本里用了pandas的describe()和corr(),你还能看到哪些特征与标签的相关性较高,这对后续做特征筛选有直接参考价值。
注意:不同版本的
pandas和numpy在输出格式上可能有细微差异,如果脚本报错,先检查依赖版本是否匹配。
3. 逻辑回归训练脚本:从 heart.csv 到模型文件
3.1 数据预处理的关键步骤
逻辑回归虽然叫「回归」,但它解决的是分类问题。核心思想是把特征的线性组合通过 sigmoid 函数映射到 0 到 1 之间,表示属于正类的概率。在code目录下的 Python 训练脚本里,预处理通常包含以下几步:读取 CSV、分离特征和标签、处理缺失值、特征缩放、划分训练集和测试集。下面是一段典型的预处理代码结构:
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 读取数据 df = pd.read_csv('../data/heart.csv') # 检查缺失值,数值列用中位数填充 df.fillna(df.median(numeric_only=True), inplace=True) # 分离特征和标签,假设目标列名为 'target' X = df.drop('target', axis=1) y = df['target'] # 特征标准化:逻辑回归对尺度敏感,这一步不能省 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 按 8:2 划分训练集和测试集,随机种子固定保证可复现 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.2, random_state=42 )这段代码里几个参数值得说明:test_size=0.2表示 20% 的数据用于测试,这是小数据集常用的比例;random_state=42固定随机种子,保证每次运行划分结果一致,方便调试;StandardScaler的fit_transform只在训练集上拟合,测试集要用transform,否则会造成数据泄露。很多新手在这里翻车——把整个数据集拿去拟合 scaler,导致测试集的统计信息泄露到训练过程中,评估结果虚高。
3.2 模型训练与评估指标
预处理完成后,训练逻辑回归模型本身并不复杂:
from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report # 创建逻辑回归模型,使用 L2 正则化 model = LogisticRegression( penalty='l2', # 正则化类型,防止过拟合 C=1.0, # 正则化强度的倒数,越小正则化越强 solver='lbfgs', # 优化算法,适合小数据集 max_iter=1000 # 最大迭代次数,防止收敛警告 ) # 训练 model.fit(X_train, y_train) # 预测 y_pred = model.predict(X_test) # 评估 print("准确率:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))C参数是逻辑回归里最需要调的。C越小,正则化越强,模型越保守,适合特征多但样本少的情况;C越大,模型越倾向于拟合训练数据,容易过拟合。solver选lbfgs是因为它在小数据集上表现稳定,支持 L2 正则化。max_iter设到 1000 是为了避免「模型未收敛」的警告——如果数据没标准化好,默认的 100 次迭代往往不够。
评估部分,accuracy_score给出整体准确率,但光看准确率不够。如果正负样本比例不均衡,一个全预测为「无病」的模型也能有很高的准确率。所以要看classification_report里的查准率(precision)、查全率(recall)和 F1 分数。在医疗场景下,查全率尤其重要——漏诊的代价比误诊大得多。如果查全率偏低,可以通过调整class_weight='balanced'让模型更关注少数类。
3.3 模型持久化与 PHP 调用衔接
训练完模型后,需要把它保存下来供 PHP 侧调用。常见做法是用joblib或pickle序列化:
import joblib # 保存模型和 scaler,预测时需要同样的预处理 joblib.dump(model, 'heart_model.pkl') joblib.dump(scaler, 'heart_scaler.pkl')这里有个容易忽略的点:scaler 必须和模型一起保存。因为 PHP 侧拿到用户输入的原始数据后,需要先用同一个 scaler 做标准化,再送入模型预测。如果只保存模型不保存 scaler,预测时特征尺度不一致,结果会完全错误。保存后的.pkl文件放在code目录下,PHP 脚本通过exec()或shell_exec()调用一个 Python 预测脚本,传入用户数据,拿回预测结果。
4. PHP 侧怎么调 Python 模型:接口设计与进程通信
4.1 PHP 调用 Python 的两种方式
PHP 本身不擅长做机器学习推理,所以这套方案的核心思路是:PHP 负责接收用户输入和展示结果,Python 负责模型推理。两者之间的通信方式常见有两种。第一种是exec()直接执行 Python 脚本,把用户数据作为命令行参数或临时文件传入,Python 脚本输出结果到标准输出,PHP 读取后返回给前端。第二种是 PHP 通过 cURL 向一个独立的 Python HTTP 服务发请求,Python 侧用 Flask 或 FastAPI 暴露接口。这套源码包更可能是第一种,因为它的目录结构里没有看到独立的服务端脚本,更像是教学性质的集成演示。
用exec()的典型 PHP 代码结构如下:
<?php // 接收前端表单提交的数据 $age = $_POST['age']; $sex = $_POST['sex']; $cp = $_POST['cp']; // ... 其他特征 // 拼接成 JSON 传给 Python 脚本 $input_data = json_encode([ 'age' => $age, 'sex' => $sex, 'cp' => $cp, // ... 其他特征 ]); // 调用 Python 预测脚本,注意路径要用绝对路径 $command = 'python3 /path/to/predict.py ' . escapeshellarg($input_data); $output = shell_exec($command); // 解析返回的 JSON 结果 $result = json_decode($output, true); echo "患病概率: " . $result['probability']; ?>escapeshellarg()这个函数不能省。它把输入数据转义,防止用户输入里的特殊字符破坏命令结构。如果不加,用户输入一个带分号或反引号的字符串,就可能执行意外命令。这是 Web 安全的基本功,但在教学项目里经常被忽略。
4.2 预测脚本的输入输出约定
PHP 调用的predict.py需要做三件事:解析传入的 JSON 数据、加载模型和 scaler、输出预测结果。代码大致如下:
import sys import json import joblib import numpy as np # 从命令行参数读取输入 input_json = sys.argv[1] data = json.loads(input_json) # 加载模型和 scaler model = joblib.load('heart_model.pkl') scaler = joblib.load('heart_scaler.pkl') # 按训练时的特征顺序组装输入向量 feature_order = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal'] features = np.array([[data[f] for f in feature_order]]) # 标准化后预测 features_scaled = scaler.transform(features) prob = model.predict_proba(features_scaled)[0][1] # 输出 JSON 结果 print(json.dumps({'probability': round(prob, 4)}))这里的关键是feature_order必须和训练时X的列顺序完全一致。如果训练时X的列顺序是age, sex, cp, ...,预测时也必须按这个顺序组装。顺序错了,模型给出的概率就是随机数。predict_proba返回的是两个概率值,取索引[1]表示正类(患病)的概率。输出用json.dumps保证 PHP 侧能稳定解析。
提示:如果 PHP 和 Python 不在同一台机器上,
exec()方式就不适用了,需要改成 HTTP 接口。但对于本地部署的教学项目,exec()足够用。
5. 避坑与排查:这套源码跑不起来时先查这五处
5.1 现象:Python 脚本报「ModuleNotFoundError」
原因:依赖库没装全。这套项目至少需要pandas、numpy、scikit-learn、joblib。如果Analyze.py里还用了matplotlib或seaborn做可视化,也需要一并安装。
解决:在项目根目录下执行pip install pandas numpy scikit-learn joblib matplotlib seaborn。建议用虚拟环境,避免和系统 Python 的包冲突。如果用的是 Python 3.10 以上版本,注意scikit-learn的版本要选 1.0 以上,否则某些 API 可能不兼容。
5.2 现象:PHP 页面提交后返回空白或「null」
原因:shell_exec()执行 Python 脚本失败,但没有把错误信息返回给 PHP。常见情况是 Python 路径不对、脚本没有执行权限、或者 Python 脚本内部抛了异常但被静默吞掉。
解决:先在命令行手动执行一次 PHP 里拼接的那条命令,看 Python 侧输出什么。如果命令行能跑通但 PHP 跑不通,检查 PHP 运行用户是否有权限访问 Python 脚本和模型文件。另外,在 Python 脚本开头加sys.stderr输出,把异常信息写到标准错误,PHP 侧用2>&1捕获。
5.3 现象:预测结果每次都不一样,或者概率值很离谱
原因:特征顺序错乱或 scaler 没加载。如果predict.py里组装特征的顺序和训练时不一致,模型收到的输入就是错位的。另一种可能是 scaler 文件路径不对,加载了旧的或错误的 scaler。
解决:在训练脚本里把特征列名打印出来,保存成一个features.json,预测脚本读取这个文件来保证顺序一致。scaler 文件用绝对路径加载,避免工作目录变化导致找不到文件。
5.4 现象:模型准确率远低于标称的 84%
原因:数据划分时没有固定随机种子,或者测试集泄露到了训练集。如果每次运行train_test_split不设random_state,划分结果不同,准确率波动会很大。更严重的是,如果先对整个数据集做了标准化再划分,测试集的统计信息就泄露了。
解决:固定random_state,并且严格遵循「先划分、再在训练集上拟合 scaler、然后 transform 测试集」的顺序。另外,检查heart.csv是否有重复行或标签错误,数据质量问题会直接拉低模型表现。
5.5 现象:PHP 页面能显示但样式错乱或表单提交无响应
原因:前端静态资源路径不对,或者表单的action指向了错误的 PHP 文件。这套项目的 PHP 界面可能引用了 CSS 和 JavaScript 文件,如果目录结构变动,路径就会失效。
解决:打开浏览器开发者工具,看 Network 面板里哪些资源返回 404。根据 404 的路径反推正确的相对路径。表单提交无响应通常是action属性为空或指向了不存在的文件,检查 HTML 表单的action和method属性是否和 PHP 处理脚本匹配。
6. 把模型准确率从 84% 再往上推的技巧
这套源码标称 84% 的准确率,在心脏病预测这个经典数据集上属于中等偏上的水平。如果你想在这个基础上继续优化,有几个方向值得试。第一个是特征工程:原始特征里有些是类别型的,比如胸痛类型(cp)有四种取值,独热编码后可能比直接当数值用效果更好。另外,可以尝试构造交叉特征,比如年龄和最大心率的比值,这类组合特征有时能捕捉到单一特征无法表达的规律。
第二个是正则化参数的调优。LogisticRegression的C参数控制正则化强度,默认是 1.0。你可以用网格搜索在[0.01, 0.1, 1, 10, 100]里找最优值。配合交叉验证,比如 5 折交叉验证,能更稳定地评估不同C值下的模型表现。代码大致如下:
from sklearn.model_selection import GridSearchCV param_grid = {'C': [0.01, 0.1, 1, 10, 100]} grid = GridSearchCV( LogisticRegression(solver='lbfgs', max_iter=1000), param_grid, cv=5, scoring='recall' # 医疗场景优先看查全率 ) grid.fit(X_train, y_train) print("最佳 C:", grid.best_params_) print("最佳查全率:", grid.best_score_)注意scoring我选了recall而不是accuracy。在疾病预测里,漏诊的代价比误诊大,所以优先保证查全率。如果查全率和查准率需要平衡,可以用f1作为评分标准。
第三个方向是处理类别不均衡。如果heart.csv里患病和未患病的样本比例差距较大,可以在LogisticRegression里设class_weight='balanced',让模型自动调整权重。这个参数在小数据集上往往能带来几个百分点的提升。
还有一个容易被忽略的点:阈值调整。逻辑回归默认以 0.5 作为分类阈值,但你可以根据业务需求调整。比如把阈值降到 0.4,更多样本会被判为患病,查全率上升,查准率下降。在医疗筛查场景下,这种取舍往往是值得的。调整阈值不需要重新训练模型,只需要在预测时用predict_proba拿到概率,自己判断:
prob = model.predict_proba(X_test)[:, 1] y_pred_custom = (prob >= 0.4).astype(int)从那以后我每次拿到这类分类项目,都会先把阈值和评估指标一起调一遍,而不是直接用默认的 0.5 交差。这套源码给了一个很好的起点,但真正让它在你手里发挥价值的,是这些后续的微调。希望帮到你。
本文还有配套的精品资源,点击获取