0
点赞
收藏
分享

微信扫一扫

SimCSE:NLP中的对比学习

论文简介

论文链接:SimCSE: Simple Contrastive Learning of Sentence Embeddings

如果大家了解对比学习的话就好办了,这篇文章就是将对比学习应用到了自然语言处理领域。起初对学习先是用在图像领域的。如果你了解的话就可以继续往下看,如果你不了解的话我建议是先了解一下对比学习。
另外推荐几篇我写的对比学习的文章。

  1. 诸神黄昏时代的对比学习
  2. “军备竞赛”时期的对比学习好。

无监督获取句子向量:

  • 使用预训练好的 Bert 直接获得句子向量,可以是 CLS 位的向量,也可以是不同 token 向量的平均值。

  • Bert-flow^[On the Sentence Embeddings from Pre-trained Language Models],主要是利用流模型校正 Bert 的向量。

  • Bert-whitening^[Whitening Sentence Representations for Better Semantics and Faster Retrieval],用预训练 Bert 获得所有句子的向量,得到句子向量矩阵,然后通过一个线性变换把句子向量矩阵变为一个均值 0,协方差矩阵为单位阵的矩阵。

有监督的方式主要是:

  • Sentence-Bert (SBERT)^[Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks],通过 Bert 的孪生网络获得两个句子的向量,进行有监督学习,SBERT 的结构如下图所示。

对于对比学习来说,最重要是如何构造正负样本。

在图像中有多种构造对比学习的样本,比SimCLR中提到的:反转、局部裁剪、局部显出、裁剪翻转、调整饱和度、调整颜色、使用各种滤波器比如最大值滤波器,最小值滤波器、锐化滤波器。

image.png

在自然语言处理中也有很多的数据增广方式,但是他们对句子的影响都特别大。会严重降低对比学习的效果。为了解决这个问题SimCSE模型提出了一种通过随机采样dropout mask的操作来构造正样本的方法。模型使用的是BERT,每次出来的Dropout是不同的。随机dropout masks机制存在于模型的fully-connected layers和attention probabilities上,因此相同的输入,经过模型后会得到不同的结果。所以只需要将同一个句子两次喂给模型就可以得到两个不同的表示。使用这种方法产生出来的相似样本对语义完全一致,只是生成的embedding不同而已,可以认为是数据增强的最小形式。比其他的数据增强方法都要好很多。

无监督

image.png
无监督的目标函数是这样的。看一下上边,他图中示例是把三个句子作为输入传给编码器,然后编码器会得到对应句子的embedding。输入两次会得到两次不同的embedding。一个句子和它对应增强的句子是正样本,其余的句子作为负样本。最终使用的损失函数如下:
$$
\ell{i}=-\log \frac{e^{\operatorname{sim}\left(\mathbf{h}{i}^{z{i}}, \mathbf{h}{i}^{z{i}^{\prime}}\right) / \tau}}{\sum{j=1}^{N} e^{\operatorname{sim}\left(\mathbf{h}{i}^{z{i}}, \mathbf{h}{j}^{z{j}^{\prime}}\right) / \tau}}
$$

有监督

image.png

使用有监督学习的一个难点,就是要找到适合构造正负样本的数据集。最终作者的选择如下:
image.png

那它的正负利是如何构造的呢。
以其中的NLI数据集为例,在这个数据集中打进一个前提。就是注释者需要手动编写一个绝对正确的句子及蕴句子。一个可能正确的句子,中立句子。和一个绝对错误的句子矛盾句子。然后这篇论文就将这个数据集进行扩展,将原来的(句子,蕴含句子)改变为(句子,蕴含句子,矛盾句子)。在这个数据集中正样本是这个句子及其包含蕴含关系的句子。负样本有两种,是这个句子包含矛盾关系的句子以及其他的句子。
损失函数如下:
$$
L{i}=-\log \frac{e^{\operatorname{sim}\left(h{i}, h{i}^{+}\right) / \tau}}{\sum{j=1}^{N}\left(e^{\operatorname{sim}\left(h{i}, h{j}^{+}\right) / \tau}+e^{\operatorname{sim}\left(h{i}, h{j}^{-}\right) / \tau}\right)}
$$

结果

image.png

因为SimCSE做的是一个句子表征的任务,即获得更好的句子的embedding,实验效果如上图。作者使用基于BERT和基于RoBERTa的SimCSE分别与Baseline进行比较,均取得较好的效果。

下边是SimCSE使用不同版本的BERT及其变体做出的模型,对应的模型可以直接从hugging face上获取.

Model Avg. STS
princeton-nlp/unsup-simcse-bert-base-uncased 76.25
princeton-nlp/unsup-simcse-bert-large-uncased 78.41
princeton-nlp/unsup-simcse-roberta-base 76.57
princeton-nlp/unsup-simcse-roberta-large 78.90
princeton-nlp/sup-simcse-bert-base-uncased 81.57
princeton-nlp/sup-simcse-bert-large-uncased 82.21
princeton-nlp/sup-simcse-roberta-base 82.52
princeton-nlp/sup-simcse-roberta-large 83.76

代码实践

既然它的效果这么好,如何快捷的在电脑上使用SimCSE呢?

先安装一下:

pip install simcse

用这两行代码加载模型。我上面那个表格里写了不同的版本, SimCSE("在这里填写不同版本")

from simcse import SimCSE
model = SimCSE("princeton-nlp/sup-simcse-bert-base-uncased")

既然是用来做sentence embedding的,那先看一下他怎么给句子编码:

embeddings = model.encode("A woman is reading.")

它反应比较慢,你需要等一下它才会出结果。不出意外的话,它应该会给出一个特别长的embedding编码。

我输出一下看一下,他应该是把一个句子编码成768维度的向量。
image.png

举报

相关推荐

0 条评论