2012年11月17日星期六

Netflix 推荐系统:第二部分

原文链接:http://techblog.netflix.com/2012/06/netflix-recommendations-beyond-5-stars.html

在 blog 的第一部分,我们详细介绍了 Netflix 个性化推荐系统的各个组成部分。我们也解释了自从我们宣布 Netflix Prize 后,Netflix 推荐系统是如何变化的。100 万美金的奖金让我们不论在算法创新,还是在品牌宣传和吸引人才加入方面都获得了丰厚的回报。不过,准确的预测电影评分仅只是我们推荐系统的一部分。在本文中,我们将更加深入的介绍个性化推荐技术,讨论当前我们使用的模型、数据,以及我们在这方面的创新方法。

排序
推荐系统的目的是给用户提供一些有吸引力的物品供用户选择。具体的做法是先选择一些候选物品,并对这些物品按照用户感兴趣的程度进行排序。展示推荐结果最常用的方式是组成某种有序列表,而在 Netflix,列表就是一行行的视频。因此,我们需要一个排序模型,利用各种各样的信息,来为每一个用户生成个性化推荐列表。

如果你正在寻找一个能够最大化用户消费的排序函数,那么最显然的基本函数就是物品的热门程度。原因很简单:用户总是倾向于观看大家都喜欢观看的视频。然而,热门推荐是个性化推荐的反义词,它将为每个用户生成千篇一律的结果。因此,我们的目标就是找到一个比热门推荐更好的个性化排序算法,从而满足用户不断变化的口味。

既然我们的目标是推荐用户最可能观看和喜欢的视频。最简单的方法是利用我们估计出的用户对物品的评分来代替物品的热门程度。不过利用预测评分会给用户推荐过于冷门或用户不熟悉的视频,而且不会给用户推荐那些他们可能不会打高分但是会观看的视频。为了解决这个问题,我们并不会只用热门推荐或者预测评分,我们希望能够设计出一个能够平衡这两种因素的排序算法。因此,我们可以将热门程度和预测评分都作为特征,在此基础上构建排序模型。

有很多方法可以用来设计排序算法,比如评分排序方法、配对优化法方法、全局优化方法。举例说明,我们可以设计一个简单的评分排序方法,对视频的热门程度和用户的期望评分进行线性加权:f(u, v) = w1*p(v) + w2*r(u, v) + b,其中 u 表示用户,v 表示视频,p 表示热门函数,r 表示期望评分。这个公式可以通过一个二维空间表示,如下图:

当我们有了这个排序函数后,我们就可以输入一组视频,然后对它们基于评分由高到底进行排序。你可能会很好奇我们是如何选择权重 w1 和 w2 的值(偏移量 b 是一个常量,不影响最终的排序)。换句话说,也就是在我们这个简单的二维模型里面如何确定热门程度更重要?还是期望分值更重要?这个问题至少有两种解决方案:第一种是可以对 w1 和 w2 简单的选一些候选值,然后放到线上通过 A/B Test 的方式找到最优的权重。但是这个方案比较耗时,效率不是很高。第二种是利用机器学习的方法:从历史数据中选择一些正样本和负样本,设计一个目标函数,让机器学习算法自动地为 w1 和 w2 学习一个权重。这种机器学习的方法叫做“Learn to rank (LTR)”,已经在搜索引擎和广告精准投放领域得到了广泛应用。但推荐系统的排序任务有一个重要的区别:排序的个性化。我们不是要获得一个全局的 w1 和 w2 权重,而是要为每个用户都找到一个个性化的值。

你们可能会猜到,除了热门程度和期望评分,我们在 Netflix 推荐系统中还尝试了很多其他的特征。有些没有效果,有些则显著的提升了排序的精度。下图展示了我们通过添加不同的特征和优化机器学习算法,对排序性能的改善效果。

很多监督学习方法都能被用来设计排序模型。其中代表性的有 Logistic 回归,支持向量机(SVM),神经网络或决策树类的算法(GBDT)。另一方面,近几年来许多算法被应用到“Learn to rank”中,比如 RankSVM 和 RankBoost。对于一个给定的排序问题,找到效果最好的算法并不容易。如果你的特征比较简单,那么可以选择一个简单的模型。但是值得注意的是有时候新加的一个特征却不起作用,这是因为模型对它不友好。或者是在一个很好的模型中表现不好,因为你的特征和模型不是很匹配。

数据和模型
在前面关于排序算法的讨论中已经强调了数据和模型对于构建个性化推荐系统的重要性。在 Netflix,我们很幸运的拥有大量相关数据和很多天才工程师,他们通过算法将数据转换为产品。以下是我们在优化推荐系统中用到的一些数据源:
  • 我们有几十亿的用户评分数据,并且以每天几百万的规模在增长。
  • 我们以视频热度为算法基线,有很多方法用来计算热度。我们可以计算不同时间视频的热度,例如:一个小时、一天、或者一周。同时,我们可以将用户按地域划分,计算视频在某地用户的中的热度。
  • 我们的系统每天产生几百万的播放点击,并且包含很多特征,例如:播放时长、播放时刻和设备类型。
  • 我们的用户每天将几百万部视频添加到他们的播放列表。
  • 每个视频都有丰富的元数据:演员、导演、类型、评论、评分。
  • 视频展现方式:我们知道推荐的视频是在什么时间、什么位置展现给用户的,因而可以推断这些因素是如何影响用户的选择。我们也能够观察到用户与系统交互的细节:鼠标滚动、鼠标悬停、点击、以及在页面的停留时间。
  • 社交数据已经成为我们个性化特征中最新的数据源,我们可以知道用户已经关注或评论的好友都在看什么视频。
  • 我们的用户每天产生几百万的搜索请求。
  • 上面提到的数据都来自我们自己的系统,我们也可以挖掘外部数据改善我们的特征,例如:电影票房、影评家的评论。
  • 当然,以上并非全部,还有很多其他的特征,如:人口统计数据、地域、语言、时间上下文等都可以用于我们的预测模型中。

介绍完数据,那什么是模型呢?我们发现,有这些高质量、类型丰富的数据,单一的模型是不够的,我们有必要对模型进行选择、训练、和测试。我们使用了许多类型的机器学习方法:从聚类算法这样的无监督学习方法到分类算法这样的监督学习方法。如果你关注个性化推荐的机器学习算法,那么你应该知道下面这些不太完整的的方法:
  • 线性回归(Linear Regression)
  • 逻辑斯特回归(Logistic Regression)
  • 弹性网络(Elastic Nets)
  • 奇异值分解(SVD : Singular Value Decomposition)
  • RBM(Restricted Boltzmann Machines)
  • 马尔科夫链(Markov Chains)
  • LDA(Latent Dirichlet Allocation)
  • 关联规则(Association Rules)
  • GBDT(Gradient Boosted Decision Trees)
  • 随机森林(Random Forests)
  • 聚类方法,从最简单的k-means到图模型,例如Affinity Propagation
  • 矩阵分解(Matrix Factorization)

消费者法则
丰富的数据来源,度量方式和相关的实验结果,使得我们能够以数据驱动的方式来组织我们的产品。从 Netflix 创立伊始,这种方法就成了公司的基因,我们称之为消费者法则。从广义上说,消费者法则的目标是通过创新让用户获得便利。真正的失败就是没有创新,就像 IBM 的创始人 Thomas Watson 先生所说“如果你想成功,那就不要畏惧失败!”。我们的创新文化要求我们能够高效地通过实验来实践我们的想法,只有这样,我们才能理解这个想法为什么成功或者为什么失败?如此,我们能够专注于改善用户体验,而不是把时间浪费在一些无用的想法上。

那么,在实际工作中,如何贯彻实施这个理念呢?和传统的科学研究不同,我们通过线上的 A/B Testing 来完成对想法的验证:
1. 提出假设
待验证的算法/特征/设计/...,这些待验证的假设将能够提升我们的服务体验,增加用户在网站的停留时间
2. 设计实验
开发一个解决方案或原型。想法的最终效果可能是原型的 2 倍,但一般不会有 10 倍那么多。
考虑依赖和独立的变量、操作、重要性...
3. 执行测试
4. 让数据说话

当我们执行 A/B 测试的时候,我们会跟踪多个维度的指标,但我们最信赖用户的视频播放时长和停留时间。每一次测试通常会覆盖上千名用户,并且为了验证想法的方方面面,测试会分成 2 到 20 份进行。我们一般都是平行进行多个 A/B 测试,这使得我们可以验证一些激进的想法,并且能同时验证多个方法,但最重要的是,我们能够通过数据驱动我们的工作。关于 A/B 测试,可以参考我们以前的技术博客和我们首席产品官 Neil Hunt 在 Quora 上的回复

另一方面,我们面临着一个有趣的问题:如何把我们的机器学习算法整合到 Netflix 以数据为驱动的 A/B 测试文化中。我们的做法是结合离线-在线测试。在线测试之前,我们会进行离线测试,先优化并检验我们的算法。为了评测算法的性能,我们采用了机器学习领域的很多种指标:有排序指标,例如:NDGC(Normalized Discounted Cumulative Gain)、Mean Reciprocal Rank、Fraction of Concordant Pairs。也有分类指标,例如:精准度、查准率、查全率、F-score。我们也使用了来自 Netflix Prize 著名的 RMSE(均方根误差)和其他一些独特的指标,例如:多样性指标。我们跟踪比较这些离线指标和线上效果的吻合程度,发现它们并不是完全一致的。因此,离线指标只能作为最终决定的参考。

一旦离线测试验证了一个假设,我们就准备设计和发布 A/B 测试,从用户的视角证明新的特征的有效性。如果这一步通过了,我们便将其加入到我们的主要系统中,为我们的用户提供服务。下图详细说明了整个创新周期。

这种创新周期是一个极端的例子,我们称之为“前十行结果的马拉松比赛(Top 10 Marathon)”。这是一个为期 10 周、高度专注、高强度的工作,旨在快速检验数十种算法,以改善我们前十行的推荐结果。可以把它看成是一项有考核指标的为期两月的黑客马拉松比赛。不同的团队和个人被邀请到一起贡献想法并编码实现。每周我们会推 6 种不同的算法到线上进行 A/B 测试,并持续评估这些算法的离线和线上指标。最终表现优异的算法会成为我们推荐系统的一部分。

结语
虽然 Netflix Prize 把推荐系统任务抽象为评分预测问题,但评分只是推荐系统众多数据来源的一种,评分预测也只是我们实际解决方案的一部分。在过去几年里,我们把推荐系统任务重新定义为提升用户选择视频、观看视频、享受我们服务、并成为我们回头客的机率。更多的数据可以带来更好的效果,但是为了达到这样的目标,我们需要不断优化我们的方法,进行合理的评测和迭代实验。

为了构建一个创新的个性化推荐平台,仅靠我们的这些研究是不够的,我们系统的提升空间还很大。在 Netflix,我们都很热衷于挑选、观看电影和电视剧,我们专注于研究,并把这份激情转化为提升系统的强大直觉。我们充分利用数据,发现更好的特征、更合理的模型和评测方法,以及弥补现有系统的不足。我们利用数据挖掘和其他的实验方法来验证我们的直觉,并按其优先级逐步实现。与科学实验一样,运气也是很重要的,但是俗话说的好:机会留给有准备的人。最后,最重要的一点是我们需要我们的用户来评测我们最终的推荐结果,因为我们的目标就是为了提升用户在 Netflix 上的体验。

————————————————————————————————————

相比 blog 的第一部分,本文(blog 的第二部分)透露了 Netflix 推荐系统的不少技术细节。可以看到在 Netflix 推荐系统中,除了预测评分,还关注视频的热门程度、多样性等其他的特征,毕竟预测评分的 RMSE 只能说明:当用户看这部电影的时候,会打高分。但这里有个前提,即:“用户要看这部电影”,所以 blog 的最后部分才强调:“我们已经把推荐系统的任务重新定义为提升用户选择视频、观看视频、享受我们服务、并成为我们回头客的机率”。可见,Netflix 推荐算法的优化目标是:吸引用户去看电影,然后给予高的评分。

本文中介绍的排序部分是 Netfix 推荐系统的重要部分,从介绍中可以明确这部分是离线计算完成的,通过机器学习算法离线训练找到不同的特征权重,然后在线通过 A/B 测试验证哪一种特征组合最优、最有效,最终根据最优的特征组合生成用户对物品的兴趣得分(一个综合的得分)。


Netflix 推荐系统:第一部分

Netflix 推荐系统:第一部分

原文链接:http://techblog.netflix.com/2012/04/netflix-recommendations-beyond-5-stars.html

在这篇包含两个部分的博文中,我们将揭开 Netflix 最有价值的资产——推荐系统的面纱。在第一部分,我们将介绍 Netflix Prize 对推荐领域的贡献,Netflix 推荐服务的主要模块,以及推荐服务如何满足网站的商业需求。在第二部分,我们将介绍我们使用的数据和模型,讨论如何将离线的机器学习实验与线上的 A/B 测试相结合。

Netflix Prize 和推荐系统
在 2006 年,我们宣布举办 Netflix Prize,这是一个旨在解决电影评分预测问题的机器学习和数据挖掘的比赛。对于那些能够将我们的推荐系统 Cinematch 的准确率提升 10% 的个人或团队,我们提供 100 万美金的奖励。我们希望通过比赛发现新的方法来改善我们提供给用户的推荐结果,这是我们商业模式的核心部分。当然,我们需要一个比较容易评测和量化的指标:我们选择的评测指标是均方根误差(RMSE,预测评分和真实评分之间的均方根误差)。比赛的要求是打败我们系统 0.9525 的 RMSE 得分,并将其降低到 0.8572 或更低。

比赛开始一年后,Korbell 的团队以 8.43% 的提升赢得了第一个阶段奖。他们付出了超过 2000 个小时的努力,融合了 107 种算法才得到了这份奖金。然后,他们将源代码交给了我们,我们分析了其中两种最有效的算法:矩阵分解(通常被叫做 SVD,奇异值分解)和局限型玻尔兹曼机(RBM)。SVD 取得 0.8914 的 RMSE,RBM 取得 0.8990 的 RMSE,将这两种方法线型融合能将 RMSE 降低到 0.88。为了将这些算法应用到我们的实际系统中,我们必须克服一些限制,例如比赛的数据集是一亿个评分,但实际的线上系统是 50 亿个,并且这些算法的设计并没有考虑用户不断产生的新评分。但最终我们克服了这些困难,并把这两种算法应用到了我们的产品中,而且作为我们推荐引擎的一部分一直被使用到现在。

如果你关注比赛的结果,可能对两年后大奖的归属很感兴趣。这是一项令人印象深刻的工作,数百种预测模型被融合在一起,最终突破了 0.8572 的临界线。我们评测了一些最新的离线算法,但是很遗憾,这些在比赛数据上胜出的算法,到了线上却表现不够出色。因此,我们并没有应用到我们的线上环境。与此同时,我们的关注点也从提升 Netflix 的个性化体验转移到了下一个层级。在下文中,我们将解释为什么要转移关注点?

从美国 DVD 租赁到全球视频流媒体服务
近几年,随着 Netflix 业务的发展,我们对推荐算法的关注点发生了变化。在 Netflix Prize 举办一年后的 2007 年,我们发布了实时流媒体服务。流媒体不仅改变了用户与系统的交互方式,也改变了推荐算法的的可用数据类型。对 DVD 租赁业务来说,目标是帮助用户找到电影,并在接下来的数天或数周内邮寄到用户邮箱。用户从选择电影到观看电影,期间有一个过程,在这个过程中收不到用户的任何反馈。一旦用户不满意,想要更换 DVD,代价会很大,需要花费一天以上的时间,所以用户一般会仔细挑选。而对流媒体用户来说,选一部电影立马就可以观看,甚至可以在很短时间内观看多部电影。同时,我们可以通过统计知道用户是看完了整部电影,还是只看了一部分。

另一个巨大的变化是,流媒体服务从单纯的 Web 网站扩展到了成百上千的不同设备。例如:Netflix 比赛举办后的两年,微软就发布了集成 Roku 播放器的 XBox。仅仅又过了一年,Netflix 发布了 iPhone 客户端。现在,各种 Android 和最新的 Apple TV 上都有 Netflix 的身影。

两年前,我们走向国际,推出了加拿大版本。2011 年,我们的服务扩展到了 43 个拉美国家和地区。最近,我们还登录了英国和爱尔兰。今天,Netflix 已经遍布 47 个国家,共有超过 2300 万的订阅用户。在 2011 年第一季度,这些用户通过上百种不同的设备观看了 20 亿个小时的视频。每天有 200 万部电影和电视剧被观看,并新增 400 万个用户评分。

我们已经在这些新的场景中添加了个性化服务,现在有 75% 的视频观看是与推荐系统有关的。我们取得这样的成绩源于我们不断优化用户体验,通过优化算法,我们改善了用户满意度。下面我们列举一些使用在推荐系统中的技术和算法。

推荐无处不在
经过几年的实践,我们发现尽可能的集成个性化推荐到功能中,会对我们的订阅用户产生巨大的价值。我们的个性化从首页就开始了,包括按行展示的视频,每一行有一个主题,主题揭示了这行视频的内在联系。大多数个性化都是基于挑选行视频的方法,包括哪些行该放那些视频,以及如何对视频进行排序。

以顶部的 10 行为例:我们猜测这是你最可能喜欢的 10 个主题。当然,我们说“你”的时候也包含了你的家人。值得注意的是,Netflix 的个性化是针对每一个家庭,而一个家庭的不同成员会有不同的兴趣和口味。这也就是为什么要选 10 行的原因,你可能会发现这 10 行已经涵盖了对爸爸、妈妈、小孩或者整个家庭的推荐。即使这个家庭只有一个用户,我们也希望能兼顾到这个用户的不同兴趣和情绪。为了做到这一点,我们系统的不仅要提高准确度,还要提高推荐结果的多样性。

Netflix 个性化推荐系统的另一个重要元素是认知(awareness)。我们想让用户知道我们是如何把握他们的喜好。这不仅能让用户信任我们的系统,而且还能鼓励用户提交更多的反馈来帮助我们把推荐做得更好。提升个性化推荐信任度的另一种方法是提供推荐理由,为什么我们要推荐这部电影或电视剧?我们不是因为商业需求给用户推荐,而是基于我们从用户那里获得的信息,包括:用户评分、观看记录、用户好友的推荐等等。

关于基于好友的推荐,我们最近在 47 个服务国家中的 46 个发布了我们的 Facebook 连接组件。这第 47 个就是美国,因为受VPPA(《录像隐私权保护法案》,1998)的影响。朋友了解的内容不仅仅为我们的推荐算法提供了另一个数据来源,也使我们能够以社交圈生成几行新的推荐结果。

我们的推荐服务中最让人印象深刻的一点便是“风格”为主题的几行推荐结果。这里既包含了像“喜剧”这样的大类,也包含了像“时空穿越”这样长尾里面的小类。每一行的展现都考虑了三个方面:选择哪一种风格,选择这个风格里的哪些视频,这些视频如何排序。用户对这个模块的关注度是很高的,当我们把长尾里的类别放到前面的时候,检测到用户的停留时间有明显的增长。其他的诸如新颖性和多样性也是推荐服务考虑的重要因素,以便为用户生成上千种可能的视频组合。

我们也为每一行的选择提供了推荐理由。有些是基于隐式反馈,如:最近观看记录、用户评分和其他交互;有些是基于显示反馈,显示反馈是通过我们邀请用户来做口味偏好调查得来的。

基于相似性的推荐也是我们个性化推荐服务的一个重要部分。相似性是一个很宽泛的概念,描述的对象可以是不同的电影、用户,也可以是评分、视频元信息等。此外,这些相似性可以混合作为其他模块的特征。相似性也可以用在多种场景之中,例如:当用户搜索一部电影或者把一部电影放到播放列表的时候,可以为用户生成“特定风格”的推荐结果,这些结果是基于用户最近关注过的视频。如果你对相似性系统的体系结构感兴趣,想进行一些深入的了解,可以关注我们过去的博客

上述的场景中,包括优选的 10 行推荐、风格推荐、基于相似性的推荐,都要涉及到排序算法,即:选择什么样的顺序来排列每一行中的视频,这是提供有效推荐结果的关键一步。排序系统的目标是在特定的场景下实时的为一组视频找到最佳的排列顺序(简单说就是把用户最感兴趣的视频排列在前)。我们将排序系统的任务分解为:评分、排序、过滤。我们的商业目标是最大化用户的满意度和每月订阅保有量,这实际上等价于最大化用户的视频观看次数。因此,我们优化我们的算法,对那些用户最可能观看和喜欢的视频给最高的分数。

现在我们已经明确的知道 Netflix Prize 的目标是提高电影评分预测的准确度,这只是一个有效的推荐系统众多构成的一种。我们还需要考虑像用户场景、视频流行度、用户兴趣、可解释性、新颖性、多样新和惊喜度等方方面面。为了考虑这些因素,我们要挑选合适的算法。在本文的第二部分,我们将详细的讨论排序问题,深入探讨的我们的数据和模型,以及我们所作的一些创新工作。

————————————————————————————————————
这篇 blog 分两部分,内容很长。第一部分介绍的内容并没有透露任何技术细节,主要是介绍了 Netflix 推荐系统的产品形式(推荐场景)以及 Netflix Prize 带给他们的收益,感觉像是产品经理写写出来的:),但是 blog 的第二部分:排序还是透露了不少技术细节。详见:

Netflix 推荐系统:第二部分

2012年11月8日星期四

什么是好的推荐算法

2009 年 Greg Linden 在 Communication ACM 上发表了一篇文章《What is a Good Recommendation Algorithm》,文章质疑了评分预测推荐算法的意义, 认为 TopN 更有价值和意义。


Netflix 为一个更好的推荐引擎提供了一百万美金的奖励。更好的推荐很显然是非常有价值的。

但是,什么是更好的推荐?为了获得更好的推荐,我们要做些什么?
在 Netflix Prize,更好的含义是非常具体的。评测的标准是:Netflix 用户对电影的真实打分和算法预测的用户打分之间的均方根误差(RMSE)。

比方说我们构建了一个赢得了比赛的推荐引擎。我们的算法使 Netflix 能够将预测值和用户的真实打分之间的误差降低 10% 以上。这是好的推荐算法吗?

根据我们的期望,这可能是一个不错的算法。如果我们希望显示用户喜欢一部电影的程度(用户的评分),同时对于每一部电影预测的评分值要尽可能准确。

然而,这可能不是我们想要的。相对于在产品功能上显示用户喜欢一些特定的电影的程度,用户更在意一些极端的错误情况。例如:将一部你喜欢的电影(真实打分为 4.5 分)预测为一般般(预测打分为 3.5 分)和将一部你觉得一般般的电影(真实打分为 3.5 分)预测为非常一般(预测打分为 2.5 分),对于用户来说,前者显得非常糟糕。

此外,我们经常希望不要去预测用户看了电影之后会给电影什么样的评分,而是希望帮忙找到用户最感兴趣的电影。TopN 推荐为用户挑出最感兴趣的 10 个左右的物品。即使不能预测哪些人会厌恶或者是不喜欢也没关系。TopN 关心的仅仅是挑出用户将会喜欢的 10 个物品。

擅长对所有电影进行预测评分的推荐引擎并不一定能很好的预测排名靠前的 N 部电影。均方根误差(RMSE)加大了对于预测不准的电影评分的惩罚,但对于一些不错的电影,也许我们真正关心的是最小误差。

这和 Web 搜索有相似之处。搜索引擎主要关心查准率(排名前 10 或排名前 3 的相关结果)。当人们看到的结果中缺少他们需要的内容时,他们仅仅只关心召回率。搜索引擎不关心关于文档的错误得分,它只是尽力找到排名前 N 的文档。

进一步探讨这个问题,在推荐系统和搜索引擎领域,人们的看法很容易受到物品以外的因素影响。人们讨厌网速慢的网站,认为结果加载慢的网站要比加载快的网站糟糕很多。对于每个物品提供的信息上的差异(特别是数据丢失或拼写错误)会影响用户感知。结果的呈现,那怕是超链接的颜色,都有可能影响用户的注意力,影响用户查看推荐结果。当推荐引擎能够向用户解释为什么给他推荐的时候,用户才能够更多的信任推荐结果。同时当用户提供了一些新的有价值的信息之后,希望推荐结果能够立即得到更新。多样性是有价值的;重复让人讨厌。新物品吸引眼球,但用户还是能够很粗糙的判断出那些不熟悉和陌生的推荐结果。

最后,我们希望用户能够快乐,满足。但是一个推荐引擎最大限度地减少均方根误差能让人快乐吗?

2012年11月6日星期二

推荐引擎反思

原文链接:http://readwrite.com/2008/02/24/rethinking_recommendation_engines

两年多年(2006年),Netflix 宣布举办推荐引擎大赛——对于能将 Netflix 的推荐系统(Cinematch)精准度提高 10% 的 team 或个人,将给予 1 百万美金的奖励。许多 team 争相参与破解这个问题,并为能使用这些前所未有的可用数据而感到兴奋。最初各支 team 的进步都很可观,但慢慢的算法的可提升空间开始止步不前,最终各支 team 的结果提升大都停留在了 8.5% 左右。

在这篇文章中,我们会讨论推荐引擎的改善不仅仅是一个算法问题,还是一个呈现问题。以过滤代替推荐,同时交付给用户的时候不要设置过高的期望,会比处理很多数据快很多。

构建一个推荐引擎是一项复杂的工作,这个我们一年前讨论过。除了技术上挑战难度的增加,也有一些根本的心理问题:人们希望推荐,如此一来,我们什么时候给用户推荐呢?也许一个更大的问题是:当用户收到不是他们想要的推荐的时候,用户该如何宽容我们?

推荐引擎遗传学
所有推荐引擎都在尝试解决以下问题:给定一个特定用户的评分,以及整个用户群,想出将会喜欢的新物品。有许多算法能用来解决这个问题,但是所有这些算法都集中于三个要素:个人的,社会化的和基本的。
  • 个性化的推荐:基于用户过去的行为进行推荐
  • 社会化的推荐:基于相似用户过去的行为进行推荐
  • 基于 item 的推荐:基于 item 本身(如内容相似形)的推荐
  • 前面三种方法的混合推荐

社会化的推荐也被称为协同过滤 —— 喜欢 X 的用户也喜欢 Y。例如:喜欢《指环王》的用户很可能会喜欢《龙骑士》和《纳尼亚传奇》。这个推荐方法的问题在于现实中难以对人们的口味进行一个简单的分类。如果两个用户对于科幻电影有着共同的喜好,并不意味着他们在戏剧和悬疑剧上也有着相似的喜好。从遗传学的角度来看待这个问题是一个比较好的方法。很多时候我们遇到人们有一些我们熟悉的特征,这些特征在其他人身上也能看到。例如:眼睛可能很面熟,或者是嘴唇,但是他们确是完全不同的人种。

另一种推荐是基于 item 的推荐。这种推荐系统最好的例子的就是 Pandora 的音乐推荐服务。Pandora 为每首音乐标注了 400 多个特征,这些特征可以形象的称为“音乐基因”,Pandora 就是基于这些“音乐基因”进行工作的,它会根据这些“基因”自动匹配音乐。然而这种方法在算法调优上有很大的挑战,并且想要把类似的方法应用在其他类型的垂直网站也是一件非常有挑战的事情。例如:对于电影,你需要考虑从哪些维度评测一部电影,从导演,演员,剧情;还有一些比较模糊的如音效,场地,灯光,摄影等等。这些电影一定会有的东西,但却很复杂。

车库里的家伙
推荐问题的复杂性在于它广泛的可能性。具体说就是:对于某一个具体的人,很难准确的确定哪一些基因适用于他,很难指出一部电影或一首音乐的哪些基因让我们给它打 5 分。转变工程师的思维是很困难的。这就是为什么《连线》文章中强调参赛者们使用了一种非常罕见的技巧来使他的算法有效运行。

这个家伙来自伦敦的 Gavin Potter,网名 Guy In The Garage(车库里的家伙),他的方法依赖了人的惰性。很显然,对电影的评分依赖于我们之前看过的电影的评分。例如:如果你连续观看了三部电影,并给它们打了 4 分,那么当你看到下一部稍好一点的电影时,你将会打 5 分。反之,如果你连续给三部电影打了 1 分,那么当你看到和刚刚一样的一部 5 分的电影时,你可能只会打 4 分。

当你还在思索这是不是真的的时候,你将会发现这类算法现在已经占据了第 5 类推荐算法的位置,并在不断的发展,情况远远比其他的算法好。通过一点心理学的知识来强化数学公式无疑是一个好办法,这也是我们接下来要谈及的问题。

用过滤取代推荐
有多少次这样的情况发生在你身上:朋友向你推荐了一部电影或一个宾馆,你兴高采烈的去了,但却败兴而归?很多!很显然,炒作使得期望的门槛变高了。用数学术语讲,这种类型的错误被称为假阳性。现在考虑这样的一种情况,如果你的朋友不是给你推荐一部电影,而是告诉你你肯定不会喜欢的一部电影,这样你就不用花钱去租它了。这种情况下会发生什么呢?

这种情况会带来什么坏处呢?答案是不会有什么坏处,因为你很可能就不会去看这部电影。但即使你真的看了这部电影并且喜欢这么电影,你也不会有负面的情绪。(相反你会感到很惊喜,哇塞!原来这部电影这么棒)。这个例子充分说明了用户对于假阳性和假阴性错误的不同反应。假阳性使我们感到沮丧,但假阴性不会。以过滤代替推荐的思想就是为了平衡这样的一种现象。

当 Netflix 作出推荐时,它总会有一定的出错比例。或早或晚,错误将会出现,推荐一部你不喜欢的电影给你。如果推荐系统不是这样做,而是推荐给你新电影,同时有一个按钮:把那些你不喜欢的电影过滤掉。同样的算法,但是感受确是不一样的。

实时过滤
在实时新闻的时代,这种想法变得越发重要和强大。我们越发需要对新的信息进行连续的过滤。就像我们每天都用 RSS 阅读器过滤文章一样。从新闻流的角度来看这个世界,过去的事情都是不相关的。我们不需要推荐,因为我们已经订阅了很多的内容。我们需要过滤掉噪音。需要一个算法说:“嗨,你一定不会喜欢这个东西的,把它隐藏掉吧!”。

如果机器能做到那样,积极地把我们周围无用的信息扔开,那么剩下的我们就可以自己来处理了。借鉴邮件过滤系统,如果我们身边的工具都有一个按钮:“帮我把这个过滤掉”,并且这个工具可能还是默认启动的,那么我们就能做更多的事情了。

结论
构建一个完美的推荐引擎是一项非常复杂的任务。不管使用什么方法,协同过滤或基于物品属性的推荐,都是不会被原谅的商业工具,假阳性的错误会让用户逐渐流失。可能把心理学应用于这个问题,可以让用户懂得感激这些复杂算法所做的事情。如果系统过滤掉那些我们一定不喜欢的东西,而不是给我们推荐一堆东西,我们可能会更加宽容和给予更多的理解。

现在请告诉我对于推荐引擎你的一些经验。你的推荐引擎真的工作的很好吗?你是否会用开放的过滤取代推荐?除了电影和新闻,你会在哪些地方使用过滤器?

2012年10月27日星期六

QCon 杭州2012参会小记

2012年10月27日,QCon 杭州2012 日程的最后一天,我幸运的从同事那里得到了一张讲师的门票,然后以听者的身份去参会。一共听了 6 场分享,如下:

facebook 的《HBase Solutions at Facebook》,演讲的 Nicolas 是位大帅哥,声音很清脆。HBase 在 facebook 的多个重要大型系统中得到深入应用,包括著名的 Message系统。只可惜我对 HBase 没有实战经验,所以听的很懵,但是对于 facebook 的数据规模还是相当震撼,根据 PPT 的介绍,目前 facebook 运行在 HBase 上的 Message 系统每月处理 1800 亿条 P2P 消息。
HBase Solutions at Facebook

毕玄的《T4:淘宝私有云》讲的的很实际,介绍了 T4 虚拟化方案的问题和选择,资源分配、动态调整(平滑扩容)、平衡、监控、自动化,还特别分享了 T4 实践中遇到的一些问题以及未来的优化思路。T4 着力于大幅度降低淘宝的运维成本,演讲中多次提到为了解决某些问题,不得不修改内核,大牛做事就是够彻底。因为 T4 是自己家的东西,所以以后还会有很多机会进行了解。

Dave Thomas(《程序员修炼之道》作者)的《敏捷,以及敏捷的腐化》,《Old Ways,New Ways》讲的都非常质朴,演讲是从给自我打标签开始的,俨然一开始就上升到人生哲学的高度。Dave 特别提到敏捷要因时而变,切勿盲目套用方法论,要多实践,多操练,不要害怕犯错,要让敏捷成为一种习惯和不用思考的东西。此外还提到 1W 小时练习定理,并告诫在场的开发者系统开发要注重设计架构和代码重构,切勿重复造轮子。能够听到 Dave 现场布道真的很幸运,Dave 还专门讲到做事情要 be a verb, not a noun…,大师就是大师,大道至简。另外不得不提的是 Dave 做演讲相当个性,直接光脚上阵演讲,哈哈~
Dave Thomas
Old Ways,New Ways

大众点评这次的主题是关于网站监控平台的构建,大众点评的监控系统出师于 eBay 的 CAL 监控系统,所以名字来也很接近,叫 CAT。CAT 集成了应用监控、运维、容量规划及业务数据监控于一身。CAT 的监控元数据是直接从 App 传回监控系统的,不会存储在 App 本地,同时 CAT 抽象了一套非常简单的 API,方便 App 接入,不过对于元数据的模型没有太详细的介绍。

最后一场听的是方越的《从另一个角度看世界》。方越是一名开源项目开发者,任职于 Red Hat,是多个 Apache 项目的 PMC Member 和 Committer,团队里的15名成员分布在8个不同国家,最让人羡慕的是他们都是在家里上班,通过全球协作的方式进行工作(太先进了)。“周围人的水平决定了你的水平;做对的事情比把事情做对更重要;你的时间在哪里,你的成就就在哪里”,方越的 PPT 不多,但是让人印象深刻。

能够参加 QCon 真的很幸运,听到的东西虽然不能马上运用在工作中,但是他们的曲折经历,踏过的坑、迈过的坎,积累的经验值得我们学习,总之获益匪浅。另外,参会的一个特别感受就是:男厕队列繁忙,响应缓慢,而女厕却是资源异常空闲~~

2012年6月8日星期五

Mahout 之(四)Taste的架构和部署Demo

Taste简介
Taste是Apache Mahout提供的一个协同过滤算法的高效实现,它是一个基于Java实现的可扩展的,高效的推荐引擎。Taste既实现了最基本的基于用户的和基于内容的推荐算法,同时也提供了扩展接口,使用户可以方便的定义和实现自己的推荐算法。同时,Taste不仅仅只适用于Java应用程序,它可以作为内部服务器的一个组件以HTTP和Web Service的形式向外界提供推荐的逻辑。Taste的设计使它能满足企业对推荐引擎在性能、灵活性和可扩展性等方面的要求。

Taste的架构
Taste Architecture

Taste由以下五个主要的组件组成:

  • DataModel:DataModel是用户喜好信息的抽象接口,它的具体实现支持从任意类型的数据源抽取用户喜好信息。Taste默认提供JDBCDataModel和FileDataModel,分别支持从数据库和文件中读取用户的喜好信息。
  • UserSimilarity和ItemSimilarity:UserSimilarity用于定义两个用户间的相似度,它是基于协同过滤的推荐引擎的核心部分,可以用来计算用户的“邻居”,这里我们将与当前用户口味相似的用户称为他的邻居。ItemSimilarity 类似的,计算内容之间的相似度。
  • UserNeighborhood:用于基于用户相似度的推荐方法中,推荐的内容是基于找到与当前用户喜好相似的“邻居用户”的方式产生的。UserNeighborhood定义了确定邻居用户的方法,具体实现一般是基于 UserSimilarity 计算得到的。
  • Recommender:Recommender是推荐引擎的抽象接口,Taste中的核心组件。程序中,为它提供一个DataModel,它可以计算出对不同用户的推荐内容。实际应用中,主要使用它的实现类 GenericUserBasedRecommender或者GenericItemBasedRecommender,分别实现基于用户相似度的推荐引擎或者基于内容的推荐引擎。

运行Demo
1. 下载mahout-0.2
    svn checkout https://svn.apache.org/repos/asf/mahout/branches/mahout-0.2/
2. 准备数据源,从Grouplen下载"1 Million MovieLens Dataset",链接:http://www.grouplens.org/system/files/ml-1m.zip
3. 解压数据源压缩包,将movie.dat和ratings.dat拷贝到/mahout-0.2/examples/src/main/java/org/apache/mahout/cf/taste/example/grouplens
4. 回到/mahout-0.2/examples目录下,运行"mvn install"
5. 进入/mahout-0.2/taste-web目录,拷贝 ../examples/target/grouplens.jar到 taste-web/lib目录
    cp ../examples/target/grouplens.jar ./lib
6. 在/mahout-0.2下运行"mvn package"
7. 将 taste-web/target 目录下生成的war包“mahout-taste-webapp-0.2.war”拷贝到Tomcat的webapp下
8. 启动Tomcat,在/bin目录运行“./startup.sh”
9. 访问“http://localhost:8080/mahout-taste-webapp-0.2/RecommenderServlet?userID=1”
Build Demo

2012年4月19日星期四

Hadoop基础篇之(一)安装部署

1. 下载hadoop
从http://hadoop.apache.org/common/releases.html下载一个hadoop的发行版本,此处使用wget方式,wget http://mirror.bjtu.edu.cn/apache/hadoop/core/hadoop-1.0.2/hadoop-1.0.2.tar.gz

解压下载的压缩包到 home/<your dictionary>/hadoop
tar –zvxf hadoop-1.0.2.tar.gz

2. set JAVA_HOME / PATH
打开.bash_profile文件
$ vi ~/.bash_profile

export JAVA_HOME=<path-to-java>
export PATH=$PATH:<path-to-java>/bin

保存退出,查看配置是否成功
$ echo $JAVA_HOME
$ echo $PATH

HADOOP_HOME是可选的,并且在hadoop-1.0.2下,配置HADOOP_HOME会抛一个警告:“Warning: $HADOOP_HOME is deprecated.”,所以实际配置中,可以不用设置HADOOP_HOME

检查配置是否成功
$ bin/hadoop
打印以下内容说明配置成功
Usage: hadoop [--config confdir] COMMAND
where COMMAND is one of:
  namenode -format     format the DFS filesystem
  secondarynamenode    run the DFS secondary namenode
  namenode             run the DFS namenode
  datanode             run a DFS datanode
  ...

伪分布式模式的配置

3. 配置相关配置文件
(1) conf/core-site.xml:
<configuration>
  <property>
    <name>fs.default.name</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>

(2) conf/hdfs-site.xml:
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>

(3) conf/mapred-site.xml:
<configuration>
  <property>
    <name>mapred.job.tracker</name>
    <value>hdfs://localhost:9001</value>
  </property>
</configuration>

4. 免密码ssh设置
现在确认能否不输入口令就用ssh登录localhost:
$ ssh localhost

如果不输入口令就无法用ssh登陆localhost,执行下面的命令:
$ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

5. 执行
(1) 格式化分布式文件系统:
$ bin/hadoop namenode -format

(2) 启动Hadoop守护进程:
$ bin/start-all.sh
Hadoop守护进程的日志写入到 ${HADOOP_LOG_DIR} 目录 (默认是 ${HADOOP_HOME}/logs)

(3) 运行示例程序:
将输入文件拷贝到分布式文件系统:
$ bin/hadoop fs -mkdir input
$ bin/hadoop fs -put conf input
运行发行版提供的示例程序:
$ bin/hadoop jar hadoop-examples-*.jar grep input output 'dfs[a-z.]+'

查看输出文件:
将输出文件从分布式文件系统拷贝到本地文件系统查看:
$ bin/hadoop fs -get output output
$ cat output/*

浏览NameNode和JobTracker的网络接口,它们的地址默认为:
NameNode - http://localhost:50070/
JobTracker - http://localhost:50030/

6. 停止守护线程
完成全部操作后,停止守护进程:
$ bin/stop-all.sh

7. 实战——WordCount
WordCount在Hadoop主目录下的java程序包hadoop-examples-1.0.2.jar 中
重新启动守护线程:
$ bin/start-all.sh
建立一个新目录,此处命名为testfile
$ bin/hadoop fs -mkdir testfile
在本地/home/usr/hadoop/tmp目录中建立一个txt文件test.txt,并填写如下内容:
hello world
hello I am a engineer
world peace
然后将此文件copy到分布式文件系统的testfile里:
$ bin/hadoop fs -put /home/usr/hadoop/tmp/test.txt testfile

执行wordcount程序:
$ bin/hadoop jar hadoop-examples-1.0.2.jar wordcount testfile output
注意:在执行上面的命令之前,需要先删除分布式文件系统中的output目录,因为hdfs中job之间的输出目录是不允许重复的,否则会抛如下的错误:
‘org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory output already exists’
删除命令:
$ bin/hadoop dfs -rmr output

查看执行结果:
$  bin/hadoop dfs -cat output/part-r-00000
I           1
a           1
am          1
engineer    1
hello       2
peace       1
world       2