返回列表

1st place solution summary

338. APTOS 2019 Blindness Detection | aptos2019-blindness-detection

开始: 2019-06-28 结束: 2019-09-07 医学影像分析 数据算法赛
第1名方案总结

第1名方案总结

作者:Guanshuo Xu
发布时间:2019-09-08

感谢 APTOS 和 Kaggle 主办了这场有趣的比赛。我还要感谢在本次比赛的 Kernel 和讨论区慷慨分享的人们,以及分享了 2015 年比赛解决方案和发现的顶尖团队。那些发现和解决方案极大地影响了我的策略。

验证策略

在任何比赛中,合适的验证策略都是最热门的话题之一。在早期阶段,我尝试使用 2015 年的数据(包括训练集和测试集)作为训练集,并使用 2019 年的训练数据作为验证集。不幸的是,验证结果与 Public LB(公开排行榜)差异很大,我无法让它们的性能很好地关联起来。在一些讨论和 Kernel 中,其他参赛者也报告了 CV(交叉验证)和 LB 之间表现不一致的情况。正因为如此,我不知道该如何继续,所以我转去参加了其他几周的比赛。当我回到这场比赛时,我决定将 2015 年和 2019 年的所有数据合并作为训练集,并完全依赖 Public LB 进行验证。

预处理

我认为没有必要对图像进行预处理来辅助建模,图像质量作为深度神经网络的输入非常完美。所以,没有特殊的预处理,只是简单的缩放。

模型与输入尺寸

我的最终提交是以下八个模型的简单平均。Inception 和 ResNet 通常融合得很好。如果我能多两周时间,我肯定会加一些 EfficientNets。

2 x inception_resnet_v2, 输入尺寸 512
2 x inception_v4, 输入尺寸 512
2 x seresnext50, 输入尺寸 512
2 x seresnext101, 输入尺寸 384

输入尺寸主要根据 2015 年比赛中的观察结果确定,即较大的输入尺寸会带来更好的性能。尽管根据 Public LB 的反馈,我发现超过 384 并没有太多益处,但我仍然将输入尺寸推向极限,因为私有测试集可能会从中受益。

损失函数、数据增强、池化

我只使用了 nn.SmoothL1Loss() 作为损失函数。其他损失函数可能也不错。我坚持使用这一种损失只是为了简化集成过程。

对于数据增强,以下设置很有帮助:

contrast_range=0.2,
brightness_range=20.,
hue_range=10.,
saturation_range=20.,
blur_and_sharpen=True,
rotate_range=180.,
scale_range=0.2,
shear_range=0.2,
shift_range=0.2,
do_mirror=True,

对于最后的池化层,我发现广义平均池化比原始的平均池化效果更好。代码复制自 https://github.com/filipradenovic/cnnimageretrieval-pytorch

from torch.nn.parameter import Parameter
def gem(x, p=3, eps=1e-6):
    return F.avg_pool2d(x.clamp(min=eps).pow(p), (x.size(-2), x.size(-1))).pow(1./p)
class GeM(nn.Module):
    def __init__(self, p=3, eps=1e-6):
        super(GeM,self).__init__()
        self.p = Parameter(torch.ones(1)*p)
        self.eps = eps
    def forward(self, x):
        return gem(x, p=self.p, eps=self.eps)       
    def __repr__(self):
        return self.__class__.__name__ + '(' + 'p=' + '{:.4f}'.format(self.p.data.tolist()[0]) + ', ' + 'eps=' + str(self.eps) + ')'
model = se_resnet50(num_classes=1000, pretrained='imagenet')
model.avg_pool = GeM()

训练与测试

训练过程可以分为两个阶段。在第一阶段,我例行训练了这八个模型,并在 Public LB 上验证每一个。为了获得更稳定的结果,模型成对评估(使用不同的种子),这就是为什么每种模型我都有 2 个。在探测 LB 时,我试图减少超参数的自由度以缓解过拟合,例如,为了确定训练的最佳 epoch 数,我使用了 5 的步长。以下是第一阶段训练后的优化结果:

  • inception_resnet_v2 public: 0.831 private: 0.927
  • inception_v4 public: 0.826 private: 0.924
  • seresnext50 public: 0.826 private: 0.931
  • seresnext101 public: 0.819 private: 0.923 (第二好的结果,错过了最好的)
  • ensemble public: 0.844 private:
同比赛其他方案