Self-Supervised Learning,又称为自监督学习,我们知道一般机器学习分为有监督学习,无监督学习和强化学习。 而 Self-Supervised Learning 是无监督学习里面的一种,主要是希望能够学习到一种通用的特征表达用于下游任务 (Downstream Tasks)。 其主要的方式就是通过自己监督自己。作为代表作的 kaiming 的 MoCo 引发一波热议, Yann Lecun也在 AAAI 上讲 Self-Supervised Learning 是未来的大势所趋。所以在这个系列中,我会系统地解读 Self-Supervised Learning 的经典工作。
总结下 Self-Supervised Learning 的方法,用 4 个英文单词概括一下就是:
Unsupervised Pre-train, Supervised Fine-tune.
这段话先放在这里,可能你现在还不一定完全理解,后面还会再次提到它。
在预训练阶段我们使用无标签的数据集 (unlabeled data),因为有标签的数据集很贵,打标签得要多少人工劳力去标注,那成本是相当高的,所以这玩意太贵。相反,无标签的数据集网上随便到处爬,它便宜。在训练模型参数的时候,我们不追求把这个参数用带标签数据从初始化的一张白纸给一步训练到位,原因就是数据集太贵。于是 Self-Supervised Learning 就想先把参数从 一张白纸 训练到 初步成型,再从 初步成型 训练到 完全成型。注意这是2个阶段。这个训练到初步成型的东西,我们把它叫做 Visual Representation。预训练模型的时候,就是模型参数从 一张白纸 到 初步成型 的这个过程,还是用无标签数据集。等我把模型参数训练个八九不离十,这时候再根据你 下游任务 (Downstream Tasks) 的不同去用带标签的数据集把参数训练到 完全成型,那这时用的数据集量就不用太多了,因为参数经过了第1阶段就已经训练得差不多了。
第1个阶段不涉及任何下游任务,就是拿着一堆无标签的数据去预训练,没有特定的任务,这个话用官方语言表达叫做:in a task-agnostic way。第2个阶段涉及下游任务,就是拿着一堆带标签的数据去在下游任务上 Fine-tune,这个话用官方语言表达叫做:in a task-specific way (这俩英文很重要啊,会不断出现)。
以上这些话就是 Self-Supervised Learning 的核心思想,如下图所示,后面还会再次提到它。

Self-Supervised Learning 经典工作的分类如下图所示。在上篇文章中主要介绍了 Self-Supervised Learning 在 NLP 领域 的经典工作:BERT模型的原理及其变体GPT, MASS, BART, ELECTRA等等,这些方法都是属于 Prediction 类别的。本文主要介绍Self-Supervised Learning 在 CV 领域的经典工作之一:SimCLR和SimCLR v2,它们都是属于 Contrastive 类别的。
那 Prediction 类别和 Constractive 类别有什么不同呢?
Prediction 类别比如说BERT,是会用一堆没有label的句子去训练BERT做填空题 :给一个句子随机盖住 (mask掉) 一个token,输入这个BERT,期望它输出盖住的部分,使用这种办法让BERT无监督地学习到结合上下文做Embedding的能力,学习的过程是一种Prediction的行为。Contrastive 类别方法并不要求模型能够重建原始输入,而是希望模型能够在特征空间上对不同的输入进行分辨,这也会在SimCLR的训练过程中体现。

SimCLR
论文名称:A Simple Framework for Contrastive Learning of Visual Representations
论文地址:https://arxiv.org/pdf/2002.05709.pdf
SimCLR 是Hinton团队在 Self-Supervised Learning 领域的一个系列的经典工作。先来通过图2直观地感受下它的性能:SimCLR (4×) 这个模型可以在 ImageNet 上面达到 76.5% 的 Top 1 Accuracy,比当时的 SOTA 模型高了7个点。如果把这个预训练模型用 1%的ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,SimCLR 就可以再达到 85.5% 的 Top 5 Accuracy,也就是再涨10个点。

那根据上一篇文章BERT的描述,我们说 Self-Supervised Learning 的目的一般是使用大量的无 label 的资料去Pre-train一个模型,这么做的原因是无 label 的资料获取比较容易,且数量一般相当庞大,我们希望先用这些廉价的资料获得一个预训练的模型,接着根据下游任务的不同在不同的有 label 数据集上进行 Fine-tune 即可。
作为 Self-Supervised Learning 的工作之一,SimCLR 自然也遵循这样的思想。我们回忆一下之前 BERT 会用一堆没有label的句子去训练BERT做填空题 (详见上篇文章):给一个句子随机盖住 (mask掉) 一个token,输入这个BERT,期望它输出盖住的部分。这就是BERT进行自监督学习的做法,那么在 SimCLR 里面是如何做的呢?一个核心的词汇叫做:Contrastive。
这个词翻译成中文是 对比的意思,它的实质就是:试图教机器区分相似和不相似的事物。

这个话是什么意思呢?比如说现在我们有任意的 4 张 images,如下图4所示。前两张都是dog 这个类别,后两张是其他类别,以第1张图为例,我们就希望它与第2张图的相似性越高越好,而与第3,第4张图的相似性越低越好。
但是以上做法其实都是很理想的情形,因为:
- 我们只有大堆images,没有任何标签,不知道哪些是 dog 这个类的,哪些是其他类的。
- 没办法找出哪些图片应该去 Maximize Similarity,哪些应该去 Minimize Similarity。

所以,SimCLR是怎么解决这个问题的呢?它的framework如下图5所示:
假设现在有1张任意的图片 \(x\) ,叫做Original Image,先对它做数据增强,得到2张增强以后的图片 \(x_i, x_j\) 。注意数据增强的方式有以下3种:
- 随机裁剪之后再resize成原来的大小 (Random cropping followed by resize back to the original size)。
- 随机色彩失真 (Random color distortions)。
- 随机高斯模糊 (Random Gaussian Deblur)。
接下来把增强后的图片 \(x_i, x_j\) 输入到Encoder里面,注意这2个Encoder是共享参数的,得到representation \(h_i, h_j\) ,再把 \(h_i, h_j\) 继续通过 Projection head 得到 representation \(z_i, z_j\) ,这里的2个 Projection head 依旧是共享参数的,且其具体的结构表达式是:
接下来的目标就是最大化同一张图片得到的 \(z_i, z_j\)。

以上是对SinCLR框架的较为笼统的叙述,下面具体地看每一步的做法:
回到起点,一开始我们有的training corpus就是一大堆 unlabeled images,如下图所示。

数据增强
比如batch size的大小是 N ,实际使用的batch size是8192,为了方便我们假设 \(N=2\) 。

注意数据增强的方式有以下3种:
- 随机裁剪之后再resize成原来的大小 (Random cropping followed by resize back to the original size)。代码:
torchvision:transforms:RandomResizedCrop
- 随机色彩失真 (Random color distortions)。代码:
from torchvision import transforms
def get_color_distortion(s=1.0):
# s is the strength of color distortion.color_jitter = transforms.ColorJitter(0.8*s, 0.8*s, 0.8*s, 0.2*s)
rnd_color_jitter = transforms.RandomApply([color_jitter], p=0.8)
rnd_gray = transforms.RandomGrayscale(p=0.2)
color_distort = transforms.Compose([
rnd_color_jitter,
rnd_gray])
return color_distort
- 随机高斯模糊 (Random Gaussian Deblur)。
random (crop + flip + color jitter + grayscale)

对每张图片我们得到2个不同的数据增强结果,所以1个Batch 一共有 4 个 Image。

通过Encoder获取图片表征
第一步得到的2张图片\(x_i, x_j\) 会通过Encoder获取图片的表征,如下图10所示。所用的编码器是通用的,可以用其他架构代替。下面显示的2个编码器共享权重,我们得到向量 \(h_i, h_j\) 。

本文使用了 ResNet-50 作为 Encoder,输出是 2048 维的向量\(h\) 。
预测头
使用预测头 Projection head。在 SimCLR 中,Encoder 得到的2个 visual representation再通过Prediction head (g(.))进一步提特征,预测头是一个 2 层的MLP,将 visual representation 这个 2048 维的向量\(h_i, h_j\)进一步映射到 128 维隐空间中,得到新的representation \(z_i, z_j\)。利用 \(z_i, z_j\) 去求loss 完成训练,训练完毕后扔掉预测头,保留 Encoder 用于获取 visual representation。

相似图片输出更接近
到这一步以后对于每个Batch,我们得到了如下图12所示的Representation \(z_i,...,z_4\) 。

首先定义Representation之间的相似度:使用余弦相似度Cosine Similarity:

Cosine Similarity把计算两张 Augmented Images \(x_i, x_j\) 的相似度转化成了计算两个Projected Representation \(z_i, z_j\) 的相似度,定义为:
式中, \(\tau\) 是可调节的Temperature 参数。它能够scale 输入并扩展余弦相似度[-1, 1]这个范围。
使用上述公式计算batch里面的每个Augmented Images \(x_i, x_j\) 的成对余弦相似度。 如下图13所示,在理想情况下,狗的增强图像之间的相似度会很高,而狗和鲸鱼图像之间的相似度会较低。

现在我们有了衡量相似度的办法,但是这还不够,要最终转化成一个能够优化的 Loss Function 才可以。
SimCLR用了一种叫做 NT-Xent loss (Normalized Temperature-Scaled Cross-Entropy Loss)的对比学习损失函数。
我们先拿出Batch里面的第1个Pair:

使用 softmax 函数来获得这两个图像相似的概率:

这种 softmax 计算等价于获得第2张增强的狗的图像与该对中的第1张狗的图像最相似的概率。 在这里,分母中的其余的项都是其他图片的增强之后的图片,也是negative samples。
所以我们希望上面的softmax的结果尽量大,所以损失函数取了softmax的负对数:

再对同一对图片交换位置以后计算损失:

最后,计算每个Batch里面的所有Pair的损失之和取平均:

- 对下游任务Fine-tune
至此我们通过对比学习,巧妙地在没有任何标签的情况下训练好了 SimCLR 模型,使得其Encoder的输出可以像正常有监督训练的模型一样表示图片的Representation信息。所以接下来就是利用这些 Representation的时候了,也就是在下游任务上Fine-tune。一旦 SimCLR 模型在对比学习任务上得到训练,它就可以用于迁移学习,如 ImageNet 分类,如下图19所示。此时在下游任务上 Fine-tune 模型时需要labeled data,但是数据量可以很小了。

性能:
SimCLR (4×) 这个模型可以在 ImageNet 上面达到 76.5% 的 Top 1 Accuracy,比当时的 SOTA 模型高了7个点。如果把这个预训练模型用 1%的ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,SimCLR 就可以再达到 85.5% 的 Top 5 Accuracy,也就是再涨10个点。
FAQ1:这个 76.5% 和 85.5% 是怎么得到的呢?
答1:76.5% 是通过Linear Evaluation得到的。
按照上面的方式进行完Pre-train之后,Encoder部分和Projection head部分的权重也就确定了。那么这个时候我们去掉Projection head的部分,在Encoder输出的 \(h_i, h_j\) 之后再添加一个线性分类器 (Linear Classifier),它其实就是一个FC层。那么我们使用全部的 ImageNet 去训练这个 Linear Classifier,具体方法是把预训练部分,即 \(h_i, h_j\) 之前的权重frozen住,只训练线性分类器的参数,那么 Test Accuracy 就作为 a proxy for representation quality,就是76.5%。
85.5% 是通过Fine-tuning得到的。
按照上面的方式进行完Pre-train之后,Encoder部分和Projection head部分的权重也就确定了。那么这个时候我们去掉Projection head的部分,在Encoder输出的 \(h_i, h_j\) 之后再添加一个线性分类器 (Linear Classifier),它其实就是一个FC层。那么我们使用 1%的ImageNet 的标签 去训练整个网络,不固定 Encoder 的权重了。那么最后的 Test Accuracy 就是85.5%。
Linear Evaluation 和 Fine-tuning的精度的关系如下图20所示:当Linear Evaluation 的精度达到76.5% Top1 Accuracy时,Fine-tuning的精度达到了50多,因为Fine-tuning 只使用了1%的标签,而Linear Evaluation 使用了100%的标签。

FAQ2:Projection head 一定要使用非线性层吗?
答2:不一定。作者尝试了3种不同的 Projection head 的办法,分别是:Non-Linear, Linear 层和 Identity mapping,结果如下图21所示。发现还是把Projection head g(.) 设置成非线性层 Non-Linear 比较好。 Non-Linear 比 Linear 层要涨3%的Top 1 Accuracy,比 Identity mapping 层要涨10%的Top 1 Accuracy。
而且,作者的另一个发现是 Projection head 前面的 hidden layer 相比于 Projection head后面的 hidden layer 更好。那这个更好是什么意思呢?
就是假设我们把 Projection head 前面的 hidden layer ℎ 作为图片的representation的话,那么经过线性分类层得到的模型性能是好的。如果把Projection head 后面的 hidden layer g(ℎ) 作为图片的representation的话,那么经过线性分类层得到的模型性能不好,如下图22所示,是对 ℎ 或者 g(ℎ) 分别训练一个额外的MLP, ℎ 或者 g(ℎ)的hidden dimension 都是2048,性能如图。


FAQ4:SimCLR 的性能与 Batch size 的大小和训练的长度有关吗?
答4:有关系。如下图所示,作者发现当使用较小的 training epochs 时,大的 Batch size 的性能显著优于小的 Batch size 的性能。作者发现当使用较大的 training epochs 时,大的 Batch size 的性能和小的 Batch size 的性能越来越接近。这一点其实很好理解:在对比学习中,较大的 Batch size 提供更多的 negative examples,能促进收敛。更长的 training epochs 也提供了更多的 negative examples,改善结果。

SimCLR v2
论文名称:Big Self-Supervised Models are Strong Semi-Supervised Learners
论文地址:https://arxiv.org/pdf/2006.10029.pdf
SimCLR v2 和 SimCLR 相比做了哪些改进?
在预训练阶段我们使用无标签的数据集 (unlabeled data),因为有标签的数据集它很贵啊,打标签得要多少人工劳力去标注,那成本是相当高的,所以这玩意太贵。相反,无标签的数据集网上随便到处爬,它便宜。在训练模型参数的时候,我们不追求把这个参数用带标签数据从初始化的一张白纸给一步训练到位,原因就是数据集太贵。于是 Self-Supervised Learning 就想先把参数从 一张白纸 训练到 初步成型,再从 初步成型 训练到 完全成型。注意这是2个阶段。所以预训练模型的时候,就是模型参数从 一张白纸 到 初步成型 的这个过程,还是用无标签数据集。等我把模型参数训练个八九不离十,这时候再根据你 下游任务 (Downstream Tasks) 的不同去用带标签的数据集把参数训练到 完全成型,那这时用的数据集量就不用太多了,因为参数经过了第1阶段就已经训练得差不多了。
第1个阶段不涉及任何下游任务,就是拿着一堆无标签的数据去预训练,没有特定的任务,这个话用官方语言表达叫做:in a task-agnostic way。第2个阶段涉及下游任务,就是拿着一堆带标签的数据去在下游任务上Fine-tune,这个话用官方语言表达叫做:in a task-specific way。
Hinton老爷子这个组的第 1 个版本 SimCLR 好像2月份才刚发布不久,在 Hinton 老爷子的带领下,没过几个月立马升级到了 SimCLR v2。v2 相比 v1 将 SOTA 结果提升了大约 22个点左右,这波操作真稳。
SimCLR v2 和 SimCLR 相比做了哪些改进?
- SimCLR v2 的第 1 个发现是:在使用无标签数据集做 Pre-train 的这一步中,模型的尺寸很重要,用 deep and wide 的模型可以帮助提升性能。
- SimCLR v2 的第 2 个发现是:使用无标签数据集做 Pre-train 完以后,现在要拿着有标签的数据集 Fine-tune 了。之后再把这个 deep and wide 的模型 蒸馏成一个更小的网络。
所以,SimCLR v2 的方法,用8个英文单词概括一下就是:
Unsupervised Pre-train, Supervised Fine-tune,Distillation Using Unlabeled Data.
所以 SimCLR v2 论文里面给出了3个论点:
- 对于半监督学习来讲,在标签量极少的情况下,模型越大,获益就越多。这很不符合直觉,常识是标签这么少了,模型变大会过拟合。
- 即使模型越大能够学到越 general 的 representations,但是这是在不涉及下游任务的task-agnostic 的情况下。一旦确定了下游任务,就不再需要大模型了,可以蒸馏成一个小模型。
- Projection head 很重要,更深的 Projection head 可以学习到更好的representation,在下游任务做 Fine-tune 之后也更好。
这3个论点就是 SimCLR v2 的贡献。那 SimCLR v2 取得的效果如何呢?我们对标一下 SimCLR:
性能:
SimCLR (4×) 这个模型可以在 ImageNet 上面达到 76.5% 的 Top 1 Accuracy,比当时的 SOTA 模型高了7个点。如果把这个预训练模型用 1%的ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,SimCLR 就可以再达到 85.5% 的 Top 5 Accuracy,也就是再涨10个点。
注意这个 76.5% 是通过 Linear Evaluation 得到的, Linear Evaluation 具体是咋做的请参考上面的 FAQ1。这个 85.5% 是通过 Fine-tune 得到的, Fine-tune 具体是咋做的请参考上面的 FAQ1。
SimCLR v2 这个模型可以在 ImageNet 上面达到 79.8% 的 Top 1 Accuracy,比当时的 SOTA 模型 SimCLR 高了 4.3 个点。如果把这个预训练模型用 1%或10% 的 ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,SimCLR v2 就可以再达到 76.6%或80.9% 的 Top 1 Accuracy。如果再蒸馏一下,迁移到更小的ResNet-50 上面,SimCLR v2 小模型就可以再达到 73.9%或77.5% 的 Top 1 Accuracy。注意 ResNet-50 有监督学习的Accuracy是76.6%。所以 SimCLR v2 在很多时候超越了有监督学习。
- SimCLR v2 具体步骤:
SimCLR v2的具体步骤如下图所示,这个图是论文里面的图,

我自己画了个更细节的图如图所示。SimCLR v2的具体步骤可以分为以下3步:

- Unsupervised Pre-train:使用无标签数据以一种 Task-agnostic 的方式预训练Encoder,得到比较 general 的 Representations。
SimCLR v2 的这一步和 SimCLR 是一模一样的,如上图所示,这里再简要概述一下:
首先对每张图片先做数据增强,每张图片我们得到2个不同的数据增强结果,假设Batch size大小为2,则1个Batch 一共有 4 个 Image。假设现在有1张任意的图片 \(x\),叫做Original Image,先对它做数据增强,得到2张增强以后的图片 \(x_i, x_j\) 。注意数据增强的方式还是以下3种:
- 随机裁剪之后再resize成原来的大小 (Random cropping followed by resize back to the original size)。
- 随机色彩失真 (Random color distortions)。
- 随机高斯模糊 (Random Gaussian Deblur)。
接下来把增强后的图片 \(x_i, x_j\) 输入到Encoder里面,注意这2个Encoder是共享参数的,得到representation \(h_i, h_j\) ,再把 \(h_i, h_j\) 继续通过 Projection head (在 SimCLR v2 里面是3层的MLP) 得到 representation \(z_i, z_j\) ,这里的2个 Projection head 依旧是共享参数的,且其具体的结构表达式是:
接下来的目标就是最大化同一张图片得到的 \(z_i, z_j\) 。怎么最大化呢?使用Cosine Similarity把计算两张 Augmented Images \(x_i, x_j\) 的相似度转化成了计算两个Projected Representation \(z_i, z_j\) 的相似度,定义为:
式中,\(\tau\) 是可调节的Temperature 参数。它能够scale 输入并扩展余弦相似度[-1, 1]这个范围。 现在我们有了衡量相似度的办法,但是这还不够,要最终转化成一个能够优化的 Loss Function 才可以。SimCLR用了一种叫做 NT-Xent loss (Normalized Temperature-Scaled Cross-Entropy Loss)的对比学习损失函数。希望上面的softmax的结果尽量大,所以损失函数取了softmax的负对数:
最后,计算每个Batch里面的所有Pair的损失之和取平均:
去优化这个损失函数 \(L\) 即可。
SimCLR v2 也是按照上面的步骤,有哪些不同呢?
- Encoder 变长变大:SimCLR v2 用了更大的ResNet架构,把原来的 ResNet-50 (4×) 拓展成了 ResNet-152 (3×) 和 selective kernels (SK),记为 ResNet-152 (3×+SK),变成这样以后,把这个预训练模型用 1%的 ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,获得了29个点的提升。
- Projection head 变深:使用了更深的\(g(.)\)。原来的结构如图5所示是2个FC层+一个激活函数构成。现在的 \(g(.)\)是3个FC层,并且在Fine-tune的时候要从第1层开始。变成这样以后,把这个预训练模型用 1%的 ImageNet的标签给 Fine-tune 一下,借助这一点点的有监督信息,获得了14个点的提升。
- 加入了MoCo 的内存机制:因为 SimCLR 本身就能通过数据增强得到很多的负样本,所以说这步只获得了1个点的提升。
- Supervised Fine-tune:使用有标签数据以一种 Task-specific 的方式 Fine-tune Encoder。
在 SimCLR 中,Projection head 在预训练完以后会被扔掉,不做Fine-tune,只保留Encoder f(.) 加一个FC层去做Fine-tune。
而在 SimCLR v2 中,Projection head 在预训练完以后不会被完全扔掉,而是扔掉一半,保留一半做Fine-tune。注意如果只保留一层,那就和 SimCLR 加一个FC层的做法实质上一样了。
- Distillation Using Unlabeled Data:使用无标签数据以一种 Task-specific 的方式蒸馏 Encoder,得到更小的Encoder。
作者把 Fine-tune 之后的网络作为了 Teacher,去蒸馏一个更小的 Student 网络。
损失函数是:
式中\(P(y|x_i)=exp(f^{task}(x_i)[y]/\tau)/\sum_{y^{'}}exp(f^{task}(x_i)[y^{'}]/\tau)\) , \(\tau\) 是可调节的Temperature 参数。它能够scale 输入并扩展余弦相似度[-1, 1]这个范围。 \(f^{task}(x_i)[y]\) 是输入图片 \(x_i\) 网络输出和 \(y\) 的相似度,这样 \(P(y|x_i)\) 就代表了输入图片 \(x_i\) 网络输出和 \(y\) 的相似的概率值。我们希望 Teacher 的这个概率 \(P^T(y|x_i;\tau)\) 和 Student 的这个概率 \(P^S(y|x_i;\tau)\) 越接近越好,如下图所示。

当我们也有一些label的时候,也可以添加一项有监督的损失:
实验
略
总结
之前的使用自监督获得representations需要特定的算法。大牛上来就说之前的方法多数属于生成方法或者判别方法。告诉我们contrastive learning才是目前的宠儿。过去一年来,相继有CPC, CMC, MoCo 出来,想要解决的共同一个问题就是,如何提高softmax中negatives的数量。其中 CPC 用了patch based方法,CMC 用了一个memory buffer,MoCo 用了momentum update去keep一个negative sample queue。这篇文章告诉大家,只要机子够多,batch size够大,每个batch中除了positive以外的都当negatives就已经足够了。
Reference
无监督表示学习(三):2020 Simple Contrastive Learning of Visual Representations(SimCLR)