返回列表

1st place solution

535. RSNA Screening Mammography Breast Cancer Detection | rsna-breast-cancer-detection

开始: 2022-11-28 结束: 2023-02-27 医学影像分析 数据算法赛
第一名解决方案
作者: Đăng Nguyễn Hồng
发布时间: 2023-03-05

第一名解决方案

更新 2023年4月10日

消融研究:尽管在特定折数上有很大改进(见下文),但软正标签技巧并没有比标准标签平滑技术表现出明显的性能提升。使用外部数据集在本地 OOF 验证和私有排行榜上将 F1 分数提高了约 0.02(使用完全相同的训练管道和超参数)。

外部数据 损失函数 OOF F1 LB PL
x label smoothing=0.1 0.4921 0.60 0.53
x soft positive label=0.8 0.4853 0.60 0.53
label smoothing = 0.1 0.5161 0.58 0.56
soft positive label = 0.9 0.5182 0.61 0.55

首先,我要感谢 Kaggle 和比赛主办方为我们提供了这样一个精彩的挑战,目标宏大且数据质量高。感谢所有参与者和 Kaggler 们积极且富有成效的讨论和代码分享。我的解决方案正是建立在你们分享的每一个片段之上。我学到了很多,对此我深表感激。

我也对获得第一名感到非常高兴和惊讶。这是我的第一枚金牌,也是我第一次写解决方案总结。这对我来说是一段很棒的旅程。

关于解决方案,我使用了一个非常简单的管道,只需几行即可描述:

  • 使用了一些外部数据集:VinDr-Mammo、MiniDDSM、CMMD、CDD-CESM、BMCD。
  • 4 个 Convnextv1-small 模型,分辨率 2048x1024,在竞赛数据的 4 折分割上进行验证。
  • 软正标签。
推理管道

现在我想分享一些实验和我对这些实验的想法。其中许多可以在其他优秀 Kagglers 的讨论中找到。许多似乎显而易见。希望这能帮助一些新来者在未来入门。请注意,这只是我个人的观点/想法,实验和知识非常有限。感谢您的讨论,如果有不对的地方请随时指正。

1. ROI 裁剪

进行 ROI 裁剪是因为它有效地帮助在固定分辨率下保留更多的纹理/细节。我使用 YOLOX-nano 416x416 作为 ROI 检测器。与基于规则的方法相比,DL 检测器的优势在于获得的边界框更小,纵横比更稳定,且专注于乳腺区域。

  1. 在 @remekkinas 的数据集(472 张边界框标注图像)上训练 YOLOX。
  2. 在所有可用的训练数据上进行推理,设置低 conf_thres 和高 iou_thres。只有 3 张漏检图像(均包含噪点)和超过 100 张有 2 个框的图像(几乎重叠)。我手动选择并标注了其中 99 张。因此,我总共有 571 张标注图像。
  3. 在新图像上重新训练 YOLOX:521 张用于训练,50 张用于验证。请注意,这 50 张验证图像包含原始 @remekkinas 数据集的所有 47 张验证图像。新数据集版本包含原始分辨率图像(与原始 dicom 相同),经过简单的 min-max 归一化预处理。尝试了各种模型/图像大小,最终选择 YOLOX-nano 416x416 作为最终模型,因为它结果一致且开销小。
模型大小 图像大小 插值 AP_new_val AP_remek_val
nano (选择) 416 LINEAR 96.26 94.21
nano 416 AREA 94.09 91.60
nano 640 LINEAR 95.85 88.40
nano 768 LINEAR 96.22 82.09
nano 1024 LINEAR 94.92 89.40
tiny 416 LINEAR 94.23 90.20
tiny 640 LINEAR 94.95 89.84
tiny 768 AREA 96.21 68.03
tiny 1024 AREA 93.69 73.70
s 416 LINEAR 95.03 0.86
s 640 LINEAR 96.10 70.80
s 768 LINEAR 96.79 78.70

所有实验中 AP@0.5 均为 1.0。我们看到两个验证集之间的 AP@0.5-0.95 存在巨大差距。原因如下:

  • 新版本增加了 3/50 个典型的困难案例。
  • 处理管道不一致:Remek 的验证图像被调整了 2 次大小(原始 --> 1024 --> 416)。
  • 训练图像的标注带有个人偏见(没有标注乳腺框的标准方法/共识)。因此,更高的 AP 可能并不代表模型更好。
  • 验证集大小也不足以判断。
  • 没有进行超参数调整。

这些问题是否导致了巨大的差距,特别是对于更强的模型和更大的图像尺寸?所有这些努力只是为了确保一个“尽可能好”的 ROI 检测模型。我认为 @remekkinas 的数据集足以训练良好的 YOLOX 模型,它们在隐藏测试集上也能表现同样出色。

在 YOLOX 漏检的情况下,使用稍微修改自 此 notebook 的简单 Otsu 阈值处理 + findCountours() 来寻找乳腺边界框作为后备方案。或者,如果两者都漏掉了乳腺框,就直接使用整个图像而不进行任何裁剪。

2. 推理管道

对大数组的操作很耗时,所以我尝试尽可能将计算任务转移到 GPU 上。

推理管道图

3. 早期实验

我的最终解决方案使用了外部数据集,但我大部分时间(直到“还剩 7 天”)都坚持仅使用竞赛数据。因此,我的大部分实验仅基于竞赛数据完成:基于 patient_id 进行 StratifiedGroupKFold 的 5 折分割。最终解决方案使用的训练超参数几乎继承自这些早期实验。

3.1. 关于指标

比赛的 pF1 分数对我来说并不稳定且难以追踪。因此,我主要基于多个指标来追踪我的实验:{ PR_AUC, ROC_AUC, best_PF1 (binarized), best_threshold },而不仅仅是一个。

  • PR_AUC:与 best_PF1 相关但比 best_PF1 更稳定。它关注正例,且受先验数据分布(正例百分比)影响很大。
  • ROC_AUC:受先验数据分布影响较小。稳定得多,但似乎过于乐观,导致好模型和坏模型之间的差距很小。
  • 为了获得高分 pF1,模型不应预测太多正例,这通常会导致大量 FP --> 从而大幅降低 best_PF1。一个高 pF1 评分的模型倾向于优先考虑 Precision 而非 Recall。我个人不喜欢这种行为,特别是对于现实生活中的应用。

3.2. 数据增强

我在所有实验中都坚持使用这个数据增强管道,完全没有调整:

A.Compose([
    # 裁剪, 改编自 A.RandomSizedCrop()
    custom_augs.CustomRandomSizedCropNoResize(scale=(0.5, 1.0), ratio=(0.5, 0.8), p=0.4),
    # 翻转
    A.HorizontalFlip(p=0.5),
    A.VerticalFlip(p=0.5),
    # 缩小
    A.OneOf([
        A.Downscale(scale_min=0.75, scale_max=0.95, interpolation=dict(upscale=cv2.INTER_LINEAR, downscale=cv2.INTER_AREA), p=0.1),
        A.Downscale(scale_min=0.75, scale_max=0.95, interpolation=dict(upscale=cv2.INTER_LANCZOS4, downscale=cv2.INTER_AREA), p=0.1),
        A.Downscale(scale_min=0.75, scale_max=0.95, interpolation=dict(upscale=cv2.INTER_LINEAR, downscale=cv2.INTER_LINEAR), p=0.8),
    ], p=0.125),
    # 对比度
    A.OneOf([
        A.RandomToneCurve(scale=0.3, p=0.5),
        A.RandomBrightnessContrast(brightness_limit=(-0.1, 0.2), contrast_limit=(-0.4, 0.5), brightness_by_max=True, always_apply=False, p=0