《 Programming Collective Intelligence》案例介绍与分析——Making Recommendations
Making Recommendations
本文将展示如何使用一群人的偏好向其他人提出建议。此类信息有许多应用,例如在线购物推荐产品,推荐有趣的网站或帮助人们查找音乐和电影。本文将展示如何建立一个系统,以查找有共同爱好的人,并根据他人的喜好自动提出建议。
在使用像Amazon这样的在线购物网站之前,您可能会遇到一个推荐引擎。亚马逊会跟踪所有购物者的购物习惯,当您登录网站时,它将使用此信息来推荐您喜欢的产品。亚马逊甚至可以为您推荐您喜欢的电影,即使您以前只是从电影中购买过书籍也是如此。一些在线音乐会门票代理商会查看您以前看过的演出的历史,并提醒您可能会感兴趣的即将到来的演出。像reddit.com这样的网站可以让您对指向其他网站的链接进行投票,然后使用投票来推荐您可能会感兴趣的其他链接。
从这些示例中,您可以看到可以以多种不同方式收集首选项。有时,数据是人们购买的物品,对这些物品的意见可能表示为赞成/反对票,或表示为一到五的评分。在本文中,我们将研究代表这些案例的不同方式,以便它们都可以使用相同的算法集,并且我们将创建电影评论者得分和社交书签这样有效的示例。
协同过滤算法
获取产品、电影或娱乐网站的推荐的最简单的方法是询问您的朋友。 您还知道一些朋友的“品味”比其他朋友好,这是您通过观察他们通常是否喜欢与您相同的事物而逐渐了解到的。 随着越来越多的选项可用,通过问一小群人来决定您想要什么变得越来越不现实,因为他们可能并不知道所有选项。 这就是为什么开发了一套称为协同过滤的技术的原因。
协作过滤算法通常通过搜索一大群人并能找到品味与您相似的很小的集合出来。 它会查看他们喜欢的事物,并将它们组合起来以创建推荐的排名列表。 有几种不同的方法可以确定哪些人是相似的,并结合他们的选择来列出。 本文将介绍其中的一些。
“协同过滤”一词最早由Xerox PARC的David Goldberg于1992年在一篇名为“Using collaborative filtering to weave an information tapestry”的论文中使用。 他设计了一个名为Tapestry的系统,该系统允许人们以有趣或不感兴趣的方式注释文档,并使用此信息为其他人过滤文档。
现在有数百个网站对电影,音乐,书籍,约会,购物,其他网站,播客,文章甚至笑话采用某种协作过滤算法。
收集喜好
您需要做的第一件事是描述不同的人及其喜好的方式。 在Python中,执行此操作的一种非常简单的方法是使用嵌套字典。 如果您想完成本文中的示例,请创建一个名为Recommendations.py的文件,并插入以下代码以创建数据集:
# A dictionary of movie critics and their ratings of a small
# set of movies
critics={'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5, 'The Night Listener': 3.0},
'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5, 'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 3.5},
'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0, 'Superman Returns': 3.5, 'The Night Listener': 4.0},
'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'The Night Listener': 4.5, 'Superman Returns': 4.0,'You, Me and Dupree': 2.5},
'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 2.0},
'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0}}
该词典使用1到5的等级来表示这些电影评论家(和我)对某部电影的喜欢程度。 无论偏好如何表示,都需要一种将它们映射到数值上的方法。 如果您正在构建购物网站,则可以使用值1表示某人过去曾购买过某商品,使用值0表示该人未曾购买过某商品。 对于一个对新闻报道进行投票的网站,可以使用–1、0和1来表示“不喜欢”,“不投票”和“喜欢”,如表2-1(用户对数值评分的操作的可能映射)所示:
Concert tickets |
Online shopping |
Site recommender |
|||
Bought |
1 |
Bought |
2 |
Liked |
1 |
Didn’t buy |
0 |
Browsed |
1 |
No vote |
0 |
Didn’t buy |
0 |
Disliked |
–1 |
使用字典可以方便地试验算法和达到说明目的,并且搜索和修改字典很容易。 启动您的Python解释器,然后尝试一些命令:
c:\code\collective\chapter2> python
Python 2.4.1 (#65, Mar 30 2005, 09:13:57) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>> from recommendations import critics
>> critics['Lisa Rose']['Lady in the Water']
2.5
>> critics['Toby']['Snakes on a Plane']=4.5
>> critics['Toby']
{'Snakes on a Plane':4.5,'You, Me and Dupree':1.0}
尽管您可以在词典的内存中容纳大量首选项,但是对于非常大的数据集,您可能希望将首选项存储在数据库中。
寻找相似用户
收集有关人们喜欢的事物的数据后,您需要一种方法来确定相似的人的品味。 您可以通过将每个人彼此比较并计算相似度得分来做到这一点。 有几种方法可以做到这一点,在这一节中,我将向您展示两个用于计算相似度得分的体系:欧几里得距离和皮尔森相关性。
欧几里得距离评价
计算相似度得分的一种非常简单的方法是使用欧几里得距离分数,该分数将人们共同排名的项目作为图表的轴。 然后,您可以在图表上绘制人员并查看他们之间的相似度,如图2-1所示。
Toby在Snakes轴上为4.5,在Dupree轴上为1.0。 两个人在偏好空间中的距离越近,他们的偏好就越相似。 由于图表是二维的,因此您一次只能查看两种排名,但是对于较大的排名集,原理是相同的。
要计算图表中Toby和LaSalle之间的距离,请取每个轴上的差,将它们平方并加在一起,然后取总和的平方根。 在Python中,可以使用pow(n,2)函数对数字求平方,并使用sqrt函数求平方根:
>> from math import sqrt
>> sqrt(pow(5-4,2)+pow(4-1,2))
3.1622776601683795
此公式计算距离,对于相近的人来说,距离会更小。 但是,您需要一个为相似的人提供更高的值的函数。 可以通过在函数中加1(这样就不会除以零的误差)并将其取反来完成:
>> 1/(1+sqrt(pow(5-4,2)+pow(4-1,2)))
0.2402530733520421
此新函数始终返回0到1之间的值,其中值1表示两个人的偏好相同。 您可以将所有内容放在一起以创建一个用于计算相似度的函数。 将以下代码添加到Recommendations.py:
from math import sqrt# Returns a distance-based similarity score for person1 and person2
def sim_distance(prefs,person1,person2):# Get the list of shared_items si={}for item in prefs[person1]:if item in prefs[person2]: si[item]=1# if they have no ratings in common, return 0if len(si)==0: return 0# Add up the squares of all the differencessum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2)for item in prefs[person1] if item in prefs[person2]])return 1/(1+sum_of_squares)
可以使用两个名字调用此函数以获得相似分值。 在您的Python解释器中,运行以下命令:
>>> reload(recommendations)
>>> recommendations.sim_distance(recommendations.critics,
... 'Lisa Rose','Gene Seymour')
0.148148148148
这为您提供了Lisa Rose和Gene Seymour之间的相似分值。 尝试使用其他名称,以查看是否可以找到有更多或更少共同之处的人。
皮尔逊相关系数
确定人们兴趣之间相似度的一种稍微复杂的方法是使用皮尔逊相关系数。 相关系数可以衡量两组数据在一条直线上的拟合程度。 这个公式比欧几里得距离更复杂,但是在数据标准化程度不高的情况下(例如,如果评论家的电影排名通常比平均值苛刻),它往往会产生更好的结果。
要形象化此方法,您可以在图表上绘制两个评论者的评分,如图2-2所示。 米克·拉萨尔(Mick LaSalle)将超人评为3,吉恩·西摩(Gene Seymour)将其评为5。
您还可以在图表上看到一条直线。 这被称为最佳拟合线,因为它尽可能靠近图表上的所有项目。 如果两位评论家对每部电影都具有相同的评分,则这条线将是对角线,并且会触及图表中的每个项目,因此相关分值为1。在所示的情况下,评论家对几部电影持不同意见,因此相关分值约为0.4。 图2-3显示了一个更高的相关性示例,约为0.75。
从图中可以看到,使用皮尔森系数的一个有趣方面是它可以校正夸大分值。在此图中,杰克·马修斯(Jack Matthews)的得分往往比丽莎·罗斯(Lisa Rose)高,但是这条线仍然合适,因为他们有相对相似的喜好。如果一个评论家倾向于给出比其他评论家更高的分数,那么如果他们的得分之间的差异是一致的,则仍然可以存在完美的相关性。前面描述的欧几里得距离系数说,两个评论家是不一样的,因为即使他们的品味非常相似,一个评论家总是比另一个评论家更苛刻。根据您的特定应用,此方法可能是或不是您想要的。
皮尔森相关系数的代码首先找到了两个评论家都评价过的项目。然后,它计算两个评论家评级的和与平方和,并计算两个评级的乘积之和。最后,它使用这些结果来计算皮尔逊相关系数,在下面的代码中以粗体显示。与距离评价不同,此公式不是很直观,但是它的确告诉您变量共同变化量除以变量各自变化量的乘积。
要使用此公式,请创建一个新函数,该新函数的签名与Recommendations.py中的sim_ distance函数相同:
# Returns the Pearson correlation coefficient for p1 and p2
def sim_pearson(prefs,p1,p2):# Get the list of mutually rated items si={}for item in prefs[p1]:if item in prefs[p2]: si[item]=1# Find the number of elements n=len(si)# if they are no ratings in common, return 0 if n==0: return 0# Add up all the preferences sum1=sum([prefs[p1][it] for it in si]) sum2=sum([prefs[p2][it] for it in si])# Sum up the squares sum1Sq=sum([pow(prefs[p1][it],2) for it in si]) sum2Sq=sum([pow(prefs[p2][it],2) for it in si])# Sum up the products pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])# Calculate Pearson scorenum=pSum-(sum1*sum2/n)den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n)) if den==0: return 0r=num/denreturn r
此函数将返回–1到1之间的值。值1表示两个人对每个项目的评分完全相同。 与距离指标不同,您无需更改此值即可将其调整为正确的比例。 现在,您可以尝试获取图2-3的相关分数:
>>> reload(recommendations)
>>> print recommendations.sim_pearson(recommendations.critics,
... 'Lisa Rose','Gene Seymour')
0.396059017191
您应该使用哪种相似度指标?
我在这里介绍了针对两种不同指标的函数,但是实际上还有更多的方法可以用来衡量两组数据之间的相似性。 使用哪种方法取决于您的应用程序,值得尝试使用Pearson,欧几里得距离或其他方法,看看您认为哪种方法可以带来更好的结果。
本文其余部分中的函数都有一个可选的相似性参数,它指向一个使实验更容易的函数:指定sim_pearson或sim_ vector以选择要使用的相似性参数。 只要它们具有相同的签名并返回浮点数(值越高表示越相似),就可以使用许多其他函数(例如Jaccard系数或Manhattan距离)作为相似性函数。
评论家排名
现在,您已经具有用于比较两个人的函数,您可以创建一个函数来针对给定的人对每个人评分并找到最接近的匹配项。 在这种情况下,我有兴趣了解哪些电影评论家具有与我相似的品味,从而使我知道在决定电影时应该采取谁的建议。 将此功能添加到Recommendations.py中,以获取与指定person具有相似品味的人的有序列表:
# Returns the best matches for person from the prefs dictionary.
# Number of results and similarity function are optional params.
def topMatches(prefs,person,n=5,similarity=sim_pearson):scores=[(similarity(prefs,person,other),other)for other in prefs if other!=person]# Sort the list so the highest scores appear at the top scores.sort( )scores.reverse( ) return scores[0:n]
此函数使用Python列表推导功能,使用先前定义的距离度量之一将我与字典中的每个其他用户进行比较。 然后,它返回排序结果的前n个项。
用我自己的名字调用此函数会给我一个电影评论家及其相似性得分的列表:
>> reload(recommendations)
>> recommendations.topMatches(recommendations.critics,'Toby',n=3)
[(0.99124070716192991, 'Lisa Rose'), (0.92447345164190486, 'Mick LaSalle'),
(0.89340514744156474, 'Claudia Puig')]
由此看来,我应该读Lisa Rose的评论,因为她的品味往往与我的相似。 如果您看过其中任何一部电影,都可以尝试根据自己的喜好将自己添加到词典中,看看自己最喜欢的评论家应该是谁。
推荐项目
寻找一个好的评论家很好,但是我现在真正想要的是电影推荐。 我只能看一下与我的口味最相似的人,然后找他喜欢但我还没看过的电影,这太具吸引力了。这种方法可能会意外地出现那些还没看过我可能喜欢的电影的评论者。 还可能出现一位评论家,他很奇怪地喜欢一部得到了通过topMatches()返回的所有其他评论家的不良评论的电影。
要解决这些问题,您需要通过获得对评论者进行排名的加权分数来对项目进行评分。 将所有其他评论家的票数乘以他们在每部电影给我的得分相似度。 表2-2显示了此过程的运行方式。
Critic |
Similarity |
Night |
S.xNight |
Lady |
S.xLady |
Luck |
S.xLuck |
Rose |
0.99 |
3.0 |
2.97 |
2.5 |
2.48 |
3.0 |
2.97 |
Seymour |
0.38 |
3.0 |
1.14 |
3.0 |
1.14 |
1.5 |
0.57 |
Puig |
0.89 |
4.5 |
4.02 |
3.0 |
2.68 |
||
LaSalle |
0.92 |
3.0 |
2.77 |
3.0 |
2.77 |
2.0 |
1.85 |
Matthews |
0.66 |
3.0 |
1.99 |
3.0 |
1.99 |
||
Total |
12.89 |
8.38 |
8.07 |
||||
Sim. Sum |
3.84 |
2.95 |
3.18 |
||||
Total/Sim. Sum |
3.35 |
2.83 |
2.53 |
该表显示了每个评论家的相关性得分,以及他们给我未定级的三部电影(《The Night Listener》,《Lady in the Water》和《Just My Luck》)的评分。S.x开头的列给出乘过评级的相似度,因此与我相似的人比不相似的人对整体得分的贡献更大。Total行显示所有这些数字的总和。
您可以只使用Total来计算排名,但随后有更多人评论过的电影将具有很大的优势。要对此进行纠正,您需要将评论该电影的评论家的所有相似度之和除以表格中的“Sim. Sum”行。由于《The Night Listener》已被所有人审核,因此将其总数除以所有相似之处的总和。不过,Puig没有评论《Lady in the Water》,因此影片的得分除以所有其他相似度的总和。最后一行显示了该除法的结果。
用于此的代码非常简单,并且可以与欧几里得距离或皮尔逊相关系数一起使用。将其添加到Recommendations.py:
# Gets recommendations for a person by using a weighted average
# of every other user's rankings
def getRecommendations(prefs,person,similarity=sim_pearson): totals={}simSums={}for other in prefs:# don't compare me to myself if other==person: continuesim=similarity(prefs,person,other)# ignore scores of zero or lower if sim<=0: continuefor item in prefs[other]:# only score movies I haven't seen yetif item not in prefs[person] or prefs[person][item]==0: # Similarity * Scoretotals.setdefault(item,0) totals[item]+=prefs[other][item]*sim # Sum of similarities simSums.setdefault(item,0) simSums[item]+=sim# Create the normalized listrankings=[(total/simSums[item],item) for item,total in totals.items( )]# Return the sorted list rankings.sort( ) rankings.reverse( ) return rankings
此代码循环遍历prefs词典中的每个其他人。 在每种情况下,它都会计算它们与指定person的相似程度。 然后,循环浏览他们为其评分的每个项目。项目的最终得分-每个项目的得分乘以相似度,然后将这些乘积全部相加。 最后,通过将每个分数除以相似度总和来归一化分数,然后返回排序的结果。
现在,您可以找出接下来要看的电影:
>>> reload(recommendations)
>>> recommendations.getRecommendations(recommendations.critics,'Toby') [(3.3477895267131013, 'The Night Listener'), (2.8325499182641614, 'Lady in the Water'), (2.5309807037655645, 'Just My Luck')]
>>> recommendations.getRecommendations(recommendations.critics,'Toby',
... similarity=recommendations.sim_distance)
[(3.5002478401415877, 'The Night Listener'), (2.7561242939959363, 'Lady in the Water'), (2.4619884860743739, 'Just My Luck')]
您不仅可以获得电影的排名列表,还可以猜测我对每部电影的评价。 该报告使我可以决定是否要看电影,还是完全可以做其他事情。 根据您的应用程序,如果没有符合给定用户标准的内容,您可能会决定不推荐。 您会发现,选择相似度指标对结果的影响很小。
您现在已经建立了一个完整的推荐系统,该系统可与任何类型的产品或链接配合使用。 您所要做的就是设置人员,项目和分数的字典,您可以使用它为任何人创建推荐。 在本文的后面,您将看到如何使用del.icio.us API来获取真实的数据,以便将网站推荐给人们。
配套产品
现在您知道了如何找到相似的人并为给定的人推荐产品,但是如果您想查看哪些产品彼此相似该怎么办? 您可能在购物网站上遇到了这种情况,特别是在该网站没有收集有关您的大量信息的情况下。 图2-4显示了Amazon网页上有关《 Programming Python》一书的一部分。
在这种情况下,您可以通过查看谁喜欢特定项目并查看他们喜欢的其他事物来确定相似性。 实际上,这与我们先前用来确定人与人之间相似性的方法相同,您只需要交换人与物品即可。 因此,您可以使用之前编写的相同方法,如果从:
{'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5}, 'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5}}
转变为:
{'Lady in the Water':{'Lisa Rose':2.5,'Gene Seymour':3.0}, 'Snakes on a Plane':{'Lisa Rose':3.5,'Gene Seymour':3.5}} etc..
在recommendations.py添加一个函数来实现转变:
def transformPrefs(prefs):result={}for person in prefs:for item in prefs[person]:result.setdefault(item,{})# Flip item and person result[item][person]=prefs[person][item]return result
现在,调用先前使用的topMatches函数来查找最相似于《Superman Returns》的电影集:
>> reload(recommendations)
>> movies=recommendations.transformPrefs(recommendations.critics)
>> recommendations.topMatches(movies,'Superman Returns')
[(0.657, 'You, Me and Dupree'), (0.487, 'Lady in the Water'), (0.111, 'Snakes on a Plane'), (-0.179, 'The Night Listener'), (-0.422, 'Just My Luck')]
请注意,在此示例中,实际上存在一些负相关得分,这表明喜欢Superman Returns的人们倾向于不喜欢Just My Luck,如图2-5所示。
更重要的是,您可以获得推荐的电影评论家。 比如也许您正在尝试确定邀请谁参加首映?
>> recommendations.getRecommendations(movies,'Just My Luck')
[(4.0, 'Michael Phillips'), (3.0, 'Jack Matthews')]
转变人员和物品会带来有用的结果并非总是很清楚,但是在很多情况下,它可以使您进行有趣的比较。 在线零售商可能会收集购买历史记录,以向个人推荐产品。 就像您在此处所做的那样,与人们进行逆向交易将使他们能够搜索可能购买某些产品的人。 这在计划大量清算某些物品的营销工作中可能非常有用。 另一个潜在的用途是确保最可能喜欢它们的人看到链接推荐链接的网站上的新链接。
构建del.icio.us链接推荐器
本部分向您展示如何从最受欢迎的在线书签网站之一中检索数据,以及如何使用该数据查找相似的用户并推荐他们之前从未见过的链接。 您可以从http://del.icio.us访问此站点,该站点允许人们设置帐户并发布感兴趣的链接以供以后参考。 您可以访问该网站并查看其他人发布的链接,也可以浏览许多其他人发布的“流行”链接。 来自del.icio.us的示例页面如图2-6所示。
与某些链接共享网站不同,del.icio.us不提供寻找相似人物或推荐您喜欢的链接的任何方式。 但是,您可以使用本文中讨论的技术自己添加该功能。
The del.icio.us API
来自del.icio.us的数据通过可返回XML格式数据的API提供。 为了使您更轻松,可以从http://code.google.com/p/pydelicious/source或http://oreilly.com/catalog/9780596529321下载Python API。
这个库有几个简单的调用来获取人们提交的链接。 例如,要获取有关编程的近期流行帖子的列表,可以使用get_popular调用:
>> import pydelicious
>> pydelicious.get_popular(tag='programming')
[{'count': '', 'extended': '', 'hash': '', 'description': u'How To Write Unmaintainable Code', 'tags': '', 'href': u'http://thc.segfault.net/root/phun/ unmaintain.html', 'user': u'dorsia', 'dt': u'2006-08-19T09:48:56Z'},
{'count': '', 'extended': '', 'hash': '', 'description': u'Threading in C#', 'tags': '', 'href': u'http://www.albahari.com/threading/', 'user': u'mmihale', 'dt': u'2006-05-17T18:09: 24Z'},
...etc...
您会看到它返回了一个词典列表,每个词典包含一个URL,说明以及发布该词典的用户。 由于您使用的是实时数据,因此结果与示例看起来会有所不同。 您将使用另外两个调用:get_ urlposts(返回给定URL的所有帖子)和get_userposts(返回给定用户的所有帖子)。 这些调用的数据以相同的方式返回。
建立数据集
无法从del.icio.us下载所有用户帖子的全部,因此您需要选择其中的一个子集。 您可以按照自己喜欢的任何方式执行此操作,但是要使示例显示出有趣的结果,最好找到经常发帖并发类似帖子的人。
一种方法是获取最近发布带有指定标签的流行链接的用户列表。 创建一个名为deliciousrec.py的新文件,并输入以下代码:
from pydelicious import get_popular,get_userposts,get_urlpostsdef initializeUserDict(tag,count=5): user_dict={}# get the top count' popular postsfor p1 in get_popular(tag=tag)[0:count]:# find all users who posted this for p2 in get_urlposts(p1['href']):user=p2['user'] user_dict[user]={}return user_dict
这将为您提供一个包含一些用户的字典,每个用户都引用一个空字典,等待被链接填充。 此API仅返回最后30个人来发布链接,因此该函数从前5个链接中收集用户以构建更大的集合。
与电影评论者数据集不同,在这种情况下,只有两种可能的评级:如果用户未发布此链接,则为0;如果用户发布此链接,则为1。 使用此API,您现在可以创建一个函数来为所有用户填写评分。 将此代码添加到deliciousrec.py:
def fillItems(user_dict):all_items={}# Find links posted by all users for user in user_dict:for i in range(3):try:posts=get_userposts(user) breakexcept:print "Failed user "+user+", retrying"time.sleep(4)for post in posts:url=post['href'] user_dict[user][url]=1.0 all_items[url]=1# Fill in missing items with 0for ratings in user_dict.values( ):for item in all_items:if item not in ratings:ratings[item]=0.0
这可用于构建类似于本章开头手动创建的critics字典的数据集:
>> from deliciousrec import *
>> delusers=initializeUserDict('programming')
>> delusers ['tsegaran']={} # Add yourself to the dictionary if you use delicious
>> fillItems(delusers)
第三行将用户tsegaran添加到列表中。 如果使用del.icio.us,则可以用自己的用户名替换tsegaran。
对fillItems的调用可能需要花费几分钟才能运行,因为它正在向该站点发出数百个请求。 有时,API会阻止重复太快的请求。 在这种情况下,代码将暂停并重试用户多达三遍。
推荐邻居和链接
建立数据集后,您可以将以前使用的功能应用于电影评论家数据集。 要随机选择一个用户并找到其他品味与其相似的用户,请在您的Python会话中输入以下代码:
>> import random
>> user=delusers.keys( )[random.randint(0,len(delusers)-1)]
>> user
u'veza'
>> recommendations.topMatches(delusers,user)
[(0.083, u'kuzz99'), (0.083, u'arturoochoa'), (0.083, u'NickSmith'), (0.083, u'MichaelDahl'), (0.050, u'zinggoat')]
您还可以通过调用getRecommendations获得有关此用户链接的建议。 这将按顺序返回所有项目,因此最好将其限制在前10位:
>> recommendations.getRecommendations(delusers,user)[0:10]
[(0.278, u'http://www.devlisting.com/'),
(0.276, u'http://www.howtoforge.com/linux_ldap_authentication'),
(0.191, u'http://yarivsblog.com/articles/2006/08/09/secret-weapons-for-startups'),
(0.191, u'http://www.dadgum.com/james/performance.html'),
(0.191, u'http://www.codinghorror.com/blog/archives/000666.html')]
当然,如前所述,首选项列表可以调换,使您可以根据链接而不是人员来构架搜索。 要找到一组与您发现特别有趣的链接相似的方法,可以尝试:
>> url=recommendations.getRecommendations(delusers,user)[0][1]
>> recommendations.topMatches(recommendations.transformPrefs(delusers),url)
[(0.312, u'http://www.fonttester.com/'),
(0.312, u'http://www.cssremix.com/'),
(0.266, u'http://www.logoorange.com/color/color-codes-chart.php'),
(0.254, u'http://yotophoto.com/'),
(0.254, u'http://www.wpdfd.com/editorial/basics/index.html')]
就是这样! 您已成功将推荐引擎添加到del.icio.us。 这里还有很多事情可以做。 由于del.icio.us支持按标签搜索,因此您可以查找彼此相似的标签。 您甚至可以通过发布具有多个帐户的相同链接来搜索试图操纵“热门”页面的人员。
基于物品的过滤
到目前为止,推荐引擎的实现方式要求使用每个用户的所有排名,以便创建数据集。这对于几千个人或项目可能会很好地起作用,但是像Amazon这样的大型网站拥有数百万的客户和产品,并将用户与其他用户进行比较,然后比较每个用户已评分的每种产品可能非常缓慢。此外,销售数百万种产品的网站之间的人与人之间的重叠可能很少,这可能使得很难确定哪些人相似。
到目前为止,我们使用的技术称为基于用户的协作过滤。另一种方法称为基于物品的协作过滤。在数据集非常大的情况下,基于物品的协作过滤可以提供更好的结果,并且它允许提前执行许多计算,因此需要推荐的用户可以更快地获得它们。
基于物品的过滤过程从我们已经讨论的内容中汲取了很多东西。通用技术是为每个项目预先计算最相似的项目。然后,当您希望向用户提出建议时,您可以查看该用户评分最高的项目,并创建与这些项目最相似的项目的加权列表。这里的重要区别在于,尽管第一步要求您检查所有数据,但项目之间的比较不会像用户之间的比较那样频繁地发生变化。这意味着您不必连续计算每个项目的最相似项目,您可以在流量较低的时候或在与主要应用程序分开的计算机上进行计算。
建立物品比较数据集
要比较商品,您需要做的第一件事就是编写一个函数来构建相似商品的完整数据集。 同样,不必每次都需要建议时执行此操作,而是只构建一次数据集,并在每次需要时重用它。
要生成数据集,请将以下函数添加到recommendations.py中:
def calculateSimilarItems(prefs,n=10):# Create a dictionary of items showing which other items they # are most similar to.result={}# Invert the preference matrix to be item-centric itemPrefs=transformPrefs(prefs)c=0for item in itemPrefs:# Status updates for large datasets c+=1if c%100==0: print "%d / %d" % (c,len(itemPrefs)) # Find the most similar items to this onescores=topMatches(itemPrefs,item,n=n,similarity=sim_distance) result[item]=scoresreturn result
此函数首先使用之前定义的transformPrefs函数反转分数字典,从而提供项目列表以及每个用户对项目的评分方式。 然后,它遍历每个项目,并将转换后的字典传递给topMatches函数,以获取最相似的项目及其相似性得分。 最后,它创建并返回物品的字典以及它们最相似的项的列表。
在您的Python会话中,构建商品相似性数据集并查看其外观:
>>> reload(recommendations)
>>> itemsim=recommendations.calculateSimilarItems(recommendations.critics)
>>> itemsim
{'Lady in the Water': [(0.40000000000000002, 'You, Me and Dupree'), (0.2857142857142857, 'The Night Listener'), ... 'Snakes on a Plane': [(0.22222222222222221, 'Lady in the Water'),
(0.18181818181818182, 'The Night Listener'),...
etc.
请记住,此功能仅需足够频繁地运行才能使项目相似性保持最新状态。 当用户群和评分数量较小时,您将需要更早地执行此操作,但是随着用户群的增长,项目之间的相似性分数通常会变得更加稳定。
获得推荐
现在,您可以使用物品相似性字典提供建议,而无需遍历整个数据集。 您将获得用户已排名的所有商品,找到相似的商品,然后根据相似程度对其进行加权。 物品字典可以轻松地用于获得相似性。
表2-3显示了使用基于项目的方法查找建议的过程。 与表2-2不同,评论家完全没有参与其中,而是有一些我已评分的电影与未曾评分的电影。
Movie |
Rating |
Night |
R.xNight |
Lady |
R.xLady |
Luck |
R.xLuck |
Snakes |
4.5 |
0.182 |
0.818 |
0.222 |
0.999 |
0.105 |
0.474 |
Superman |
4.0 |
0.103 |
0.412 |
0.091 |
0.363 |
0.065 |
0.258 |
Dupree |
1.0 |
0.148 |
0.148 |
0.4 |
0.4 |
0.182 |
0.182 |
Total |
0.433 |
1.378 |
0.713 |
1.764 |
0.352 |
0.914 |
|
Normalized |
3.183 |
2.598 |
2.473 |
每行都有我已经看过的电影,以及我对此的个人评价。 对于我没看过的每部电影,都有一列显示与我看过的电影有多相似的一栏,例如,Superman和The Night Listener之间的相似度得分是0.103。 以R.x开头的列显示了我对电影的评分乘以相似度,因为我给超人评分为4.0,所以超人行中“夜”旁边的值是4.00.103 = 0.412。
总计行显示每个电影的相似度得分总计和R.x列总计。 要预测我对每部电影的评价,只需将R.x列的总数除以相似度列的总数即可。 因此,我对“夜间听众”的预测评分为1.378 / 0.433 = 3.183。
您可以通过向recommendations.py添加最后一个功能来使用此功能:
def getRecommendedItems(prefs,itemMatch,user): userRatings=prefs[user]scores={} totalSim={}# Loop over items rated by this userfor (item,rating) in userRatings.items( ):# Loop over items similar to this onefor (similarity,item2) in itemMatch[item]:# Ignore if this user has already rated this item if item2 in userRatings: continue# Weighted sum of rating times similarity scores.setdefault(item2,0)scores[item2]+=similarity*rating# Sum of all the similarities totalSim.setdefault(item2,0) totalSim[item2]+=similarity# Divide each total score by total weighting to get an average rankings=[(score/totalSim[item],item) for item,score in scores.items( )]# Return the rankings from highest to lowest rankings.sort( )rankings.reverse( ) return rankings
您可以使用您之前构建的相似性数据集尝试使用此功能,以获取有关Toby的新建议:
>> reload(recommendations)
>> recommendations.getRecommendedItems(recommendations.critics,itemsim,'Toby')
[(3.182, 'The Night Listener'), (2.598, 'Just My Luck'), (2.473, 'Lady in the Water')]
The Night Listener仍然以相当大的优势排在第一位,尽管 Just My Luck和Lady in the Water虽然相距很近,但它们的位置发生了变化。 更重要的是,对getRecommendedItems的调用不必为所有其他评论者计算相似性得分,因为item相似性数据集是预先构建的。
使用MovieLens数据集
对于最后一个示例,我们来看一个称为MovieLens的真实电影评分数据集。 MovieLens是由明尼苏达大学(the University of Minnesota)的GroupLens项目开发的。 您可以从http://www.grouplens.org/node/12下载数据集。 这里有两个数据集。 根据您的平台,以tar.gz格式或zip格式下载100,000个数据集。
档案库包含几个文件,但是有趣的几个是u.item和u.data,u.item包含电影ID和标题的列表,u.data包含这个格式的实际评分:
196 |
242 |
3 |
881250949 |
186 |
302 |
3 |
891717742 |
22 |
377 |
1 |
878887116 |
244 |
51 |
2 |
880606923 |
166 |
346 |
1 |
886397596 |
298 |
474 |
4 |
884182806 |
每行都有一个用户ID,一个电影ID,用户对该电影的评分以及一个时间戳。 您可以获取电影标题,但用户数据是匿名的,因此本部分将仅使用用户ID。 该集合包含943位用户对1,682部电影的评分,每个用户至少评分20部电影。
在recommendations.py中创建一个名为loadMovieLens的新方法以加载此数据集:
def loadMovieLens(path='/data/movielens'):# Get movie titles movies={}for line in open(path+'/u.item'): (id,title)=line.split('|')[0:2] movies[id]=title# Load data prefs={}for line in open(path+'/u.data'): (user,movieid,rating,ts)=line.split('\t') prefs.setdefault(user,{}) prefs[user][movies[movieid]]=float(rating)return prefs
在您的Python会话中,加载数据并查看任意用户的一些打分:
>>> reload(recommendations)
>>> prefs=recommendations.loadMovieLens( )
>>> prefs['87']
{'Birdcage, The (1996)': 4.0, 'E.T. the Extra-Terrestrial (1982)': 3.0,
'Bananas (1971)': 5.0, 'Sting, The (1973)': 5.0, 'Bad Boys (1995)': 4.0,
'In the Line of Fire (1993)': 5.0, 'Star Trek: The Wrath of Khan (1982)': 5.0, 'Speechless (1994)': 4.0, etc...
现在,您可以获得基于用户的建议:
>>> recommendations.getRecommendations(prefs,'87')[0:30]
[(5.0, 'They Made Me a Criminal (1939)'), (5.0, 'Star Kid (1997)'),
(5.0, 'Santa with Muscles (1996)'), (5.0, 'Saint of Fort Washington (1993)'), etc...]
根据计算机的速度,以这种方式获取建议时您可能会注意到暂停。 这是因为您现在正在使用更大的数据集。 您拥有的用户越多,基于用户的推荐时间就越长。 现在尝试改为执行基于物品(item)的建议:
>>> itemsim=recommendations.calculateSimilarItems(prefs,n=50)
100 / 1664
200 / 1664
etc...
>>> recommendations.getRecommendedItems(prefs,itemsim,'87')[0:30]
[(5.0, "What's Eating Gilbert Grape (1993)"), (5.0, 'Vertigo (1958)'),
(5.0, 'Usual Suspects, The (1995)'), (5.0, 'Toy Story (1995)'),etc...]
尽管构建item相似度字典需要花费很长时间,但建好后几乎所有建议都是即时的。 此外,随着用户数量的增加,获得推荐所需的时间不会增加。
这是一个很好的数据集,可以进行试验以查看不同的评分方法如何影响结果,并了解基于项目的过滤和基于用户的过滤如何不同。 GroupLens网站上还有其他一些数据集,包括书籍,笑话和更多电影。
基于用户还是基于物品的过滤?
当获取大型数据集的建议列表时,基于物品的过滤比基于用户的过滤要快得多,但是它确实具有维护物品相似性表的额外开销。 另外,准确性存在差异,取决于数据集的“稀疏”程度。 在电影示例中,由于每个评论家都对几乎每部电影都进行了评分,因此数据集很密集(不稀疏)。 另一方面,不可能找到两个具有相同del.icio.us书签集的人,大多数书签是由一小群人保存的,从而导致数据集稀疏。 在稀疏数据集中,基于物品的过滤通常要优于基于用户的过滤,而在密集数据集中,这两项的性能大致相同。
要了解有关这些算法之间性能差异的更多信息,请查看Sarwar等人的论文“Item-based Collaborative Filter- ing Recommendation Algorithms”。 在http://citeseer.ist. psu.edu/sarwar01itembased.html
话虽如此,基于用户的过滤更易于实现,并且没有多余的步骤,因此通常更适合于频繁更改的较小的内存中数据集。 最后,在某些应用程序中,向人们显示其他用户具有与自己的偏好相似的偏好具有自己的价值-可能不是您想在购物站点上执行的操作,而是可能在链接共享或音乐推荐站点上执行的操作。
您现在已经了解了如何计算相似性得分以及如何使用相似度来比较人员和项目。 本章介绍了两种不同的推荐算法:基于用户的推荐算法和基于物品的推荐算法,以及保持人们的喜好并使用del.icio.us API构建链接推荐系统的方法。
《 Programming Collective Intelligence》案例介绍与分析——Making Recommendations相关推荐
- programming collective intelligence 读书笔记(一):配置eclipse+pydev环境,运行delicious模块
快考toefl了,又产生了考前综合症,只能看看 programming collective intellifence放松放松..哎革命尚未成功,同志还需努力,什么时候不用再去考试.. 闲话不说了,在 ...
- An Algorithm Summary of Programming Collective Intelligence
Optimization 优化 PCI里面介绍了两个算法:simulated annealing,模拟退火和genetic algorithms,遗传算法. 无论哪种优化算法,这里都需要一个代价计算函 ...
- An Algorithm Summary of Programming Collective Intelligence (1)
就按照最后一章的顺序来说吧.很多名字都不知道中文该怎么说,就直接用英文名称了. Naive Bayesian Classifier 朴素贝叶斯分类器 nb算法是通过学习样本中已经分类的条目,计算生成条 ...
- 【自然语言处理与文本分析】用两个项目案例介绍文本挖掘方法论。
文本挖掘概要 文本挖掘的应用:(有实际案例) 运用文本挖掘进行公司治理(台湾证券交易所的案例) 证券交易所的功能就是监管上市公司的问题(财务不实,内部被掏空的问题).但是会出现一个盲点 比如一家公司宣 ...
- 2021年大数据Spark(二十一):Spark Core案例-SogouQ日志分析
目录 案例-SogouQ日志分析 业务需求 准备工作 HanLP 中文分词 样例类 SogouRecord 业务实现 搜索关键词统计 用户搜索点击统计 搜索时 ...
- Java Review - 线程池资源一直不被释放案例源码分析
文章目录 概述 问题复现 源码分析 小结 概述 在日常开发中为了便于线程的有效复用,经常会用到线程池,然而使用完线程池后如果不调用shutdown关闭线程池,则会导致线程池资源一直不被释放. 下面通过 ...
- 什么是HADOOP、产生背景、在大数据、云计算中的位置和关系、国内外HADOOP应用案例介绍、就业方向、生态圈以及各组成部分的简介(学习资料中的文档材料)
1. HADOOP背景介绍 1. 1.1 什么是HADOOP 1. HADOOP是apache旗下的一套开源软件平台 2. HADOOP提供的功能:利用服务器集群,根据用户 ...
- Spark Streaming 图片处理案例介绍
Spark Streaming 图片处理案例介绍 本文首先介绍了流式处理框架的设计原理.Spark Streaming 的工作原理,然后通过一个基于 Spark Streaming 编写的读取.分析. ...
- 视频教程-Python数据分析与案例教程:分析人口普查数据-Python
Python数据分析与案例教程:分析人口普查数据 多年互联网从业经验: 有丰富的的企业网站.手游.APP开发经验: 曾担任上海益盟软件技术股份有限公司项目经理及产品经理: 参与项目有益盟私募工厂.睿妙 ...
最新文章
- java安装好了打不开机_劝告大家!早餐打豆浆,黄豆泡好直接打不对,教你1招,豆浆香浓更丝滑...
- Kobject结构体分析
- 48.动态分区匹配算法(连续分区)
- 路由器选华硕还是tp_路由器的坑太多,就算写着“千兆”你也要当心丨618选购指南...
- 初始化模型参数 python_pytorch 网络参数 weight bias 初始化详解_python_脚本之家
- 简练软考知识点整理-建设项目团队
- Editplus中添加System.out.println()快捷键
- js检查身份证号是否正确
- dao层如何调用对象_如何实现DBCP数据库连接池工具类&mvc分层开发web流程操作?...
- Python:bs4的使用
- 不可逆调速matlab,双闭环不可逆直流调速系统课程设计(matlab仿真设计)
- vue 设置表单必填项
- Unity关于Layer的管理
- 性能优化 之 节流(throttle)与防抖(debounce) vue 使用
- 2020-11-13 Python 文件读写、os模块及递归函数
- 建立集群——rsh(remote shell)实现无密码访问
- VictoriaMetrics与Thanos方案对比
- php 中文 验证码,php 验证码 支持中文验证码
- CentOS6.5搭建asterisk及配置软电话
- 【论文分享】Sequence Directed Hybrid Fuzzing
热门文章
- Pytorch加载模型只导入部分层权重,即跳过指定网络层的方法
- 教你用JavaScript制作背景图像滚动效果
- 计算机专业课程计划,计算机专业课程表(教学计划)
- 2018最佳计算机配置,2018年主流的组装电脑配置是什么样的?
- 如何连接Access数据库
- VMOS+小黄鸟无root抓包(解决抓包无网络问题)(附工具)
- 脑的计算模型能带我们走多远
- 阿里王坚:用“机器智能”取代“人工智能”概念
- Heart Rate Variability Analysis with the HRV Toolkit: Basic Time and Frequency Domain Measures/背景
- 什么软件可以搜索python答案_什的组词