代码(训练 + 推理,可直接提交):https://www.kaggle.com/code/jeroencottaar/czii-11th-place-solution
我们要感谢竞赛组织者——这是介绍 3D 分割的绝佳机会。当然,我还要感谢我的队友 @davidlist!
引言
竞赛的目标是在冷冻电子断层扫描数据(基本上是电子密度的 3D 地图)中识别各种蛋白质,这使得这是一个 3D 粒子识别问题。一个关键要素是竞赛指标严重偏向召回率而非精确率——遗漏粒子(假阴性)的代价远高于预测过多(假阳性)。
与大多数解决方案一样,我们的核心是一个 UNet 分割模型。我们添加了一些其他解决方案中不常见的元素:
- 批归一化(Batch Normalization),稳定训练并使推理在不同窗口位置保持一致
- 基于 2D 分类神经网络的后过滤器,去除β-半乳糖苷酶和甲状腺球蛋白的假阳性
- 预测近距离内的多个核糖体,使我们在混淆区域能命中更多核糖体
我们真正错过的是没有在公共测试数据集上 properly optimize 我们的模型。尽管我们进行了许多提交,但这些并没有真正用于探测。我们在 UNet 架构和超参数上做了一些实验,但仅在交叉验证中进行,由于训练集 size 较小,几乎不可能得出任何有用的结论。
在这篇 writeup 中,我们将非常简要地讨论我们解决方案的 outline,然后依次关注这 3 个元素。
解决方案 outline
我们解决方案的核心是一个 UNet:
- 32 个模型集成,混合了 ResNet 架构。不在 CV 或 LB 上进行选择;我们只是用不同的种子训练 32 次。
- 模型通过在 softmax 之前取热力图的均值进行集成。
- 所有归一化都是 BatchNorm(详见下文)。
- 训练始终为 2200 个 epoch,无早停,学习率在后期降低
- 数据增强:绕 Z 轴旋转 90 度,沿 Y 轴翻转。其他方法没有帮助。
- 损失:交叉熵和 Tversky 损失(alpha=0.5, beta=8)的加权组合。
- 无测试时增强。
从 UNet 分割图到粒子位置:
- 应用阈值并识别连通分量。
- 将每个簇的质心作为预测。如果簇非常大,则进行多次预测(详见下文)。
- 根据连通簇的大小选择包含哪些粒子。对于β-半乳糖苷酶和甲状腺球蛋白,应用基于 2D 神经网络分类器的额外过滤器(详见下文)。
批归一化 (Batch Normalization)
对于 UNet 中的所有归一化层,我们使用批归一化。这意味着归一化因子基于训练期间发现的运行平均值(并在推理期间固定),而不是归一化每个实例。这显著提高了分数和训练稳定性。为什么?
想象一下,我们使用下面的红色或蓝色窗口进行推理,并考虑如果我们使用除批归一化以外的任何方法,位置 X 会发生什么
如果我们使用红色窗口,右下角的伪影对我们位于 X 的预测有很大影响,因为归一化基于窗口中的所有数据。当我们使用蓝色窗口时,我们在 X 处突然得到了非常不同的预测。虽然某种类型的长程影响在神经网络中可能是有益的,但应该清楚的是,这种影响完全是虚假的。注意,这也意味着我们在推理窗口之间的过渡处会得到不连续(即使丢弃边缘像素)。
在 MONAI 中,具体来说,你可以通过在构造函数中添加 "batch='NORM'" 轻松地从实例归一化更改为批归一化,尽管你可能还需要调整动量以获得稳定的训练(详见我们的代码)。
2D 分类器
正如竞赛论文中所述,主办方包含了基于 2D 图像的分类。这启发我们也尝试同样的方法。
我们在 UNet 模型的真阳性和假阳性上训练了一个 2D 神经网络(沿 Z 轴求和)。最简单的架构效果最好:仅仅是 3 个全连接层(带有 dropout)。对于β-半乳糖苷酶,我们在开始时添加了一个卷积层(无激活)。应用了典型的数据增强,并做了一些工作来平衡真阳性和假阳性(详见代码)。
如下所示,对于β-半乳糖苷酶,这允许我们过滤掉相当多的假阳性(相比仅使用簇大小作为特征)。决策边界确实需要在排行榜上进行调整,遗憾的是我们在这方面做得不够。
改进的核糖体检测
这也许更像是一种'Kaggle 特色做法'(即你通常不会添加到生产机器学习解决方案中的东西)...
我们注意到我们在处理核糖体簇以及被其他 stuff 包围的单个核糖体时遇到了困难。这是因为簇倾向于融合在一起。我们通过预测大型细长簇的多个核糖体来解决这个问题,使用 K-means 来分割簇