Momentum Contrast for Unsupervised Visual Representation Learning 论文笔记
论文地址:https://arxiv.org/pdf/1911.05722v3.pdf
源码地址:https://github.com/facebookresearch/moco
Introduction
利用动量对比的无监督视觉表示学习
从对比性学习可以作为字典查找的角度来看,MoCo构建了一个带有一个队列和一个移动平均编码器的动态字典
这可以建立一个大型的并且一致的字典,可以促进对比无监督学习
MoCo在ImageNet分类任务的通用线性评估协议下提供了有竞争的结果
无监督学习的一个主要目的是预训练表示(即,特征),可以转移到下游任务通过微调
MoCo可以很好的向下游任务转移,在许多视觉任务中,无监督和有监督表征学习之间的差距在很大程度上被缩小
无监督学习在NLP领域很成功,诞生了GPT和BERT,但是在视觉领域,还是有监督占据主导地位
语言任务具有离散的信号空间(单词、子词单位等)用于构建标记化字典,它可以基于无监督学习
相比之下,计算机视觉的原始信号是在一个连续的高维空间中,而不是针对人类通信的结构
很多无监督学习可以被认为是构建动态字典
字典中的“键”(令牌)从数据(例如,图像或补丁)中采样,并由编码器网络表示。
无监督学习训练编码器来执行字典查找:一个编码的“查询”应该与它的匹配键相似,而与其他键不同
这种学习被表述为最小化对比损失
从这个角度来看,假设建立这样的字典是可取的:
- 大
- 在训练过程中的发展是一致的
直观地说,一个更大的字典可以更好地对底层的连续的、高维的视觉空间进行采样,而字典中的键应该由相同或相似的编码器表示,以便它们与查询的比较是一致的。
然而,使用对比损失的现有方法可以限制在这两个方面中的一个
MoCo将字典维护为数据样本的队列:
当前小批处理的编码表示入队,最老的表示出队。队列将字典大小与小批量大小解耦,允许它很大
此外,由于字典键来自于前面的几个小批,因此提出了一个缓慢发展的键编码器,作为查询编码器的基于动量的移动平均值来实现,以保持一致性
MoCo使用很多借口任务来训练
MoCo所使用的借口任务:如果查询是同一图像的编码视图(例如,不同的裁剪),则查询与键匹配
无监督学习,其核心是两个内容:
- 借口任务
- 损失函数
借口任务的种类:
- 恢复某些被破坏的输入
- 去噪自动编码器
- 上下文自动编码器
- 跨通道自动编码器
各种借口任务都可以基于某种形式的对比损失函数
对比损失度量了表示空间中样本对的相似性
Method
Contrastive Learning as Dictionary Look-up
对比学习任务,可以被认为是使用一个字典查找任务来训练一个编码器
考虑一个编码的查询q和一组编码的样本{k0,k1,k2,…},它们是字典的键
假设q匹配的是k+
对比损失是当q与它的正键k+相似,而与所有其他键不同(被认为是q的负键)时,其值较低的函数
用点积度量相似度
对比损失函数其中的一个,InfoNCE:
L q = − log exp ( q ⋅ k + / τ ) ∑ i = 0 K exp ( q ⋅ k i / τ ) \mathcal{L}_q=-\log \frac{\exp \left(q \cdot k_{+} / \tau\right)}{\sum_{i=0}^K \exp \left(q \cdot k_i / \tau\right)} Lq=−log∑i=0Kexp(q⋅ki/τ)exp(q⋅k+/τ)
对比损失也可以使用其他的形式的,如margin-based losses,或者NCE losses的其他变种
这里的q是经过编码器提取的特征:
q
=
f
q
(
x
q
)
q=f_{\mathrm{q}}\left(x^q\right)
q=fq(xq)
x是样本,f是编码器
x可以是图像,补丁,或者一组补丁构成的上下文
f q f_{\mathrm{q}} fq 和 f k f_{\mathrm{k}} fk 这两个网络可以是相同的,部分共享的,或者是完全不同的
Momentum Contrast![请添加图片描述](https://i-blog.csdnimg.cn/blog_migrate/a7653a3f9340a2fc14506663b1ea17eb.png)
从上述角度来看,对比学习是一种在如图像这种高维连续输入上构建离散字典的一种方法
字典是动态的,因为键是随机采样的,并且键编码器在训练过程中进化
我们的假设是,好的特征可以通过一个包含丰富的负样本集的大型字典来学习,而字典键的编码器尽管还在进化,但仍尽可能地保持一致
Dictionary as a queue
我们方法的核心是将字典作为数据样本队列
这允许我们重用前面小批的编码过的键
队列的引入可以将字典大小与batch大小解耦
我们的字典大小可以比一个batch大小大得多,并且可以灵活地、独立地设置为一个超参数
字典中的样本逐渐被替换。当前的小批将被排队到字典中,队列中最老的小批将被删除
该字典总是代表所有数据的一个采样子集,而维护该字典的额外计算是可管理的
此外,删除最古老的batch的数据可能是有益的,因为它的编码的key是最过时的,因此与最新的key最不一致
Momentum update
使用队列可以使字典变大,但它也使通过反向传播更新键编码器变得困难(这个梯度应该传播到队列中的所有样本)
一个简单的做法是直接复制查询编码器给key编码器,忽略梯度的信息
但是这种做法的效果不好
我们假设,这种失败是由快速变化的编码器,减少了关键表示的一致性
动量更新方法:
θ k ← m θ k + ( 1 − m ) θ q \theta_{\mathrm{k}} \leftarrow m \theta_{\mathrm{k}}+(1-m) \theta_{\mathrm{q}} θk←mθk+(1−m)θq
m是(0,1)的动量系数
只有q的参数的更新是使用反向传播的,k的参数并不会被反向传播更新,这样k的参数的更新还会比q的更加平滑
因此,尽管队列中的key由不同的编码器编码(以不同的batch)编码,但这些编码器之间的差异可以很小
一个相对较大的m=0.999 会比一个小m=0.9的效果更好
这表明,一个缓慢进化的key编码器是使用队列的核心
MoCo 和其他方法的不同
三者的不同在于:
- key是如何维护的
- key encoder是如何被更新的
第一种end-to-end,dict和batch都在显存中,因此dict的大小是有限制的,这可能会使这些网络向下游任务的转移复杂化
memory bank也是使用了动量更新,它的动量更新是在同一个样本的表示上,而不是编码器。
这个动量更新与MoCo不同,因为MoCo并没有跟踪每个样本。此外,我们的方法内存效率更高,可以在十亿规模的数据上训练,这对于memory bank来说是难以处理的
Pretext Task
如果一个查询和一个键来自同一图像,则我们将它们视为正对,否则则视为负样本对
我们在随机数据增强下对同一图像进行两个随机的“视图”,形成一个正对
查询和键分别由它们的编码器fq和fk进行编码。该编码器可以是任何卷积神经网络
技术细节
使用resnet作为encoder,最后一层全连接网络的输出向量,经过L2的归一化
t=0.07
数据增强为:
- 224x224的裁剪
- 随机的颜色抖动
- 随机的水平翻转
- 随机的灰度转换
Shuffling BN
我们发现,使用BN不会让模型学习的更好
模型似乎“欺骗”借口任务,容易找到低损失的解决方案。这可能是因为样本之间的批内通信(由BN引起的)泄漏信息
我们通过Shuffling BN来解决这个问题
我们使用多个GPU进行训练,并为每个GPU独立地对样本执行BN(正如在常见实践中所做的)
对于key编码器Fk,我们在将其分配到gpu之间之前,在当前的小批中打乱样本顺序(并在编码后调回顺序);查询编码器Fq的小批量的样本顺序没有改变。这确保了用于计算查询的批处理统计信息及其正键来自两个不同的子集
这有效地解决了作弊问题,并允许训练从BN中受益。
使用memory bank的模型没有这个问题,因为正键来自过去不同的batch
Experiments
使用的数据集
-
ImageNet 1k类,128万数据
-
Instagram-1B, 10亿数据, 该数据集相对未被整理好,并且具有真实世界数据的长尾、不平衡的分布
-
ImageNet 1k
- batch=256
- 8 GPU
- 200 epochs
- 53个小时
-
IG-1B
- batch=1024
- 64 GPU
- 1.4 epochs
- 6天
线性分类
1-crop, top-1
三种机制的比较,上面的K是字典大小
end-to-end的方式的性能与MoCo是相近的,但是因为很占用内存,因此最大的batch会达到限制
memory bank的方式性能会低,这与我们的假设是一致的:记忆库中的键来自过去的epoch的encoder,而且它们并不一致