
简介基于离散小波变换DWT特征提取结合WEKA与MATLAB监督训练的心律失常分类资料包面向生物医学信号处理、机器学习初学者及相关研究人员重点解决ECG信号中异常节律的自动识别与分类问题旨在帮助使用者跨越信号处理与机器学习结合的门槛。资源共539个文件、约28.11MB包含MATLAB的m脚本、WEKA可用的arff训练/测试集、csv特征数据、mat中间结果、png可视化图以及论文LaTeX模板和PDF说明文档目录结构清晰便于按模块查阅。已有54人学习。资料完整覆盖从ECG预处理、DWT多分辨率特征提取到WEKA中构建并评估分类模型的完整流程arff文件区分全体样本与按比例采样等不同划分并附多个训练/测试变体方便对比模型在不同数据条件下的表现m脚本可直接运行并输出特征arff文件则用于WEKA中的监督训练与交叉验证。借助源码脚本与数据集读者可快速搭建自己的心律失常分类实验并在此基础上替换分类器或调整特征参数适合课程设计、毕业设计或科研入门参考。 心律失常分类Arrhythmia Classification这个方向我前后做了快两个月核心管线就是标题里那条路在 MATLAB 里完成信号预处理和离散小波变换DWT特征提取再把特征向量导出成数据集交给 WEKA 做监督训练。今天把这套流程完整复盘一遍从信号预处理、小波基选择、分解层数设定、特征构造到 ARFF 生成、分类器调参、结果评估包括我踩过的那些坑全部写出来。适合正在做生物医学工程毕设、或者刚接触心电信号分类想快速落地一套流程的朋友也适合被特征提取后分类精度上不去困扰的人。1. 项目整体思路与方案选型1.1 这个项目到底在做什么把问题说清楚输入是一段心电信号ECG输出是一个类别标签比如正常、室性早搏、房性早搏、束支传导阻滞。中间要解决的核心问题是如何把一段两三秒甚至更长的波形压缩成一组能代表这个心跳特征的数字再交给机器学习算法去学规律。这套流程里的三个关键角色分工很明确MATLAB 负责信号处理和小波分解因为它做数值计算和滤波器设计实在太成熟WEKA 负责分类训练因为它集成了几十个分类器不需要自己造轮子离散小波变换则是连接两者的桥梁负责把原始波形变成特征向量。整套链路就是原始 ECG → 预处理 → DWT 分解 → 特征构造 → ARFF 导出 → WEKA 训练评估。1.2 为什么偏偏是 DWT MATLAB WEKA先回答为什么不用 FFT。ECG 是典型的非平稳信号QRS 波、T 波这些成分持续的时间很短频带范围却很宽。FFT 只能告诉你整个信号里有哪些频率成分完全丢失了这些成分在什么时间点出现的信息。你去分正常心跳和早搏关键恰恰就藏在波形出现的时间和形态变化上所以频域整体分析基本帮不上忙。DWT 的处理方式则是把信号拆成不同尺度低频部分对应波形的大致轮廓高频细节部分对应波形的尖锐变化时间位置信息完整保留下来。这个特性跟 ECG 形态学分析天然契合。至于工具选型MATLAB 的 Wavelet Toolbox 里有wavedec、detcoef、wentropy这些现成函数十几行代码就能完成完整分解WEKA 则让我免去了写决策树、随机森林、SVM 代码的时间GUI 点几下就能完成训练和十折交叉验证。更重要的是特征工程和模型训练两个环节彻底解耦特征不对就回头调小波参数分类不好就单独调分类器不用整个流程推倒重来。2. 数据准备与预处理好特征的前提2.1 数据来源与清洗流程我用的是 MIT-BIH 心律失常数据库这是做 ECG 分类绕不开的公共数据集。但下载下来不能直接用原始记录里基线漂移、工频干扰、肌电噪声全都有。如果跳过预处理直接分解小波系数里很大一部分能量来自噪声后面构造的特征向量全是噪声贡献分类器再强也救不回来。我的预处理顺序固定为三步先用移动平均或高阶高通滤波去除基线漂移截止频率设在 0.5 Hz 左右把呼吸引起的基线摆动去掉。用陷波滤波器处理 50 Hz 工频干扰这一条在国内数据里尤其重要。做 R 波定位把所有心跳切成等长片段每个样本都代表一个完整心跳。这里有个非常关键的点切窗长度必须统一而且不能太短。我一开始用的是 R 峰前后各 0.25 秒总共 0.5 秒的窗后来发现部分早搏心拍的时限偏长T 波被截断了特征计算结果抖动明显最终把窗长调整到 0.6 秒才稳定下来。建议你先把数据集里的心跳时限分布看一眼再定窗长别凭感觉选。2.2 标签体系怎么定心律失常的类别极其多别指望一上来就分几十类。我最终做的版本是五分类正常N、房性早搏APB、室性早搏PVC、左束支传导阻滞LBBB、右束支传导阻滞RBBB。选这五类的原因是样本量够、类别间有区分度既有典型的形态差异又有容易混淆的类别训练出来模型有分析价值。如果你只需要一个快速基线模型做三分类正常/室早/其他也行但其他类不能包得太杂。我试过把所有异常心跳全归为异常结果分类器学到的只是波形怪就是异常具体异常类型完全分不开这类标签设计对你理解模型没有任何帮助。我的建议是宁可少分几类也要保证每一类在形态学上有清晰边界。3. MATLAB 端 DWT 特征提取实操3.1 小波基与分解层数怎么定DWT 里两个最影响结果的软参数就是小波基和分解层数。ECG 领域最常用的是 Daubechies 小波族特别是 db4 和 db6。原因很实际db4 的波形和 QRS 波的形态有一定相似性紧支撑、正交、计算量小分解出来的近似系数能比较好地保留心拍的形状信息。也有人用 sym5、coif3实测效果差距不大对初学者来说 db4 是最省心的起点。分解层数跟采样率强相关。MIT-BIH 原始采样率是 360 Hz我选 4 层分解最低频带大约对应 22.5 Hz 以下覆盖了 QRS 波的主要能量区间如果你用的是 1000 Hz 采样版本就需要 5 层甚至 6 层才能把能量压到相应频带。核心原则是你关心的波形成分要落在近似系数低频部分越往高层细节系数越接近噪声别把层数堆得过高。3.2 特征向量构造与代码骨架这里踩过最大的坑是把小波系数原封不动当成特征扔给 WEKA。系数维度太高、冗余大分类器很容易过拟合正确做法是把每一层的系数压缩成统计量。我实际用下来的特征组合是这些各层细节系数和近似系数的能量占比这是最核心的特征能反映不同频带的能量分布模式每一层系数的均值、绝对均值、标准差刻画波形的幅值和波动程度小波熵把能量占比当作概率分布计算香农熵用来量化信号的复杂度最后一层近似系数的均值和标准差相当于心跳形态的低频轮廓。最终每个心拍生成一个 18 维左右的特征向量代码骨架长这样function feat extractDWTFeatures(x, level, wname) [C, L] wavedec(x, level, wname); feat []; for i 1:level d detcoef(C, L, i); % 第 i 层细节系数 a appcoef(C, L, wname, i); % 第 i 层近似系数 E_d sum(d.^2); E_a sum(a.^2); feat [feat, mean(abs(d)), std(d), E_d / (E_d E_a)]; end aL appcoef(C, L, wname, level); % 最后一层近似 feat [feat, mean(aL), std(aL), wentropy(aL, shannon)]; end特别提醒一点wavedec返回的 C 是把所有层系数拼接成的大向量必须配合第二个输出 L 使用detcoef和appcoef来取系数不要自己去数 C 里的位置。我曾经因为想当然地以为系数排列顺序固定手工取位取错了半个月特征全错但程序不报错分类准确率居然比随机猜还低排查了很久才发现是取位错误。3.3 把特征导出成 ARFF特征算完之后得让 WEKA 能读懂。WEKA 默认的数据格式是 ARFF本质是一个带声明头的 CSV。你可以用csv2arff工具转换也可以直接在 MATLAB 里把特征矩阵写成 ARFF我更推荐后者省一步转换还能顺便检查数据。ARFF 文件的结构是这样的类标签放在最后一个属性位relation arrhythmia attribute feat1 numeric attribute feat2 numeric attribute feat3 numeric attribute label {N, APB, PVC, LBBB, RBBB} data 0.312,0.114,0.452,N 0.287,0.098,0.471,PVC 0.298,0.121,0.436,LBBB这个格式看着简单实际写的时候坑不少。类标签名只能用英文字母、数字、下划线有空格或者中文会导致解析直接失败。另外data后面每行数据的属性数量和attribute声明的数量必须严格一致多一个逗号少一个逗号都不行。我建议在导出后先用文本编辑器打开看一眼确认没有空行、没有 NaN、没有多余逗号再做训练否则排查数据问题会非常耗时。4. WEKA 监督训练与分类器调参4.1 训练集和测试集怎么划分监督训练第一步是数据划分这一步直接决定后面所有结果的可信度。我用的是每个类别内部按 7:3 随机分成训练和测试而不是对整个数据集整体随机分层。这么做的原因很直接心律失常数据往往类别不平衡整体随机分层容易让某些样本数量少的类别在测试集里只剩几个样本评估结果波动极大。如果样本量不大更推荐做十折交叉验证在 WEKA 的 Classify 面板里选 Cross-validationFolds 填 10 就行。需要注意的是交叉验证的 fold 划分尽量保持分层抽样WEKA 默认的StratifiedRemoveFolds就是分层抽样的别随意改成随机抽取否则类别不平衡时某些 fold 里可能压根没有少数类样本评估结果完全不靠谱。4.2 三个值得先试的分类器WEKA 里有几十个分类器但我建议你先盯住三个J48 决策树、RandomForest 随机森林、SMOSVM。J48 的最大价值是可解释性训练完能直接看到能量占比特征大于 0.5 就判为 PVC这样的规则这对论文分析和特征有效性判断很有帮助。随机森林在小样本高维特征下通常表现最好我用默认参数、100 棵树就能到 90% 左右的准确率。SMO 适合特征维度高、类别边界非线性明显的情况但需要调惩罚系数 C 和核函数参数训练时间也相对更长。我建议的顺序是先用 J48 跑个基线看它选中的特征有没有道理再切 RandomForest 看当前特征的上限最后才根据具体问题调 SMO。不要一上来就做参数网格搜索那是本末倒置——你得先确认特征本身够不够好再谈让分类器发挥到极限。4.3 从准确率到混淆矩阵准确率是最直观的指标但绝不能只看它。心律失常分类的典型场景是类别严重不平衡正常心跳数量远多于早搏如果模型把所有样本都判成正常准确率也可能有 85% 上下但真正要抓的 PVC 一个都没抓到。所以必须看混淆矩阵、Precision、Recall、F-measureWEKA 的 classifier output 面板默认会输出这些值。我的习惯是把每个类别的 F-measure 列出来横向比较。如果某个小类别的 Recall 明显偏低说明特征对它区分度不足这时候应该回 MATLAB 加特征而不是继续调分类器参数。之前有段时间 RBBB 的 Recall 一直卡在 70%后来加入相邻层系数相关性特征才提到 85%这个经验说明误分类信息比总体准确率更能指导特征迭代方向。5. 常见问题与排查技巧实录5.1 精度上不去的排查顺序遇到分类精度上不去我建议按固定顺序排查而不是盲目换模型改参数。第一查特征矩阵里有没有 NaN 或 Inf这是最容易被忽略的第二查标签和特征是否对齐我经历过数据顺序打乱后标签没跟着打乱精度直接跌到 50% 以下第三查小波分解层数是否匹配采样率换了数据集之后忘记调整层数会导致效果大幅下降第四查特征里有没有类别信息泄露比如误把记录编号当特征传进去。这个顺序能覆盖我遇到过的九成问题。最典型的例子是 NaN某个心拍如果 R 波定位失败切出来的窗是空的std会直接算成 NaNWEKA 某些分类器遇到 NaN 会静默忽略该样本训练集和测试集样本数对不上训练完都不知道数据去哪了。所以特征提取脚本里一定要加一步any(isnan(feat))的检查必要时直接丢弃异常样本。5.2 类别不平衡怎么办如果样本比例严重失衡先在 WEKA 里用SpreadSubsample或SMOTE过滤器做数据重采样。前者是欠采样适合样本量多的场景后者是过采样通过在特征空间插值生成合成样本适合少数类样本不足的情况。但这里必须泼一盆冷水SMOTE 是在特征空间插值生成的不是真实心电信号过量使用会让模型在验证集上表现虚高换到真实数据就容易翻车。我一般只对少数类做轻度过采样控制在原始数量的两倍以内更多精力还是放在优化特征本身。特征是根数据增强只是辅助。5.3 MATLAB 和 WEKA 联调的效率问题整套流程里最耗时间的不是训练而是反复MATLAB 算特征 → 导出 → WEKA 训练的循环。我强烈建议把 MATLAB 端特征提取封装成函数每次只需要输出一个features.csv然后写一个脚本自动调用 WEKA 命令行接口训练java -Xmx4g -cp weka.jar weka.classifiers.trees.RandomForest -t features.arff -i -o这样改参数、换特征、调分类器都能脚本化一条命令跑完比在 GUI 里反复点鼠标高效得多而且每次实验的参数和结果都能留档记录。实测用命令行跑十折交叉验证比 GUI 启动速度快不少尤其当特征矩阵变大时-Xmx4g这个内存参数是必须的否则容易 OOM 中断。我个人在实际操作中的体会是这类项目的瓶颈往往不在算法本身而在数据够不够干净、特征有没有真正描述清楚心律失常。DWT 和 WEKA 都是非常成熟的工具教程铺天盖地但很少有人告诉你切窗该选多长、小波基要不要换、特征算出来到底在表达什么。这些细节才是决定项目成败的地方。如果你也在做类似的方向建议把数据预处理和特征可视化的功夫做扎实分类之前先看看每个类别的特征分布长什么样再谈调参。最后再分享一个小技巧别把全部精力花在让测试集准确率再涨 1% 上多花点时间把混淆矩阵打出来看。很多你以为效果很好的模型其实只是把容易分的类别分对了真正难分的那几个类别才是你要解决的核心问题。把难分样本的特征拉出来对比一下往往比盲目调参更有效。本文还有配套的精品资源点击获取