返回列表

[2nd place] Solution Overview & Code

404. RSNA STR Pulmonary Embolism Detection | rsna-str-pulmonary-embolism-detection

开始: 2020-09-10 结束: 2020-10-26 医学影像分析 数据算法赛
[第二名] 解决方案概述与代码

[第二名] 解决方案概述与代码

作者:Ian Pan (Grandmaster)
比赛:RSNA-STR Pulmonary Embolism Detection

更新:代码已发布在 https://github.com/i-pan/kaggle-rsna-pe

恭喜所有参赛者和获奖者。特别祝贺获奖者 @osciiart@jpbremer,他们正走在成为医师 Grandmaster 的道路上。鉴于数据量大且时间紧迫,这是一场艰难且计算量巨大的挑战。我的设置是 4 张 24 GB 的 Quadro RTX 6000 GPU。在这样的比赛中,拥有强大的计算资源让我感到有些愧疚。模型使用 PyTorch 1.6 中的 DDP 和自动混合精度进行训练。

尽管结果尚未最终确定,而且我的提交可能因违反标签一致性要求而被移除(希望我的修复启发式方法奏效了!),我仍然想分享我的解决方案。

解决方案示意图

这是我的解决方案示意图。它包含许多组件,所以如果总结很长,我深表歉意。

步骤 1:特征提取

去年的 RSNA 颅内出血检测挑战赛与今年的挑战赛有很多相似之处。大多数顶尖解决方案结合了 2D CNN 特征提取和序列建模。我的解决方案骨干也依赖于类似的设置。

我首先在 2D 图像上训练 ResNeSt50。图像使用我在一篇关于 DICOM 处理的初始帖子中提到的 PE 特定窗口(WL=100, WW=700)进行窗位处理。每张“图像”有 3 个通道,每个通道代表一个单独的切片。因此,2D 图像是由 3 个连续切片堆叠而成的。目标是 7 个 PE 相关标签(即排除 RV/LV 比率标签)。注意:大多数 PE 相关标签是检查级别的标签。然而,我只是将检查标签分配给每个 PE 阳性的切片(阴性切片全为零),并理解这会引入标签噪声。此外,我预测图像中 3 个切片中间那个切片的标签。模型使用普通的二元交叉熵损失(PyTorch 中的 BCEWithLogitsLoss),512x512 输入并进行 448x448 的随机裁剪(推理时为单中心裁剪),RandAugment 数据增强,批次大小 128,5 个 epochs,每个 epoch 2500 步,使用 RAdam 优化器和余弦退火学习率调度器。我使用了广义平均池化,并将最终特征向量缩减为 512 维。平均损失约为 0.06(PE 与无 PE 的切片级预测 AUC 为 0.95-0.96)。然后提取所有切片的特征。

步骤 2:序列建模

去年的许多解决方案选择 LSTMs/GRUs 作为序列模型。在这次比赛中,我使用了 huggingface transformers(具体来说是 transformers.modeling_distilbert 中的 Transformer 类)。我使用一个 4 层 transformer 来生成切片级的 pe_present_on_image 预测,另一个 transformer 来预测检查级的 PE 标签。训练时序列长度为 512,根据需要进行填充/截断。在推理过程中,我使用未经修改的序列。

重要的一点:来自非 PE 检查的图像不会计入损失。起初,我在所有检查上训练切片级 transformer。后来,我决定只在阳性检查上训练这些模型。这使我的 CV 降低了约 0.01-0.02。我使用了一个自定义加权损失,根据阳性 PE 切片的比例(如指标中所述)对每个示例的损失进行加权,尽管我仍然不确定我写得是否正确。

检查级 transformer 使用基于比赛标签权重的加权 BCE 损失进行训练。检查级验证损失范围在 0.15-0.17 之间。

步骤 3:时间分布 CNN (Time-Distributed CNN)

为了增加建模的多样性,我随后训练了一个时间分布 CNN,这只是另一种说法,意思是我将一个 transformer 堆叠在 CNN 特征提取骨干之上并进行端到端训练。

但在那之前,我使用切片级 transformer 模型进行推理,以获取每个切片的 PE 分数(5 折 OOF 预测)。然后,在训练 TD-CNN 时,我只在每个检查中 PE 分数排名前 30% 的切片上进行训练。这些是在大小为 32x416x416 的 3D 体积上训练的,裁剪为 32x364x364,使用相同的

同比赛其他方案