集体智慧编程1寻找相近用户

首先给出一组数据:

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}}
怎样寻找相近用户呢,也就是将每个人与其他人进行对比,通过相似度评价值来进行衡量。
介绍两种相似度评价体系有:欧几里得距离和皮尔逊相关度
欧几里得距离也就是我们平时学习的距离。
比如我们对于Toby有'Toby': 'Snakes on a Plane':4.5,'You, Me and Dupree':1.0
对于LaSalle有:Mick LaSalle':  'Snakes on a Plane': 4.0, 'You, Me and Dupree': 2.0
这样我们就可以大概计算在两个属性情况下的距离。
>>> from math import sqrt
>>> sqrt(pow(4.5-4,2)+pow(1-2,2))
1.118033988749895

这样就可以计算出距离,偏好越相似的人,计算出来的距离就越小。另外我们也可以给出倒数,这样偏好越相似的人值就越大,不过考虑到倒数之后可能分母为0,这样在分母上加1,于是有如下:

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

这样我们给出一个完整的计算相似度的函数:

# Returns a distance-based similarity score for person1 and person2
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+sum_of_squares)
运行结果:
>>> import recommendations
>>> recommendations.sim_distance(recommendations.critics,'Lisa Rose','Gene Seymour')
0.14814814814814814
除了用欧几里得距离之外,我们可以用一个更复杂一点的方法来判断人们兴趣的相似度,用皮尔逊相关系数
用皮尔逊方法,可以解决一个叫“夸大分值”的情况,就是当一个人的评分总是比较高的时候,可能跟另外一个评分给都的比较低的人有同样的喜好。
皮尔斯相关系数给出的计算比较特别,我们从下面的过程中看出:
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 0
    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 score
    num=pSum-(sum1*sum2/n)
    den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
    if den==0: return 0
    r=num/den
    return r
运行结果:
>>> import recommendations
>>> print recommendations.sim_pearson(recommendations.critics,'Lisa Rose','Gene Seymour')
0.396059017191

上面介绍了两种度量方法,但实际上还有很多其他的方法,比如Jaccard系数或曼哈顿距离。

现在我们可以对两个人进行比较,那下面我们就可以编写函数根据某个人对其他人进行计算,找到最接近的匹配结果了。

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]

n是返回个数,运行

>>> import recommendations
>>> recommendations.topMatches(recommendations.critics,'Toby',n=3)
[(0.9912407071619299, 'Lisa Rose'), (0.9244734516419049, 'Mick LaSalle'), (0.8934051474415647, 'Claudia Puig')]
这样对于Toby可以参考Lisa Rise所撰写的评论。
不过我们的目的是为了推荐物品,目前可以找到相近的评论者,不过这个会出现一定的问题,比如对于一些影片还没有做评论,或者有些人对影片随意评价。
为了解决上述问题,我们对评价值进行加权。
image
加权就通过前面算法的相关系数,这样相关性越大,给定的权重也就越大,这样对于一个影片的推荐,我们通过计算所有人的评价值乘以相关系数的和 除以 相关系数的和,如上图total表示所有评价值乘以相关系数的和  sim.sum表示相关系数的和,如果没有评价则不加入。
上述过程代码如下:
def getRecommendations(prefs,person,similarity=sim_pearson):
  totals={}
  simSums={}
  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:
        # Similarity * Score
        totals.setdefault(item,0)
        totals[item]+=prefs[other][item]*sim
        # Sum of similarities
        simSums.setdefault(item,0)
        simSums[item]+=sim
  rankings=[(total/simSums[item],item) for item,total in totals.items()]
  rankings.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')]

现在我们知道了如何根据一个人寻找相近品味的人,以及如何推荐商品。

现在有这么一个问题,我们想了解一个商品跟哪些商品相近。

对于这个问题,我们可以通过查看哪些人喜欢某一个物品,以及这些人喜欢的其他物品,这样我们将数据反一下就可以用之前的方法了。

对调方法:

def transformPrefs(prefs):
  result={}
  for person in prefs:
    for item in prefs[person]:
      result.setdefault(item,{})
      result[item][person]=prefs[person][item]
  return result
这样我们看了一下匹配结果:
>>> import recommendations
>>> 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')]
而且根据匹配结果也可以看到相关评论人。
>>> recommendations.getRecommendations(movies,'Just My Luck')
[(4.0, 'Michael Phillips'), (3.0, 'Jack Matthews')]

 

下面就给出了一个真实的案例 构建一个基于del.icio.us的链接推荐系统

posted @ 2014-03-14 11:52  越影&逐日而行  阅读(369)  评论(0编辑  收藏  举报