对比学习介绍

2,187 阅读7分钟

对比学习简介

前言

对比学习属于无监督学习中的自监督学习,所以它是没有label的,而在传统的监督学习中,我们往往使用label和预测值进行损失函数构建,进而对模型进行优化,而在自监督学习中,我们采取代理任务(proxy task)来后天构建label,进而可以像监督学习一样构建损失函数,我们最终的目的不是代理任务,而是利用代理任务学到的神经网络作为特征提取器,将他作用在下游的任务中,是他只需要少量的标注或微调就能得到较好的效果。所以,我们相信代理任务学到特征能泛化到下游任务中。

而在对比学习中,最常用的代理任务就是实例判别,即将所有的样本都判断为独立的类别,我们要学到的就是能判别是否属于同一类别的特征,尽管这些样本本身并无标签、可以被视为彼此独立的类别,但这恰恰无法训练出能够泛化的语义特征,因此我们通过数据增强人为构造“正样本对”,将该任务转化为一个“判断样本对是否来自同一语义类别”的分类任务。来让模型学到能泛化的类别判别特征。在实例判别的代理任务中,我们认为该任务构建了一种类别的label,将任务转换为了多分类问题。就能构建损失函数了。

流程

数据增强

前面我们提到需要构建正样本,而对比学习一般用在cv领域,所以这里提到的是图片的数据增强。

对每个样本,我们都进行数据增强(裁切,翻转,颜色扰动,模糊等),生成两个(只用两个,大多数方法只要一个正样本,要多个正样本的也不是通过这里获得的)不同视图,将它们作为一个正样本对,将其他样本作为负样本

特征提取(编码器Encoder)

为什么要特征提取,因为我们要学到的是类别判别的特征来作用于下游任务,并且我们要进行对比,也需要用特征来对比。我们使用ResNet、transformer等方式进行特征提取,我们后续用损失函数优化的也是这里的超参数。有时还会使用投影头(Projection Head),例如一个 MLP,将表示进一步映射到一个对比空间。

构建对比损失

  1. 计算正样本之间的相似度,想要他们靠近
  2. 计算负样本之间的相似度,想要他们远离
  3. 常见的损失函数有NT-Xent(Normalized Temperature-scaled Cross Entropy),InfoNCE,Triplet Loss,NCE

模型优化

  • 通过梯度下降等优化方法最小化对比损失;

  • 编码器学习到的特征表示能捕捉样本间的语义相似性。

表示使用或下游任务评估

训练完后,去除投影头,仅保留编码器,就可以作为提取特征的部分作用于下游任务

不同的负样本方式

大 Batch 构造(end-to-end)

代表SimCLR

该方式使用的负样本为同一批次的样本构建而来,不使用其他批次样本。这种方式的效果极度依赖于一批次的样本数,适用于有TPU的情况下,可以采用成百上千的批次样本数。能生成一个很大的、能更新的负样本库。这样的优势是他能够在每次反向传播更新encoder超参数后更新负样本,但这种方式依赖大内存,不然无法存太多负样本。

memory bank

静态

代表Inst Disc

在Inst Disc(最早期对比学习论文提出的方式)中,使用的是一个静态的memory bank,初始化时直接用编码器训练的初始特征。他的更新只在样本前向传播并计算损失与梯度后更新。而且这个表是全局表,会存储所有。但他的更新就会很少,所以说他是静态的。

动量编码器更新的负样本队列

代表MoCo

在MoCo中,使用了一种动量编码器更新的memory bank,他存储在一个更新的队列中。相较于 InstDisc 中静态、长时间未更新的 memory bank,MoCo 使用动量编码器搭配有限长度队列动态更新负样本,更能保持负样本的时效性与稳定性。

什么是动量更新

在一些对比学习的方式中,正样本的编码器和负样本的编码器是共用参数的,也就是说他们实际上是一样的。但是这种不断直接更新的编码器会对负样本的memory bank造成影响。因为不同参数的Encoder获得的负样本差别太大。所以MoCo采取了一种动量的更新方式,即正负样本的编码器用两套参数(正:Query encoder,负:Key encoder)其中query encoder是采用传统的反向传播更新方式,而key encoder是采用的动量的更新,是怎样动量的呢,可以看看他的公式:

θkmθk+(1m)θq\theta_k \leftarrow m \cdot \theta_k + (1 - m) \cdot \theta_q

该公式代表了 Key Encoder 权重以一定比例向 Query Encoder 权重靠拢,从而保持稳定的特征表示。其中k和q正是我们的两个编码器,m是一个可调的参数,它决定了这个编码器更新的程度。在MoCo论文中,发现m在0.99和0.999等很大的参数时效果最好。

为什么用更新队列

就像之前说的不同参数的Encoder获得的负样本差别很大,特别是key encoder更新很多轮后,如果我们还是像字典一样将特别久之前的还没更新的负样本存起来,不仅效果不好,还会占内存。所以我们用一个有限的队列存储。新的负样本进入队列,同时队列满时会将老的负样本弹出队列。这样就能保证负样本的动态更新,同时节约内存。

不使用负样本(无负样本对比)

代表BYOL、SimSiam

这是后期的对比学习采用的方式。它完全不使用负样本,而是通过正样本自对齐来学习特征。能避免复杂的负样本设计。但本文不讲这个(或许是我也没学😭)

上图是inst disc中生成负样本的流程

上图是MoCo论文中对几种方式的介绍

损失函数

在对比学习中有多种损失函数,本文讲其中的两个,NCE,和InfoNCE

NCE

下面的 n(i)\P_n(i)是人为可添加的采样的噪声分布,如均匀分布、正态分布等。(iV)\P(i|V)是真实分布下样本i的后验概率。参照上面的MoCo的图可以看出,就是指从memory bank中按这种分布抽取负样本进行对比

NCE

下面这张图是NCE的对数似然损失函数,其实就类似于sigmoid的对数似然损失函数。第一项是最大化真实样本被判为“真”的概率

第二项是最小化噪声样本被误判为“真”的概率,乘上 m 表示噪声采样数量。

loss-NCE

InfoNCE

LInfoNCE=logexp(sim(zi,zi+)/τ)j=1Nexp(sim(zi,zj)/τ)\mathcal{L}_{\text{InfoNCE}} = -\log \frac{\exp\left(\text{sim}(\mathbf{z}_i, \mathbf{z}_i^+)/\tau\right)}{\sum\limits_{j=1}^{N} \exp\left(\text{sim}(\mathbf{z}_i, \mathbf{z}_j)/\tau\right)}

(zi,zi+)\sim(z_i,z_i^+)表示相似度,常用余弦相似度或向量点积(这两个实际上只有数值上的差异,初高中数学就有讲),它的作用是最大化查询与正样本的相似度,最小化与负样本的相似度。类似于softmax。

结语

本文只是对对比学习进行了简单的介绍。对比学习从早期探索到18年Inst DIsc论文发表进入快速发展时期,涌现了大量优秀和别具匠心的论文,他们使用巧妙的方式将没有标签的困难克服,证明了Yann LeCun的蛋糕假说(AI的三层结构中,感知(perception)是蛋糕主体,代表的是通过自监督学习获取的表征;监督学习只是蛋糕上的糖霜。),人们逐渐发现了无监督学习领域具有的优秀潜力,而对比学习领域正是其中的佼佼者。学习它的路径是坎坷并漫长的,但正如“路漫漫其修远兮,吾将上下而求索”,怀揣着一颗求知的心,总会抵达前方的。