基于深度学习的 ECG 心律失常分类入门
ECG 分析: 基于深度学习的 ECG 心律失常分类入门
本文主要用于备份保存。
写作动机
由于受突发疫情的影响,开学时间推迟了(在此特向奋斗在前线的各行各业的工作者们致以崇高的敬意!)。前天晚上刚好看到一篇新出的论文,跟自己之前做的方向类似,当时想着抽个时间复现一下,一方面是为了做一个备忘笔记,另一方面,考虑到有一部分后来者可能也是 ECG 算法研究相关的方向,所以做这个简单的入门探讨,也许会起到一丁点的作用。这个是从数据获取到最后的结果评估,还是比较完整的,主要特点是内容简单,写的代码也是没有经过封装的,还是比较容易上手的。特别强调,** 只适合小白食用,大佬请绕行!!哈哈哈…**。
深度医疗
随着 “人工智能” 的兴起,“深度医疗”这个概念也被提了出来,越来越多的基于机器 / 深度学习的医学研究项目已经展开,比如基于深度学习处理医学图像,对特定医学病变做诊断识别等。如果在未来这类研究真的大面积落地了,那真将是一件大快人心的事情,很大程度上解决了很多现实问题,比如节约了医疗资源等等。
本篇主要是介绍基于深度学习进行心电图(ECG)疾病分类的应用,用的数据库是 MIT-BIH Arrhythmia Database(MITAB);MITAB 后面会具体介绍的,通过复现一篇新出的论文 https://doi.org/10.1007/s10916-019-1511-2,用卷积神经网络 (CNN) 做一个简单的心律失常分类。后面也会把代码上传到个人的 github 上。那现在开始吧。
** 简单认识 ECG**
ECG(Electrocardiogram)就是我们常说的心电图,相信每一个人都在医院做过心电图检查。
既然要做 ECG 分析,那么在这里我们来具体认识一下 ECG:
如图,典型的心电图包含了一系列特征波和特征波段:
特征波:P 波,R 波,T 波,U 波等;
特征波段:P-R 间期,QRS 波群,S-T 段,R-R 间期,Q-T 间隔等。
这些波段都包含了非常多的病例信息。例如:RR 间期可以反映心动周期的时限;相邻心动周期的 RR 间期的比值可以反映室性早搏;R 波和 S 波幅值的比值和 R 波和 S 波之间的时限可以反映房性早搏等异常情况,等等。医生分析心电图的时候也是结合这些典型的特征,进一步做医学诊断。
因此,在用深度学习算法做心律失常分类之前,首先要做的一个工作是,对 ECG 信号特征波和特征段的提取,也就是前期的预处理工作,这个后面会具体讲到。
所以接下来分别从 **MITAB 数据库的获取 , 数据预处理 ,ECG 分类 ** 等方面展开。开始也说了,这是一个入门的探讨,内容是基础的,这方面的博客也出来了很多,很多前辈的工作做的很详实,比如:https://blog.csdn.net/qq\_15746879 等, 关于这方面的文献也有很多,感兴趣的可以联系我或者自己去查。另外,前面有提到,算法设计是基于新出的一篇文章 https://doi.org/10.1007/s10916-019-1511-2 ,基本上是一个复现工作,部分操作是依赖个人经验,如果存在错误和疏漏,欢迎批评指正,毕竟水平有限。
正文
ECG 数据库的获取
** 数据来源:MIT-BIH Arrhythmia Database 数据库介绍和获取 **
前面已经对 ECG 信号有了简单的认识,那么现在来简单看看我们的研究主角——**MITAB 心电数据 **。本篇基本上是以图为主啦,哈哈,基本上是一步一步的操作过程展现。
获取网站:https://www.physionet.org/ ,Data 是我们获取数据的入口:
可以看到,侧栏不仅仅有 ECG 数据:
直接找到我们需要的 ECG 就好啦,
额,比较拖沓,其实直接从这里 **https://www.physionet.org/content/mitdb/1.0.0/\*\* 进入就可;这里面有关于这个数据库的具体介绍,建议下载之前,仔细阅读一下,毕竟做饭之前要把厨房的柴米油盐都熟悉一下:
可以看到:
** 这个 MITAB 包含 48 条双导联的 ECG 记录,除少数记录外,每条记录的第一个导联都是 II 导联,每条记录长度为 30 分钟,采样率 360 Hz,算起来有 30_60_360=648000 个采样点,但其实每条记录都有 650000 个点,也就是并非严格等于 30 分钟。(引用别人介绍的,讲得很清晰了)**

如下位置下载数据:
下面就那 100 信号做一个展示的一个小例子吧:
view 一下:
Table 选项是关于该信号的相关信息:标签数目,疾病类型,导联号等等。
点击上面选项自行查看,也可以切换其他记录感受一下。关于数据的获取就介绍到这里。
最后,还需要介绍一下标签,这个数据库的标签有两种,一种是基于心拍的;还有一种是基于片段的,考虑的是信号在某一时段的节律变化。因此,我们要根据问题的需要,使用相应的标签文件。
具体如下:
还可以点开每一条记录查看标签信息:
下面是一个所有涉及到的类型的标签汇总,我们用到的也就是上半部分的心拍标签:
python 读取数据和预处理操作
** 数据库的 Python 读取 **
本次读取数据,用的是一款专门读取 MITAB 数据的工具——WFDB-python,WFDB 包下载 ,全称是 Python waveform-database ,这是一个用于读取、写入以及处理 WFDB 信号和注释的工具库,重点是还加了关于生理信号处理等功能。不得不说在处理 ECG 方面,包含的 API 真的是太友好啦。安装的话,直接:
1 | pip install wfdb |
下面结合读取一条记录的例子,做一个简单的示范:
1). 关于用 wfdb 对 119 记录的相关操作
1 | import wfdb |
可以看到,record 元组里面包含了一个 array,里面就是 ECG 信号幅值,也就是我们需要的数据,前面说过,这个数据库采集的都是两个导联的数据;另外一个是字典,包含了该条记录的相关信息:
plot 一段:
最后贴上一个包含数据和标签的展示图:
这里说一下,关于 R 峰定位算法有很多,有一个经典的就是 Pan-Tompkins algorithm: Pan, J., and Tompkins, W. J., A real-time QRS detection algorithm.IEEE transactions on biomedical engineering 3:230–236, 1985. 由于 R 峰定位等基准点检测不是本次研究的重点,所以这里直接采用给定的 R 峰的索引,也就是标签索引。
2). 数据预处理
(1)滤波:
这里采用文章里面提到的小波去噪的方法,原因解释得也很清楚:
(2)心拍截取:
因为我们的是基于心拍做心律失常分类的,那么我们需要做的一个工作是心拍截取,文章种提到,以 R 峰位基准点,取前面 0.4s 后面 0.5s 作为一个心拍,也就是:
采样率 = 360Hz,也就是心拍长度是 0.4_360+0.5_360=324.
这里只给出这一操作步骤,这里还需要考虑边界值,哈哈哈,这是刷题习惯啦
1 | beat=record[label_index[j]-144:label_index[j]+180] |
1 | label_store symbol description |
根据 AAMI 标准,这 14 个小类集成为 5 个大类(N, SVEB, VEB, F, Q):
我们的任务也是基于对这 5 个大类开展的,所以在截取心拍的时候,就要根据标签类型,将样本归类:
1 | # 这是截取中间步骤的代码块 |
到这里,心拍截取好了,也归类了。下面就是模型的搭建(这里就不再专门介绍卷积神经网络了,需要很长篇幅)直接开始模型搭建吧:
** 模型搭建 **
这个模型其实没什么新意的,2 层卷积 2 层池化和 3 个全连接层,算是最入门的了,不过这里作者用了一个 “多头” 输入(笑 cry…)3 个输入都是同一条数据,只不过每一个分支设置的卷积核参数不一样,例如第一层的三个通道的卷积核尺寸分别是 4,6,8,个人觉得这里没有使用图像处理中的 3x3,1x1 等更小卷积核,是因为我们的 ECG 是一个基于幅值的低频数据,小的感受野不一定能够包含一个较大范围的幅值变化,也就是较小的局部区域,很难构成特征性较强的波形。而为什么没有使用诸如 16,32 之类的大卷积核呢?因为我们的心拍本身就是一个短时间数据,过度关注整体上的变化而忽略了局部,所谓“过犹不及”,所以选择了三个中等大小的卷积核。总的来说,调参是一部分,还有一部分是要基于问题本身,从数据层面的分析,再到模型层面的分析。
原本是 324 的心拍长度,看到这里的时候,输入是 251,作者好像是没提,为了尽可能复现,在这里我就重采样到了 251。
** 交叉验证 **
拿到文章的第一眼先看看作者做的是关于 inter-patient,还是 intra-patinet,(关于两种分类模式,推荐看我有一个师兄的博客inter-patient 和 intra-patienthttps://blog.csdn.net/qq\_15746879/article/details/80487543,介绍得很清楚)
读到下面的流程图,一眼就看出来了(其实看最后的结果展示,也看得出来):
这里,作者是先截取心拍,然后数据集划分,采用了三个不同折的交叉验证:
基于 intra-patient 的分类,完全不考虑个体差异性问题,训练集和测试集可以来自同一个病人,个体差异性带来的消极影响最小,实现难度很小,
很容易就可以达到很高的性能。所以,这里我只采用了 5 折交叉验证的例子(即 Experiment3)
实验结果的评估和分析
数据和模型完成了之后,就是训练和测试了,这里顺带提一下,MITAB 的数据是 48 条记录的,而我们在做 ECG 分析的时候,都是去掉了四条记录(102,104,107,217)所以最后的数据是 44 条记录的。
所有各类心拍样本数如下:
五折交叉验证,分别做了 4 分类和 5 分类。另外,论文中采用敏感度 (Se) 和正预测率(+P)作为评估指标。
下面展示一下结果:
** 实验结果评估和分析 **
- 实验结果
由于网络本身的随机性,每次的结果可能有一些偏差,5 折交叉验证之后,也分别计算了平均 Acc 和 F1。最后给出了相对好一点的混淆矩阵和具体的指标。
(1) 五分类(N,SVBE,VEB,F,Q)结果:
平均 Acc=99.07%, 平均 F1=0.77
归一化的混淆矩阵:
N:
Se=0.996061
+P=0.992263
SVEB:
Se=0.814607
+P=0.979577
VEB:
Se=0.970014
+P=0.979577
F:
Se=1.0
+P=0.979072
Q:
Se=0.0
+P=nan
(2) 四分类(N,SVBE,VEB,F)结果:
平均 Acc=99.0%, 平均 F1=0.95
归一化的混淆矩阵:
N:
Se=0.995605
+P=0.992019
SVEB:
Se=0.802792
+P=0.886320
VEB:
Se=0.974252
+P=0.982456
F:
Se=1.0
+P=0.993243
(3)文章中的四分类结果:
混淆矩阵:
性能指标:

2. 结果分析
(1) 从数据和分类结果上看,** 样本类别不平衡问题 ** 严重,这也是面临疾病分类问题的常有的情况,毕竟异常的是占少数的,所以大部分工作也是去掉了对 Q 类心拍的四分类问题。这类问题一方面是从数据层面入手,比如过采样和欠采样,同时带来的问题是可能会出现过拟合;另一方面是从算法层面入射,比如修改损失函数,如:Sellami A, Hwang H. A robust deep convolutional neural network with batch-weighted loss for heartbeat classification[J]. Expert Systems with Applications, 2019, 122: 75-84. 这篇文章作者是还是在 inter-patient 的情况下做的,通过设计一个动态的基于 Batch 的加权损失,得到了良好结果,这里就不展示了,感兴趣的可以自行查看。
(2) 本次探讨是基于 intra-patient 的例子,前面已经说了,intra-patient 的模式忽略了个体差异性,带来的直接影响就是训练和测试的数据不一定存在分布不一致的情况。也就是说,难度大大降低了,其次,基于这种模式的研究是没有太多现实价值的。毕竟在临床研究中,一般是通过以往的诊断经验去分析新的别人数据,这个个体差异性就特别明显了。所以,做此类研究还是着眼于 inter-patient 模式下进行,或者 patient-specific,后者难度稍微低一点。所以说,一开始就有提到,这是一个入门的小例子。
(3) 还有一点,本次实验是基于基准点检测的心拍分类,如果使用任意节拍,或者一个长序列段,难度会有所上升,而且也要尽量摆脱基准点检测,因为准确率不可能百分之百,鲁棒性也有要求,其次是在操作过程上尽可能简单和稳定,这也是做所谓 “深度医疗” 的初心所在。
** 总结 **
总的说来,关于这方面的研究还是很有提升空间的,也相信会有越来越多优秀的方法被提出来。
技术有限,如果存在表述和方法错误等问题,敬请指导。
本次操作的所有代码将会整理上传至个人 GitHub https://github.com/cay846545867,代码极其简单易懂。
来源:惊鸿 cloud
to be continued…