Momentum Contrast for Visual Representation Learning。Kaiming大神的文章还是需要细细琢磨的。先简单回顾一下Unsupervised Learning。
Unsupervised Learning
虽然身处数据时代, 每个人都无时无刻不在产生着数据,但是能用的数据实际上是很少的,而且而且现在大量的数据都是没有标注的。目前性能最好的当然是监督学习,但它的表现是完全靠大规模标注数据集+多GPU/TPU的算力支撑的,对于数据集的收集和人工标注需耗费大量的人力,所以果然…无监督是未来!
VS Others Learning
先比较几种学习方法的损失函数:
- 有监督学习:对于数据X都有对应的标签Y,所以直接最小化差距就可以了。
- 弱监督学习:理论上分不完全监督(Incomplete supervision,半监督),不确切监督(Inexact supervision,只有粗标签),不精确监督(Inaccurate supervision,标签有错误)三种。半监督需要尽可能使用有监督的标签去up没有标签的,只有粗标签的可以用多示例学习,或者:,其中C是coarse-grained label。不精确监督一般考虑带噪学习。
- 半监督学习:部分数据有标签是X,没有标签的数据是Z,所以损失函数一般由两块组成。,其中R是针对具体任务的函数,以找到有标签X的无标签的Z之间的关系。
- 无监督学习:本文的重点: ,其中P是pretext task自动生成的,不需要人类标记,那么如何设计一个合理的pretext task?
Self-supervised Learning
自监督学习是无监督的一个变种,根据牛津大学Andrew Zisserman给出的定义,自监督是数据能够提供监督信息的一种无监督学习方式。这种学习方式往往需要预先定义一个pretext task对模型进行预训练,然后再将模型进行迁移or微调以适应对应的任务。
这个Pretext tasks 关键就是预先设计的合理的任务,然后网络通过学习Pretext tasks 的目标函数来学习特征。而pretext其实也挺常见的,如NLP里面耳熟能详的BERT所使用的mask抠词填空,前后句子是否相关等等,这些都是不需要再额外标定的自监督预训练模型,然后再对应具体的任务做迁移or微调,也就是做下游任务Downstream Task。
这篇文章主要是做visual representation的,对于CV来说的pretext tasks也有很多:
- 伪标签:pseudo,用其他的模型直接给图片以伪标签,然后再按监督学习的方法进行训练。
- 增强:原图image加一些噪声z,再尝试还原原图。DAE就是做这个事情的,对泛化能力的帮助还是挺大的,更升级的就是GAN来生成数据。同样的思路还有把图变灰度了再还原原图这种上色操作,把Image旋转,折叠,对称等等一下之后,来预测旋转角度等等。总之就是用图像增强方法加颜色做裁剪加抖动之类的,再成对判别。
- 补全:和mask很像,尝试扣掉image的某个区域patch,再补全这个网络。
- 打乱:不再抠图,而是直接打乱,再尝试拼图整个图。对于视频也是,可以打乱所有帧,再排序或者直接正负例成对训练判断是否打乱。
更标准的划分应该是下面这张图,但是主要的手段上面的总结基本都涵盖了。
Contrastive loss
前面提到了要做判断是否是原图等等,一般采取的loss会是对比损失Contrastive loss,这个叫法似乎出自Yann LeCun “Dimensionality Reduction by Learning an Invariant Mapping”,本来是用于处理在降维空间中正样本和负样本之间的相似/不相似的远近距离关系,式子为:其中,代表两个样本特征的欧氏距离,y为两个样本是否匹配的标签,y=1代表两个样本相似或者匹配,y=0则代表不匹配,margin为设定的阈值。损失函数主要惩罚如果原本相似的样本y=1,但在特征空间的欧式距离较大,则说明当前的模型不好,损失变大。同样的如果原本不相似y=0,但其特征空间的欧式距离反而小的话,损失也会变大。
上图是loss与样本特征的欧式距离d之间的关系,其中红色虚线表示的是相似样本的损失值,蓝色实线表示的不相似样本的损失值。这种成对loss的思想在其他领域如搜索推荐会有其他的变体:
- Pairwise Ranking Loss:
- Triplet Ranking Loss:
MoCo使用的其实是Contrastive loss一种变体InfoNCE:一个正例k+,K个负例ki,这样可以使只有真正匹配(与query q算点积)的样本更相似,并且同时不匹配的不相似时,loss才低。最初出自Contrastive Predictive Coding,据说使用InfoNCE,可以同时优化encoder和自回归模型…
哦,有点绕远了。回到MoCo的图:
对于查询xq,要在一个batch中(dictionary size = mini-batch size)的所有K中区别开来(其中有一个正例),有上图三种方法:
- end-to-end:先编码encoder(可同可不同),然后内积算loss再梯度。但是这种方法由于dictionary size 和 mini-batch 的强耦合性,在batch大的时候优化难,而在batch小的时候,batch之间的参数会不一样,也就是GPU大小限制了模型的性能。
- memory bank:把dictionary size 从 mini-batch 中解耦出来,即先把所有样本的特征保存下来bank,然后再梯度query的encoder的参数。但是这样只有当所有key被sample完以后才会更新memory bank,不同的key在和query是不一致的,因为每一次sample encoder都会更新虽有memory bank后面也加入了momentum,但是是针对sample来的,在更新memory bank时会保留一部分上一轮的特征值。
- MoCo:是以上两者的融合版本,将 dictionary 作为一个 queue 进行维护。
把memory bank改成了queue,每sample一个batch key(所以一个trick就是会使用Shuffling BN,打乱再BN),进队后相对于一些最早进入队列的 mini-batch 对应的 key 进行出队操作,这样保证一些过时的、一致性较弱的 key 可以被清除掉。这样就同样是解耦,K是队列长度,K可以设置很大,同时更新也不会有问题。