Fork me on GitHub

深度语义模型 BERT 在 58 同城搜索的实践

分享嘉宾: 熊威,58同城TEG搜索排序部资深算法工程师
整理出品: 张劲,AICUG人工智能社区

PPT下载:http://www.aicug.cn/#/docs

(视频回放)

导读

传统基于Term-Match检索技术可以较好的解决Query-Doc字面匹配问题,而对于没有词命中时的Query-Doc语义匹配问题稍显捉襟见肘。本次分享将主要围绕我们在深度语义模型上的探索,通过模型结构、采样方式上的迭代调优提升效果,以及如何在线上搜索系统中进行合理的应用,更全面的解决Query-Doc语义匹配问题。

本次议题主要介绍:

  1. 58搜索简介
  2. 深度语义模型探索
  3. 语义模型在58搜索的应用

听众收益:

语义模型在搜索排序、召回场景的落地应用

新技术/实用技术点:

(半)交互式、双塔式语义模型,搜索排序、语义检索

01 58搜索简介

图片

上图是58搜索的简易的流程图。分为两个部分,一个是离线构建,主要是针对58商户发布的文档和帖子进行处理之后构建索引。二是在线检索,是根据用户输入的query,从索引中去检索出最满足用户查询的一个Top文档的集合。这部分也是本次分享的一个重点。

检索过程,一般是分为召回和排序两块。召回,也可以看成是一个初排的过程,快速的从一个全量的文档集合中去筛选出一个小规模的候选集合。排序,则是对候选集合进行精排。这两部分有时候并没有明确的界限,各自的内部可以是一个多层多段式的设计。这种设计是为了更好的去兼顾我们检索的目标,既需要快速的响应,也需要尽可能的准确,满足Query,满足用户。

多段式检索

图片

这里我们对在线检索做了一个更细致的划分,形状像个漏洞,共有4层,每层需要处理的文档集合的规模是不一样的。L1层,需要从全量的数据中进行检索,一般规模在亿级以上,所以这部分我们采用的是一个倒排索引设计。它的特点是速度非常快。但从相关性的角度去看的话,它做的就比较粗糙。

L2层是基础相关性计算,它会更精细的去计算候选文档和查询的相关性,大概会进行万到10万次的计算,然后筛选出相关性较好的一小批文档集合。

接下来的L3层和L4层,是一个精排。它们处理的文档规模相对较小,我们在这部分可以加入更多的特征去训练机器学习排序和深度学习排序模型,来优化点击或转化,提升连接效率。

从图上可以看出,文本相关性主要是通过L1层和L2层来得到保障。它们两个结合起来,可以看成是基于Term-Match的相关性设计。其思想是把查询文本和文档文本看成是一个词或者短语的集合,认为词之间是相互独立的,那么相关性计算就是对这两组词序列去进行匹配的过程。匹配的算法也比较多,包括像比较常用的BM25,它可以计算全文相关性,以及我们做的一些局部的改进方法,基本可以实现匹配到的词越多,词越重要,命中的位置越好,命中的词越紧密,这样的文档可以排的更靠前。

Term-Match 相关 VS 语义相关

图片

这种策略,它可以比较好的去解决词命中时的字面语义相关。但对于没有词命中时候的语义相关,它解决起来是比较麻烦的。比如像归一化、同义词、同义句这种,它们在词形或者句法结构上虽然不同,但是语义却是相近的。还有像司机、代驾、驾校教练这种,虽然它们不是同义词,但是从语义上讲,它们也是强相关的(都有驾驶的能力)。那对于这样没有词命中时的语义相关问题,应该怎么去建模来计算文本之间的相关性呢?

02 深度语义模型的探索

下面介绍下我们在深度语义模型上的探索和调优工作。

深度匹配模型

图片

这里给出的是两种比较常用的深度匹配模型。总的思想是将两个文本放到一个高阶的语义空间里面去进行匹配。

第一种是一个表示模型,它是一个标准的双塔结构。查询端和文档端分别经过一个隐层的网络,表达为一个低维、稠密的语义向量,然后选择一个合适的匹配函数去计算它们的相似度。另外一种是交互式的模型,特点是在开始的时候,两个文本就进行交互,然后匹配信号经过一个隐层和聚合层,得到一个最终的匹配信号,然后基于匹配信号得到一个匹配的得分。、

基于这样一个结构,为了学习到一个准确、丰富的语义知识,其隐层的结构一般都比较大,这就需要足够的算力和标注样本。但是在实际应用中,这两个都需要比较大的成本,特别是高质量的标注样本更难获取。那么有没有一些更高效且成本低的来完成模型的训练呢?

预训练语言模型

图片

这里就需要借助预训练语言模型。图中给出的是预训练语言模型的发展历程,到目前也算发展比较成熟。预训练语言模型是NLP类任务的最佳伴侣,前面的文本匹配任务,它本质上也是一个NLP的任务。相比深度匹配模型,这两种模型都是处理文本的,它们可以有相似的输入和隐层结构,而区别可能在于输出层以及它们的目标任务不同。特别的,语言模型的训练是一个无监督的任务,可以低成本的获取到足够多的语料去学习到丰富的语义知识,而这些知识可以很方便地迁移到下游任务中,比如文本匹配任务。这类应用一般有两种方式,第一种是基于特征的方式,语义模型可以直接提供文本的特征到下游的任务去,参与训练。

另外一种是基于Fine-Tuning的方式,需要两种任务的模型结构是类似的。语言模型的网络的参数可以作为下游任务模型的初始化参数,再配合一定的标注数据进行微调,这样我们就能以较小的代价来训练出高精度的文本匹配模型。以上这两块介绍了深度匹配模型的一般的方法,下面我们会结合具体的模型来分析。

58的Query-Doc

图片

在介绍模型之前先感性的认识一下58的Query-Doc的数据形式。查询,一般是一个短文本。文档,是一个多标签类的文本,包括了标题、类目、地域,以及职位、行业、公司名,以及人工抽取出来的其他的一些标签,还包括详情描述的信息。

数据采样

图片

这部分是样本数据的获取。我们训练的是一个语义匹配的模型,会将Query-Doc语义相关的认为是正的,反之则为负。前面也提到,想获取比较高质量的标注样本,成本是比较大的。我们一般需要从检索日志或者人工构造的方式去产出尽可能准确的样本,还需要根据具体的数据特点,或者模型的效果去进行有针对性的调整。虽然很琐碎,但是这个过程还是非常重要的。我们也是在不断的尝试和调整。

这里罗列了几个比较泛的采样规则供参考。第一个是从检索日志中获取正采样,曝光并转化或点击的数据作为正样本;负采样,是从曝光未点击的数据里面去采样。但是这里面,也不所有的都是负样本,因为有一些虽然没有点击,但也是语义相关的,所以这里面要进行一些噪声的处理。

另外是通过人工构造的方式,包括随机负采样,以及针对已经采样好的样本,对查询词,即对query,或者对文档的一些局部的文本,去进行一些改写,来构造一些比较难训练的样本。

BERT语义模型初探

图片

有了数据之后,就可以开始来做模型实验。第一种尝试的结构是一个标准的基于Bert的交互式文本匹配任务。结构是将 Query和DOC文本拼接之后,输入到Bert,对Bert的输出的向量序列进行一个聚合,得到一个聚合的匹配的向量,然后基于这个向量得到匹配得分。训练方式是采用Fine-Tuning方式。这种结构,两段文本的交互是比较充分的,而且两段文本间都上下文依赖也能够更充分的学习到,因此匹配的精度会更高。但是整个模型的计算都是需要在线实时计算的,所以它的在线检索速度是比较慢的。不能满足大规模文档集合的计算。对应到之前的检索策略,这种结构只能适用于L4层的深度学习排序。

双塔式BERT

图片

第二种,基本结构是双塔式的文本匹配模型,也就是前面说的表示模型。两段文本分别输入到各自的Bert塔,得到各自的语义向量,接匹配函数得到匹配得分。这种结构的特点是在语义表达阶段,两段文本是没有交互的,丢失了文本间的上下文信息,各自表达出语义向量,然后在匹配层拉进两个向量空间,其匹配精度是不佳的。但是这种结构,也因为缺少了交互,模型的主体的语义向量计算部分是可以在离线完成的,特别是文档Embedding。在线计算的话,我们只需要计算匹配层的匹配函数,因此它在线检索的速度非常快,可以支持一个较大规模的文档集合的一个计算。对应之前的四层的策略,这种结构,可以应用到L2、L3、L4三层,应用场景更广。这种结构匹配精度不佳,所以我们在双塔结构的效果调优上做一些工作。

双塔式BERT调优1

图片

首先我们在匹配层和训练方式上进行了不同的实验。第一种结构是对两向量进行Cos计算,基于Cos值做一个二分类。第二种结构是对两段向量进行拼接,拼接之后,接MLP加Softmax来进行一个二分类。第三种是回归的方式来训练,对cos值进行回归,正例是1,负例是-1。第四种是Pairwise的方式,同时输入查询和正例文档和负例文档,去预测正例的相似度和负例相似度的差值,它的差值是需要大于一定的阈值。这里给的是1,这个参数是可以调整。从实验数据可以看到,回归的方式和Pairwise这种方式,它的效果相对会更好一些,而相比交互式的话,它的差距也还比较明显。

双塔式BERT调优2

图片

接下来基于前面两个比较好的结构,在训练方式上也做一些调整,因为交互式这个模型效果会更好,所以我们借鉴了知识蒸馏的思想,用训练好的交互式模型,去指导双塔式模型的训练。

以回归的双塔为例,原来我们预测的标签,正的为1,负的为-1。这里让训练好的交互式模型去预测样本,将预测值作为双塔模型的一个软标签,试图去拉近他们之间的效果。Pairwise的双塔结构也是类似的过程,去预测正负例的相似度差值,它不再是一个固定值,而是一个依赖交互式模型预测结果的灵活的值。从实验数据可以看到,加入软标签之后,在原来的基础上有近2个点的提升,这种情况下也更接近了交互式的效果。单纯的这种双塔结构,优化到这个程度,其实就已经可以开始考虑做一些实际的应用。

半(弱)交互式BERT

图片

应用会放到后面一起介绍,这里再介绍一下,从模型的交互方式上去进行改良,这里介绍的是Poly-Encoder,是2020年提出来一种结构。它是一个半交互式或者弱交互式的一种匹配模型结构。顾名思义可以看得到,用交互式的模型匹配效果好速度慢,而双塔式的效果差一点,但是速度很快。那么半交互式,是希望对两段文本去做一定的交互,但同时又保证较快的检索的速度。它的主体上设计还是双塔的设计,但会在Bert的两边塔的Bert输出层去对向量做一定的交互。Doc塔这边,基本上没变,经过Bert之后,会去聚合出文档的语义向量。但是在查询端这边,Bert输出之后,它会去构造不同的Code向量,然后每个Code的向量和Bert output序列进行Attention计算,得到Query的一种语义表达。可以构造m个Code,最终得到Query的m个语义表达,再与文档的语义表达,再进行一次Attention计算,得到最终query的动态语义表达。然后对这两个语义向量计算匹配得分。这种结构兼顾双塔式和交互式优点,有一定的交互,Doc塔这边它还是可以放在离线计算,而在Attention 这一部分的计算和匹配这部分的计算是需要在线计算的。

半交互式BERT

图片

半交互式,在Poly-Encoder之前,我们也尝试了另外一种方式。因为58的文档,是一个多标签的文本,是拼接起来的。我们可以人为的把文档划分成多个文本域,每一个文本域经过一个独立的Bert塔,得到各文本域的语义向量。划分成多个文本域之后,文档这部分,就可以得到多个文本域的语义表达,而Query塔这边得到一种语义表达,然后也是进行Attention计算,得到一个动态的文档语义表达,最后接一个匹配层。这是在Embedding层进行交互。另外也尝试了直接在cos匹配层进行交互的方式,结构这里没有画出来。

图片

最终,从实验数据可以看到,半交互式的这种结构,特别是Poly-Encoder这种,它的效果会更好。就算你只加入少量的code,比如4个code,它的效果也接近双塔式最好的一个效果,而且它还不是软label这种。更多的code的话,因为文本最大序列长度为64,所以最多的code数是64,它最好的效果是可以达到0.71,已经是比较接近交互式的一个效果。

语义模型小结

图片

这里给出语义模型的小结,三种类型的语义模型。交互式的效果最好,但是检索速度特别慢,基本上只能支持几十到百级的这种文档的计算。也不支持类似faiss这样的向量检索,因为它们之间的向量是相互依赖的。双塔式的效果有所差差距,但是它的检索速度非常快,应用场景更多,可以支持几万到几十万的文档规模的计算,这种结构可以支持向量检索的应用。半交互式的效果更接近交互式,它的速度也比较快,但会依赖attention的计算,attention依赖code的个数。

03 语义模型在58搜索的应用

语义模型在L3排序中的应用

图片

首先是在精排层的一个应用。之所以先在精排应用,一个是因为之前在精排层是没有用到文本相关的一些信息,加入之后可以进一步保证相关性。另外,精排层处理的文档的规模会更小,应用的量级也更轻,对系统的性能的影响是更柔和一些。L3层的传统机器学习排序,我们以XGB为base,在这上面进行了实验。原来是没有文本特征的,现在我们可以加入语义模型,引入语义特征,然后从性能上看,可以使用双塔和半交互式的语义模型。因为这一层可能会对大概几千篇的文档进行打分,适用于这两种模型。接入特征可以接入一个语义向量特征,也可以接入一个匹配后的一维的相似度特征。在训练上是可以进行两个模型的联合的训练,也可以去进行两段式训练,先训练语义模型,语义模型提供文本特征,再去训练一个排序的模型。考虑到之后会在其它层上去应用,为了在不同层的应用中保持统一,我们使用的是双塔模型,提供的是单独的相似度特征来去进行实验。可以看到加入语义特征之后,XGB排序的效果,其AUC提升了1.7个点。这个模型上线之后,相比于基线的XGB,CTR和CVR都有不错的提升。

语义模型在L4排序中的应用

图片

接下来再看一下在深度学习排序上的应用,其应用方式跟L3层类似。这一层需处理文档的规模会更小,所以这一层,三种语义模型结构,均可在这一层应用。目前这一层是以双塔模型来提供语义向量的特征。以DIEN为base,加入文本语义特征。模型中的查询和候选文档,以及用户序列中文档序列的特征里会拼接上文本向量特征,然后进行训练。从离线的实验可以看得到加入语义向量之后,基于CTR和CVR预估的AUC指标上都有一定的提升。这一层的应用,目前还处于一个在线实验阶段。

语义模型在召回中的应用

图片

继续深入,则是在召回层的应用。从漏斗图可以看出,精排层的效果,其实在一定程度上是受限于召回层的,即是L1和L2层。Term-Match这种策略,有时候比较严格。过于严格,可能会导致进入到精排层的文档不足,这也会限制精排层语义排序作用的发挥,所以为了进一步提升检索的效果,我们需要在召回层也引入语义信息进行召回。

语义召回的话,它怎么去运用?是应用在L1还是L2层?在我们的结构里面,因为L1层是对全量数据进行作用,数据是在亿级或者十亿级。如果在这一层应用语义召回,可能仅适用于双塔式结构,且需要用到类似faiss这种向量检索模型。但是在58的大部分检索场景中,除了文本的检索之外,还叠加了像地域、类目,以及结构化参数的检索。这部分是通过倒排的方式实现的,对于这种倒排检索和向量模型检索结合的问题,解决起来比较复杂,而且成本也较大,所以目前在L1层,我们没有应用到语义召回,而是在L2层应用。这一层,是万到十万级的计算。应用的方式是,我们在之前基础相关的计算基础上去加入语义相似度因子,来做一个总的相关性的计算。且在L1层,不能让它过于严格,可以去掉一些词,甚至可以把所有的文本条件都去掉,这样来保证足够文档流入到第二层,不会因为Term Match策略的严格导致不能召回。

图片

策略确定之后,就是上线实验。对这样一个新的基于语义召回的策略,它是否可以去替代原有的Term-Match召回策略。这里我们做了两种召回策略的评测。从数据上可以看得到,语义召回的检索结果会更充分,无结果率更低。而Term-Match更严格一些,会有大比例的少无结果的情况。从召回之后结果的满意度上,可能也是源于双塔结构的一个局限性,召回上,还是会有一定的不满意结果占比。所以在这上面直接去替代原有的Term-Match策略,起码目前还是不太合适的,所以我们选择的一个应用场景,是Term-Match策略少无结果下做一个补充召回。

图片

这是上线之后的效果体验。可以看到,搜索“汽车机械工程师”的话,在原有的Term-Match策略,仅召回了两条相关结果。而经过语义补召,会召回更多的和汽车工程师或者机械工程师相关的一些职位。

图片

另一个是搜索“老娘舅”,老娘舅是一家餐饮公司。像搜索公司这种,一般召回结果都会很少,这里原有只召回了一条,经过语义补召,它可以补充回更多的和餐饮公司相关的职位,像传菜员、厨师、配菜等。

图片

最终上线CVR也是有比较明显的提升。

后续工作

图片

简单介绍一下后续的一些工作。之前的应用介绍中,可以知道我们目前的半交互式模型,离线上取得了一些效果,但是目前还没有进行上线的应用,后续会考虑推进应用。第二个是在深度学习排序上,目前也用的是基于双塔模型的语义特征,但是在这一层是可以匹配精度更高的交互式模型来进行上线应用。第三个,是语义召回应用的一个深化。目前我们是在少无结果下的应用,随着模型效果的提升以及半交互式模型的应用,语义召回可以应用到更多的场景中。第四个,就是在深度语义模型上持续深挖,在效果以及检索速度上的持续改进。

图片

最后简单分享下实际应用过程中可能碰到的一些需要注意的问题。因为新技术的应用,对原有的系统都会有挑战,问题也会比较多,这里给出印象比较深的一些问题。第一个是产出向量维度的影响,特别是在语义召回应用场景,它可能是需要对万级、十万级,甚至是有的查询需要百万次的计算。这个时候如果在维度上进行压缩,对性能的提升还是很明显的。第二部分是索引离线构建的情况下,对在线语义预测服务的压力。在线服务是通过TensorFlow Serving部署在GPU上,离线构建,对服务的直接压力会很大,这个地方是需要注意。第三个是不同的语义版本迭代,以及不同应用集群下语义版本不同步的问题。如何在维护更少的语义版本下保证不同集群不同版本的迭代,这也是需要注意的。


本文地址:https://www.6aiq.com/article/1617063677499
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出