医疗GPT中基于向量数据库的文本匹配任务

现在在构建基于大语言模型的医疗GPT,为了一定程度上缓解幻觉问题,决定将大量医学指南存入向量数据库,然后对用户query进行embedding后送入数据库进行查询。因此这部分内容本质上来讲是一个文本匹配任务,于是本周对该任务进行了一点探索,总结成这篇博文。

文本匹配模型范式

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

paradigm.png

两种方法各有优缺点:

  1. 双塔式:优点是速度会快很多,一般来说候选的句子都是已经计算好embedding的,然后直接用query的embedding跟候选embedding去计算相似度就好,计算相似度要比送入模型里面进行推理要快很多。Representation-based 方法侧重于对每个输入文本进行表示学习,然后通过对比两个文本的表示来进行匹配。这种方法通常使用预训练的深度神经网络(如BERT、GPT等)来学习文本的表示,首先将输入文本编码成固定长度的向量表示,然后通过比较两个文本的向量表示来确定它们之间的相似性或相关性。代表模型有InferSent和Sentence-BERT等。
  2. 交互式:优点是准确率更高一些,因为多了query和key的交互,所以一般来说准确率会有所提升。Interaction-based 方法侧重于建模两个输入文本之间的交互信息,通常使用神经网络或其他复杂的模型来对两个文本进行交互建模,以捕捉它们之间的语义关系。代表模型有ESIM和RE2等。

下图是一个简单的文本匹配任务模型分类图:

classify.png

Representation-based model

双塔式的模型有三个大类:词袋模型,有监督模型以及pretrained-finetuning模型。

词袋类方法的主要思路是先使用词向量(如Word2Vec)将句子中的每个词转换为词向量,然后将句子中所有词的词向量求平均或求和,生成一个定长的向量来表示整个句子。显然,这种方法有很多问题:首先,这种方法忽略了词序信息,未考虑词语间的语义关系;其次,对不同词语的词向量简单求和/平均,忽略了词语对句意的不同贡献;此外,这样生成的句向量的表示能力有限,难以反映句子的深层语义关系;最后,当句子的长度变化很大时,向量比较容易受影响。典型的词袋类方法有SIF和WMD等等。

词向量虽然可以经过处理变成句向量,但词袋式的融合也会丢失掉顺序信息,同时在训练时其目标还是word-level的,想要获得「真正的句向量」,还是需要寻找sentence-level的目标函数。传统的有监督类方法包括DSSM和SiamLSTM等等。

pretrained-finetuning方法是我打算在医疗GPT中采用的。比较著名的有InferSent、Sentence-BERT以及CoSENT等。

Interaction-based model

尽管双塔式模型的速度比较快,但是却有两个比较大的缺陷:

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

于是在一些不太在意速度而更在意精度的任务上,交互式模型就更为合适。典型的交互式方法有ESIM和RE2等。

InferSent

InferSent是2017年提出的一种有监督的方法,其在自然语言推理(NLI)数据集上进行训练,学习到句子的通用表示。InferSent使用BiLSTM结构对句子进行编码并采用max-pooling策略提取关键特征来将时间序列信息汇总成一个固定维度的向量。如下图所示:

InferSent.png

Sentence-BERT

Bert模型在NLP的各大任务中都表现出了很好的性能,STS(semantic textual similarity)任务也不例外。但是Bert作为一种交互型方法,需要将两个句子同时送入模型,进行信息交互,这造成大量的计算开销。例如,有10000个句子,我们想要找出最相似的句子对,需要计算(10000*9999/2)次,需要大约65个小时,因此这种方法在实时交互的系统中,是不可能上线的。

因此作者提出了Sentence-BERT模型,将同样任务的耗时从65小时缩减到了5秒。SBERT的模型架构如下图所示,左侧是训练时的架构,右侧是推理时的架构:

SBERT.png

作者进行了多种实验:

  1. pooling策略:
    • CLS策略:直接使用CLS这个token的输出。
    • mean策略:按维度取均值。
    • max策略:按维度取最大值。
  2. 目标函数:
    • 分类目标函数:采用上图左侧的训练架构,将两个句子的向量还有他们的差的绝对值进行拼接,然后优化CE loss。
    • 回归目标函数:采用推理时的架构作为训练架构,计算两个句子向量的余弦相似度,然后优化MSE loss。
    • triplet目标函数:采用三塔式架构,一个锚点句子a,一个正例句子p,一个负例句子n,然后让a与p的距离比a与n的距离越小越好。

实验结果证明,双塔式架构+分类目标函数+平均池化策略的效果最好。

CoSENT

其实对于文本匹配任务而言,由于在检索阶段是基于余弦相似度进行相似句子的检索,因此最自然的想法是直接优化基于两个句子的余弦相似度的loss。然而,直接优化这样目标的实验结果往往特别差(至少明显比InferSent要差),在某些情况下甚至还不如随机初始化的效果。

作者认为这是因为负样本对的优化目标过低了。具体而言,对于那些“困难”的负样本对而言,它们往往语义不相同但字面上有比较多的重合;对于那些“简单”的负样本对而言,它们往往语义不相同的同时字面上也很不相同;然而在训练的时候,大多数数据集中的负样本对都是那些“困难”的例子,这时如果将它们的优化目标设置为最低值(例如-1)显然是不太合理的。作者进行了实验,设置负样本对的相似度只要低于0.7就不优化了,发现模型的效果有了一定提升。

于是作者设计了新的损失函数来进行改进。目标是设计一个基于两个句子向量余弦相似度的loss,让正样本对的相似度大于负样本对的相似度,至于大多少,模型自己决定就好。事实上语义相似度常见的评价指标spearman也是一样,它只依赖于预测结果的相对顺序,而不依赖于具体的值。因此,借鉴circle loss,作者设计了下面基于cos的排序损失函数:
$$
loss=log\left(1+\sum_{sim(i,j)>sim(k,l)}e^{\lambda(cos(uk,ul)-cos(ui,uj))}\right)
$$
也就是说,只要我们认为样本对(i,j)的真实相似度应该大于(k,l)的真实相似度,就可以往log里边加入$e^{\lambda(cos(uk,ul)-cos(ui,uj))}$;换句话说,只要我们能够为样本对设计顺序,那么就可以用此公式。

实验证明CoSENT的表现明显优于Sentence-BERT,我参考的开源库text2vec中效果最好的那些模型也都采用了CoSENT。