就像Google不会公布其搜索排名算法一样,大多数公司都不会对外公布涉及排名的算法。因为这个算法一旦公布,总会有人去做SPAM。同理,我烧 网的热文排序算法也不会对外公布,我们只是向用户提供一些基本的原则。这一算法被我们命名为SmartHot,是在2009年底我烧网改版时设计的,到现 在已有一年多时间。这期间我们对算法进行了数次改进和调整,也正因如此,才使得我有机会可以将当时的一些设计思路拿出来与大家分享。希望通过对这一算法的 产生过程的介绍,给更多的网站、程序设计人员一些启发,同样也希望大家踊跃拍砖,共同学习、共同进步。
我烧网是一个以博客内容聚合为基 础的阅读社区,像其他内容聚合网站一样,由于存在海量的内容,因此对内容的提炼尤为重要。在改版之前,我们一直通过人工方式推荐内容,但人工方式有两个缺 陷:一个是人力成本过高,另一个是推荐出的内容往往是编辑喜欢而用户不一定喜欢的。这次改版,我们希望采用众包的方式,让用户为用户推荐内容,从而节约团 队的人力成本,同时也能够真正把用户喜欢的内容推荐出来,使用户的互动更加活跃。这个推荐列表就是后来的我烧网热文。从访问统计来看,热文页的PV也远远 高于其他页面。而热文的幕后,就是靠这套SmartHot算法支撑的。
在提出要研制一套文章排序算法之前,我们先是对这次改版进行了一 个规划,整个规划有一个中心思想,那就是让更多的用户参与到网站中来,通过用户的参与提高网站内容的质量,然后再把高质量的内容反馈给用户。 SmartHot要为这一中心思想服务。后来的实践证明,在这套算法的支撑下,我烧网实现了飞速的发展,最终带来的主要影响有三:一是更多用户参与到社区 的活动中来,二是内容质量大幅提升,三是节约了大量的人力成本而且带来了比人工更好的效果。在这一中心思想的指导下整个团队开始了对热文排序算法的思考。
提出问题
首先,以用户行为为推荐的基础并不是说让用户去推荐,因为用户做太多贡献会使网站失去活力。更加明智的做法应该是基于用户的各种单一操作,通过数据挖掘 找出最值得推荐的文章。这样就细化成了一个数据挖掘的问题—通过对用户行为数据的挖掘找到好的文章再推荐给全部的用户。面对这样的问题,团队提出了很多种 解决思路。最简单、最常规的当然就是按照文章的浏览量来排序。然而无数经历都证明了这不是一个合理的算法,浏览量的高低只能代表访问者被标题、图片等吸引 的程度。如果想要了解用户对文章的真实感受,就得用添柴数(我烧网中用户通过为文章添柴来表达对其的喜爱)来排序。但是,无论是浏览数还是添柴数,都是累 计的数字,最终必然会因马太效应产生严重问题,使排在前面的文章永远排在前面,新文章无法进入前列。这时,将二者结合的思路出现了。
我 们把添柴数作为正向的参数,把浏览量作为反向的参数。如果一篇文章被用户浏览了100次、添柴10次,那么就计为0.1分(10/100=0.1)。这就 意味着,浏览量越大,最终的排序分值越小!这是一个很具突破性的想法,因为我们知道很多同行都把浏览量作为正向参数给文章排序。问题貌似解决了,但是新的 问题也随之产生:大多数用户只看不操作,换句话说很多用户即便觉得文章很不错,也懒得去添柴,这样势必导致很多好的文章因为浏览量大反而失去排在前面的机 会。几番斟酌,我们最终决定放弃浏览量这个依据—文章得分以添柴数为基础。
在我烧网中,对文章的其他操作如评论、分享等也类似于添柴, 被赋予一定的分数计入文章的得分。当然,单纯按得分来排序也是不合理的,这与直接用添柴数排序的结果是一样的。为了保证用户喜欢的新文章能够永远排在用户 同样喜欢的旧文章之前,我们必须要引入时间参数,由用户操作的得分和文章的发布时间共同决定文章的排序值。添柴数(包括其他用户操作)作为正向的参数,文 章发布时间作为反向的参数,最终得到的排序用的排序值与文章得分成正比、与发布时间成反比。于是又一重要原则产生了—文章排序值随时间的前进而衰减。
分析到这里,团队里的每一个人都欣喜若狂:我们知道算法即将完成了。但是一个尖锐的矛盾又把我们拉回现实:文章得分是非常容易作弊的。一个人如果对一篇 文章连续添柴,包括换用很多马甲连续添柴,都可以直接获得很多得分。我们可以限制一名用户连续添柴的次数,可是我们无法限制一个用户注册马甲的数量。于 是,一个关于用户权重的参数—用户系数,出现了。用户系数与等级相关,等级越高的用户权重越大,刚注册的用户权重极低。基本思路产生后,问题也就很明确 了,接下来就是根据这些问题确定各个目标,然后解决问题。
确定目标
根据前面的分析,可以确定如下目标。
1. 为每种用户操作设定其固有的分值。
2. 设定一个时间衰减的速率。
3. 确立一个用户系数模型,使用户的权重随等级变化而变化。
4. 设计一个用户等级体系。
解决问题
操作常量(A)
我烧网中用户对文章的操作包括添柴、泼水(添柴的逆操作)、分享和评论等。添柴、泼水是对文章最简单、最直接的表态:喜欢就添柴,不喜欢就泼水。其他的操作所对应的分数应该参考这两项操作。因此将添柴常量定为:
1,泼水常量定为-1。
A添柴=1
A泼水=-1
分享也是对文章的肯定。如果一个用户给一篇文章添柴,说明他喜欢这篇文章;如果用户分享这篇文章,说明他不仅喜欢这篇文章,还希望其他人也来看这篇文章。两者比较,分享是比添柴更为强烈的行为。因此分享应当比添柴得到更多的分,这里将其设定为1.2。
A分享=1.2
评论则是一个双面性的操作。用户可能为赞同某一篇文章而对其评论,也可能是由于非常反对文章中的观点而进行评论。经过反复思考和研究,我们发现当一个用 户对某一篇文章持反对意见而评论时,其实这篇文章是更值得推荐的,因为往往这类文章是那种容易引起争议的或者干脆就是那些最容易被炒火的内容。从这个角度 上看,无论是正面还是负面的评论,这篇文章都应该被推荐,而且应该比添柴、分享有更高的推荐度。所以我们将评论的操作常量定为1.5。
A评论=1.5
时间衰减率(T)
时间衰减率的核心问题就是让文章在多长的时间内衰减多大的比例。
一 般来说,我们都希望网站上排在前面的内容是新的、热点的。但是这个“新”可以是一个小时以内,也可以是一天以内、一周以内。“新”应该视具体情况而定。访 问我烧网的人通常是每天来看看有什么新鲜的内容,因此按天来衰减对于我烧网来说是比较合理的。以t0表示文章的发布时间,t1表示当前时间,T应该是 (t1-t0)的函数,即:
T=f(t1-t0),其中t0和t1以天为单位。
我们在参考了某网站的Digg版之后发现,即使是排在第一的文章,推荐数也很难超过100;推荐数最多也是100多,极少数的会在200以上,大部分在50~99之间。根据我烧网的文章量和用户量,我们推测文章的得分将很难超过100。
我们列举了一组数据:当t 1 - t 0=0 , 1 , 2 ,3,4,5,6,7,文章得分(D)=10,20,30,40,50,60,
70,80,90,100时,应该设计何种排序方式?
①同一天的文章t相等,所以D越大排名越靠前。
②同样的D,t1-t0越小排名越靠前。
③昨天的文章100分,今天的文章10分,20分,30分,40分,这个100分的文章应该还是排在前面。