揭秘我烧网热文排序算法—SmartHot

2019-03-22 04:12:40来源: 《程序员》 阅读 ()

新老客户大回馈,云服务器低至5折

就像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分的文章应该还是排在前面。

④7天前的文章100分,今天的文章10分,20分,30分,40分,这个100分的文章应该排在中间。

①和②是最基本的原则,很好理解。③和④要解决的是前一个或前N个时间周期的超级热门和今天的一般热门是怎样的先后位置。

那么我们大胆设想了两个函数:

[1]

[2]

将(t1-t0)=0,1,2,3,4,5,6,7分别代入以上两式,得:

T = +, 1 , 1/2 , 1/3 , 1/4 , 1/5 , 1/6 [3]

图1 时间衰减率函数的模型图

T = +, 1 , 1/4 , 1/9 , 1/16 , 1/25 , 1/36 [4]

以上两列结果显然[3]更合理,[4]让以前的超级热门也没有任何机会,是不合理的。经过优化,我们需要把正无穷的结果剔除,最终得出:T =1/(t1− t0 +1)

用户系数(u)

关于用户系数u的确定,我们首先确定了下面几个原则。

·新注册用户没有话语权。

·用户话语权衡量标准是用户等级(G)。

·新注册用户需要跨过一个很高的门槛才

能拥有话语权。但是一旦越过这个门槛,话语权就应该有一个迅速的提升。就是说,当某个用户不再是新注册用户之后,话语权就应该和大家差不多。

有话语权的用户之间比较,u不能相差太大,不然不利于保护新手。总不能让人家新手们添几十次柴还不如老手添一下柴吧!

随着等级提升,用户系数u的提升应该越来越慢,并且应该有个极限值,不至于让高等级用户太占便宜,这也是为了保证公平性。根据上面的原则,我们可以看出,用户系数是一个关于等级的函数,这个函数模型应该如图2所示。

图2 用户系数函数的模型图

在图2中,当用户等级G=1时,u=0;当G趋进于正无穷时,u=1。这样的话,u就永远处于0和1之间,即0 1。于是老用户能比新用户占便宜,但也占不到太多。这条曲线还有一个重要点,就是当G离开1变为2时,u应该猛增;而当G从大数再增大时(如从1 0 变为11),u的增量应该极小。

由上面的模型,我们设计了如下公式:u =1−1/(2G −1)。

用户等级(G)

等级的具体计算公式与SmartHot算法关系不大,这里不展开说明,只谈谈我们对等级的特殊处理。

最初在没有考虑SmartHot算法的时候,我们希望为用户的每一步操作奖励一定的经验,经验达到某个阈值就升级。但是,由于用户等级关系到文章的排序,需要在最初想法的基础上加入一些限制以防止作弊。

对等级的作弊其实很好理解。比如在一般的RPG网游中,玩家可以通过不停地刷怪获得经验以达到升级的目的,放在我烧网中就是可以通过不停地分享文章来达到同样的效果。当用户可以很容易地升级时,作弊者就可以通过注册N个马甲随意地控制热文了。因此在这里我们做出了一定的限制:在用户操作某一篇文章后的一分钟内,再次进行文章操作(即使是对不同的文章)不能获得经验。这样一来,用户每小时、每天能够得到的经验都是有限的,有效地避免了刷经验、刷等级。

算法确定

解决了以上问题,完整的SmartHot算法已经出来了:

Q = D * T

T = 1 /( t1 - t0 + 1 )(t1,t0单位为天)

D = D1 + D2 + D3 +... + Dn

Dn = A * u

u = 1 -(1 /(2^G -1))

其中:

Q——文章排序的最终依据

D——文章总得分

G——用户等级

A——操作常量

u——用户系数

T——时间衰减率

t0——文章发布时间

t1——当前时间

一点特殊处理:前面曾经提到过,同一用户对同一篇文章的添柴只允许进行一次,但是我们不能按同样办法限制用户对同一文章的评论次数。因此,为了保证同一用户对同一篇文章操作,该文章只能得到一次加分,我们做了一个特殊处理:当同一用户对同一文章进行了N次操作时,只将其第一次操作产生的Dn计入

D,以后的操作皆不影响文章的D值。

总结

至此,我完整地介绍了我烧网SmartHot算法设计的全部思路,可以说这是我们团队整体智慧的结晶,也为后来我烧网的发展起到了重要的作用。SmartHot算法不仅省去了很多人工劳动,更重要的是让我烧网的内容越来越贴近用户。

世上唯一不变的是变化,算法也一样,像Google一样我们也在不停地对算法进行着优化和调整,在即将完成本文时,我注意到版本库中关于SmartHot的修订记录总共有37条。这样算来,从第一个版本到现在,我们平均每个月要对算法改进2~3次,这是连我自己都没有想到的。

现在运行在我烧网上的SmartHot已经跟本文提到的大大不同了,但我们一直坚持着一个重要的原则—只改算法,不改结果。虽然期间很多算法上的重大Bug都影响了我烧网热文的质量,有时也被SPAM钻了空子,但我们这种坚持一直没有变,我们宁可牺牲一整夜的睡眠来改进算法也不愿轻松地修改一下数据库中的结果。因为我们清楚地知道,这样做一次其实并没有什么,可是做过一次之后就将无法停止继续去做。也正是这种坚持,给我们带来了更好的算法、更优质的内容和更多用户的喜爱。

希望以本文与所有正在做和即将做产品设计、算法设计的朋友共勉。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:实战分析如何应对黑客入侵网站

下一篇:浅析访谈形式软文的优势与价值