news 2026/10/7 5:04:41

心脏病预测源码实战:逻辑回归与PHP调用Python模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
心脏病预测源码实战:逻辑回归与PHP调用Python模型

简介:这份资源是面向机器学习与Web开发初学者的实战案例包,围绕逻辑回归二分类算法构建心脏病预测模型,帮助读者理解从数据处理到模型部署的完整链路。包内共8个文件,以xml配置、csv数据集、py脚本和md说明为主,另有iml工程文件,压缩包约7KB,体量轻便,便于快速解压研读。已有245人学习下载,适合希望将Python模型集成到PHP网页应用中的开发者参考。案例中Python侧负责数据清洗、特征缩放、模型训练与准确率等指标评估,PHP侧则承担用户界面与前后端交互,把预测结果返回给使用者,预测正确率超过84%。通过分析数据文件与模型脚本,读者可掌握逻辑回归的sigmoid原理、超参数选择与交叉验证思路,并借鉴前后端调用方式,完成一次从算法到Web落地的完整实践。

1. 从一份 84% 准确率的心脏病预测源码说起

一份标称预测准确率超过 84% 的心脏病预测源码包,技术栈是 Python 做逻辑回归二分类、PHP 做 Web 界面,这个组合在课程设计和入门级落地项目里其实相当典型。很多人第一次拿到这类压缩包,第一反应是「逻辑回归这么简单的东西能有什么好看的」,但真正拆开跑一遍就会发现,坑不在算法本身,而在数据清洗、前后端数据格式对齐、以及 PHP 调用 Python 的那一层胶水代码上。这份资源适合三类人:想找一个完整二分类项目练手的 Python 初学者、需要把模型塞进 Web 页面给非技术人员用的开发者、以及正在做数据挖掘课程作业、需要一个能跑通全流程参考实现的学生。它不追求 SOTA 指标,追求的是「从 CSV 到网页预测结果」这条链路完整可复现,这一点比很多只丢一个 notebook 的项目实在得多。

2. 逻辑回归做心脏病二分类:数据、特征与训练链路

2.1 为什么心脏病预测适合用逻辑回归而不是别的

心脏病预测本质上是一个二分类问题:给定一组医疗指标,判断患者是否患病。逻辑回归的输出经过 sigmoid 函数压缩到 0 到 1 之间,天然就是一个概率值,这对医疗场景很友好——医生不只需要「是/否」,还需要知道「有多大可能是」。相比决策树容易过拟合、SVM 在小数据集上调参麻烦,逻辑回归在几百到几千条样本的表格数据上表现稳定,系数还能直接解释特征影响方向,这在课程答辩或者给非技术同事讲结果时是实打实的优势。

这份源码用的 heart.csv 是典型的表格型医疗数据,特征一般包括年龄、性别、胸痛类型、静息血压、胆固醇、空腹血糖、最大心率、运动诱发心绞痛等。逻辑回归的假设是特征与对数几率呈线性关系,所以连续特征(年龄、血压、胆固醇)和类别特征(性别、胸痛类型)需要区别对待。源码里 Python 部分大概率用 scikit-learn 的 LogisticRegression,配合 StandardScaler 做特征缩放,因为逻辑回归对特征尺度敏感——血压数值在 100 到 200 之间,而性别是 0/1,不缩放的话梯度下降会震荡得厉害。

2.2 数据预处理:缺失值、异常值和特征缩放

拿到 heart.csv 之后第一步不是直接喂模型,而是先看清楚数据长什么样。常见做法是用 pandas 做一轮体检:

import pandas as pd import numpy as np # 读取数据,先看基本信息和缺失情况 df = pd.read_csv('data/heart.csv') print(df.shape) # 行列数 print(df.isnull().sum()) # 每列缺失值数量 print(df['target'].value_counts()) # 标签分布,检查是否严重不平衡 # 连续特征做描述性统计,找异常值 print(df[['age', 'trestbps', 'chol', 'thalach']].describe()) # 简单异常值处理:胆固醇为 0 或血压为 0 的样本通常是录入错误 df = df[df['chol'] > 0] df = df[df['trestbps'] > 0]

这段代码的逻辑是先摸清数据规模和缺失情况,再针对医疗数据里常见的「0 值异常」做过滤。参数上,df.isnull().sum()返回每列缺失数量,如果某列缺失超过 20%,就要考虑填充还是丢弃;describe()给出的 min 和 max 能帮你快速定位不合理值。注意,过滤异常值之后要重新检查样本量,别把数据删得太狠导致训练集不够。

特征缩放用 StandardScaler 或者 MinMaxScaler 都行,前者适合梯度下降,后者适合需要固定范围的场景。我一般会先划分训练集和测试集再缩放,避免数据泄漏:

from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X = df.drop('target', axis=1) y = df['target'] # 先划分,再在训练集上 fit scaler,测试集只 transform 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)

stratify=y保证训练集和测试集的标签比例一致,这在医疗数据里很重要,否则可能测试集里正样本特别少,准确率虚高。random_state=42是为了结果可复现,换个数结果会变,但不会差太多。

2.3 模型训练、评估与 84% 准确率的来源

训练逻辑回归的核心代码很短,但评估不能只看准确率:

from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score # C 是正则化强度的倒数,越小正则化越强 model = LogisticRegression(C=1.0, max_iter=1000, solver='lbfgs') model.fit(X_train_scaled, y_train) y_pred = model.predict(X_test_scaled) y_prob = model.predict_proba(X_test_scaled)[:, 1] print('Accuracy:', accuracy_score(y_test, y_pred)) print('Precision:', precision_score(y_test, y_pred)) print('Recall:', recall_score(y_test, y_pred)) print('F1:', f1_score(y_test, y_pred)) print('AUC:', roc_auc_score(y_test, y_prob))

C=1.0是默认值,调小比如 0.1 会增强正则化,防止过拟合;max_iter=1000是防止默认迭代次数不够导致不收敛警告。84% 的准确率在心脏病数据集上属于正常水平,但要注意:如果正负样本比例是 6:4 左右,一个全预测为正的模型也能有 60% 准确率,所以必须看 recall 和 AUC。医疗场景下 recall 往往比 precision 更重要,漏诊的代价比误诊大。

评估之后可以把模型和 scaler 一起保存,供 PHP 调用:

import joblib joblib.dump(model, 'model/logistic_model.pkl') joblib.dump(scaler, 'model/scaler.pkl')

保存成 pkl 文件是为了后面 PHP 通过 Python 脚本加载时不用重新训练,直接推理。

3. PHP 调用 Python 模型:接口层怎么搭才不翻车

3.1 PHP 和 Python 的三种对接方式及选型

PHP 本身不能直接跑逻辑回归,所以必须通过某种方式调用 Python。常见做法有三种:一是 PHP 用exec()或shell_exec()直接执行 Python 脚本,把用户输入当参数传进去,Python 输出 JSON 结果;二是 PHP 用 cURL 请求一个独立的 Python Flask/FastAPI 服务;三是用消息队列异步处理。这份源码大概率用的是第一种,因为简单、不需要额外起服务,适合课程设计和小型部署。

exec()方式的优点是部署简单,PHP 和 Python 在同一台机器上就行;缺点是每次请求都要启动 Python 解释器,并发高了会慢,而且输入参数需要严格转义,否则有命令注入风险。cURL 方式适合模型服务独立部署的场景,但要多维护一个进程。选哪种取决于你的使用场景:如果只是本地演示或者低并发内部工具,exec()够用;如果要给多人同时用,建议走 HTTP 接口。

3.2 用 exec() 打通 PHP 到 Python 的推理链路

先写一个 Python 推理脚本,接收命令行参数,输出 JSON:

# predict.py import sys import json import joblib import numpy as np # 加载训练好的模型和 scaler model = joblib.load('model/logistic_model.pkl') scaler = joblib.load('model/scaler.pkl') # 从命令行参数读取特征值,顺序要和训练时一致 features = [float(x) for x in sys.argv[1:]] features = np.array(features).reshape(1, -1) features_scaled = scaler.transform(features) prob = model.predict_proba(features_scaled)[0][1] result = { 'probability': round(prob, 4), 'prediction': int(prob >= 0.5) } print(json.dumps(result))

PHP 端这样调用:

<?php // 接收表单提交的特征值,做基本校验 $features = [ floatval($_POST['age']), floatval($_POST['sex']), floatval($_POST['cp']), // ... 其他特征 ]; // 转义参数,防止命令注入 $args = implode(' ', array_map('escapeshellarg', $features)); $cmd = 'python3 predict.py ' . $args; $output = shell_exec($cmd); $result = json_decode($output, true); if ($result === null) { echo json_encode(['error' => '模型调用失败']); } else { echo json_encode($result); } ?>

escapeshellarg是必须的,它会把每个参数用单引号包起来,防止用户输入; rm -rf这种恶意内容。shell_exec返回的是字符串,用json_decode转成数组再返回给前端。注意 Python 脚本里的特征顺序必须和训练时X的列顺序完全一致,否则 scaler 和模型都会算错。

3.3 前端表单与结果展示的最小实现

前端不需要太复杂,一个 HTML 表单加一段 JavaScript 就行:

<form id="predictForm"> <input type="number" name="age" placeholder="年龄" required> <select name="sex"> <option value="1">男</option> <option value="0">女</option> </select> <!-- 其他字段省略 --> <button type="submit">预测</button> </form> <div id="result"></div> <script> document.getElementById('predictForm').addEventListener('submit', async function(e) { e.preventDefault(); const formData = new FormData(this); const resp = await fetch('predict.php', { method: 'POST', body: formData }); const data = await resp.json(); document.getElementById('result').innerText = '患病概率:' + (data.probability * 100).toFixed(2) + '%'; }); </script>

这段代码用 fetch 提交表单,拿到 JSON 后把概率显示出来。注意 PHP 返回的 probability 是 0 到 1 之间的小数,前端乘以 100 显示成百分比更直观。

4. 避坑与排查:这份源码跑不起来时先看这几条

4.1 现象:Python 脚本单独跑正常,PHP 调用返回空

原因通常是 PHP 执行环境的工作目录和 Python 脚本里的相对路径不一致。joblib.load('model/logistic_model.pkl')是相对路径,PHP 的shell_exec默认工作目录可能是/var/www/html而不是脚本所在目录。

解决:在 Python 脚本里用绝对路径,或者在 PHP 里先chdir()到脚本目录:

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) model = joblib.load(os.path.join(BASE_DIR, 'model', 'logistic_model.pkl'))

4.2 现象:预测结果每次都不一样,或者概率明显不合理

原因一般是特征顺序错位或者 scaler 没有正确加载。训练时X的列顺序是固定的,如果 PHP 传参顺序和训练时不一致,scaler 会把年龄当成性别来缩放,结果自然离谱。

解决:在 Python 脚本里显式定义特征顺序,并在 PHP 端按同样顺序组装参数。可以在 README 里写清楚每个位置对应哪个特征,或者改成用 JSON 传参、Python 端按 key 取值,减少顺序依赖。

4.3 现象:准确率只有 60% 多,达不到标称的 84%

原因可能是数据没有做异常值过滤,或者训练集测试集划分时没有 stratify,导致测试集分布偏移。另外,如果 heart.csv 本身有重复行,也会影响评估。

解决:先跑一遍df.duplicated().sum()看有没有重复,有就去重;再检查value_counts()确认标签比例;最后确认 scaler 是在训练集上 fit 的,不是在全量数据上 fit 的。

4.4 现象:PHP 页面提交后报 500 错误,日志显示 python3 找不到

原因:Web 服务器运行用户(如 www-data)的 PATH 环境变量里没有 python3,或者 Python 依赖没有装在这个用户下。

解决:在 PHP 里用 Python 的绝对路径,比如/usr/bin/python3;同时确认joblib、scikit-learn、numpy这些库在对应 Python 环境下都装了。可以用sudo -u www-data /usr/bin/python3 -c "import sklearn"测试。

4.5 现象:并发请求时预测变慢甚至超时

原因:每次请求都启动一个 Python 进程,加载模型和 scaler 需要时间,并发高了 CPU 扛不住。

解决:如果只是演示,可以接受;如果要优化,把模型加载改成常驻内存的 HTTP 服务,PHP 用 cURL 调用,这样模型只加载一次。或者用 PHP 的缓存机制,把相同输入的预测结果缓存起来。

5. 进阶技巧:把模型推理从 exec 改成常驻服务

exec()方式在课程设计里够用,但如果你想让这个心脏病预测页面稍微像样一点,建议把 Python 推理改成 Flask 常驻服务。这样 PHP 只负责转发请求,模型加载一次就一直在内存里,响应速度会快很多。

先写一个最小的 Flask 接口:

# app.py from flask import Flask, request, jsonify import joblib import numpy as np app = Flask(__name__) model = joblib.load('model/logistic_model.pkl') scaler = joblib.load('model/scaler.pkl') @app.route('/predict', methods=['POST']) def predict(): data = request.get_json() features = np.array(data['features']).reshape(1, -1) features_scaled = scaler.transform(features) prob = model.predict_proba(features_scaled)[0][1] return jsonify({ 'probability': round(prob, 4), 'prediction': int(prob >= 0.5) }) if __name__ == '__main__': app.run(host='127.0.0.1', port=5000)

PHP 端改成 cURL 调用:

<?php $payload = json_encode(['features' => $features]); $ch = curl_init('http://127.0.0.1:5000/predict'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); echo $response; ?>

这样改完之后,模型只在 Flask 启动时加载一次,后续请求都是内存推理,延迟从几百毫秒降到几毫秒。注意 Flask 默认是单线程的,如果要处理并发,可以用 gunicorn 起多个 worker,但模型内存会翻倍,小机器上要权衡。

验证服务是否正常,可以用 curl 直接测:

curl -X POST http://127.0.0.1:5000/predict \ -H "Content-Type: application/json" \ -d '{"features": [55, 1, 2, 130, 250, 0, 1, 150, 0, 1.5, 2, 0, 2]}'

返回的 JSON 里 probability 应该在 0 到 1 之间,prediction 是 0 或 1。如果返回 500,先看 Flask 控制台的报错,大概率是特征数量对不上——训练时用了 13 个特征,你只传了 12 个,reshape 就会失败。

还有一个容易忽略的点:Flask 服务默认只监听 127.0.0.1,如果 PHP 和 Python 不在同一台机器上,需要改成0.0.0.0并配置防火墙规则。但生产环境不建议直接暴露 Flask 开发服务器,应该用 gunicorn 或 uWSGI 加 Nginx 反向代理。

从那以后我每次拿到这种前后端加模型的源码包,都会先把 Python 部分单独跑通、确认模型文件能加载、再测 PHP 调用链路,最后才看前端页面。顺序反了的话,出了问题根本分不清是模型错了还是接口错了。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 5:04:21

从AI硬写到可视化编排:构建可维护的Agent工程化方案

上个月有朋友找我吐槽&#xff0c;说他们团队花了两周时间让大模型“硬写”一个带RAG和多工具调用的Agent&#xff0c;Demo演示时全场鼓掌&#xff0c;一接真实业务数据就频频翻车。不是答非所问&#xff0c;就是把不该调用的接口调了&#xff0c;再要么就是同一个问题换个问法…

作者头像 李华
网站建设 2026/10/7 5:04:16

DeepSeek Harness桌面端安装配置与插件管理实战指南

1. 桌面端来了&#xff0c;为什么这件事比想象中重要DeepSeek Harness 出官方桌面端这件事&#xff0c;我第一反应不是“终于有 GUI 了”&#xff0c;而是“终于不用再跟终端里的环境变量和路径斗智斗勇了”。如果你最近一直在用命令行版本的 dsh&#xff0c;或者通过第三方壳子…

作者头像 李华
网站建设 2026/10/7 5:03:38

JVM StringTable、直接内存与垃圾回收实战:从底层原理到调优

之前聊过JVM的内存区域划分&#xff0c;很多人以为把堆和栈搞清楚就万事大吉&#xff0c;结果在面试里被问到“String对象到底存在哪”或者“为什么用了Netty比传统BIO要快”直接卡壳。这两个问题背后&#xff0c;恰好就是StringTable和直接内存这两个容易被忽略的知识点。再加…

作者头像 李华
网站建设 2026/10/7 5:02:09

Bootstrap4表单控件完全指南:布局、校验与自定义实践

1. 别再手写样式了&#xff1a;Bootstrap4表单控件到底帮你省了多少事做前端这些年&#xff0c;我见过太多团队还在用一套“祖传”的CSS片段拼表单——输入框换个边框颜色要改三处地方&#xff0c;复选框对齐全靠margin-top: 2px慢慢蹭&#xff0c;一旦设计稿改个圆角&#xff…

作者头像 李华
网站建设 2026/10/7 5:02:06

PCB测试点设计全攻略:从Altium规则到ICT治具落地

我最早对“测试点”这三个字留下深刻印象&#xff0c;是在一款消费电子主板的ICT治具回签阶段。结构工程师拿着针床图来找我&#xff0c;开口就问&#xff1a;这两个测试点中心距只有1.9mm&#xff0c;我的探针导套外径最小2.0mm&#xff0c;你打算往哪儿扎&#xff1f;当时我第…

作者头像 李华