在深度学习中,文本匹配模型可以分为两种结构:双塔式和交互式

双塔式模型也称孪生网络、Representation-based,就是用一个编码器分别给两个文本编码出句向量,然后把两个向量融合过一个浅层的分类器交互是也称Interaction-based,就是把两个文本一起输入进编码器,在编码的过程中让它们相互交换信息,再得到最终结果。如下图:

nlp 双塔 bert双塔模型_数据

双塔式模型中有监督句向量比较主流的方案是Facebook提出的“InferSent”,而后的“Sentence-BERT”进一步在BERT上肯定了它的有效性。无监督模型中比较主流的方案是SimCSE

标准的SimCSE是只需要正样本对的(通过Dropout或者人工标注构建),然后它将batch内的所有其他样本都视为负样本;而有监督版的SimCSE则是需要三元组的数据,它实际上就是把困难样本补充到标准的SimCSE上,即负样本不只有batch内的所有其他样本,还有标注的困难样本,但同时正样本依然不能缺,所以需要“(原始句子, 相似句子, 不相似句子)”的三元组数据。

至于CoSENT,它只用到了标注好的正负样本对,也不包含随机采样batch内的其他样本来构建负样本的过程,我们也可以将它理解为对比学习,但它是“样本对”的对比学习,而不是像SimCSE的“样本”对比学习,也就是说,它的“单位”是一对句子而不是一个句子。

SimCSE

https://spaces.ac.cn/archives/8348 本质上来说就是(自己,自己_Dropout)作为正例、(自己,别人)作为负例来训练对比学习模型。

英文实验效果

nlp 双塔 bert双塔模型_损失函数_02

InferSent

nlp 双塔 bert双塔模型_nlp 双塔_03

Sentence-BERT

BERT和RoBERTa在文本语义相似度等句子对的回归任务上,已经达到了SOTA的结果。但是,它们都需要把两个句子同时喂到网络中,这样会导致巨大的计算开销。论文作者提出了Sentence-BERT(SBERT)网络结构,该网络结构利用孪生网络和三胞胎网络结构生成具有语义意义的句子embedding向量。可以完成一些特定的任务,如相似度对比,聚类,基于语义的信息检索。

训练阶段的Sentence-BERT

预测阶段的Sentence-BERT

nlp 双塔 bert双塔模型_nlp 双塔_04

nlp 双塔 bert双塔模型_损失函数_05

获得句子向量的三种方式

  • 使用 [cls] 位置的输出
  • 计算所有token的mean/max pooling 的结果

代码实现:

sentence_transformer pooling

from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/bert-base-nli-max-tokens')
sentences = ['This framework generates embeddings for each input sentence',
             'Sentences are passed as a list of string.',
             'The quick brown fox jumps over the lazy dog.']

sentence_embeddings = model.encode(sentences)
for sentence, embedding in zip(sentences, sentence_embeddings):
    print("Sentence:", sentence)
    print("Embedding:", embedding.shape) # (768,)
    print("")

孪生网络和三胞胎网络简介

孪生网络:共享参数的两个神经网络。(每次是两个输入经过同一个网路)

损失函数

\[L = (1-Y)\frac{1}{2}(D_w)^2+Y\frac{1}{2}{max(0, m-D_w)}^2 \]

  • \( D_w \)被定义为两个输入之间的欧式距离。计算公式如下:

\[D_w = \sqrt{\{G_w(X_1)-G_w(X_2)\}^2} \]

  • \( G_w(x) \)是网络对输入数据的编码
  • \( Y \) 的值为0或者1。如果\(X_1\) 和 \(X_2\) 是同一类样本,则\( Y=0 \),否则\( Y=1 \)
  • \( m \)是边际价值。即当\( Y=1 \),如果 \( X_1 \)与\( X_2 \)之间距离大于\( m \),则不做优化(省时省力);如果\( X_1 \)与\( X_2 \)之间的距离小于m,则调整参数使其距离增大到

三胞胎网络

三个输入:一个正例 + 两个负例,或一个负例 + 两个正例。训练的目标仍然是让相同类别间的距离尽可能小,不同类别间的距离尽可能大

损失函数:

\[L = max(d(a, p)- d(a, n)+margin, 0) \]

  • p 表示正样本
  • n 表示负样本
  • \( d(a,n) \)表示a和n的距离

希望a和p之间的距离小于a和n之间的距离。margin是超参数,表示\( d(a,p) \) 和\( d(a, n) \) 之间应该差多少。

Classification Objective Function

\[O = softmax(W_t[u;v;|u-v|]) \]

\(W_t \in R^{3n*k}\) 其中n是向量的维度,k是label的数量。

损失函数:交叉熵损失。

Regression Objective Function

余弦相似度计算。损失函数为MAE(mean squared error)

三胞胎网络的目标函数

\[max(||s_a - s_p)|| - ||s_a-s_n||+ϵ,0 \]

其中, \(s_x\)表示句子\(x\)的 embedding,\(||・||\)表示距离,边缘参数ϵ表示\(s_a\)与\(s_p\)的距离至少应比\(s_a\)与\(s_n\)的距离近ϵ。在实验中,使用欧式距离作为距离度量, 设置为 1

引用一些结论

nlp 双塔 bert双塔模型_数据_06

双塔模型,其主要的重点都在编码器的优化,对速度要求高的召回场景可以用BOW+MLP、CNN,精度要求高的排序场景可以用LSTM、Transformer。同时两个向量的融合方法以及loss也都可以优化,比如做一些轻微的交互、像Deformer一样前面双塔接后面的多层交互,或者根据需要选择pointwise、pairwise(排序场景)损失。

但真要想做句间关系SOTA的话,比如刷榜,光靠双塔模型还是不行的,它有两个问题比较大:

  1. 位置信息。如果用BOW的话“我很不开心”和“我不很开心”两句的意思就变成一样了,虽然用RNN、BERT引入位置编码可以减缓一些,但不去让两个句子相互比较的话对于最后的分类层还是很难的
  2. 细粒度语义。比如“我开心”和“我不开心”这两句话只有一个字的区别,但BOW模型很可能给出较高的相似度,交互式模型则可以稍有缓解