POI 识别在阿里飞猪搜索的探索与实践
导读: 本文分享主题为搜索领域里面关于 Query 理解的范畴,包括了实体识别和 Linking 两个方面,主要聚焦在 POI 识别在飞猪搜索的探索与实践。
今天的介绍围绕以下四点展开:
- 背景与挑战
- 算法实践
- 线上效果
- 总结思考
分享嘉宾|韦峰 飞猪 算法专家
编辑整理|Tony Wang Linksure
出品社区|DataFun
01 背景与挑战
1. POI 识别的定义
首先介绍一下 POI 识别的定义。POI 主要包括三类:景点、酒店和地标。比如下图中的三个例子,景点搜索迪士尼,要出现上海迪士尼度假区这个 POI;酒店搜索,需要识别出给定 Query 对应的酒店 ID,之后进行一个精准的召回和精准推荐;最后一个是地标 POI 的例子,通过地标搜索酒店,需要识别出对应地标的 ID,通过这个 ID 寻找地标的经纬度,进行画圈召回,并展示到这个 POI 的距离。
2. 背景及挑战
飞猪场景的搜索是实体搜索,主要包括 4 类实体,分别是 POI、泛词、目的地和品牌。泛词搜索例如搜索露营,目的地搜索例如搜索杭州,品牌搜索例如搜索万豪酒店。用户的 Query 都是这些实体的单个或组合。用户利用飞猪搜索主要是通过搜索某一类实体去寻找这类实体后面对应的供给。例如搜索万豪酒店去寻找万豪品牌对应的酒店。今天分享的 POI 搜索,在飞猪搜索占比大概是 63% 左右。
之前的 Query 理解主要是通过 Query 改写和向量召回来扩充召回,以缓解Query 和商品之间的语义 gap,但是这样很容易出现相关性的问题。例如搜索一个 POI,经过扩充召回后,会召回出其他不相关的 POI。产生这个问题的原因是 POI 改写和向量召回的目的在于泛化,但是 POI 搜索是一个精准的场景,改写是很容易改错的,所以我们的重点将转移到实体识别和 Linking 方面,尤其是 POI 识别和 Linking。
相应地,POI 识别的挑战有两个方面。
首先是用户输入的 Query 是多变的, 用户经常是通过别名来寻找对应的 POI。例如用户搜索小蛮腰去寻找广州塔,小蛮腰是广州塔的一个别名,单靠文本的维度信息无法得到这两者之间的关系,所以需要进行别名挖掘。
另一个挑战是旅行是一个低频的场景 。下图中左侧图例,横坐标是旅行次数,纵坐标是旅行人数,可以看到大部分用户一年只进行 1~2 次旅行。单靠旅行场景的行为是无法提供一个置信的行为样本,所以我们需要使用其他样本。
02 算法实践
1. 整体架构
接下来介绍算法的细节。
首先是 POI 识别的整体框架,其目标是输入一个 Query 之后识别出该 Query 对应的 POI ID,并应用于后续链路。
POI 识别的整体架构从下往上包含的各模块分别为:
- 首先是预训练模型,我们基于自己的场景特点预训练了一个自己的模型,并应用于后面的所有链路。
- 往上是别名挖掘部分,基于 POI 库和搜索日志进行别名挖掘。别名挖掘主要包括抽取式、行为和 CETAR 等三路挖掘,最后一路挖掘是我们今年发表在 CIKM 2022 上面的一篇 Paper。
- 再往上层是召回部分,最主要的是基于别名的mention召回,需要进行 Query 中 POI 的 mention 识别。这里给了一个例子,例如给出的一个 Query 是西湖门票,需要识别西湖 POI 的 mention,然后进行 mention 相关的召回。
- 最后是排序层,排序的结果输出给后续链路进一步使用。
2. 领域预训练
下面介绍我们这边领域的一个预训练模型。预训练模型的语料是基于飞猪所有的Query、商品标题和 POI 名称。
预训练包括三个子任务,分别为 MLM、SimCSE 和位置多分类。
- 最基本的任务是图右边所示的基于字或词的一个 masking 机制;
- 另外一个任务是基于去年的 SimCSE方法引入了对比学习的预训练任务,该子任务是基于无监督训练,文本样本是经过两次 dropout 作为正样本,对应的负样本是当前训练 Batch 内的其他样本作为其负样本;
- 第三个任务是一个多分类的任务,引入这个任务的原因是因为在旅行场景,我们这边的文本不管是 Query、商品的标题还是 POI 背后都隐含了一个位置属性,为了提高文本表征在位置上的表现,我们引入了一个城市多分类的任务。
这三个子任务是一起训练的,并最终产生两个模型,包括一个大模型和一个小模型,其中大模型是用于离线处理已经见过的 Query,并进行线上 Cache 实现在线直接读取;小模型是用于处理新 Query,相对耗时会小些。
3. 别名挖掘概述
别名挖掘,主要包括抽取式、行为数据和 CETAR 三路。其中,抽取式挖掘是从 POI库出发,基于 BERT_CRF 进行核心词识别,例如杭州西湖风景区到西湖的核心词识别;基于行为数据挖掘是基于用户的点击和成交,之后进行召回,并使用一个置信度进行过滤,例如小蛮腰到广州塔的实例;CETAR 是我们今年发表于 CIKM 2022的一篇 paper,后续重点介绍下 CETAR 方法。
4. 别名挖掘 CETAR 算法
CETAR 将别名挖掘定义为缩略预测问题,就是预测 POI 名称的可能的缩写形式。例如复旦大学 POI 的别名是复旦,这类别名出现在 POI 名字的前面;上海迪斯尼POI别名是迪斯尼,这类别名出现在 POI 名称的后面;北京大学这个例子,POI 的别名是北大且是不连续的;最后一个例子是多个别名情况,中国中央电视台的别名可以是央视,也可以是中央台。
我们回顾了之前的一些研究,发现其存在一些缺点,第一个问题是之前研究只用了POI 名字本身的信息,但是 POI 名字本身是比较短的,蕴含的信息是比较少的,我们发现跟 POI 相关的描述里面可能会蕴含一些信息,例如包含 POI 的别名本身或者 POI 的一些类型,这些信息是可以帮助我们进行别名识别。
下图展示了两个例子,第一个例子是十三陵定陵,它的文本描述里面就有其对应的一个别名叫做定陵;第二个例子是阿里巴巴电子商务公司,它的描述里面包含了类别信息,这些信息都有助于别名识别。
第二个问题是之前的模型都是基于序列标注的,例如基于 CRF 的模型,是使用转移矩阵来寻找概率最高的标签,往往就因为它只是一个概率,会忽略了标签之间的依赖关系,而且这些模型也几乎都没有利用整个句子的整体语义来进行预测。
为了缓解上述问题,我们做了一些改进。模型的输入包括了 POI 名字 𝒙 和它相关的描述文本 𝒅,输出 𝒚ˆ 是对应的一个别名或缩写。其中,𝒚ˆ 的每一位要么是来源于 𝒙 的一个同位置,要么是星号,星号表示当前的 token 是需要被缩写的,模型的整体结构是由一个编码器和一个解码器组成,具体如下:
- Context-Enhanced encoder
在这个模块中,源实体的完整形式 𝒙(阿里巴巴电子商务公司)及其相关文本 𝒅(阿里巴巴...)的 token sequence 作为输入。类似于 BERT 初始化方法得到输入 token sequence 的 embedding,连同它们的 position embedding 一起被输入到 𝐿 层 transformer encoder layer 中,以生成一些关键 feature representations。这些特征表示对有关输入 token 的重要信息进行编码,考虑数据的噪音,最后只选择了实体对应的信息供解码过程使用。
Abbr-Recover decoder
这是我们模型中生成缩略词序列 𝒚ˆ 的关键模块。它是由 𝐿 层 transformer decoder layer 和两个对应于缩略和恢复策略的分类器分别组成。整个解码过程实际上是一个交替迭代的过程。单次解码过程中,前一次解码生成的中间序列的初始化 embedding 和 position embedding 都被输入到 transformer decoder 当中,同时 encoder 生成的特征表示用于解码,而后得到每个输入序列 token 的隐藏状态(嵌入),再将其输入到缩略分类器(针对缩略操作)或者恢复分类器(针对恢复操作),让分类器对每个 token 进行二分类,最后得到新的序列 𝒚。
以单次循环为例,可以看到图的最右边,中间序列【阿里巴电子***】输入到 AR decoder 中,先进行了缩略操作,得到【阿里巴*******】序列,而后进行恢复操作,得到【阿里巴巴******】。在迭代译码过程中,最终两个分类器的输出序列相同时,迭代将会终止,将生成最终序列 𝒚ˆ。
接下来是 CETAR 方法的实验部分,我们的实验是基于三个数据集的结果,包括了D1,D2 和 D3,其中 D1 和 D2 是公开的数据集,D3 是我们自己的数据集,D1 和 D2 的 POI 的相关描述是来源于百科,D3 的 POI 描述是来源于 POI 的评论和该 POI 有过行为的 Query。我们对比的指标包括 Hit 命中率(Hit)、准确率(Accuracy)、新别名的发现占比(R_NA)、预测别名不可以成为一个词的占比(R_NW)和预测的别名是有意义的但别名是错的占比(R_WOM),其中,R_NA 指标用于衡量预测模型的发现能力, R_NW 和 R_WOM 指标是越低越好的。可以看出,我们的模型显著地优于之前的模型。在实验中我们的模型有几个变种,包括 TAR和CETAR1,两个模型变种的输入只有 POI 名称本身并没有 POI 的描述,可以看出它的准确率是有降低的。CETAR1 表示 decoder 迭代只有一次,可以看到它的准确率下降很厉害。经过别名挖掘后,得到了我们的别名库。
5. 多路召回
别名挖掘后的召回步骤主要包括三路的召回,第一路是基于别名的 mention 召回,需要识别出 Query 里面对应的 POI 的 mention,下图是 mention 识别的整体流程图。基于挖掘的 POI 别名的词典,接收到一个 Query 后,通过词典进行匹配,得到一个词典样本结果。同时将该词典样本结果加上人工标注的结果来训练一个NER模型,通过模型预测 Query 里面对应的 POI mention。接下来是一个融合的步骤,我们会对词典样本的结果和模型的结果进行路径打分,并得到一个融合的结果。融合是以词典结果为准,只有当模型的结果打分远大于词典的结果打分时(比如高 30% 以上),才会选择模型的结果,融合之后进行输出。同时,我们会寻找词典结果和模型结果的差异,并进行人工审核,通过人工标注去增加一些困难的样本以丰富人工样本,得到 POI mention 之后进行别名召回。
另外两个召回分别是分词召回和 ngram 召回。由于 POI 的 mention 可能会识别错误,另外 POI 的别名可能不是很足够,所以增加了这两个召回来提高召回率。
6. 消歧排序
最后一个部分是消岐排序模块,下图是整个架构的示例。它的建模方式是基于 point wise 的二分类模型,主要目的是建模召回的候选和当前 POI 的相关程度。
先从样本准备来看,我们最开始是基于搜索场景的行为日志来做样本选择的。下边的表格是一个实验,可以看到我们在基于行为样本的条件下,模型的 F1 值只有 85%,这个值还是比较低的。
通过人工审核发现行为样本里面存在不少噪声,所以我们尝试用替代方式来替换掉这部分样本,第一个是去使用外部样本,例如 Wikipedia 的例子,输入小蛮腰之后,它会返回广州塔 POI。我们基于外部样本去清洗除了一个大概千万级别的样本集,同时加入了一个人工标注的大概万级别的样本集。通过实验可以看出,引入了外部和人工样本后,同时加入到我们的预训练模型,得到的 F1 值提高到了 91% 左右。之前的两个实验的特征主要包括文本特征,分别是用户定位地、Query、POI 名称、POI 地址,其中,引入用户定位地的目的是为了建模结果跟用户位置的一个相关性,例如在杭州搜索动物园和在上海搜索动物园,其实它需要的 POI 是不一样的。
在模型架构中,文本特征是经过我们的预训练模型之后得到的文本表征,其他的特征是包括了增加的NER、位置、点击/转化和文本相似特征,这些特征经过一个 Embedding 层后,跟文本特征一起做了一个多头 self-attention,然后经过一层全联接层进行输出。可以看到,增加特征后,我们的模型效果是逐渐变好的,最后的F1值大概是 95% 左右,准确率是在 96% 以上。
03 线上效果
第三部分介绍下我们的线上效果。先介绍下我们在线是如何使用的,得到 POI 识别结果之后,识别的结果输出给多个场景使用,包括我们给联想页做了一个 POI 卡片的展示,然后会给结果页进行 ID 召回。ID 召回是指我们对商品上也会进行POI 挖掘,打上对应的 POI ID。ID 召回是 ID 之间的匹配,召回即相关,同时还会给相关性模块,作为属性相关性的一个补充,并进行相关性加权。同时我们还会给排序模块用于 POI 卡片的置顶和作为特征参与 ranking 的一起训练。从最终的效果看,我们的模型结果对我们的转化率是有提升的,同时相关的 bad case 比例的降幅是比较大。
04 总结思考
最后进行一下总结。下图是我们整个 Query 理解的一个大图,可以看到除了最基本的模块,例如归一化、分词、纠错、Tagging 和 Weighting 之外,我们的主要模块在做核心元素的识别,包括目的地识别、POI 识别、泛词识别、品牌识别和意图识别。
第二个是做 NLP 或算法的同学都需要面对的问题:当接收一个 Query 后,因为模型的更新导致它本来是对的,但后面变错了。我们的思路比较直接,会对算法结果引入人工审核,当这部分 Query 被审核后,算法就不更新了,这样逐步扩大已审核的占比,而模型只是去处理那些没有审核的长尾 Query。
另一方面,我们需要和业务深入合作,从数据分析的角度出发,分析出每类问题的占比,按照问题占比的顺序从高到低分别去做优化。
|分享嘉宾|