逐字稿 | 2 MoCo 论文逐段精读【论文精读】

bryanyzhu的个人空间-bryanyzhu个人主页-哔哩哔哩视频

评价

今天我们一起来读一下 MOCO 这篇论文。 MOCO 是 CVPR 2020 的最佳论文提名,算是视觉领域里使用对比学习的一个里程碑式的工作。而对比学习作为从 19 年开始一直到现在视觉领域乃至整个机器学习领域里最炙手可热的方向之一。它简单、好用、强大,以一己之力盘活了从 2017 年开始就卷得非常厉害的计算机视觉领域,涌现了一大批优秀的工作,而 MOCO 就是其中之一。

MOCO 作为一个无监督的表征学习的工作,它不仅在分类这个任务上逼近了有监督的基线模型,而且在很多主流的视觉任务上, 比如说检测、分割人体关键点检测,都超越了有监督预训练的模型,也就是 Imagenet 上预训练的模型,有的数据集上甚至是大幅度超越。所以说 MOCO 的出现从某种意义上来说是给视觉领域吃了一个定心丸,就是无监督学习真的可以,我们有可能真的不需要大规模的标好的数据去做预训练。

这个结论其实从侧面上也证明了之前颜乐坤在 Europes 2016 做演讲时候用的一张图。这张图其实后来在 Twitter 上都已经被大家玩坏了,已经变成了一张梗图。它的意思就是说,如果你把机器学习比作一个蛋糕的话,那强化学习只能算是这个蛋糕上的一个小樱桃,有监督学习最多就算这个蛋糕上的这层糖霜,只有无监督学习才是这块蛋糕的本质,才是我们真正想要的。

而现在也确实如此,**不光是在自然语言处理那边,很多我们耳熟能详的大模型都是用自监督的方式去预训练得到,那在视觉这边也基本上快了。**所以说多听大佬的 talk 也是有好处的,说不定就是找到下一个研究方向了。

那在接着往下读之前,我想先介绍一下什么是对比学习以及前人的一些工作,这样好做个铺垫。因为我发现 MOCO 这篇论文的写作是,假设你对比学习已经有一定的了解了,如果我们对之前的工作不是很了解的话,就不能理解这里为什么要做 momentum contrast(动量对比学习),也无法体会到 MOCO 的这个精妙之处。

1什么是对比学习?

那到底什么是对比学习?假如说我们有两张图一,图二,还有一个图3,然后图一里有一个人儿,图 2 里也有一个人儿,可能图一里的这个人是高兴的,图 2 这个人是不高兴的,然后图 3 里我们有一只狗,我这个画画水平有点儿糙,大家忍一下。那我们希望模型在看到这三张图片以后,它能分辨出前面这两张图片属于一个类别,后面这个明显跟前面不是一个类别,所以对比学习,顾名思义就是说对比着去学习模型 并不需要真的知道这两张图片代表的是人,也不需要知道这张图片代表的是狗,它只需要知道 这两张图片类似 ,而这张图片跟前两张图片不类似

那如果说的再明白一点,就是假如说我现在把这三张图片都通过一个网络m,然后我去得到三个特征,F1、F2、 F3 分别对应的就是这三张图片的特征。那现在假如说我们最后有一个学好的 embedding space,就一个特征空间,F1、F2、 F3 分别是这个特征空间里的三个点 ,那我们希望对比学习能做到什么呢?就是能把这两个类似图片的这个特征尽量的拉近,而让不相似图片的特征尽量跟这两个远离。

1.1还是需要知道哪两张图片相似,哪两张图片不相似,你才能去做这个模型的训练。那为什么对比学习在视觉领域一般被认为是无监督的训练方式?

那如果我们真的能做到所有类似的物体都在这个特征空间里相邻的区域,而不类似的物体都在不相邻的区域的话,那其实我们目的也就达到了,我们学到的这个特征就会是一个很好的特征,那可能这里就会有人问了,对比学习虽然不需要知道标签的信息,比如说它不需要知道图一、图二是人,或者说图 3 是狗,它不需要知道这个标签信息,但它不还是需要知道这两张图片是类似的,这张图片跟前两张不类似吗?也就意味着你不还是需要标签信息去做这种有监督学习吗?你还是需要知道哪两张图片相似,哪两张图片不相似,你才能去做这个模型的训练。那为什么对比学习在视觉领域一般被认为是无监督的训练方式?

因为在视觉领域,大家通过去设计一些巧妙的代理任务,就是 pretext task,从而人为地定立一些规则,这些规则可以用来定义哪些图片是相似的,哪些图片是不相似的,从而可以提供一个监督信号去训练模型,这也就是所谓的自监督训练。

**1.2举例一个最广为应用的代理任务:**instance discrimination

那我们现在就先举个例子,讲一个最广为应用的代理任务,叫 instance discrimination 。这个代理任务就是说如果我们有一个没有标注的数据集 ,里面如果有 n 张照片,比如说X1,X2,一直到 x n,那我们该如何去定义哪些图片是相似的,哪些图片是不相似的? Instant discrimination 是这么做的,如果我们从这个数据集里随机选一张图片,比如说XI现在我在这张图片上做 随机裁剪,从而得到另外两张图,一张我们叫 X I 1,一张我们叫做 X I 二、当然在这个裁剪之后我们还做了很多数据增广,我们把这个裁剪和数据增广叫做transformation 。所以这块,比如说我们用 T1 来代表这块,用 T2 来代表,那我们现在就得到两张看起来应该很不一样的照片,但因为它们都是从同一张照片 XI 经过某些变化而得到的,它们的语义信息不应该发生变化,然后这两张照片就被称作是正样本 。至于哪些图片是跟 XI 这张图片不相似的, Instant discrimination这个代理任务就认为这个数据集里剩下所有的图片都可以被当作是不相似,也就是说 XJJ 不等于i,那这些样本相对于 XI 来说就都是负样本。所以说到这儿,你可能也明白为什么这个代理任务叫做 Instant discrimination,而直译过来就是叫个体判别。

因为在这个代理任务看来,每张图片都是自成一类,剩下所有的图片都跟它不是一个类的。 那拿 image net 的数据集来举例的话,我们现在就不是有 1000 个类了,我们想而是有 100 多万个类,因为每个图片都是它自己的类。那一旦我们有了这个代理任务,有了这么一种去定义什么是正样本,什么是负样本的规则,那接下来就好办了。

**接下来就是我们通过一个模型再去得到一些特征,然后在这些特征上使用一些常见的对比学习的目标函数就可以了。**比如说 NCE loss 这个目标函数的细节,我们之后会来正文的时候讲到。

1.3为什么对比学习简单好用强大

那基本这样一个框架就是对比学习里常见的一种实现方式了,看起来好像平平无奇的样子,但对比学习最厉害的地方就在于它的灵活性 。就像我们这里看到的一样,你只要能找着一种方式去定义什么是正样本,什么是负样本,这就够了。剩下这些操作都是比较标准的 ,那这样就很有意思,因为你可以大开脑洞去制定很多如何定义正样本和负样本的规则。比如说在视频领域 ,很多人就认为同一个视频里的任意两帧都可以认为是正样本,而其他视频里的所有帧都可以是负样本。那在 NLP 领域 你也可以这么用,比如说 SimCSE 那篇论文,就是把同样的句子扔给模型,但是做两次forward,每次 forward 使用不同的这个 drop out,这样得到的两个特征,它认为是正样本,然后其他所有句子的特征就是负样本。

CMC 这篇论文作者是说一个物体的不同, wheel 就是不同视角,也可以是作为正样本的,比如说一个物体的正面和背面,一个物体的 RGB 图像和一个物体的深度图像,这些通通都可以作为不同形式的正样本。所以你可以看到对比学习实在是太灵活,真的是比天比地比空气,什么都能比,只要你脑洞够大,哪个领域都能用,所以自然而然后来也扩展到了多模态领域 ,也就造就了 OpenAI 的 CLIP模型。

2题目

那简单的介绍完对比学习和 Instant discrimination 这个代理任务之后,我们现在就来精读一下 MOCO 这篇论文题目说用 MOMENTUM contrast:动量对比学习的方法,去做无监督的表征。学习 MOCO 这个方法的名字其实也就来源于前两个单词的头两个字母,那对比学习我们之前已经简单介绍过了。

2.1什么是动量?

那什么是动量?**动量可以从数学上理解成为是一种加权移动,**平均就是 y t 等于 m 乘以 y t 减一,加上一减 m 乘以 x t,这里的 m 就是动量这个超参数。 YT 减一是上一个时刻的输出, YT 是你这一时刻你要想改变的输出, XT 是当前时刻的输入,说白了它的意思就是说我不想让我当前时刻的输出完全依赖于当前时刻的输入。所以说我让之前的这个输出也来凑凑热闹,也给它一个权重,因为这里动量这个超参 m 是介于 0 和一之间的一个数,如果 m 很大,就是趋近于一的时候,其实这个 YT 是改变得非常缓慢,因为后面这个一减 m 就基本是趋近于0。意思就是不怎么依赖于当前的输入。那反过来说,如果 m 很小的话,那就是说当前的输出更多的依赖于当前的输入。 MOCO 也是利用了动量的这种特性,从而去缓慢地更新一个编码器,从而让中间学习的字典中的特征尽可能的保持一致。

作者团队来自fair,个个都是鼎鼎有名的大佬,我就不用过多介绍了。他们五个人在 Google scholar 上的引用加起来已经超过 50 万了,在各个视觉会议上也是拿奖拿到手软。好,我们来看摘要,上来就说,哇,本文我们就提出来 MOCO 这个方法,去做无监督的表征学习。虽然我们是基于对比学习的,但是我们是从另外一个角度来看对比学习,也就是说把对比学习看成是一个字典查询的任务。具体来说就是我们做了一个这个动态的字典,这动态的字典由两个部分组成,第一个部分就是我们有一个队列,第二个部分我们有这么一个移动平均的编码器,因为这个队列里的样本我们不需要做梯度回传,所以我们就可以往队列里放很多副样本,从而使得这个字典可以变得很大。

至于为什么用这个移动平均的编码器,我们是想让字典里的特征尽量的保持一致,然后我们发现在训练的过程中,如果你能有一个很大的而且比较一致的这个字典,会对无监督的对比学习非常有好处。而这篇论文主要的亮点在于它的结果,所以说剩下大篇幅的摘要留给了结果。

首先是分类,就是在 Imagenet 数据集上,如果你用这个大家普遍采用的这个 Linear Protocol 去做测试的话, MOCO 是能取得跟之前最好的无监督学习方式差不多或者更好一点的结果。这里的 Linear Protocol 指什么呢?就说如果我先预训练好了一个骨干网络,当我现在要把它用到不同的数据集上的时候,我把它的骨干网络冻住,就是 backbone freeze,然后只去学习最后的那个全连接层,也就是那个分类头。这样就相当于我们把一个提前学好的预训练模型当做了一个特征提取器,我们只从它去抽特征,这样就可以间接地证明之前预训练好的那个模型的特征到底学的好不好。然后这是分类。接下来他说更重要的是, MOCO 学到的特征是能够很好地迁移到下游任务的,这个才是这篇文章的精髓,也是这篇文章最大的卖点。因为我们之所以想做大规模的无监督预训练,就是想去学到一个很好的特征,然后这个特征拥有很好的迁移性,我们就可以在一些没有那么多标注数据的下游任务里获得很好的结果。

MOCO 作为一个无监督的预训练模型,能够在 7 个下游任务上就分割检测,而且在比如说VOC、 Coco 这些各种数据集上超越之前的这个有监督的预训练模型,有时候甚至是大幅度的超越它。这里的 counterpart 的意思就是说我的模型使用的是一样的,比如说都是 rise 50,我们只是训练的方式不一样,一个是用有监督带标签的去训练,一个是用无监督不带标签的数据去训练。

最后作者总结说这个就意味着什么呢?意味着无监督和有监督的表征学习中间的这个鸿沟对于很多视觉任务来说已经填上了。那这个结果确实是非常惊人,因为之前虽然有一些无监督的工作能在某个数据集或者某个任务上能够比它对应的有监督预训练模型好一点,但是 MOCO 是第一个能够在这么多主流视觉任务上全面的让无监督训练的模型比有监督训练模型表现要好。那接下来我们一起看引言。

他说 GPT 和 Bert 已经证明了无监督的表征学习在 NLP 领域里是非常成功的,但是在视觉领域有监督的预训练还是占主导地位。虽然也有很多出色的无监督工作,但是他们往往要比这些有监督的模型效果上要差很多。那这是为什么呢?作者认为这个原因有可能是来源于他们非常不同的这个原始的信号空间。他说对于自然语言处理的任务来说,它们是离散的这个信号空间,也就是说它原始的信号空间是由这些单词或者这些词根词缀去表示的,从而可以很容易地建一些这个 tokenized 字典。

tokenize 的意思就是说把某一个词对应成某一个特征,那一旦有了这个字典,无监督学习也可以基于它还很容易地展开。因为你可以简单的把这个字典里所有的key,就是所有的条目想象成是一个类别,这不就又变成了一个有监督学习的范式了吗?你还是有一个类似于标签一样的东西去帮助你进行学习,所以在 NRP 那边无监督学习就很容易去建模,而且建好的这个模型也相对比较容易优化。

但是对于视觉来说就完全不一样,因为视觉的原始信号是在一个连续的,而且高维的空间里,它并不像单词那样有很强的语义信息,而且浓缩得非常好,没有那么简洁,所以导致说它并不适合去建立一个这样的字典。那如果没有这个字典,无监督学习就很难去建模,所以导致了在视觉这边无监督学习还远不如有监督学习。接下来第二段说,他说最近有一些无监督表征学习的方式是基于对比学习的,而且取得了非常不错的效果。虽然这些方式的出发点都不一样,或者说具体的做法不一样,但是它们都可以被归纳成一种方法,也就是说在构造一个动态的字典。

那这句话该怎么理解?如果我们去看之前画的流程图,就是说有一个数据集,里面有 n 张图片,现在我们随机选一张图片,假如说是X1,在这张图片上经过不同的变换以后,我们得到了 X11 和X12,那这两张图片就组成了一个正样本。对,一般我们是管这个 XEEE 叫Anchor,也就是锚点基准点的意思,那这个 XER 我们叫它positive,也就是说相对于这个基准点来说, XER 是 XEE 的一个正样本,然后剩下的所有的这些图片,从 X2 到 XN 这些全都是负样本,这是negative。那一旦有了正负样本这些概念,接下来就是把这些样本扔给编码器去得到一些特征输出。比如说图片 XEE 进到编码器 EEE 里就会得到一个特征FEE,然后图片 XER 进到编码器 EER 里就会得到特征FER。这两个编码器既可以是同一个模型,也可以是不同的模型。因为在 MOCO 这篇论文里选用的是不同的模型,所以我就把它们分开画了。

那至于这些负样本到底该用哪个编码器,负样本其实是应该用 E12 这个编码器的,因为所有的这些负样本和这个正样本 f 一二都是相对于原来的这个锚点 F1 来说的,所以说这些负样本也应该和这个正样本使用同样的编码器,从而让它们的特征保持一致性。于是这些负样本也通过这个编码器从而得到了这些F2, F3 到 FN 的特征。

那我们之前说对比学习是干什么的?对比学习就是让这个正样本对的特征在特征空间里尽可能的相近,而让这些负样本的特征在特征空间里尽量地远离FEE。那为什么 Moco 的作者认为之前的这些对比学习的方法都可以被归纳成为在做一个动态的字典?他说如果你把这些特征当成是一个字典,也就是他这里说的这句话。他说字典里的这些条目,也就是这些 key 是从数据里面抽样出来的,然后用一个编码器去表示,也就是说这里的 key 其实对应的是特征,而不是原来的图像。

那如果我们把这个 FEE 当成是一个query,那所有这些字典里的特征都当成是key,那对比学习不就转化成为一个字典查询的问题了吗?具体来说就是对比学习要去训练一些编码器,从而去进行一个字典的查找。查找的目的是什么呢?就是让一个已经编码好的query,也就是 query 这个 feature F E 1 尽可能的和它匹配的那个 key 的特征相似。那匹配的 key 的特征是什么呢?就是 f 一二,就是这个正样本的特征,然后和其他的 key 远离,也就是和其他负样本的这些特征了。远离,那整个学习的过程不就变成了一个对比学习的框架,从而你只需要去最小化一个对比学习的目标函数就可以。

那在 MOCO 这篇论文里,因为它已经把所有的这些对比学习的方法归纳成了一个动态字典的问题,所以它很少使用 Anchor 或者正负样本这些词,而代之的它都用的是 query 和key。所以说这里的 x 一,它一般用 XQ 表示,这里的 x 一二,它用 XK 表示,就是 x query 和 x key 同样的,那特征 f 一一,它用的是 q 来代表,这是query。剩下的这些特征,它用K0, K1 一直下去。

那读完这两段以后,大家可能就明白为什么我刚开始要先讲一下对比学习是什么了,因为 MOCO 的作者是一个自顶向下的方式来写这篇论文的,它假设你对比学习的这些前任的工作已经了如指掌了,它假设你能跟上它的节奏。那我们接着读第三段,作者说从这个角度来看,哪个角度就是把对比学习当成动态字典的角度,他们猜想最后要有好的结果。

这个字典应该具有两个特性,第一个就是说必须要大,第二个就是在训练的时候要保持尽可能的一致性。那为什么这么说?我们先把这个 query 和这个字典这里面全都是 key 的这个字典写在这里说,因为这个字典越大,就能更好的从这个连续的高维的视觉空间去做抽样。那这个很容易理解,因为这字典里的 key 越多,你所能表示的这个视觉信息视觉特征就越丰富。那当你拿一个 query 去跟后面的这些 key 做对比的时候,你就真的有可能学到那些把物体之间区分开的特征,那些更本质的特征,那如果这个字典很小,你的模型很有可能就学到了一个捷径,就一个 shortcut solution,从而导致你预训练好的模型不能很好地做泛化。

那第二个关于一致性,作者说字典里的这些 key 都应该用相同或者说相似的编码器去产生得到。也就是说,这个 K0K 一直到 k n 的直接 key 应该是用相同或者相似的编码器抽取得到的。这样当你去跟 query 做对比的时候,才能保证这个对比尽可能力一致。

否则,如果你这些特征是由不同的编码器得到的,很有可能这个 query 就找到了和它使用相同或者相似编码器的那个key,而不是真的和它含有相同语义信息的那个key,这样其实也就是变相地引入了一个 shortcut solution,这引入了一条捷径,从而让模型学不好。

所以作者最后说,但是现在已有的这些使用对比学习的方法都至少被上述说的两个方面中的一个所限制。其实如果这里说一下现在已有的方法是怎么受限的,会更有助于我们大家理解,但确实是一言难尽。所以受限于篇幅,作者只能说接下来在该谈的时候我们会谈的。那介绍完了研究动机,还有之前工作的局限性以及想要达到的目标,那接下来很自然的就是作者要提出他们自己的方法了。他说他们提出 MOCO 是为了给无监督的对比学习构造一个又大又一致的字典,具体的模型总览图看图一,所以我们现在来看图1。

其实图一跟我们刚才画的对比学习的一般框架是很相像的,我们也有一个 query 的图片,还有一些 key 的这些图片,然后这些图片通过一些编码器从而得到了最后的特征,然后这个 query 的特征去跟所有的 key 的特征去做类比,最后用对比学习的 loss 去训练整个模型,那它跟我们之前讲的框架有哪儿不同?其实也就是这个 q 和这个 momentum encoder 不同,也就是 MOCO 这篇论文的贡献所在。

首先我们来看为什么要用一个队列去表示这个字典,主要还是受限于显卡的内存,因为我们知道如果这里的字典太大,也就意味着我们要输入很多很多的图片,那如果这个字典的大小是几千或者甚至上万的话,显卡的内存肯定是吃不消的。所以我们需要想一个办法能让这个字典的大小当每次模型去做前向过程时的 batch size 的大小剥离开。那于是作者就想到了一个巧妙的办法,就是用队列的这种数据结构来完成这项任务。

具体来说就是这个队列可以很大,但是我们每次更新这个队列是一点儿进行的,也就是说当我们用一个很小的 batch size 的时候,现在这个 batch 抽得的特征进入队列,然后把最老的也就是最早的那个 mini batch 移出队列,这样一下就把训练时候用的 mini batch 的大小跟队列的大小直接分开了,所以最后这个队列的大小也就是一个字典的大小可以设得非常大,因为它里面大部分的元素都不是每个 iteration 都需要更新的,这样只用普通的 GPU 我们也能训练一个很好的模型。

但是作者之前也说了,这个字典里的这些 key 最好要保持一致性,也就是说它最好是用同一个或者说相似的编码器而产生得到的。那你现在如果只有一小部分,也就是当前的 batch 是从当前的编码器得到的,而之前的这些 key 都是用不同时刻的编码去抽取的特征,那不就不一致了吗?所以作者又提出了第二个改进,也就是这个 momentum encoder 动量编码器,那如果我们用刚才讲过的动量的概念来写一下数学表达式,就是如果现在这个编码器用 set a q 代替,就 set a query,然后这个 momentum encoder 用 set a key 代替 seed a key,那这个 c 大 k 的更新就等于 m c 大 k 减一加上一减 m 乘以 c 大q。

也就是说,虽然我这个动量编码器刚开始是由这边的编码器 c 大 q 初始化而来的,但是在模型的训练过程中,如果我们选择了一个很大的动量,那这个动量编码器 Theta k 其实是更新的非常缓慢的,它不会跟着这个 Theta q 去快速的改变,从而保证了这个字典里所有的 key 都是由相似的编码器抽取得到的,尽最大可能地保持了它们之间的一致性。

所以作者最后说,基于这两点贡献 MOCO 这个方法可以构建一个又大又一致的字典,从而去无监督地学习一个视觉表征。那讲完了 MOCO 是什么,接下来就该说选用什么代理任务去充当这个自监督信号,从而进行模型的训练了。因为 MOCO 只是建立中间模型的一个方式,它只是为对比学习提供了一个动态的字典。那具体选择什么样的代理任务去做这个字监督学习这里它说其实 MOCO 是非常灵活的,它可以跟很多代理任务去合起来用。那在这篇论文里,它就选择了一个比较简单的 Instant discrimination 任务,也就是我们之前讲过的个体判别任务。那为什么选它?其实不光是简单,而且是因为它的效果确实也非常好。

那这个任务是什么呢?简单用一句话来概括,就是说如果一个 query 和一个 key 是同一个图片,不同的视角,比如说不同的随机裁剪得到的,那我们就说这个 query 和这个 key 能配上。对,也就是说能在这个动态字典里查找到这个 query 所对应的key。那用了这个代理任务之后, Moco 在 image net 这个数据集上去做 Linear evaluation 的时候,能跟之前最好的方法打个平手或者有更好的表现。

那最后一段自然还是老套路,慢一下,结果他说无监督的表征学习最主要的目的就是,当你在很大的一个没有标注的数据集上去做完预训练以后,你这个预训练好的特征是能够直接迁移到下游任务上, MOCO 就做到了这一点。他们说在 7 个下游的任务上,这七个任务既有检测也有分割。

MOCO 这种无监督的预训练方式能不能超越?用 image net 去做有监督的预训练方式,有的时候甚至还是大幅度的超越,那这个重要性是不言而喻的, MOCO 是第一个做到这么好的结果。然后其实大家对无监督学习还有另外一个要求,或者说还有另外一个期待,就是像 NLP 那边,如果你用更多的数据,用更大的模型,我们希望这个模型的提升是永无止境的,最好不要有性能保护的现象。所以说,作者这里为了实验的完整性,他又做了另外一组实验。他除了把 Moco 在 image net 去做预训练之外,他在 Facebook 自己的一个数据集就有 10 亿, Instagram 图片的数据集上也去做了预训练,最后的结果还能提升。所以这就证明了 MOCO 是可以在一个更偏向于真实世界,而且有亿级规模图片的数据集上工作的很好的它。

这里为什么说 relative accurated scenario,因为这个数据集并不像 Imagenet 一样是精心挑选过,而且大部分图片都是只有一个物体在中间的。 Instagram 图片的场景当然就非常丰富了,而且也会展示出真实世界中数据有的一些特性,比如说数据分裂不均衡导致的长尾问题,或者说一张图片可能现在含有多个物体,所以跟 image net 比起来,可能从数据的挑选和标注上都没有那么严格。然后,因为 MOCO 不论是在中型数据集上去做预训练,还是在大型数据集上去做预训练,都能取得很好的结果,所以作者最后说,这些结果证实了 MOCO 可以在很多视觉的任务上把无监督学习和有监督学习的坑填平,而且甚至可以在一些真实的应用上去取代大家之前一直使用的这个 image net 预训练的模型。

这句话虽然读起来平平无奇,但它的影响力实在是巨大的,因为不光是学术界几万篇甚至几十万篇论文之前都是用 Imagenet 预训练的模型去接着往下做实验,那在工业界更是有数不清的产品之前是基于 Imagenet 预训练的模型的。那 MOCO 的影响力就是说所有的这些应用,你都可以换成用 MOCO 无监督的训练方式,训练好的模型,说不定效果还会更好。

接下来我们看文章的最后一节结论部分。 MOCO 的结论部分其实不光是结论,主要的内容都围绕在了讨论上。结论其实就一句话,我们的方法在一系列视觉的任务和数据集上取得了比较正面就比较好的结果。大佬的论文就是这么直白且有力。接下来主要就是讨论了两个比较有趣的问题。第一个就是通过实验, Moco 的作者发现,当这个预训练的数据集从 Imagenet 换到 Instagram 的时候,提升虽然是有的,但是都相对来说比较小,就是说只有零点几个点或者一个点,但是你的数据集可是从 100 万增加到 10 个亿了,就你扩大了 1000 倍。这样的提升着实是有些小。所以说作者觉得是因为大规模的数据集没有被很好地利用起来,他们觉得可能一个更好的代理任务有可能解决这个问题。

接下来这一段话其实是我读 MOCO 这篇文章给我感触最深,令我大受震撼的一段话。我们来看作者接着说,除了这个简单的个体判别任务,我们有没有可能把 MOCO 和另外一个代理任务,也就是 mask auto encoding 结合起来用,就像 NLP 那边 Bert 一样,用 mask language modeling 完形填空句子监督的训练模型。

那你读到这儿是不是发现这个词很熟悉?那这不就是凯明大神最近星火的 m a e 吗?也就是说这个想法大佬在两年前就已经有了,甚至有可能已经做了一些初步的实验,这在当时来看还是非常前瞻性的,毕竟当时多模态也没有很火, vision Transformer 也还没有到来, NLP 降维打击 CV 也是最近一年才有的事儿。所以我每每读到这儿都被大佬的远见所折服,而且同时也说明做研究真的急不来,它真的需要积累。

最后毕竟是结论,所以作者还是展望了一下,点了个题说希望 MOCO 能对其他那些使用对比学习的代理任务有帮助。之所以强调对比学习,是因为 MOCO 设计的初衷就是去构造一个大的字典,从而让正负样本能够更有效地去对比,提供一个稳定的自监督信号,最后去训练这个模型。

好,我们现在一起回来读相关工作。他说无监督学习或者自监督学习一般有两个方向可以做,一个就是在代理任务上做文章,一个就是在目标函数上做文章。作者这里打了个脚注,那下面解释说,其实自监督学习是无监督学习的一种,但之前在前人的工作中,大家一般都不去做区分,就都是混着叫的。所以在 MOCO 这篇论文里,作者就选择使用了无监督学习这个词儿,因为定义的更广泛一些。

接下来就是大概介绍一下什么是代理任务,什么是目标函数。他说代理任务一般指的是那些大家不太感兴趣的任务,就是说不是分类分割检测这种有实际应用场景的任务。这些代理任务的提出主要是为了学习一个好的特征,然后目标函数自然就很广泛了。所以它这里说目标函数的研究其实是可以和这个代理任务分开的。 MOCO 主要就是在目标函数上下的功夫,它提出的这个又大又一致的字典主要影响的是后面info、n,c, e 这个目标函数的计算。先说目标函数,这里的目标函数主要是针对无监督的方式而说的,说最常见的构建目标函数的一个方式就是去衡量一下一个模型的输出,而它应该得到的那个固定的目标之间的差距。比如说如果你用 auto encoder on 自编码器的话,就是你输入一张原图或者一张已经备感扰过的图,你现在通过一个编码器解码器,然后你现在想把这张图重建出来,这里你既可以用 l e loss,也可以用L2LOSS,但总之你衡量的是原图和你现在新建图之间的差异。这种就属于生成式网络的做法,因为你是在生成一张图片。

如果你是想走判别式网络,那一般有什么做法?比如说它这里说的 eight position 其实就是 15 年的一篇论文,它主要的思想就是说如果你把一张图片打成九宫格,假如这个九宫格儿有序号,就是从 123 一直到9,现在先把中间这一格儿 5 给你,然后再随机从剩下的格儿里挑一格儿给你。

你能不能预测出这个随机挑选的格儿是位于中间这一格儿的哪个方位?比如说左上还是右下?因为这里的每一块儿它其实都自带序号儿,其实就相当于把这个代理任务转化成了一个分类任务,因为它只有 8 个方位可以去预测,所以 MOCO 这里就用 8 个位置来代表这个方法。然后作者又说,除了判别式或者生成式这种常见的目标函数,还有一些方法我们立马就讲,一个就是对比学习的目标函数,另外一个就是对抗性的目标函数,那对比学习的目标函数主要是去一个特征空间里衡量各个样本对之间的这个相似性,它要达到的目标就是要让相似物体的特征拉得尽量近,两不相似物体之间的特征推开的尽量远。

那对比学习跟我们之前说的这种判别式或者生成式的目标函数有什么不一样?它有一个很大的区别,就是我们刚才说的,不论你是判别式,你是去预测 8 个位置,还是说生成式,你是去重建整张图,你的目标都是一个固定的目标。但是在对比学习这边,它的目标是在训练的过程中不停地改变的,也就是他这里说的在训练的过程中,目标其实是由一个编码器抽出来的这个数据特征而决定的,也就是 Moco 这篇文章指的字典。接下作者说最近几篇效果不错的方法,他们的核心思想都是用的对比学习,具体的讨论我们一会儿会在三点儿一章节看到那对抗性的目标函数。之前牧神在讲干这篇论文的时候也提到过,它主要衡量的是两个概率分布之间的差异。那对抗性的目标函数我们都知道它主要是用来做这个无监督的数据生成的,但是后来也有一些对抗性的方法是用来去做这种特征学习了。因为大家觉得如果你能生成很好很真实的图片的话,按道理来说你是已经学到了这个数据的底层分布,那这样模型学出来的特征应该也是不错的。

接下来作者就说另外一个方面,也就是这代理任务,那代理任务其实过去几年大家提出非常多的形式,脑洞真是开得五花八门。像这个 denosing auto encoder 重建整张图,或者说 context auto encoder 就是重建某个patch,或者说我们刚才说过的 colorization 的方法,用给图片上色当词监督信号。然后作者说还有很多代理任务是去生成一些伪标签。像第一个说的这个 example image,其实也就是给同一张图片做不同的这个数据增广,它们都属于一个类 patch ordering,也就我们刚刚说的九宫格方法,要么就是打乱了以后你去预测它的顺序,要么就是说随机选一个patch,你去预测它的这个方位。当然还有利用视频的信息去做tracking,以及就是一些聚类的方法了。

那最后一段作者还讨论一下对比学习和之前这些不同的代理任务之间的关系。他说不同的这个代理任务是可以和某种形式的这个对比学习的目标函数配对使用。比如说 Moco 这篇论文里使用的这个个体判别的方式,就跟之前的这个 Exemplar based 代理任务很相关。那对于之前两个比较重要的工作,一个是CPC,一个 CMC 来说, CPC 做的是预测性的对比学习,它是用上下文的信息去预测未来。

这个就跟我们上面讲到的这个上下文字编码代理任务非常相关。对于 CMC 来说,它是利用一个物体的不同视角去做对比,那这个就跟给图片上色这个代理任务就非常像了,因为给图片上色这个任务就涉及了同一个图片,两个视角,黑白和彩色。那大概总结一下相关工作还是写得相当简洁明了。

之所以围绕目标函数和代理任务这两个方向去写相关工作,是因为这两个部分是主要跟有监督学习不一样的地方。那如果我们和有尖头学习的对比一下,假如说我们有一个输入x,然后现在通过一个模型,然后得到一个y,也就是输出有了输出之后,我们就去和一个 ground truth 去做比较,然后我们需要一个目标函数去衡量一下这个比较的结果,这个就是有监督学习的流程了。

对于无监督学习或者自监督学习来说,那我们缺少的也就是标签,就是这里这个 ground truth,那如果没有标签儿怎么办?那就自己造呗。所以这时候代理任务就派上用场了,代理任务的用处就是去生成一个自见度的信号儿,从而去充当这个 ground truth 这个标签信息。

那一旦我们有了输出y,又有了这个标签信息,那接下来我们还需要什么呢?就需要一个目标函数去衡量它们之间的差异,从而让模型去学得更好。所以说这也就是为什么 MOCO 这篇论文从这两个角度就是目标函数和代理任务去写这个相关工作。接下来就到了文章的主体方法部分,三点儿一的标题直接就跟它的第一句话相呼应,就是说之前的对比学习以及最新的一些效果比较好的变体,它们都可以想象成是训练一个编码器,从而去做一个字典查找的人物。然后我们就往细里说,假设我们已经有一个编码好了的query, q 也就是一个特征,还有一系列这个已经编码好的样本,也就是K0、K1、K2,这些可以看作是一个字典里的那些key。然后他这里做了个假设,他说在这个字典里只有一个 key 是跟这个 query 是配对儿的,也就是说它俩互为正样本,对,它们把这个 key 叫做 key positive。

之所以有这个假设,其实我们之前在讲个体判别任务的时候也提到过这个代理任务,就是从一个图片经过两种变换得到两个图片,其中一个作为基准图片,另外一个作为正样本,所以说就是只有一个正样本对。当然这里理论上你可以是使用多个正样本对的,之后也有工作证明了使用更多的正样本对有可能会提升你任务的性能。然后我们一旦定义好了正样本和负样本,接下来就需要一个对比学习的目标函数了。作者说这个对比学习的目标函数最好能满足以下这个要求,就是当 query q 和唯一的那个正样本 key k plus 相似的时候,它的这个 loss 的值应该比较低。然后还有就是当这个 query q 和其他所有 key 都不相似的时候,这个 loss 的值也应该低,因为这个本来就是我们的目标,如果我们已经能达到这么一种状态,就说明模型差不多训练好了。我们当然希望这个目标函数的 loss 值尽可能的低,就不要再去更新模型了。那反之,如果 q 和正样本 key plus 不相似,或者说这个 query q 和本来应该是负样本的那些 key 相似,那这个目标函数的 loss 值就应该尽可能的大,从而去惩罚这个模型,让模型赶紧更新它的参数。

接下来作者说,在 MOCO 这篇论文里,我们就采取了一个叫 info NCE 的对比学习函数来训练整个模型。那英符 NCE loss 到底是什么呢?我们先不着急看这个公式一,如果我先写这么一个式子,你会联想到什么呢?就是一个向量的exponential,然后处于很多向量 exponential 的之和。很多人一看这不就是 Softmax 吗?对的,这就是 Softmax 操作。那如果我们现在是在有监督学习的范式下,也就是说我们有一个 one hot 向量当作这个 ground truth,那其实在前面加上一个副log,整个这个公式其实就是 cross entry loss,也就是交叉生目标函数了。

那注意一下,这里的这个 k 在有监督学习里,指的是这个数据集一共有多少类别。比如说 Imagenet 的话就是 1000 类,这里的 k 就是1000,它是一个固定的数字。那我们现在回到对比学习,其实对比学习理论上来说是可以用这个公式去计算 loss 的,但是实际上行不通。如果说我们像大部分对比学习的工作一样,就使用 instance discrimination 这个代理任务去当资金 no 信号的话,那它这里的类别数 k 将会是一个非常巨大的数字。就拿 Imagenet 数据集来举例,这里的 k 就不再是 1000 了,而是 128 万,就是你有多少图片,它就有多少类。那我们大家也知道 Softmax 操作在有这么多类别的时候,它其实是工作不了,同时因为这里还有 exponential 操作,当你这个向量的维度是几百万的时候,计算复杂度是相当高的。

如果每个训练的 iteration 你都要去这样去算loss,那训练的时间将会大大的延长,所以该怎么办?那这个时候 NCE loss 就出来了,CEE,也就是 noise contrasted estimation 的缩写,它是什么意思?那之前你不是说因为类别太多,所以我没法去算Softmax,从而没法去算这个目标函数吗? i n c e 就说不如我们把这么多类就简化成一个二分类。

问题就是我现在只有两个类别了,一个是数据类别,就是 data sample,一个是噪声类别,就是 noise sample。那我每次只需要去拿这个数据样本去和这个噪声样本做对比就可以,所以这也就是这里说的 noise contrasted。

但是如果你还是把整个数据集剩下的图片都当作负样本,那即使 noise contrast 解决了你这个类别多的问题,但你的计算复杂度还是没有降下来,那怎么能让这个 loss 算得更快一点儿呢?没有别的办法,只有取近似了。那意思就是说,与其你在整个数据集上去算这个loss,不如我就从这个数据集里去选一些这个负样本来算 loss 就可以了。这也就是这里这个 estimation 的含义就是它只是一个估计,一个近似。但是我们也知道,按照一般的规律,如果你这里选取的样本很少,那它就没有那么近似了,结果自然也就会比较差。那它选的样本越多,自然就跟使用整个数据集的图片的结果更加近似,效果自然也会更好。

所以这也就是 MOCO 为什么一直强调的,他希望这个字典足够大,因为越大的字典就能够提供一个越好的近词,所以说总结一下就是 NCE 这个 loss 就是把一个超级多类分类的问题变成了一系列的二分类问题,从而让大家还是可以开心的使用 Softmax 操作。

那这里说的 Infor NCE 又是什么呢?其实它就是 NCE 一个简单的变体,它觉得如果你只把问题看作是一个二分类,就只有数据样本和噪声样本的话,可能对模型学习不是那么友好,毕竟在那么多噪声样本里,大家很有可能不是一个类,所以还是把它看成一个多分类的问题比较合理。于是 n c e 华丽转身就变成了这个公式一,它从而也就变成了 influence loss。那我们现在来看公式一,这里的 q 乘以k,还有底下的 q 乘以k,其实就相当于是Logics,也就可以类比于我们之前的softmaxy,这里用了这个 z 也是模型出来的Logics。

那这里的套其实下面说了,是一个温度的超参数,这个温度一般是用来控制分布的形状的,比如说你原来的 largest 分布大概长这个样子,然后如果你现在这个套的值变大,也就是一减套变小了,就相当于把这个分布里的数值都变小了,尤其是经过 exponential 之后就变得更小了,最后就会导致这个分布变得更平滑。

那相反,如果你这个 TA 取得值小,也就是 1 除以 TA 变得更大,那就是说这个分布里的值都相应的变大,尤其是经过 exponential 之后,原来大的值会更大,就使得这个分布更集中,也就是变得更 peak 了。所以说这个温度超参数的选择也是很有讲究的。

如果温度设得越大,那对比损失对所有的负样本都一视同仁,导致模型的学习没有轻重。但如果温度的值设的过小,又会让模型只关注那些特别困难的负样本,其实那些负样本很有可能是潜在的正样本。如果模型过度的关注这些特别困难的负样本,会导致模型很难收敛,或者学好的特征不好去泛化。

但是温度的这个超参数终究只是一个标量,我们如果把它忽略不看,你就会发现,其实这个 influencie loss 不就是 cross centripy loss 吗?唯一的区别就在于在 cross centripy loss 中,这个 k 指代的是数据集里类别的多少,但是在对比学习的 influencer e loss,这个 k 指的是负样本的数量。然后作者接下来说公式下面的这个和,就是这个 sum 其实是在一个正样本和 k 个负样本上做的,因为是从 0 到k,所以是 k 加一个样本,也就指的是字典里所有的key。

那接着说,如果你直观的想一想,这个 influence loss 也就是一个 cross entry loss,它做的就是一个 k 加一类的分类任务,然后它的目的就是想把 q 这个图片分成 k plus 这个类。所以我们读到这儿也会发现 info n c e 也不是那么难理解,跟之前我们常用的 cross entry loss 是有这么大联系的。而且其实如果我们现在直接跳到后面的这个 MOCO 的伪代码的话,我们也会发现 MOCO 这个 loss 的实现,它就是基于 cross Centrby loss 的实现。

接下来第二段,作者说既然你已经有了这个代理任务,提供给你的正负样本也有了可以用来训练模型的目标函数,那接下来就该讨论一下这个输入和模型大概是什么样。但这里作者还是从一个字顶向下的方式去写这篇论文,比如他说普遍来讲,这个 query q 是一个输入 SQ 通过一个编码器 FQ 得到的,那同理,那所有的那些 key 的表示也都是那些 key 的输入通过了一个 key 的编码器,至于这个输入到底是什么模型,到底是什么,作者说它们具体的实现是由具体的这个代理任务决定。比如说在代理任务不一样的时候,这个输入的 SQ 和 SK 既可以是图片,也可以是图片块儿,或者是含有上下文的一系列的图片块儿。

至于模型,作者说这个 query 的编码器和 key 的编码器 g 可以是相等的,就是说模型架构也一样,它们的参数也是完全共享的,或者说它们的参数是部分共享,那再或者说就是彻底不一样的两个的网络。那从总体上大概讲完了什么是输入,模型输出还有目标函数之后,接下来就该具体讲一讲什么是 MOCO 了,作者在这里又写了一段,再次强调了一下他们的研究动机到底是什么,为什么要剔除MOCO?这其实是一种很好的写作方式,就是说你整个论文每段和每段之间最好都有承上启下的段落。当你每开始讲一个新的东西的时候,最好先讲一下我们为什么需要它。一旦有了这个承上启下的段落,也就是这个因为所以的这个逻辑关系之后,大家读起你的论文来就会更顺畅。否则如果你的论文上来每一段都是直接就讲方法的话,很容易会让人看得一头雾水的。无论你觉得你写得有多清晰,读者可能从一开始就根本没明白你为什么要这么做,这就很伤了。

我们回到文章作者说从以上的这个角度对比学习是一种在高维的连续的输入信号上去构建字典的一种方式,它这里的高维连续其实就指的是图片了,然后他说这个字典是动态的,为什么是动态的?因为这个字典里的 key 都是随机去取样的,而且用来给j、j、 k 做编码的那个编码器也是在训练的过程中不停的改变。

这个就跟我们之前讲过的,不论是有监督还是无监督的方法都不太一样,因为之前的那些工作,他最后学习的那个 target 都是一个固定的目标。所以说作者认为如果想学一个好的特征,这个字典就必须拥有两个特性。我们已经也说过很多遍了,一个就是大,一个就是一致性,因为大的字典能够包含很多很多这个语义丰富的负样本,从而有助于能让你学到更有判别性的那些特征。那一致性主要是为了模型的训练,避免他学到一些 trivial solution,就是一些捷径解。所以说基于这些研究动机,作者们提出了 momentum contrast,所以说这整个一段都是在承上启下。

接下来就先介绍文章的第一个公信,就是如何把一个字典看成是队列。作者说我们方法的核心其实就是把一个字典用队列的形式表现出来,队列其实是一种数据结构,这样如果你现在有一个队列,里面有很多元素,假如说新来的元素现在从下面进来,那为了维持这个队列的大小就得有一些东西,就是说最老的那个数据会从队列里出去。

所以说队列一般都被称作是一个 FIFO 的值的数据结构,也就是 first in, first out 就先进先出的数据结构。那作者这里是用这个队列就代表一个字典,也就是说这整个的这个队列就是一个字典,里面的元素就是我们的放进去的那些key。在模型训练的过程中,每一个 Mini batch 就会有新的一批 key 被送进来,同时也会有一批老的 key 移出去。

所以作者说用队列的好处可以让我们重复用那些已经编码好的key,而这些 key 是从之前的那些 Mini batch 里得到的,它的意思就是说比如说这块儿的元素就指代的是当前 Mini batch 送进来新的key,那紧挨着它上面的这些 key 就是它之前的那些 Mini batch 编码好送进来的。这样使用了队列之后,就可以把这个字典的大小和这个 mini batch 的大小彻底就剥离开了。那我们就可以在模型的训练过程中使用一个比较标准的 mini batch, size 一般就是 128 或者256,但是我们的这个字典的大小可以变得非常大,它的大小非常灵活,而且可以当做一个超参数一样才可以单独设置。然后文章接着说,这个字典一直都是所有数据的一个子集,因为我们之前说过,想在算对比学习的这个目标函数的时候,只是取一个近似,而不是在整个数据集上去算一个loss。而且作者说使用队列这个数据结构可以让维护这个字典的计算开销非常小。事实上也确实如此,我自己其实也试过,如果把这个字典的大小从几百变到几千或者上万,整体的这个训练时间基本是不变的。

最后作者又强调了一下说为什么就要使用队列这个数据结构,是因为队列有这个先进先出的特性,这样每次移出队列的都是最老的那些 mini batch,也就是最早计算的那些 Mini batch,这样对于对比学习来说是很有利的,因为从一致性的角度来说,最早计算的那些 Mini batch 的 key 是最过时的,也就是说跟最新的这个 Mini batch 算的这个新的 key 是最不一致的。

那讲完队列之后,作者就开始写文章的第二个贡献,也就是如何用动量的思想去更新这个编码器。他说用队列的形式当然可以让这个字典变得非常大,但是也因为用了非常大的字典,也就是说非常长的这个队列,就导致我们没有办法给这个队列里所有的元素去进行梯度回传了。也就是说这个 key 的编码器没办法通过反向传播的方式去更新它的参数。那这个问题怎么解决?总不能 query 编码器那边那个 FQ 一直在更新,但是你这边 key 的编码器就一直不动。然后作者说如果你想更新这个 key 的编码器,其实有一个非常简单的方法,就是说你每个训练 iteration 结束之后,你把那个更新好的编码器参数 FQ 直接复制过来给这个 key 的编码器 FK 不就行了吗?那这个想法简单确实是简单,但是作者紧接着说这个方式的结果并不好,他们觉得这个结果不好的原因是因为你一个快速改变的编码器,降低了这个队列里所有 key 的这个特征的一致性。

那这句话怎么理解?假如说我们现在有一个队列,新的元素从左边进,然后旧的元素就右边出,假设我们的 Mini batch size 就是一,意思,就是我们每次只更新一个key,那K1、K2、K3、K4,K5,就所有的直接 key 都是由不同的编码器产生。那这样你这些快速改变的编码器就会降低所有 key 之间的这个一致性。

所以作者接下来说我们提出了一个动量更新的方式来解决这个问题。那现在如果我们把 key 编码器 FK 的参数设为CDAK, query 编码器 FQ 的参数设为CDAQ,那 CDAK 就是以这种动量改变的方式进行更新的。这个式子我们之前在引言的时候也已经讲过了,这里面 m 就是动量参数,它是一个 0- 1 之间的数。 set a q 也就是 query 编码器是通过梯度反向回传来更新它的模型参数的。而 set a k 除了刚开始是用 set AQ 去初始化以外,它后面的更新大部分主要是靠自己,因为如果这个动量 m 设的很大,那 set a k 更新就非常缓慢了。

这位作者杰西尔说,因为使用了这种动量更新的方式,所以虽然在队列里这些 key 都是由不同的编码器产生得到的,但是因为这些编码器之间的区别都太小,所以产生的这些 key 它的一致性还是非常强的。那为了强调这个观点,作者说在他们的实验里,他们用了一个相对比较大的动量,就是 0. 2999,那这个确实比较大了。意思就是说这个 seed k 每次的更新 99. 9% 就是原来的编码器参数,只有 0. 1% 是从更新好的 c 代 q 里借鉴过来的,那自然这个 CDAK 的更新就非常缓慢了。

然后作者还做了对比实验,就是当它把这个动量变小一点,就是变成 0. 9 的时候,其实 0. 9 也不算小了,但是作者说使用一个相对较大的动量,零点儿九九九要比零点儿 9 效果好得多。所以就意味着说,如果你想充分利用好队列,那一个缓慢更新的 key 编码器是至关重要的,因为它可以保证这个队列里所有的 key 是相对一致的。那读到这儿,其实 MOCO 的主要贡献就已经讲完了,但是如果对比学习不是很熟的人来说,可能现在还是有点儿懵,就是 MOCO 它这个前向过程到底长什么样儿?它有没有一个模型总览图可以让我们看呢?那可惜 MOCO 这篇论文并没有提供一个很直观形象的模型总览图,取而代之的它直接给你上了伪代码,那我们再等几分钟就能读到这份伪代码了,写的是相当简洁明了,别说是理解 MOCO 了,就算是去复现 MOCO 也没有问题。

那在讲伪代码之前,作者还有一个坑要填,这是之前在引言第三段提到过的,他说之前的那些对比学习的方法都可以看作是字典查找,但它们都或多或少受限于这个字典的大小和这个字典一致性的问题。所以作者这里就得详细的解释一下,到底之前的方法是怎么受限的,那 MOCO 又是如何通过动量对比的方式去解决这些局限性的。

所以作者这里就把之前的方法大概总结了一下,就归纳成了两种架构,第一种就是比较直接的这个端到端学习的方式,那我们现在就去看一下这个图 2A 跟着图讲会比较直观。那端到端学习,顾名思义就是说这个编码器都是可以通过梯度回传来更新模型参数的。在图 2 的标题里,作者也写到,这个编码器 q 和 k 可以是不同的网络,但是之前很多工作都用的是同样的网络。为了简单起见, MOCO 的实验里,这个编码器 q 和 k 是同一个模型,也就是一个 RES 五十。

那为什么它可以用同一个模型?因为它的正负样本都是从同一个 mini batch 里来的,就是说这里的 XQ 和 XK 都是从同一个 batch 里来,它做一次 forward 就能得到所有样本的特征,而且这些样本是高度一致的,因为都是从一个编码器里来,听起来确实很美好,编码器都能用反向回传学习了,特征也高度一致了。

但它的局限性就在于字典的大小,因为在端到端的学习框架中,字典的大小和 Mini batch science 的大小是等价的。那如果我们想要一个很大的字典,里面有成千上万个 key 的话,也就意味着 Mini batch 的 size 大小必须也是成千上万,那这个难度就很高了,因为现在的 GPO 是塞不下这么大的 batch size,而且就算你有内存更大的硬件,你能塞下那么大的 batch size。但我们也知道大 batch size 的优化也是一个难点,如果处理不得当,模型是很难收敛的。所以作者这里说这种端到端学习的方式就是受限于字典的大小。但我这里想插一句,之所以我们选择读 MOCO 这篇论文,而不是读 same clear,是因为 same clear 其实就是这里这种端到端的学习方式,当然它还用了更多的这个数据增强,而且提出了在编码器之后再用一个Projector,会让学到的特征大大变好。

但是总体来说, same clear 就是这里的端到端学习方式。他们之所以能这么做,是因为 Google 有TPU, TPU 内存大,所以就可以无脑上 batch size。像 same clear 里,他们就选用了 8192 当作 batch size,这样最后就会有 1 万多个负样本,那这个负样本的规模就足够对比学习了,所以 same clear 就可以用这么简单的方式直接去做端到端的学习。

那看完端到端学习的这种做法以后,我们发现它的优点在于编码器是可以实时更新的,所以导致它的字典里的那些 key 一致性是非常高的。但它的缺点就在于因为字典的大小就是 Mini bash 的大小,导致这个字典不能设的过大,否则硬件内存持不行。那这样的话很自然另外一个流派就应运而生了,也就是说我更关注字典的大,然后牺牲一些一致性,这也就是我们马上要看的第二种方式就是 memory bank。

在 memory bank 里其实就只有一个编码,也就是 query 的编码器是可以通过梯度回传进行更新学习的,但是对于字典这个 key 这边是没有一个单独的编码器的,那它是怎么操作的?其实 memory bank 就是把整个数据集的特征都存到了一起,那对于 Imagenet 来说,这个 memory bank 里就有 128 万的特征,听起来好像很大的样子,但其实 memory bank 的作者,也就是 MOCO 这篇论文里的文献61,他在论文里说,因为每一个特征只有 128 维,所以即使我整个 memory bank 里有 128 万个key,最后我也只需要 600 兆的空间就能把所有的这节 key 存下来了。即使是对整个数据集的特征做一遍,最近连查询在泰坦 x 的 GPU 上也只需要 20 毫秒,所以是非常高效的。

然后一旦有了这个 memory bank,那在每次模型做训练的时候,只需要从这个 memory bank 里去随机抽样很多 key 出来当作字典就可以了。就相当于这整个右边这些操作都是在线下执行的,所以完全不用担心硬件内存的问题。也就是说这个字典可以抽样抽得很大,但有得必有舍, member bank 的做法在特征一致性上就处理的不是很好。那假如说我们现在有这么一个 memory bank,里面有 128 万个key,它在训练的时候是随机抽样很多样本出来当作这个字典的。那我们这里为了讲解方便,我们就说它是顺序抽样,而且这个字典大小就是三。那就是说当你在做对比学习的时候,你当前的 mini batch 可以先把这个K1、K2、 K3 这三个 key 抽出来,把当成负样本,然后去跟你这个 query 算loss,当你算完这个loss,回传了梯度,然后更新了这边这个编码器之后, memberbank 的做法就是会用这个新的编码器在原来对应位置上,也就是这个K1、K2、 K3 的位置上的那些样本去生成新的特征。也就是我用蓝色笔写的新的K1、K2、 K3 这三个key,然后我们把这个新的 key 放回到 memory bank 里。

3 它就把这个 memory bank 更新了,依次类推。那下次假如说我把 456 这三个 key 抽出来做一次模型更新,然后再把新的 key 456 放回去,那这个 456 也就被更新了,那这里就会有一个问题,也就我们之前反复讲过的问题,因为你这里的特征都是在不同时刻的编码器得到的,而而且这些编码器都是通过梯度回传来很快的更新的,那就意味着你这里得到的这些特征都缺乏一致性。而且 memory bank 还有另外一个问题,因为 memory bank 里存了所有的图片,那就意味着你模型训练了整整一个epoch,才能把整个 memory bank 更新一遍。

那就意味着当你开始下一个 epoch 训练的时候,比如说下一个epoch,我选了 158 这三个key,那这 3 个 key 的特征都是上一个 epoch 不知道是哪个时间点算出来的特征了,这也就导致说这个 query 这边的特征跟 key 这边的特征差非常远。所以说总结一下 member bank 的做法就是牺牲了特征的一致性,从而获得了可以构造很大的字典的特权。但显然,这两种流派无论是端到端的学习还是 memory bank 的方法,它都像 MOCO 的作者说的一样,受限于字典大小和特征一致性这两个方面中的至少一个。所以为了解决之前这两种做法的局限性,作者提出了 MOCO 采用队列的形式去实现一个字典,从而使得它不像端到端的学习一样受限于 batch size 的大小。同时,为了提高字典中特征的一致性, MOCO 使用了动量编码器。其实从整体上来看, MOCO 跟 memberbank 的方法是更加接近的,它们都是只有一个编码器,就是query,这边的编码器是通过梯度回传来更新模型参数的,然后他们的字典都是采取了额外的数据结构进行存储,从而和 batch size 剥离开来。

memberbank 里就是使用了memberbank, MOCO 里就是使用了队列,而且 memory bank 这篇论文它也意识到特征不一致性带来的坏处了,所以他们当时还加了另外一个loss,叫 proximal optimization,目的就是让训练变得更平滑。

其实跟 MOCO 里的这个动量更新是有异曲同工之效的。这里对这个 loss 我就不展开说了,感兴趣的同学可以去看他们原来的论文,也就是这里的文献。 61 关于这一点, MOCO 的作者在后面也提到了 memory bank 这篇论文,他们也用了动量更新的方法,只不过他们的动量更新的是特征,而我们动量更新的是编码器。而且 MOCO 的作者还补充说, MOCO 的扩展性是很好的,它可以在上亿级别的图像库上去进行训练。但是对于特别大的数据集, Marabank 的方法就捉襟见肘,因为你需要把所有的特征都存到一个 memory bank 里,那对于 Imagenet 这种百万规模的数据集来说,存下所有的特征只需要 600 兆的空间,那对于拥有一个亿级图片规模的数据集,存储所有的这些特征就需要几十 g 上百 g 的内存了。所以 member bank 的扩展性不如 MOCO 好。

总之就是一句话, MOCO 既简单又高效,而且扩展性还好,它能同时提供一个又大而且又一致的字典,从而进行对比学习。终于读完了 3. 2 章节,大家应该对 MOCO 的研究动机,它的贡献有了一定的了解,那接下来马上就可以看伪代码了。作者在 3. 3 节写的是代理任务,但因为我们已经讲了很多遍了,所以这里就不重复了。作者就是说为了简单起见,他们就用了这个个体判别的任务,然后个体判别的任务定义就在这里。接下来作者说算法一提供了 MOCO 的伪代码,我们现在来看。算法一它先定义了一些参数,比如说 f q, f k 分别是 query 和 key 的编码器,然后 q 这个队列就指的是字典里面一共有 cake key,所以它的维度就是 c 乘以key,这里的 c 就是每个特征的维度。然后 m 是动量, e 就是算 info n c e loss 的时候那个温度。

好,那我们现在走一下这个模型的整体的前向过程。首先我们要初始化两个编码器,那对于 query 编码器这个 FQ 来说,它是随机初始化的。然后我们把 FQ 的参数直接复制过去给FK,就把 FK 也初始化了。接下来从 dataloader 里去拿一个 bash 的数据,这里数据用 x 表示,然后它有 n 个sample,那在 MOCO 的代码里,这个默认的 batch size 是256,也就是这个 n 是256,所以是非常标准的 batch size。你是可以在常用的 GPU 上进行训练的。

接下来第一步就是得到一个正样本。对,所以从原始的数据 x 开始,我先做一次数据增强,得到了一个 query 图片。然后我再去随机的做一次数据增强,得到了一个 key 的图片。因为这个 query 和 key 都是从同一个数据 x 得到的,它的语义不应该发生太大的变化。所以说这个 SQ SK 就成为了一个重要门队。

接下来我们把 query 的数据通过 query 的编码器做一次前向,从而得到真正的那个特征 q q 的特征维度是 n 乘c,也就是我们刚才说的 256 乘以128,那为了便于理解,我们这里再具体化一些。那假如说这个编码器FQFK,它就是一个 rise 50 的网络,那我们也知道 rise 50 等到最后一层做完 global average pulling 以后,它会得到一个 2048 尾的特征。一般如果你是在 Imagenet 数据集上去做,有监督训练的时候,你会加一个分类头,把这个 2048 维变成 1000 维,然后这样你就可以去做分类了。那在这里呢,作者就是把那个 1000 换成了128,也就是说从 2048 维变成了 128 维。那为什么使用128?其实是为了跟之前的工作保持一致,也就是之前 memory bank 那个工作文献61。 memory bank 那篇文章里为了让整个 memory bank 变得尽可能的小,所以它特征的维度选的也相对较小,就只有128。

好,我们回到尾代码,那接下来就是这个key,通过编码器 FK 得到正样本的那个key,它的维度也是 256 乘以128。接下来因为是 Pytorch 的代码,所以用一个这个 detach 操作,把这个 gradient 去掉,这样对于 key 来说就没有梯度回传了,反正也是要放到队列里去的。

那接下一步就是算Logics,也就是之前公式一里算 info NCE loss 的时候,这样的那个分子就是 q 乘以 k positive,也就是这里的这个 q 乘以 k positive,那我们就得到了这个正样本的logics,这特征维度就变成了 n 乘一2561。那对于负样本来说,我们怎么去算它的Logis?首先我们得从队列里把负样本拿出来,也就是这里的q,那接下来就是算公式一里应付 NCE 的分母了,也就是你做一个求和,然后你的 query 乘以KI,而这个 i 是从 0 到 k 的。

那做完这一步乘法之后,你就得到了负样本的logges,也就是这里的 n 乘以k,所以就是 256 乘以6536。因为 MOCO 这篇论文里它默认的字典大小是65536,里面有 6 万多个 key 这么大。那最后总体的logics,也就是既有正样本又有负样本,就是所有的 Logis 拼接起来就变成了一个 256 乘以 65537 的一个向量。也就是像公式一里说的一样,我们现在其实就是在做一个 k 加一路的分类问题,那一旦我们有了正负样本的Logis,那接下来就是算 loss 了。这里我们之前也提过,它其实就是用了一个 cross attribute loss 去做这个实现。那既然你是交叉伤loss,你肯定就得有一个 ground truth,那作者这里非常巧妙,它这里的 ground truth 就设计了一个全零的向量。为什么是全零?因为按它这种实现方式,所有的正样本永远都是在这个 largest 第一个位置上的,也就是位置0。所以对于正样本来说,如果你找对了那个key,你在分类任务里得到的正确的类别就是类别0,所以就巧妙地用这种方式创建了一个 ground truth,从而算了一个对比学习的loss,那有了 loss 之后,自然就是先做一次梯度回传,有了梯度之后就可以去更新这个 query 的编码器了。

接下来就到了 MOCO 的第二个贡献,也就是动量更新。因为我们不想让这个 FK 变得太快,所以 FK 的参数大部分都是从上一个时刻的参数直接搬过来的,只有非常少一部分是从当前更新过的 FQ 里拿过来的,这样就保证了这个 key network 是缓慢更新的。

最后一步就是更新队列,把你新算的这波 key 放进队列里,然后把最老的那个 key 队列里移除。那读完了这份伪代码,其实我们应该对 MOCO 就有一个更全面的了解了,而且我非常建议你去看一下 MOCO 的那个官方代码,真的是写得极其出色,非常简洁明了,而且那个代码基本就跟这个伪代码是一模一样的。那读到这里就把 MOCO 整体的算法以及大部分的细节都说完了。

最后我还想大概提一讲 MOCO 这篇论文里用的一个小trick,也就是这个 shuffle b n 的操作。我并不会很细致的讲,因为这个操作在接下来很多论文里,甚至包括作者团队接下来的工作,比如 Sam 也没有再用 shuffle b n 这个操作了。

它的意思是说,因为你用了 b n 以后,很有可能造成当前 batch 里的这些样本中间的信息会泄露,因为 b n 要算这些样本的 running mean 和 running variance,也就是说它能通过这些泄露的信息很容易的去找到那个正样本,而不需要真正去学一个好的模型,也就是作者这里说的这个模型会走一条捷径。

那怎么解决这个问题?因为 BN 这个操作大部分时候都是在当前这个 GPU 上算的,所以作者们在做多卡训练之前,先把样本的顺序打乱,再送到所有的 GPU 上去,等算完了特征,再把顺序恢复来去算最后的loss,那这样对 loss 是没有任何影响的,在你每个 GPU 上的 BN 计算就可就变了,就不会再存在这种信息泄露的问题了。

类似的 BN 操作还在后续 BYOL 那篇论文里引起了一段很有意思的乌龙事件,我们接下来会录一期关于对比学习的论文串烧,会在那里讲一讲 b n 到底引起了一个怎样的讨论。总之, b n 这个操作真是让人又爱又恨。用得好当然威力无穷,但是 90% 的情况可能都是属于用得不好会带来各种莫名其妙的问题,而且很难去debug。所以现在换成 Transformer 也好,这样直接就用 layer known 就能暂时不用理会鼻音了。

最后我们来看一下实验。作者先说了一下数据集的使用,就是在做无监督的预训练时候,他们用了标准的 image net 数据集,然后为了验证 MOCO 的扩展性很好,他们还是了 Facebook 自己的 Instagram Evilian 的数据集。然后作者这里说Imagenet,一般之前大家都叫它 Imagenet one k,因为它有 1000 个类别,但是它们这里叫 Imagenet one million。至于 MOCO 的论文里用的是个体判别的任务,所以类别数不再是1000,而是整个数据集图片的数量,也就是 one million。然后这个 Instagram 数据集作者之前在引言也提到过,就是说它跟 image net 有一些不一样的特性,比如它展示了真实世界中数据的分布,就是长尾的,或者说是不均衡的,同时它的图片既有那种只有一两个物体的,也有那种有很多物体或者是场景层面的图片。

接下来作者又讲了一下训练的情况,因为还是 CNN 时代,所以说就用的是 SGD 当成优化器,那对于 Imagenet 这个数据集来说,他们用的是标准的batch, size 就是256,然后是在一台扒卡机上训练的,如果用 RES 50 这个网络结构的话,训练 200 个 epoch 大概需要是 53 个小时。这真的已经是大佬给我们送福利了。

因为相对于之后的工作, same clear, swab 或者 BIOL 这些工作, moco 不仅是从硬件本身,还是从训练时长上,都已经是最 affordable 的方法了。而且其实我们后面还可以了解的 MOCO 或者 MOCO v two 这一系列的方法,它的泛化性非常好,就是说当你在做下游任务的时候,它学到的特征依旧非常强大。所以虽然看起来 same clear 的这个引用会比 MOCO 稍微高一些,但其实真正在视觉领域里,大家在别的任务上用对比学习的时候,绝大多数工作都是沿用的 MOCO 这套框架。

说完了预训练所用的数据集以及是如何训练模型的,接下来就该看结果了。作者是分了两个大部分去展示结果的,第一部分就是用这种 Linear classification Protocol,就我们之前讲过的,把这个训练好的模型当作一个特征提取器。在 4. 2 章节,主要作者就讲了一下牵引学习的效果。

我们先看 4. 1,作者说在他们完成了这个无监督的预训练之后,他们把模型的 backbone 冻住,只把它当作一个特征提取器,然后在它上面训练一个全连接层,去充当一个分类头训练,这个分类头用了 100 个一炮。至于结果的衡量,是在 Imagenet 的测试集上报告了 one crop top one 的这个准确度。这里面其实最有意思的是接下来这段话,他说他们做了一个 great search,然后发现这个最佳的学习率是30。

这个其实是很不可思议的,因为除了在神经网络搜索就是 NARS 那些工作里可能会搜到一些比较夸张的 learning rate 之外,过去几十年里用神经网络的工作都没有用超过一的这种 learning rate,因为你已经预训练好一个网络了,你现在要做的只是微调,所以说大家最大可能也就是设等 0. 1,然后降到 0. 010. 001 这样往下降。

很少有人会设一个比一大的 learning rate,但在这里竟然是30,所以说做无监督学习或者说对比学习的同学,可以回去看一下你的 learning rate,如果你的结果还不好,很有可能是你的这个学习率还没有设置对。然后基于这个现象,作者其实也总结了一下,他说这个学习率确实比较诡异。他安,但是就是这种无监督对比学期学到的特征的分布,跟有监督学习到的这个特征分布是非常不一样。接下来我们看第一个消融实验,这个消重实验主要是用来对比之前图 2 里展示着三种对比学习的流派,也就是端到端学习 a memory bank 和本文提出的MOCO。这个图的横坐标用 k 表示,也就是说我用了多少个这个负样本,你可以粗略地把它理解成字典的大小,然后纵坐标就指的是在 Imagenet 上的这个 top one 准确率,也就是 Linear classification protocol 下的那个准确率,那我们就按顺序看。

首先第一个我们看黑色这条线就是端到端的学习,那端到端的学习的结果就只有这三个点,因为受限于显卡内存,作者刚才也说了,用一台现在已经很好的 8 卡 V100 机器,就是有 32 g b 内存的那种,它能塞下的最大的 batch size 也就只有1024,所以即使是大佬也得止步于此。那对于第二种方法就是 memory bank 的形式,当然它可以用很大的字典,所以说它的这个曲线可以走得很远,但是它的效果整体上要比端到端和 MOCO 的结果都要差一截,作者说这就是因为特征的不一致性导致。

最后我们来看 MOCO 确实可以有很大的字典,之所以停在6万这个数字,我们也可以看到,其实从 1 万到6万这个性能也已经比较饱和了,所以说再大可能也不会带来更多的性能提升了。如果拿 MOCO 去跟端到端学习的方法做比较,我们可以看到在刚开始的时候,这个曲线的重合度还是非常高的,但是作者说因为没有实验的支撑,我们也不知道这条黑线是否能继续按照这个趋势增长下去,它有可能结果会更高,当然也有可能结果会更低,但是做不了实验,所以我们也无从得知。所以基于这个图3, MOCO 是性能最好,对硬件要求最低,而且扩展性也比较好的方法。

那说完了使用队列的好处,接下来就该做消融实验去证实第二个贡献,也就是动量更新所带来的好处了。所以说这也是写论文时候必须注意的东西,就是如果你提出了几点贡献,那你就一定得做针对这几点贡献的消融实验,这样才能有效地证明你提出的贡献是有效的,否则口说无凭。

那我们来看针对动量从小到大到底会对模型的性能带来什么样的影响。作者说使用一个相对较大的值,就是 0. 999 或者 0. 9999 的时候,性能是最好的,它俩差不多都是59。这也就说明一个变化非常缓慢的编码器是对比学习有好处的,因为它能提供一个一致性的特征,但是当你把这个动量逐渐变小,变到 0. 99 或者 0 点儿九的时候,这个性能的下降就比较明显了。尤其是当你直接去掉动量,我就是把那边儿快速更新的 query 编码器直接拿过来当 key 的编码器用,你就会发现,不光是性能下降的问题,整个模型甚至都不能收敛了,它的这个 loss 一直在震荡,从而导致训练失败。所以说,别看小小的一个 table 就一行,它其实非常有力地证明了作者的论点,就是说我们要建立一个一致性的字典。

那讲完了消融实验,接下来我们就来到了这个兵家必争之地,也就是 Imagenet 上的这个效果的比较。作者强调说这里的所有的方法都是在这个 Linear classification protocol 下面进行的,也就是说都是把这个网络当成一个特征提取器,抽出来特征,再去训练一个全连接层当做分类头,最后得到这些结果,所有的这些结果都没有去更新那个backbone。然后这个表格上面这些方法都不是用对比学习的,这些方法就都是用了对比学习的。我们首先可以看到,就对比学习效果还是确实不错的,因为明显这些结果就要比上面这些结果要高一大截。然后同时作者还强调说无监督学习里这个模型的大小是非常关键的,因为一个公认的事实就是说,如果你的模型越大,一般你的效果就会越好,所以如果你只比最后的这个准确度,而不去关注模型大小的话,这个就有点赖皮了。

所以说作者在这个表格里也把具体使用的网络结构,还有就是网络所用的这个模型参数大小也都罗列了出来,这样就可以去做一个相对全面,而且相对公平的比较了。比如说如果我们只看标准的 rise 50,也就是这块儿 rise 50,它们的模型参数都是 24 米连这么大,那之前利用标准的 rise 50 得到的结果,这个最高也就是这个 58 点儿 8 了,而 MOCO 是能达到 60. 6 的,所以要比之前的结果高两个点,已经算是很大的提升了。

然后我们现在如果不对模型架构做限制,就是你可以变得很大,比如说这里这些三层、二层、四层,就是说我把这个模型里的这个 channel 数增多,让它变得更宽,它还是 50 层那么深,但是它更宽了,也就是说它的模型参数更大了。那在这种情况下,之前最好的结果也就是比如说这块儿 68 点儿四 60 八点儿一这种,但 MOCO 只用 375 million 就已经能达到 68 点儿 6 了,还是比之前的方法都高的。而且作者这里还加了特殊标记,而是有这些标记的方法都用了 fast auto Augment 去做这个数据增强,而这个数据增强的策略其实之前是在 Imagenet 那边用有监督的训练方式训练得到的,其实这就不太好说,就有点儿不公平。

然后对于 56 这个方法,也就是 CMC 来说,它其实是用了两个网络,而不是只用一个网络,所以你这里可以看到它其实是 47 兆,也就是两个 24 这么大,但不论你是用了更好的这个数据增强,还是用了两个甚至多个编码器。总之 MOCO 既能在小模型上得到最好的结果,也能在大模型的比较中得到最好的结果。接下来就是最后一个章节了,也是全文的点睛之处,作者就是想验证一下 MOCO 预训练模型得到的这个特征,到底在下游任务上能不能有好的这个迁移学习效果。接下来这两句话要划重点,作者说无监督学习最主要的目标就是去学习一个可以迁移的特征,用 Imagenet 去做这种有监督的预训练,它最有用的时候,也就是说最有影响力的时候,就是当你在下游任务上做微调,你可以用这个预训练好的模型去做这个模型的初始化,从而当下游任务只有很少的标注数据的时候也能获得很好的效果。所以说这个章节的结果才是我们真正关心的。那接下来作者就拿视觉领域里最常见、应用最广的任务,也就是检测来做。

这个无监督的 MOCO 预训练模型和 image net 有监督预训练模型之间的比较,但是在展示结果之前,作者说我还得讲两个事情,为什么要谈归一化?因为我们之前在这个 4. 1 章节刚开始的时候也提到了,如果我们拿 MOCO 学出来这个模型直接当一个特征提取器,那他在做微调的时候,他的最佳学习率竟然是30,也就说明 MOCO 学到的特征跟有监督学到的特征它分布是非常不一样,那我们现在是要拿这个特征去做不同的下游任务,我不可能在每个下游任务上都去做一遍 great search,都去找一下它最佳的那个学习率是多少。那这个就有点太麻烦了,也有点儿失去了这个无监督预训练的意义。

这位作者还是想,如果我们可以拿之前就大家为这些有监督的预训练已经设置好的超参数来做微调的话,那不就既可以做公屏对比,而且也不用去做这个 grade search 了?那一般当特征这个分布不一致的时候,你最常想到的解决方法是什么呢?对,当然就是归一化了,所以作者这里说我们就用了一个就特征归一化的方法,那其实具体来说就是整个模型现在都在微调了,而且尤其是这个 BN 层,它现在用的是这个 sync BN,也就是 synchronized batch norm,就是它把你多机训练的时候,所有 GPU 上的那个 batch norm 的统计量都合起来算完 running mean、 running variance 以后再去做这个 BN 层的更新,这样就会让特征的归一化做得更彻底一点,也会让模型的训练更稳定一些。同时他说还在新加的这些层里,比如说你在做检测的时候,你都会用到 FPN 这个结构,在这里面它也用了BN,目的就是去调整一下这个值域的大小,从而好做这个特征的归一化。那一旦做完这个特征归一化,作者就发现我就可以开心地拿之前有监督训练那边用的超参数来做这个微调了。

第二个作者要强调就是在训练的时候,这个 schedule 为什么要说这个事儿?是因为作者团队之前有一篇论文就这个31。那在这篇论文里发现了一个很惊人的结论,就是说当你下游任务的这个数据集足够大的时候,比如说像Coco, Coco 就很大,你可以不需要预训练,直接就从随机初始化开始,从头训练,最后的效果一样可以很好。

那如果有了这个结论的话,那你不论是有监督的预训练,还是无监督的预训练,这不都无所谓了吗?反正我们已经不需要预训练的模型去做模型初始化了,那这样还怎么体现出 MOCO 的优越性呢?那其实很简单,因为在 31 那篇论文里,它说的是当你训练足够长的时候,也就是说训练 6 成或者 9 的那个学习时长的时候。

如果训练的短,预训练模型还是有用的,那我们现在就在预训练时长短的时候做比较不就行了吗?所以在 MOCO 的论文里,作者说我们在这篇论文里就是只用一成或者 2 成的这个学习时长,在这个时候预训练还是非常有用的,那我们就可以比较到底是 MOCO 预训练的好,还是有监督的 Imagenet 训练的好了。但总之讲,这个归一化和这个学习时长都是为后面这段话做铺垫的。

作者就是想说,当我们用 MOCO 的预训练模型去做微调的时候,我们的微调也是跟之前就是有监督预训练模型,然后在微调时候的方式是一样的。这样的好处就是说,当你去在不同的这个数据集或者不同的任务上做微调的时候,我们就不用再去调参了,就用之前已经调好的参来做就行了。然后作者又补充说,这么做有可能对 MOCO 来讲是不利的,因为如果你针对你提出的那个方法去做超参搜索的话,有可能是能得到更好结果的。作者很自信地说,无所谓,即使如此, MOCO 的表现还是很亮眼的。接下来作者就展示一下在 Pascal VOC 这个数据集上做检测是什么结果。这里 a 和 b 分别是用了两种不同的这个网络结构,每一个表格里都是 4 行,第一行就是用随机初始化的模型再去做微调,所以它是一个基线网络,分数比较低。第二行就是用有监督的 Imagenet 预训练的模型做初始化,然后再去做微调,也就是一个比较强的极限。结果最后两行就分别是 MOCO 在 Imagenet 上和在 Instagram ebillion 上就做无监督预训练,当做模型的初始化,然后再去做微调。

我们可以看到,除了这一行的这个效果, MOCO 是略微不如有监督预训练的模型,在其他所有的这个衡量指标下, Moco Imagenet 就已经超过了这个有监督的预训练模型。当换成更大的数据集就 Instagram one BILLING 的时候,还会有进一步的提升,但其实之前我们在结论里也讲过了,就是这个Instagram, one BILLING 带来的提升都不是很大,可能都是零点几个点。接下来作者又再次比较了一下之前的三种对比学习的方式,也就是端到端 member bank 和MOCO。因为之前那次对比是在 Imagenet 做 Linear evaluation Protocol 下面去做测试的,那现在在下游任务这边再做一次对比,如果还是 MOCO 最好,那这个说服力就比较强了。

而事实上也确实如此, MOCO 跟前面两种方式比起来是好了很多的。而且最主要的是之前的这两种方法它都没有超越,有监督预训练模型的结果,只有 Moco 是真的超越了。那说完了, VOC Coco 数据集肯定是跑不了的。那在这里作者比较了四个设置,前面两个都是用的 rise 50 f p n,但是它的学习时长不一样,一个是一乘,一个是 2 乘,后面两行用的是 rise 50 C 4 的结构,一个是一乘,一个是 2 乘。除了在设置 a 里面,这个 MOCO 的模型稍显逊色,在剩下三个设置下, MOCO 预训练的模型都比 Imagenet 有监督,预训练的模型得到的效果好。

然后大佬不辞辛苦又帮我们把别的任务也全都测试了一遍,从这个 key point detection 和人体关键点检测,然后到这个姿态检测,然后再到在 LVS 和 cityscape 上去做这个实例分割,和 cityscape 上去做这个语义分割,作者全都测试了一遍,基本的结论还是说 MOCO 预训练好的这个模型在大部分时候都比 image net 那个预训练模型要好,即使偶尔比不过也是稍显逊色。

所以说作者最后在这儿提供了一个总结,他说 MOCO 在很多这个下游任务上都超越了这个 Imagenet 有监督预训练的模型,但是在零零星星的几个任务上, MOCO 稍微差了一点,主要是集中在这个实力分割和语义分割的任务上。所以接下来大家也都怀疑说对比学习这种方式是不太适合做这种 dance predictional task,就是这种每个像素点都要去预测的这种任务,所以后续也涌现了很多基于这个出发点的工作,比如说 dance contrast 或者 Pixel contrast 之类。

总体来说,文章最后的结论就是, MOCO 在很多的这个视觉任务上,已经大幅度地把无监督和有监督之间这个坑填上了。最后,作者为了鼓舞大家,为了跟 LP 那边儿保持步调一致,作者还特别强调了一下,在所有的这些任务里, Moco 在 Instagram 这个数据集上训练出来的模型是要比在 Imagenet 训练出来的模型要好的,而且是在所有任务上普遍的都好。这说明什么呢?说明 MOCO 扩展性好,也就是说给你更多的数据, MOCO 有可能就能帮你学到更好的模型。就跟 NLP 那边得到的结论是一样的,这个也就符合了无监督学习的这个终极目标。

总之,感谢 MOCO 这篇论文以及它高效的实现,能让我们大多数人有机会用普通的 GPU 就能去跑对比学习的实验,做这些激动人心的研究。也因为 MOCO 在各个视觉任务上取得这么好的性能,也激发了很多后续分析性的工作。去研究 MOCO 学出来的这种特征到底和有监督学出来的特征有什么不一样,我们还能从什么别的方向去提高?对比学习来还准备串一下对比学习这一系列的工作,比如说从最开始第一阶段的instituss,CPC,CMC,到第二阶段这 MOCO V1, same clear V1,然后 MOCO V2, same clear V2。然后到第三阶段就不需要副样本的必备 OL 和 same Sam,以及到第四阶段用了 vision Transformer 的 MOCO V3 和低等。但是因为光 MOCO 就讲了 90 分钟了,那这个论文串烧就留到下次再讨论。

相关推荐
Elastic 中国社区官方博客3 分钟前
Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
jwolf23 分钟前
摸一下elasticsearch8的AI能力:语义搜索/vector向量搜索案例
人工智能·搜索引擎
有Li12 分钟前
跨视角差异-依赖网络用于体积医学图像分割|文献速递-生成式模型与transformer在医学影像中的应用
人工智能·计算机视觉
新加坡内哥谈技术32 分钟前
Mistral推出“Le Chat”,对标ChatGPT
人工智能·chatgpt
GOTXX41 分钟前
基于Opencv的图像处理软件
图像处理·人工智能·深度学习·opencv·卷积神经网络
IT古董1 小时前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
CV学术叫叫兽1 小时前
快速图像识别:落叶植物叶片分类
人工智能·分类·数据挖掘
WeeJot嵌入式1 小时前
卷积神经网络:深度学习中的图像识别利器
人工智能
脆皮泡泡2 小时前
Ultiverse 和web3新玩法?AI和GameFi的结合是怎样
人工智能·web3
机器人虎哥2 小时前
【8210A-TX2】Ubuntu18.04 + ROS_ Melodic + TM-16多线激光 雷达评测
人工智能·机器学习