参考链接
Introduction
- 无监督的表示学习在NLP领域已经取得了巨大的成功,比如:bert预训练模型;但是再CV领域,监督的表示学习还是比无监督的表示学习要好。这主要的原因是什么呢?论文认为:主要的原因是NLP和CV的信号空间不一样
- NLP是基于字典的离散的信号空间,所以可以基于这个字典进行无监督的表示学习。
- CV是信号空间是连续的,高维的和无结构的,所以该领域一直在关注构建字典进行无监督表示学习。
- 【对于预训练(表示学习)与词典的关系,可以通过原论文进行更深入的思考】
- 而最近的对比学习为CV的表示学习带来了希望,论文认为:对比学习相当于构建一个动态的字典。这个动态字典的key从数据中采样获得images 或者 patches【这些key就相当与NLP领域词典中的字】;key的表示通过一个编码网络产生【key的表示相当于词的embedding或者bert最后的隐藏层输出】,这个编码网络就是我们要学习的预训练模型。
-
思考:这里可以将图像的信息空间视为一个无限大(连续)字典,而对比学习构建的这个动态字典就是这个无限大字典的一个采样。这些样本的表示(embedding)是编码器的输出,那么这个编码的过程可以理解为查字典过程。
- 论文中提出了 Momentum Contrast(MoCo) 模型,用于构建一个大的一致性的字典。并且使用一个 队列 来存储这个字典(中间存储之前mini-batch中被编码样本的表示)。这种方法使得字典的大小不在依赖与mini-batch的大小,这就使得字典可以更大。
Method
Pretext Task(预训练任务)
-
为了更好的理解这篇论文的方法,我们先介绍一下论文所使用的预训练任务;
-
MoCo模型适用许多的预训练任务(pretext tasks),但论文中使用实例消歧任务(instance discrimination task) 来进行实验:预测一个query和一个key是否是来自同一张图片的不同视角(view)
-
了解的任务我们就可以看一下模型整体架构图:
-
顺着这张图可以直观的介绍一下论文使用的对比损失函数(contrastive loss):
其中
T
T
T是温度,
k
+
k_+
k+表示唯一的正例。分子的求和是
K
K
K个负例和一个正例。很明显的可以看到这实质上就是一个交叉熵损失函数。
-
这里我们也可以对比NLP的语言模型,把
q
q
q理解为前一个词,而
K
+
1
K+1
K+1个
k
i
k_i
ki理解为一个词汇表(或者词汇表的一个采样),那个这个唯一的正例
k
+
k_+
k+就可以理解为
q
q
q的下一个词;所以从这个角度来讲NLP的预训练也是一种对比学习。
-
对比学习的思想在于:用一对正例与很多对负例进行对比以此产生高质量表示(有区分度的表示)
-
实例消歧任务(instance discrimination task)的伪代码如下:
需要注意的是:每个当前mini-batch(
x
x
x)中只包含正例对
(
q
,
k
+
)
(q,k_+)
(q,k+)而负例都是从队列/字典(
q
u
e
u
e
queue
queue)中取出来的。
Momentum Contrast(动量对比)
- 这一步主要介绍:MoCo模型如何构建字典,如何更新字典,如何更新编码器。
- 上面已经讲到,论文认为:最近的对比学习可以被认为就是构建一个动态字典,然后训练一个encoder来完成字典查询的任务(对字典中key的编码任务);并且论文还指出越大的词典可能对学习好的表示和特征是有利的。
- 论文中将这个动态字典使用队列(queue)来实现,那么这个队列存的什么东西呢?如果更新这个队列中的东西呢?
-
队列里面存的是之前的mini-batch中样本的编码表示,那么对于当前的mini-batch中的样本,之前mini-batch中样本必定视为负样本。所以上面的伪代码中所有的负样本都是从队列取出来的,而当前mini-batch只包含正样例对。
-
队列的更新方式是:当当前步迭代完成后,将当前mini-batch中的样本进行编码替换队列中最老的mini-batch。是一个典型的先进先出的原则。
- 使用队列的方式可以使得字典的大小不受mini-batch大小的影响,使得我们可以使用更大的字典,在每个对比损失中使用更多的负样本,以此来提升表示的质量。当然有利就有弊,这也为我们优化key的编码器带来了问题。主要的原因在于:key编码器是在不断优化改进的;而队列中样本的表示是由以前key编码器产生的,这个编码器参数与当前编码器参数上已经有了一些不同。而在当前步计算对比损失时队列中的样本表示会将他们的梯度(以前key编码器产生的梯度)回传给当前的key编码器,这就会导致模型优化(学习)出现问题。
-
解决key编码器优化问题最简单的方法:每次将
f
q
f_q
fq(query的编码器)拷贝为
f
k
f_k
fk(key的编码器),丢弃之前的
f
k
f_k
fk;这就相当于不让字典中存的key进行梯度回传。但实验的结果不好。
- 论文认为,上面这种做法之所以效果不好是由于:key编码器变化太快,每次都之间武断的将
f
q
f_q
fqcopy给
f
k
f_k
fk,这降低了队列中key的编码表示的一致性。于是论文提出了使用动量更新的方法来更新key的编码器,以解决这个问题,具体公式如下:
θ
k
θ_k
θk是key编码器的参数,
θ
q
θ_q
θq时query编码器的参数,
m
m
m是动量系数;
- 在这种做法下,
θ
k
θ_k
θk已不经过反向传播进行更新,而是通过上面这个动量公式进行更新,只有
θ
q
θ_q
θq进行方向传播进行更新
- 然字典队列中的key的编码表示可能来自不同key编码器,但是通过这种基于动量的更加平滑的更新方式使得这些key的表示尽量保持一致。
- 在实验中:大的
m
=
0.999
m=0.999
m=0.999效果比更小的
m
=
0.9
m=0.9
m=0.9更好。
Relations to previous mechanisms(与以前相关工作的对比)
- 整体对比图:
-
end-to-end方式:这种方式可能是非常自然的想法,如果在显存足够的情况下这种方式应该是最好的,因为它没有如何不一致的情况。这里两个编码器同时通过反向传播去更新,这种方法有以下缺点:
- 在这种情况下,当前的mini-batch就是字典;由此可以看出mini-batch的大小就是字典的大小,所以字典的大小受到了GPU现存的限制。无法使用更大字典来改善模型的性能
- 即使有足够的现存,超大mini-batch的优化问题又是一个比较大的挑战。
-
memory bank方法:这个方法应该是本篇论文最大借鉴的方法
- 这种方法使用一个memory bank存储数据集中的所有样本,这相当与一个超大的字典;对于当前的mini-batch,从这个超大字典中抽取一个小的字典进行对比损失的计算【这类似于NLP中应对词典太大的负采样方法】。
- 需要注意的是这个框架是没有key编码器,memory bank中所用样本是通过query编码器进行编码的,每个样本在采样被采到后都会被query编码器重新编码再通过动量的方式去更新memory bank中样本的表示。这个动量方式是作用到样本表示上的,而不是编码器上,所以导致memory bank中的样本一致性比较差【这个观点应该是经验之谈,感觉不一定一致性会很差】。
Experiments
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)