融会贯通:Sparrow RecSys中的电影相似推荐功能是如何实现的?

来自CloudWiki
跳转至: 导航搜索

前言

你好,我是王喆。

课程进行到这里,推荐系统架构的大部分知识点,包括特征工程、Embedding 模型,到推荐服务的搭建,线上推荐过程的实现,我们都已经学习并且实践过了。如果你坚持跟着我一起学下来的话,可以说已经是“武功小成”了。

为了帮你巩固所学,今天,我就带你从头到尾地实现一个完整的推荐功能,相似电影推荐,来帮助你打通推荐系统的“任督二脉”。

“清点技能库”,看看我们已有的知识储备有哪些

在开始实现相似电影推荐功能之前,我想先带着你一起清点一下自己的技能库。我喜欢把推荐的过程比喻成做菜的过程,接下来,我就按照做菜的四个关键步骤,带你回顾一下前面学过的重点知识。

第一步,准备食材

准备食材的过程就是我们准备推荐所需特征的过程。在特征工程篇中,我们不仅学会了怎么挑选“食材”,怎么处理“食材”,而且还实践了“备菜”的高级技能 Embedding 技术。具体来说就是,我们能够利用物品序列数据,通过 Item2vec 方法训练出 Embedding,也能够使用 Deep Walk 和 Node2vec 把图结构数据生成 Graph Embedding。

总的来说,因为 Embedding 技术的本质就是利用了物品之间的相关性,所以 Embedding 是做好“相似推荐”这盘菜的关键。

第二步,食材下锅

备好了菜,在正式开炒之前,我们肯定要把食材下锅。在推荐系统中“食材下锅”的过程有两个:一是把线上推荐所用的特征存储到数据库中,在之前的课程中我们已经实践过使用 Redis 作为特征数据库的方法,另一个是把模型部署到模型服务模块,我们也已讲过了预训练 Embedding,Embedding 加轻量级线上模型,TensorFlow Serving 等多种模型服务方式,这节课我们将采用预训练 Embedding 的方式进行模型服务。

第三步,做菜技术

“做菜的技术”说的是推荐服务器线上推荐的整个流程是否合理。那回到推荐系统中就是指,召回层要快速准确,模型排序部分要精确。这些具体的实现都影响着最终的推荐效果。

对于召回层来说,我们已经学过单策略召回、多路召回和基于 Embedding 的召回。对于排序来说,我们会主要利用 Embedding 相似度来排序,后续我们还会学习基于多种推荐模型的排序。

最后是菜品上桌的过程

也就是把推荐的结果呈现给用户的过程。这节课,我会带你一起实现这个过程。提前“剧透”一下,在 Sparrow Recsys 中,我们会先利用 JavaScript 异步请求推荐服务 API 获取推荐结果,再利用 JavaScript+HTML 把结果展现给用户”。因为,这一部分内容不是推荐系统的重点,所以我们这里只要做到界面清爽、逻辑清晰就可以了。相信到这里,各位“大厨”已经准备好了所要用到的技能,下面就让我们一起来实现 Sparrow RecSys 中的相似电影推荐功能吧!

如何实现相似电影推荐功能?

在正式开始相似电影推荐功能之前,我们先来看看我总结的 Sparrow Recsys 相似电影推荐功能的详细技术架构图。细心的你可能已经发现了,这个架构图就是 Sparrow Recsys 架构的精简版。因为我们还没有学习深度学习推荐模型和模型评估的相关知识,所以把重点聚焦在已经学过的知识上就可以了。

Python21080210.png

图1 Sparrow Recsys 相似电影推荐功能的技术架构图

接下来,我就结合这个技术架构图,带你一步步地实现其中的每一个模块。并且,我还会给你讲解一些项目中没有实现的其他业界主流方法,如果你还学有余力,希望你能抓住这个机会,来扩展一下自己的知识面。

1. 数据和模型部分

数据和模型部分的实现,其实和我们第 8 讲讲的 Embedding 的实战思路是一样的,我们可以选用 Item2vec、Deep Walk 等不同的 Embedding 方法,来生成物品 Embedding 向量。考虑到大数据条件下,数据处理与训练的一致性,在 Sparrow Recsys 中,我们会采用 Spark 进行数据处理,同时选择 Spark MLlib 进行 Embedding 的训练。这部分内容的代码,你可以参考项目中的_com.wzhe.sparrowrecsys.offline.spark.embedding.__Embedding_对象,它定义了所有项目中用到的 Embedding 方法。

对于一些比较复杂的 Embedding 方案,比如特征种类很多,网络结构也更多样化的 Embedding 模型,业界也多采用 Spark 进行原始数据处理,生成训练样本后交由 TensorFlow、PyTorch 训练的方案。

但是不论训练平台是怎样的,Embedding 方法的产出都是一致的,就是物品 ID 对应的 Embedding 向量。那为了方便线上服务使用,我们还需要在生成 Embedding 后,把它们存入某个高可用的数据库。Sparrow Recsys 选择了最主流的内存数据库 Redis 作为实现方案,这一部分的具体实现,你可以参照com.wzhe.sparrowrecsys.offline.spark.embedding.Embedding对象中 trainItem2vec 函数的这一部分的具体实现,你可以参照com.wzhe.sparrowrecsys.offline.spark.embedding.Embedding对象中 trainItem2vec 函数的 Redis 存储操作。当然,业界也会使用 Cassandra+ 缓存,RocksDB 等不同的存储方案来实现 Embedding 向量的高效读取,但我们现阶段只要学会 Redis 存储和读取操作就够用了。

到这里,Redis 成为了连接线下和线上的关键节点,那我们的线上服务部分又是怎么利用 Redis 中的 Embedding 数据进行相似电影推荐的呢?

2.线上服务部分

线上服务部分是直接接收并处理用户推荐请求的部分,从架构图的最左边到最右边,我们可以看到三个主要步骤:候选物品库的建立、召回层的实现、排序层的实现。我们逐个来讲一讲。

首先是候选物品库的建立。Sparrow Recsys 中候选物品库的建立采用了非常简单的方式,就是直接把 MovieLens 数据集中的物品数据载入到内存中。但对于业界比较复杂的推荐业务来说,候选集的选取往往是有很多条件的, 比如物品可不可用,有没有过期,有没有其他过滤条件等等,所以,工业级推荐系统往往会通过比较复杂的 SQL 查询,或者 API 查询来获取候选集。

第二步是召回层的实现。我们在第 11 讲曾经详细学习了召回层的技术,这里终于可以学以致用了。因为物品的 Embedding 向量已经在离线生成,所以我们可以自然而然的使用 Embedding 召回的方法来完成召回层的实现。同时,Sparrow Recsys 也实现了基于物品 metadata(元信息)的多路召回方法,具体的实现你可以参照com.wzhe.sparrowrecsys.online.recprocess.SimilarMovieProcess类中的 multipleRetrievalCandidates 函数和 retrievalCandidatesByEmbedding 函数。

第三步是排序层的实现。根据 Embedding 相似度来进行“相似物品推荐”,是深度学习推荐系统最主流的解决方案,所以在 Sparrow Recsys 中,我们当然也是先根据召回层过滤出候选集,再从 Redis 中取出相应的 Embedding 向量,然后计算目标物品和候选物品之间的相似度,最后进行排序就可以了。这里“相似度”的定义是多样的,可以是余弦相似度,也可以是内积相似度,还可以根据你训练 Embedding 时定义的不同相似度指标来确定。因为在 Word2vec 中,相似度的定义是内积相似度,所以, 这里我们也采用内积作为相似度的计算方法。同样,具体的实现,你可以参照 com.wzhe.sparrowrecsys.online.recprocess.SimilarMovieProcess 类中的 ranker 函数。

经历了这三个主要的线上服务步骤,Sparrow Recsys 就可以向用户返回推荐列表了。所以接下来,我们要解决的问题就是,我们要解决的问题就是,怎么把这些结果通过前端页面展示给用户。

3.前端部分

Sparrow Recsys 的前端部分采用了最简单的 HTML+AJAX 请求的方式。AJAX 的全称是 Asynchronous JavaScript and XML,异步 JavaScript 和 XML 请求。它指的是不刷新整体页面,用 JavaScript 异步请求服务器端,更新页面中部分元素的技术。当前流行的 JavaScript 前端框架 React、Vue 等等也大多是基于 AJAX 来进行数据交互的。

但前端毕竟不是我们课程的重点,你知道我在上面提到的基本原理就可以了。如果你已经在本地的 6010 端口运行起了 Sparrow Recsys,那直接点击这个链接:http://localhost:6010/movie.html?movieId=589 , 就可以看到电影《终结者 2》的详情页面和相似电影推荐结果了(如图 2)。


Python21080211.png

图2 终结者2的相似电影推荐结果

相似电影推荐的结果和初步分析

到这里,我相信你已经串联起来了 Sparrow Recsys 相似电影推荐的所有实现,看到了推荐结果。那么问题来了,推荐结果的好坏到底是如何判断的呢?关于这个问题,我们也会在后面的“模型评估篇”中进行系统性的学习。不过,这里我也想先跟你聊聊这个话题,让你对它有一个大体认识,这对你建立后续的模型评估体系是非常有帮助的。

首先提醒你的是,Sparrow Recsys 开源项目中自带的 MovieLens 数据集是经过我采样后的缩小集,所以基于这个数据集训练出的模型的准确性和稳定性是比较低的。如果你有兴趣的话可以去MovieLens 官网选择 MovieLens 20M Dataset 下载并重新训练,相信会得到更准确的推荐结果。

其次,针对相似物品推荐这个推荐场景,我们其实很难找到一个统一的衡量标准。比如,你能说出《功夫熊猫》这部电影是跟《玩具总动员》更相近,还是跟《飞屋环游记》更相近吗?好在,工程师们还是总结出了一些有效的评估方法。这里,我挑出了三个最常用的来给你讲讲。

方法一:人肉测试(SpotCheck)。 在一种 Embedding 结果新鲜出炉的时候,你作为创造它们的工程师,应该第一时间做一个抽样测试,看一看基于 Embedding 的相似推荐结果是不是符合你自己的常识。比如说,我在 Embedding 训练完之后,随便在 Sparrow Recsys 中翻了翻,随便在 Sparrow Recsys 中翻了翻,看到了两个页面,一个是儿童电影《Free Willy》(《人鱼童话》)的相似电影推荐页面(图 3 左),另一个是著名动画电影《Toy Story》(《玩具总动员》)的相似电影推荐页面(图 3 右)。

Python21080212.png

图3 随机测试

直观上来看,《Free Willy》的推荐结果就非常不错,因为你可以看到相似电影中都是适合儿童看的,甚至这些电影都和动物相关。但是《玩具总动员》就不一样了,它的相似电影里不仅有动画片,还有《真实的谎言》(《True Lies》)、《阿甘正传》这类明显偏成人的电影。这明显不是一个非常好的推荐结果。

为什么会出现这样的结果呢?我们来做一个推测。事实上,《玩具总动员》本身是一部非常流行的电影,跟它近似的也都是类似《真实的谎言》、《阿甘正传》这类很热门的电影。这就说明了一个问题,热门电影其实很容易跟其他大部分电影产生相似性,因为它们会出现在大多数用户的评分序列中。

针对这个问题,其实仅利用基于用户行为序列的 Embedding 方法是很难解决的。这需要我们引入更多内容型特征进行有针对性的改进,比如电影类型、海报风格,或者在训练中有意减少热门电影的样本权重,增大冷门电影的样本权重等等。总的来说,遇到推荐结果不合理的情况,我们需要做更多的调查研究,发掘这些结果出现的真实原因,才能找到改进方向。

方法二:指定 Ground truth(可以理解为标准答案)

虽然我们说,相似影片的 Ground truth 因人而异。但如果只是为了进行初步评估,我们也可以指定一些比较权威的验证集。比如,对于相似影片来说,我们可以利用 IMDB 的 more like this 的结果去做验证我们的相似电影结果。当然要补充说明的是,要注意有些 Ground truth 数据集的可用范围,不能随意在商业用途中使用未经许可的数据集。

方法三:利用商业指标进行评估

既然相似影片比较难以直接衡量,那我们不如换一个角度,来思考一下做相似影片这个功能的目的是什么。对于一个商业网站来说,无非是提高点击率,播放量等等。因此,我们完全可以跃过评估相似度这样一个过程,直接去评估它的终极商业指标。

举个例子,我们可以通过上线一个新的相似电影模型,让相似电影这个功能模块的点击率提高,假设提高了 5%,那这就是一个成功的模型改进。至于相似电影到底有没有那么“相似”,我们反而不用那么纠结了。

小结

这节课,我们使用 Embedding 方法准备好了食材,使用 Redis 把食材下锅,做菜的步骤稍微复杂一点,分为建立候选集、实现召回层、实现排序层这 3 个步骤。最后我们用 HTML+Ajax 的方式把相似电影推荐这盘菜呈现出来。

既然有做菜的过程,当然也有品菜的阶段。针对相似物品推荐这一常见的功能,我们可以使用人肉测试、Ground truth 和商业指标评估这三种方法对得到的结果进行评估。也希望你能够在实际的业务场景中活学活用,用评估结果指导模型的下一步改进。

我希望,通过这节课的总结和实战,能让你融会贯通的厘清我们学过的知识。所以我把你需要掌握的重要知识点,总结在了一张图里,你可以利用它复习巩固。


好了,那到这里,我们线上服务篇的内容就全部结束了。通过这一篇的学习,我相信你已经清楚了推荐系统的全部技术架构,以及深度学习核心技术 Embedding 的运用方法。

但盛宴还未开始,下一篇我们将进入深度推荐模型的学习和实践。我曾经说过,深度推荐模型是深度学习推荐系统这个王冠上的明珠,正是它对推荐模型的革命,让深度学习的浪潮席卷推荐系统领域。希望你再接再厉,让我们一起把这颗明珠摘下吧!

课后思考

刚才我说到,《玩具总动员》的相似电影推荐结果并不好,我认为可能是因为热门电影的头部效应造成的。你认同这一观点吗?你觉得还有其他可能的原因吗?如果让你去做一些 Embedding 方法上的改进,你还有什么好的想法吗?