18

SimCSE:简单有效的句向量对比学习方法

 2 years ago
source link: https://mp.weixin.qq.com/s?__biz=MjM5ODkzMzMwMQ%3D%3D&%3Bmid=2650429837&%3Bidx=2&%3Bsn=0254ec20791844dafb550b82d17816a4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

SimCSE:简单有效的句向量对比学习方法

AINLP 2022-04-06 14:16
640?wx_fmt=jpeg

在本文中,我们将介绍EMNLP2021的一篇论文SimCSE。这是一种简单有效的NLP对比学习方法,通过Dropout的方式进行正样本增强,模型能够学习到良好的句向量表示。按照惯例,我们也对该模型在STS-B数据集上进行了实验复现。

640?wx_fmt=png

论文链接:

https://arxiv.org/abs/2104.08821

实验复现代码:

https://github.com/yangjianxin1/SimCSE

01

对于很多自然语言处理任务来说,学习到一个良好的句向量表示是非常重要的。例如在向量检索,文本语义匹配等任务中,模型将输入的两个句子进行编码得到句向量,然后计算句向量之间的相似度,从而判断两个句子是否匹配。

说到计算两个句子的相似度,最直接的做法便是,将两个句子输入到BERT模型中,使用[CLS]对应的输出或者整个句子序列的输出的平均向量,作为句向量,然后再计算两个句向量的相似度。但是由于BERT模型的MLM与NSP这两个预训练任务的局限性,模型无法很好地学习到句子表征能力。

640?wx_fmt=png

为什么经过MLM与NSP任务训练之后,BERT无法学习到良好的句向量表示呢?我们简单回顾一下MLM与NSP任务的做法。

  1. MLM任务是遮住某个单词,让模型去预测遮住的单词,在这个训练中,模型并没有显式地对[CLS]向量进行训练,没有告诉模型[CLS]这个向量就是用来编码句子的语义信息的。在MLM任务中[CLS]学习到的并不是句子的语义表征。

  2. NSP任务是给定两个句子,让模型判断两个句子是否为上下文关系,使用[CLS]的输出来进行二分类。在这个任务中,[CLS]是用来编码两个句子之间的关系的,而不是描述某个句子的语义信息。

综上所述,未经过fintune的BERT模型,必然无法得到良好的句子的语义表征。

美团的ConSERT论文研究表明,如果BERT模型不经过微调的话,模型输出的句向量会坍塌到一个非常小的区域内。下图所展示的是在STS数据集中,文本相似度的分布情况,其中横坐标表示人类标注的句子相似度等级,纵坐标表示没有经过finetune的BERT模型预测的句子相似度分布。可以很明显看到模型预测的所有句子对的相似度,几乎都落到了0.6-1.0这个区间,即使含义完全相反的两个句子,模型输出的相似度也非常高。这便是BERT的句子表示的“坍塌”现象。

640?wx_fmt=png

其中BERT的句向量的坍缩和句子中的高频词有关。当我们使用整个句子序列的输出的平均向量作为句向量时,句子中的高频词将会主导句向量,使得任意两个句向量之间的相似度都非常高。为了验证该想法,美团的ConSERT论文对此也进行了实验。

下图中蓝色线条展示的是去除若干高频词后,BERT模型在STS数据集上的Spearman得分。其中Spearman得分越高,说明模型在数据集上的表现越好。我们可以看到,当计算句向量时,如果去除若干个top-k的高频词,Spearman得分显著提高,句向量的坍塌现象得到了一定程度的缓解。

640?wx_fmt=png

由此可知,BERT的MLM与NSP预训练任务难以胜任下游的语义匹配任务。为了解决该问题,我们可以使用对比学习的方法对模型进行预训练,从而使模型能够学习到更好的句子语义表示,并且更好地应用到下游任务中。

02

对比学习起源于计算机视觉任务,它的核心思想是,拉近每个样本与正样本之间的距离,拉远其与负样本之间的距离。其中正样本是语义相似的样本,负样本是语义不相似的样本。这个思想与文本语义匹配、向量检索等任务的思想是相符。

在对比学习中,我们经常使用InfoNCE loss作为损失函数。假设我们有正样本对的集合  ,其中  与  互为正样本,  和  分别表示样本  与  经过模型编码之后的向量。则在规模为N的batch中,第i个样本对应的InfoNCE损失为:

上式中,  表示温度超参数,  表示相似度。可以看到这个损失函数类似于交叉熵的形式,鼓励拉近正样本之间的距离,而在分母部分,鼓励拉远负样本之间的距离。

如何为每个样本构造正样本与负样本是对比学习中的关键问题。负样本的构造往往比较容易,随机采样或者把同一个batch里面的其他样本作为负样本即可,难点在于如何构造正样本。

对于图像来说,对图像进行翻转、裁剪、旋转、扭曲等操作即可很容易地生成正样本。对于NLP来说,往往会采用替换、删除、添加词语的方法来进行正样本构造,但是上述操作非常容易引入噪声,并且改变原有文本的语义。例如对【我爱你】进行替换操作得到【我恨你】,就改变的原来的文本的语义。

为了解决上述问题,SimCSE论文中提出了一种基于Dropout的无监督对比学习方法,同时也对有监督对比学习方法进行了探索。

640?wx_fmt=png

无监督SimCSE

Dropout是一种用来防止神经网络过拟合的方法,在训练的时候,通过dropout mask的方式,模型中的每个神经元都有一定的概率会失活。所以在训练的每个step中,都相当于在训练一个不同的模型。在推理阶段,模型最终的输出相当于是多个模型的组合输出。

在无监督SimCSE中,作者将同一个样本  ,分别输入模型两次,使用不同的dropout mask得到两个向量  与  。则在规模为N的batch中,第i个样本对应的InfoNCE损失为:

Dropout可以视为一种数据增强的手段,通过dropout mask的方式,模型在编码同一个句子的时候,引入了数据噪声,从而为同一个句子生成不同的句向量,并且不影响其语义信息。其中dropout rate的大小可以视为引入的噪声的强度。

为了验证模型dropout rate对无监督SimCSE的影响,作者在STS-B数据集上进行了消融实验,其中训练数据是作者从维基百科中随机爬取的十万个句子。从下表的实验结果可以看到,当dropout rate设置为0.1的时候,模型在STS-B测试集的效果最好。下表中  表示对于同一个样本,它的dropout mask是一样的,也就是说编码两次得到的向量是一样的,模型几乎学不到东西。

640?wx_fmt=png
有监督SimCSE

作者还尝试了使用各种人工标注的数据集对模型进行有监督训练,包括QQP、Flickr30k、ParaNMT、NLI数据集。

与无监督SimCSE一样,作者利用数据集中人工标注的正样本对,使用InfoNCE loss对模型进行训练,实验结果如下表所示。可以看到使用SNLI+MNLI数据集训练的模型效果最好,并且其指标也比无监督SimCSE提高了2.4个点。

640?wx_fmt=png

除此之外,作者还尝试在NLI数据集上,为模型引入了困难负样本,将模型的输入由  扩展为  ,其中  与  分别表示  的entailment与contradiction。损失函数扩展为:

从上表的实验数据可以看到,为模型添加了难负样本之后,模型的指标从84.9提升到了86.2,说明了在对比学习中添加难负样本的有效性与必要性。

对于无监督SimCSE与有监督SimCSE,论文的实验结果如下表,可以看到,在STS任务中,无论是无监督还是有监督的训练方法,都比之前的方法有了较大幅度的提高,这证明了论文方法的有效性。

640?wx_fmt=png

作者还进行了一些关于池化层、  以及迁移任务的实验,详细内容可以参考原论文。

03

按照惯例,笔者尝试对SimCSE进行了复现实验,但由于资源有限,笔者仅在STS-B数据集上进行了实验。

中文数据集的复现结果可以参考苏剑林的复现实验:

https://kexue.fm/archives/8348

实验所使用的训练集与原论文一致,均训练2个epoch,每隔100个step进行一次验证集的评测,并且保存最好的checkpoint。预训练权重使用bert-base-uncased,并且直接将BERT的[CLS]的输出作为句向量,没有添加额外的池化层。

复现的总体效果如下表所示。可以看到,在无监督训练中,当dropout=0.2时,复现效果比原文略高。但在有监督训练中,复现效果与原论文的差距较大,甚至比无监督效果还略差。这个结果比较反常,笔者对代码进行了一轮debug,暂未定位到问题所在,后续会再次对有监督部分的训练代码进行排查。

640?wx_fmt=png
无监督SimCSE

笔者在无监督任务上,对batch size、dropout进行了对比实验,实验结果如下表所示。我们发现模型在验证集与测试集的指标差距有点大,大概有3-5个点的差距,说明训练得到的模型的泛化能力还有待提高。

640?wx_fmt=png

Batch Size比较

从上表可知,当lr=3e-5,dropout=0.1时,batch size设为256最佳,在测试集上的得分为0.761。在原论文中,作者通过实验证明SimCSE对batch size不敏感,但从复现实验结果看来,batch size越大,效果会更好。

Dropout比较

当lr=3e-5,batch size=64时,可以看到dropout设为0.2效果更好,取0.1或0.3都会变差。笔者认为,从某种意义上说,dropout的大小相当于噪声的强度,取0.1的时候,噪声强度较小,模型编码得到的两个向量太相似,导致模型无法学到足够的语义知识。当dropout取0.3时,噪声强度太大,改变了文本向量的语义信息。而在原论文中作者仅在0-1之间进行了dropout的对比实验,证明了dropout=0.1效果更好。

下图展示了无监督SimCSE在训练过程中验证集的spearman相关系数的变化趋势。可以看到大概在12k步时,模型在验证集上效果最好,保存了最好的checkpoint。大概在12k步之后,模型在验证集上的得分不升反降,说明模型开始过拟合。

640?wx_fmt=png

无监督SimCSE的损失函数计算方式如下:

def simcse_unsup_loss(y_pred, device, temp=0.05):    """无监督的损失函数    y_pred (tensor): bert的输出, [batch_size * 2, dim]    """    # 得到y_pred对应的label, [1, 0, 3, 2, ..., batch_size-1, batch_size-2]    y_true = torch.arange(y_pred.shape[0], device=device)    y_true = (y_true - y_true % 2 * 2) + 1    # batch内两两计算相似度, 得到相似度矩阵(对角矩阵)    sim = F.cosine_similarity(y_pred.unsqueeze(1), y_pred.unsqueeze(0), dim=-1)    # 将相似度矩阵对角线置为很小的值, 消除自身的影响    sim = sim - torch.eye(y_pred.shape[0], device=device) * 1e12    # 相似度矩阵除以温度系数    sim = sim / temp    # 计算相似度矩阵与y_true的交叉熵损失    # 计算交叉熵,每个case都会计算与其他case的相似度得分,得到一个得分向量,目的是使得该得分向量中正样本的得分最高,负样本的得分最低    loss = F.cross_entropy(sim, y_true)    return torch.mean(loss)


有监督SimCSE

下表展示了有监督SimCSE的复现效果,复现效果与原论文的差距较大,甚至比无监督效果还略差。这个结果比较反常,后续会再次对实验代码进行排查。

640?wx_fmt=png

有监督SimCSE的损失函数计算方式如下:

def simcse_sup_loss(y_pred, device, temp=0.05):    """    有监督损失函数    y_pred (tensor): bert的输出, [batch_size * 3, dim]    """    similarities = F.cosine_similarity(y_pred.unsqueeze(0), y_pred.unsqueeze(1), dim=2)    row = torch.arange(0, y_pred.shape[0], 3)    col = torch.arange(0, y_pred.shape[0])    col = col[col % 3 != 0]    similarities = similarities[row, :]    similarities = similarities[:, col]    similarities = similarities / temp    y_true = torch.arange(0, len(col), 2, device=device)    loss = F.cross_entropy(similarities, y_true)    return loss

04

本文首先介绍了BERT编码的句向量质量不佳的原因,然后介绍了一种简单有效的句向量对比学习方法SimCSE。最后笔者在STS-B数据集上进行了复现实验,验证了无监督SimCSE的有效性,可惜在有监督SimCSE方法上,未能复现论文中的实验效果,还得再debug一下实验代码。

总体来说,SimCSE的效果确实非常惊艳,通过Dropout这种非常简单的方式进行正样本增强,确实让人眼前一亮。也许很多人有过类似的想法,但是却不一定有动手做实验,实验是检验想法的最佳手段。

笔者认为这种通过对数据添加噪声来构造正样本的对比学习方法,还可以进行深挖。例如在CV中,一般会给图片添加高斯噪声来增强模型的泛化性,可以考虑以某种方式为句向量添加高斯噪声来进行正样本增强,这也不会改变句向量的语义信息。其次,在对比学习中也可以通过增大batch size、引入难负样本、难正样本,来增强模型的泛化性。

0?wx_fmt=png
AINLP
一个有趣有AI的自然语言处理公众号:关注AI、NLP、机器学习、推荐系统、计算广告等相关技术。公众号可直接对话双语聊天机器人,尝试自动对联、作诗机、藏头诗生成器,调戏夸夸机器人、彩虹屁生成器,使用中英翻译,查询相似词,测试NLP相关工具包。
343篇原创内容
Official Account
进技术交流群请添加AINLP小助手微信(id: ainlper)
请备注具体方向+所用到的相关技术点
640?wx_fmt=jpeg

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。

640?wx_fmt=jpeg

阅读至此了,分享、点赞、在看三选一吧🙏


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK