PTB-XL心电信号处理

ECG-Classfier

选修了一门模式识别的课程,这篇博客是对本人模式识别课程设计的一个简单的总结,相关的代码已经上传至github,详情参见后文的github链接

PTB-XL database

首先是数据的说明,这里用到的心电数据来自PTB-XL数据库,以下数据库的说明来自官方网站,详情可以参看PTB-XL详情页。

简介

在1989年10月至1996年6月的近七年中,使用Schiller AG的设备收集了PTB-XL ECG数据集的基础波形数据。通过从Schiller AG收购原始数据库,将全部使用权转让给了Schiller AG。 PTB。在Physikalisch-Technische Bundesanstalt(PTB)的一个长期项目中,整理了这些记录并将其转换为结构化数据库。该数据库已在许多出版物中使用,但是直到现在,访问仍然受到限制。机构伦理委员会批准了匿名数据在开放访问数据库(PTB-2020-1)中的发布。在2019年的公开发布过程中,对现有数据库进行了简化,特别是针对机器学习社区的可用性和可访问性。

数据采集

  1. 原始信号数据已记录并以专有压缩格式存储。对于所有信号,我们在右臂上提供12组标准导线(I,II,III,AVL,AVR,AVF,V1,…,V6),并带有参考电极。
  2. 一般对应的元数据(如age,sex,weight和height)被收集在数据库中。
  3. 每条记录都用报告字符串(由心脏病专家生成或由ECG设备自动解释)进行注释,该报告字符串将转换为一组标准化的SCP-ECG声明(scp_codes)。对于大多数记录,还提取了心脏的轴(heart_axis)和梗塞区(infarction_stadium1和infarction_stadium2,如果存在的话)。
  4. 大部分记录已由第二位心脏病专家确认。
  5. 所有记录均由主要关注信号特性的技术专家验证。

数据预处理

心电图和患者由唯一的标识符(ecg_id和patient_id)标识。元数据中的个人信息(例如,验证心脏病专家,护士的姓名和记录的记录地点(医院等))是假名。出生日期仅作为ECG记录时的年龄,其中符合HIPAA标准的89岁以上的年龄在300的范围内。此外,每位患者的所有ECG记录日期均偏移了随机偏移量。用于注释记录的ECG声明遵循SCP-ECG标准。

数据集说明

数据集组织结构如下所示

ptbxl
├── ptbxl_database.csv
├── scp_statements.csv
├── records100
│   ├── 00000
│   │   ├── 00001_lr.dat
│   │   ├── 00001_lr.hea
│   │   ├── ...
│   │   ├── 00999_lr.dat
│   │   └── 00999_lr.hea
│   ├── ...
│   └── 21000
│        ├── 21001_lr.dat
│        ├── 21001_lr.hea
│        ├── ...
│        ├── 21837_lr.dat
│        └── 21837_lr.hea
└── records500
   ├── 00000
   │     ├── 00001_hr.dat
   │     ├── 00001_hr.hea
   │     ├── ...
   │     ├── 00999_hr.dat
   │     └── 00999_hr.hea
   ├── ...
   └── 21000
          ├── 21001_hr.dat
          ├── 21001_hr.hea
          ├── ...
          ├── 21837_hr.dat
          └── 21837_hr.hea

将下载后数据集解压后拷贝至data文件夹下:

PTB-XL-CLASSFIER
├── code
├── dataPreprocess.ipynb
├── data
    ├── ptbxl_database.csv
    ├── scp_statements.csv
    ├── records100
    │   ├── 00000
    │   │   ├── 00001_lr.dat
    │   │   ├── 00001_lr.hea
    │   │   ├── ...
    │   │   ├── 00999_lr.dat
    │   │   └── 00999_lr.hea
    │   ├── ...
    │   └── 21000
    │        ├── 21001_lr.dat
    │        ├── 21001_lr.hea
    │        ├── ...
    │        ├── 21837_lr.dat
    │        └── 21837_lr.hea
    └── records500
    ├── 00000
    │     ├── 00001_hr.dat
    │     ├── 00001_hr.hea
    │     ├── ...
    │     ├── 00999_numhr.dat
    │     └── 00999_hr.hea
    ├── ...
    └── 21000
            ├── 21001_hr.dat
            ├── 21001_hr.hea
            ├── ...
            ├── 21837_hr.dat
            └── 21837_hr.hea
    ...

该数据集包含18885名患者的10837秒的21837条临床12导联ECG记录,其中52%是男性,48%是女性,年龄范围从0到95岁(中位数为62,分位数范围为22)。数据集的价值来自对许多不同的同时发生病理的全面收集,也来自于大部分健康对照样品。诊断的分布如下,为了简化起见,我们将诊断语句限制为聚合到超类中的诊断语句(注意:由于每个记录可能有多个标签,因此语句的总和超过了记录数):

RecordsSuperclassDescription
9528NORMNormal ECG
5486MIMyocardial Infarction
5250STTCST/T Change
4907CDConduction Disturbance
2655HYPHypertrophy

波形文件以16位精度以WaveForm数据库(WFDB)格式存储,分辨率为1μV/ LSB,采样频率为500Hz(records500/)。为了方便用户,我们还以100Hz(records100/)的采样频率发布了下采样版本的波形数据。

所有相关的元数据ptbxl_database.csv均以标识,每条记录存储一行。它包含28个列,可以分为:ecg_id

  1. 标识符:每个记录都由唯一的来标识ecg_id。对应的患者通过编码patient_id。原始记录(500 Hz)的路径和记录的降采样版本(100 Hz)存储在filename_hr和中filename_lr。
  2. 常规元数据:人口统计和记录元数据,例如年龄,性别,身高,体重,护士,部位,设备和recording_date。
  3. ECG语句:核心组件是(SCP-ECG语句作为字典,带有以下形式的条目)
    scp_codesstatement: likelihood,其中可能性(如果未知,则设置为0)并进行报告(报告字符串)。附加字段,,,,,和。
  4. 信号元数据:信号质量,例如噪声(和),基线漂移()和其他伪像,例如。我们还提供了计数额外的心脏收缩和起搏器的信号模式,以指示起搏器处于活动状态。
  5. 交叉验证折叠:建议进行10倍火车测试拆分(heart_axisinfarction_stadium1infarction_stadium2validated_bysecond_opinioninitial_autogenerated_reportvalidated_by_human
    static_noiseburst_noisebaseline_driftelectrodes_problemsextra_beats
    strat_fold)是在尊重患者分配的同时通过分层抽样获得的,即特定患者的所有记录都分配了相同的首折。第9和10折中的记录至少经过了一次人工评估,因此具有特别高的标签质量。因此,我们建议将1-8倍用作训练集,将9倍用作验证集,将10倍用作测试集。

与使用的注释方案有关的所有信息都存储在专用文件中,该文件中充斥着与其他注释标准(例如AHA,aECGREFID,CDISC和DICOM)的映射。我们提供了其他附带信息,例如可以将每个语句分配给的类别(诊断,形式和/或节奏)。对于诊断语句,我们还提供了一个建议的层次结构到和中。scp_statements.csvdiagnostic_classdiagnostic_subclass

获取数据

当然处理数据的第一步不可获取的需要获取数据,获取数据的网页连接和方法如下:

  • 该数据集的下载页面在这里:PTB-XL
  • 压缩的数据集下载:下载ZIP文件
  • 在终端下载可以直接通过wget命令行实现
    wget -r -N -c -np https://physionet.org/files/ptb-xl/1.0.1/
    得到的12通道的数据如下图所示:
    在这里插入图片描述

数据预处理

数据预处理通过code/dataPreprocess.ipynb处理实现

主要的流程包括:平滑滤波、50Hz陷波、R波分段提取、基线漂移去除、数据集划分

平滑滤波

通过窗口滑动平均滤波实现,这里采用的是10点平滑滤波,最开始是尝试的5点平滑滤波,但是根据导师的建议,平滑滤波的点数 N N N 是通过采样频率 f s fs fs 和工频干扰频率 f c fc fc 的比值得到的,具体的计算公式:
N = f s / f c N = fs/fc N=fs/fc
调用的是np的卷积函数:

# 平滑滤波函数
def np_move_avg(a,n,mode="same"):
    return(np.convolve(a, np.ones((n,))/n, mode=mode))
# 调用平滑滤波函数 
ecg_filtered = np_move_avg(ecg_original, 5)

平滑滤波的效果如下图所示:
在这里插入图片描述

50Hz陷波

通过scipy.signal.iirnotch实现

最开始的处理没有考虑到平滑滤波的点数,导致50Hz的公频没用去掉,所以尝试使用一个陷波器去除 公频干扰,如果之前的平滑滤波在500Hz采样频率信号的点数为10点的话,工频干扰基本可以去除的很干净,之后就不用采用陷波处理了

R波分段提取

R波的检测是通过固定差分阈值实现,基本原理是设定一个固定的峰值阈值作为限定条件,一般通过获取某一段时间内心电信号最大值和最小值,来设置捕获条件阈值,计算式如下:

T h r e s h h o l d = ( m a x ( x ) − m i n ( x ) ) × 0.7 + m i n ( x ) Threshhold = (max(x)-min(x)) \times 0.7 + min(x) Threshhold=(max(x)min(x))×0.7+min(x)

这里主要是通过II导联的心电信号进行差分阈值得到R波的位置

基线漂移去除

基线漂移的去除是采用PR段作为基线,先取每个导联PR段上10个数据点的均值作为基线的近似值,然后用所有的数据减去该近似值,即可得到去基线的心电图数据

关于要不要去除基线这个问题持保留情况,主要是心电信号在低频部分也有部分很重要的有用信号,例如ST段,考虑到基线对心电前期预处理没有太大的干扰,这里就没有去除基线漂移
导师给的意见是如果要想模型建立的更加稳定精确,最好去除一下基线漂移

机器学习模型分类

这部分的内容就较为简单,主要是调用sklearn库函数实现分类,具体实现的模块在mlModel.ipynb

数据超采样

这里的心电数据样本存在样本不均衡的问题,所有的分类中正样本比例均显著低于负样本比例,如果采用欠采样的话会使训练集丢失部分数据,而过采样会导致一个数据点在高维空间出现多次,增加过拟合风险,很多研究通过在过采样中加入少量随机噪声来减少这类风险

这里需要安装imblearn包,使用以下的命令行安装

pip install imbalanced-learn

oversampler = SMOTE(random_state=42)

os_features, os_label = oversampler.fit_sample(X_train.reshape(len(X_train), 6000), y_train)

PCA降维

在进行最后的分类前,可以看到数据输入的维度较大为6000,在真实分类之前首先对训练数据进行PCA降维,将6000维的数据降为240维的数据。这样得到降维后的数据还保留原始数据超过99%的信息,而且在训练中计算量大大减少,也可以在一定程度上降低过拟合的情况。

from sklearn.decomposition import PCA

pca_train = PCA(n_components=240)
pca_train.fit(os_features)

分类模型

尝试了下svm,运行的时间太慢了,实在等不下去了,测试了下随机森林分类,效果在预期之内,就利用随机森林跑了一下分类,之后有时间再修改一下

from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train_pca, y_train_norm)

利用随机森林对特定分类的病例二分类的分类器的准确率如下:

classaccuracyprecisionrecallf1-scoreroc score
MI0.9040.9930.5350.6950.950
STTC0.8990.9870.5160.6780.936
CD0.9050.9110.5870.7140.914
HYP0.9400.9900.7200.8330.963

得到的不同类别的病症的分类roc曲线如下图所示:
在这里插入图片描述

代码地址

本文的相关代码已经上传到github,后续的改进可能不会在CSDN及时更新,可以在github查询
github网址
也可以在百度网盘查看相关代码和文件:链接 密码: la01
个人博客地址:madao’s blog

  • 17
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 28
    评论
以下是一个基于PTB-XL数据集的深度学习心电信号分类代码的示例: ```python import tensorflow as tf from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout from tensorflow.keras.models import Sequential # 加载数据集 X_train, y_train, X_test, y_test = load_ptbxl_data() # 构建模型 model = Sequential() model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=X_train.shape[1:])) model.add(Conv1D(filters=64, kernel_size=3, activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Dropout(0.5)) model.add(Conv1D(filters=128, kernel_size=3, activation='relu')) model.add(Conv1D(filters=128, kernel_size=3, activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Dropout(0.5)) model.add(Conv1D(filters=256, kernel_size=3, activation='relu')) model.add(Conv1D(filters=256, kernel_size=3, activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Dropout(0.5)) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1, activation='sigmoid')) # 编译模型 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test)) # 评估模型 score = model.evaluate(X_test, y_test, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1]) ``` 这个示例代码中,我们构建了一个包含多个卷积层和池化层的深度学习模型,用于对心电信号进行分类。我们使用了PTB-XL数据集中的数据,并将其分为训练集和测试集。在训练过程中,我们使用了二元交叉熵作为损失函数,并使用了Adam优化器来调整模型权重。最后,我们评估了模型在测试集上的准确率。 请注意,这只是一个示例代码,您需要根据您的需求和数据集进行适当的调整。
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值