《集体智慧编程》笔记(2 / 12):提供推荐

Making Recommendations

文章目录

    • 协作型过滤
    • 搜集偏好
    • 寻找相近的用户
      • 欧几里得距离评价
      • 皮尔逊相关度评价
      • 应该选用哪一种相似性度量方法
      • 为评分者打分
    • 推荐物品
    • 匹配相似商品
    • 构建一个基于某数据平台的链接推荐系统
      • 数据平台API
      • 构造数据集
      • 推荐近邻与链接
    • 基于物品的过滤
      • 构造物品比较数据集
      • 获得推荐
    • 使用MovieLens数据集
    • 基于用户进行过滤还是基于物品进行过滤
    • 小结
    • 源码
      • recommendations.py源码

如何根据群体偏好来为人们提供推荐。

协作型过滤

Collaborative Filtering

一个协作型过滤算法通常的做法是对一大群人进行搜索,并从中找出与我们品味相近的一群人。

算法会对这些人所偏爱的其他内容考察,并将它们组合起来构造出一个经过排名的推荐列表。有许多不同的方法可以帮助我们确定哪些人与自己品味相近,并将他们的选择组合成列表。

搜集偏好

Collecting Preferences

这里使用Python嵌套字典保存影迷对电影的评价。

数据结构:

评分={人a:{电影A:(1~5评分),电影B:(1~5评分),...},人b:......
}
# 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}}

recommendations.py源码

>>> import os
>>> os.getcwd()>>> os.chdir('.')>>> 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, 'Superman Returns': 4.0, 'You, Me and Dupree': 1.0}
>>>

寻找相近的用户

Finding Similar Users

我们需要方法来确定人们品味的相似程度。

为此,我们可将每个人与所有其他人进行对比,并计算他们的相似度评价值

有两种方法:

  1. 欧几里得距离;
  2. 皮尔逊相关度。

欧几里得距离评价

Euclidean Distance Score

若两人在“偏好空间”中的距离越近,他们的兴趣偏好就越相似。

多数量的评分项同样适用这距离公式。

计算两人间距离

>>> from math import sqrt
>>> sqrt(pow(4.5-4,2)+pow(1-2,2))
1.118033988749895
>>> 

通常要对这数进行处理,来对偏好越相近的情况给出越大的值。

因此,可计算得到距离值加1(这可避免遇到被零整除的错误),并取其倒数:

>>> 
>>> 1/(1+sqrt(pow(4.5-4,2)+pow(1-2,2)))
0.4721359549995794
>>> 

这样就返回0与1之间的值,值越大,偏好更相似。

构造出用来计算相似度的函数。

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_itemssi={}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)

使用示例

>>> import recommendations
>>> recommendations.sim_distance(recommendations.critics,'Lisa Rose', 'Gene Seymour')
0.14814814814814814
>>> 

皮尔逊相关度评价

Pearson Correlation Score

皮尔逊相关系数是判断两组数据与某一直线拟合程度的一种度量。

对应的公式比欧式距离公式要复杂,但是它在数据不是很规范normalized的时候(譬如,影评人对影片的评价总是相对于平均水平偏离很大时),会倾向于给出更好的结果。

上图,可看出一条直线。因其绘制原则是尽可能地靠近图上的所有坐标点,故而被称作最佳拟合线best-fit line

若两位评论者对所有影片的评分情况都相同,那么这条直线将成为对角线,并且会与图上所有的坐标点都相交,从而得到一个结果为1的理想相关度评价。

比上图有更佳拟合度的图。

皮尔逊方法能修正“夸大分值grade inflation”。若某人总是倾向于给出比另一个人更高的分值,而二者的分值之差又始终保持一致,则他们依然可能会存在很好的相关性。

换成欧式距离公式评价方法,会因为一个人的评价始终比另一个人的更为“严格”(从而导致评价始终相对偏低),得出两者不相近的结论,即使他们品味很相似也是如此。

构造出用来计算相似度的函数。

# Returns the Pearson correlation coefficient for p1 and p2
def sim_pearson(prefs,p1,p2):# Get the list of mutually rated itemssi={}for item in prefs[p1]: if item in prefs[p2]: si[item]=1# if they are no ratings in common, return 0if len(si)==0: return 0# Sum calculationsn=len(si)# Sums of all the preferences#1sum1=sum([prefs[p1][it] for it in si])#2sum2=sum([prefs[p2][it] for it in si])# Sums of the squares#3sum1Sq=sum([pow(prefs[p1][it],2) for it in si])#4sum2Sq=sum([pow(prefs[p2][it],2) for it in si])	# Sum of the products#5pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])# Calculate r (Pearson score)num=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则表明两个人对每一样物品均有则完全一致的评价。

>>> recommendations.sim_pearson(recommendations.critics,'Lisa Rose', 'Gene Seymour')
0.39605901719066977
>>> 

应该选用哪一种相似性度量方法

哪一种方法最优,完全取决于具体的应用。

为评分者打分

给出前几名相似度高的评论者。

# 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]scores.sort()scores.reverse()return scores[0:n]

使用示例

>>> recommendations.topMatches(recommendations.critics,'Toby',n=3)
[(0.9912407071619299, 'Lisa Rose'), (0.9244734516419049, 'Mick LaSalle'), (0.8934051474415647, 'Claudia Puig')]
>>> 

推荐物品

先前程序是找趣味相近的人。

我们希望一个影片的推荐。


当然可以找出趣味相近的人喜欢的影片中找出一部自己还未看过的影片。

这做法有点随意permissive

这是因为:

  1. 有可能该评论者未对某些影片作出评论,而这些影片恰恰是你中意的。
  2. 有可能该评论者对某影片情有独钟,其他评论者却对此影片嗤之以鼻。

因此,需要通过一个经过加权的评价值来为影片打分,评论者的评分结果因此而形成了先后排名的。

# 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 myselfif other==person: continuesim=similarity(prefs,person,other)# ignore scores of zero or lowerif 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 similaritiessimSums.setdefault(item,0)simSums[item]+=sim# Create the normalized listrankings=[(total/simSums[item],item) for item,total in totals.items()]# Return the sorted listrankings.sort()rankings.reverse()return rankings

接下来便可以得到适合的影片推荐

>>> import 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.461988486074374, 'Just My Luck')]
>>> 

到此,我们已经建立起了一个完整的推荐系统,它适用于任何类型的商品或网路链接。

我们所要做的全部事情就是:建立一个涉及人员、物品和评价值的字典,然后就可以借此来为任何人提供建议。

匹配相似商品

Matching Products

我们想了解哪些商品是彼此相近。

在这种情况下,我们可以通过查看某一特定物品被哪些人喜欢,以及哪些其他物品被这些人喜欢来决定相似程度。

事实上,和先前来决定人与人之间相似度的方法是一样的——只需将人员与物品对换即可。

先前的数据结构

评分={人a:{电影A:(1~5评分),电影B:(1~5评分),...},人b:......
}

换成

评分={电影A:{人a:(1~5评分),人b:(1~5评分),...},电影B:......
}

定义一个函数来进行对换

def transformPrefs(prefs):result={}for person in prefs:for item in prefs[person]:result.setdefault(item,{})# Flip item and personresult[item][person]=prefs[person][item]return result

然后,调用topMatche函数,得到一组与《Superman Returns》最为相近的影片:

>>> movies = recommendations.transformPrefs(recommendations.critics)
>>> recommendations.topMatches(movies,"Superman Returns")
[(0.6579516949597695, 'You, Me and Dupree'), (0.4879500364742689, 'Lady in the Water'), (0.11180339887498941, 'Snakes on a Plane'), (-0.1798471947990544, 'The Night Listener'), (-0.42289003161103106, 'Just My Luck')]
>>> 

'Just My Luck’与"Superman Returns"呈负相关关系

我们还可以为影片推荐评论者,例如,我们正在考虑邀请谁和自己一起参加某部影片的重映。

>>> 
>>> recommendations.getRecommendations(movies, 'Just My Luck')
[(4.0, 'Michael Phillips'), (3.0, 'Jack Matthews')]
>>> 

更多案例

  1. 为了向不同的个体推荐商品,在线零售商可能会收集人们的购买历史,然后找到购买商品的潜在客户。
  2. 在专门推荐链接的网站上,这样做可以确保新出的链接,能够被那些最有可能对它产生兴趣的网站用户找到。

构建一个基于某数据平台的链接推荐系统

这数据平台是del.icio.us在线书签网站。该网址登陆不了。

目标:利用平台的数据查找相近的用户,并向他们推荐以前未曾看过的链接。

数据平台API

pydelicious.py以及deliciousrec.py未知原因不能使用(可能网站问题)

下面两节置空。

构造数据集

推荐近邻与链接

基于物品的过滤

Item-Based Filtering

考虑到性能上的问题

试想对于Amazon有这海量客户,将一个客户 和 所有其他客户进行比较,然后再对 每位客户评分过的商品进行比较,工作量何其巨大。

同样,一商品销售量百万的网站,或许客户在偏好方面彼此间很少有重叠,这可能令客户的相似性判断变得十分困难。


先前才用到的技术成为基于用户的协作型过滤user-based collaborative filtering

接下来介绍基于物品的协作型过滤item-based collaborative filtering

基于物品的协作型过滤的优点

在拥有大量数据集的情况下,基于基于物品的协作型过滤能够得出更好的结论,而且它允许将大量计算任务预先执行(空间换时间),从而需要给予推荐的用户能够更快地得到他们所要的结果。


基于物品的协作型过滤的总体思路是

  1. 为每件物品预先计算好最为相近的其它物品。
  2. 然后,当为某位客户提供推荐,就可以查看它曾经评分过得的物品,从中选出排位靠前者,
  3. 再构造出一个加权列表,其中包含看了与这些选中物品最为相近的其他物品。

这里最为显著的区别在于,尽管第一步要求我们检查所有的数据,但是物品间的比较不会像用户间的比较那么频繁

这就意味着,无需不停计算与每样物品最为相近的其他物品,可将这样的运算任务安排在网络流量不是很大的时候进行,或者在独立于主应用之外的另一台计算机上独立进行(空间换时间)。

构造物品比较数据集

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-centricitemPrefs=transformPrefs(prefs)c=0for item in itemPrefs:# Status updates for large datasetsc+=1if c%100==0: print "%d / %d" % (c,len(itemPrefs))# Find the most similar items to this one#物-人 数据集 得出 最相似的 物scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)result[item]=scoresreturn result

运用示例

>>> import recommendations
>>> itemsim = recommendations.calculateSimilarItems(recommendations.critics)
>>> itemsim
{'Lady in the Water': [(0.4, 'You, Me and Dupree'), (0.2857142857142857, 'The Night Listener'), (0.2222222222222222, 'Snakes on a Plane'), (0.2222222222222222, 'Just My Luck'), (0.09090909090909091, 'Superman Returns')], 
'Snakes on a Plane': [(0.2222222222222222, 'Lady in the Water'), (0.18181818181818182, 'The Night Listener'), (0.16666666666666666, 'Superman Returns'), (0.10526315789473684, 'Just My Luck'), (0.05128205128205128, 'You, Me and Dupree')], 
'Just My Luck': [(0.2222222222222222, 'Lady in the Water'), (0.18181818181818182, 'You, Me and Dupree'), (0.15384615384615385, 'The Night Listener'), (0.10526315789473684, 'Snakes on a Plane'), (0.06451612903225806, 'Superman Returns')], 
'Superman Returns': [(0.16666666666666666, 'Snakes on a Plane'), (0.10256410256410256, 'The Night Listener'), (0.09090909090909091, 'Lady in the Water'), (0.06451612903225806, 'Just My Luck'), (0.05333333333333334, 'You, Me and Dupree')], 
'You, Me and Dupree': [(0.4, 'Lady in the Water'), (0.18181818181818182, 'Just My Luck'), (0.14814814814814814, 'The Night Listener'), (0.05333333333333334, 'Superman Returns'), (0.05128205128205128, 'Snakes on a Plane')], 
'The Night Listener': [(0.2857142857142857, 'Lady in the Water'), (0.18181818181818182, 'Snakes on a Plane'), (0.15384615384615385, 'Just My Luck'), (0.14814814814814814, 'You, Me and Dupree'), (0.10256410256410256, 'Superman Returns')]}
>>> 

获得推荐

现在,已经可以在不遍历整个数据集的情况下,利用反映物品相似度的字典(上一节给出的)来给出推荐。

可以取到用户评价过的所有物品,找出其相似物品,并根据相似度对其进行加权

可以容易地根据物品字典来得到相似度。

利用基于物品的方法寻找推荐过程


先前[#推荐物品]章节用到的表

本节用到表与[#推荐物品]表不同之处是没有涉及所有评论者

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 itemif item2 in userRatings: continue# Weighted sum of rating times similarityscores.setdefault(item2,0)scores[item2]+=similarity*rating# Sum of all the similaritiestotalSim.setdefault(item2,0)totalSim[item2]+=similarity# Divide each total score by total weighting to get an averagerankings=[(score/totalSim[item],item) for item,score in scores.items( )]# Return the rankings from highest to lowestrankings.sort( )rankings.reverse( )return rankings

运行示例

>>> 
>>> recommendations.getRecommendedItems(recommendations.critics,itemsim,'Toby')
[(3.182634730538922, 'The Night Listener'), (2.5983318700614575, 'Just My Luck'), (2.4730878186968837, 'Lady in the Water')]
>>> 

使用MovieLens数据集

涉及电影评价的真实数据集

网站提供很多有关电影数据集,最后选择大小最小的文件ml-100k.zip

该网站还有图书,笑话等数据,等待你的发掘。

注:个人将数据文件放置配套的源码包中。


只需关注的文件之一u.item

文件的前5行

一组有关影片ID和片名的列表

1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0
5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0

只需关注的文件之二u.data

文件的前5行

用户id	影片id	用户对影片的评分	用户的评价时间196	242	3	881250949
186	302	3	891717742
22	377	1	878887116
244	51	2	880606923
166	346	1	886397596

加载文件的函数

def loadMovieLens(path='/data/movielens'):# Get movie titlesmovies={}for line in open(path+'/u.item'):(id,title)=line.split('|')[0:2]movies[id]=title# Load dataprefs={}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

运用示例

>>> 
>>> prefs['20']
{'Return of the Jedi (1983)': 4.0, 'Jungle2Jungle (1997)': 4.0, 'Back to the Future (1985)': 3.0, 'Jurassic Park (1993)': 4.0, 'Sting, The (1973)': 3.0, 'Sabrina (1995)': 4.0, 'Island of Dr. Moreau, The (1996)': 1.0, 'Mission: Impossible (1996)': 3.0,
'Twister (1996)': 4.0, 'Toy Story (1995)': 3.0, 'Willy Wonka and the Chocolate Factory (1971)': 3.0, 'Sound of Music, The (1965)': 3.0, 'Home Alone (1990)': 2.0, 'Scream (1996)': 1.0, 'Braveheart (1995)': 5.0, 'Indiana Jones and the Last Crusade (1989)': 4.0,
'Young Frankenstein (1974)': 2.0, 'Raiders of the Lost Ark (1981)': 4.0, "Dante's Peak (1997)": 4.0, "Mr. Holland's Opus (1995)": 4.0, 'Die Hard (1988)': 2.0, 'Speed (1994)': 4.0, 'Michael (1996)': 1.0, 'Christmas Carol, A (1938)': 4.0, 'Lost World: Jurassic Park, The (1997)': 4.0,
'Ghost and the Darkness, The (1996)': 5.0,'African Queen, The (1951)': 3.0, 'Space Jam (1996)': 2.0, 'Ransom (1996)': 4.0, 'Silence of the Lambs, The (1991)': 3.0, 'Searching for Bobby Fischer (1993)': 5.0, "Preacher's Wife, The (1996)": 4.0, 'Blues Brothers, The (1980)': 3.0, 'Happy Gilmore (1996)': 1.0, 'Volcano (1997)': 4.0,
'Aliens (1986)': 2.0, 'Independence Day (ID4) (1996)': 3.0, 'E.T. the Extra-Terrestrial (1982)': 2.0, 'Seven (Se7en) (1995)': 2.0, 'Forrest Gump (1994)': 1.0, 'Aladdin (1992)': 3.0, 'Miracle on 34th Street (1994)': 3.0, 'Empire Strikes Back, The (1980)': 3.0, 'Eraser (1996)': 3.0,
"It's a Wonderful Life (1946)": 5.0, 'Star Wars (1977)': 3.0, 'Beauty and the Beast (1991)': 4.0, "One Flew Over the Cuckoo's Nest (1975)": 1.0}
>>> 

基于用户的推荐

>>> recommendations.getRecommendations(prefs,'20')[0:10]
[(5.0, 'World of Apu, The (Apur Sansar) (1959)'), (5.0, 'Whole Wide World, The (1996)'), (5.0, 'Thieves (Voleurs, Les) (1996)'), (5.0, 'Strawberry and Chocolate (Fresa y chocolate) (1993)'),
(5.0, 'Star Kid (1997)'), (5.0, "Someone Else's America (1995)"), (5.0, 'Sliding Doors (1998)'), (5.0, 'Santa with Muscles (1996)'), (5.0, 'Saint of Fort Washington, The (1993)'), (5.0, 'Quiet Room, The (1996)')]
>>> 

基于物品的推荐

>>> 
>>> itemsim = recommendations.calculateSimilarItems(prefs,n=50)
100 / 1664
200 / 1664
300 / 1664
400 / 1664
500 / 1664
600 / 1664
700 / 1664
800 / 1664
900 / 1664
1000 / 1664
1100 / 1664
1200 / 1664
1300 / 1664
1400 / 1664
1500 / 1664
1600 / 1664>>> 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)'), (5.0, 'Titanic (1997)'), (5.0, 'Sword in the Stone, The (1963)'), (5.0, 'Stand by Me (1986)'), (5.0, 'Sling Blade (1996)'), (5.0, 'Silence of the Lambs, The (1991)'),
(5.0, 'Shining, The (1980)'), (5.0, 'Shine (1996)'), (5.0, 'Sense and Sensibility (1995)'), (5.0, 'Scream (1996)'), (5.0, 'Rumble in the Bronx (1995)'), (5.0, 'Rock, The (1996)'), (5.0, 'Robin Hood: Prince of Thieves (1991)'), (5.0, 'Reservoir Dogs (1992)'), (5.0, 'Police Story 4: Project S (Chao ji ji hua) (1993)'), (5.0, 'House of the Spirits, The (1993)'),
(5.0, 'Fresh (1994)'), (5.0, 'Denise Calls Up (1995)'), (5.0, 'Day the Sun Turned Cold, The (Tianguo niezi) (1994)'), (5.0, 'Before the Rain (Pred dozhdot) (1994)'), (5.0, 'Assignment, The (1997)'), (5.0, '1-900 (1994)'), (4.875, "Ed's Next Move (1996)"), (4.833333333333333, 'Anna (1996)'),
(4.8, 'Dark City (1998)'), (4.75, 'Flower of My Secret, The (Flor de mi secreto, La) (1995)'), (4.75, 'Broken English (1996)')]

基于用户进行过滤还是基于物品进行过滤

在针对大数据集成生成推荐列表时,基于物品进行过滤的方式明显要比基于用户的过滤更快,不过他的确有维护物品相似度表的额外开销

对于稀疏数据集(如大多数书签都是为小众所收藏),基于物品的过滤方法通常要优于基于用户的过滤方法,而对于密集数据集(电影评价)而言,两者效果几乎一致。

基于用户的过滤方法更加易于实现,而且无需额外步骤,因此它通常更适用于规模较小的变化非常频繁的内存数据集。

在应用方面,告诉用户还有哪些人与自己有着相近偏好是有一定价值的,如有交友业务相关的。但对于一个购物网站而言,并不想这么做。

小结

  • 相似度评价值两方法

    1. 欧几里得距离公式
    2. 皮尔逊相关度公式
  • 协作型过滤

    1. 基于用户的协作型过滤user-based collaborative filtering
    2. 基于物品的协作型过滤item-based collaborative filtering

基本原数据结构:

  • 人-物
评分={人a:{电影A:(1~5评分),电影B:(1~5评分),...},人b:......
}
  • 物-人
评分={人a:{电影A:(1~5评分),电影B:(1~5评分),...},人b:......
}

本文用到的recommendations.py源码中重要函数或变量

函数或变量作用备注讲解所在章节
critics人-物数据集prefs参数
sim_distance(
prefs,person1,person2)
欧几里得距离公式计算相似度recommendations.sim_distance(
recommendations.critics,‘Lisa Rose’, ‘Gene Seymour’)
[#欧几里得距离评价]
sim_pearson(
prefs,p1,p2)
皮尔逊相关度公式计算相似度recommendations.sim_pearson(
recommendations.critics,‘Lisa Rose’, ‘Gene Seymour’)
[#皮尔逊相关度评价]
topMatches(prefs,person,
n=5,similarity=sim_pearson)
根据给出 人-物 / 物-人数据集,相关度计算算法(欧氏,皮氏) 得出前n最相似个人 / 物recommendations.topMatches(
recommendations.critics,‘Toby’,n=3)
[#为评分者打分],[#匹配相似商品]
getRecommendations(prefs,
person,similarity=sim_pearson)
根据人-物数据集, 相似度计算后再加权平均,得出推荐物recommendations.getRecommendations(
recommendations.critics,‘Toby’)

recommendations.getRecommendations(
movies, ‘Just My Luck’)
[#推荐物品],[#匹配相似商品]
transformPrefs(prefs)人-物数据集转换成物-人数据集recommendations.transformPrefs(
recommendations.critics)
[#匹配相似商品]
calculateSimilarItems(prefs,n=10)物-人 数据集 得出 各物的相似度,记作物-物recommendations.calculateSimilarItems(
recommendations.critics)
[#构造物品比较数据集]
getRecommendedItems(prefs,
itemMatch,user)
(一个)人-物,物-物数据集加权平均 得出推荐物recommendations.getRecommendedItems(
recommendations.critics,itemsim,‘Toby’)
[#获得推荐]

源码

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}}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_itemssi={}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)# Returns the Pearson correlation coefficient for p1 and p2
def sim_pearson(prefs,p1,p2):# Get the list of mutually rated itemssi={}for item in prefs[p1]: if item in prefs[p2]: si[item]=1# if they are no ratings in common, return 0if len(si)==0: return 0# Sum calculationsn=len(si)# Sums of all the preferencessum1=sum([prefs[p1][it] for it in si])sum2=sum([prefs[p2][it] for it in si])# Sums of the squaressum1Sq=sum([pow(prefs[p1][it],2) for it in si])sum2Sq=sum([pow(prefs[p2][it],2) for it in si])	# Sum of the productspSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])# Calculate r (Pearson score)num=pSum-(sum1*sum2/n)den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))if den==0: return 0r=num/denreturn r# 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]scores.sort()scores.reverse()return scores[0:n]# 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 myselfif other==person: continuesim=similarity(prefs,person,other)# ignore scores of zero or lowerif 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 similaritiessimSums.setdefault(item,0)simSums[item]+=sim# Create the normalized listrankings=[(total/simSums[item],item) for item,total in totals.items()]# Return the sorted listrankings.sort()rankings.reverse()return rankingsdef transformPrefs(prefs):result={}for person in prefs:for item in prefs[person]:result.setdefault(item,{})# Flip item and personresult[item][person]=prefs[person][item]return resultdef 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-centricitemPrefs=transformPrefs(prefs)c=0for item in itemPrefs:# Status updates for large datasetsc+=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 resultdef 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 itemif item2 in userRatings: continue# Weighted sum of rating times similarityscores.setdefault(item2,0)scores[item2]+=similarity*rating# Sum of all the similaritiestotalSim.setdefault(item2,0)totalSim[item2]+=similarity# Divide each total score by total weighting to get an averagerankings=[(score/totalSim[item],item) for item,score in scores.items( )]# Return the rankings from highest to lowestrankings.sort( )rankings.reverse( )return rankingsdef loadMovieLens(path='/data/movielens'):# Get movie titlesmovies={}for line in open(path+'/u.item'):(id,title)=line.split('|')[0:2]movies[id]=title# Load dataprefs={}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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445675.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode - Easy - 637. Average of Levels in Binary Tree

Topic Tree Description https://leetcode.com/problems/average-of-levels-in-binary-tree/ Given the root of a binary tree, return the average value of the nodes on each level in the form of an array. Answers within 10−510^{-5}10−5 of the actual answer w…

在CodeBlocks下配置GoogleTest单元测试框架

环境准备 Windows 10Code::Blocks 20.03Google Test 1.7.0CMake 3.11.0 编译GoogleTest 一、创建一个工作目录D:\gtest&#xff0c;将刚下载的Google Test 1.7.0、CMake 3.11.0的压缩包解压到刚创建的工作目录。 二、进入CMake文件夹的bin下&#xff0c;运行cmake-gui.exe&…

傻子都能看懂的马拉车Manacher

Manachers Algorithm 马拉车算法操作及原理 package advanced_001;public class Code_Manacher {public static char[] manacherString(String str) {char[] charArr str.toCharArray();char[] res new char[str.length() * 2 1];int index 0;for (int i 0; i ! res.len…

简单暴力到dp的优化(萌新篇)

想写一系列文章&#xff0c;总结一些题目&#xff0c;看看解决问题、优化方法的过程到底是什么样子的。 系列问题一&#xff1a;斐波那契数列问题 在数学上&#xff0c;斐波纳契数列以如下被以递归的方法定义&#xff1a;F(0)0&#xff0c;F(1)1, F(n)F(n-1)F(n-2)&#xff08…

LeetCode - Medium - 114. Flatten Binary Tree to Linked List

Topic TreeDepth-first Search Description https://leetcode.com/problems/flatten-binary-tree-to-linked-list/ Given the root of a binary tree, flatten the tree into a “linked list”: The “linked list” should use the same TreeNode class where the right…

简单暴力到dp的优化(入门篇)

上篇&#xff0c;我们提到&#xff0c;遇到问题&#xff0c;首先根据定义写出笨方法&#xff0c;找出依赖关系&#xff08;有些题这一步就不太简单&#xff0c;要自己归纳关系&#xff09;&#xff0c;然后进行优化&#xff0c;下面&#xff0c;我们通过几道此方面的经典的&…

简单暴力到dp的优化(初级篇)

一、一维非脑残 1 一个只包含A、B和C的字符串&#xff0c;如果存在某一段长度为3的连续子串中恰好A、B和C各有一个&#xff0c;那么这个字符串就是纯净的&#xff0c;否则这个字符串就是暗黑的。例如&#xff1a;BAACAACCBAAA 连续子串"CBA"中包含了A,B,C各一个&am…

ccpc河北大学生程序设计竞赛dp小总结

近期题目来自校赛&#xff0c;赛前训练&#xff0c;省赛热身&#xff0c;河北ccpc正式比赛。 题目一&#xff1a; 题目描述&#xff1a; 由于第m个台阶上有好吃的薯条&#xff0c;所以薯片现在要爬一段m阶的楼梯. 薯片每步最多能爬k个阶梯&#xff0c;但是每到了第i个台阶&a…

c语言简便实现链表增删改查

注&#xff1a;单追求代码简洁&#xff0c;所以写法可能有点不标准。 //第一次拿c开始写数据结构&#xff0c;因为自己写的&#xff0c;追求代码量少&#xff0c;和学院ppt不太一样。有错请指出 #include <stdio.h> #include <stdlib.h> #include <string.h>…

第一次课 课上代码

第一次课内容 学习心态及注意事项 信心 谦虚 脚踏实地 多动手 python简介 代码量少&#xff0c;简介&#xff0c;易上手&#xff0c;语法要求不过于严格&#xff0c; Python 库。 速度慢&#xff0c; 不可加密。 输出、变量、输入 数据类型&#xff1a;整数、浮点数…

计算机考研专业课只考一科的学校汇总

下列学校专业课只考1门 &#xff08;每项科目下的学校均按照最新学科评估结果由高到低进行排名&#xff09; C语言程序设计 1. 湖南大学 计算机技术&软工专硕&#xff08;信息科学与工程学院&#xff09; 2. 中国海洋大学 计算机技术&#xff08;01计算机应用技术方向&am…

数组实现栈

学习了改进&#xff0c;利用define typedef比上次写的链表更容易改变功能&#xff0c;方便维护&#xff0c;代码更健壮。 大佬别嫌弃&#xff0c;萌新总是很笨&#xff0c;用typedef都想不到。 #include<stdio.h> #include<stdbool.h> #define maxsize 10 typede…

简单暴力到dp的优化(中级篇)

下面再放三道我比较喜欢的&#xff0c;需要好好写一下的题。 第一题比较水 1. White Cloud is exercising in the playground. White Cloud can walk 1 meters or run k meters per second. Since White Cloud is tired,it cant run for two or more continuous seconds. Whi…

第二次课 课上代码

敲一遍&#xff0c;体会每行代码想表达的意思。 第二讲 创建.py文件 数据类型&#xff1a;布尔(and\or\not) 条件判断语句(if elif else) 列表基础操作&#xff08;特点、创建、增加元素、len()、下标、py切片&#xff09; >>> 5>4 True >>> 4>5 Fa…

第一次课 优秀作业展示

18级河北师大软件编程训练 很多同学非常认真的完成了作业&#xff0c;这里选出比较优秀的作业展示出来。 注&#xff1a;展示顺序不是排名 为了尊重同学们的劳动成果&#xff0c;并没有要代码&#xff0c;只是截图展示。 范天祚 &#xff08;傻兔子&#xff09; 熊静祎&…

dp打开思路:HDU1029 HDU1087 HDU1176 HDU1257 POJ1458(水题不水)

题目&#xff1a;https://vjudge.net/contest/68966#overview HDU - 1029 题意&#xff1a;找出出现次数超过一半的数字 蠢思路&#xff1a;排序找中间 DP&#xff1a;扫一遍一个变量count记录解出现的次数&#xff0c;是当前解就&#xff0c;否则--&#xff0c;count为负就…

dp打开思路2:POJ2533 HDU1114 HDU1260 HDU1160(水题不水)

题目&#xff1a;https://vjudge.net/contest/68966#overview POJ2533 最长上升子序列&#xff0c;很平常的题&#xff0c;但是维持单调队列二分还是值得一贴的&#xff0c;O(nlogn) 关键思想&#xff1a;出现在单调队列里的数都在当前接收的数之前&#xff0c;所以找到最小…

二分查找及一般拓展总结

二分-不止是查找哦 二分过程&#xff1a;首先&#xff0c;假设表中元素是按升序排列&#xff0c;将表中间位置记录的关键字与查找关键字比较&#xff0c;如果两者相等&#xff0c;则查找成功&#xff1b;否则利用中间位置记录将表分成前、后两个子表&#xff0c;如果中间位置记…

第三次课 课上代码

这次可能比较简短&#xff0c;这样也好&#xff0c;可读性比较强。 别问我为什么&#xff0c;我不会告诉你们我把代码关了的哼哼。 简单复习、注意事项及小知识强调讲解 作业讲解 列表的遍历 For循环&#xff08;这个参考切片&#xff0c;视频有详细讲解&#xff0c;一样的…

排序算法基本介绍及python实现(含详细注释)

对数组排序可以说是编程基础中的基础&#xff0c;本文对八种排序方法做简要介绍并用python实现。 代码中注释很全&#xff0c;适合复习和萌新学习。这是刚入学自己写的&#xff0c;可能难免比不上标准的写法&#xff0c;但是懒得改了。 文末会放和排序相关的基本拓展总结链接…