为帮助鲸鱼保护工作,科学家们使用照片监测系统来监测海洋活动。使用鲸鱼尾巴的形状和在录像中发现的独特标记来识别正在分析的鲸鱼的种类。在过去的40年中,大部分工作都是由个别科学家手工完成的,留下了大量的数据未开发未使用。因此Kaggle举办了这场比赛,提供了25000多张训练图片以及将近8000张测试图片,参赛选手需要编写算法,在测试图片中预测每张图片对应的最有可能的5个鲸鱼个体(每个鲸鱼个体有一个id)。注意,这不是种类分类,而是鲸鱼之间的个体识别。这还不止,训练数据中,有3000多条鲸鱼的尾巴图片只有几张,少至一张,并且有大量的属于未知个体的鲸鱼尾巴(标记为new_whale)。小编挣扎到最后也只拿到第72名(public LB:0.9248,private LB:0.92546)的成绩,top4%。离第一名的0.973的成绩差了近0.05。那么来看看这次比赛前5名的解决方案,他们都有哪些过人之处。
首先看看前5名的private LB:
排名private LB1st0.973092nd0.972083rd0.971134th0.967835th0.96781一开始使用了softmax和固定阈值来训练模型,但是没有得到很好的效果(< 0.9)。为了在网络中使用new_whale图片,于是决定对每条鲸鱼进行二分类。经过几星期的试验,使用senet154网络得到了最好的效果,public LB和private LB都得到了0.96的准确率。加入了一些技巧(下面会提到)之后得到了0.969,然后又进行类平衡处理,四折交叉验证,结果提升到了0.973。然而尝试将senet154与其他网络,比如seresnext101,dpn131集成,并没有得到任何提升。
Heng CherKeng提出,将图片翻转,并且赋予翻转之后的图片新的id,即认为翻转之后的鲸鱼与之前的不是同一只,但是保持标签为new_whale的鲸鱼翻转之后的id依旧为new_whale。
向训练数据中加入了2000张测试图片(标签置信度 > 0.96)
在尝试中发现,准确率和标签数目有关。因此使用了以下策略来平衡预测:对于top 5的预测结果class1到class5,如果 conf class1 - conf class2 < 0.3,并且class2没有出现在所有的top 1结果中,并且class1在top 2结果中出现了很多次,那么交换class1和class2的顺序。
端到端鲸鱼识别模型:
作者列出了各个模型的表现:
single modelpirvate LBresnet101_fold0_256x5120.9696seresnet101_fold0_256x5120.9691seresnext101_fold0_256x5120.9692resnet101_fold0_512x5120.9682seresnet101_fold0_512x5120.9664seresnext101_fold0_512x512-当作者的public LB达到0.940时,开始对1500张图片生成伪标签,并一直使用这些伪标签到比赛结束。作者使用arcface模型(baseline模型)提取出来的bottleneck特征来计算训练测试图片的余弦距离。对于那些few shot类(包含的样本少于2),作者使用0.65作为阈值来过滤高置信度的样本。作者认为如果使用LB为0.97的模型来生成伪标签还能得到更好结果。
下面是使用了伪标签后模型的表现:
single modelpirvate LBresnet101_fold0_256x5120.9705seresnet101_fold0_256x5120.9704seresnext101_fold0_256x512-组合模型表现:
single modelpirvate LBresnet101_seresnet101_seresnext101_fold0_256x5120.97113resnet101_seresnet101_seresnext101_fold0_512x512_pseudo0.9707210 models(final submisson)0.97209探索过程
开始的时候,作者将只有一张图片的鲸鱼和是new whale的鲸鱼排除在训练数据之外。与训练数据中最为相似的鲸鱼个体作为预测结果。
Public LB: 0.90, Private LB: 0.90使用中心特征向量:
Public LB: 0.942 / Private LB: 0.939使用decay 0.0005:
Public LB: 0.946 / Private LB: 0.946将只有一张图片的鲸鱼加入到训练数据中:
Public LB: 0.963 / Private LB: 0.961作者使用对齐的图片进行训练,训练速度更快,但是并没有得到提升:
Public LB: 0.962 / Private LB: 0.959一些图片的bounding box和landmark结果并不是很好,所以成绩也没有提升(这和小编碰到的情况一样),因此也考虑使用不对齐的图片。
Public LB: 0.965 / Private LB: 0.961最后,作者通过水平翻转图像,加倍了鲸鱼个体数。翻转的图像属于不同的鲸鱼个体,但是看起来非常相似。所以作者将翻转的图像的logit值设为0以防止流动梯度。
Public LB: 0.968 ~ 0.971 / Private LB: 0.965 ~ 0.968作者说到,参加此次比赛的目的是学会low-shot learning(或者few shot learning,表示有些类的出现次数很少),并且试着拿到金牌。
作者首先从赞助商的角度考虑,他们为什么举办这次比赛,如果作者是他们的话,作者想从中获得什么。因为在playground中已经有一个类似的比赛,为什么还要举办一次?作者做了如下猜想:
或许Kaggle想展示没有奖金的比赛和有奖金的比赛的解决方案的质量差异。或者赞助商想得到其中最具挑战性问题的解决方案,也就是如何识别没有出现过new_whale(N = 0)和只出现一次(N = 1)的鲸鱼。因此作者把重点放在后者,特别是如何尽可能多的识别出N = 1的鲸鱼。
作者的实现流程中有三大主要模块:
关键点匹配——老派的方法加上一些新技巧孪生网络——和其他许多人一样,使用了Martin前期工作中的孪生网络(小编也同样使用了这种方法)后处理——对few shot类公平处理关键点匹配占了最终预测的80%以上,使用的是经典的关键点匹配。作者尝试了SIFT,ROOTSIFT以及大量的二进制描述符和匹配器,但是差别不大。
作者选择了完整分辨率下的纯暴力方法,对所有的测试训练图片不使用bag或者关键点聚类。
从训练测试图片(原始图片)中获取关键点保存到hdf5文件中,将关键点限制在unet预测出的鲸鱼吸虫区域内。匹配: 循环遍历所有的测试/训练对使用faiss匹配关键点关键点双单应滤波xgboost预测验证单应矩阵如果匹配值大于阈值,那么作为预测结果作者使用了部分Martin的代码,组合了InceptionResNetV2, InceptionV3, 和ResNet50。没有使用数据增强,与Martin一样使用灰度图片。
为使训练更快,作者对backbone网络做了些预训练,之后再送入孪生网络中。预训练过程如下:
在样本数最多的前200个鲸鱼个体上做分类在N > 8(样本数大于8的鲸鱼个体,大约576只)的数据上fine tune网络在所有数据上fine tune使用所有数据+mixup+384×384的图片上fine tune作者通过分析孪生网络计算出的预测矩阵发现,总是有一些相同的训练图像不成比例的出现在top5中。这让作者想到需要想办法抑制这些比例高的预测,或者如何让那些出现次数少的鲸鱼个体拥有同等的机会进入top5的预测中。作者提出了一个非常简单的方法,不使用传统的“哪张训练图片与测试图片最为接近”思路,而是将矩阵转置并判断“哪张测试图片与训练图片最接近”。从转置后的矩阵中得到N=1的样本,作者发现对于N=1的训练样本,可以使用一个新的阈值。这样在top1的结果中,对N=1的样本的预测大大增加。
作者使用的网络同样也是孪生网络,非常适合处理这种one-shot的问题。
那么什么是孪生网络(SNN)呢,从上面一直说到这里都还没详细介绍。孪生网络会训练一个卷积网络从两张图片中提取特征,然后使用这两个特征进行判断是否属于同一个类别,网络的结构有两条相似路线,就像孪生兄弟一样,因此被称为孪生网络。
作者在Martin模型的基础上,尝试了以下方式:
使用3通道(RGB)通道作为输入(小编也尝试过,并没有提升)在384x384图像的基础上,还使用了512,768大小的图片(小编也尝试过,也没有提升)调整卷积层,比如增加Squeeze-Excitation层(SE-Net),为最终模型提供了更多的模型多样性。在Marin的实现中,使用了一个复杂的神经网络来计算两个特征向量的相似度,而没有使用类似L1,L2这些在tripletmodel中常用的距离指标。
小编与作者一样,使用bounding box来截取鲸鱼尾巴区域,去除了背景、海鸟等的一些噪声。
数据增强
作者采用了动态图像增强,使用了剪切,旋转,翻转,对比度,高斯噪声,模糊,颜色增强,灰度,随机剪切等操作。
对抗训练
作者可以很容易的从15k张图片中生成非匹配的图像对,如果单单随机的选取这些进行训练,模型很容易就达到0 log loss的良好结果。但是大多数的非匹配图像对都是训练冗余的,并且,模型对一些非匹配图像对的区分仍旧存在难度。
因此,好的训练策略可以事,通过模型的效果,在每轮epoch中持续的去“挖掘”出那些难以区分的图像对。
训练开始的时候,将容易区分的匹配和非匹配图像对输入到网络进行训练。因为如果一开始就把那些难以区分的送给网络训练,那么网络很可能预测出与之相似但不同的鲸鱼出来,或者把不同的鲸鱼预测成同一条,结果就是训练损失将会发散而不会收敛。
随着训练过程的进行,模型识别能力越来越强,可以开始将一些难以区分的非匹配图像对送入网络。
考虑到非匹配的图像对会有很多,而匹配的图像对必须来自同一只鲸鱼,因此匹配的图像对会少很多,因此作者考虑只对非匹配鲸鱼生成对抗性图像对,对匹配的鲸鱼不做此处理,而是对齐进行随机抽样。
作者同样尝试过对匹配图像对进行对抗性训练,但是相比随机抽样没有得到提升。
交叉验证和K折训练
作者使用4折分层交叉验证训练模型,将样本数大于1的鲸鱼作为训练图片,因为只包含一张图片的鲸鱼无法形成匹配图像对。对于只有2张图片的鲸鱼,保证它们一直在训练数据中,并且不分割。因此对于所有训练数据,所有鲸鱼最少有两张图片。对于图片数目大于2的鲸鱼,将它们平均分为4份。
验证过程中,加入了25%的从new whale中随机挑选的图片,比例与LB数据集比例一致。训练数据中不包含任何new whale图像。
作者同样使用了伪标签的方法,使用最好的模型对测试集进行预测,选择预测置信度大于某个阈值(比如概率大于0.99999)的结果作为图像的伪标签加入到训练数据中。
值得注意的是,每次模型效果得到提升之后(LB 0.938 -> LB 0.950 -> LB 0.965)都会更换一次伪标签数据集,并且每次都需要重新构建新的K折数据。作者有一次忘记这么做而导致模型十分过拟合。
作者最后的解决方案包含了大约15种集成的预测结果(比如test vs train的score矩阵),有些来自于不同的模型,比如在ImageNets上预训练的densenet121, Inception V3, SE-ResNet等模型,以及与Martin类似的自定义的卷积网络结构。
同时也将使用同样网络,不同训练策略的结果集成起来,比如不同的图像大小,不同的数据增强策略,不同的初始化。
同样还使用了叠加方法,对于每个图像对,使用15个模型的预测结果生成15维的新特征。然后将新的特征输入到叠加的模型比如xgboost或nnets中训练二分类器。
看了前5名获奖者的解决方案,我们能够发现,对于数据方面,除了常用的一些数据增强方式外,针对这个比赛,有人提出了将图片水平翻转,赋予新的id的策略,因为对于鲸鱼尾巴,水平翻转后的尾巴确实可以看成出自于另外一只,因此增加了鲸鱼个体数目,增强了模型的鲁棒性。同时,伪标签的做法也在一定程度上增加了训练数据中出现次数少的鲸鱼的图片数量。而为了过滤掉海水背影、海鸟等噪声,预测尾巴bounding box的做法便成了首选,事实上这种做法是小编我在训练模型前做的第一件事。而对于这种few shot learning的情形,孪生网络也用的比较多,通过同时提取两张图片的特征来对比相似度,以此判断两张图片中的尾巴是否属于同一鲸鱼。组合模型的方法在这里也很常见,通过集成多个模型的结果,以此来提高最终的结果。
扫码关注微信公众号:机器工匠,回复关键字“whale”获取top5的代码实现。