创见博客
对比学习方法的迭代史
七崽爱吃小饼干2025/03/25阅读 15专栏 深度学习

1.三元组学习(Triplet)

三元组学习一般是基于有监督学习的,一般通过标签把数据分为:

  • 锚样本(Anchor):查询的实例
  • 正样本(Positive):和查询实例相同的对象
  • 负样本(Negative):和查询实例不相同的对象

一般用三元组损失作为损失函数:

L=max(d(a,p)−d(a,n)+margin,0)L=max(d(a,p)-d(a,n) + margin,0)L=max(d(a,p)−d(a,n)+margin,0)

通过这个损失函数调整模型,让正样本和锚样本的特征尽量接近,让负样本和锚样本尽量远。

从而实现相似目标的鉴别。

2.对比学习

对比学习一般是基于无监督学习的,其正样本是通过对锚样本做数据增强得到的。

其比较过程和三元组损失相似,也是通过让锚样本和正样本的特征距离接近,让负样本和锚样本尽量远实现的。

只不过对比学习采用的损失函数为InfoNCE损失。

InfoNCE 损失公式为:

LInfoNCE=E[−logexp(q⋅k+/τ)∑i=0Kexp(q⋅ki/τ)]\mathcal{L}_{InfoNCE}=\mathbb{E}\left[-log\frac{exp\left(q\cdot k_{+}/\tau\right)}{\sum_{i = 0}^{K}exp\left(q\cdot k_{i}/\tau\right)}\right]LInfoNCE​=E[−log∑i=0K​exp(q⋅ki​/τ)exp(q⋅k+​/τ)​]

  • E\mathbb{E}E :是期望符号,即加权平均操作,1N∑n=1N\frac{1}{N}\sum_{n=1}^{N}N1​∑n=1N​

  • exp()exp()exp():是指数函数的意思,即e()e^{()}e()

  • 其中的分数部分就是采用了softmax函数,将特征的分布情况变成概率,其结果就是正样本对和锚点样本相似的概率。

  • −log()-log()−log():−log(p)-log(p)−log(p)是一个单调递减函数,是正样本p的概率越大,其损失越小。相反,是正样本的概率越小,其损失就越大。用来计算损失,从而引导模型学习到正样本和负样本之间的区分。

  • 温度系数:

存在的问题:

因为每次都是在一个batch上进行比较,观察的非常局部。就比如第一个batch中的样本大多数是鸟,进过对比、反向传播以后调整模型,更适用于鸟。而第二个batch可能变成了狗,之前的模型显然不会太适用。每次更新参数都会有很大的变化,会造成震荡的情况。

如果想要比较好的效果,就需要batch尽量的大,让模型能一次从更大的样本数据中去比较学习,这样就非常大吃显存。

3.三种对比学习方法

下面是MoCo论文当中提到的三种对比学习方法:

1.end-to-end

xqx^qxq表示查询的样本,xkx^kxk则是其正样本和负样本。encoder qencoder\ qencoder q和encoder kencoder\ kencoder k是两个模型,分别用来对xqx^qxq和xkx^kxk提取特征。qqq和kkk即为经过提取以后的特征。

end-to-end方法十分简单粗暴,模型需要对所有的负样本进行特征提取和比较,然后反向传播以后分别更新encoder qencoder\ qencoder q和encoder kencoder\ kencoder k。

这样做能最大化batch,让其能够从全局去感受q样本和k样本之间的区别。但是这样超级吃显存,batch规模稍微大一点就直接爆显存了。

2.memory bank

第二种方法名为memory bank其目的在于解决前面提到的batch过大导致显存不足的问题。

基本过程:

选取一批样本通过encoder k提取出特征以后,放到内存上。然后通过抽样,得到特征集合k,最后和q进行比较。通过反向传播分别更新encoder q和encoder k,然后新的一批样本通过新的encoder k提取特征,放到memory bank当中,再从memory bank当中抽样。

这样一来,提取特征的时候,还是对一定大小的batch进行,就不会爆显存了。另一方面,因为减轻了显存压力,memory bank当中存了很多的样本特征,就可以一次抽取更多的样本特征进行比较。

memory bank的更新:

memory bank的容量通常是有上限的,当达到上限时,通常采用基于队列的更新方法。在这种方法中,Memory Bank 通常被视为一个队列。当新的特征向量进入时,将其添加到队列的一端,而队列的另一端则移除最旧的特征向量。例如,如果 Memory Bank 的队列长度设定为 N,当有新的特征向量进来时,就把第 N + 1 个旧的特征向量移除。这样可以保证 Memory Bank 中始终包含最新的 N 个样本的特征,并且有一定的顺序和容量控制。

这种方式可以有效地管理 Memory Bank 的容量,保证其中的特征具有一定的时效性,同时避免 Memory Bank 无限制地增长。

存在的问题:

memory bank虽然解决了显存容量不足的问题,但是又带来了新的问题。

memory bank当中存储着不同训练阶段的模型提取的样本特征,虽然这样节约了显存空间,可以一次抽取更广泛的样本进行比较学习,但是同时用旧的样本特征和新的样本特征学习会影响模型学习的精度,我们将这个问题称之为不一致问题。

3.MoCo

MoCo方法的提出目的就是在于解决memory bank方法出现的不一致问题。其主要的想法就是,让encoder k的更新慢一点,让新提取出来的样本的特征和旧的样本特征不至于差异过大,从而减小不一致问题带来的影响。

简介:

论文地址

英文名称为:Momentum Contrast for Unsupervised Visual Representation Learning,翻译过来叫基于动量对比的无监督视觉表示学习。

基本过程:

MoCo的基本过程和memory bank一致,其主要的区别在于MoCo的momentum encoder(也就是encoder k)的参数更新是基于一种名为动量更新的方法,基于自身的参数θkt−1\theta_k^{t-1}θkt−1​和encoder q的更新后的参数θqt\theta_q^tθqt​进行更新。

动量更新:

动量更新(Momentum Update)是一种在优化算法中使用的技术,它的基本思想是在更新参数时,不仅考虑当前的梯度信息,还考虑之前的更新方向,类似于物理学中的动量概念。通过积累之前的更新 “动量”,可以加速收敛并且使更新过程更加稳定。

一般的梯度下降公式为:

θt+1=θt−αΔJ(θt)\theta_{t+1} = \theta_t - \alpha \Delta J(\theta_t)θt+1​=θt​−αΔJ(θt​)

其中θt\theta_{t}θt​是在ttt时刻的参数,α\alphaα是学习率,∇J(θt)\nabla J(\theta_{t})∇J(θt​)是损失函数JJJ关于参数θt\theta_{t}θt​的梯度。

动量更新一般公式为:

而在动量更新中,引入一个动量变量vvv,更新公式变为vt+1=βvt−α∇J(θt)v_{t + 1}=\beta v_{t}-\alpha\nabla J(\theta_{t})vt+1​=βvt​−α∇J(θt​)和θt+1=θt+vt+1\theta_{t + 1}=\theta_{t}+v_{t + 1}θt+1​=θt​+vt+1​,其中β\betaβ是动量系数,取值范围通常在000到111之间。vtv_{t}vt​可以看作是积累的动量,它综合了之前的梯度信息,使得参数更新方向具有一定的惯性。

联立以后就可以得到:

θt+1=θt+βvt−αΔJ(θt)\theta_{t+1} = \theta_t + \beta v_t - \alpha \Delta J(\theta_t)θt+1​=θt​+βvt​−αΔJ(θt​)

继续推导:

θt+1=θt+β(βvt−1−αΔJ(θt−1))−αΔJ(θt)=θt+β(β(β(...(β(v0)−αΔJ(θ0)))−αΔJ(θt−2))−αΔJ(θt−1))−αΔJ(θt)\theta_{t+1} = \theta_t + \beta(\beta v_{t-1}-\alpha \Delta J(\theta_{t-1})) - \alpha \Delta J(\theta_t) = \theta_t + \beta(\beta(\beta(...(\beta(v_0)-\alpha \Delta J(\theta_0))) - \alpha \Delta J(\theta_{t-2}))-\alpha \Delta J(\theta_{t-1})) - \alpha \Delta J(\theta_t)θt+1​=θt​+β(βvt−1​−αΔJ(θt−1​))−αΔJ(θt​)=θt​+β(β(β(...(β(v0​)−αΔJ(θ0​)))−αΔJ(θt−2​))−αΔJ(θt−1​))−αΔJ(θt​)

v0v_0v0​通常被初始化为0。

股票的例子:

在股市当中,股票每天都在更新,且波动可能很大。如果我们想绘制一条股市走向的曲线,直接套用每天的价格显然不太适用。

这里就可以利用当天的股价和之前的股票走向一起绘制波动比较小,但是又能反映股票整体走向的曲线。

EMAt=αxt+(1−α)EMAt−1EMA_t = \alpha x_t+(1 - \alpha)EMA_{t - 1}EMAt​=αxt​+(1−α)EMAt−1​

其中α\alphaα是平滑系数(也称为衰减因子),取值范围在000到111之间。EMAt−1EMA_{t - 1}EMAt−1​是t−1t - 1t−1时刻曲线的取值(移动平均指数:moving averaged weight)。当α\alphaα越接近111时,模型对近期数据的权重越高,曲线越贴近最新的数据点;当α\alphaα越接近000时,历史数据的权重相对更高,曲线更加平滑。

momentum encoder的更新:

其更新公式和股票更新的公式更接近:

θkt+1=αθkt−(1−α)θqt+1\theta_k^{t+1} = \alpha\theta_k^t - (1-\alpha)\theta_q^{t+1}θkt+1​=αθkt​−(1−α)θqt+1​

query encoder和momentum encoder之间的关系就像图中的股票数据和光滑的走向曲线。query encoder以比较大的波动幅度去更新自己,学习提取有用的特征。而momentum encoder则是从query encoder身上比较保守的学习,保证自己的学习方向不出大问题。

在学习初期,momentum encoder因为变化幅度小,所以提取出来的样本特征和旧的样本特征差距比较小,减小了一致性问题带来的波动。

随着学习迭代,query encoder会逐渐收敛,而向其学习的momentum encoder也会逐渐收敛。放在memory bank当中的样本特征之间的差异也会越来越小。这保证了学习的有效性和稳定性。

MoCo伪代码解读:

4.三种方法的表现

下面的实验结果摘选自MoCo论文。纵轴为精确率。横轴为batch规模,采用了对数尺度,采用了log2()log_2()log2​()的变换。

我们可以看到end-to-end方法的精确度很高,但是随着规模的增大,在batch为1024的时候无法继续进行了,最终精确率只能停留在57.3%。

memory bank方法随着规模的上升,精确率也在上涨,最后大概收敛在58%。受到不一致问题影响,精确率比较低。

MoCo方法的精确率比较高,最后收敛在了大概60.6%。

评论
0/100