特征工程是所有机器学习项目的起点。为了训练推荐模型,我们需要准备好模型所需的样本和特征。此外,在进行模型线上推断的时候,推荐服务器也需要线上实时拼装好包含了用户特征、物品特征、场景特征的特征向量,发送给推荐模型进行实时推断。
在“模型实战准备二”这一讲,我们就通过 Spark 处理好了 TensorFlow 训练所需的训练样本,并把 Spark 处理好的特征插入了 Redis 特征数据库,供线上推断使用。
为了在线上做出尽量准确或者说推荐效果尽量好的排序,我们需要在离线训练好排序所用的推荐模型。我们在这一篇中学习和实践的所有深度推荐模型,都是围绕着这个目的展开的。虽然这些深度推荐模型的结构各不相同,但它们的输入、输出都是一致的,输入是由不同特征组成的特征向量,输出是一个分数,这个分数的高低代表了这组特征对应的用户对物品的喜好程度。
在离线训练好模型之后,为了让模型在线上发挥作用,做出实时的推荐排序,我们需要通过模型服务的模块把推荐模型部署上线。因为我们的深度学习模型是基于 TensorFlow 训练的,所以我们采用 TensorFlow Serving 作为模型服务的方式。
模型服务虽然可以做到“猜你喜欢”中电影的排序,但要进行排序,仍然需要做大量的准备工作,比如候选集的获取,召回层的构建,特征的获取和拼装等等。这些推荐逻辑都是在推荐服务器内部实现的。推荐服务器就像推荐系统的线上的心脏,是所有线上模块的核心。

首先,我们来看数据和模型部分。左上角是我们使用的数据集 MovieLens,它经过 Spark 的处理之后,会生成两部分数据,分别从两个出口出去,特征部分会存入 Redis 供线上推断时推荐服务器使用,样本部分则提供给 TensorFlow 训练模型。TensorFlow 完成模型训练之后,会导出模型文件,然后模型文件会载入到 TensorFlow Serving 中,接着 TensorFlow Serving 会对外开放模型服务 API,供推荐服务器调用。接下来,我们再看推荐服务器部分。在这部分里,基于 MovieLens 数据集生成的候选电影集合会依次经过候选物品获取、召回层、排序层这三步,最终生成“猜你喜欢”的电影推荐列表,然后返回给前端,前端利用 HTML 和 JavaScript 把它们展示给用户。
在召回层,我们通过三步生成了最终的候选集。
第一步,我们获取用户的 Embedding。
第二步,我们获取所有物品的候选集,并且逐一获取物品的 Embedding,计算物品 Embedding 和用户 Embedding 的相似度。
第三步,我们根据相似度排序,返回规定大小的候选集。
这里也可以使用局部敏感哈希
经过召回层之后,我们会得到几百量级的候选物品集。最后我们到底从这几百部电影中推荐哪些给用户,这个工作就交由排序层来处理。因为排序的工作是整个推荐系统提高效果的重中之重,在业界的实际应用中,往往交由评估效果最好的深度推荐模型来处理。整个的排序过程可以分为三个部分:
准备线上推断所需的特征,拼接成 JSON 格式的特征样本;
把所有候选物品的特征样本批量发送给 TensorFlow Serving API;
根据 TensorFlow Serving API 返回的推断得分进行排序,生成推荐列表。接下来,我们就详细来讲讲这三步中的实现重点。
首先,第一步的实现重点在于特征样本的拼接。因为实践例子里,我们选用了 NeuralCF 作为排序模型,而 NerualCF 所需的特征只有 userId 和 itemId ,所以特征是比较好准备的。我们下面看一下如何拼接特征形成模型推断所需的样本。我们先把 userId 和 movieId 加入了 JSON 格式的样本中,然后再把样本加入到 Json 数组中。接下来,我们又以 http post 请求的形式把这些 JSON 样本发送给 TensorFlow Serving 的 API,进行批量预估。在收到预估得分后,保存在候选集 map 中,供排序层进行排序。
获取返回得分和排序。 我们先来看一下 TensorFlow Serving API 的返回得分格式。它的返回值也是一个 JSON 数组的格式,数组中每一项对应着之前发送过去的候选电影样本,所以我们只要把返回的预估值赋给相应的样本,然后按照预估值排序就可以了。