596. SenNet + HOA - Hacking the Human Vasculature in 3D | blood-vessel-segmentation
首先,我想用几句重要的话开始我的解决方案描述:
我要感谢乌克兰武装部队、乌克兰国家安全局、乌克兰国防情报局和乌克兰国家紧急服务局,为参与这项伟大的竞赛、完成这项工作提供了安全和保障,并帮助科学、技术和商业不仅没有停滞,反而不断前进。
我的最终模型是2D和3D模型的混合体,并采用了d4 TTA(测试时增强)。对于2D模型,应用了多视角TTA。所有模型都采用2折交叉验证设置,选择kidney_2和kidney_3_dense作为验证集。集成时对2D和3D模型赋予相等权重。
所有最终(3D和2D)模型都在kidney_1_dense、kidney_2、kidney_3_dense、kidney_3_sparse以及伪标签50um_LADAF-2020-31_kidney_pag-0.01_0.02_jp2_上进行训练。最初我使用逐片归一化来归一化图像,但后来改为基于百分位的堆栈级归一化。
2D模型采用多视角设置进行训练:所有图像被堆叠成一个张量,随后在不同轴上进行切片。在训练过程中,增强策略和采样策略至关重要。加权采样基于稀疏度百分比:密集样本权重为1,而稀疏样本的权重等于其稀疏度。对于伪标签,我选择与kidney_2相同的权重,例如:
kidney_1_dense: 1,
kidney_2: 0.65,
kidney_3_dense: 1,
kidney_3_sparse: 0.85,
50um_LADAF-2020-31_kidney_pag-0.01_0.02_jp2_: 0.65.
增强策略如下,有0.5的概率应用CutMix增强。裁剪来自同一器官和同一投影轴。然后在CutMix之上,应用下一个增强管道:
A.Compose(
[
A.PadIfNeeded(*crop_size),
A.CropNonEmptyMaskIfExists(*crop_size, p=1.0),
A.ShiftScaleRotate(scale_limit=0.2),
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.5),
A.RandomRotate90(p=0.5),
A.OneOf([
A.RandomBrightnessContrast(),
A.RandomBrightness(),
A.RandomGamma(),
],p=1.0,),
],p=1.0,)
裁剪大小设置为512。我也尝试过更高分辨率,但效果相差不大。
我进行了一些2.5D方法的实验(3通道和5通道),但结果相同或更差。
3D模型的增强方案仅包含d4增强和随机裁剪。裁剪时有0.5的概率裁剪到空掩码区域。这是为了处理在肾脏体积外出现的假阳性问题。这可以通过引入两类3D分割来改进,但我没有足够的时间和资源进行此类实验。因此,我决定创建一个后处理来解决这个问题。3D模型的裁剪大小为192x192x192。
两个模型都采用2折交叉验证设置,使用kidney_2(fold_1)和kidney_3_dense(fold_0)作为验证。从训练集中移除kidney_1会导致CV和LB性能下降,因此我放弃了fold_2,没有在该设置下进行训练。
我能够获得的最佳结果是使用efficientnet系列模型与UnetPlusPlus解码器以及来自segmentation_models_pytorch库的SCSE注意力机制。我尝试过resnet50模型(如讨论部分所述)、不同的transformer和seresnext模型,但都无法超越efficientnet-b5的性能(它在CV和LB上表现最佳)。在我的本地验证中,efficientnet_b7编码器和mit_b5编码器获得了不错的分数,但在LB上分数显著降低。
训练进行了30个epoch,使用从3e-4到1e-6的余弦学习率调度器。我保存了前3个检查点,并提交时使用了best-last检查点。
采用此设置训练的efficientnet_b5_UnetPlusPlus模型在0.05阈值下,公共LB得分为0.878,私有LB得分为0.714。
黄色为TP,绿色为FP,红色为FN。

3D模型的设计很大程度上受到nnUnet模型架构的启发,两者基本相同。我使用monai库中的DynUnet代替原生的nnUnet模型,配置几乎为默认设置,训练设置也与nnUnet几乎相同。优化器使用初始学习率为0.01的SGD,并采用余弦退火学习率方案(而非LinearLR),训练500个epoch,每个epoch包含2000个样本。
该模型在公共LB上得分为0.869(0.868和0.866——分别为fold 0和fold 1),私有LB上得分为0.694(0.758和0.663——分别为fold 0和fold 1)。
两个模型都使用BoundaryDOULoss(https://arxiv.org/pdf/2308.00220.pdf)进行训练,该损失函数表现最佳。我曾尝试修改它以更好地处理稀疏数据,但未能成功。
根据预印本,我从http://human-organ-atlas.esrf.eu网站下载了额外数据(2个数据集)。结果显示,其中一个数据集与kidney_3重叠,因此为避免泄露,我将其剔除。我使用另一个数据集生成伪标签。对于伪标签生成,我使用了一个由2D模型(efficientnet-b5和efficientnet-b6与UnetPlusPlus)组成的集成,这些模型采用相同的设置训练但不包含CutMix。CutMix的正确设置以及3D模型我是在竞赛截止日期前才发现的,因此我没有重新训练原始集成,而是坚持使用第一版本的伪标签。
两个模型的推理都使用monai库的sliding_window_inference进行。此外,对于2D模型,我执行了多视角TTA,这有助于检测小血管并提升整体性能。
对于2D模型,裁剪大小为800像素,而对于3D模型——256像素,重叠率为0.25,并使用高斯合并。所有模型都使用ttach库的d4_transform。我曾forked ttach仓库并实现了3D图像的逻辑,但推理时间显著增加,性能提升不大,因此我坚持对2D和3D模型都使用2D d4_transform :)
如前所述,3D模型在非空立方体上表现良好,但空立方体容易使模型混淆。为处理此问题,我决定尝试后处理。思路如下:尝试找到血管存在的感兴趣区域(ROI)。由于2D模型没有此问题,我决定为每个2D切片找到血管的边界多边形。获得ROI掩码后,我将其与3D模型的预测结果相乘,使单个3D模型的得分从0.869提升到公共LB的0.881和私有LB的0.701。
将2D模型和3D模型的预测结果以1:1的权重集成,我的得分从公共LB的0.881提升到0.884,私有LB提升到0.712。
我尝试的另一种后处理方法是使用cv2的Canny滤波器来分割肾脏。该分割算法并不完美,但应用此类后处理使我的公共LB得分从0.884提升到0.892,而在私有LB上表现糟糕,仅得0.313。

P.S. 如果你能读到这里,私有LB上的最高分是2D和2.5D模型的简单组合(1通道和3通道):)
P.P.S. 感谢阅读!