基于 PPG 信号与机器学习算法的心肌梗死预测研究
0 引言
心血管疾病是全球范围内导致死亡和残疾的主要原因,其中心肌梗死以其高发病率、高致残率和高死亡率,构成了最严峻的公共卫生挑战之一。心肌梗死的救治效果具有极强的时间依赖性,“时间就是心肌,时间就是生命”。然而,传统的心肌梗死诊断依赖于心电图和血清生物标志物检测,这些方法通常需要在医疗机构内由专业人员进行操作,存在诊断延迟、设备昂贵、可及性差等问题,难以实现对高危人群的早期、快速、连续筛查与预警。
近年来,光电容积脉搏波(PPG )信号技术作为一种无创、低成本、易于获取的生理信号检测手段,展现出巨大的应用潜力。PPG 信号是通过光学手段检测血液容积变化,从而间接反映心率、血氧饱和度等生理信息的无创监测信号。它因无创、低成本的特点,已广泛应用于智能穿戴设备与临床诊断场景。PPG信号反映了外周血管在心脏周期性搏动下的血容量变化,根据研究表明,PPG波形的时域特征中,蕴含着丰富的心血管信息。这为将其用于心肌梗死的早期识别与预测提供了重要的生理学基础。
与此同时,机器学习(ML)技术的飞速发展为处理复杂、非线性的医学生理信号开辟了新途径,已广泛应用于医疗诊断当中。因此,本研究旨在探索基于PPG信号与机器学习算法的心肌梗死预测方法。研究将首先基于包含心肌梗死患者和健康对照者的PPG信号数据集;随后,提取相关时域特征;最后,构建并比较多种机器学习模型(如支持向量机、随机森林、logistic回归等)的性能,以此开发出一种能够实现心肌梗死早期、无创、便捷预测的新方法,为心血管疾病的早期检查和识别提供一种新的技术解决方案。
目录
本文完整代码:基于PPG信号与机器学习算法的心肌梗死预测研究的完整代码资源-CSDN下载
此外相关数据可在下载:基于一维卷积神经网络的PPG信号心肌梗死预测研究的完整代码资源
针对本文改进:1.基于统计信息融合的PPG时域特征在心肌梗死预测的研究-CSDN博客
2.基于主成分分析的PPG信号特征用于心肌梗死预测研究-CSDN博客
3.基于一维卷积神经网络的PPG信号心肌梗死预测研究-CSDN博客
1 数据
1.1 数据来源
本文采用的数据来自于kaggle平台上,具体链接如下:Photoplethysmography (PPG) Dataset数据简介:该数据集包含多个特征,代表不同时间点的 PPG 信号幅度。目标变量:目标列命名为“标签”,代表每个数据样本的标签。正常('Normal):表示没有心肌梗死迹象的生理状况。心肌梗死 (MI):表示信号表现出与心脏病发作或心肌梗死相关的迹象。
1.2 数据查看
首先查看数据的基本信息:这是一个含 2576 行、2001 列(含 2000 列 float64 类型和 1 列 object 类型 “Label” 列)、占用约 39.3MB 内存的 pandas DataFrame。
import pandas as pd
# 加载数据集
df = pd.read_csv('PPG_Dataset.csv')
print('数据基本信息:')
df.info()
# 查看数据集行数和列数
print( df.shape)

其次,绘制 PPG 信号前若干行数据及标签(Label)分布图。前者展示前 20 行 PPG 信号的变化趋势,可观察到部分信号存在较明显噪声;后者统计结果显示,正常标签与心肌梗死标签数量分别为 1282 和 1294,表明两类标签分布较为均衡。
import matplotlib.pyplot as plt
n =20 # 要显示的前N条
df=df.drop(columns='Label')
plt.figure(figsize=(12, 8))
for i in range(min(n, len(df))):
data = df.iloc[i].dropna()
plt.plot(data.values, label=f'n{i+1}')
plt.xlabel('time')
plt.ylabel('AMP')
plt.title(f'{n}')
#plt.legend()
plt.grid(alpha=0.3)
plt.show()

last_col = df.columns[-1]
plt.figure(figsize=(10, 6))
bars = df[last_col].value_counts().plot(kind='bar', color='skyblue')
for bar in bars.patches:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{height}',
ha='center',
va='bottom',
fontsize=10)
plt.xlabel('label', fontsize=12)
plt.ylabel('count', fontsize=12)
plt.show()

1.3 数据预处理
1.3.1 滤波
从上节可以看到部分PPG数据的噪声较高,所以对信号的进行滤波处理,这里采用高斯加权移动滤波方法。该算法的主要思想是通过对信号进行加权平均来消除噪声,其中权值是根据高斯分布计算得到的,越接近中心点的权值越大,越远离中心点的权值越小。这样可以使得噪声的影响减小,同时保留信号的主要特征[1]。 高斯函数理论如下[2]:
其中,表示与中心点的距离,
(标准差)决定曲线的宽度,其越大则平滑度越高,反之平滑度越低。由高斯函数的得到权重系数为
,
表示窗口大小,设置输入信号为
,则滤波的输出信号为
式中,是时间点
上的滤波结果。
是信号
在时间
位置周围的值。本文中窗口大小设置为51,标准差设置为5,与原信号对比,达到一定的滤波效果。
data_to_filter = df.drop(columns='Label')
original_data = data_to_filter.iloc[3, :].values
# 高斯加权移动滤波函数
def gaussian_filter(data, window_size, sigma):
"""
高斯加权移动滤波
:param data: 输入数据(一维数组)
:param window_size: 窗口大小(奇数)
:param sigma: 高斯核的标准差
:return: 滤波后的数据
"""
# 生成高斯核
kernel = np.arange(window_size) - window_size // 2
kernel = np.exp(-0.5 * (kernel / sigma) ** 2) # 高斯函数
kernel /= np.sum(kernel) # 归一化,确保权重和为1
filtered_data = np.convolve(data, kernel, mode='same')
return filtered_data
window_size = 51
sigma = 5
filtered_data = gaussian_filter(original_data, window_size, sigma)
# 绘制原始数据和滤波后的数据对比
plt.figure(figsize=(12, 6))
plt.plot(original_data, label='Original PPG', alpha=0.5)
plt.plot(filtered_data, label='Gaussian Filtered PPG', color='red')
# 设置图表标题和坐标轴标签
plt.title('PPG Signal')
plt.xlabel('TIME')
plt.ylabel('AMP')
plt.legend()
plt.grid(alpha=0.3)
plt.show()

进一步,我们对所有数据进行滤波。新的数据集命名为:filtered_data。
data_to_filter = df.drop(columns='Label')
def gaussian_filter(data, window_size, sigma):
kernel = np.arange(window_size) - window_size // 2
kernel = np.exp(-0.5 * (kernel / sigma) ** 2)
kernel /= np.sum(kernel)
filtered_data = np.convolve(data, kernel, mode='same')
return filtered_data
window_size = 51
sigma = 5
filtered_rows = []
for index, row in data_to_filter.iterrows():
filtered_row = gaussian_filter(row.values, window_size, sigma)
filtered_rows.append(filtered_row)
filtered_df = pd.DataFrame(filtered_rows, columns=data_to_filter.columns, index=data_to_filter.index)
filtered_df.to_csv('filtered_data.csv', index=False)
1.3.2 提取单周期信号
为了简化建模的复杂度,本文针对一个心动周期内的数据进行处理。故需要对数据filtered_data进行提取单周期的信号。PPG原始信号具有周期性,则周期内会呈现 "上升 - 峰值 - 下降 - 谷值" 的重复表现,相邻两个谷值(或峰值)之间的部分可视为一个完整周期。如下图可以看出谷值(或峰值)定位准确,并展示一部分第二周期的选择部分。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.signal import find_peaks
original_data =df.iloc[9, :].values
def gaussian_filter(data, window_size, sigma):
kernel = np.arange(window_size) - window_size // 2
kernel = np.exp(-0.5 * (kernel / sigma) **2)
kernel /= np.sum(kernel)
filtered_data = np.convolve(data, kernel, mode='same')
return filtered_data
window_size = 50
sigma = 3
filtered_data = gaussian_filter(original_data, window_size, sigma)
# 检测峰值
peak_indices, _ = find_peaks(
filtered_data,
height=np.mean(filtered_data) + 0.1*np.std(filtered_data),
distance=150
)
valley_indices, _ = find_peaks(
-filtered_data,
height=-(np.mean(filtered_data) - 0.1*np.std(filtered_data)),
distance=150
)
start_idx = valley_indices[1]
end_idx = valley_indices[2]
single_cycle = filtered_data[start_idx:end_idx+1]
cycle_time = np.arange(start_idx, end_idx+1)
# 可视化结果
plt.figure(figsize=(15, 10))
plt.plot(original_data, label='Original Data', alpha=0.5)
plt.plot(filtered_data, label='Filtered Data', color='red')
plt.scatter(peak_indices, filtered_data[peak_indices], c='green', label='Peaks')
plt.scatter(valley_indices, filtered_data[valley_indices], c='blue', label='Valleys')
plt.axvline(x=start_idx, color='purple', linestyle='--', label='Cycle Start')
plt.axvline(x=end_idx, color='orange', linestyle='--', label='Cycle End')
plt.title('PPG Signal with Features')
plt.xlabel('Time Index')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

因此,对数据集filtered_data进行提取第二部分单周期的信号,并且统一单周期信号的长度(250),最后展示所有行提取的结果,发现均符合单周期的规律,则新的数据集命名为:cycles。可见:【免费】一个心动周期内的PPG信号资源-CSDN下载

2 特征提取
对于预处理好的数据,进行提取时域特征,我们参考文献[3]对PPG信号提取时域特征。如下图中,收缩宽度(Systolic Width, SW):表示收缩期内,PPG从 “特定百分比幅值” 到峰值的时间。如下:
- SW10:收缩期内,PPG幅值达到 “峰值的 10%” 时的宽度;
- SW25、SW33、SW50、SW66、SW75 以此类推(对应峰值的 25%、33%、50%、66%、75%)。
舒张宽度(Diastolic Width, DW):表示舒张期内,PPG从峰值到 “特定百分比幅值” 的时间。如下:
- DW10:舒张期内,PPG幅值从峰值下降到 “峰值的 10%” 时的宽度;
- DW25、DW33、DW50、DW66、DW75 以此类推(对应峰值的 25%、33%、50%、66%、75%)。
SW和DW衍生参数:基于 SW 和 DW,还定义了组合或比值参数:
- 加法组合:如 DW
10+SW10(舒张 10% 宽度 + 收缩 10% 宽度)等; - 比值组合:如DW
10/SW 10(舒张 10% 宽度 / 收缩 10% 宽度)等;

基于 SW 、DW以及衍生参数共4×6=24种,加上峰值、收缩期和舒张期的时间,最终得到共27种时域特征。所提取的特征集命名为:feature。
3 机器学习与评价指标
根据上节所得的特征集feature,首先,划分训练集和测试集(7:3)。因为机器学习实施至少涉及两个阶段,训练和测试。训练阶段是模型本身学习的阶段。在此阶段使用的数据称为训练数据,测试阶段是使用或测试在训练阶段学习的模型的新样本的阶段。在此阶段使用的数据称为测试数据,模型在正确预测这些数据方面的性能称为测试性能。需要注意的是,训练数据中包含的任何样本在测试阶段都不能被使用,因为这将使测试性能失效。其次,本文选择常用的机器学习方法:Logistic回归、支持向量机、K-近邻分析、随机森林和梯度提升,这五种模型覆盖了传统线性、非线性和集成学习方法。其原理如下:
(1)Logistic 回归用于解决分类问题,基于线性回归,但通过 Sigmoid 函数将线 性回归的输出映射到[0,1]区间,从而表示样本属于某一类别的概率。假设数据存在线性可分关系,通过最大似然估计来确定模型参数,使样本属于其真实类别的概率最大化。
其中,是表示在给定自变量
,
的概率,同样
如此,
是截距项,
是回归系数,它们表示每个自变量的影响程度。
(2)支持向量机模型旨在找到一个最优超平面,以最大化不同类之间的间隔。当数据不是 线性可分时,支持向量机模型使用核函数将数据映射到高维空间。在这个高维空间内,找 到一个超平面来分隔不同类别。支持向量机模型的核心是一个优化问题,旨在最小化一个 正则化项和一个损失函数。优化目标可以表示为:
其中,是松弛变量,
表示惩罚参数,其用于平衡间隔和分类错误之间的权衡。
(3)K-近邻分析是一种基本分类的方法。是数据挖掘技术中原理最简单的算法之一,核心功能是解决有监督的分类问题。KNN能够快速高效地解决建立在特殊数据集上的预测分类问题,其核心思想是:若某样本在特征空间中的k个最邻近样本多数属于某个类别,则该样本也归为此类[4]。
(4) 随机森林是一种集成学习模型,基于重抽样自举法的样本创建多个子集,用于构建每个决 策树,将多个决策树整合一起,最终构成整片森林。其核心思想是 “集体智慧优于个体”,它通过集成学习策略,先利用重抽样自举法从原始数据中随机生成多个不同的样本子集,再为每个子集独立构建一棵决策树,最后让所有决策树共同参与预测,通过投票(分类任务)或取平均(回归任务)的方式输出最终结果,以此降低单棵决策树的过拟合风险,提升模型的稳定性和预测精度。
(5) 梯度提升算法同样是一种集成学习方法,它的核心思想是利用损失函数的负梯度作为残差的近似值,然后用一个基学习器拟合这个残差,再将其加到之前的模型上,从而不断地减小损失函数的值。这种算法可以用任何可微分的损失函数,并且可以用任何基学习器,如决策树等。因此,梯度提升算法比其他基于单一类型损失函数的算法更加灵活和通用[5]。
最后,依据这五种模型输入到训练集中进行训练,将测试集输入训练好的模型,采用评价指标进行模型性能检测。二分类的评价指标如下:
(1)Accuracy(准确率):指模型正确预测的样本数占总样本数的比例,反映模型整体的预测正确性,公式为:
其中 TP 为真阳性,TN 为真阴性,FP 为假阳性,FN 为假阴性。

(2)recision(精确率 / 精准度):针对模型预测为正类的样本,其中真正为正类的比例,公式为:
(3)Recall(召回率 / 查全率):针对实际为正类的样本,模型成功预测为正类的比例,公式为:
(4)F1 Score(F1 分数):是精确率和召回率的调和平均数,综合衡量两者的表现,公式为:
(5)AUC(Area Under the ROC Curve,ROC 曲线下面积):ROC 曲线以假阳性率(FPR)为横轴、真阳性率(TPR)为纵轴绘制,AUC 是该曲线下的面积,用于评估模型在不同分类阈值下的整体性能,取值范围 0-1,越接近 1 说明模型分类能力越强。
,
代码如下:
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_curve, auc
import matplotlib.pyplot as plt
import numpy as np
df=pd.read_csv('feature.csv')
X = df.drop('numeric_label', axis=1)
y = df['numeric_label']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义模型
models = {
'Logistic Regression': LogisticRegression(max_iter=1000),
'SVM': SVC(probability=True),
'Random Forest': RandomForestClassifier(),
'Gradient Boosting': GradientBoostingClassifier(),
'KNN': KNeighborsClassifier()
}
results = {}
for model_name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
roc_auc = auc(fpr, tpr)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='weighted')
recall = recall_score(y_test, y_pred, average='weighted')
f1 = f1_score(y_test, y_pred, average='weighted')
results[model_name] = {
'Accuracy': accuracy,
'Precision': precision,
'Recall': recall,
'F1 Score': f1,
'AUC': roc_auc
}
# 绘制ROC曲线
plt.plot(fpr, tpr, label=f'{model_name} (AUC = {roc_auc:.2f})')
results_df = pd.DataFrame.from_dict(results, orient='index')
print(results_df)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.xticks(rotation=45)
plt.ylabel('True Positive Rate')
plt.title('ROC Curves of Various Models')
plt.legend(loc='lower right')
plt.show()
4 结果与讨论
从模型性能指标来看,所有模型在准确率(Accuracy)、精确率(Precision)、召回率(Recall)和F1分数(F1-Score)上均表现优异,数值普遍高于0.95,说明各模型在分类任务中具有很强且稳定的判别能力。其中,支持向量机、随机森林和K-近邻分析在准确率、精确率、召回率和F1分数上表现完全一致,且略高于逻辑回归和梯度提升模型。从AUC值来看,所有模型的曲线下面积均超过0.96,进一步验证了模型具有良好的整体分类性能,尤其是梯度提升模型的AUC最高(0.964),显示出其在区分正负类样本方面的最优能力。综合来看,各模型性能接近,均可有效应用于该分类任务,梯度提升在AUC上略占优势。
尽管上述模型在默认参数下均取得了不俗的性能表现(各项评估指标均超过0.95),但需要指出的是,当前结果存在一定的局限性。首先,所有模型均采用默认参数运行,未经过系统的超参数调优,其性能潜力可能尚未被完全发掘。其次,模型的训练与验证仅依赖于单一周期的数据,未能捕捉数据在时间维度上可能存在的动态变化与模式。此外,当前模型所采用的特征主要集中于时域,可能忽略了频域等其他特征空间中蕴含的判别信息。因此,若要在实际应用中实现性能的进一步提升,后续工作应着重于模型参数的精细优化、引入跨时间周期的数据进行训练,并融合多领域的特征工程。
| 模型 | Accuracy | recision | Recall | F1Score | AUC |
| Logistic回归 | 0.957364 | 0.957604 | 0.957364 | 0.957355 | 0.961824 |
| 支持向量机 | 0.963178 | 0.963238 | 0.963178 | 0.963175 | 0.959435 |
| 随机森林 | 0.963178 | 0.963238 | 0.963178 | 0.963175 | 0.962530 |
| 梯度提升 | 0.959302 | 0.959308 | 0.959302 | 0.959302 | 0.964461 |
| K-近邻分析 | 0.963178 | 0.963238 | 0.963178 | 0.963175 | 0.961644 |

5 参考
[1] MATLAB | 数字信号处理 | 高斯加权移动平均滤波算法 | 附数据和出图代码 | 直接上手 - 知乎
[2] 算法 - 高斯加权移动平均滤波(GWMA) - 世界尽头号
[3] Maqsood S, Xu S, Springer M, et al. A benchmark study of machine learning for analysis of signal feature extraction techniques for blood pressure estimation using photoplethysmography (PPG)[J]. Ieee Access, 2021, 9: 138817-138833.
[4] 一文掌握KNN(K-近邻算法,理论+实例) - 知乎
[5] 梯度提升(Gradient Boosting)算法:原理与实践-百度开发者中心
注:若有侵权部分,请留言将会删除。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)