返回列表

13th Place Solution

686. PhysioNet - Digitization of ECG Images | physionet-ecg-image-digitization

开始: 2025-10-21 结束: 2026-01-22 医学影像分析 数据算法赛
第 13 名解决方案

第 13 名解决方案

作者: Maruichi01
发布时间: 2026-01-23
成绩: 公共 LB: 21.62 dB | 私有 LB: 21.46 dB

感谢组织者举办这场有趣的比赛!以下是我们方法的总结。


概述

我们的解决方案基于一个端到端 (End-to-End, E2E) 深度学习 pipeline,直接从 ECG 图像预测 ECG 时间序列信号。关键的洞察是将图像分割与信号 refinement 结合在一个可微分的 pipeline 中。


Pipeline 架构

Pipeline Architecture
ECG 图像 (阶段 1 预处理)
    │  [H=1280, W=5600, C=3]
    ↓
阶段 2: UNet 分割 (4 通道软 mask)
    │  [H=1280, W=5600, C=4]
    │  ← 辅助损失 1: 分割 BCE 损失
    ↓
可微分质心提取 (mask → 1D 信号)
    │  [C=4, L=10250]
    │  ← 辅助损失 2: 信号质心 L1 损失
    ↓
阶段 3: 1D ResUNet  refinement
    │  [C=4, L=10250]
    │  ← 主要损失:L1 + SNR 损失
    ↓
重采样集成 (decimate_fir + polyphase + linear)
    │  [C=4, L=fs×10] (例如 5000 for fs=500Hz)
    ↓
最终 ECG 信号 (12 leads)

阶段 0/阶段 1: 预处理 (来自 hengck23 的 notebook)

  • 我们使用了 hengck23 优秀的公开 notebook 进行预处理
  • 包括图像旋转校正、裁剪,并调整大小为 5600×1280

阶段 2: UNet 分割

  • Encoder: EfficientNet-B3/B4 或 ResNet34 (timm 预训练)
  • Decoder: 带有坐标通道的 UNet (CoordConv 风格)
  • 输出: 4 通道软分割 mask (每行 ECG 一个通道)
  • 损失: BCE,pos_weight=10 用于处理类别不平衡

可微分质心转换

这是一个关键组件,实现了端到端训练:

# 对于每一列,计算概率分布的加权质心
y_coords = torch.arange(H).view(1, 1, H, 1)
prob_sum = seg_prob.sum(dim=2) + eps
centroid_y = (seg_prob * y_coords).sum(dim=2) / prob_sum

# 将像素位置转换为 mV
signal_mv = (base_y_position - centroid_y) / y_scale

这允许梯度从信号损失流回分割网络。

阶段 3: 1D ResUNet

  • 架构: 残差 1D UNet
  • 输入/输出: 4 通道 (对应 4 行 ECG)
  • 深度: 4-5 层
  • 目的: refinement 质心提取的信号,修正伪影

训练策略

损失函数

结合分割和信号损失,并进行调度加权:

# 早期训练:专注于分割
# 后期训练:转向信号质量
seg_weight = seg_start + (seg_end - seg_start) * progress
signal_weight = sig_start + (sig_end - sig_start) * progress

loss = seg_weight * seg_bce_loss + signal_weight * (l1_loss + snr_loss)

关键训练细节

  • 优化器: AdamW (lr=0.005, weight_decay=1e-4)
  • 调度器: Cosine annealing (60 epochs 计划,32 epochs 训练)
  • Batch size: 4 配合梯度累积 (有效 batch 16)
  • 精度: FP32 (FP16 会导致 NaN 问题)
  • EMA: 启用 (decay=0.995)
  • 全数据训练: 最终模型无验证集分割

数据增强

增强对于泛化至关重要:

增强方法 概率 影响
水平翻转 (Horizontal Flip) 0.5 +0.6 dB (最大改进!)
灰度 (Grayscale) 0.2 帮助应对颜色变化
亮度/对比度 (Brightness/Contrast) 0.3 对光照的鲁棒性
JPEG 压缩 (JPEG Compression) 0.2 处理低质量扫描
切割 (Cutout) 0.2 处理遮挡/损坏
高斯噪声 (Gaussian Noise) 0.2 处理传感器噪声

水平翻转增强出人意料地有效 (+0.6 dB),可能是因为它使有效训练数据翻倍,并帮助模型学习方向不变的特征。


集成策略

模型多样性

我们训练了多个具有不同配置的模型:

模型 Encoder 阶段 3 水平翻转 LB 分数 (无重采样集成)
m009 ResNet34 resunet1d (d=4) 0.0 20.02
m010 EfficientNet-B3 resunet1d (d=4) 0.0 20.21
m013 ResNet34 resunet1d (d=4) 0.5 20.64
m014 EfficientNet-B3 resunet1d (d=4) 0.5 20.84
m018 EfficientNet-B3 resunet1d (d=5) 0.5 20.87
m020 EfficientNet-B4 resunet1d (d=4) 0.5 20.61

最终集成

models = ['m009', 'm010', 'm013', 'm014', 'm018', 'm020']
weights = [0.05, 0.1, 0.2, 0.25, 0.25, 0.15]

基于单模型性能的加权平均,并对不同架构给予轻微的多样性奖励。

重采样集成

一个微妙但有效的技术:在将模型输出长度转换为目标采样频率时,集成多种重采样方法

ensemble_methods = ['decimate_fir', 'polyphase', 'linear']

不同的重采样算法会引入不同的伪影(尤其是在信号边缘)。对它们进行平均可以消除特定方法的伪影。


有效的方法

  1. 端到端训练 - 联合优化分割和信号提取优于单独阶段
  2. 水平翻转增强 - 出人意料地带来了 +0.6 dB 的改进
  3. EfficientNet 编码器 - consistently 比 ResNet 高 +0.2 dB
  4. 更深的阶段 3 - depth=5 有助于捕捉更长范围的依赖关系
  5. 重采样集成 - 减少了边缘伪影
  6. 集成中的模型多样性 - 混合架构和增强设置

无效的方法

  1. 阶段 3 使用 BiLSTM - 比 ResUNet 差 (-0.9 dB)
  2. 更高分辨率 (7200×1280) - 无改进,训练更慢
  3. 更大的 output_length (15000) - 改进微小,不值得增加复杂度
  4. ConvNeXt 编码器 - 训练不稳定问题

致谢

非常感谢 @hengck23 提供的优秀公开 notebooks!我们的阶段 1 预处理完全基于 hengck23 的工作,这为我们的 pipeline 提供了基础。阶段 1 提供的干净、标准化图像对于有效训练我们的 E2E 模型至关重要。

同时也感谢 Kaggle 社区在整个比赛期间提供的富有洞察力的讨论。

同比赛其他方案