推荐系统简介
我们平时网购会发现买过某个物品后会跳出各种同类产品广告,这背后其实是一整套商品推荐系统。
一个推荐系统可以看作搜索排序系统,输入为用户和上下文相关信息(后面统称query),输出为经过排序的条目(items)。
给定一个 query,推荐系统任务是从数据库中找到相关条目并将它们按照某种目的(通常是点击率和购买率)排序,一个好的推荐系统往往能让用户尽可能多地在该网站停留并购买更多的商品,从而创造巨大的商业价值。
下面看一个实际的推荐系统【1】:Google Play(一个商业移动 app store,有超过 10 亿活跃用户和超过 100 万应用,安卓机用户应该比较熟悉) 应用推荐系统如下图所示。
一个 query 可能包括不同用户和上下文特征,当用户访问 app store 时自动产生。推荐系统则为用户界面返回一组用户可能安装的应用(称为 impressions),用户会执行特定动作,例如点击或购买。这些用户行为,以及相应的 query 和 impressions 都会记录在日志(Logs)中,作为机器学习系统(Learner)的训练数据。由于数据库中有超过 100 万个应用,对每个 query 都穷举每个应用的推荐值是不可行的,因为不满足服务延迟需求(一般几十毫秒)。因此,第一步是先做检索(Retrieval)。检索系统通常采用机器学习的模型和人为定义的规则返回最匹配当前 query 的条目构成的短列表,候选数据量从几百万大幅减少到几百个左右。
之后,排序系统(Ranking)将所有条目根据推荐值排序。推荐值通常为 P(y∣x),即给定输入 x 时用户行为标签为 y 的概率。
输入 x 向量包括用户特征(例如国家,语言,种族等)、上下文特征(例如设备,当前时间,星期)、impressions 特征(例如应用年限,应用历史统计)。
这里排序系统的实现时采用 Wide & Deep 模型。
Wide & Deep 模型
推荐系统和类似的通用搜索排序问题共有的一大挑战为同时具备记忆能力与泛化能力。记忆能力可以宽泛定义为学习那些经常同时出现的条目或特征,发掘历史数据中存在的关联性。泛化能力则基于迁移相关性,探索之前几乎没有出现过的新特征组合。
基于记忆能力的推荐系统通常更着眼当下,直接与用户已经采取的动作相关;泛化能力相比记忆能力则更趋向于提升推荐条目的多样性。
对工业界大规模线上推荐和排序系统中,广义线性模型(如逻辑回归)得到广泛应用,因为它们简单,可扩展,可解释。这些模型一般在二值稀疏特征上训练,这些特征一般用独热编码,举个例子,如果用户安装了 Netflix(一个流媒体播放平台),则二值特征“user_installed_app=netflix”的值为 1。模型的记忆能力可以有效地通过稀疏特征之上的外积变换获得,类似 当用户安装了 Netflix,随后又展示了 Pandora(一个互联网广播应用),那么 AND(user_installed_app=netflix, impression_app=pandora) 的值为 1。这解释了同时发生的一对特征是如何与对应标签关联的。
可以通过使用小颗粒特征提高泛化能力,例如 AND(user_installed_category=video, impression_category=music) ,但这些特征常常需要人工来选。外积变换有一个限制,它对于不在训练数据中的查询项不具备泛化能力。
基于嵌入的模型(如 factorization machine 和深度神经网络)对以前没出现过的查询项特征对也具备泛化能力,通过为每个查询和条目特征学习一个低维稠密的嵌入向量,减轻了特征工程负担。但它很难有效学习低维表示,当 query-item 矩阵稀疏且高秩时,例如用户有特殊偏好,或者只有极少量需求的条目。
这种情况下,大多数 query-item 是没有交集的,但稠密嵌入会给所有 query-item 带来非零预测,从而可能过度泛化,给出完全不相关的推荐。而使用外积特征变换的线性模型只需少量参数就能记住这些“例外规则”。
所以自然而然地想到,通过联合训练一个线性模型组件和一个深度神经网络组件得到 Wide & Deep 模型(如下图所示),这样用一个模型可以同时获得记忆能力和泛化能力。
上图左侧为 Wide 模块,它是一个广义线性模型,形式为 y = wT x + b 。其中 y 为预测值,x = [x1, x2, ..., xd] 是 d 维特征向量,w = [w1,w2,...,wd] 为模型参数,b 为偏置项。特征向量包括原始输入特征和变换的特征,一种重要的变换是 外积变换
定义为:
其中 的一部分则为 1,否则为 0。对于二值特征,外积变换例如 AND(gender=female, language=en) =1,当且仅当 gender=femail 和 language=en 都为 1 时成立,否则为 0。这种机制可以捕捉二值特征之间的交互,增加了非线性。
上图右侧为 Deep 模块,它是一个前馈神经网络。
类别特征的原始输入为特征串(如“language=en”),这些稀疏,高维类别特征首先转换为低维稠密实数向量(通常称为嵌入向量),维度在几十到几百。嵌入向量随机初始化,以最小化最终损失函数为目标进行训练。嵌入向量是深度神经网络的输入。每个隐层执行如下计算:
其中 l 为层编号;f 为激活函数,一般使用 ReLU;a, b, W 为每层的响应、偏置、模型权值。
Wide 模块和 Deep 模块各自输出的加权和作为预测值,送入同一个 logistic 损失函数进行联合训练,误差反向传播到 Wide 和 Deep 模块,对各自权值进行更新。其中 Wide 模块用 L1 正则化的 FTRL 算法进行优化,Deep 模块使用 AdaGrad 算法进行优化。
系统实现
上述应用推荐流水线包括三个阶段:数据生成,模型训练和模型上线服务,如下图所示:
在数据生成阶段,一段时间内的用户和 app impression 数据用于产生训练数据。每个样本对应一个 app impression,标签为 app acquisition,如果该 app 被安装则值为 1,否则为 0。
模型训练阶段,输入层获取训练数据,产生稀疏和稠密特征以及标签。Wide 模块包括针对 user installed app 和 impression app 的外积变换。Deep 模块未每个类别特征学习一个 32 维嵌入向量,将所有嵌入向量拼接为稠密特征,产生一个将近 1200 维的稠密向量。该向量送入 3 个 ReLU 层,最后是一个 logistic 层。使用的模型结构如下图
该模型在超过 5000 亿样本上训练。每次新样本到来,会重新训练,但会保留旧模型的嵌入层和线性模型权值。
发布模型之前,需要做模型验证,保证不会引起问题。
之后模型会上传到模型服务器进行部署。对每个请求,服务器从 app 检索系统收到一组候选 app 和为每个 app 打分的用户特征,使用 Wide & Deep 模型做inference 对这些候选 app 打分得到推荐值,按从高到低排序,按该顺序呈现给用户。为了保证每个请求响应时间控制在 10 ms 左右,需要用多线程并行处理多个小批量数据。
该系统在 Google Play 线上试验中评估结果表明,Wide & Deep 系统相比单独 wide 模型和单独 deep 模型显著提高了应用购买率。
TensorFlow 上的 Wide & Deep 实现已经开源【3】【4】。
运行 TensorFlow Wide & Deep 例程
首先需要安装 TensorFlow
$ sudo pip install --upgrade tensorflow
切换目录到 TensorFlow 源码
$ cd tensorflow/tensorflow/examples/learn/
$ python wide_n_deep_tutorial.py
运行输出如下:
参考
【1】Wide & Deep Learning for Recommender Systems, http://arxiv.org/abs/1606.07792
【2】TensorFlow Wide & Deep Learning Tutorial, https://www.tensorflow.org/tutorials/wide\_and\_deep
【4】https://github.com/tflearn/tflearn/blob/master/examples/others/recommender\_wide\_and\_deep.py