本文已参与「新人创作礼」活动,一起开启掘金创作之路。
比赛地址Google Universal Image Embedding | Kaggle
首先这个比赛是不提供数据集的,所以数据集的整理也是一大难点,下面对北大的崔同学发表的discussion做一个总结,写下自己的理解,中文也方便自己去查阅,如果哪里解读有错误欢迎指正。
开始上手的历程-预训练模型的选择
通过其他的discussion,作者发现
- 有人仅适用预训练模型就能达到比较高的分数
- 有人说在训练和fine-tuning后得到了更差的效果
所以第一步,作者首先收集了一些比较好的预训练模型,得知预训练数据集越大得分越高,然后把目光转向了clip模型,把它作为开始比赛的最好选择,并尝试了所有vit-L预训练模型,发现最好的分数为Laion-400M 31ep上训练的,分数为0.499,此时和当时的第一名还有0.05的差距,接下来开始寻找tricks。
(比赛需要使用一个64维的向量,所以模型输出的向量怎么进行降维是接下来研究的部分。)
对于权重比较weak的模型(这里的weak我理解的是1权重量比较小 2训练数据少,模型不强大),比如在ImageNet-1K上训练的efficientnet-b0,这类模型使用random projection降维会有比较好的效果,但是对于vit-L这种在Laion-400M 31ep大数据上训练的strong weight模型效果较差。
对于其他方法,比如PCA、t-SNE在他们的实验中并不适用。
他们使用了一种貌似比较稳健的方法,去提高未训练模型的分数,simply using less values from embedding vectors to calculate the mean will boost ~0.010 on both public LB and private LB(这里应该就是使用了平均池化吧 )
为什么使用平均值不好呢,作者猜测说,计算平均值会让embedding中真实的特征消失,但是对当时来说平均是最好的方法了,在没有训练和微调的情况下,最终达到0.510 的分数,所以他们推测当时的第一名一定是在某些数据上训练了。
开始训练吧
训练并不是那么容易,许多参赛者声称他们训练后得到的分数更低了,还有就是这个比赛的规则是禁止人们使用没有商业使用许可的数据集。
想到这个比赛是Google Landmark比赛的拓展,他们开始尝试在GLDv2-clean上进行训练。最好的方法可能是简单地添加一个投影层来缩小embedding到64维,只微调最后一层。作者查看了当时Google Landmark比赛得分最高的方法,总结出arcface起到了非常大的作用。所以他们先使用m为0.5、s为30的arcface微调最后一层,经过6个epoch后得到了令人惊讶的分数0.560,说明方向是对的。
(但是作者害怕因为数据集不许商用,导致大部分竞赛者取消资格,所以开了帖子求证,最后得到的回复是:任何在讨论中被提及的公开的数据集都可以使用。这就不担心数据问题了)
现在就可以更自由地去进行训练了。他们认为应该遵循这个顺序:选择数据集->决定模型,训练。接下来就开始测试不同的数据集。为了保持良好的性能和节省时间,他们迭代地将数据集添加到训练列表中,而不是从一开始就对每个数据集进行训练,然后他们添加了Products-10k, Shopee, MET Artwork Dataset, Alibaba goods, H&M Personalized Fashion, GPR1200, GLDv2-Full, DeepFashion - Consumer-to-shop Clothes Retrieval Benchmark part这些数据集,得到了0.610的分数。
但是光训练最后一层的话,训练后期的epoch的准确率没有任何改进,所以肯定是需要对网络的其他部分去训练的。作者觉得很难在模型结构上去改进,因为预训练权重很容易受到任何额外结构的影响,即使能够想出解决办法,也对计算资源和消耗时间不够友好。
再来看一下训练的tricks吧,保持backbone的参数不变,肯定不是个好主意。由于现在的linear head已经训练好了,他们不需要担心随机初始化的linear head会影响到backbone。所以他们解冻了backbone部分,使用比linear低10倍的初始学习率训练整个部分。但是这种做法,如果也像在linear head上那样在6个或6个以上的周期上进行训练,它很可能会导致过拟合。通过对这一现象的研究,他们发现当我们对整个模型进行训练时,会导致linear head的权重产生大幅度的抖动。这很奇怪,因为对linear head单独进行微调时,它已经收敛了,现在需要思考linear head的权重本质上是什么。
给定投影层F(C, X)的一组权值,其中C表示全部类别X中每个类的central embedding。
从上面可以看出,F(C, X)的抖动表明,各个类的central embedding变化迅速,导致类之间的欧氏距离发生了很大的变化。每个类之间的欧氏距离确实是有意义的,因为它表明了每个类之间的关系。central embedding的快速变化可能是一种不考虑成本去适应训练损失的结果。他们认为这是过度拟合的根本原因之一,所以最后决定在backbone训练时冻结最后的fc层。
在全连接层中添加dropout已经是一个众所周知的技巧,以避免训练cnn或vision transformers的过度拟合。这在大多数情况下并不总是有效,但在这里是值得尝试的,因为1)模型有2个fc层,1层用于嵌入512的长度,最初由VIT提供,接着增加了第二层fc,将其缩小到64。更多fc层往往不会因为dropout而丢失那么多信息。2)这里的过拟合比较严重。
将上述两种方法结合起来,只用了3个epoch,模型提高到0.650-0.660。
在之前的训练中,他们发现使用products-10k这个数据集最有可能使模型大幅度提升,所以他们使用products-10k,按照“先fc,再backbone”的顺序进行单独fine-tune,达到了0.671。
似乎很难继续改善,kaggler现在应该做什么:)
是的,ENSEMBLE!
但是在这场比赛中,ensemble似乎并不奏效,那么为什么不起作用呢?作者比较同意manwithaflower的观点如下:
我猜这行不通是因为:
- 如果你平均embedding,你会得到一个非常奇怪的东西,它不知道如何正确地描述图像的embedding。这只是一个假设:第一个模型对一副关于“花”的图像的“重要神经元”在25到55之间(64个神经元中),第二种模型可能在展示“汽车”图片与第一个模型有相同的表示。但是如果你把它们平均起来,你可能会得到一团乱麻,分不清车和花。
- 如果你做平均池化,对于每个模型你有大约32个embedding。也许这太小了,不能很好地代表图像特征。
回想一下前面关于F(C, X)的东西,如果在不同的模型中得到相似的F(C, X),那么这些模型的ensemble应该成立。
如何做到这一点?基本上我们有两种方法:
第一种,如果我们在不同的超参数中微调模型,像model soups[arxiv.org/abs/2203.05…]那样,这种方法可能对模型集成有效果,因为F(C, X)不会改变很多。这就是他们所尝试的,最后在综合分辨率224和280的情况下提高到0.680。
第二种,如果试图保持F(C, X)在性能上保持一致,也可以使模型集成工作,甚至取得比第一种方法更好的性能,因为模型的方差可以是巨大的,这有助于集成。这种方法是基于一个假设,即不同模型的F(C, X)表示的不同空间可以通过线性操作对齐。如下:
如果有
y^ = F1(C, X) * B1(W, x), and,
y^ = F2(C, X) * B2(W, x),
Then ∃ G(C1, C2), such that,
y^ = F1(C, X) * G(C1,C2) * B2(W, x)
因此只需要在模型上训练一个G(*)来做ensemble,这是他们想在比赛后去进一步研究的。
但是dschettler的研究打破了上面的猜想,因为clip huge模型实在太强大了。所以上面的训练过程,作者又在laion-2b VIT-H model上做了一遍,此外做了些改动 1)不ensemble了,因为vit-H真的是一个大块头2)同时训练所有的数据集,去掉product -10k,留下product -10k作为最后的微调数据集。
然后达到0.703分。在280尺寸上微调后,达到0.705。这里有一些tricks,用更少的时间和更低的学习率在280尺寸上训练last layer to backbone,达到了0.723的成绩。还有一个另外的选择可以不适用更高的分辨力,但是需要一些overlap的patch。最终使用了 290分辨率和4像素重叠的方法,在比赛最后一天达到了最高的成绩公共LB 0.732和私有LB 0.728。
Github repository:github.com/LouieShao/1…
Paper:arxiv.org/abs/2210.08…
其他注意点
- 选择数据集时,一个数据集一个数据集的训练,只保留能提高分数的数据集。选完之后,在模型上一次性将所有数据进行训练,最后只留下product -10k做最后的微调
- 混合数据集进行训练时,每个batch需要对不同的数据集进行取样
- 近30%的域没有选择任何数据集,因为太小可能会影响得分
- 如果一个数据集比另一个数据集大的多,对于小的数据集不必太在意,因为它对得分几乎没什么贡献,也许会有小小的提升,但是在那种情况下,会首先保证大数据集得到良好的训练,不管对小数据集的影响
- 作者使用的计算资源是4 A100 GPUs, 全部的训练过程花费了大约20天
这篇discussion作者把自己从最开始上手到最后的改进过程都说得很详细,很适合初学者去学习和参考~