推荐系统的目的是联系用户的兴趣和物品,这种联系需要依赖于不同的媒介。GroupLens在文章1中认为目前流行的推荐系统基本上通过三种方式来联系用户兴趣和物品。如图1所示,第一种方式是通过用户喜欢过的物品:可以给用户推荐与他喜欢过的物品相似的物品,这就是前面提到的基于物品的算法(item-based)。第二种方式是通过和用户兴趣相似的其他用户:可以给用户推荐那些和他们兴趣爱好相似的其他用户喜欢的物品,这也是前面提到的基于用户的算法(user-based)。除了这两种方法,第三个也是最重要的方式是通过一些特征(feature)来联系用户和物品,可以给用户推荐那些具有用户喜欢的特征的物品。这里的特征有不同的表现方式,比如可以表现为物品的属性集合(比如对于图书,属性集合就包括了作者、出版社、主题和关键词等),也可以表现为隐语义向量(latent
factor vector),这可以通过前面提出的隐语义模型(Latent Factor
Model)学习得到。在本章中,我们将讨论一种重要的特征表现方式:标签。
图1 推荐系统联系用户和物品的几种途径
根据维基百科的定义2,标签是一种无层次化结构的、用来描述信息的关键词。因此,标签可以用来准确地描述物品的语义。根据给物品打标签的人的不同,标签应用一般分为两种。第一种是让作者或者编辑给物品打标签,而另一种是让普通用户给物品打标签,也就是UGC的标签应用。表1列出了这两种不同的标签系统的代表网站。在本章中,我们主要讨论UGC的标签应用,研究用户给物品打标签的行为,以及如何通过分析这种行为给用户进行个性化推荐。
表1 两种不同的标签系统的代表网站
UGC的标签系统是一种很重要的表示用户兴趣和物品语义的方式。当一个用户对一个物品打上一个标签后,这个标签一方面描述了用户的兴趣,另一方面也表示了物品的语义,从而将用户和物品联系了起来。
UGC标签系统的代表应用
UGC标签系统是很多Web 2.0网站的必要组成部分,本节将讨论使用UGC标签系统的代表网站:UGC标签系统的鼻祖美味书签(Delicious)、论文书签网站CiteULike、音乐网站Lastfm、视频网站Hulu、书和电影评论网站豆瓣等。下面将分别介绍这些应用。
Delicious
美味书签(Delicous)是标签系统里的开山鼻祖了,它允许用户给互联网上的每个网页打上标签,从而通过标签的方式重新组织整个互联网。图2是Delicious中被用户打上recommender
system标签最多的网页,这些网页反应了用户心目中和推荐系统最相关的网页。图3是Delicious中“豆瓣电台”这个网页被用户打的最多的标签,可以看到这些标签确实准确地描述了豆瓣电台。
图2 Delicious中被打上recommender system标签的网页
图3 Delicious中“豆瓣电台”网页被用户打的最多的标签
CiteULike
CiteULike是一个著名的论文书签网站,它允许研究人员提交或者收藏他们感兴趣的论文,给论文打标签,从而帮助用户更好地发现和自己研究领域相关的优秀论文。我们知道,研究人员搜索自己研究领域值得参考的论文是很费时费力的工作,而CiteULike通过群体智能,让每个研究人员对自己了解的论文进行标记,从而帮助用户更好更快地发现自己感兴趣的论文。图4展示了CiteULike中一篇被用户打的标签最多的有关推荐系统评测的文章,可以发现,最多的两个标签是collaborative-filtering(协同过滤)和evaluate(评测),确实比较准确地反应了这篇论文的主要内容。
图4 CiteULike中一篇论文的标签
Lastfm
Lastfm是一家著名的音乐网站,它通过分析用户的听歌行为来预测用户对音乐的兴趣,从而给用户推荐个性化的音乐。作为多媒体,音乐不像文本那样可以很容易地分析它的内容信息。为了在不进行复杂的音频分析的情况下获得音乐的内容信息,Lastfm引用了标签系统,让用户用标签标记音乐和歌手。图5展示了披头士乐队在Lastfm中的标签云(tag
cloud)。从这个标签云可以看到,披头士应该是一个英国的传统摇滚乐队,流行于上世纪60年代。
图5 Lastfm中披头士乐队的标签云
豆瓣
豆瓣是中国著名的评论和社交网站,同时也是中国个性化推荐邻域的领军企业之一。豆瓣在个性化推荐领域进行了广泛的尝试,标签系统也是他们尝试的领域之一。他们允许用户对图书和电影进行标签,从而获得图书和电影的内容信息,并用这种信息来改善他们的推荐效果。图7展示了《数据挖掘导论》在豆瓣被用户标记的情况。如图7所示,最多的几个标签分别是:数据挖掘、计算机、计算机科学、数据分析、IT数据分析。这些标签准确地反应了这本书的内容信息。
图6 豆瓣读书中《数据挖掘导论》一书的常用标签
Hulu
Hulu是美国著名的视频网站。视频作为一种最为复杂的多媒体,获取它的内容信息是最困难的,因此,Hulu也引入了用户标签系统来让用户对电视剧和电影进行标记。图7展示了美剧《豪斯医生》的常用标签,可以看到,Hulu对标签做了分类,并展示了每一类最热门的标签。从类型(genre)看,豪斯医生是一部医学片(medical
drama);从时间看,这部剧开始于2004年;从人物看,这部美剧的主演是hugh laurie,他在剧中饰演的人物是greg house。
图7 Hulu中《豪斯医生》的常用标签
从前面的各种应用可以看到,标签系统在各种各样的网站中(音乐、视频和社交等)都得到了广泛的应用。标签系统的最大优势在于可以发挥群体的智能,获得物品内容信息的比较准确的关键词描述,而准确的内容信息是提升个性化推荐系统的重要资源。
标签系统中的推荐问题
标签行为作为一种重要的用户行为,蕴含了很多反映用户兴趣的信息,因此深入研究用户的标签行为可以很好地指导个性化推荐系统提升自己的推荐质量。同时,标签作为一种重要的内容表示方式,比传统的内容属性表示更能反应用户对物品的看法,并且表示形式非常简单,便于很多算法处理。
标签系统中的推荐问题主要有以下两个。
-
如何利用用户的标签行为给用户推荐物品(tag-based recommendation)?
-
如何在用户给物品打标签时给用户推荐适合于该物品的标签(tag recommendation)?
为了研究上面的两个问题,我们首先需要解答下面三个问题。
-
用户为什么要打标签(Why)?
-
用户怎么打标签(How)?
-
用户打什么样的标签(What)?
用户为什么要标注
在设计基于Tag的个性化推荐系统之前,我们需要深入了解用户的标注行为,知道用户为什么要标注,用户怎么标注,只有深刻地了解用户的行为,我们才能基于这个行为给用户设计出令他们满意的个性化推荐系统。
Morgan
Ames研究图片分享网站中用户标注的动机问题3,他将用户标注的动机分解成两个维度。首先是社会维度,有些用户标注是为了给内容的上传者使用的,而有些用户标注是为了给广大用户使用的。令一个维度是功能维度,有些标注是为了更好地组织内容,方便用户将来的查找,而另一些标注是为了传达某种信息,比如照片的拍摄时间和地点等。
用户如何打标签
在互联网中,尽管每个用户的行为看起来是随机的,但其实这些表面随机的行为的背后蕴含着很多规律。在这一节中,我们通过研究美味书签的数据集,来发现用户标注行为中的一些统计规律。
德国的研究人员公布过一个很庞大的美味书签的数据集4,该数据集包含了2003年9月到2007年12月美味书签用户4.2亿条标签行为记录。本节选用该数据集2007年一整年的数据进行分析,对该数据集的统计特性进行研究。
本节将统计数据集的以下信息。
-
用户活跃度的分布。
-
物品流行度的分布。
-
标签热门度的分布。
-
用户标签行为随时间演化的曲线。
-
用户相隔一段时间兴趣变化的情况。
-
物品的生命周期。
[*具体统计结果待书正式出版时公布*]**
用户打什么样的标签
用户在看到一个物品时,我们最希望他打的标签是能够准确描述物品内容属性的关键词。但用户往往不是按照我们的想法去操作,而是可能会给物品打上各种各样奇怪的标签。
Scott A. Golder 总结了美味书签上的标签,将它们分为如下的几类。
-
表明物品是什么:比如是一只鸟,就会有“鸟”这个词的标签;是豆瓣的首页,就有一个标签叫“豆瓣”;是乔布斯的首页,就会有个标签叫“乔布斯”。
-
表明物品的种类:比如在美味书签中,表示一个网页的类别的标签包括 article(文章)、 blog(博客)、 book(图书)等。
-
表明谁拥有物品 :比如很多博客的标签中会包括博客的作者等信息。
-
表达用户的观点:比如用户认为网页很有趣,就会有funny(有趣)的标签,认为很无聊,就会打上boring(无聊)的标签。
-
用户相关的标签:有些标签,比如 my favorite(我最喜欢的)、my comment(我的评论)等。
-
用户的任务:比如 to read(即将阅读)、 job search(找工作)等。
很多不同的网站也设计了自己的标签分类系统,比如Hulu对视频的标签就做了分类。
图8是著名的美剧《豪斯医生》的标签。可以看到,Hulu将电视剧的标签分成了几类。
-
类型(Genre):主要表示这个电视剧的类别,比如《豪斯医生》是属于医学剧情片(medical
drama),同时有喜剧(comedy)、悬疑(mystery)的成分。 -
时间(Time):主要包括电视剧发布的时间,有时也包括电视剧中事件发生的时间,比如是二战期间,或者是上世纪90年代。
-
人物(People):主要包括电视剧的导演、演员和剧中重要人物等。
-
地点(Place):剧情发生的地点,或者是视频拍摄的地点等。
-
语言(Language):这部电视剧使用的语言。
-
奖项(Awards):这部电视剧获得的相关奖项。
-
其他(Details):包含了不能归类到上面各类的其他所有标签。
图8 著名美剧《豪斯医生》在视频网站Hulu上的标签分类
1 文章名是Tagsplanations : Explaining Recommendations
using Tags。
2
具体见http://en.wikipedia.org/wiki/Tag_(metadata)。
3 具体见 Why We Tag: Motivations for Annotation in
Mobile and Online
Media。
4 数据集见
http://www.dai-labor.de/en/competence_centers/irml/datasets/。
===============================================
用户用标签来描述自己对物品的看法,因此,标签成为了联系用户和物品的纽带。因此,标签数据是反应用户兴趣的重要数据源,而如何利用用户的标签数据来提高用户个性化推荐结果的质量,是推荐系统研究的重要问题。
在如何利用标签数据的问题上,豆瓣无疑是这方面的代表。豆瓣将标签系统融入到他们的整个产品线中。下面以豆瓣读书为例进行介绍。首先,在每本书的页面上,都提供了一个叫做“豆瓣成员常用标签”的应用,它给出了这本书上用户最常打的标签。同时,在用户希望给书做评价时,豆瓣也会让用户给图书打标签。最后,在最终的个性化推荐结果里,豆瓣利用标签将用户的推荐结果做了聚类,显示了不同标签下用户的推荐结果,从而增加了推荐的多样性和可解释性。
一个用户标签行为的数据集一般由一个三元组的集合表示,其中记录(u, i, b) 表示用户u给物品i打上了标签b。当然,用户的真实的标签行为数据远远比三元组表示的要复杂,比如用户标签的时间、用户的属性数据、物品的属性数据等。但是,在本章中,为了集中讨论标签数据,我们只考虑上面定义的三元组形式的数据,即用户的每一次标签行为都用一个三元组(用户,物品,标签)来表示。
在下面的各节中,我们将利用Delicious的数据集,讨论如何利用用户标签数据进行个性化推荐的各种算法。
实验设置
我们将Delicious的数据集按照9:1随机分成训练集R和测试集T。这里分割的键值是用户和物品,不包括标签,也就是说,用户对物品的多个标签记录要么都被分进训练集,要么都被分进测试集。
在分完数据集后,我们将通过学习训练集中的用户标签数据,来预测测试集上用户会给什么物品打标签。对于用户u,令R(u)是给用户u的长度为N的推荐列表,里面包含着我们认为用户会打标签的物品。而另T(u)是测试集中用户u实际上打过标签的物品集合。然后,我们利用准确率/召回率(Precision/Recall)来评测个性化推荐算法的准确率:
如果用Python实现上面的两个指标,我们可以通过如下的代码:
defPrecisionRecall(test_data, recommendations, N):
hit =0
n_recall =0
n_precision =0for user,ru in recommendations.items():
tu = test_data[user]for item in sorted(ru.items(), key=itemgetter(1), reverse=True)[0:N]:if item in tu:
hit +=1
n_precision +=1
n_recall += len(tu)
recall = hit /(n_recall *1.0)
precision = hit /(n_precision *1.0)return list(recall, precision)
同时,为了全面评测个性化推荐的性能,我们同时评测了推荐结果的覆盖度(Coverage)、多样性(Diversity)和新颖度。
覆盖度我们通过如下的公式和代码计算:
defCoverage(train_data, test_data, recommendations, N):
total_items =set()
recommend_items =set()for user, items in train_data.items():for item in items:
total_items.add(item)for user, ru in recommendations.items():for item, weight in sorted(ru.items(), key=itemgetter(1), reverse=True)[0:N]:
recommend_items.add(item)return(len(recommend_items)*1.0)/(len(total_items)*1.0);
关于多样性,我们在第1章中讨论过,多样性的定义取决于相似度的定义。在本章中,我们用物品的标签向量的余弦相似度来度量物品之间的相似度。对于每个物品i,itemtags[i]存储了物品i的标签向量,其中itemtags[i][b]是物品i上标签b被打的次数,那么物品i和j的余弦相似度可以通过如下的程序计算:
defCosineSim(item_tags, i, j):
ret =0for b,wib in item_tags[i].items():if b in item_tags[j]:
ret += wib * item_tags[j][b]
ni =0
nj =0for b, w in item_tags[i].items():
ni += w * w
for b, w in item_tags[j].items():
nj += w * w
if ret ==0:return0return ret / math.sqrt(ni * nj)
在得到物品之间的相似度度量后,我们通过如下的公式来计算一个推荐列表的多样性:
如果用程序实现,代码如下:
defDiversity(item_tags, recommend_items):
ret =0
n =0for i in recommend_items.keys():for j in recommend_items.keys():if i == j:continue
ret +=CosineSim(item_tags, i, j)
n +=1return ret /(n *1.0)
而推荐系统的多样性定义为所有用户的推荐列表的多样性的平均值。
至于推荐结果的新颖性,这里我们简单地用推荐结果的平均热门程度(AveragePopularity)来度量。对于物品i,定义它的热门度item_pop(i)为给这个物品打过标签的用户数。而对推荐系统,我们定义它的新颖度如下:
如果用程序实现,代码如下:
defAveragePopularity(item_pop, recommend_results):
ret =0
n =0for u, recommend_items in recommend_results.items():for item in recommend_items.keys():
ret += math.log(1+ item_pop [item])
n +=1return ret /(n *1.0)
一个最简单的算法
当拿到了用户标签行为数据时,大家都可以想到一个最简单的算法来给用户推荐个性化的物品。这个算法的描述如下所示。
-
统计每个用户最常用的标签。
-
对于每个标签,统计被打过这个标签的次数最多的物品。
-
对于一个用户,首先找到他常用的标签,然后对于这些常用标签,找到具有这些标签的最热门的物品,推荐给这个用户。
如果用公式描述上面的算法,那么用户u对物品i的兴趣可以用如下的公式度量:
这里,B(u)是用户u打过的标签集合,B(i)是物品i被打过的标签集合, 是用户u打过标签b的次数,
是物品i被打过标签b的次数。本章用SimpleTagBased来标记这个算法。
在Python中,我们用 records 来存储标签数据的三元组,其中
records[i]=[user, item, tag]
用 usertags 来存储 ,其中usertags[u][b] =
。
用 tagitems来存储,其中tagitems[b][i] =
。
如下的程序可以从records统计出usertags和tagitems:
defInitStat(records):
user_tags = dict()
tag_items = dict()for user, item, tag in records.items():if user notin user_tags:
user_tags[user]= dict()if tag notin user_tags[user]:
user_tags[user][tag]=1else:
user_tags[user][tag]+=1if tag notin tag_items:
tag_items[tag]= dict()if item notin tag_items[tag]:
tag_items[tag][item]=1else:
tag_items[tag][item]+=1
统计出usertags和tagitems之后,可以通过如下程序对用户进行个性化推荐:
defRecommend(user):
recommend_items = dict()for tag, wut in user_tags[user].items():for item, wti in tag_items[tag].items():if item notin recommend_items:
recommend_items[item]= wut * wti
else:
recommend_items[item]+= wut * wti
return recommend_items
我们在Delicious数据集上对上面的算法进行评测,结果如表2所示。
表2 简单的基于标签的推荐算法在Delicious数据集上的评测结果
算法的改进
我们再来回顾一下上面提出的简单算法,该算法通过如下公式预测用户u对物品i的兴趣:
仔细研究上面的公式,可以发现上面的公式有很多缺点。下面我们将逐条分析该算法的缺点,并提出改进意见。
归一化
如果我们从概率论的角度出发,认为用户u喜欢物品i的概率取决于u曾经打过的标签,那么我们会得到如下的概率公式:
这个公式和SimpleTagBased算法的公式相比,对参数做了归一化,而且他的解释也是从概率的角度出发,更加明确,本章用NormTagBased来代表这个算法。表3给出了SimpleTagBased算法和NormTagBased算法在各种指标上的实验结果的比较。
表3 SimpleTagBased算法和NormTagBased算法的比较
如表3所示,经过归一化之后的NormTagBased算法无论在召回率/准确率,还是在覆盖度、多样性和热门程度等指标上,均优于SimpleTagBased算法。因此,NormTagBased算法是对SimpleTagBased的算法的一个有效的改进。
数据稀疏性
在前面的算法中,用户兴趣和物品的联系是通过B(u)∩B(i)中的标签建立的。但是,如果这个用户是新用户,或者物品是新物品,那么这个集合(B(u)∩B(i))中的标签数量会很少。为了提高推荐的准确率,我们可能要对标签集合做扩展,比如用户曾经用过“推荐系统”这个标签,我们可以将这个标签的相似标签也加入到用户标签集合中,比如“个性化”,“协同过滤”等标签。
为了说明数据稀疏性对性能的影响,我们将用户按照打过的标签数分成两组。第一组用户打过10次以下的标签,而第二组用户打过超过10次标签,我们分别统计这两组用户的推荐结果的准确率和召回率,结果如表4所示。
表4 不同活跃度的用户的召回率/准确率对比
[具体实验结果待正式发表时公布]
进行标签扩展有很多方法,其中著名的有话题模型(Topic Model)。不过这里我们遵循简单的原则,只介绍一种基于邻域的方法。
标签扩展的本质是对每个标签找到和它相似的标签,也就是计算标签之间的相似度。最简单的相似度可以是同义词。如果我们有一个同义词词典,就可以根据这个词典来进行标签扩展。如果没有这个词典,我们还是可以从数据中统计出标签的相似度。
如果认为同一个物品上的不同标签具有某种相似度的话,那么如果两个标签同时出现在很多物品的标签集合中,就可以认为这两个标签具有较大的相似度。对于标签b,令N(b)为有标签b的物品的集合,n_(b,i)为给物品i打上标签b的用户数,可以通过如下的余弦相似度公式计算标签b和标签b'的相似度:
[具体实验结果待正式发表时公布]
标签清理
不是所有的标签都能反应用户的兴趣。比如,在一个视频网站中,用户可能对一个视频赋予了一个表示情绪的标签,比如“不好笑”(no funny)。但我们不能因此认为用户对“不好笑”有兴趣,并且给用户推荐其他具有“不好笑”这个标签的视频。相反,如果用户对视频打过“成龙”这个标签,我们可以据此认为用户对成龙的电影感兴趣,从而给用户推荐成龙其他的电影。同时,标签系统里经常出现词形不同、词义相同的标签,比如recommender system和recommendation engine就是两个同义词。
标签清理的另一个重要意义在于用标签作为推荐解释。如果我们要把标签呈现给用户,作为给用户推荐某一个物品的解释时,对标签的质量要求就很高。首先,这些标签不能包含没有意义的停止词或者表示情绪的词,其次这些推荐解释里不能包含很多相同意义的词语。
本章我们使用的标签清理的方法有以下几种。
-
去除词频很高的停止词。
-
去除因词根不同造成的同义词,比如 recommender system和recommendation system。
-
去除因分隔符造成的同义词,比如 collaborative_filtering和collaborative-filtering。
[具体实验结果待正式发表时公布]
为了控制标签的质量,很多网站也采用了让用户反馈的思想,即让用户来告诉系统某个标签是否合适。MovieLens在他们的实验系统中就采用了这种方法,关于这方面的研究可以参考GroupLens的Shilad同学的博士论文 。此外,电影推荐网站Jinni也采用了这种方式(如图9所示)。当然,Jinni不属于UGC的标签系统,它给电影的标签是专家赋予的,因此它让用户对标签反馈其实是想融合专家和广大用户的知识。
图9 Jinni允许用户对编辑给的标签进行反馈
基于图的推荐算法
前面讨论的简单算法很容易懂,也容易实现,但缺点是不够系统化和理论化。因此,在这一节中,我们主要讨论如何利用图模型来做基于标签数据的个性化推荐。
首先,我们需要将用户的标签行为表示到一个图上。我们知道,图是由顶点、边和边上的权重组成的。而在用户标签数据集上,有三种不同的元素:用户、物品和标签。因此,我们需要定义三种不同的顶点:用户顶点、物品顶点和标签顶点。然后,如果我们得到一个表示用户u给物品i打了标签b的用户标签行为(u,i,b),那么,最自然的想法就是在图中增加三条边,首先在用户u对应的顶点v(u)和物品i对应的顶点v(i)之间需要增加一条边(如果这两个顶点已经有边相连,那么就应该将边的权重加1),同理,在v(u)和v(b)之间需要增加一条边, v(i)和v(b)之间也需要边相连接。
图10是一个简单的用户-物品-标签图的例子。
图10 一个简单的用户-物品-标签图的例子
通过用户标签行为构造出图之后,为用户u推荐物品的问题就转化为计算图上所有物品节点相对于用户节点v(u)的相关度排名的问题。图上的排名算法很多,其中最著名的就是PageRank算法。
PageRank算法最初是用来对互联网上的网页进行排名的算法。网页通过超级链接形成了图。PageRank假设用户从所有网页里随机挑出一个网页,然后开始通过超级链接进行网上冲浪。到达每个网页后,用户首先会以d的概率继续冲浪,而在冲浪时,用户会以同等的概率在当前网页的所有超级链接中随机挑选一个进入下一个网页。那么,在这种模拟下,最终每个网页都会有一个被用户访问到的稳定概率,而这个概率就是网页的排名。
PageRank算法通过如下的迭代关系式来计算网页的权重:
其中PR(i)是网页i的排名,d是用户每次继续冲浪的概率,N是所有网页的总数。in(i)是指向网页i的所有网页的集合,out(j)是网页j链向的网页的集合。
下面我们举一个简单的例子来说明PageRank算法,我们用图11所示的例子来演示一下PageRank的迭代过程。
图11 说明PageRank算法的图例
(1)一开始,每个顶点的排名都是一样的,PR(A) = PR(B) = PR(C) = PR(D) = PR(E) = 1 / 5,令d = 0.85。
(2)根据前面的迭代关系式有
PR(A)=(1–0.85)/5+0.85*(PR(B)/2)=0.115
PR(B)=(1–0.85)/5+0.85*(PR(C)/1)=0.2
PR(C)=(1–0.85)/5+0.85*(PR(A)/2+ PR(D)/2+ PR(E)/2)=0.285
PR(D)=(1–0.85)/5+0.85*(PR(E)/2)=0.115
PR(E)=(1–0.85)/5+0.85*(PR(A)/2+ PR(B)/2+ PR(D)/2)=0.285
(3)继续按照前面的迭代关系式,有
PR(A)=(1–0.85)/5+0.85*(PR(B)/2)=0.115
PR(B)=(1–0.85)/5+0.85*(PR(C)/1)=0.27225
PR(C)=(1–0.85)/5+0.85*(PR(A)/2+ PR(D)/2+ PR(E)/2)=0.248875
PR(D)=(1–0.85)/5+0.85*(PR(E)/2)=0.151125
PR(E)=(1–0.85)/5+0.85*(PR(A)/2+ PR(B)/2+ PR(D)/2)=0.21275
我们可以按照上面的步骤一步步迭代下去,最终得到所有顶点的PageRank排名。
但是,从上面的描述可以看到,PageRank只是计算了所有顶点的全局排名,并不能用来计算一个顶点相对于另一个顶点的相关度排名。因此,很多研究人员对PageRank做出了修改,其中一个著名的修改就是TopicRank算法。
PageRank算法认为,用户每次都是从所有顶点中以相同的概率随机挑选一个顶点,然后开始随机游走,而且在每次随机游走经过每个顶点时,都会有1 - d的概率停止游走。那么,如果我们要计算所有点相对于某一个顶点的相关度排名,我们可以假设用户每次都从某一个顶点v出发,然后在每次随机游走经过每个顶点时都以1-d的概率停止游走,从v重新开始。 那么,最终每个顶点被访问的概率就是这些顶点和v的相关度排名。
PageRank可以用来给图中的顶点进行全局的排名,但它无法用来给每个用户个性化的对所有物品排序。为了解决个性化排名的问题,斯坦福大学的Haveliwala提出了TopicRank的算法 ,这个算法可以用来做个性化排序,因此本文将其称为PersonalRank。PersonalRank的迭代公式如下:
可以看到,PersonalRank和PageRank的区别在于用ri代替了1/N,也就是说,从不同的点重新开始的概率不同了。那么,假设如果我们要计算所有顶点和顶点k的相关度排名,我们可以定义如下:
然后利用上面的迭代公式,就可以计算出所有顶点相对于k的相关度排名。我们将这里的称为顶点i的启动概率。
回到给用户推荐物品这个问题上来,在我们构造出用户-物品-标签的图之后,如果我们要给用户u做推荐,我们可以令顶点v(u)的启动概率为1,而其他顶点的启动概率为0。然后用上面的迭代公式来计算所有物品对应的顶点相对于v(u)的排名。
下面两段Python代码给出了如何从用户行为记录集合tagging_records中构建图,以及如何在图上给用户进行推荐。
defBuildGraph(tagging_records):
graph = dict()for user, item, tag in tagging_records:
addToMat(graph,‘u:’+user,‘i:’+item,1)
addToMat(graph,‘i:’+item,‘u:’+user,1)
addToMat(graph,‘u:’+user,‘t:’+tag,1)
addToMat(graph,‘t:’+tag,‘u:’+user,1)
addToMat(graph,‘t:’+tag,‘i:’+item,1)
addToMat(graph,‘i:’+item,‘t:’+tag,1)defRecommend(user, d, K):
rank = dict()
rank[‘u:’+user]=1for step in range(0:K):for i, ri in rank.items():for j, wij in graph[i]:
tmp_rank[j]+= d * ri * wij
tmp_rank[‘u:’+ user]=(1– d)
sum_weight = sum(tmp_rank.values())
rank = dict()for i, ri in tmp_rank.items():
rank[i]= ri / sum_weight
tmp_rank = dict()return rank
这里,d是前面提到的继续随机游走的概率,K是迭代的次数。在上面从某一个用户节点开始随机游走时,迭代K步最多可以走到离该用户节点距离为K之内的所有顶点,而其他顶点的权重为0。
在传统的PersonalRank中,我们需要迭代很多次,直到所有顶点的权重都稳定了为止。但是,如果我们为每个用户做推荐,都需要在全图上进行迭代,直到全图的所有顶点的权重都收敛,这样的时间复杂度太大了。因此,我们在实际的应用中一般只迭代比较少的次数。
用图模型解释前面的简单算法
在介绍了图模型后,我们可以用图模型来重新看待前面提到的简单的算法。在那个算法中,用户对物品的兴趣通过如下的公式计算:
这个公式认为用户对物品的兴趣通过标签来传递,因此这个公式可以通过一个比前面简单的图来建模(记为SimpleTagGraph)。给定用户标签行为记录(u,i,b),SimpleTagGraph会增加两条有向边,一条由用户节点v(u)指向标签节点v(b),另一条由标签节点v(b)指向物品节点v(i)。
图12就是一个简单的SimpleGraph的例子。在构建了SimpleGraph后,利用前面的PersonalRank算法,令K = 1,就是我们前面提出的简单推荐算法。
图12 SimpleGraph的例子
[相关实验:发表时公布
A. 迭代次数K对精度的影响
B. 边权重的定义对精度的影响
]
基于标签的推荐解释
基于标签的推荐的最大好处是可以利用标签来做推荐解释,这方面的代表性应用是豆瓣的个性化推荐系统。图13展示了豆瓣读书的个性化推荐界面。
图13 豆瓣读书的个性化推荐应用“豆瓣猜”的界面
如图13所示,豆瓣读书推荐结果包括两个部分。上面是一个标签云,表示用户的兴趣分布,标签的尺寸越大,表示用户对这个标签相关的图书越感兴趣。比如图13显示了我在豆瓣的阅读兴趣,从上方的标签云可以看到,豆瓣认为我对“编程”、“机器学习”、“软件开发”感兴趣,这是因为我看了很多IT技术方面的图书,豆瓣认为我对“东野圭吾”感兴趣,是因为我看了好几本他的侦探小说,同时因为我对人文学科比较感兴趣,所以豆瓣认为我对“传记”、“文化”比较感兴趣。单击每一个标签云中的标签,都可以在标签云下方得到和这个标签相关的图书推荐,比如图13就是机器学习相关的图书推荐。
豆瓣这样组织推荐结果页面有很多好处。首先这样提高了推荐结果的多样性。我们知道,一个用户的兴趣在长时间内是很广泛的,但在某一天又比较具体。因此,我们如果想在某一天击中用户当天的兴趣,是非常困难的。而豆瓣通过标签云,展示了用户的所有兴趣,然后让用户自己根据他今天的兴趣选择相关的标签,得到推荐结果,从而极大地提高了推荐结果的多样性,使得推荐结果更容易满足用户多样的兴趣。
同时,标签云也提供了推荐解释的作用。用户通过这个界面可以知道豆瓣给自己推荐的每一本书都是基于它认为自己对某个标签感兴趣。而对于每个标签,用户总能通过回忆自己之前的行为来知道自己是否真的对这个标签感兴趣。
我们知道,要让用户直接觉得推荐结果是有道理的,是很困难的。而豆瓣将推荐结果的可解释性拆分成了两个部分,首先让用户觉得标签云是有道理的,然后让用户觉得从某个标签推荐出某本书也是有道理的。因为生成让用户觉得有道理的标签云比生成让用户觉得有道理的推荐图书更加简单,标签和书的关系就更容易让用户觉得有道理,从而让用户最终觉得推荐出来的书也是很有道理的。