对比学习(Contrastive Learning)中的损失函数

写在前面

  最近在基于对比学习做实验,github有许多实现,虽然直接套用即可,但是细看之下,损失函数部分甚是疑惑,故学习并记录于此。关于对比学习的内容网络上已经有很多内容了,因此不再赘述。本文重在对InfoNCE的两种实现方式的记录。

一、Info Noise-contrastive estimation(Info NCE)

1.1 描述

  InfoNCE在MoCo中被描述为:
L q = − log ⁡ exp ⁡ ( q ⋅ k + / τ ) ∑ i = 0 K exp ⁡ ( q ⋅ k i / τ ) (1) \mathcal{L}_{q}=-\log \frac{\exp \left(q \cdot k_{+} / \tau\right)}{\sum_{i=0}^{K} \exp \left(q \cdot k_{i} / \tau\right)} \tag{1} Lq=logi=0Kexp(qki/τ)exp(qk+/τ)(1)
其中 τ \tau τ是超参。

  • 分子表示: q q q k + k_+ k+点积。所谓点积就是描述 q q q k + k_+ k+两个向量之间的距离。
  • 分母表示: q q q所有 k k k的点积。所谓所有就是指正例(positive sample)和负例(negative sample),所以求和号是从 i = 0 i=0 i=0 K K K,一共 K + 1 K+1 K+1项。

1.2 实现

  MoCo源码\moco\builder.py中,实现如下:

	# compute logits
	# Einstein sum is more intuitive
	# positive logits: Nx1
	l_pos = torch.einsum('nc,nc->n', [q, k]).unsqueeze(-1)
	# negative logits: NxK
	l_neg = torch.einsum('nc,ck->nk', [q, self.queue.clone().detach()])
	
	# logits: Nx(1+K)
	logits = torch.cat([l_pos, l_neg], dim=1)
	
	# apply temperature
	logits /= self.T
	
	# labels: positive key indicators
	labels = torch.zeros(logits.shape[0], dtype=torch.long).cuda()
	...
	return logits, labels

这里的变量logits的意义我也查了一下:是未进入softmax的概率

这段代码根据注释即可理解:l_pos表示正样本的得分,l_neg表示所有负样本的得分,logits表示将正样本和负样本在列上cat起来之后的值。值得关注的是,labels的数值,是根据logits.shape[0]的大小生成的一组zero。也就是大小为batch_size的一组0。

  接下来看损失函数部分,\main_moco.py

	# define loss function (criterion) and optimizer
	criterion = nn.CrossEntropyLoss().cuda(args.gpu)
	...
	# compute output
	output, target = model(im_q=images[0], im_k=images[1])
	loss = criterion(output, target)

这里直接对输出的logits和生成的labels计算交叉熵,然后就是模型的loss。这里就是让我不是很理解的地方。先将疑惑埋在心里~

二、HCL

2.1 描述

  在文章《Contrastive Learning with Hard Negative Samples》中描述到,使用负样本的损失函数为:
E x ∼ p , x + ∼ p x + [ − log ⁡ e f ( x ) T f ( x + ) e f ( x ) T f ( x + ) + Q N ∑ i = 1 N e f ( x ) T f ( x i − ) ] (2) \mathbb{E}_{x \sim p, x^{+} \sim p_{x}^{+}}\left[-\log \frac{e^{f(x)^{T} f\left(x^{+}\right)}}{e^{f(x)^{T} f\left(x^{+}\right)}+\frac{Q}{N} \sum_{i=1}^{N} e^{f(x)^{T} f\left(x_{i}^{-}\right)}}\right] \tag{2} Exp,x+px+[logef(x)Tf(x+)+NQi=1Nef(x)Tf(xi)ef(x)Tf(x+)](2)

  • 分子: e f ( x ) T f ( x + ) e^{f(x)^{T} f(x^{+})} ef(x)Tf(x+)表示学到的表示 f ( x ) f(x) f(x)和正样本 f ( x + ) f(x^+) f(x+)的点积。(其实也就是正样本的得分)
  • 分母:第一项表示正样本的得分,第二项表示负样本的得分。

其实本质上适合InfoNCE一个道理,都是mean(-log(正样本的得分/所有样本的得分))

2.2 实现

  但是在这篇文章的实现中,\image\main.py

def criterion(out_1,out_2,tau_plus,batch_size,beta, estimator):
	# neg score
	out = torch.cat([out_1, out_2], dim=0)
	neg = torch.exp(torch.mm(out, out.t().contiguous()) / temperature)
	old_neg = neg.clone()
	mask = get_negative_mask(batch_size).to(device)
	neg = neg.masked_select(mask).view(2 * batch_size, -1)
	
	# pos score
	pos = torch.exp(torch.sum(out_1 * out_2, dim=-1) / temperature)
	pos = torch.cat([pos, pos], dim=0)
	
	# negative samples similarity scoring
	if estimator=='hard':
	    N = batch_size * 2 - 2
	    imp = (beta* neg.log()).exp()
	    reweight_neg = (imp*neg).sum(dim = -1) / imp.mean(dim = -1)
	    Ng = (-tau_plus * N * pos + reweight_neg) / (1 - tau_plus)
	    # constrain (optional)
	    Ng = torch.clamp(Ng, min = N * np.e**(-1 / temperature))
	elif estimator=='easy':
	    Ng = neg.sum(dim=-1)
	else:
	    raise Exception('Invalid estimator selected. Please use any of [hard, easy]')
	    
	# contrastive loss
	loss = (- torch.log(pos / (pos + Ng) )).mean()
	
	return loss

可以看到最后计算loss的公式是:

	loss = (- torch.log(pos / (pos + Ng) )).mean()

的确与我上文中的理解相同,可是为什么这样的实现,没有用到全0的label呢?

三、文字解释

  既然是同一种方法的两种实现,已经理解了第二种实现(HCL)。那么,问题就出在了:不理解第一种实现的label为何要这样生成? 于是乎,查看交叉熵的计算方式:
loss ( x , c l a s s ) = − log ⁡ ( exp ⁡ ( x [ c l a s s ] ) ∑ j exp ⁡ ( x [ j ] ) ) = − x [ c l a s s ] + log ⁡ ( ∑ j exp ⁡ ( x [ j ] ) ) (3) \text{loss}(x, class) = -\log\left(\frac{\exp(x[class])}{\sum_j \exp(x[j])}\right)= -x[class] + \log\left(\sum_j \exp(x[j])\right) \tag{3} loss(x,class)=log(jexp(x[j])exp(x[class]))=x[class]+log(jexp(x[j]))(3)

交叉熵的label的作用是:将label作为索引,来取得 x x x中的项( x [ c l a s s ] x[class] x[class]),因此,这些项就是label。而倘若label是全0的项,那么其含义为: x x x中的第一列为label(正样本),其他列就是负样本。然后带入公式(3)中计算,即可得到交叉熵下的loss值。

  而对于HCL的实现方式,是直接将InfoNCE拆解开来,使用正样本的得分和负样本的得分来计算。

四、代码解释

  首先,生成pos得分和neg的得分:
得分
注意,这里省略了生成的特征,直接生成了得分,

4.1 Info NCE

在这里插入图片描述

4.2 HCL

HCL loss
嗒哒~两者的结果“一模一样”(取值范围导致最后一位不太一样)

  • 79
    点赞
  • 247
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
自监督对比学习是一种无监督学习方法,旨在通过将数据样本与其在相同任务下的变体进行比较来进行特征学习。其核心思想是将一个样本与自身的不同变体进行对比,以推动特征的区分度增加。 在自监督对比学习,通常使用一种转换函数对输入样本进行变换,生成多个变体。这些变换可以是图像旋转、裁剪、亮度调整等,也可以是对文本数据进行掩码、重排等操作。对于每个输入样本及其变体,模型将利用一个对比损失函数来度量它们之间的相似性。 通过自监督对比学习,模型会学习到一组鲁棒的特征表示。这些特征不仅能够区分同一样本与其变体,还能够区分不同样本之间的差异。通过不同样本之间的对比学习,模型可以学习到更加丰富的语义信息,提高数据的表征能力。 自监督对比学习在计算机视觉和自然语言处理等领域得到了广泛的应用。例如,在图像领域,可以利用自监督对比学习来学习图像的局部特征、形状和纹理等信息。而在自然语言处理领域,可以通过对文本进行掩码、重排等方式来进行自监督对比学习,以学习词语、句子和文档的语义表示。 自监督对比学习的窥探给了我们一个更好的方式,通过无监督学习方法来解决许多现实世界的问题。它为我们提供了一种从大规模数据学习有用表示的方式,提高了学习算法的效率和泛化性能。通过进一步的研究和发展,自监督对比学习注定将在更多的领域发挥重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值