参考:
- 论文:Momentum Contrast for Unsupervised Visual Representation Learning(用动量对比的方法去做无监督的表征学习)
- 李沐论文精度系列之《MoCo 论文逐段精读》、精读笔记
MoCo
于2019年11月13在 CVPR发表,并获得 CVPR2020最佳论文提名,它是用一种对比学习的方式进行无监督训练的模型。MoCo
是第一个在很多主流的机器视觉领域上(比如分类、检测、分割、人体关键点检测等),都超越了有监督预训练模型的无监督模型,从某种程度上证明了无监督学习在机器视觉领域,也能达到很好的效果。
MoCo
这个词,来自于论文标题的前两个单词动量对比Momentum Contrast
。
举个例子,从imagenet中抽出猫、猫、狗、飞机四张图,那么猫和猫的图片肯定是相似的,和狗不相似。但是和飞机比起来,猫和狗是相似的。所以对比学习就是对比着差异去学习,模型并不需要真的知道图片中代表的是什么,而只需要知道哪些图片是类似的,哪些图片是不一样的就可以了。
embedding space
)中,特征向量尽量靠近;反之还希望不同的数据学到的特征向量,尽量远离。pretext task
(代理任务):对比学习是不需要标签的(比如不需要知道图片是哪一类),但模型还是需要知道哪些图片是类似的,哪些是不相似的,才能训练。这就需要通过通过设计一些巧妙的代理任务,人为指定一些任务来实现。transformation
,比如随机裁剪等数据增广),得到新的图片 x i 1 x_{i1} xi1、 x i 2 x_{i2} xi2。那么样本 x i 1 x_{i1} xi1叫做基准点(锚点), x i 2 x_{i2} xi2被认为是正样本(两者都是从 x i x_i xi变化得到的,虽然看起来有差异,但语义信息不应该发生变化),数据集中其它所有图片都是负样本。instance discrimination
直译过来就是个体判别,在这个任务中,只有经过这张图片转换的样本才是正样本,其它图片都是负样本,所以每张图都自成一类。对于ImageNet来说,就不是1000类,而是128万个类别。NCE loss
等。 我们在机器视觉领域提出了一种新的无监督学习方法——MoCo
。MoCo
虽然是基于对比学习的,但是本文是从另外一个角度来看对比学习,即把对比学习看作是一个字典查询任务。
比如将上面提到的 x i x_i xi当做是
query
,其它包括 x i 1 x_{i1} xi1、 x i 2 x_{i2} xi2这些图片都是字典中的key
。我们每次判断正负样本,就是看字典中的这些key和query是否相似,而这些key
都是通过encoder
来更新的。
具体来说,我们构建了一个动态的字典,这个字典有两个特性:队列特性和moving-averaged encoder
(这两点在下文模型结构中会具体说明,现在记住就行)。因为这样,我们的字典非常大,且特征一致性非常好,从而便于进行对比学习。
最终,MoCo
作为一个无监督的预训练模型,能够在7个下游任务(分割、检测等)上 ,超越之前的有监督的预训练模型 ,填平了CV领域中,无监督训练和有监督训练之间的坑。
GPT和BERT已经证明了无监督的表征学习在NLP领域是非常成功的,但是在视觉领域,无监督学习效果差很多,作者认为可能是二者的原始信号空间不同。
- 在NLP任务中,原始信号空间是离散的(都是一些含有不同语义的单词或者词根),信号本来就拉得比较开,容易建立tokenize(将单词映射成向量)的字典。这样无监督学习容易建模,且模型容易优化。
- CV中,视觉信号都是在一个连续且高维的空间里,不想单词那样信息和语义浓缩的那么好,不够简洁,这样就不容易建立一个这样的字典,也就不容易进行无监督学习。
最近有一些无监督学习方法表现不错,但是都可以归结为建立动态字典。
如果将上一节讲到的所有样本都构建到一个字典中,字典的key就是各个样本,字典的value就是编码之后的特征(后面直接以 k 0 k_0 k0表示第一个样本的编码特征)。我们先编码好锚点的特征,当做query;其它所有样本特征当做字典中不同的key,那么那对比学习就转化成为了一个字典查询的问题了。
如下图所示,我们训练一些编码器,再根据q去字典中查找key。查找的目的,就是让已经编码好的特征q,和与它匹配的特征key(其实就是正样本 x i 2 x_{i2} xi2的特征)最相似;与其它不匹配的特征不相似。
在MoCo这篇论文当中,因为作者已经把所有的对比学习的方法归纳成为了一个动态字典的问题,所以很少使用anchor或者正负样本这些词,用的都是query和key。所以锚点 x i 1 x_{i1} xi1用 x q u e r y x^{query} xquery表示,其编码特征用q表示。其它样本和对应特征分别用 x i k e y x_{i}^{key} xikey和 k i k_i ki表示。
作者认为,一个好的字典应该有两个特性:
key
越多,所能表示的视觉信息、视觉特征就越丰富 ,这样拿query
去做对比学习的时候,才越能学到图片的特征。key
都应该用相同或者说相似的编码器去编码得到,否则模型在查找query
时,可以简单的通过找到和它使用相同或者相似编码器的key
,而不是真的和它含有相同语义信息的key(变相引入两一个捷径)。以前的对比学习,都至少被上述所说的两个方面中的一个所限制(要么一致性不好,要么字典不够大)。本文最大的贡献,就是使用队列以及动量编码器来进行对比学习,解决了这个问题。具体来说:
key
(编码特征)并不需要梯度更新,而是通过更新编码器,新的编码器使输出的key
更新。queue
:整个队列里面的元素都是字典,队首输入当前batch的编码特征,队尾弹出最旧的batch特征。每次移除的是最老的那些key,从一致性的角度来说 ,有利于对比学习。
momentum encoder
:
MoCo
中m=0.999
)。query
的编码器,之后每次更新,只有1‰
的参数会从query
的编码器参数里拿过来更新,所以这个编码器参数更新的非常缓慢。从而保证了字典中所有的key都是由相似的编码器抽取得到的,尽最大可能地保持了他们的一致性。(直接更新编码器k的所有参数,会导致编码器更新过快,降低了这个队列中所有key的特征的一致性)端到端学习,顾名思义就是编码器都是可以通过梯度回传来更新模型参数的,优缺点都很明显:
memory bank
中,q的编码器是梯度更新的,但是字典中的k,是没有单独的编码器。memory bank
把整个数据集的特征都存到了一起。每次训练时,只需要从memory bank
中采样一些key来作为字典(比如 k 1 k_1 k1、 k 2 k_2 k2、 k 3 k_3 k3),然后正常计算q和k的loss,进行梯度回传更新编码器。memory bank
的更新,依此类推。ImageNet虽然有128万张图片,即128w的key,但是特征维度为
dim=128
,用memory bank
存下来只需要600M,所以这样做是没问题的。但是对于一个拥有亿级图片规模的数据,存储所有的特征就需要几十G甚至上百G的内存了,所以memory bank的扩展性不如MoCo好。
但是这样做有一个明显的问题,就是特征的一致性非常差。表现在:
memory bank
存储了所有的图片,也就意味着模型训练了整整一个epoch才能把整个memory bank更新一遍,那也就意味着,当开始下一个epoch训练的时候,假如选了三个key,那这三个key的特征都是上一个epoch不知道哪个时间点算出来的特征了,这也就导致query的特征和key的特征差的特别远。memory bank
的作者也意识到了这一点,所以使用另外一个loss(proximal optimization),目的就是为了让训练变得更加平滑,而且也提到了动量更新,只不过它的动量更新的是特征。 由此,作者才提出了MoCo
,采用队列的形式去实现字典,使其不必受限于字典的大小;使用动量编码器进行缓慢更新,使特征保持一致性。
在本文中,采取了一个叫做InfoNCE
的对比学习函数来训练整个模型。
式子中,τ
是一个超参数。如果去掉τ
,整个式子其实就是交叉熵损失函数(cross entropy loss
),在后面的伪代码中,也是基于cross entropy loss实现。
MoCo
使用 instance discrimination
作为代理任务,那么光是ImageNet数据集,就有128万个类别,直接计算,复杂度会非常高,难以训练(128万类的softmax)。NCE loss
(noise contrastive estimation
):将超级多分类转为二分类——数据类别data sample和噪声类别noisy sample。这样解决了类别多的问题。
estimation
:近似的意思。为了降低计算复杂度,不是在每次迭代时遍历整个数据集128万张负样本,而是只从数据集中选一些负样本来计算loss(也就是选队列字典中的6万多个负样本),相当于一种近似。所以这也是MoCo
一直强调的希望字典足够大,因为越大的字典,越能够提供更好的近似。
InfoNCE
:NCE的一个简单的变体.
τ
:一个超参数,用来控制分布的形状 。τ越大,分布中的数值越小,经过exp之后就更小了,分布就会变得更平滑,相当于对比损失对所有的负样本都一视同仁,导致学习的模型没有轻重 对于整个模型来说,在代理任务不一样的时候,输入 x q x^q xq和 x k x^k xk既可以是图片,也可以是图片块(CPC),或者是含有上下文的一系列的图片块。
query
的编码器和key
的编码器既可以是相同的(模型的架构一样,参数完全共享,比如Inva Spread),或者说它们的参数是部分共享的,也可以是彻底不一样的两个网络(CMC,多视角多编码器)。
上面提到的
CPC、CMC、Inva Spread、SimCLR、InstDisc
在后面对比学习综述中都会简单介绍。
下面是论文中作者给出的伪代码,其中:
c*k
,c指的是每个特征的维度(c=128
)m
是动量,t
是InfoNCE
里面的超参数τ
fq
,并将其参数赋值给编码器f_k
x_q
和x_k
,然后通过各自的编码器得到特征q
和特征k
(大小都是N*C
)。key不需要梯度回传,所以用.detach() 去掉梯度信息。q 、k
之间计算logit
(正样本),也就是之前公式1中算InfoNCE loss的时候的分子 q ∗ k + q * k+ q∗k+,其特征维度就变成了n * 1
(256,1
)。q、queue
拿出来计算,得到InfoNCE的分母,也就得到了负样本的logit,维度是n*k
(256*65536
,MoCo中,字典大小为65536)cat
拼接ground truth
来进行计算。fq
f_k
f_k.params = f_q.params # 初始化
for x in loader: # 输入一个图像序列x,包含N张图,没有标签
x_q = aug(x) # 用于查询的图(数据增强得到)
x_k = aug(x) # 模板图(数据增强得到),自监督就体现在这里,只有图x和x的数据增强才被归为一类
q = f_q.forward(x_q) # 提取查询特征,输出NxC
k = f_k.forward(x_k) # 提取模板特征,输出NxC
# 不使用梯度更新f_k的参数,这是因为文章假设用于提取模板的表示应该是稳定的,不应立即更新
k = k.detach()
# 这里bmm是分批矩阵乘法
l_pos = bmm(q.view(N,1,C), k.view(N,C,1)) # 输出Nx1,也就是自己与自己的增强图的特征的匹配度
l_neg = mm(q.view(N,C), queue.view(C,K)) # 输出Nxk,自己与上一批次所有图的匹配度(全不匹配)
logits = cat([l_pos, l_neg], dim=1) # 输出Nx(1+k)
labels = zeros(N)
# NCE损失函数,就是为了保证自己与自己衍生的匹配度输出越大越好,否则越小越好
loss = CrossEntropyLoss(logits/t, labels)
loss.backward()
update(f_q.params) # f_q使用梯度立即更新
# 由于假设模板特征的表示方法是稳定的,因此它更新得更慢,这里使用动量法更新,相当于做了个滤波。
f_k.params = m*f_k.params+(1-m)*f_q.params
enqueue(queue, k) # 为了生成反例,所以引入了队列
dequeue(queue)
下图是端到端学习、memory bank和MoCo三种流派的模型,在只做特征提取时候的精度对比:
MoCo
性能最好,对硬件要求最低,而且扩展性也比较好归一化
预训练好的MoCo
做微调,其学习率需要设为30,远大于以前模型的微调时的一些学习率(比如lr=0.03)说明MoCo学到的特征跟有监督学到的特征的分布是非常不一样的,但是不能每次微调时都去grid search找一下它最佳的学习率是多少,这样失去了微调的意义。
当分布不一致的时候,最常想到的方法就是归一化,所以作者这里使用了特征归一化的方法(整个模型都做BN,包括检测时用到的FPN结构,也使用BN)。做完归一化之后,就可以拿这些有监督训练用的超参数来做微调了。
在keypoint detection人体关键点检测、pose estimation姿态检测、实例分割、语义分割四个任务中做测试:
结论:MoCo
预训练的模型在大部分时候都比ImageNet的有监督预训练模型要好,在实例分割和语义分割的任务上有时候会稍差一些。
所以大家怀疑对比学习可能不太适合做这种每个像素的都要预测的任务,基于这一点,后续发展出dence contrast或者是pixel contrast。
MoCo
的主要贡献就是把之前对比学习的一些方法都归纳总结成了一个字典查询的问题,并提出了队列存储和动量编码器。前者解决字典太大不好存储和训练的问题,后者解决了字典特征 不一致的问题;从而形成一个又大又一致的字典,能帮助模型更好的进行对比学习。
MoCo
跟Inst Disc
是非常相似的,比如它用队列取代了原来的memory bank作为一个额外的数据结构去存储负样本,用动量编码器去取代了原来loss里的约束项,这样就可以动量的更新编码器,而不是动量的去更新特征,从而能得到更好的结果。其整体的出发点以及一些实现的细节(比如backbone和lr、batch_size,dim、τ等等超参数都是一样的)和Inst Disc
都是非常类似的,所以可以说MoCo
是Inst Disc
的改进工作。但是MoCo真正出色的地方其实有两点 :
MoCo
还有一个优点,就是训练比较便宜。在一张8卡V100 16G GPUs
上,训练200个epoch只需要53
小时(batch_size=256,GPU memory=5.3G),完全就是大佬给我们送福利。MoCo
这篇论文以及它高效的实现,能让大多数人有机会用普通的GPU就能跑对比学习实验,做自己的研究。
最后,因为MoCo在各个视觉任务上取得了更好的性能,也激发了很多后续分析性的工作,去研究MoCo学出来的特征到底和有监督学出来的特征有什么不同,还能从别的什么方向去提高对比学习。
参考:李沐论文精度系列之《对比学习论文综述》、精度笔记
如果把 近几年对比学习在视觉领域有代表性的工作做一下总结,那么对比学习的发展历程大概可以分为四个阶段:
下面就简单介绍一下这14篇工作,重点是其研究动机。
- 《Unsupervised Feature Learning via Non-Parametric Instance-level Discrimination》
- 参考:李沐论文精度系列之《对比学习论文综述》、精度笔记
- 参考:《对比学习一 |Instance Discrimination》、《Instance Discrimination论文阅读笔记》、
这篇文章提出了个体判别任务(代理任务)以及memory bank
,非常经典,后人给它的方法起名为InstDisc。
在有监督学习的分类模型中,如果给一张豹子图片进行分类,会发现排前几名的都是跟这张图很像的图片,而排名靠后的那些往往是跟豹子一点关系都没有的类别。
作者研究发现,让这些图片聚集在一起的原因并不是因为它们有相似的语义标签,而是因为这些照片里的物体都很相似。最后作者由此提出了个体判别任务:把每一个instance(实例,这里就是指每一张图)都看成是一个类别,目标是学一种特征,把每张图片都区分开来。
1. 模型结构
将图片经过CNN网络编码后得到的图片特征,使用对比学习的方式将其在特征空间中尽可能的区分开来(因为每张图都是自己的类)。
既然是对比学习,就需要正负样本。InstDisc中正样本就是就是这个图片本身(可能经过一些数据增强),负样本就是数据集里所有其它的图片,这些负样本都存储在 memory bank里。对于ImageNet有128万张图片,那么memory bank就要存储128万行,所以最后每张图都用128维特征表示(维度太高存储不了)
2. 前向过程
NCELoss
。然后根据loss更新backbone和memory bank(把 mini batch里的数据样本所对应的那些特征,在 memory bank 里更换掉,这样无论是训练还是测试就都来自于一个度量空间了)。3. 训练细节
本文的一些超参数设定,比如backbone选择ResNet50,batch_size=256,负样本采样数为4096,特征维度dim=128,epoch=200,初始lr=0.03,计算NCELoss时τ=0.07;这些超参数在在MoCo 中也是沿用的,没有进行更改。
P ( i ∣ v ) = e x p ( w i T v ) ∑ j = 1 n e x p ( w j T v ) P(i|v)=\frac {exp(w_i^{T}v)} {\sum_{j=1}^n exp(w_j^Tv)} P(i∣v)=∑j=1nexp(wjTv)exp(wiTv)
其中 v是卷积网络输出的特征表示,i 是预测类别(实例级),w是需要优化的权重向量。
P ( i ∣ v ) = e x p ( v i T v / τ ) ∑ j = 1 n e x p ( v j T v / τ ) P(i|v)=\frac {exp(v_i^{T}v/\tau)} {\sum_{j=1}^n exp(v_j^Tv/\tau)} P(i∣v)=∑j=1nexp(vjTv/τ)exp(viTv/τ)
使用Mermory Bank V 来存储上述的 v j v_j vj,在每个iteration对应修改其值 f i → v i f_i\to v_i fi→vi,在初始化时通过单位随机向量对V进行初始化。
P ( i ∣ v ) = e x p ( v T f i / τ ) Z i P(i|v)=\frac {exp(v^{T}f_i/\tau)} {Z_i} P(i∣v)=Ziexp(vTfi/τ)
Z i = ∑ j = 1 n e x p ( v T f i / τ ) Z_i=\sum_{j=1}^n exp(v^{T}f_i/\tau) Zi=j=1∑nexp(vTfi/τ)
我们设定噪声分布为一个均匀分布 P n = 1 / n P_n=1/n Pn=1/n,则v属于第i个个体的后验概率为:
h ( i , v ) = P ( D = 1 ∣ i , v ) = P ( i ∣ v ) P ( i ∣ v ) + m P n ( i ) h(i,v)=P(D=1|i,v)=\frac {P(i|v)}{P(i|v)+mP_n(i)} h(i,v)=P(D=1∣i,v)=P(i∣v)+mPn(i)P(i∣v)
训练目标为最小化似然函数 J N C E ( θ ) = − E P d [ log h ( i , v ) ] − m ⋅ E P n [ log ( 1 − h ( i , v ′ ) ] J_{NCE}(\theta)=-E_{P_d}[\log h(i,v)]-m \cdot E_{P_n}[\log(1-h(i,v^\prime)] JNCE(θ)=−EPd[logh(i,v)]−m⋅EPn[log(1−h(i,v′)]
其中 P d P_d Pd指代真实数据分布,对 P d P_d Pd而言v 是 x i x_i xi 的特征;v ′ 是来自另一幅图片,从噪声分布 P n P_n Pn中随机采样得到,v 和v ′ 都是从Memory Bank中采样得到的。
在正向计算时, 分母项 ∑ j = 1 n exp ( v j T v / τ ) \sum_{j=1}^{n} \exp \left(\mathbf{v}_{j}^{T} \mathbf{v} / \tau\right) ∑j=1nexp(vjTv/τ)的计算是无法避免的, 直接计算的计算量同样很大, 于是本文使用蒙特卡罗方法来估计这一项:
Z ≃ Z i ≃ n E j [ exp ( v j T f i / τ ) ] = n m ∑ k = 1 m exp ( v j k T f i / τ ) . Z \simeq Z_{i} \simeq n E_{j}\left[\exp \left(\mathbf{v}_{j}^{T} \mathbf{f}_{i} / \tau\right)\right]=\frac{n}{m} \sum_{k=1}^{m} \exp \left(\mathbf{v}_{j k}^{T} \mathbf{f}_{i} / \tau\right). Z≃Zi≃nEj[exp(vjTfi/τ)]=mnk=1∑mexp(vjkTfi/τ).
由于每个“类”只有1个样例,在每个epoch中,一个“类”只被访问一次,训练的过程比较不稳定。为了使训练更加平滑,在损失函数上增加一项针对v 的惩罚, 来稳定训练过程:
− log h ( i , v i ( t − 1 ) ) + λ ∥ v i ( t ) − v i ( t − 1 ) ∥ 2 2 -\log h\left(i, \mathbf{v}_{i}^{(t-1)}\right)+\lambda\left\|\mathbf{v}_{i}^{(t)}-\mathbf{v}_{i}^{(t-1)}\right\|_{2}^{2} −logh(i,vi(t−1))+λ∥ ∥vi(t)−vi(t−1)∥ ∥22
其中, v i ( t ) = f θ ( x i ) v_i^{(t)}=f_\theta(x_i) vi(t)=fθ(xi)(第t次迭代时backbone的输出特征), V = v i ( t − 1 ) V={v_i^{(t-1)}} V=vi(t−1)来自于memory bank。这样随着多次迭代,由于 v i ( t ) − v i ( t − 1 ) {v_i^{(t)}}-{v_i^{(t-1)}} vi(t)−vi(t−1)的加入,backbone和memory bank存储的特征就逐渐相同了,回到了原始的损失,加速了收敛。
所以Proximal Regularization相当于模型的训练加了一个约束,从而能让 memory bank 里的那些特征进行动量式的更新(当前时刻的输出和上一时刻的输入有关),跟 MoCo 的想法是非常一致的。
Inst Disc
这篇论文也是一个里程碑式的工作:它不仅提出了个体判别这个代理任务,而且用这个代理任务和 NCE loss做对比学习,从而取得了不错的无监督表征学习的结果。同时它还提出了用别的数据结构存储这种大量的负样本,以及如何对特征进行动量的更新,所以真的是对后来对比学习的工作起到了至关重要的推进作用。
- 论文:《Unsupervised Embedding Learning via Invariant and Spreading Instance Feature》
- 知乎《对比学习二 | Unsupervised Embedding Learning via Invariant and Spreading Instance Feature》
这篇文章作者同样没有为自己的方法起名字,所以后面一般将其简称为Inva Spread
。Inva Spread
是一种端到端的训练方式,直接训练特征本身,无需额外的数据结构(比如上文的memory bank),提升了效率和准确度。作者还使用了新的采样方式,降低了计算复杂度。
简单来说,本文中的正负样本都来自同一个mini_batch。比如对于图片 x i x_i xi,其正样本就是数据增强后的图片 x i ′ {x_{i}}' xi′,而负样本就是这个mini_batch中除了 ( x i , x i ′ ) (x_i,{x_{i}}') (xi,xi′)之外的所有样本,而不是整个数据集中的所有其它样本。这样负样本数大大减少,可以不需要额外的数据结构来存储,就可以用一个编码器做端到端的训练了。
Inva Spread
可以看做是SimCLR
的前身,但由于数据增强策略不足以及负样本数量太少,也没有SimCLR
提出的mlp projector ,使得最终的训练效果不好,没有太大的影响力。
Inva Spread
的作者太穷,没有TPU,只能选择batch_size=256
来训练。这样每次迭代的负样本只有255*2个,数量太少,对比学习的效果不够好(也就是在MOCO中说过的字典太小)。而SimCLR
的作者来自谷歌,可以使用大量的TPU,最终训练的batch_size=8192
,足以达到不错的训练效果。
作者认为提升效率的方法就是直接优化特征本身,拒绝额外的数据结构,也就是用端到端的方式。但这样做会有两种阻碍:一是如果抛弃通过参数w来学习,也不采用memory bank利用时间差更新而让特征自己乘自己,就会使得网络得不到训练。二是不采用NCE等方式,训练的复杂度就太大了。
作者认为,相似图片通过编码器以后,它的特征应该很类似,不同的图片,它的特征出来就应该不类似,这就是题目中说的invariant和 spreading 。于是作者提出的孪生神经网络结构,有效地解决了这两个问题:
论文:《Representation Learning with Contrastive Predictive Coding》
之前的几篇代理任务都是个体判别任务,那么自然也有生成式的代理任务,CPC就是其中之一,它使用预测的代理任务去做对比学习。CPC是一个通用结构,其输入是一个序列,可以是图片(不同patch)、文字或者音频、视频等等。本文使用音频为输入,如下图所示:
论文:《Contrastive Multiview Coding》、《对比学习四 | Contrastive Multiview Coding》
CMC
使用一个物体的多个视角来作为正样本。这个思想来自于人类对世界的感受、观察。
在摘要中,作者说人类观察这个世界是通过很多个不同视角的传感器,比如说眼睛或者耳朵,来给大脑提供不同的信号。每一个视角都是带有噪声的,而且有可能是不完整的。但是最重要的那些信息,比如物理性质,几何形状以及语义信息,在所有的这些视角中间共享。例如一只狗可以被看到、听到、感受到。
基于此,作者认为一个强大的特征,应该具有视觉不变性(不论是看到还是听到,都应该能判断出那是一只狗)。所以CMC目的,就是最大化同一个场景不同视角的互信息,并且可以扩展到任意数量的未知视角,且视角越多效果越好。
如上图所示,CMC
选用 NYU RGBD
数据集进行 训练。数据集中每张图有4个视角(view):原始的图像、原图对应的深度信息(每个物体离观察者到底有多远)、SwAV ace normal以及原图的分割图像。
在CMC中,一张图的四个视角就是互为正样本,因为其代表的是同一个东西;其它的图片就是负样本。在上图表示,就是特征空间中四个绿色的点互相靠近,而都和红色的点远离。
CPC可以看做是学习过去和未来两个视角,个体判别是学习一张图片的不同crops,但使用的却都是一种目标函数。本文使用的也是普通的NCELoss目标函数,但作者将其进行扩展以适应不同视角的需求,对比学习也扩展到了很多其他领域。
这里的 f θ 1 f_{\theta }^{1} fθ1 和 f θ 2 f_{\theta }^{2} fθ2 是两种backbone,不共享参数,这个和Spreading Instance是有区别的。
2. 多个视角目标函数,有两种范式:
cmc原班作者人马还用对比学习的思想做了一篇蒸馏的工作。
对于teacher
模型和student
模型,不论用什么网络,不论这个网络是好是坏是大是小,只要你的输入是同一张图片,那得到的这个特征就应该尽可能的类似,即二者的输出尽可能的相似。通过这种方式把teacher
和student
做成了一个正样本对,从而可以做对比学习。
CMC
正负样本确定的方式由个体升级成了个体的不同的视角(如色彩模型)。它同样使用了NCE,但将其扩展以适应不同的视角。CMC
采用多视角对比学习,证明了对比学习的灵活性,也同时证明了多视角多模态的可行性,为之后的CLIP工作(图文配对的多模态对比学习)打下了基础。
但是本文也有一个局限,即处理不同的视角(模态)时,可能需要不同的编码器,因为不同的输入特点不一样。 如果每个视角都有一个编码器,那么训练的成本就有点高(比如在CLIP里,文本编码器是BERT,图片编码器是ResNet或者ViT)。
所以这也是现在Transformer
最吸引人的地方,这个结构可以同时处理文本和图片,那么就可以用一个解码器处理两种模态,而不用做针对每种数据去做特有的改进。今年在ICLR上发表的MA-CLIP,就是用一个Transformer
去同时处理两个输入模态,效果反而更好。
关于CLIP及其7篇拓展工作(目标检测。实例分割。图片生成),可以参考我另一篇博文《李沐论文精读系列四:CLIP和改进工作串讲(LSeg、GroupViT、VLiD、 GLIPv1、 GLIPv2、CLIPasso)》
参考:李沐论文精度系列之《对比学习论文综述》、精度笔记
这段时间(2019Mid-2020Mid)代表作就是MoCo和SimCLR,最后还有个SwAV没用负样本,承上启下。
见上文
《A Simple Framework for Contrastive Learning of Visual Representations》、代码(TF)
SimCLR
是2020年2月13上传到arxiv的,其想法非常简单,在很多博客里介绍对比学习都会将其作为例子,如下图所示:
图片x经过不同的数据增强得到不同的图片 x i ~ \tilde{x_{i}} xi~和 x j ~ \tilde{x_{j}} xj~,这两个就是互为正样本;同一个mini_batch里面的其它图片都是负样本,这点和inva spread
一样。
正负样本经过同一个编码器 f ( ⋅ ) f(\cdot ) f(⋅)(权重共享)得到编码特征 h i , h j h_i,h_j hi,hj。比如encoder选ResNet50,就是输出2048维特征。
h i , h j h_i,h_j hi,hj经过同一个projector即图中的 g ( ⋅ ) g(\cdot ) g(⋅)(其实就是一个mlp层,全连接层+relu激活)得到最终的对比学习特征 z i , z j z_i,z_j zi,zj(128维)。
对比学习的训练目标就是使正样本特征更相似(同一张图片得到的 z i , z j z_i,z_j zi,zj),而负样本的特征不相似。
选用的损失函数是 NT-Xent loss
(the normalized temperature-scaled cross entropy loss)。normalized是指在特征后面进行了 L2 归一化,temperature-scaled 就是说在 loss 里加了个τ,所以和infoNCE loss
也是非常接近的。
projector在训练时才使用,推理时直接去掉,只用特征h特征。
inva spread
SimCLR
可以被认为是inva spread
的改进工作。其最大创新点就是在图片编码特征之后加了一个projector
,但就这么简简单单的一层mlp,能让模型在ImageNet 分类任务上直接涨了近10个点。 SimCLR
的前两点贡献,添加projector
和使用的数据增强,在之后的对比学习模型(MoCov2、BYOL)中也一直被沿用。
1. 模型效果
SimCLR (4×)
这个模型可以在 ImageNet 上面达到 76.5% 的 Top 1 Accuracy,比当时的 SOTA 模型高了7个点。如果把这个预训练模型用 1%的ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,SimCLR 就可以再达到 85.5% 的 Top 5 Accuracy,也就是再涨10个点。
2. 数据增强
作者试验了以上10种数据增强,比如随机裁剪、变换色彩、翻转、Cutout、高斯噪声、blur噪声等等;并做了如下的消融试验(除了最后一列,余下是两两组合)。最后发现随机的裁剪和随机色彩变换组合效果最好。
3. Projection head 及特征维度
论文:《Improved Baselines with Momentum Contrastive Learning》、代码
MoCov2
主要是借鉴了SimCLR
而做的优化,比如引入了mlp projection head以及使用更多的数据增强。MoCov2
刷新了ImageNet 上的最好成绩,比之前的MoCo
以及最新的SimCLR
都高很多 。其上传的日期是3月9日,离SimCLR
的发布还不到一个月。
MoCov2
对比MoCo
主要有4个改动:
上图列出了模型效果对比图。
MoCov1
以及 SimCLR
在ImageNet数据集上分类效果对比。MoCov2
比SimCLR
高了大概一个点MoCo v2
能到71.1,比SimCLR
训练了1,000个epochs还要好将近2个点。这就意味着MoCov2
能更好的利用数据,能在更短的时间内取得更好的结果MoCov2
相比SimCLR
,其训练时消耗的内存以及训练时长都更少。下表的end-to-end其实就是指SimCLR
。8卡V100 16G GPUs
上,训练200个epoch只需要53
小时(batch_size=256,GPU memory=5.3G),大佬又给我们送福利啦†
这个符号表示是作者的推测,但是因为没有这么大的GPU,所以最终没有训练时长这一项。
- 论文: 《Big Self-Supervised Models are Strong Semi-Supervised Learners》
- 知乎《自监督黑马SimCLRv2来了》、 《半监督学习之Noisy Student》
SimCLRv2
的主要思想体现在其标题里,即大的自监督模型很适合做半监督学习。在摘要中,作者提出:一种从少量带标签数据+大量无标签数据中进行学习的方案是:无监督预训练(必须是大模型)+有监督微调,这种半监督学习的方案在ImageNet上极为有效,具体的可以总结为三步:
pretrain
:在无标签数据上无监督训练(SimCLR对比学习)一个Big ResNet模型(模型大小至关重要)以学习广义视觉特征表达。fine-tune
:在少量有标签数据上通过进行有监督的微调distill
:用微调后的模型作为teacher
模型,在之前的无标签数据集上生成伪标签,然后训练一个student
模型进行自监督训练(蒸馏阶段采用KL散度)。微调后,作者发现:模型的任务已知预测属性可以进一步改善并蒸馏到一个更小的网络中。为此,作者对无标签数据进行了二次利用以促使学生网络尽可能的模拟老师网络的标签预测性能,且蒸馏阶段采用伪标签方式且不会造成额外的更多复杂度。
整个框架其实也是受启发于google的另外一篇工作 Noisy Student。noisy student
就是在ImageNet
数据集上先训练了一个 teacher 模型,然后在JFT 300M
那个数据集上生成了很多的伪标签,最后一起训练了一个student模型,其精度为88,霸榜ImageNet快一年。
SimCLRv2
在仅仅采用1%/10%
有标签数据时,backbone使用ResNet50就取得了73.9%/77.5%
的top-1精度。
SimCLRv2
相比SimCLRv1
有三处改进:
ResNet50
替换为ResNet152+SK net
(selective kernels)protection head
:从一层加到两层。MOCO
的动量编码器,效果提升了一个点。SimCLR
模型的 batch_size已经够大了,也就是字典的大小和字典里特征一致性,SimCLR v2 都已经做的很好了。换成MOCO
这种队列结构的动量编码器,虽然可训练的负样本更多,但是提升没有那么明显了。微调
SimCLRv1
在微调时,是去掉 g ( ⋅ ) g(\cdot ) g(⋅)(projector层),只保留编码器 f ( ⋅ ) f(\cdot ) f(⋅)进行微调,即 f t a s k ( x i ) = W t a s k f ( x i ) f^{task}(x_{i})=W^{task}f(x_{i}) ftask(xi)=Wtaskf(xi);SimCLRv2
在微调时,是保留 g ( ⋅ ) g(\cdot ) g(⋅)的第一层 ,即 f t a s k ( x i ) = W t a s k ⋅ σ ( W M L P ⋅ f ( x i ) ) f^{task}(x_{i})=W^{task}\cdot \sigma (W^{MLP}\cdot f(x_{i})) ftask(xi)=Wtask⋅σ(WMLP⋅f(xi)) 下表给出了微调的SimCLRv2
、编码器+线性分类器(SimCLRv2
不微调)和有监督基线模型,在ImageNet上的Top-1精度。可以看到:提升模型宽度、深度以及添加SK可以取得更好的性能。
- 论文《Unsupervised Learning of Visual Features by Contrasting Cluster Assignments》
- 《无监督对比学习之假装自己有监督的SwAV》、
SwAV
即swap assignment view的缩写,意思就是一张图片不同视角的特征可以互相预测,因为来自同一张图片的不同视角特征按道理来说都是相似的。具体的做法,就是将聚类加入到了对比学习中。(将匹配问题转为预测问题,预测时借助簇类中心)
作者认为之前的对比学习,直接拿所有图片的编码特征去做对比有点原始而且计算量太大,因为所有的图片都是自己的类。作者考虑,能不能不做近似,能不能借助一些先验信息,一些更简洁的东西比进行对比,而不是和所有负样本直接进行对比。由此作者提出了可以和聚类中心特征进行对比(128万张图片被聚成3000个簇类中心cluster center
)。
比如MoCo在ImageNet上训练那就有128万类,即使在计算loss时取近似,只是取队列编码器里的作为负样本,那负样本也有6万多个。
之前的一些聚类方法常常将ImageNet
数据集聚成3000
个簇类中心。
作者选择聚类这个想法有两个原因。首先,聚类方法也是一种无监督的特征表示学习方式,其目标也是希望相似的物体聚在一起,不相似的物体尽量互相远离,这个思想与做法和对比学习都比较接近;第二就是论文一作之前是做聚类的,比如deep cluster,也是一篇很好的无监督学习论文。
SwAV
的做法
用聚类做对比学习的好处到底有哪些?
SwAV
也提出了一种新的数据增强方法Multi-crop
(多次裁剪)。
Multi-crop
:一张图片经过两个160×160的crop,和四个96×96的crop得到6个正样本。这个想法非常简单但是确实有用,在后面的很多对比学习中也被一直沿用。
1. 不同模型在ImageNet上的Top-1精度对比
SwAV
(batch_size=4096,且训练了800epoch)比目前最好的无监督模型MoCov2
还要高4.2个点,和自监督的baseline模型只差了1.2%(之后要讲的BYOL和SimSiam都是74点几)。SwAV5×
)和有监督baseline模型精度差缩小到了0.6%。下面是将模型作为特征提取器后在ImageNet上训练不同epoch时的的top-1精度对比(backbone都是ResNet50)
SwAV
训练不同的epoch时,训练时长和精度对比简单提一下。CPCv2其实也是融合了很多的技巧,它用了更大的模型、用了更大的图像块、做了更多方向上的预测任务,把batch norm 换成了 layer norm,而使用了更多的数据增强,所以这一系列操作下来,CPC v2直接就把CPC v1之前在 ImageNet 上40多的准确率一下就拔到70多。
参考:李沐论文精度系列之《对比学习论文综述》、精度笔记
其实在上一阶段已经有不用负样本的趋势了,比如SwAV
就是用的聚类中心进行对比。接下来要讲的BYOL和SimSiam其实就是正样本自己在玩,已经没有负样本或者聚类中心这样明确的一个对比的东西去做对比了。
- 论文 《Boostrap Your Own Latent:A New approach to Self-Supervised Learning》
- BYOL分析博客《Understanding self-supervised and contrastive learning with “Bootstrap Your Own Latent” (BYOL)》
BYOL
就是论文标题Boostrap Your Own Latent
的缩写。Latent、Hidden、Feature、Embedding其实都是特征的意思,就是各种花里胡哨的用法而已;Boostrap就是类似自我改造的意思。
BYOL
使用了一种新的对比学习方法(A New approach),即没有引入任何形式的负样本,而是用图片的编码特征(梯度更新)去预测自己的编码特征(动量更新),模型就这样训练起来了。(相当于用一个视角的特征取预测另一个视角的特征,将匹配转为预测问题)
这种训练方式类似SwAV
,但是这次连簇类中心都没了,所以听起来有点不可思议。后来还有一篇博文分析了BYOL
,认为其实是在使用BacthNorm时引入了隐式的负样本进行对比学习。BYOL作者一听不高兴了,这样不是说明我的工作大大折扣了吗,所以立马写了一篇技术实验论文驳斥了这个说法,证明了对比学习完全不使用负样本是可行的(后面会详细介绍)。
SimCLR
中一样的projection head g ξ g_{\xi } gξ和 g θ g_{\theta } gθ(也是一个MLP,BYOL
中也把这个结构叫predictor
),将特征降到256维,得到特征 z θ , z ξ ′ z_{\theta },{z_{\xi }}' zθ,zξ′。BYOL
中,上分支使用prediction head
(也是predictor
结构)将 z θ z_{\theta } zθ映射为 q θ ( z θ ) q_{\theta }(z_{\theta }) qθ(zθ),然后用 q θ ( z θ ) q_{\theta }(z_{\theta }) qθ(zθ)去预测 s g ( z ξ ) ′ sg({z_{\xi }})' sg(zξ)′来进行对比学习,其中sg表示stop-gradient
,因为下分支编码器是动量更新。MSELoss
,即直接计算预测特征 q θ ( z θ ) q_{\theta }(z_{\theta }) qθ(zθ)和标签 s g ( z ξ ) ′ sg({z_{\xi }})' sg(zξ)′这两个向量之间的mse。推理:
当训练完成只留下编码器 y θ y_{\theta } yθ,剩下所有的东西都被拿掉了。然后用这个编码器编码图片,输出维特征去做下游任务的推理。
对比:
BYOL
就是将上分支输入经过一个梯度更新的编码器和两个predictor
得到的 q θ ( z θ ) q_{\theta }(z_{\theta }) qθ(zθ),去预测下分输入经过一个动量更新的编码器和一个predictor
得到的 s g ( z ξ ) ′ sg({z_{\xi }})' sg(zξ)′。BYOL
使用了MoCo的动量编码器、SimCLR的projection head
以及预测任务,但是没有负样本,目标函数也不一样。通过自己预测自己就学起来了。BYOL
的两个分支叫online和target,其实就相当于MoCo
中的query和key分支。model collapse
,表示模型根本就没有在学习)。BYOL
之所以神奇就是它没有用负样本,正样本自己跟自己学最后在ImageNet上也达到了74.3的top-1准确率,也是相当高了。SimCLR
MoCov2
(MoCo v1没有用projection head)Linear(2048×2048)+ReLU +Linear(2048×128)
。BYOL
Linear+BN+ReLU +Linear
BYOL
被认为是使用了隐式负样本 BYOL
发布到arxiv之后,在reddit、twitter、知乎全都引起了剧烈的讨论,因为大家都觉得很不可思议;不用负样本,只是自己预测自己,模型的学习怎么能不坍塌。由此引出了一篇博文《Understanding self-supervised and contrastive learning with “Bootstrap Your Own Latent” (BYOL)》。
这篇博文的作者在复现BYOL
时遗漏了一个小细节,即借用了 MoCov2
的projection head
导致projection head
中没有加batch norm
,最终模型坍塌。作者就觉得实在是太奇怪了,所以赶紧又做了一些额外的实验,如下表所示 :
Name | Projection MLP Norm | Prediction MLP Norm | Loss Function | Contrastive | Performance 5 |
---|---|---|---|---|---|
Contrastive Loss | None | None | Cross Entropy | Explicit | 44.1 |
BYOL | Batch Norm | Batch Norm | L2 | Implicit | 57.7 |
Projection BN Only | Batch Norm | None | L2 | Implicit | 55.3 |
Prediction BN Only | None | Batch Norm | L2 | Implicit | 48 |
No Normalization | None | None | L2 | None | 28.3 |
Layer Norm | Layer Norm | Layer Norm | L2 | None | 29.4 |
Random | — | — | — | None | 28.8 |
Projection MLP Norm
:第二第三列这里指的是两层Projector
有没有用归一化Loss Function
:普通对比学习loss是交叉熵损失函数,而BYOL
用的是L2 loss,即mse 损失函数。performance
:测试模型性能是在一个STL-10的数据集上做的,不是 ImageNet,但衡量标准还是准确度。实验结果:
random
:使用一个随机初始化的残差网络,没有经过任何训练,直接去抽特征。然后在这个特征上训练一个全连接层,最后的结果是28.8。所以这个结果是一个完全随机的结果。BYOL
:两层Projector
都使用BN,效果最好BYOL
变体:只使用一层BN,模型起码 也有学到东西。如果是都用LN或者干脆都不用BN,模型坍塌,什么都没学到。最终分析:
作者认为在Projector
层使用BN之后,是计算了整个batch的均值和方差,这意味着是有信息泄露的(MoCo使用了 Shuffling BN ,就是为了防止这种信息泄露)。模型不光是正样本自己和自己学,还和batch norm产生的平均图片(mode,中值)对比着学,这种平均图片就类似 SwAV
的聚类中心了。
所以说,这篇博客的作者认为batch norm是BYOL
能够成功的关键,其实是做了一种隐式的对比学习,这个观点很快就被大家所接受了,因为听起来确实很合理,而且后续试验也都验证了这一点。batch norm确实至关重要,拿掉batch norm以后模型就是不好训练,对超参数的设置非常的敏感,稍有不慎它就啥也不学了。
BYOL
不使用负样本的思想是没问题的 BYOL的作者看到博客就急了,如果真是这样的话,BYOL就还是没有逃脱出对比学习的范畴,它还是找了一个东西去做对比,其创新性就大大降低了。所以作者赶紧做实验,看看能不能找到BYOL 模型不坍塌的另外一种解释。最终又写了一篇论文进行回应。
这篇论文叫BYOL works even without batch statistics,即在没有batch norm的时候BYOL
照样能工作,详细的消融实验结果如下表所示 :
作者是在encoder(比如ResNet50)和两层Projector
里分布使用BN/LN和什么都不用去做对比实验,最后发现:
SimCLR
性稍微下降;但是BYOL
全都模型坍塌了BYOL
还是训练失败了 。如果BN真的很关键,它真的提供了隐式负样本的对比学习的话,训练就不应该失败SimCLR
也坍塌(最后三列的结果。要注意SimCLR
只有一层projector)。这表明完全不用归一化,SimCLR
这种使用负样本进行对比学习的方式也无法训练。 最终结论:BN跟它原来的设计初衷一样,主要作用就是提高模型训练时的稳定性,从而不会导致模型坍塌 。作者进一步延伸,如果一开始就能让模型初始化的比较好,后面的训练即使离开了BN也没有问题。
作者为此又设计了一个实验,借鉴BEiT
中的group norm+weight standardization
(前者也是一种归一化方式,后者是一种模型初始化的方式,但都没有进行批量统计操作),BYOL的top-准确率可以达到74.1%,和原来精度可以认为是一样了(74.3%)。
- 论文:《Exploring Simple Siamese Representation Learning 》
- 《CVPR 2021 Oral | 何恺明团队提出SimSiam:探索简单的孪生表示学习》
SimSiam
:化繁为简 SimSiam
即simple Siamese network(简单孪生网络)。在BYOL发布时,就已经有很多对比学习的分析性工作了。大家发现,对比学习的成功好像是被很多trick一点点堆起来的性能,比如projection head、更多的数据增强、使用用动量编码器、更大的 batch size等等,好像都缺一不可。
这样因素太多就不方便分析,也不知道每个点到底带来了哪些贡献,所以凯明团队又再次出手,把整个过程化繁为简了一下,最后提出了SimSiam。
SimSiam结构非常简单,不需要用负样本(结构类似 BYOL
)、大的batch size,也不需要动量编码器。然而即使在这种情况下,模型效果也很好。
1. 模型结构
具体的模型总览图如下图所示,整体结构非常类似 BYOL
:
2. 伪代码
Algorithm 1 SimSiam Pseudocode, PyTorch-like
# f: backbone + projection mlp
# h: prediction mlp
for x in loader: # load a minibatch x with n samples
x1, x2 = aug(x), aug(x) # random augmentation
z1, z2 = f(x1), f(x2) # projections, n-by-d
p1, p2 = h(z1), h(z2) # predictions, n-by-d
L = D(p1, z2)/2 + D(p2, z1)/2 # loss
L.backward() # back-propagate
update(f, h) # SGD update
def D(p, z): # negative cosine similarity
z = z.detach() # stop gradient
p = normalize(p, dim=1) # l2-normalize
z = normalize(z, dim=1) # l2-normalize
return -(p * z).sum(dim=1).mean()
D函数就是定义怎么计算loss,这里使用的也是mse损失函数。结合模型结构和伪代码,其前向过程如下:
encoder f
(结构一样参数共享,所以叫孪生网络)得到编码特征 z 1 , z 2 z_1,z_2 z1,z2。Projector
得到预测 p 1 , p 2 p_1,p_2 p1,p2,然后计算对称性loss( p 1 p_1 p1预测 z 2 z_2 z2,同时 p 2 p_2 p2预测 z 1 z_1 z1,单次结果除以2)。BYOL
不同的是,这里没有使用动量编码器,两个encoder完全一样。stop-gradient
避免了模型坍塌作者做了一系列实验分析,发现SimSiam能够成功训练,而没有模型坍塌,主要是因为有stop gradient
这个操作。
stop gradient
操作的影响stop gradient
操作,红色线表示witdout stop-gradientstop gradient
时,优化器快速找到一个退化解,并且达到最小损失值− 1stop gradient
时精度为0结论:上述实验表明,“坍塌”确实存在,但不足以证明是stop gradient
避免了坍塌
SimSiam
依旧有学习,尽管精度只有34.6%。对backbone隐含层添加BN后精度则提升到了67.4%;encoder f
最后的线性层输出(从2048降维)添加BN,精度可以进一步提升到68.1%;Projector h
添加BN ,训练反而不稳定,loss波动太大结论:BN有助于训练优化,但主要是提高模型训练的稳定性,而非避免模型坍塌(见第一行结果)。
通过上面的一些列消融实验对比分析可知,优化器、BN、相似性函数、对称损失可能会影响精度,但与“坍塌”避免无关;对于避免“坍塌”起关键作用的是stop-gradient
操作。
SimSiam
到底在隐式的优化什么?作者认为可以将SimSiam当做是一个EM算法。因为stop gradient操作将一套模型参数被人为劈成了两份,即需要解决两个子问题,模型的更新其实也是在交替进行的。
作者假设SimSiam是一种类似交替优化的方案后(其SGD更新间隔为1),基于该假设,此方案在多步SGD更新下应该同样有效。为此,作者设计了一组实验验证上述假设,结果见下表:
SimSiam
SimSiam
更优的结果结论:交替优化是一种可行的方案,而SimSiam
是其特例。
作者接下来又做了一些推导,到最后可以把SimSiam
理解成是一个k-means聚类问题。在k-means中,也是分两步走的。每次先要把所有的点分配给一些聚类中心;分配完后再更新这些聚类中心。后面就是不断迭代这两个过程。
作者对比了所提方法与其他对比学习SOTA方法的区别&联系所在,见下图:
SimCLR
:SimSiam可以是作为“SimCLR without negative”(SimCLR依赖于负采样以避免“坍塌”);SwAV
:SimSiam可以视作“SwAV without online clustering”;BYOL
: SimSiam可以视作“没有动量编码器的BYOL”。SimSiam
与其他SOTA无监督学习方法效果对比:
SimSiam
具有最高的精度;但更长的训练时长所得收益反而变小。参考:李沐论文精度系列之《对比学习论文综述》、精度笔记
论文:《An Empirical Study of Training Self-Supervised Vision Transformers》、官方代码Code
无监督的预训练(BERT/GPT等)已经彻底改变了NLP,自从Vision Transformer成功之后,将ViT引入CV领域的自监督训练已经是大势所趋了。但是使用ViT作为backbone会导致训练很不稳定,这种不稳定性是造成模型准确率降低的一个主要问题。
本文作者发现只需要做一点小小的改动(冻结ViT的patch projection
层),就能让这个训练变得更稳定、效果也更好。所以作者不得不写一篇论文来把这个发现告诉大家,也就是标题说的An Empirical Study (一个实验性的study )。这篇论文是ICCV 21的一篇口头报告论文,但它的的影响力依旧很大。
MoCo v3的架构,其实就相当于是MoCo v2和SimSiam 的一个合体。因为没有模型总览图,所以直接看伪代码:
# f_q: query encoder: backbone + proj mlp + pred mlp
# f_k: key momentum encoder: backbone + proj mlp
# m: momentum coefficient
# tau: temperature,也就是τ
for x in loader: # load a minibatch x with N samples
x1, x2 = aug(x), aug(x) # augmentation
q1, q2 = f_q(x1), f_q(x2) # queries: [N, C] each
k1, k2 = f_k(x1), f_k(x2) # keys: [N, C] each
loss = ctr(q1, k2) + ctr(q2, k1) # symmetrized
loss.backward()
update(f_q) # optimizer update: f_q
f_k = m * f_k + (1-m) * f_q # momentum update: f_k
# 对比 loss
def ctr(q, k):
logits = mm(q, k.t()) # [N, N] pairs
labels = range(N) # positives are in diagonal
loss = CrossEntropyLoss(logits/tau, labels)
return 2 * tau * loss
MoCov2
loss = ctr(q1, k2) + ctr(q2, k1)
。所以从这个角度讲,它又是SimSiam
。MoCov3
就是MoCov2
和SimSiam
一个延伸工作。patch projection
可以解决训练不稳定的问题 作者后来观察了一下模型训练时每一层梯度回传的情况。作者发现,每次准确度大幅下降时,模型第一层梯度也会有一个波峰。于是作者尝试将这一层的权重全部冻住,结果发现问题就解决了。而且很神奇的是这个trick不光是对MoCov3
有用,它对BYOL
和 SimCLR
也有用。
第一层就是
ViT
的patch projection
层,会将图片分割成一个个patch,然后经过线性层映射为Pacth embedding。
MoCov3 ViT-BN-L/7
精度最高,达到了81%。之前最好结果是SimCLR v2 (SK-ResNet152-3×)
的79.8% , 以及BYOL (ResNet200-2×)
的79.6%.
- 论文《Emerging Properties in Self-Supervised Vision Transformers 》
- 《论文笔记 :DINO - Emerging Properties in Self-Supervised Vision Transformers》、《DINO》
DINO
这个名字,来自于它的题目self distillation with no labels,也就是无标签的自蒸馏方法(学生网络预测教师网络的输出)。本文和MoCov3
一样,也是一种自监督训练Vision Transformer的方式,但作者使用另一种操作——centering,使ViT可以稳定训练。另外本文发现自监督训练为 Vision Transformer features提供了一些新的特性。
1. 研究动机
CV领域,Vision Transfomer(ViT)虽然可以取得和convnets(卷积网络)相比拟的结果,但是还没有展现出足够的优势。比如,相比于convnets,ViT需要更多的计算资源和数据,但是他们的features并没有展现出独特的特性。
transformer在NLP中的成功的一个主要方面来自自监督预训练的应用(BERT,GPT),因为自监督训练出来的特征会包含更丰富的语义和信息。另一方面,卷积网络的自监督学习在CV领域也表现出很大的潜力。受此启发,本文将ViT和自监督学习结合,并研究自监督预训练对ViT feature的影响。
自监督学习通过利用句子中的词创建
pretext tasks
,相比于有监督学习中每个句子对应一个label,pretext task
提供了更加丰富的学习信号。类似的,图像层面的有监督学习将丰富的图片信息减少到单一的分类概念。
2. 发现
通过研究,本文发现自监督ViT features具有一些独有的特性
一个完全不用任何标签信息训练出来的
Vision Transformer
,将它的自注意力图进行可视化,会发现能非常准确的抓住每个物体的轮廓,效果甚至可以媲美对这个物体做分割。
ViT-S/8
),自监督训练出来的ViT features 就能在KNN分类器中表现的很好,ImageNet数据集的 top-1精度达到78.3%,超过之前的自监督方法。(也就是ViT features直接去做最近邻分类,连线性分类头或微调都不需要)。另外在消融实验中证明,动量编码器、multi-crop 数据增强和更小的 ViT patches(计算量更高)都有重要的作用。
# gs, gt: student and teacher networks
# C: center (K)
# tps, tpt: student and teacher temperatures
# l, m: network and center momentum rates
gt.params = gs.params
for x in loader: # load a minibatch x with n samples
x1, x2 = augment(x), augment(x) # random views
s1, s2 = gs(x1), gs(x2) # student output n-by-K
t1, t2 = gt(x1), gt(x2) # teacher output n-by-K
loss = H(t1, s2)/2 + H(t2, s1)/2
loss.backward() # back-propagate
# student, teacher and center updates
update(gs) # SGD
gt.params = l * gt.params + (1-l) * gs.params
C = m * C + (1-m) * cat([t1, t2]).mean(dim=0)
def H(t, s):
t = t.detach() # 教师网络stop gradient
s = softmax(s / tps, dim=1)
t = softmax((t - C) / tpt, dim=1) # center + sharpen
return - (t * log(s)).sum(dim=1).mean() #
前向过程:
DINO
的知识蒸馏是一种范式,是通过训练一个学生网络 g θ s g_{\theta _{s}} gθs 去match一个教师网络 g θ t g_{\theta _{t}} gθt的输出。- 两个网络分支最后分别输出概率分布 p s , p t p_s,p_t ps,pt,这里概率P是对网络输出进行softmax归一化的结果:
P s ( x ) i = e x p ( g θ s ( x ) i ) / τ s ∑ k = 1 K e x p ( g θ s ( x ) k ) / τ s P_{s}(x)^{i}=\frac{ exp(g_{\theta _{s}}(x)^{i}) /\tau_{s} }{\sum_{k=1}^{K}exp(g_{\theta _{s}}(x)^{k}) /\tau_{s} } Ps(x)i=∑k=1Kexp(gθs(x)k)/τsexp(gθs(x)i)/τs
其中温度参数 τ s > 0 \tau_{s} >0 τs>0控制分布的sharp程度。 P t P_{t} Pt结果也是这样的公式算出。然后通过固定教师网络,训练学生网络使其参数 θ s \theta _{s} θs最小化交叉熵损失函数来匹配分布:
m i n θ s H ( P t ( x ) , P s ( x ) ) , w h e r e H ( a , b ) = − a l o g b min_{\theta _{s} }H(P_{t}(x),P_{s}(x)),whereH(a,b)=-alogb minθsH(Pt(x),Ps(x)),whereH(a,b)=−alogb- Teacher Network:和知识蒸馏不同,这里没有一个预先已知的teacher网络。teacher网络来自过去几轮的student网络,因为作者实验发现经过一个epoch训练后冻结teacher网络的训练方式表现不错。(应该是理解为教师网络使用动量编码器,如果参数全部从student网络复制,模型坍塌)
前向过程可以看出,DINO也是自己预测自己(student要预测teacher,teacher的输出当成是ground truth ),所以叫自蒸馏。DINO其实就是延续的BYOL,只不过是换了个名字。
模型 | 左分支 | 右分支 |
---|---|---|
MoCo | query 编码器 | key编码器 |
BYOL | online network | target network |
BYOL | student network | teacher network |
centering:可以看作在teacher分支上加一个偏置项c: g t ( x ) ← g t ( x ) + c g_{t}(x)\leftarrow g_{t}(x)+c gt(x)←gt(x)+c,其中c通过EMA更新: c ← m c + ( 1 − m ) 1 B ∑ i = 1 B g θ t ( x i ) c\leftarrow mc+(1-m)\frac{1}{B}\sum_{i=1}^{B}g_{\theta _{t}}(x_{i}) c←mc+(1−m)B1∑i=1Bgθt(xi),m是一个大于0的参数。
centering可以看做是计算整个batch样本的均值,然后减掉这个均值。centering类似BYOL对于 batch norm 的讨论,因为batch norm也是对整个batch里的样本做了一个均值和方差 。
感觉只是看了个大概,还有很多细节,有空再补把
ViT-S/16
网络预训练300个epoch,然后分别使用线性分类器(Lin.)和KNN测试精度DINO
的默认结构:使用动量编码器、 multi-crop 数据增强和交叉熵损失函数。上图标红色和和默认模型不一样的地方
- 论文《DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection》、代码
- 知乎《DINO: 让目标检测拥抱Transformer》
DINO
从三月初霸榜至今(7月),该模型第一次让DETR (DEtection TRansformer)类型的检测器取得了目标检测的SOTA性能,在COCO上DINO(Swin-L)
取得了63.2 AP的性能,相比之前的SOTA检测器将模型参数和训练数据减少了十倍以上!
主要特性:
SOTA
性能:在大模型上以相对较小的数据和模型(~1/10相比之前SwinV2)取得了最好的检测结果。在ResNet-50的标准setting下取得了51.3 AP。End2end
(端到端可学习):DINO属于DETR类型的检测器,是端到端可学习的,避免了传统检测器许多需要手工设计的模块(如NMS)。Fast converging
(收敛快): 在标准的ResNet-50 setting下,使用 5 个尺度特征(5-scale)的 DINO 在 12 个 epoch 中达到 49.4 AP,在 24 个 epoch 中达到 51.3 AP。使用4个尺度特征(4-scale)的DINO达到了了类似的性能并可以以 23 FPS 运行。模型 | 创新点 | 优势 | 局限性 |
---|---|---|---|
阶段一 | 百花齐放 | ||
Inst Disc | 提出了个体判别的任务,对比学习loss,使用一个 memory bank的外部数据结构去存储负样本来做对比学习 | 特征一致性差 | |
Inva Spread | 只使用一个编码器而不需要额外的数据结构去存储负样本 | 可以进行端到端的对比学习 | 字典太小,对比学习效果不好 |
CPC v1 | 提出了infoNCE Loss,以及预测型的代理任务,其输入可以是图像、音频、视频、文字或加强学习 | 是一个非常全能的结构 | |
CMC | 把两个视角的任务扩展到了多个视角,为以后的多视角多模态对比学习打下了基础 。 | ||
阶段二 | |||
MoCov1 | Inst Disc的延伸工作,使用队列结构代替 memory bank来存储负样本,使用动量更新编码器代替动量更新特征;把之前对比学习方法都归纳成字典查询问题;第一个让无监督预训练媲美有监督预训练的方法 | 字典大且特征一致性好,训练便宜 | |
SimCLR v1 | Inva Spread延伸工作。batch-size加大到8192,引入projection head ,使用更优的数据增强(随机裁剪和随机色彩变换) |
端到端训练 | |
CPC v2 | 引入SimCLR v1的几个技术,ImageNet精度直接从40多提到70多 | ||
MoCov2 | 相比 MoCov1,引入了projection head ;使用更多数据增强、cosi调度器和更长的训练epoch |
||
SimCLR v2 | 受noisy student影响,使用伪标签进行半监督训练。相比SimCLRv1使用了更大的backbone,动量编码器和两层的 projection head |
||
SwAV | 结合聚类和对比学习,使得对比学习不再需要负样本(跟聚类中心对比);使用multi crop 技术 |
||
阶段三 | 不用负样本 | ||
BYOL | 处理负样本实在是太过麻烦,所以完全舍弃负样本,自己预测自己(mse loss),也可以训练 | ||
SimSiam | 化繁为简,使用孪生网络,不需要动量编码器、负样本、大的batch-size就可以训练。不过一个分支必须是stop gradient,这样交替 优化,类似K-means | ||
阶段四 | 引入Vision Transformer | ||
MoCov3 | 冻住ViT 结结构中的patch projection layer就可以稳定训练 |
||
DINO | teacher网络的输出先做centering归一化也可以稳定训练 |
MAE
火爆了以后,大家都去尝试掩码学习,对比学习又从一个火爆发展期变成了一个发展潜伏期。