论文简介
论文链接:SimCSE: Simple Contrastive Learning of Sentence Embeddings
如果大家了解对比学习的话就好办了,这篇文章就是将对比学习应用到了自然语言处理领域。起初对学习先是用在图像领域的。如果你了解的话就可以继续往下看,如果你不了解的话我建议是先了解一下对比学习。
另外推荐几篇我写的对比学习的文章。
- 诸神黄昏时代的对比学习
- “军备竞赛”时期的对比学习好。
无监督获取句子向量:
-
使用预训练好的 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中提到的:反转、局部裁剪、局部显出、裁剪翻转、调整饱和度、调整颜色、使用各种滤波器比如最大值滤波器,最小值滤波器、锐化滤波器。
在自然语言处理中也有很多的数据增广方式,但是他们对句子的影响都特别大。会严重降低对比学习的效果。为了解决这个问题SimCSE模型提出了一种通过随机采样dropout mask的操作来构造正样本的方法。模型使用的是BERT,每次出来的Dropout是不同的。随机dropout masks机制存在于模型的fully-connected layers和attention probabilities上,因此相同的输入,经过模型后会得到不同的结果。所以只需要将同一个句子两次喂给模型就可以得到两个不同的表示。使用这种方法产生出来的相似样本对语义完全一致,只是生成的embedding不同而已,可以认为是数据增强的最小形式。比其他的数据增强方法都要好很多。
无监督
无监督的目标函数是这样的。看一下上边,他图中示例是把三个句子作为输入传给编码器,然后编码器会得到对应句子的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}}
$$
有监督
使用有监督学习的一个难点,就是要找到适合构造正负样本的数据集。最终作者的选择如下:
那它的正负利是如何构造的呢。
以其中的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)}
$$
结果
因为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维度的向量。