
简介这是一份面向心电信号处理与深度学习研究者的毕业论文PDF围绕生成对抗网络在 ECG 降噪中的建模、实现与性能评估展开适合生物医学工程、计算机相关专业学生用于课题参考或算法设计。全文共1个PDF文档压缩包约6.36MB内容包括GAN生成器与判别器的架构设计、训练流程、数据实验以及SNR、MSE、PSNR等评价指标的对比分析并讨论了与传统降噪算法的性能差异和应用前景。文中还梳理了基于深度学习的信号处理方向与心电信号监测应用场景对生成器与判别器的对抗训练过程、损失函数设计及数据预处理均有覆盖可为读者提供从原理推导到实验评估的完整论文框架。目前已有132人浏览学习适合需要快速了解GAN在信号降噪中落地方案的毕业设计选题或论文写作参考。1. 生成对抗网络做心电信号降噪是把“像不像”变成学习目标的一类端到端方案医院里最常踩的一个坑是这样的监护仪上的心电波形看着挺顺滑一旦导出做ST段偏移分析结果就不敢信——带通滤波把QRS波群磨钝T波起点被振铃带歪最后只能靠人工复核。问题出在传统降噪在“滤掉噪声”和“保留波形形态”之间天然冲突。生成对抗网络这套方案把降噪重新定义成一个端到端的映射问题生成器学习含噪心电信号到干净心电信号的变换判别器负责判断输出到底像不像一段真实心电图两者互相较劲最终让模型在去噪声的同时把P波、QRS、T波的形态尽量还原。这篇笔记就沿着这个标题拆落地路径网络结构怎么选、数据怎么准备、训练怎么少翻车、性能分析怎么报才站得住。适合正在做心电信号预处理研究、毕业设计选型以及想把设备端降噪模块升级的工程师。2. 为什么是 GAN 而不是更花哨的滤波器心电噪声特征与网络结构选型先说结论GAN 在 ECG 降噪里并不是全盘替代传统方法而是把“波形保真”从隐含假设变成了显式优化目标。这一章把心电噪声拆开看再讲清楚生成对抗网络里的生成器、判别器和损失函数到底该怎么选。2.1 心电噪声三件套基线漂移、工频干扰与肌电噪声谁在压制传统滤波心电信号本身是毫伏级频谱集中在 0.05~100 Hz。真正让它难处理的不是噪声大而是三类噪声分别落在不同频段每类都在跟信号频谱重叠工频干扰50 Hz 及其谐波国内电网就是 50 Hz。它幅度大能把 P 波整个淹掉而且陷波器在处理它时经常留下振荡尾巴。基线漂移0.05~1 Hz 的亚低频来源是呼吸、电极拉扯和患者体动。高通滤波能压它但转折频率拉高以后会把 ST 段压歪直接影响临床判断。肌电噪声频带从 20 Hz 一直到几千 Hz形态上像毛刺和小波系数里的“突变”特征高度重叠所以小波阈值缩的时候最容易把 R 波顶端削平。传统链路里带通滤波和陷波器是基石零相位滤波filtfilt能解决相位失真但解决不了频率重叠。自适应滤波需要额外参考导联单导联设备根本不具备这个条件。这些问题导致一个很尴尬的现实处理完的波形“能看”但做定量分析时不敢用。GAN 的出发点和它们不同不做固定频响整形而是从大量“含噪-干净”配对样本里学习心电波形流形。噪声频谱和信号频谱重叠得越厉害这种学习式方法的优势越明显。2.2 两种降噪路线回归网络与对抗生成网络差在细节保真上如果只做监督回归问题会变得简单很多输入含噪 ECG输出干净 ECG损失函数用 MSE 或 MAE网络自己会学出一个映射。这个做法的短板在统计上很明确——L1/L2 损失会迫使网络输出条件均值遇到波形细节不确定的片段时它倾向于给一个模糊的中间形态。P 波和 T 波在不同个体之间差异很大最容易成为“合理抹平”的对象。对抗生成网络把判别器加进来以后生成器需要骗过判别器才能降低对抗损失。判别器本质上在回答一个问题这段输出到底是不是真实心电形态所以生成器被迫保留 R 波切迹、ST 段平缓过渡这些结构性特征。这不代表回归网络不能做而是在低信噪比、边界模糊的任务里对抗训练对细节的约束力更强。代价就是训练不稳定、可复现性差指标涨跌经常要看玄学但这些可以通过后续的 WGAN-GP 和参数约束来解决。2.3 网络结构选型U-Net 生成器配 PatchGAN 判别器生成器我通常选 U-Net。这个结构有 4 层下采样提取多尺度语义4 层上采样恢复时序分辨率中间用跳级连接把浅层细节直接送到深层输出。对 ECG 这种长程依赖和局部突变共存的一维信号跳级连接比普通残差网络好用得多。判别器则推荐 PatchGAN输出不是一个全局真假标量而是一张 patch 级的真伪概率图每个点对应一段局部感受野。这样判别器不会只盯着整段波形的平均形态而是会逐段检查“这段是不是真实心电的局部纹理”。模块推荐配置说明生成器主体U-Net4 层下采样基础通道 64每层翻倍判别器主体PatchGAN4 层卷积输出 patch 图局部判别输入长度2048 点约 8 秒250Hz必须能被 32 整除批大小32显存不足时降到 8配合梯度累积归一化信号整体归一化到 [-1,1]避免激活函数饱和输入长度 2048 是常见做法不是硬性标准。下采样 4 次后特征图长度变成 128每 16 个点对应一个判别感受野基本能覆盖 1~2 个完整心拍。如果你处理的是 360 Hz 采样率建议先重采样到 250 Hz让长度设计和后续部署更一致。2.4 损失函数WGAN-GP 加 L1 内容损失为什么默认不用 BCE传统 GAN 用 BCE 损失判别器很容易训练得太强梯度进入饱和区后生成器学不到东西。WGAN-GP 的做法是用 Wasserstein 距离度量真实分布和生成分布的差异并在样本插值路径上施加梯度惩罚让判别器保持相对平滑。这个改动对 ECG 降噪来说收益非常明显训练曲线更平稳生成器不容易一夜之间崩掉。生成器总损失通常写成生成器损失 L1 系数 × 内容损失 对抗损失其中内容损失用 L1 是因为它对偶发的大幅值QRS突变容忍度比 MSE 好。L1 系数我习惯从 10 起步信号归一化到 [-1,1] 时如果设成 100对抗损失会完全被内容损失压死输出偏保守细节照样被磨平。梯度惩罚系数默认取 10这是 WGAN-GP 原论文的常用值实测里翻车概率最低。整体思路是内容损失保证逐点接近对抗损失保证分布真实两者缺一不可。3. 数据准备这条路的三个关键决策干净参考、可控信噪比与数据隔离模型结构只是一个方面真正决定论文结果能不能复现的是数据怎么造。这一章把从取数到训练集的完整链路拆开讲每个步骤都会给可跑的代码。3.1 取数用 wfdb 读取记录并筛选导联MIT-BIH 心律失常数据库是 ECG 降噪研究里最常用的公共数据源格式是 WFDB 记录。第一步通常是用 wfdb 工具包读取原始信号。需要说明的是我这里不会贴下载地址数据获取方式请自行查阅学术资源渠道。# 读取一条 MIT-BIH 记录先取 10 秒看看波形 import wfdb import numpy as np fs 360 record wfdb.rdrecord(100, samptofs * 10) ecg record.p_signal[:, 0] # 第一导联一般是 MLII print(f采样率: {record.fs}, 导联数: {record.p_signal.shape[1]})rdrecord 返回的 record 对象里p_signal 是二维数组维度是样本数, 导联数。用[:, 0]取出第一导联。MIT-BIH 里两种常用导联是 MLII 和 V5取哪一导联没有绝对标准但要保持一致同一个实验里不要混用导联否则模型会去学导联间的形态差异而不是降噪本身。sampto 参数控制读取到哪个样本点fs*10 表示读 10 秒这只是预览正式建数据集时应该读完整长度。如果要用标注过滤恶性心律失常片段还需要读 annotation把室颤、停搏这些片段排除掉。实际执行中很多论文并不做这一步因为降噪网络对心律类型不敏感但如果你在性能分析里要按信噪比分段统计最好还是把每条片段的标注类型记下来备用。3.2 “干净”参考信号怎么来滤波参考与合成信号的权衡这里有个天然的坑MIT-BIH 原始记录本身是有噪声的没有真正意义上的干净标签。常见做法有两种。方案 A 是对原始信号做 0.5~45 Hz 带通滤波把滤波结果当成“干净参考”。优点是简单缺点是一旦用了带通QRS 的高频成分就已经被压制了模型学到的“干净”其实是“滤波后的干净”最终评估指标的绝对值会虚高。方案 B 是把原始信号经过轻度去噪后当作干净源然后人为叠加模拟噪声来构造训练对测试时再使用真实噪声片段。这种做法的好处是信噪比可控适合做性能分析。我一般倾向方案 B并且建议在实际部署阶段再加一半真实噪声做微调否则纯仿真噪声训练的模型在真实设备上表现会明显下滑。# 生成“干净参考”轻度带通只去掉亚低频和更高频毛刺 from scipy.signal import butter, filtfilt b, a butter(4, [0.5, 45], btypebandpass, fsfs) clean_ref filtfilt(b, a, ecg)butter 的第一个参数 4 是滤波器阶数阶数越高频率截止越陡峭但振铃越明显filtfilt 做的是零相位滤波能保证 R 波位置不偏移。带通范围 0.5~45 Hz 是在保留 QRS 形态和去掉基线漂移之间最常见的选择。如果你想更保守可以放宽到 0.5~50 Hz但高于 45 Hz 的肌电成分会残留较多影响后续训练对质量。3.3 构造带噪信号不要把 np.random.randn 当万能噪声有了干净参考下一步就是生成“含噪”输入。我通常会同时叠加三种噪声这样训练出的模型才能应对真实设备的多干扰环境。# 分段参数每段 2048 点段间重叠 50% seg_len 2048 stride seg_len // 2 t np.arange(seg_len) / fs # 基线漂移低频正弦组合 baseline 0.05 * np.sin(2 * np.pi * 0.3 * t) 0.03 * np.sin(2 * np.pi * 0.15 * t) # 工频干扰50Hz 主频 100Hz 谐波随机初始相位 phi np.random.rand() * 2 * np.pi powerline 0.15 * np.sin(2 * np.pi * 50 * t phi) 0.05 * np.sin(2 * np.pi * 100 * t phi) # 肌电噪声带限随机信号用 rand 而不是 randn避免直流偏置 muscle 0.08 * (np.random.rand(seg_len) - 0.5) noisy clean_seg baseline powerline muscle snr_db 10 * np.log10(np.sum(clean_seg**2) / np.sum((noisy - clean_seg)**2))几个参数值得细说。基线漂移用 0.3 Hz 和 0.15 Hz 两个低频正弦叠加是为了模拟呼吸和电极缓慢位移的多分量特征。工频干扰的幅度 0.15 相对比较大这是刻意为之——实际监护环境里工频往往强于肌电。注意phi这个随机相位如果每次都用固定相位模型会学到“噪声起点固定在某个位置”测试时一换相位就失效。肌电噪声用np.random.rand - 0.5而不是randn是因为均匀分布自带截断不会偶尔产生极端尖峰干扰训练稳定性。叠加完成后按公式算一下 SNR通常控制在 5~15 dB 之间。3.4 训练集/验证集数据隔离同源泄漏是最隐蔽的水分数据拼装完之后很多新手直接train_test_split随机切分这是大忌。同一个人不同时段的心电形态高度相似随机切分会把同一个记录的数据同时放进训练集和测试集模型本质上在“背”这个人测试指标虚高得离谱。正确做法是按记录 ID 隔离比如用 100、101、102 号记录做训练103、104 号记录做验证105、106 号记录做测试三个集合之间完全不允许有同一病人出现。# 保存为 HDF5按记录分组写入避免训练时反复造噪声 import h5py with h5py.File(ecg_dataset.h5, w) as f: f.create_dataset(noisy, datanoisy_stack, dtypefloat32) f.create_dataset(clean, dataclean_stack, dtypefloat32)HDF5 的好处是数据写入后可以在 PyTorch 的 DataLoader 里直接做随机读取不用把全部数据载入内存。noisy_stack 是形状为N, seg_len的数组clean_stack 与之对应。如果你内存紧张也可以把每条记录单独存成一个 dataset训练时按索引取。数据卫生这条规则是整个项目里优先级最高的哪怕因此训练样本量变小也比测试集里混入训练分布强得多。4. 生成器、判别器与训练主循环把网络结构落到 PyTorch 代码这一章进入实现层面。我会给出一套最小可跑的代码组织方式按“生成器定义、判别器定义、梯度惩罚、训练主循环、状态监控”五步拆开。4.1 生成器U-Net 轻量实现与通道设计一维 ECG 信号的主干网络用 Conv1d 实现。U-Net 的核心是下采样和上采样之间的跳级连接。import torch import torch.nn as nn import torch.nn.functional as F class Down(nn.Module): def __init__(self, in_ch, out_ch, use_normTrue): super().__init__() self.conv nn.Sequential( nn.Conv1d(in_ch, out_ch, 4, 2, 1, biasFalse), nn.BatchNorm1d(out_ch) if use_norm else nn.Identity(), nn.LeakyReLU(0.2, inplaceTrue), ) def forward(self, x): return self.conv(x) class Up(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.up nn.ConvTranspose1d(in_ch, out_ch, 4, 2, 1, biasFalse) self.conv nn.Sequential( nn.BatchNorm1d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x, skip): x self.up(x) x torch.cat([x, skip], dim1) # 沿通道拼接 x self.conv(x) return xDown 模块里的 Conv1d 参数kernel_size4stride2padding1这是实现“长度减半”最常用的一组配置。LeakyReLU 的 0.2 斜率能给负半轴一定梯度避免下采样过程中信息死掉。Up 模块里用 ConvTranspose1d 恢复长度然后在通道维拼接来自同一层级的跳级特征。拼接顺序必须是[x, skip]因为卷积通道数要匹配拼接后的总通道数。4.2 生成器主网络四层下采样接四层上采样把 Down/Up 组装成完整生成器。输入是含噪 ECG输出是干净 ECG形状与输入完全一致。class Generator(nn.Module): def __init__(self, in_ch1, base64): super().__init__() self.d1 Down(in_ch, base, use_normFalse) self.d2 Down(base, base * 2) self.d3 Down(base * 2, base * 4) self.d4 Down(base * 4, base * 8) self.mid Down(base * 8, base * 16) self.u1 Up(base * 16, base * 8) self.u2 Up(base * 8, base * 4) self.u3 Up(base * 4, base * 2) self.u4 Up(base * 2, base) self.out nn.Conv1d(base, 1, 1) self.tanh nn.Tanh() def forward(self, x): d1 self.d1(x) d2 self.d2(d1) d3 self.d3(d2) d4 self.d4(d3) m self.mid(d4) u1 self.u1(m, d4) u2 self.u2(u1, d3) u3 self.u3(u2, d2) u4 self.u4(u3, d1) return self.tanh(self.out(u4))注意第一层 Down 不用 BatchNorm原因是输入分布变化较大第一层先做归一化容易把原始幅度信息压掉。最后一层输出用 Tanh 把结果限制在 [-1,1]和前面数据归一化保持对齐。这里的 base64 是通道基准数显存紧张时可以降到 32但生成器的表达能力会同步下降。输入张量形状是 (B, 1, 2048)中间特征图长度依次为 1024、512、256、128所有长度都能被 32 整除。4.3 判别器PatchGAN 加谱归一化PatchGAN 的输出不是单个标量而是一张特征图。判别器输入是“含噪信号”和“待判别的信号”拼接而成的双通道通道拼接在时间域上不影响对齐。class PatchDiscriminator(nn.Module): def __init__(self, in_ch2, base64): super().__init__() self.model nn.Sequential( nn.Conv1d(in_ch, base, 4, 2, 1), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv1d(base, base * 2, 4, 2, 1), nn.BatchNorm1d(base * 2), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv1d(base * 2, base * 4, 4, 2, 1), nn.BatchNorm1d(base * 4), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv1d(base * 4, 1, 4, 1, 1), ) def forward(self, x, y): # x 为含噪信号y 为真实干净信号或生成器输出 return self.model(torch.cat([x, y], dim1))输入torch.cat([x, y], dim1)后的通道数为 2最后一个卷积把通道压缩到 1但空间维度不是标量而是与输入长度相关的一张分数图。每一个分数代表对应局部片段的真假。PatchGAN 的反直觉优势在于它不对整段波形做平均判断而是逐段检查局部字节这正好抓住了 ECG 中 P 波、QRS、T 波形态不规则的细节。如果你发现判别器输出经常整体偏向一侧可以在第一个卷积上套nn.utils.spectral_norm让权重的谱范数受限训练会更稳定但推理速度会略降。4.4 训练主循环WGAN-GP 梯度惩罚与判别器更新策略WGAN-GP 的关键在梯度惩罚项它让判别器在真实样本和生成样本的插值路径上梯度范数尽量接近 1。下面代码实现了核心逻辑。def gradient_penalty(D, real, fake, noisy): alpha torch.rand(real.size(0), 1, 1, devicereal.device) interp (alpha * real (1 - alpha) * fake).requires_grad_(True) d_out D(interp, noisy) grads torch.autograd.grad( outputsd_out, inputsinterp, grad_outputstorch.ones_like(d_out), create_graphTrue, retain_graphTrue )[0] gp ((grads.norm(2, dim1) - 1) ** 2).mean() return gpinterp是真实信号和生成信号之间的随机插值点梯度惩罚会迫使判别器在这个插值路径上保持 1-Lipschitz 约束。grad_outputstorch.ones_like(d_out)声明梯度来源create_graphTrue让这个梯度计算本身可被反向传播这是梯度惩罚能够参与训练的前提。训练循环的组织方式如下# 超参数 n_critic 5 gp_weight 10.0 l1_weight 10.0 lr_g 2e-4 lr_d 1e-4 for epoch in range(epochs): for real, noisy in loader: # 1. 更新判别器 n_critic 次 for _ in range(n_critic): fake G(noisy) d_real D(real, noisy) d_fake D(fake.detach(), noisy) gp gradient_penalty(D, real, fake, noisy) d_loss d_fake.mean() - d_real.mean() gp_weight * gp opt_d.zero_grad() d_loss.backward() opt_d.step() # 2. 更新生成器 1 次 fake G(noisy) g_adv -D(fake, noisy).mean() g_l1 F.l1_loss(fake, real) g_loss g_adv l1_weight * g_l1 opt_g.zero_grad() g_loss.backward() opt_g.step()d_loss d_fake.mean() - d_real.mean()是 Wasserstein 距离的近似目标。判别器要让真样本分数高、假样本分数低所以这个差值越小代表判别能力越强生成器的对抗损失取负让假样本分数尽量高。n_critic5 是 WGAN 训练里常见的更新比例即判别器每更新 5 次生成器才更新 1 次。这种做法能保证判别器不会在生成器还没学会的时候就崩掉。l1_weight 我在这里给 10.0如果你的输出波形看起来太模糊说明权重偏高可以降到 5如果残留噪声明显说明对抗约束还不够可以升到 20但不能超过生成器对抗损失的十倍量级。4.5 监控训练状态别只盯 loss要盯输出波形和判别器输出差训练过程中真正值得关注的有四个量生成器总损失、生成器 L1 损失、判别器对真实样本和假样本的均值差以及最关键但最容易被忽略的——生成器输出波形的目视检查。每 5 个 epoch 保存一次输出样例对比含噪输入、干净标签和生成结果看 P 波和 T 波是否还在。如果判别器的d_real.mean()和d_fake.mean()始终贴在一起说明判别器已经失去区分能力如果一方瞬间压倒另一方说明训练失衡。我一般会在脚本里加一个简单的打印逻辑print(fEpoch {epoch} | G_adv {g_adv.item():.3f} | G_l1 {g_l1.item():.3f} | D_real {d_real.mean().item():.3f} | D_fake {d_fake.mean().item():.3f})D_real和D_fake的差值可以理解为当前 Wasserstein 距离估计它会先增大后收敛。如果某个时刻开始剧烈震荡优先排查学习率和信号归一化范围不要急着调网络结构。5. 避坑指南GAN 收益欠佳时先查这五处每一条都是赔过时间换来的这一章只写踩坑记录按“现象 → 原因 → 解决”的顺序写每一条都可以对照自己的训练日志快速排查。5.1 现象十几个 epoch 后生成器输出变成全零原因通常是判别器先收敛生成器的对抗梯度消失。BCE 损失下判别器很容易达到饱和区WGAN-GP 相对好些但如果判别器学习率比生成器大太多同样会提前崩。我排查时会先看D_real和D_fake的均值差如果这个差快速拉大超过 20基本就是这个原因。解决把判别器学习率先降到生成器的二分之一比如生成器 2e-4、判别器 1e-4增加 n_critic 到 5给生成器更多学习时间同时检查输入信号是否归一化到 [-1,1]。如果输出还是全零把生成器的 BatchNorm 全部去掉再试一轮有些时候是批大小太小导致 BatchNorm 统计量不稳。5.2 现象P 波和 T 波被磨平QRS 振幅变低这是 L1 内容损失权重过高的典型症状。模型发现与其冒险恢复细节不如输出一个中间形态因为任何偏离都会提高 L1 损失。判别器虽然试图对抗这种模糊化但内容损失权重一旦压过对抗损失生成器就会阳奉阴违。解决把 l1_weight 从 100 降到 10 或更低。如果波形整体还是偏保守可以尝试在内容损失中加入一阶差分项比如F.l1_loss(fake[:, :, 1:] - fake[:, :, :-1], real[:, :, 1:] - real[:, :, :-1])让模型额外关注波形斜率变化避免细节被过度平滑。这个技巧在我自己测试里对保留 QRS 波群形态效果明显。5.3 现象合成噪声上指标很好真实设备数据上一塌糊涂这是噪声分布不一致导致的泛化崩溃。训练时用的高斯噪声太“干净”真实环境里的工频相位在漂移、基线漂移不是正弦叠加而是随机游走、肌电噪声不是频带白噪声而是有脉冲特性。解决构造噪声时把随机种子打散每次迭代重新生成不同相位和幅度的噪声而不是固定一份噪声文件。同时按 SNR 从 5 到 15 dB 随机采样让模型见过不同程度退化。如果真实数据量充足标准做法是在预训练后用少量真实带噪片段对模型做微调只用 L1 损失不加对抗损失防止微调阶段震荡。5.4 现象损失一直在降验证集 SNR 反而变差先怀疑数据泄漏。之前在 3.4 里强调过按记录隔离这里再补一个隐蔽案例如果你把一个病人的 10 秒信号切成 5 段随机放进了训练集和验证集那模型相当于见过同一波的临近片段验证集结果必然虚高。另一种可能是一个病人出现在训练集另一个病情极度相似的病人出现在验证集形态太像也会让验证指标失真。解决严格按记录 ID 分组宁可验证集只有少数几条记录。同时每次评估前用互相关对齐输出和标签因为生成器偶尔会在时间轴上产生微小移位直接影响 SNR 计算。5.5 现象50 Hz 工频没消干净反而多出 100 Hz 谐波这是生成器非线性的副作用它对周期噪声不是直接滤除而是在某些片段“照抄”在另一些片段用数值近似结果产生谐波串。另一个相关原因是 50 Hz 在 2048 点窗口内只有约 17 个周期判别器对周期结构的判断不如对 QRS 突变敏感所以模型偷懒。解决不要指望 GAN 单独吃掉工频。建议在数据预处理阶段先用窄带陷波器把 50 Hz 和 100 Hz 分量压掉一部分再让 GAN 处理残余噪声和肌电干扰。这一条完全符合工程直觉传统滤波擅长处理频段固定、窄带的干扰GAN 擅长处理频谱重叠、形态复杂的干扰两者结合比单独用任何一个都稳。6. 性能分析这样写才站得住三个指标、对照组设计和一个必调参数表性能分析不是最后拿一个 SNR 数字就完事而是要让审稿人或同事看完之后相信这套方案可复现、可信度够高。这一章讲清楚指标怎么选、对照怎么做、参数怎么调。6.1 评估指标SNR 改善量、RMSE、相关系数和功率谱密度指标计算方式注意点SNR 改善量输出 SNR 减输入 SNR必须同一段信号计算单位 dBRMSE输出与干净参考逐点均方根误差对幅度尺度敏感报告前先说明归一化方式相关系数输出与干净参考的 Pearson 系数对整体形态相似度敏感对局部失真不敏感功率谱密度对比降噪前后频谱残差看 50 Hz 处是否还有残余峰值SNR 改善量是最直观的指标但它容易被“过度平滑”骗到模型把信号也一起抹平噪声确实少了信号也弱了SNR 反而会虚高。所以必须结合 RMSE 和相关系数看。相关系数只反映整体波形相似度对局部 ST 段偏移不敏感如果有临床分析需求还要额外看 ST 段重建误差。功率谱密度是一个很好的补充在频域里看 50 Hz 工频峰是否真正消失100 Hz 谐波是否新增这是时域指标看不出来的。6.2 对照组设计四种方法、三种噪声、三种信噪比我建议至少跑四组对照传统带通滤波0.5~45 Hz、小波阈值降噪、纯 L1 回归网络把生成器里的对抗损失去掉、完整 GAN。每种方法在同一批测试记录上跑按噪声类型分组报结果至少覆盖工频主导、基线漂移主导、混合噪声三种场景信噪比按 5 dB、10 dB、15 dB 三档。每个配置用三个随机种子重复报告均值和标准差。这样设计的好处是能直接回答三个问题GAN 是否优于传统方法对抗损失是否真的比纯 L1 有用在哪种噪声类型上优势最大6.3 必调的三个参数L1 权重、梯度惩罚系数、判别器更新次数参数症状线索调整方向l1_weight输出模糊P 波消失降低到 5~10gp_weight训练震荡、梯度范数失控从 10 降到 5或检查信号幅度是否超过 ±1n_critic判别器快速收敛生成器跟不上从 5 降到 3保持生成器更新频率lr_dD_real/D_fake 差距跳跃过大调低到生成器的 1/2我自己的习惯是先用 256 点短片段把全流程跑通确认没有 NaN、没有全零输出再逐步加长到 2048 点。早期直接在 2048 点上训练三天查不明一个训练崩坏的问题换成短片段后一次定位到是梯度惩罚里create_graph没置位。这种问题在长序列上更难发现。做这个方向建议你从一套最小脚本起步确认数据流和梯度流都正常再加对抗损失、加多尺度一步步把性能堆上去。希望帮到你。本文还有配套的精品资源点击获取