如何使用预训练模型优雅的生成sentence embedding

前言

预训练语言模型在目前的大部分nlp任务中做个微调都能取得不错的一个结果,但是很多场景下,我们可能没办法微调,例如文本聚类,我们更需要的是文本的向量表示;又亦如文本匹配,特别是实时搜索场景,当候选集数量较多时,直接使用ptm做匹配速度较慢。那么如何优雅的使用预训练模型生成文本embedding呢?本文将会从不同的预训练模型与不同的优化方法去介绍文本embedding的生成方式,如有理解错误,欢迎大家指正。

本文所有的测试结果基于哈工大开源的LCQMC数据集,共20+w条数据,测试方法是计算两个句子的Spearman Rank Correlation与Pearsonr Rank Correlation,从测量的相关关系上区分,Pearson主要是用来测试线性关系,而Spearman主要是用来测试单调关系,因此建议大家以Spearman的结果为主要参考

word2vec与elmo

baseline还是需要的,word2vec使用了Tencent AI Lab提供的word2vec取了前50w个词(一共16G加载太慢,感觉也没必要全部加载),维度是200维,句向量采用结巴分词后取均值。elmo使用的是哈工大的ELMoForManyLangs,句向量采用的是embedding层的输出值,两层BiLSTM的输出值取均值作为句向量,对比结果如下。

modelpoolpearsonspearman
word2vecavg0.52214496807850760.608919562764869
elmoall layers avg0.5094836293279930.5385281534683071

bert类模型的基操

bert类模型输出句向量的方式主要有两种方式,取cls表示句向量或者对所有的token值做一个pool操作。pool又有很多种,有取均值,最大值,用哪一层的结果又有很多讲究,有取最后一层的,取后两层的,甚至取第一层与最后一层。这里采用了均值的方式,对比了bert与roberta。

modelpoolpearsonspearman
bertpool cls0.278713224723956930.3310821317298972
bertlast layer avg0.57728012582738370.6064618026402799
bertlast two layers avg0.58000124727800510.6059884888103338
bertfirst and last layer avg0.61273070614811120.649701785152543
robertapool cls0.60145000364646230.6750408040822591
robertalast layer avg0.62484980388995560.6769580860060049
robertalast two layers avg0.62780315899549110.6770390647187172
robertafirst and last layer avg0.63984327702779780.691869016734878

可以看到bert的cls可以说是完全用不了的,甚至被word2vec无情碾压。但是反观roberta的cls表现还不错。个人认为应该是因为roberta取消了nsp任务没有segment embedding,cls更能代表句向量。反观bert因为有nsp的任务,两个句子可能对cls的结果有一定的影响。当然也不排除是因为roberta本身效果就优于bert。效果最理想的方案是取第一层与最后一层的输出值做平均池化操作,个人想法,最后一层的值会有些“过拟合”,而第一层的值又较为“欠拟合”,中和一下选个折中的方案。

虽然roberta的效果有所提升,但是依旧有进步空间,我们还是需要考虑下为啥bert类模型生成的句向量效果不够理想,我这里有两种解释,先看第一种

在这里插入图片描述

上述表格第一行表示词频,数值越大表示词频越小。第二行表示的是bert生成的每个词的embedding到原点的 l 2 l_2 l2距离,可以发现,词频高的词距离原点距离更近。第三到五行表示的是词embedding的k个邻居词的 l 2 l_2 l2距离,可知词频越高的词,周围的词越多,词频越高周围的词也约稀疏。

从表格中得出的结论,可以发现在bert的embedding空间内,只要词频低的词,其附近会有很多“空洞”,即无意义的空间,而我们在计算sentence embedding时通常都是取均值,取了均值后就有一定的概率落在“空洞”的位置,或者说是语义不平滑的,这就对最终的的相似度计算造成了一定的影响。其次,高低频词的分布特性,导致在计算相似度时会出现相似度过高或者过低的情况,当句子都是由高频词组成,相似度就会很高,相反当句子中低频次很多,相似度就会很低。

再看第二种解释,一般用于计算文本相似度的方法是cosine similarity,其优点在于值域在[0,1]区间内,便于选择阈值,bert的输出是一个高维空间的向量,假设这个向量已经能充分的表示文本的语义,但cosine similarity计算较为简单,可能没办法把这么复杂的值用这么简单的方法计算出其相似度。其次,cosine similarity的计算公式是两个向量的內积除以各自的模长。

在这里插入图片描述

但是上式要成立需要保证我们的向量的基底是标准正交基,基底如果不同,那么內积的计算公式就不一样,cosine similarity的计算自然也就不一样了,bert生成的embedding的坐标系是否是标准正交基我们不得而知了,但是既然效果不理想我们完全可以猜测大概率不是。

知道了为啥效果不好,那么我们就来针对其缺点进行改进,下文会为大家介绍监督学习与非监督学习的两种改进方法。

基于监督学习的bert类模型embedding

既然直接使用bert生成的句向量效果不够理想,那么能不能先做一个微调再来使用bert生成句向量呢?通常我们微调都是最小化cross entropy,我们的目标并不是cosine similarity,既然如此,我们何不直接把目标修改为cosine similarity,让cls的值就是为计算cosine similarity来服务的,这就是sentence bert(下文简称sbert)的思路。

在这里插入图片描述

如上图所示,我们需要使用NLI或者STS的数据集对模型进行微调,把单个bert改成孪生网络的结构,这里虽然是两个部分,但是权重是共享的,首先使用NLI的数据训练模型,分别输入两个句子,得到对应的cls的值,及上图的u与v,然后把u,v与|u-v|进行拼接,再进行分类。

在这里插入图片描述

然后使用STS的数据再进行训练,结构还是一样的,只不过输出的u与v这个时候需要计算cosine similarity,最后损失函数换成了平方损失函数。

sbert中还提到了一种替换成hinge loss的方法

在这里插入图片描述

a是原句,p是正例,n是负例,目标是要让原句与正例的差别要比原句与负例的差别大 ϵ \epsilon ϵ

最后我们看下sbert的效果,使用的论文作者提供的一个多语言版本的sbert

modelpoolpearsonspearman
sbert-multilingual-v1pool cls0.69472669353648820.7500983592620584

除了sentence bert我们再看一种方法,来自苏剑林的simbert,其思路是让模型不再简单的判断两个句子是否相似,而是提高了任务难度,让模型选择哪个句子和当前的句子相似度最大,其结构如下

在这里插入图片描述

模型使用的是微软提出的unilm,即半编码半回归模型,输入是两个句子,与nsp任务类似,然后把两个句子的顺序颠倒再输入一次,这样的输入我们称为一组数据,每个batch需要n组这样的数据,n取决于你的显存大小。然后把整个batch内的cls向量都拿出来,得到一个句向量矩阵 V ∈ R b × d V \in R^{b×d} VRb×d b b b是batch_size, d d d是hidden_size),然后对 d d d维度做 l 2 l_2 l2归一化,得到 V ~ \tilde V V~,然后两两做内积,得到 b × b b×b b×b的相似度矩阵 V ~ V ~ T \tilde V \tilde V^T V~V~T,接着乘以一个scale simbert中取了30,并mask掉对角线部分,最后每一行进行softmax,作为一个分类任务训练,每个样本的目标标签是它的相似句(至于自身已经被mask掉)。说白了,就是把batch内所有的非相似样本都当作负样本,借助softmax来增加相似样本的相似度,降低其余样本的相似度。simbert因为使用的是unilm的结构,除了可以计算相似度,还可以用于生成同义句。当然也可以把unilm替换成bert,如果使用bert就只输入一个句子即可。

最后来看结果吧

modelpoolpearsonspearman
simbertpool cls0.70576745705592290.7611765158950129

sbert效果略逊于simbert,simbert算是目前我尝试过的模型中的SOTA,但也不排除如果有充足的中文语料,sbert也可能超越simbert。

基于非监督学习的bert类模型embedding

这部分我们来介绍下非监督学习的方法来改善embedding的方法

上文提到想要计算余弦相似度,首先需要向量所属的坐标系是标准正交基。既然正交则所属向量必不相关,那么其协方差就为0,对应的向量则应该表现出各向同性(各向同性是指协方差矩阵为单位阵乘以一个正的常数),所以,我们应该想办法把让bert的embedding的值变得或者说趋近于各向同性。

这里依旧是介绍两种方法,第一种方法是bert-flow,bert-flow的思路是把数据转变到标准的高斯分布,标准高斯分布是各向同性的,并且高斯分布是凸的,或者说是没有"空洞"的,因此语义分布也会更加的光滑。

在这里插入图片描述

bert-flow采用了基于流的生成模型来做这个数据分布的转变,flow是一个google提出来的用于做图像生成的模型,其思路是将服从高斯分布的随机变量 z z z经过 f f f函数映射到bert的编码 u u u, 其中 f f f需要是可逆的,这样 u u u即可通过 f − 1 f^{-1} f1映射到高斯分布上。

在这里插入图片描述
在这里插入图片描述

训练的过程只更新flow模型的参数,优化目标为最大化从高斯分布到bert分布的概率即

在这里插入图片描述

bert-flow中的流模型实际使用的是flow的改进版本glow模型,具体细节这里就不展开说明了,感兴趣的童鞋可以参考源码,这里也提供一篇讲解flow的博客

除了bert-flow转变成正态分布的方法,我们再看另一种方法bert-whitening,标准正态分布的均值为0,协方差矩阵为单位阵,那我们能不能想办法把bert的embedding的均值转为0,协方差矩阵变为单位阵,实际上,这就是机器学习中常用的白化操作(whitening是指不同特征间相关性最小,接近0即协方差是0,并且所有特征的方差相等)。

在这里插入图片描述

上式就是whitening操作,那我们的目标就很明确了,求解 μ \mu μ W W W

μ \mu μ很简单,计算均值即可

在这里插入图片描述

关键点在于求解 W W W,协方差矩阵的计算如下

在这里插入图片描述

根据上面的公式,我们可以得到 x ~ \tilde x x~的协方差矩阵为

在这里插入图片描述

whitening需要协方差矩阵是单位阵,所以有

在这里插入图片描述
在这里插入图片描述

协方差矩阵 Σ \Sigma Σ是一个正定对称阵,因此做SVD分解得到

在这里插入图片描述

这里的 U U U是正交阵, Λ \Lambda Λ是对角阵且对角线是正数,因此可以得到

在这里插入图片描述
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I3ws0XME-1618487449589)(/Users/joezhao/Library/Application Support/typora-user-images/image-20210401172944204.png)]

有了 μ \mu μ W W W即可对embedding做whitening了。初次之外,做SVD分解时, Λ \Lambda Λ是对应的奇异值,这个是按照从大到小排序的,因此,我们可以只取前k维的数据,毕竟奇异值小的特征,保留着意义也不大,这里其实就是做了个pca的处理。

最后我们来看下flow与whitening的效果,两篇论文在英文数据集上测试提升都较为明显,但在本文的中文测试集上提升并不大。

modelpoolpearsonspearman
bertfirst and last layer avg0.61273070614811120.649701785152543
bert-whiteningfirst and last layer avg0.65236563366795650.6532168453336087
bert-whitening-256first and last layer avg0.64249185205780620.659421830463197
bert-flowfirst and last layer avg0.61171117356473370.6541951509643525

总结

本文介绍了bert类模型的各种文本embedding的生成方式,每种方法各有优缺。如果你有足够多打了标签的同义句,那么采用有监督的方式肯定是最优的选择,没有数据,不妨尝试下两种无监督的方法,毕竟聊胜于无嘛~,除了bert自身的embedding,也可以考虑去融合其它模型生成的embedding,或者静态的词向量。

如果你有其它更有意思的embedding方式,欢迎留言,我会根据你提供的方法进行评测。

References

Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks

Whitening Sentence Representations for Better Semantics and Faster Retrieval

On the Sentence Embeddings from Pre-trained Language Models

Combining BERT with Static Word Embeddings for Categorizing Social Media

LCQMC: A Large-scale Chinese Question Matching Corpus

鱼与熊掌兼得:融合检索和生成的SimBERT模型

生成模型——流模型(Flow)

Transformer 架构的预训练模型主要分为两个阶段:预训练和微调。其中,预训练阶段主要包括两个任务:掩码语言模型(Masked Language Model,MLM)和下一句预测(Next Sentence Prediction,NSP)。微调阶段主要针对具体的自然语言处理任务进行模型微调。 下面是 Transformer 架构的预训练模型的详细结构说明: 1. 输入嵌入层(Input Embedding Layer):将输入的文本转换为向量表示,通常使用词嵌入(Word Embedding)技术。 2. Transformer 编码器(Transformer Encoder):包含多个相同的编码器层,每个编码器层包含多头自注意力机制(Multi-Head Self-Attention Mechanism)和前馈神经网络(Feedforward Neural Network)两个子层。 3. Transformer 解码器(Transformer Decoder,只适用于 GPT 类型的模型):包含多个相同的解码器层,每个解码器层包含多头自注意力机制、多头注意力机制(Multi-Head Attention Mechanism)和前馈神经网络三个子层。 4. 自注意力机制(Self-Attention Mechanism):用于在不损失序列信息的情况下,对输入序列中的每个位置进行加权处理,以获得更好的序列特征表示。 5. 多头自注意力机制(Multi-Head Self-Attention Mechanism):将自注意力机制分为多个头,分别计算不同的注意力权重,最后将结果拼接在一起,以获得更好的特征表示。 6. 多头注意力机制(Multi-Head Attention Mechanism,只适用于解码器):将输入序列和输出序列分别进行自注意力计算和注意力计算,以获得更好的特征表示。 7. 前馈神经网络(Feedforward Neural Network):用于对特征表示进行非线性变换和降维,以获得更好的特征表示。 8. 输出层(Output Layer):将特征表示转换为对应的输出结果,如文本分类、序列生成等。 总的来说,Transformer 架构的预训练模型采用了自注意力机制和多头注意力机制等技术,能够有效地捕捉输入文本的序列信息,从而获得更好的特征表示。同时,该模型的结构非常灵活,可以通过增加或删除编码器层和解码器层等方式进行调整,以适应不同的自然语言处理任务。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值