返回列表

6th Place Solution for the HMS - Harmful Brain Activity Classification Competition

600. HMS - Harmful Brain Activity Classification | hms-harmful-brain-activity-classification

开始: 2024-01-09 结束: 2024-04-08 临床决策支持 数据算法赛
作者:Quan Vu
竞赛排名:第 6 名
发布日期:2024-04-10

HMS - 有害脑活动分类竞赛 第六名方案

我简直不敢相信能够通过每天一次实验,仅用少量实验就获得了一枚单人金牌。我现在非常开心,也非常感谢 Kaggle 社区的每一位分享者。感谢 Chris Deotte 为大家分享的精彩内容,也感谢 seanbearden 提供的两阶段训练思路。

1. 背景

2. 方法概览

在初期,我尝试了 Kaggle 频谱图,随后是 Kaggle + EEG 频谱图,再往后是 Kaggle + EEG 频谱图 + 原始 50 秒 EEG。最终,我采用了多模态模型,使用 4 种输入:Kaggle 频谱图、EEG 频谱图、原始 50 秒 EEG 数据和原始 10 秒 EEG 数据。模型包含 4 个独立的 backbone,分别处理各输入。我使用 50 秒(20 秒)原始 EEG 数据提取全局特征,使用 10 秒原始 EEG 数据提取局部特征。

2.1 交叉验证划分

我使用 GroupKFold,以 patient_id 作为分组依据。

2.2 数据预处理

Kaggle 频谱图

我对 Kaggle 频谱图进行裁剪并做对数变换:

spec_img = np.clip(spec_img, np.exp(-4), np.exp(8))
spec_img = np.log(spec_img)
spec_img = np.nan_to_num(spec_img, nan=0.0)

使用均值和标准差进行归一化。

生成 EEG 频谱图

我使用 4 条链路,每条链路包含 4 个特征:

Fp1-F7, F7-T3, T3-T5, T5-O1
Fp1-F3, F3-C3, C3-P3, P3-O1
Fp2-F8, F8-T4, T4-T6, T6-O2
Fp2-F4, F4-C4, C4-P4, P4-O2

版本 1

  • 根据 eeg_label_offset_seconds 裁剪原始 EEG 片段,并使用 signal.spectrogram 生成频谱图。将 16 张形状为 (128, 142) 的图像拼接成 4×4 的图像,形状为 (512, 568)。
  • 使用对数变换并除以 2.0 进行归一化。
  • 在送入 ViT 之前,将图像 resize 为 (518, 518, 1)。

版本 2

  • 与版本 1 相同,只是将 nperseg 从 70 改为 39,以生成更大的图像,形状为 (128, 256)。4×4 图像的新形状为 (512, 1024)。
  • 在送入 ViT 时保持图像尺寸为 (512, 1024, 1)。

版本 3

  • 将同一条链路内的 4 个特征取平均,然后将 4 条链路图像拼接为 4×1 的图像,形状为 (512, 256)。
  • 在送入 ViT 之前,将图像 resize 为 (518, 518, 1)。
  • 根据经验,我发现版本 3 比版本 1 差,但在集成时 KL 损失略有下降。

生成全局原始 EEG 图像

版本 1

  • EEG_LENGTH = 50
  • 使用均值填充 NaN 值。
  • 使用 Butter 带通滤波器 [0.5, 40]。将新 EEG 裁剪到 (-1024, 1024)。将 EEG 形状从 (4, 10000) 重塑为 (4, 200, EEG_LENGTH)。随后将 4 幅 (200, EEG_LENGTH) 图像拼接为一幅 (200, 4×EEG_LENGTH) 的图像。最后将 4 条链路的图像拼接为单幅图像,形状为 (200, 4×4×EEG_LENGTH)。
  • 归一化方式为除以 104。
  • 在送入 ViT 前,将图像 resize 为 (518, 518, 1)。

版本 2

  • 与版本 1 相同,只是 EEG_LENGTH = 20。

生成局部原始 EEG 图像

  • 与全局原始 EEG 图像的版本 1 相同,只是 EEG_LENGTH = 10。

2.3 数据增强

  • 我使用 alpha=1.0 的 Mixup。
  • 对 Kaggle 频谱图和 EEG 频谱图使用 A.XYMasking
  • 对原始 EEG 数据使用自定义增强:随机向原始 EEG 数据(全局原始和局部原始)插入 NaN。

2.4 训练策略

我使用 Adan 优化器配合单周期学习率调度器,模型 EMA 衰减为 0.995,使用梯度检查点。学习率 LR = 0.00017。Backbone 采用 timm 库中的 dinov2 vit 系列。训练时每个 epoch 随机选取一个 eeg_label_offset_seconds;评估时使用每个 eeg_id 的第一个 eeg_label_offset_seconds

阶段 1

用 5 个 epoch 训练投票数 < 10 的数据。

阶段 2

用 7 个 epoch 训练投票数 ≥ 10 的数据。

3. 提交细节

3.1 测试时增强(TTA)

使用 3×TTA,针对 10 秒原始 EEG 输入:向左平移 2 秒、居中、向右平移 2 秒。3×TTA 仅提升了一点公开分数,约 < 0.01。

3.2 集成细节

模型 OOF OOF Kaggle 频谱 OOF EEG 频谱 OOF 全局原始 OOF 局部原始 Public Private
模型 1 0.221329 0.318149 0.267640 0.251836 0.264803 0.232427 0.289764
模型 2 0.217614 0.325917 0.266803 0.240895 0.264423 0.229686 0.287550
模型 3 0.217881 0.317720 0.268293 0.247168 0.263158 0.228330 0.285105
模型 4 0.218214 0.315174 0.264225 0.250879 0.263150 0.229865 0.290503
模型 5 0.223236 0.318931 0.334042 0.248258 0.261953 0.230250 0.287976
集成 0.22600 0.283302

我的方案不足之处是集成缺乏多样性。由于 GPU 显存限制,无法在所有 backbone 上使用 ViT‑Base 或 ViT‑Large。

3.3 提交策略

  • 策略 1:使用最佳 CV,在全部数据上进行训练和验证。
  • 策略 2:使用最佳 LB,在投票数 ≥ 10 的数据上进行训练和验证。

3.4 未成功的尝试

  • 我尝试过 Wavenet、1DCNN‑GRU、Squeezeformer、EEGNet、EEGConformer 等原始数据模型,但均未能超越 2D Vision Transformer 的表现。
  • 尝试将 4 条链路堆叠为 4 通道图像,但在 EEG 频谱图和原始 EEG 数据上未能成功。
  • 尝试对 EEG 频谱图和原始 EEG 数据按通道进行均值、标准差归一化。
  • 尝试使用低通滤波器及其他频段的 Butter 滤波器。
  • 尝试将原始 EEG 图像的原始尺寸直接送入 ViT,但 KL 损失更高。最终需要将原始 EEG 图像 resize 为 (518, 518, 1),更大的图像尺寸提升了 KL 损失。
  • 数据增强效果有限,除 Mixup 外其他增强几乎没有帮助。

参考资料

同比赛其他方案