推荐之协同过滤(思路简单梳理)

来源:互联网 发布:神仙劫进阶准确数据 编辑:程序博客网 时间:2024/06/17 00:10

用途

  只要是平时经常浏览购物网站的人都会发现在网站的某些地方会有一些商品,推荐给我们,例如京东的首页,每个人浏览京东首页,看到的都是经过分析后推荐的商品。
  由于IBM软件工程师的[探索推荐引擎内部的秘密,第 1 部分: 推荐引擎初探] 、[探索推荐引擎内部的秘密,第 2 部分: 深入推荐引擎相关算法 - 协同过滤]、[探索推荐引擎内部的秘密,第 3 部分: 深入推荐引擎相关算法 - 聚类]三篇文章已经很好讲述推荐系统原理,下面只详细介绍下协同过滤

是什么

  找一群跟我品味相近的人,去看看他们经常浏览的商品,并且我没有浏览的推荐给我。

基于用户的协同过滤怎么做

第一步就是要收集每个人的偏好

  以电影为例,如果我有一份数据清单,有每个人对一些电影的评分,通过这些评分,我们就能知道某两个人对电影品味相近程度。
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}}
  上面是一个清单的例子,8个人对不同电影的评分。

第二步:计算用户之间相似

  三种计算两个用户之间相似程度的方法:分别是欧几里得距离、皮尔逊相关度和Tanimoto相似度。
  1.欧几里得距离
  简单说就是计算同一个用户对两部电影的评价在二维坐标系上的距离。这样的话,距离越近,两个用户就越相似,距离越远,两个用户的相似程度就越低,不过为了便于比较,我们需要把这个距离处理一下,使用这个距离的倒数,那么倒数越大,相似性就越大,倒数越小,相似性就越低,不过如果完全一样的时候,就会出现距离为0,就无法得到倒数,那么我们给除数加1,就解决了这个问题。

from imp import reloadfrom math import sqrtdef sim_distance(prefs,person1,person2):    si={}    for item in prefs[person1]:        if item in prefs[person2]: si[item]=1    #if they have no rating in common, return 0    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))

  时间复杂度: O(n)
  关键公式:d=(p1p2)2
  在python下输入:

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

就可以得到Lisa Rose和Gene Seymour的相似程度为0.14814814814814814
  2.皮尔逊相关系数
  如果两个人对电影的评分,一个人总是比另一个人高,那是不是说明这两个人之间的品味相差很大呢?不一定,也许是评判标准不一样,但是之间一定是有相关性的。通过二维坐标系,观察两个人对电影的评分,是可以找出一个线性关系。而这个直线的斜率,就是相关系数。
  求皮尔逊相关系数的代码:

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    r=num/den    return r

  时间复杂度: O(n)
  关键公式:d=E(XY)E(X)E(Y)(E(X2)(E(X))2)(E(Y2)(E(Y))2)
  在python下输入

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

后可以得到Lisa Rose和Gene Seymour的相关系数为0.39605901719066977
  0就是不相关,越靠近1就是正相关,反之靠近-1就是负相关。
  3.Tanimoto相似度
  Tanimoto系数也称为Jaccard系数,是Cosine相似度的扩展,也多用于计算文档数据的相似度
什么情况下使用:处理无打分的偏好数据。

def sim_tanimoto(prefs, p1, p2):    si = {}    for item in prefs[p1]:        if item in prefs[p2]:            si[item] = 1    length = len(si)    if length == 0: return 0    t = length/(len(prefs[p1]) + len(prefs[p2]) - length)    return t

  关键公式:T(x,y)=xiyixi2+yi2xiyi
  什么时候用欧几里德,什么时候用皮尔逊相关系数,取决于具体的应用和数据。

第三步:求与某一用户品味相近的其他用户的前几名

#从字典中,选出品位最近的用户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]

第四步:根据品味相近的用户推荐没有看过的电影

  简单的办法就是去看那些品位相近的用户,然后找一些我们没看的电影看,不过这才草率了,如果这个用户恰好喜欢什么变态电影,我们岂不是要中标。所以最终还是需要对电影评分,这个分数需要和用户的相似程度相关。

#利用所有他人评价值的加权平均,为某人提供建议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:                #相似度*评价值                totals.setdefault(item,0)                totals[item]+=prefs[other][item]*sim                #相似度之和                simSums.setdefault(item,0)                simSums[item]+=sim        #创建一个列表,列表里面是person没有看过的电影和评分        rankings=[(total/simSums[item],item) for item,total in totals.items()]        #排序        rankings.sort()        rankings.reverse()        return rankings

  在python下输入:

>>> recommendations.getRecommendations(critics, 'Michael Phillips')

就能得到为Michael Phillips推荐的电影和评分
[(2.8092760065251268, ‘Just My Luck’),
(2.694636703980363, ‘You, Me and Dupree’)]

  如果想知道哪些电影比较类似呢?怎么做?
  我们可以把用户和电影反过来,和寻找品位相近的用户一样,寻找类似的电影。

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

基于物品的协同过滤怎么做

  如果用户成千上万,这个时候去根据用户计算就比较麻烦,因为用户会不断的增长,但是用户所评价的物品的增长幅度不会有这么快,那么我们可以先把物品的相关程度计算出来,再推荐给用户。
  为了比较物品,首先构造一个每个物品的相关物品的集合。

def calculateSimilarItems(prefs,n=10):    #与这些电影最为相近的其他所有电影    result={}    #以物品为中心对偏好矩阵实施倒置处理    itemPrefs=transformPrefs(prefs)    c=0    for item in itemPrefs:    scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)        result[item]=scores    return result

  在python下输入

>>> itemsim = recommendations.calculateSimilarItems(critics)

  这样,给用户推荐物品的时候,通过物品的相似度,还有相似物品的评分,通过公式计算,就能得出推荐用户未购买或者评分的物品评分。

def getRecommendedItems(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

  在python下输入:

>>> recommendations.getRecommendedItems(critics, itemsim, 'Toby')

  可以得到:
[(3.182634730538922, ‘The Night Listener’),
(2.5983318700614575, ‘Just My Luck’),
(2.4730878186968837, ‘Lady in the Water’)]

另附

腾讯推荐系统架构图
腾讯推荐系统架构图


参考书籍:

  集体智慧编程

1 0