ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PCG+ECG同步心音数据集:从预处理到一维CNN分类的完整实践

PCG+ECG同步心音数据集:从预处理到一维CNN分类的完整实践 简介一个面向心音信号处理与疾病辅助诊断的专用数据集包含3126条心音图PCG记录与同步心电图ECG单次记录时长在1060秒之间每条记录均提供同步双模态信号便于进行多模态分析。数据采集严格参照听诊标准涵盖主动脉区、肺动脉瓣区、三尖瓣区及二尖瓣区四个最常见听诊位置可用于心音分类、心音分割、异常心音筛查、心脑血管疾病辅助诊断等任务。压缩包内共2000个文件整体约206.81MB文件类型以mat数值文件、wav音频、hea头文件、dat原始信号记录为主其中mat与wav可直接在MATLAB或Python中读取适合进行信号预处理与特征工程。压缩包另附REFERENCE系列CSV标签文件以及records-normal与records-abnormal划分列表便于快速构建训练集和测试集免去手动整理标签的麻烦。已有378人学习下载适合生物医学工程、电子与信息、机器学习等相关专业的学生与研究人员借助该数据快速开展心音信号分析实验并验证算法模型。1. 心音图数据集3126条PCG与ECG同步记录能做什么心音图PCG记录的是心脏瓣膜开闭与血流冲击产生的机械振动心电图ECG记录的是心脏电活动。两者在临床上互为补充ECG提供心脏节律的时相基准PCG提供瓣膜状态的直接声学证据。这个包含3126个PCG记录、且每一条都带同步ECG信号的数据集在公开心音数据里属于规模与信息量都可用的一档尤其适合做心音分类、异常心音检测、S1/S2自动分割这类任务研究单纯心音特征的同事也可以拿同步ECG当标注基准来用。数据集来自医院场景的采集流程覆盖正常、瓣膜病、心律失常等常见情况的听诊记录。对做医学信号处理或者多模态融合的人来说真正有价值的是这3126条记录的同步性不需要自己雇人做听诊标注直接用ECG定位心音事件这在训练集和测试集划分、模型评估时能省掉大量人工。读这篇文章的人大多是在找“怎么把这个PCGECG数据集跑起来”的完整路径。下面按一套可复现的方案展开。2. 心音图数据集的PCG与ECG文件结构从读取到波形可视化2.1 PCG与ECG信号的采集参数和时相对应关系这套数据集用电子听诊器同时采集两路信号原始波形通常是双通道的wav文件第一通道是心音第二通道是同步ECG。心音的频率范围主要落在20Hz到200Hz之间瓣膜开闭的瞬态成分能到600Hz左右所以常见采样率是2000Hz到4000HzECG的QRS波群能量集中在0.5Hz到45Hz同一文件用同一采样率存下来并不会损失关键形态。时相对应关系上S1心音出现在ECG的R波后20ms到50ms由房室瓣关闭产生S2心音在T波终点附近由主动脉瓣与肺动脉瓣关闭产生。这个对应关系是后面做自动分割的核心依据。很多公开数据集只给单个PCG通道而这个数据集同时给了ECG通道就可以用R峰位置作为锚点来切分心搏而不需要跑复杂的心音包络检测算法。2.2 用Python读取wav文件并分离PCG与ECG通道读取这类双通道记录标准做法是用scipy的wavfile模块它直接返回采样率和原始PCM数据。import numpy as np import scipy.io.wavfile as wavfile sample_rate, data wavfile.read(record_0001.wav) print(采样率:, sample_rate, Hz) print(数据形状:, data.shape, dtype:, data.dtype) pcg data[:, 0].astype(np.float64) # 第一通道心音 ecg data[:, 1].astype(np.float64) # 第二通道心电图 print(PCG时长(秒):, len(pcg) / sample_rate)读取后把整型PCM转成float64是为了后续滤波和特征计算时避免整型溢出。数据形状通常是(N, 2)N是总采样点数第一维是时间轴。如果文件的位数是16bit取值范围在-32768到32767之间后续如果要把振幅归一化到[-1,1]直接除以32768.0就好。可视化是检查数据质量的第一步。把两路信号画在同一个时间轴上先看心音包里有没有明显的S1、S2形态再看ECG的R波是否清晰可辨。import matplotlib.pyplot as plt time np.arange(len(pcg)) / sample_rate fig, (ax1, ax2) plt.subplots(2, 1, figsize(14, 6), sharexTrue) ax1.plot(time, pcg, colorsteelblue, linewidth0.8) ax1.set_ylabel(PCG (mV)) ax1.set_title(心音图波形) ax2.plot(time, ecg, colorcrimson, linewidth0.8) ax2.set_ylabel(ECG (mV)) ax2.set_xlabel(时间 (秒)) plt.tight_layout() plt.savefig(pcg_ecg_preview.png, dpi150)画图时把两路信号用不同颜色区分检查时间轴对齐情况。如果PCG的包络和ECG的R波有明显的固定延迟说明采集硬件本身就有延迟如果延迟超过50ms后续做R波锚定时要先把两路信号做互相关校正。这一步不要跳过直接决定后面分割的精度。2.3 数据集的统计分布与切分注意事项拿到3126条记录后先做一轮统计确认每条音频的时长和采样率是否一致。常见情况是采样率统一、但时长从2秒到12秒不等。时长差异大的数据集做批量训练之前需要统一到固定长度或者按心动周期切块。统计项常见范围处理建议采样率2000-4000 Hz若不统一重采样到2000Hz时长2-12秒按平均心动周期切成定长片段通道数2PCGECG先拆通道再分别处理位深16bit除以32768归一化切分时还要注意一个容易踩的坑如果把同一条记录的心音片段放进训练集和验证集会导致数据泄露模型验证指标虚高。常见做法是按record级别划分数据集也就是整条wav文件只能出现在训练、验证或测试中的某一个集合里。3126条记录按8:1:1划分训测比对这个量级的数据是合理的。3. 心音图预处理链路滤波、R波定位与心搏节段分割3.1 带通滤波参数PCG与ECG各用哪一组截止频率PCG信号在采集过程中会混入环境噪声、呼吸音和传感器摩擦声直接用于建模会引入大量无效特征。滤波这一步在传统特征工程和深度学习管线下都需要做差别只是放在特征提取前还是网络输入前。PCG的带通范围我一般设20Hz到600Hz低端切掉基线漂移和呼吸音高端保留瓣膜开闭的瞬态成分。ECG则设0.5Hz到45Hz保留QRS波群为主的能量带。用scipy的butter和filtfilt实现零相位带通滤波from scipy.signal import butter, filtfilt def bandpass_filter(signal, lowcut, highcut, fs, order4): nyquist 0.5 * fs low lowcut / nyquist high highcut / nyquist b, a butter(order, [low, high], btypeband) return filtfilt(b, a, signal) pcg_filt bandpass_filter(pcg, 20, 600, sample_rate) ecg_filt bandpass_filter(ecg, 0.5, 45, sample_rate)filtfilt做双向滤波零相位延迟保证滤波后的波形和原始波形的R波位置对齐不会因为滤波引入时移。order为4时过渡带足够陡对心音这种瞬态信号来说不会明显过冲。如果明显听到高频噪声残留把PCG高阶截止频率降到400Hz如果ECG的T波模糊把低端截止频率调到1Hz以下重新试。3.2 基于ECG的R峰检测跨过心音包络检测的坑单纯在PCG上做心音分割需要先算包络Hilbert或香农能量再找峰值配对算法复杂且容易在瓣膜病理性杂音干扰下出错。有了同步ECG直接检测R峰然后按生理时序推算S1/S2窗口可靠性高很多。R波在ECG里是能量最集中的波段用scipy的find_peaks就够用。from scipy.signal import find_peaks abs_ecg np.abs(ecg_filt) threshold 0.6 * np.mean(abs_ecg) 0.4 * np.percentile(abs_ecg, 95) peaks, properties find_peaks( ecg_filt, distanceint(0.4 * sample_rate), # 心率上限150bpm对应的最小间隔 heightthreshold, prominencethreshold * 0.5 ) r_peaks_sec peaks / sample_rate print(检测到R峰数量:, len(r_peaks_sec), 平均心率(bpm):, 60 / np.mean(np.diff(r_peaks_sec)))distance参数控制两个R峰之间的最小采样点数按心率上限150次/分来设防止T波被误检成R波。threshold用的是绝对幅值和分位数组合比单一固定阈值适应性强因为ECG在运动过程中基线会漂移。prominence参数进一步筛掉低幅尖峰。检测完R峰后把R峰位置在原始图上叠画一遍肉眼检查漏检和误检比例这个步骤值得做一次。3.3 按心动周期切分PCG与ECGS1和S2的时窗怎么定有了R峰时间戳按生理知识定义每个心动周期的切分范围。每个R峰两侧各取一段比如R峰前200ms到下一个R峰前200ms这个区间恰好覆盖S1、收缩期、S2、舒张期四个部分。S1位于当前R峰后20-50msS2位于下一个R峰前约100ms。具体做法是取相邻R峰的间隔作为周期T从当前R峰前0.2T到下一个R峰前0.2T作为一段。def split_heartbeats(pcg_filt, ecg_filt, r_peaks, fs, pad_ratio0.2): beats [] for i in range(len(r_peaks) - 1): start int(r_peaks[i] - pad_ratio * (r_peaks[i1] - r_peaks[i])) end int(r_peaks[i1] - pad_ratio * (r_peaks[i1] - r_peaks[i])) if end - start int(0.4 * fs): continue pcg_beat pcg_filt[start:end] ecg_beat ecg_filt[start:end] beats.append((pcg_beat, ecg_beat)) return beats beats split_heartbeats(pcg_filt, ecg_filt, peaks, sample_rate)切分时过滤掉过短的片段这类片段通常是心率异常或者R峰误检导致的保留下来会污染训练数据。这套切分逻辑可以做成一个通用函数批量处理3126条记录时只需要一个循环。所有切出来的片段保存成npy文件按标签归档后续加载时直接np.load比每次训练都重新读wav和处理要快。训练集和测试集的切分必须在切分心搏之前按record维度完成。处理阶段输出保存格式说明原始wav双通道整型wav保持原始采样率滤波后双通道浮点npy消除呼吸音与基线漂移R峰检测时间戳数组npy与wav文件名一一对应心搏切分定长片段npz每个片段附带源记录ID4. 用PCG与ECG做心音分类特征向量构造与PyTorch模型训练4.1 心音分类任务定义与模型输入形态选择心音分类的常见任务是把每个心动周期划分为正常或异常两类也可以细分为瓣膜狭窄、二尖瓣反流、主动脉瓣病变等类别。3126条记录的量级做二分类或三分类是合适的类别超过5个数据集规模就会显得不足需要靠数据增强或用预训练模型迁移。输入形态的选择上三类方案各有取舍。原始波形经过滤波和重采样后直接输入一维卷积网络是最省事的方式本次预处理链路产出的心搏片段正好匹配用短时傅里叶变换或梅尔频谱转成二维图像再用ResNet做特征是稳定的但会丢失时序上的相位信息第三种是把PCG包络和ECG的RR间期作为手工特征训练传统机器学习模型适合做基线。本文用第一种方案把切好的PCG片段长度统一到固定采样点数喂给一维CNN。4.2 PyTorch一维CNN模型轻量化网络结构对心音这种一维生理信号一维卷积网络比二维网络参数更少、收敛更快。下面这个模型包含三个卷积块和一个分类头。import torch import torch.nn as nn class PCGClassifier(nn.Module): def __init__(self, input_length4000, num_classes2): super().__init__() self.conv1 nn.Sequential( nn.Conv1d(1, 32, kernel_size15, stride2, padding7), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2) ) self.conv2 nn.Sequential( nn.Conv1d(32, 64, kernel_size11, stride2, padding5), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2) ) self.conv3 nn.Sequential( nn.Conv1d(64, 128, kernel_size7, stride2, padding3), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) ) self.fc nn.Linear(128, num_classes) def forward(self, x): x x.unsqueeze(1) x self.conv1(x) x self.conv2(x) x self.conv3(x) x x.squeeze(-1) return self.fc(x)输入张量形状是(batch, input_length)在forward里通过unsqueeze(1)变成单通道送进卷积网络时支持批量计算。kernel_size取15和11对应到2000Hz采样率下是7.5ms和5.5ms的时间窗既能捕捉瓣膜开闭的瞬态又不会过多关注高频噪声。AdaptiveAvgPool1d(1)把时序维压缩成1让全连接层的输入维度与输入长度解耦——也就是说训练时用4000点部署时遇到不同时长的片段也能前向传播。BatchNorm1d放在激活函数前面能加速训练收敛并降低对学习率的敏感性。4.3 训练参数选择这个量级该用什么学习率与批量大小训练数据是切好的心搏片段每个片段的标签来自数据集的注释或按record维度映射。训练循环的常见写法如下。def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0.0, 0, 0 for pcg_batch, labels in loader: pcg_batch pcg_batch.to(device).float() labels labels.to(device).long() optimizer.zero_grad() outputs model(pcg_batch) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * pcg_batch.size(0) preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) return total_loss / total, correct / total每轮迭代先清零梯度再前向传播计算损失然后反向传播并更新参数。这里的criterion用CrossEntropyLoss它内部已经包含softmax所以模型中不需要额外加log_softmax。学习率是医学信号里最容易调错的参数1e-3适合充分训练时从零学起的CNN1e-4到5e-5适合迁移学习继续微调的阶段。批量大小受显存限制一维信号复杂度低32到64都是安全的起点输入长度4000点、批量64时一块12G显存的卡完全够用。超参数推荐值调整依据学习率1e-3从头训练、1e-4微调损失不降时降到1/10批量大小32-64显存允许范围内尽量大训练轮数30-60早停看验证损失优化器AdamWweight_decay设1e-4权重初始化默认初始化不需要特殊处理训练过程中要监控验证损失而不是训练损失。心音数据的类别通常有较大不平衡比如正常样本占七成直接按准确率评估会让模型偏向多数类F1分数和敏感度、特异度都应该一起打印。数据量不够的时候可以给ECG也同时输入进一个并联分支做简单的双模态融合尾部concat后接分类头把这个数据集的最大价值利用起来。5. 用同步ECG做片段级验证与跨模态校准的落地技巧选用同步ECG作为一个额外的监督信号而不是只把它当作R峰参考这是这个数据集区别于纯PCG数据集的比较优势。做法是把ECG的QRS持续时间、RR间期变异度算出来作为辅助特征拼接到CNN的中间层输出上帮助区分心律不齐和单纯瓣膜杂音。落地的第一步是从验证集里随机抽出300段切好的心搏把PCG波形和ECG波形按时间轴叠画并保存成图。用R峰位置验证模型分类正确样本的S1时间分布如果正常类样本的S1集中出现在R波后20ms到60ms异常类样本的S1位置方差明显变大就说明模型学到的是病理特征而不是波形幅度的偶然偏差。这一步在论文复盘时也特别有用。第二个技巧是用RR间期做数据清洗。滤波后的ECG里RR间期变异超过20%的片段多半是R峰误检或早搏把这些片段单独抽出来人工复核而不是直接扔进训练集。3126条记录里这类片段通常占5%以内清洗后分类效果的提升往往比调整网络结构更明显。第三个技巧落在测试时的滑动窗口预测上。推理阶段的输入和训练输入长度不一致可以用一个固定长度的窗在整条PCG上滑动得到多个窗口的概率输出取多数投票作为这条记录的最终分类结果。窗口之间重叠50%既覆盖了心动周期的完整边界又不会引入过多冗余计算。注意这条操作要在与训练数据相同的前置处理和归一化参数下执行否则窗口概率会系统性偏移。最后做一个五折交叉验证来评估3126条记录的综合性指标五折按record维度划分每折大约625条记录做验证。最终要看的不是单折最高准确率而是五折平均F1和标准差。如果标准差超过0.04说明有某些折里混入了分布外的记录类型这时回到R峰检测结果检查该折数据的波形质量而不是无脑加正则化系数。这套流程跑完再回头调整滤波范围训练参数一版比一版更稳。本文还有配套的精品资源点击获取
返回列表