上一篇文章,讨论了语义匹配的语义场景,NLP 语义匹配:业务场景、数据集及比赛
这篇跟大家讨论相关的技术,主要包括BERT-avg、BERT-Whitening、SBERT、SimCES四个。
为了方便,还是从狭义的语义匹配的场景出发,输入一对句子,输出这对句子相似性(回归,0~1)。
BERT-avg做法很简单,「直接拿预训练后的预训练模型来做相似度匹配」,因为默认经过预训练后的模型含有知识,能够编码句子的语义。
但假如不经过fine-tuning的话,不能单纯把它们拼接起来作为输入,而是两个句子要分别输入BERT模型。具体的,我们可以采用
但实质上,这种效果并不好。原因在于「各向异性」。
什么叫各向异性?这里引入知乎某答主的回答。
我理解的是输出的每个token是768维的(即向量空间的基向量由768个768维的向量组成),embedding的数值取决于当前选的坐标系,假如当前坐标系非标准正交基,则会出现空间上是正交的向量(空间正交不依赖于坐标系,无论你取哪个作为基底,在空间中它们就是正交),但在该坐标系下计算出的投影不为0。
论文全称:《Whitening Sentence Representations for Better Semantics and Faster Retrieval》
项目代码:https://github.com/bojone/BERT-whitening
解决各向异性有BERT-flow、BERT-Whitening等方法,它们的目标都是通过基底变换,把当前的坐标系变换到标准正交基。其中BERT-Whitening尤为简单,「本质上就是对语料数据求特征值分解,从而达到把当前坐标系变换到标准正交基的目的」。
特征值分解的好处还在于能降维,大家想想PCA的原理,假如目前是768维的向量空间,经过正交标准变换后,有一些方向的特征值是很小的,可以把这些维度抛弃,达到节约存储空间,提高计算效率的目的。
上图为步骤,把当前任务的语料,分别一句句地输入到预训练模型中得到各自的embedding,然后对embeddings做特征值分解,得到变换矩阵,然后存起来。应用时,输入新的句子,把它们输入预训练模型,得到句子embedding,再用存起来的变换矩阵u和W做变换,这时候得到的embedding就是标准正交基表示的embedding。
论文全称:《SimCSE: Simple Contrastive Learning of Sentence Embeddings》
项目代码:https://github.com/princeton-nlp/SimCSE
上面提到的BERT-avg和BERT-whitening都是无监督的方法,这里提到的SimCSE有无监督和有监督两种模式,其中的无监督模式是当前无监督的SOTA方法。
SimCSE的核心思想是当前较火的对比学习,如下图所示。
和
,其中同一个句子的输出是相似的,即
是相似的,拉近它们的余弦距离,而
和
是不一样的语义,目标是拉大它们之间的距离,训练函数如下:
本来NLI的任务是输入一个句子对,输出它们的label是【entailment、contradiction、neutral】中的一个。SimCSE里作了点小改动,label为entailment的句子对作为语义相近的样本,目标拉近它们的余弦距离,而label为contradiction的句子对则作为语义不同的句子,要拉远它们的距离。损失函数如下
论文全称:《Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks》
项目代码:https://github.com/UKPLab/sentence-transformers
SBERT是近年工业上应用很广泛的方法,思想很简单,就是如上图左边得双塔模型,有的地方称为Siamese network(孪生网络)和Bi-Encoder。
有的朋友会疑问,这几年句子相似度的算法竞赛,发现都没见到这种双塔模型的身影,常常是上图右边这种Cross-Encoder的结构。原因是,双塔模型相对Cross-Encoder结构效是稍差的,但落到工业应用上,它的优点就是快很多!
很简单,回想上一篇博客检索式机器人的场景,假设现在知识库中有500个候选问题,现在传来一个用户的问题,Cross-Encoder结构要把用户问题和500个候选问题,分别拼接后输入模型,相当于要做500次前向计算。 而双塔模型则只需要把500个候选问题的embeddings提前算好存储起来,当有新问题时,只需要算这个问题的embedding,然后用新问题的embedding与500个embeddings做矩阵计算就OK了,相当于只进行了一次前向计算! 所以检索的一种方法是,首先用SBERT做检索,然后用Cross-Encoder做精排。
论文中的一些一些细节:
使用哪种损失函数依据手头数据集的形式,但无论采用哪种方式进行训练,预测时,用的都是两个句子分别输入encoder,得到输出后求pooling得到u和v,再求余弦相似度从而得到句子对的相似度。
- Classification Objective Function:如上图的左边,当训练集是分类数据集时,采用优化交叉熵损失。值得注意的是,这里的concatenation可以采用多种方式,如(u,v)、(|u-v|)及图中的(u, v, |u-v|)等等,论文实验证明,|u-v|是影响最大的因子。强调一下,concatenation只有在训练采用交叉熵损失才考虑,预测时,用的只有u和v。
- Regression Objective Function:如上图右边,采用平方损失。
- Triplet Objective Function:三元组损失,当训练的数据集是如NLI这种三元组数据集时(即包含原句、与原句语义相同的句子、与原句矛盾的句子组成一个三元组),就可以采用这种目标函数,损失计算如下
库地址:https://www.sbert.net/examples/training/sts/README.html#training-data
该库发布了用不同数据训练的SBERT模型可供直接调用(不过没看到中文的,只看到个多语言的),也封装了用你自己数据训练SBERT模型、Cross-Encoder模型的API,以及训练完后如何调用的API。库里还列举了SBERT的使用场景,如下图,包括计算句子的embedding、计算语义相似度、语义搜索、检索重排、聚类等等应用,每个应用都有示例代码。
总而言之,这个库对工业应用十分友好,建议做做成的同学充分掌握,业务上就能快速实现闭环。
想和你一起学习进步!『NewBeeNLP』目前已经建立了多个不同方向交流群(机器学习 / 深度学习 / 自然语言处理 / 搜索推荐 / 图网络 / 面试交流 / 等),名额有限,赶紧添加下方微信加入一起讨论交流吧!(注意一定要备注信息才能通过)
- END -