返回列表

[3rd Place solution] Refine from Sparse to Dense

596. SenNet + HOA - Hacking the Human Vasculature in 3D | blood-vessel-segmentation

开始: 2023-11-07 结束: 2024-02-06 医学影像分析 数据算法赛
第三名解决方案:从稀疏到密集的优化

第三名解决方案:从稀疏到密集的优化

作者:ForcewithMe (Kaggle Grandmaster)

排名:3 / 723

发布时间:2024年2月7日

票数:69

首先,我要向组织者和官方Kaggle团队为策划这场出色的比赛表示感谢。我在比赛后期才加入。尽管有一些分割比赛的经验,但我必须感谢@hengck23@yoyobar@junkoda(指标实现),以及其他社区参与者,感谢他们的开源贡献和讨论,这让我能快速跟上比赛的节奏。

我的方法非常简单,完全依赖2D模型,在整个训练和推理流程中仅使用了smp(segmentation models pytorch)和timm(pytorch image models)。

全局关键点

  1. 从稀疏到密集的标签优化。
  2. 模拟私有测试集的放大倍数。
  3. 保持适当的分辨率。

1. 从稀疏到密集

由于一半的训练集具有密集标签(kidney 1,kidney 3 dense),另一半是稀疏的,利用密集标签来优化稀疏标签是关键步骤。整体流程如下:

  1. 使用kidney 1和kidney 3 dense训练UNet(maxViT512)和UNet(EfficientNetv2s)。
  2. 使用训练好的UNet maxViT512和UNet EfficientNetv2s模型为kidney 3 sparse生成补充标签。
  3. 使用kidney 1、kidney 3(dense、sparse加上补充标签)恢复UNet maxViT512和UNet EfficientNetv2s的训练,进行几个epoch。
  4. 在kidney 2上重复步骤2。
  5. 使用所有肾脏的真实标签和伪标签训练三个UNet模型(EfficientNetv2s、SeResNext101、MaxViT512)和一个UNet++。

注:由于组织者公开了kidney 3和kidney 2中注释的比例,我在选择伪标签的阈值时,尽量基于像素数量选择与官方比例尽可能接近的阈值。

2. 模拟私有测试集的放大倍数

我决定参加这场比赛的一个关键原因是主办方公开了训练集和测试集的放大倍数。训练集的放大倍数是50um/voxel,公开测试集同样是50um/voxel,而私有测试集是63um/voxel。更大的放大倍数意味着更低的分辨率。例如,一个600um的对象在训练集和公开测试集中会占据12个像素,但在验证集中只有10个像素。因此,在训练过程中,我将缩放中心设为0.8,而不是1,缩放范围设为0.55到1.05,以模拟私有测试集。

A.ShiftScaleRotate(shift_limit=0.3,
                    scale_limit=(-0.45, 0.05),  
                    rotate_limit=45,
                    # value=0,
                    border_mode=4,
                    p=0.95),

3. 保持适当的分辨率

在这场比赛中,分别沿x轴、y轴和z轴进行训练和推理是一个非常关键的trick。然而,这带来了显著的风险。整个测试集包含1500个切片,其中公开测试集占67%,私有测试集占33%。这意味着私有测试集仅包含约500个切片。沿z轴使用更高分辨率(例如1024)进行推理是可行的。但如果沿y轴或x轴推理,则意味着其中一条边只有500像素长。此时,如果模型和代码配置为更大的分辨率(比如1024),将存在巨大的分数下降风险。

我的模型主要运行在512的分辨率下,其中一个模型在切片具有适当分辨率时,会切换到大分辨率切片的更高分辨率权重。

模型 骨干网络 分辨率 Public Private
UNet MaxViT-Large 512 512 0.846 0.727(提交1)
UNet SeResNext 512 0.819 0.753
UNet Efficiennet_v2_s 448, 832 0.799 0.703
UNet++ Efficiennet_v2_l 512 0.817 0.692
ensemble - - 0.846 0.727(提交2)

4. 若收敛稳定则在全部数据上训练

在比赛初期,无论是在kidney 2还是kidney 3上进行验证,我观察到如果训练20个epoch,在最初的几个epoch之后,验证集上的dice系数(非surface dice)变化非常小,其中MaxVit512 large模型的波动最小。考虑到我们只有三个肾脏,鉴于收敛的稳定性,我在完成伪标签过程后,决定直接在所有肾脏上训练。

5. 最小化阈值的影响

我感谢@junkoda提供的指标计算方法。我最稳定的单模型能够在0.2的阈值范围内保持surface dice分数的极小波动(小于1)。在模型融合后,稳定的阈值范围可能在0.3~0.4之间。在分割比赛中,稳定的阈值极其关键。在这场比赛中,由于我的最终模型没有验证集,我不得不使用早期包含验证集的训练模型搜索到的阈值,并将其应用于最终版本。幸运的是,在完整数据集上训练的模型似乎拥有与早期在k1+k2(sparse)上训练、在k3上验证的模型非常接近的阈值。同时,阈值在kidney 3 dense、公开测试集和私有测试集之间的波动非常小。

6. 大量强度增强

正如@hengck23提到的,不同肾脏在强度上存在很大差异。因此我使用了大量的强度增强。

A.RandomBrightnessContrast(p=1.0),
A.RandomGamma(p=0.8),

7. 快速消融实验

模型 骨干网络 使用的关键点 Public Private
UNet MaxViT-Large 512 3, 5, 6 0.818 0.586
UNet MaxViT-Large 512 3, 4, 5, 6 0.857 0.633
UNet MaxViT-Large 512 2, 3, 4, 5, 6 0.849 0.652
UNet MaxViT-Large 512 1, 2, 3, 4, 5, 6 0.846 0.727

8. 最终提交

我的最终提交是一个MaxViT单模型和一个包含四个模型的集成。令人惊讶的是,两个提交在私有排行榜上的得分都是0.727。我没有使用任何形式的加权,MaxViT只占集成提交的四分之一,但它们在私有排行榜上的得分完全相同。更令人惊讶的是,SeResNext在私有排行榜上的单模型得分反而是最高的。它的交叉验证表现并不出色,收敛性也不如MaxViT稳定,公开排行榜得分也不高,所以我没有理由选择它。

结果对比图

最后,我要再次向组织者、Kaggle以及所有参与者表示感谢!

同比赛其他方案