出品:贪心科技•作者:阿泽
今天学习的是一篇 2018 年 Airbnb 的一篇工业论文《Real-time Personalization using Embeddings for Search Ranking at Airbnb》,介绍的是 Word2Vec 在 Airbnb 推荐场景中的应用。大概内容就是从用户日志中抽取用户行为并组成序列,然后通过 Word2Vec 完成训练,最后得到 Item 的 Embedding Vector。虽然看似简单,但这篇论文却拿到 KDD 2018 Best Paper,知易行难,在看本文文章之前,我们先来试着回答几个问题:
读者阅读完本文后将一一得到答案:
工程师在 Airbnb 的应用场景中设计了两种 Embedding 方式:
我们先来看如何构建反应短期实时兴趣的 Item Embedding。
我们将用户的点击行为构建为序列
,其中 S 为 N 个用户的行为序列集合。为了消除噪声和增强负反馈信号,我们需要对序列进行清洗:
我们使用 Skip-Gram 模型来训练 Embedding,窗口大小为 2m + 1 的损失函数为:
整体样本的代价函数为:
为方便起见,我们以下讨论的损失函数均为窗口的损失函数。
为防止每次迭代计算更新输出向量的所有参数值,我们采用 Negative Samping 进行负采样,损失函数为:
其中,
为上下文
的输出向量;
为房源
的输入向量;
为正例集合(长度为 m 的窗口内,点击的房源为 l ,窗口内的其他点击房源为 c,用房源 l 来预测其他房源 c),
为 Negative Sampling 的负采样集合(l 为点击的样本,c 为样本库中其他样本)。
我们以点击序列进行模型训练可以计算出 Item 的 Embedding 向量,但显然的 Item 丢失了有效信息,被预订的 Item 更能反映出用户的兴趣。
目前来看我们现在的点击序列共有两种类型:
于是我们们将预订序列单独拿出来,探索序列的损失函数不变,修改预订序列的损失函数:
从预订序列的损失函数中我们可以看到,每一个窗口都会预测一次预订的 Item。
下图显示预订序列的工作方式,虚箭头表示预订的 Item 始终会进行预测。
为 Negative Sampling 的负采样集合,Word2Vec 的负采样是以一定从语料库中抽取单词,这显然不符合 Airbnb 的商业场景——用户本身就不会预订目的地外的房源。
为了增强负反馈,我们将从 Central Item (Skip-Gram 的输入)所在地区中随机添加一组负样本。损失函数为:
冷启动问题一直是推荐、CTR 等领域的一大难题。新的房源每天都会发布,但是这些房源没有 Embedding 可以使用,就没办法给用户进行推荐。为了给新 Item 完成冷启动,我们可以利用其他 Item 的 Embedding:
我们将 Item 分解为更细的元数据,如:位置、价格、类型等。然后我们找出新 Item 周围具有相同元数据的三个 Item。将这三个 Item 的 Embedding 向量取质心作为新 Item 的 Embedding 向量,这种方法可以 cover 掉 98% 的需求。
我们用八亿点击序列来训练数据,最终为每个 Item 得到 32 维的 Embedding 向量。为了评估 Embedding 的质量呢,可以从以下几个方面进行评估:
Item Embedding 可以很好的用来发现 Item 之间的相似性,非常适用于用户短期的个性化点击场景,但是这种 Embedding 没法捕捉到用户的长期兴趣,比如说用户几个月在洛杉矶和纽约预订了房子,现在要在旧金山预订房子,已有的 Embedding 方式没法捕捉到用户的长期偏好。如果我们将用户预订的 Item 组成预订序列
采用对待点击序列相同的方式会遇到很多问题(区别与上文提到的预订序列):
为了解决这种问题,我们提出了学习 Item-type 的 Embedding 来代替原本的学习 Item-Id 的 Embedding。利用元数据的思想,将一个 Item 分解为位置、价格、类型、房间数、床位数等等,这样每个 Item 都可以由多个元数据拼接而成。下图是我们定义的元数据及映射规则。
同样的方法,我们也适用于用户:
有了元数据后我们可以利用聚类的方式将相似用户聚在一起形成一个随时间变化的预订序列,这便很好的解决了数据的稀疏性。
为了给用户推荐相似 Item,该如何让 User-type 和 Item-type 处于同一向量空间?
一个简单的方法就是将 User-type 和 Item-type 混合并利用交替训练使其参数相互影响,从而同处一个空间内。
这种随时间变换的预订序列:
其中,
表示由用户订购的房源时的 User-type 和 Item-type 组成,对同一个用户来说,
可能随时间发生变化,所以相邻的两个
不一定相同,在实际应用时使用最新的
。
至此我们便解决了训练数据的问题,对于第二个问题,我们直接将所有的
拼接成一整条数据,滑动窗口可以在整条数据上滑行,这样便保证了其处于同一向量空间内。
目前我们已经利用了用户的点击行为和预订行为,还有一种行为没有被利用——房东拒绝。与点击预订反应用户偏好类似,拒绝行为也反映出了房东的偏好。
房东拒绝用户的原因可能有:用户的评分低、资料不完整、养宠物等等,这是一种强烈的负反馈信号,我们也可以在训练过程中加以利用。
我们将房东的 Reject 行为编码到 User-type 的元数据中(User-type 相对于 Item-type 来说更敏感,如用户低评分、资料不完整等等),这样用户碰到的 Rejection 会越来越少,可以提高预订转换率。我们将 Reject 行为加入到损失函数中:
其中,
为 Reject 集合。
下图为加入 Reject 后的模型,在预订序列中预订失败会有负号标记。
User 和 Item 都具有某些属性,比如说地理位置、价格等,对于新用户或者新 Item 可以通过现有的元数据完成冷启动。
通过余弦相似度计算 User-type 和 Item-type 之间的相似度,下图是某个例子,我们可以看到与 User-type 相似度高的 Item-type 和相似度低的 Item-type 类型正好相反。
Expenriments
我们来评估一下这两种 Embedding 的质量。
首先我们将 Item Embedding 应用于搜索模型中,d32 表示维度大小为 32。横坐标为用户预订前最新的 17 次点击,纵坐标为预订 Item 平均排名,我们我可以看到对于系统本身的 Search Model,用户点击次数越多越精准,而我们的 Embedding 向量加入也是非常有效的。
Airbnb 的首先都会推荐给用户相似的 Item,我们将现有的相似检测算法和基于 Embedding 的相似计算方法进行 A/B 测试,结果表明基于 Embedding 的方法提高了 21% 的点击率,通过提高了 4.9% 的预订概率(通过推荐页进行预订)。
Airbnb 使用基于 Pariwise 并支持 Lambda Rank 的 GDBT 模型进行搜索排序,使用的特征包括房源特征、用户特征、搜索特征和交叉特征等共 104 个特征,标签数据包括 (0, 0.01, 0.25, 1, -0.4) 分别对应,浏览没点击、点击、联系但是没预订、预订成功、拒绝预订。采用 NDCG 来作为排序的评价指标。
我们先来看下如何利用 Embedding 来构建特征,这个对工业界来说非常实用。首先从用户行为日志中收集用户过去两周的房源信息:
然后计算候选房源与上述房源(1-7)的相似性,并作为新的特征输入到模型中。第 8 个特征中,一个用户可能有多个 User-type,取最近的一次。
下图为构建特征的重要性程度:
下图为线上的实验指标:
Airbnb 在标题中 Real-time Personalization using Embeddings for Search Ranking,应该是指:在搜索后段加载全部的 Embedding 向量到内存中,然后利用 Kafka 实时计算候选房源与历史房源的相似性作为实时特征。
这里的候选房源,也就是召回策略其实是由用户确定的,通常用户会输入日期和地点,这种情况下的候选集通过只有几百个,这就对性能的要求很低了,而实时计算的相似性也只是与历史房源的质心做计算,所以计算速度是非常快的。
看完这篇文章后的思考: