精读2018 KDD rec best paper—embedding在Airbnb的应用实践

Real-time Personalization using Embeddings for Search Ranking at Airbnb

前一段看了这篇文章,由于公司也在用w2v做相似商品推荐,很有感触,发现之前用的数据集简直不忍直视,在此总结了一下个人对这篇文章的看法,分享出来,有不足之处还请指教和讨论。

主要参考了两位大佬王喆吴海波的知乎,少绕了很多弯路,深表感谢。

文章用的模型很简单,但是为了拟合业务需要,做了很多改进,对目标函数和数据集的构建花费了大量的时间,这也需要对自己模型适用的场景进行深入分析。

首先说一下airbnb的业务模式,每个推荐模型都是建立在不同的业务场景,模型可以迁移,但数据集和所用特征才是提高模型效果的核心,那么深入理解业务才能得到100%的发挥模型的潜力。类似于携程和美团,通过guest自发的搜索行为 -> guest联系房东host ->进行短租listing(这里的listing表示一个短租房,类比电商的一个推荐商品item)。airbnb这种host可以回guest的租住请求给予反馈,也就是可以选择拒绝或者接受,如果接受,交易结束。

文章的目标:给一个query,包括位置,旅行日期后,根据用户的偏好,将位置,价格,风格特征抽象表达出来,也就是一个embedding 向量,计算相似后返回一个高质量的排序结果。这种构造embedding的思想,是典型的end2end learning,优势便是可以将工程耗时从繁杂的特征工程变成了专注于模型构建上来。embedding 的构建好坏自然就是其核心。

主要算法:w2v(skip-gram)

核心: 如何修改w2v的目标函数,使学到的listing和user的embedding vector能够更好地捕捉用户的短期兴趣和长期偏好;如何更好的切分session构造训练数据集。

文章大体分为两部分讲述:短期行为建模和长期行为建模。

 

short-term interest personalization

数据准备(综合文章4.1部分)

训练8个亿的search click session数据(这部分数据来自airbnb数月的真实数据),生成listing的embedding。目的是为了进行listing的相似推荐,以及对用户进行session内的实时个性化推荐。

每个search  click session指的是一个用户在一次搜索过程中,点击的listing的序列,这个序列需要满足两个条件,一个是只有停留时间超过30s的listing page才被算作序列中的一个数据点(排除意外点击),二是如果用户超过30分钟没有动作,那么这个序列会断掉,不再是一个序列。每个session至少有2个clicks。

接下来就是如何构造 Listing Embeddings,以及对目标函数的优化,顺便复习一下w2v

在w2v中,每个word都是一个one hot vector,超高维度,非常稀疏。

因为w2v的训练样本是(中心词,相邻词)的pair形式,j是一个window中的相邻词。假设窗口大小为c,中心词为l。根据极大似然估计的思想,由这些训练样本估计模型参数,连乘取对数,变加和,得到对数似然函数:D是所有中心词和其context的 pairs集合。

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

用softmax函数定义????????ω;θ,假设中心词和context分别属于不同的vocabularies。注意此处为了简便,和airbnb中的符号略有不同,不影响理解。中心词w,而aribnb是l。v就是想要学得的embedding vector。

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

c’表示单词表中的所有单词。用w2v论文中的表述如下:

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

仔细看下,是和airbnb论文中的表述是一致的。如下:

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

为了降低计算复杂度,airbnb采用negative sampling,把训练集D分为了正负pairs。在时间窗口m中,点击l的之前或之后点击的listing设为c,组合为形如(l,c)的positive pairs,作为训练集Dp。同理,c是从完整的vovabulary中随机选择的未点击的listing,然后组成形如(l,c)作为negative pairs,Dn。

在采用negative sampling的训练方式之后,分为正负pairs,objective转换成了如下形式:(二分类,softmax退化为sigmoid。)

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

为了使similar item列表中更倾向于推荐之前booking成功session中的listing。airbnb将booked listing看成全局上下文,当成正样本多次参与训练。自然地切分session,产生booking行为的叫booked session(最后一个是booking),没有的称做exploratory session。即:

因为每个booked session只有最后一个listing是booked listing,所以为了把这个booking行为引入objective,我们不管这个booked listing在不在word2vec的滑动窗口中,我们都会假设这个booked listing与滑动窗口的中心listing相关,所以相当于引入了一个global context到objective中。

 

因此,对于booked sessions来说,embedding的更新规则变为:

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

图1展示了listing embeddings是如何从booked sessions中进行学习的,它会使用一个滑动窗口size=2m+1(从图上看出来窗口大小标志应该是m,和原文有差异)。

文章中还说,对于booked sessions,进行过采样,重复五次,得到了最佳效果的embedding,经验而已。

分析airbnb的业务可以了解,用户预订的时候,通常都会连续的点击或者搜索一个地区内(他们想住宿的地方)的listing。这样,Dp中的listings很大概率上都在同一个market的,而Dn中的listings就大概率不在一个market。为了得到能够分辨同一市场内部listing的差异的embedding,在式子中增加一个随机负采样集合D_mn,由中心词 l 的同个market中采样选取。

最终的objective目标函数为:

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

注意:倒数第二项全局的正例没有加sigma符号,是因为整个(5)式是用来替换(1)中两个sigma加和符号后面括号中的式子,而这个括号中的式子表示的是一个session中的listings,故对于一个session只有一个booked listing。

 

listing的冷启动

和通常做法一样,对于新的listing,因为host在上传每个listing时要上传一些meta-data,例如位置,价格,类型等,那么我们可以找到这个心listing的三个最近的,有相似价格的listing,用这三个listing的embedding平均值作为listing的冷启动。

 

如何发现用户长期的偏好?

airbnb用到了较强的隐式反馈,即购买行为(booked listing).

传统的方法是对每个用户获得一条过去book的listing session,其中的每一项都是过去预定过的listing。然而,这样学习会有很多缺点:

1.booking sessions数据Sb比click sessions数据S要小很多,因为预定是低频事件。

2.许多用户在过去只预定单个listing,我们不能从session length=1中进行学习。

3.为了上下文信息中的任意实体学习一个有意义的embeddings,至少需要该实体出现5-10次,然而在平台中的许多listing_ids会低于5-10次。

4.最后,由同用户的两个连续预定可能会有很长时间的间隔,这时候,用户偏好( 比如:价格点)可能会随职业发展而变化。

于是,airbnb在实践中用了粒度跟粗的,在listing_type级别学习embeddings,而非listing_id级别。给定一个特定listing_id的meta-data,比如:位置,价格,listing-type,空间,床数等核心参数,通过人为制定的映射关系,把user_id, listing_id映射到大类type,然后再构建训练样本。

特别注意

为了学习在相同向量空间中的user_type和listing_type的embeddings,我们将user_type插入到booking sessions中,即直接将二者放在一个语料里面训练,保证在一个空间。如此,计算的cosine距离具有实际的意义。

这样一个session里面不仅有listing,还有user!具体数据形式是怎样?

根据原文的意思,训练样本应该是根据booked session得到的(user_type, listing_type) tuple 训练样本。形式应该和传统的w2v相同。其中每个booked session只包含相同的user id,但是他们的user type可能不同。应该是控制window step,使每个训练样本只包含一个u和一个l,即(user_type,listing_type)

数据形式如下图(a):

精读2018 KDD rec best paper—embedding在Airbnb的应用实践

具体的objective会根据中心词是user_type还是listing_type而不同。

因为(b)和airbnb业务结合太深,融合了host对user的反馈,在我司的推荐产品线上不存在这类反馈,就没仔细看。

Experiments部分也有很多亮点

用相同的seed每天随机初始化vectors,训练每个listing_id。(这里有个和一般理解不同的结果,airbnb发现,每天重新训练embedding,效果将好于对已有的embedding vector继续训练。这个可能需要具体情况具体分析,就是试)

综合内存和离线指标,trade-off,listing embeddings维度设置32。W2v的 context window size设置为 m =5 , 训练数据迭代10次。可想而知,8亿数据训练10次,能够达到线上要求,生产环境有多么令人羡慕……

为了快速的比较不同方案listing embedding效果的好坏,airbnb提出一个评估方法:“用embedding向量做排序,去和真实的用户反馈数据比较”,即:

拿到最近一次点击的listing A (个人理解就是booked listing之前的那次click)和需要rank的listings,这些listings里面包含最终预定的listing。通过计算listing A和候选listings在embedding空间的余弦相似度,可以对候选房源进行排序,观察最终被预定的房源在排序中的位置。

精读完这篇文章,如何把idea用数学形式表达真的需要好好培养。