返回列表

9th place solution

672. RSNA Intracranial Aneurysm Detection | rsna-intracranial-aneurysm-detection

开始: 2025-07-28 结束: 2025-10-14 医学影像分析 数据算法赛
第 9 名解决方案 - RSNA 颅内动脉瘤检测

第 9 名解决方案

副标题:Vibes and Genius Trade-Off

作者: Tom (@tom99763), Sergio Alvarez (@sersasj), IAmParadox (@iamparadox), fate (@chihantsai), atom1231 (@atom1231)

发布日期: 2025-10-15

竞赛排名: 第 9 名

团队: Vibes and Genius Trade-Off

致谢

首先,我们要感谢竞赛主办方 @evancalabrese, @shosys 以及每一位参与数据准备和竞赛相关流程的 RSNA 成员,同时也感谢 Kaggle 工作人员组织本次竞赛。

下面,我们将介绍团队 Vibes and Genius Trade-Off 的解决方案!

简述 (TL;DR)

我们的解决方案结合了三种互补的方法:

  • 使用不同骨干网络的 YOLO 2.5D。源自 BYU 竞赛
  • 带有 2D Effv2s 提取器的 3D CenterNet。
  • 元分类器 (LightGBM, XGBoost, CatBoost)

最终概率是通过平均 YOLO 2.5D 模型、带有 2D Effv2s 提取器的 3D CenterNet 以及三个元分类器的输出获得的。

以下是我们方法的图表。

我们使用了两种不同版本的 YOLO:

  1. YOLOv11m - 来自 Ultralytcs 的标准 YOLO11 中等模型
  2. 带有 timm 骨干的自定义 YOLO - 使用 timm/tf_efficientnetv2_s.in21k_ft_in1k 作为骨干的 YOLO 架构

关于 YOLO+timm 自定义的详细信息,请参阅 BYU _writeup

我们将 13 个血管位置视为单独的边界框类别

  • 左/右床突下颈内动脉
  • 左/右床突上颈内动脉
  • 左/右大脑中动脉
  • 前交通动脉
  • 左/右大脑前动脉
  • 左/右后交通动脉
  • 基底动脉尖
  • 其他后循环

2.5D 策略

图像被调整为 512×512,使用最小 - 最大值归一化,并转换为 2.5D 变体:

R (红通道) = 切片 i-1
G (绿通道) = 切片 i
B (蓝通道) = 切片 i+1

下图展示了 2D 与 2.5D 变体的切片示例:

2D vs 2.5D 示例

正如示例中所示,图像差异很大,因为我们没有标准化 Z 轴间距

我 (@sersasj) 花了 1-2 周时间尝试 Z 轴调整大小,但结果一直更差。也许是我做错了什么,但没有时间进一步调查。

我们非常不愿意在没有 proper Z-spacing resize 的情况下尝试 2.5D(在我看来这不是个好主意 - @sersasj)。尽管如此,即使没有 Z 轴重采样,2.5D 方法也在 CV 中提高了 0.02+ 的结果。

训练配置

数据采样策略:

对于负样本,我们从每个序列中均匀采样 10 个切片。

例如:对于 100 切片的序列 → 切片 [1, 11, 22, 33, 44, 56, 67, 78, 89, 99]

对于正样本,我们使用了所有包含标注的切片

带有 timm/tf_efficientnetv2_s 骨干的 YOLO

batch_size: 16
epochs: 50
mixup: 0.4
mosaic: 0.4
drop_path_rate: 0.2 
cls_loss: 1.0
optimizer: AdamW
momentum: 0.9
learning_rate: auto

YOLOv11m

batch_size: 32
epochs: 80
mixup: 0.4
mosaic: 0.4
droput: 0.3
cls_loss: 1.0
optimizer: AdamW
momentum: 0.9
learning_rate: auto

我们还修改了适应度函数以包含 AUC 指标并优先考虑 mAP@50。

fitness = 0.5 × mAP@50 + 0.25 × mAP@50-95 + 0.25 × mAUC

这有帮助吗?也许有一点。在 BYU 竞赛中,我们已经发现优先考虑 mAP@50 而不是 Ultralytics 标准的 1.0×mAP@50-95 能带来更好的结果。

添加 AUC 当时看起来是个好主意,因为它是竞赛指标,但我们没有彻底调查它是否显著提高了性能。好处可能是微乎其微的,但为了保持一致性我们保留了它。

训练硬件和时间

硬件设置 GPU CPU 内存 每折时间 总计 (5 折)
@sersasj RTX 3090 Intel Core i5-12400F (12) @ 4.4GHz 32GB 4-5 小时 ~24 小时
@iamparadox RTX 4090 AMD Ryzen 9 7950X (32) @ 5.883GHz 64GB ~2 小时 ~10 小时

推理

对于推理,我们按空间位置对 DICOM 切片进行排序 (SliceLocation → ImagePositionPatient → InstanceNumber),为切片 1 到 N-1 创建 2.5D 三元组。
在两个模型上运行批量推理,提取每个类别的最大置信度,并使用 max(localization_confidences) 作为整体动脉瘤存在概率。
该过程可以在下面的 gif 中观察到:

推理过程 GIF

交叉验证分数

使用 MultilabelStratifiedKFold 对折进行分层,以确保在以下方面分布平衡:

  • 动脉瘤存在
  • 所有 13 个血管位置
  • 模态

YOLO11m 2.5D 结果

折 (Fold) loc_macro_auc cls_auc combined_mean
fold00.81840.75490.7867
fold10.81140.78970.8005
fold20.81340.78270.7981
fold30.81620.78720.8017
fold40.85400.82810.8410
平均0.82270.78850.8056

EfficientNetV2-S 2.5D 结果

折 (Fold) loc_macro_auc cls_auc combined_mean
fold00.79780.77900.7884
fold10.81590.82690.8214
fold20.81550.79210.8038
fold30.81530.79070.8030
fold40.84990.85600.8529
平均0.81890.80900.8139

集成结果

折 (Fold) loc_macro_auc cls_auc combined_mean
fold00.83930.78490.8121
fold10.83300.82910.8310
fold20.83990.81350.8267
fold30.83700.80930.8232
fold40.87380.86200.8679
平均0.84460.81980.8322

未生效的方法:

在 YOLO 开发的初始阶段,@sersasj 创建了一个集成,包含 3xYolo11m 和 0.69LB 的 EfficientNetB2 公开笔记本。这给了我们 0.78 的 LB 分数,并在竞赛早期阶段将我们置于前 3 名。

在稍微探测 leaderboard 之后,@iamparadox 发现 YOLO 的动脉瘤分类 AUROC 非常低(约 ~0.58)。然后我们开始开发能够补充 YOLO 并提高动脉瘤分类 AUROC 的模型。(我们后来发现,早期 YOLO 开发结果不佳是由于训练中负样本数量不足。)

我们使用的一类模型是仅检测给定序列中动脉瘤存在的 2.5D 模型,带有辅助分割头。这些模型灵感来自 https://www.kaggle.com/code/hengck23/3d-unet-using-2d-image-encoder。这些模型与 yolo11m 结合给了我们 0.80 的 LB。

当我们与 @chihantsai, @atom1231 合并后,这些模型变得过时了。

在 BYU 竞赛中,我们表现最好的骨干是 timm/convnextv2_base.fcmae_ft_in22k_in1k。然而,在本次竞赛中,使用半精度训练导致损失迅速变为 NaN。根据我们的调查,这似乎是 ConvNeXt 架构的常见问题。我们假设如果在全精度下训练,该骨干可以实现与 YOLO11m 和 timm/tf_efficientnetv2_s.in21k_ft_in1k 相当的性能。不幸的是,由于时间限制,我们无法验证这一点。

修改 neck 和 head 也被考虑过。根据我们与 @tatamikenn 的 出色的逆向工程笔记本 的调查,P3 特征似乎是最常用的。我们在单个折上进行了快速测试,但验证结果大致相同,所以我们决定继续前进。

特征利用图

我们还尝试使用我们的 3D 分割结果来 过滤掉 YOLO 的无效位置预测,但召回率下降了很多。
此外,我们尝试使用 YOLO 提取补丁,并在预测类别之前对补丁应用小波和 log-polar 变换;然而,这种方法 没有产生好的结果。

EfficientV2s + 3D-CenterNet (Flayer)

这是一个高度实验性的项目,在 AI 的大量帮助下完成。我们从流行的"LB 0.69 公开笔记本”开始,从中重建了整个训练管道和数据预处理,然后结合了CenterNet -like 机制来检测动脉瘤的中心

模型架构

我们采用切片式 2D 编码器 + 浅层 3D 头设计:

  • 编码器。 来自 timm 的 2D ImageNet 预训练 EfficientNetV2-S (tf_efficientnetv2_s.in21k_ft_in1k),处于 features_only 模式,使用倒数第二个特征图 (out_indices = (1,-2))。
  • 辅助 2D 头。 中间特征图(早期阶段)被送入轻量级 2D 卷积头以产生每切片辅助 logits,这鼓励编码器在 3D 聚合之前捕获血管相关的空间线索。辅助头使用切片级目标进行监督以稳定早期层学习。
  • 2D→3D 融合。 每个体积 (B, C=1, D, H, W)重排为 B·D 个 2D 切片,独立编码,然后在 3D 头之前重新组装(B, C_feat, D, H′, W′)。如果骨干需要 3 个通道,单通道输入会被通道重复。
  • 3D 时间头。 轻量级头 (Conv3d-BN-ReLU ×2, base_channels) 聚合深度信号。
  • 输出。 两个 1×1×1 卷积头:
    • 热力图头: num_classes = 13 血管位置 (3D 中心图)
    • 偏移头: 3 通道亚体素偏移 (dz, dy, dx)

监督目标

  • 在每个类别的特定热力图上的每个标注中心放置一个3D 高斯峰(步长感知)。
  • 在最近的网格单元存储亚体素残差偏移,并用偏移掩码标记它们。
  • 将坐标从原始序列大小缩放到当前 (D, H, W);使用 (stride_d=1, stride_h=stride_w=16) 映射到输出网格,因此输出为 D × H/16 × W/16
  • 默认高斯 σ = 0.2

分割掩码

血管掩码是在物理空间中通过训练 3D DynUNet 生成的,体积重采样到 0.7 mm 各向同性间距。提供的 13 个血管类别的体素级标注被合并为单个二值血管掩码用于训练。生成的 3D 掩码随后被转换为切片式 2D 掩码以监督辅助头。

损失函数

我们使用四项进行训练并加权求和:

  • CenterNet 风格 focal loss (权重 = 1.0) 在热力图上 (α=2, β=4)。
  • L1 偏移损失 (权重 = 1.0) (仅在有效中心单元)。
  • BCE-with-logits 分类损失 (权重 = 1.0) 来自序列级 logits。
  • 辅助 Dice & BCE 损失 (权重 = 0.5) 在 2D 辅助输出上(当有血管分割掩码时);否则设为零,鼓励切片级特征一致性。

从体素图到序列级 logits (用于 AUC & 集成)

给定热力图 logits H ∈ ℝ^{B×13×D×H′×W′}

  • 每类序列 logits: 对 13 个类别中的每一个在 (D, H′, W′) 上进行空间最大值。

  • 动脉瘤存在 (全局) logit: 13 个类别 logits 的最大值

    14 个 logits 驱动 BCE 损失并导出用于元集成。

数据管道 & 增强

  • 输入。 预计算的 .npy 体积;(D,H,W) 扩展为 (1,D,H,W)— 这里 D = 64。
  • 标签缩放。 将坐标缩放到当前体积大小;应用步长映射以进行目标网格。
  • 增强。 每个体积的共享 2D 仿射变换(所有切片相同的变换):随机旋转、缩放、平移;垂直翻转可用(默认关闭)。
  • 归一化。 Albumentations 归一化 → 张量转换;如果需要则通道重复为 3 通道。

优化 & 调度

  • 优化器。 AdamW (LR=2e-4, weight decay=1e-5), AMP 混合精度,梯度裁剪 (=1.0)。
  • LR 调度。 默认 CosineAnnealingLR (T_max = epochs, eta_min = 1e-6);也支持 StepLR / ReduceLROnPlateau
  • 检查点。 最佳模型由验证集上的平均 AUC 选择。

训练配置

  • Epochs: 16
  • Batch size: 4 (训练) / 2 (验证)
  • accumulate: 2
  • Workers: 16
  • Strides: 深度 1, 横向 16
  • Gaussian σ: 0.2
  • Backbone: EfficientNetV2-S, pretrained=True, 默认不冻结

交叉验证 & 指标

  • 由训练 CSV 拆分驱动的 K-fold 协议。
  • 计算每标签 ROC-AUC,以及每个 epoch 的平均存在加权 AUC。
  • 每个 fold 的调度器步进和按平均 AUC 保存最佳模型;最终总结报告每折和平均 AUC。

Flayer 结果 (无辅助头)

折 (Fold) loc_macro_auc cls_auc combined_mean
fold00.77620.77470.7754
fold10.76760.80690.7873
fold20.75080.78920.7700
fold30.75530.77780.7666
fold40.77340.77580.7746
平均0.76470.78480.7748

Flayer 结果 (有辅助头)

折 (Fold) loc_macro_auc cls_auc combined_mean
fold00.77790.79910.7885
fold10.78280.79870.7908
fold20.76010.80510.7826
fold30.76830.78260.7755
fold40.75860.79540.7770
平均0.76950.79620.7829

总结

Flayer 使用浅层 3D 头将 2D EfficientNetV2-S 切片特征转换为 3D 证据图,使用 CenterNet 风格的热力图 + 偏移进行监督,并生成稳健的14-logit 序列级预测(包括聚合的动脉瘤存在 logit),这些预测可以 cleanly 插入我们的堆叠元分类器中。

元分类器

我们设计了一个堆叠集成架构来整合来自 YOLO11m, YOLO11-EffV2s, 和 EffV2s-3D-CenterNet 模型的预测。具体来说,每个元分类器接收来自所有基础模型的连接预测作为输入,并为动脉瘤存在特定位置检测生成最终预测。这种设置使模型能够通过考虑所有模型中所有位置的预测来评估给定位置的动脉瘤存在,有效地捕捉集成对动脉瘤存在的集体推理。

# 对于每个 fold_id
metadata = np.array([age, sex])
X = np.concatenate([np.array([yolo11m_cls_preds[fold_id]]), yolo11m_loc_preds[fold_id],
                    np.array([effv2s_cls_preds[fold_id]]), effv2s_loc_preds[fold_id],
                    flayer_fold_preds[fold_id], metadata], axis=0)[None, :]
lgb_pred = predict_prob_lgb(X, fold_id) #(14, )
xgb_pred = predict_prob_xgb(X, fold_id)
cat_pred = predict_prob_cat(X, fold_id)

我们尝试了两种集成结构:

  • 串联 (Series): 单个 YOLO 预测与一个 Flayer 预测组合后送入集成模型以产生最终输出。
    (30 特征,[yolo(14) + flayer(14) + meta(2)])
  • 并联 (Parallel): 来自两个 YOLO 的两个预测和来自 Flayer 的一个预测并行处理,并由集成模型共同使用以生成最终预测。
    (44 特征,[yolo11m(14) + yolo_effnets(14)+ flayer(14) + meta(2)])
元模型图表

这种比较允许我们评估在并行中组合多个特征流是否提供了超越顺序(串联)配置的额外互补信息。

我们发现,在并行中组合来自不同 YOLO 骨干和 flayer 的预测比在集成 CV 分数中的串联提供了更好的改进。这表明 GBDT 有效地利用了每个模型对跨位置动脉瘤存在的感知来做出准确判断。由于最后一天遇到的问题,我们未能完成 5 折提交。因此,我们在此展示我们最终选择的结果,该结果基于 2 折的平均值。

串联 (Series) 并联 (Parallel)
5 折 CV (平均)0.840.852
5 折 CV (Nelder-Mead 优化)0.8410.858
公共分数 (平均; 2 折)0.830.83082
私有分数 (平均; 2 折)0.810.8230

我们之前也在 Tom 的 BYU 方法 中尝试过基于关键点的双图预测器,发现它在集成 CV 性能方面表现良好。然而,基于 YOLO 的特征提取和预处理太耗时,经常导致超时。因此,我们没有将此方法包含在最终解决方案中。

同比赛其他方案