本文基于《集体智慧编程》的第二章,提供推荐的一篇文章,不得不说,这本书真的很好,在写这一篇文章的时候,我已经看到优化那一章,单单从优化角度来讲,特别清晰,从最简单的随机优化开始,讲述了爬山法,到模拟退火算法,到最为流行的遗传算法,感觉很有必要将学习的过程记录下来,便于我,或初学者来进入机器学习领域。
1.协作性过滤
随着社会的发展,根据群体偏好来为人们提供推荐显得越来越重要,比如,在线购物的商品推荐、热门网站的推荐,以及帮助人们寻找音乐和影片的应用。协作性过滤的思想就是集合大家的智慧,来为某人进行推荐。让我们实战实现这样的一种应用。
首先收集偏好,这篇博客代码基于python,在python中,使用嵌套的字典很容易实现表达不同人及偏好的方法。字典内容如下:
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}}2.寻找相近的用户:
收集完人们的偏好之后,我们需要一种方法来确定人们在品味方面的相似度。为此,我们可以将每个人与其他人进行对比,并计算他们的相似评价值。有多种评价方式,这篇博客介绍欧几里得距离和皮尔逊相关系数。
欧几里得距离评价:
def sim_distance(prefs,person1,person2): si ={} for item in prefs[person1]: if item in prefs[person2]: si[item] = 1 if len(si) ==0: return 0 sum_of_squares = sum([pow(prefs[person1][item]-prefs[person2][item],2) for item in prefs[person1] if item in prefs[person2]]) return 1/(1+sqrt(sum_of_squares))
皮尔逊相关系数:
数学公式如下,字迹有点丑。。。
代码如下:
def sim_pearson(prefs,p1,p2): si = {} for item in prefs[p1]: if item in prefs[p2]: si[item] = 1 n = len(si) if n==0: return 1; sum1 = sum([prefs[p1][it] for it in si]) sum2 = sum([prefs[p2][it] for it in si]) sum1Sq = sum([pow(prefs[p1][it],2) for it in si]) sum2Sq = sum([pow(prefs[p2][it],2) for it in si]) pSum = sum([prefs[p1][it]*prefs[p2][it] for it in si]) num = pSum-(sum1*sum2/n) den = sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n)) if den==0: return 0 return num/den3.为评论者打分:
既然我们已经有了对两个人的进行比较的函数,下面我们就可以编写函数,根据指定人员对每个人打分,并找出最接近的结果,以得到一个人员的有序列表,代码如下:
def topMatches(prefs,pearon,n=5,similarity=sim_pearson): scores = [(similarity(prefs,pearon,other),other) for other in prefs if other!=pearon] scores.sort() scores.reverse() return scores[0:n]
终于到最后一步了,找到一位趣味相投的影评者很不错,但我们需要一份影片的推荐,从相似的人找一部自己寒没有看过的影片。代码如下:
def getRecommendations(prefs,person,similarity=sim_pearson): total = {} simSum = {} for other in prefs: if other==person:continue sim=similarity(prefs,person,other) if sim<=0:continue for item in prefs[other]: if item not in prefs[person] or prefs[person][item]==0: total.setdefault(item,0) total[item]+=prefs[other][item]*sim simSum.setdefault(item,0) simSum[item]+=sim rankings = [(total/simSum[item],item) for item,total in total.items()] rankings.sort() rankings.reverse() return rankings
这样,我们不仅得到了一个经过排名的影片列表,而且还推测出了自己对每部电影的评价情况。根据这份结果,我们决定自己究竟要不要观看其中的某部电影。到这里,我们已经学会了基于用户的协同过滤算法。
匹配商品:
到现在为止,我们已经知道了如何为指定人员寻找品味相近者,以及如何向其推荐商品的方法。但是假如我们想了解哪些商品是彼此相近的,在这种情况下,我们可以通过查看那些人喜欢某一特定物品。在这种情况下,我们只需要将人员与物品对换即可:
<span style="font-size:12px;">def transformPrefs(prefs): result = {} for person in prefs: for item in prefs[person]: result.setdefault(item,{}) result[item][person]=prefs[person][item] return result</span>基于物品的过滤:
到现在为止,我们已经完成了一种推荐算法,但对于像Amazon这样的有着上百万客户和商人的大型网站而言,讲一个用户和所有的其他用户进行比较,然后再每位用户评过分的商品进行比较,其速度是无法忍受的。这样,我们要介绍另一种可供选择的算法成为基于物品的协作型过滤。
总体思路为为每件物品预先计算好最为相近的其他物品,然后,当我们想为某位用户提供推荐时,就可以查看他曾经评价过的物品,从中选择排位靠前者,再构造出一个加权列表,其中包含了最为相近的其他物品,因为物品见得比较不会像用户间的比较那么频繁。
1.构造物品比较数据集
计算物品之间的相似程度:
<span style="font-size:12px;">def calculateSimilarItems(prefs,n=10): result = {} itemPrefs=transformPrefs(prefs) c = 0 for item in itemPrefs: c+=1 if c%100==0: print '%d / %d' % (c,len(itemPrefs)) score = topMatches(itemPrefs, item, n=n, similarity=sim_distance) result[item]=score return result </span>2.获得推荐:
代码如下:
<span style="font-size:12px;">def getRecommendItems(prefs,itemMatch,user): userRatings = prefs[user] scores ={} totalSim={} for(item,rating) in userRatings.items(): for(similarity,item2) in itemMatch[item]: if item2 in userRatings: continue scores.setdefault(item2,0) scores[item2]+=similarity*rating totalSim.setdefault(item2,0) totalSim[item2]+=similarity rankings=[(score/totalSim[item],item) for item,score in scores.items()] rankings.sort() rankings.reverse() return rankings</span>到现在为止,已经成功的实现了推荐算法原理解释。让我们现在使用真实数据集来实战,使用的数据为MovieLens数据集。 点击打开链接
代码如下:
<span style="font-size:12px;">def loadMovieLens(path='ml-100k'): movies = {} for line in open(path+'/u.item'): (id,title)=line.split('|')[0:2] movies[id]=title 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 #print loadMovieLens() prefs = loadMovieLens() #基于用户的推荐 print getRecommendations(prefs, '87')[0:30] #基于物品的推荐 itemsim=calculateSimilarItems(prefs, n=50) print getRecommendItems(prefs, itemsim, '87')</span>到此为止,完整的推荐算法已经实现完。