协作型过滤是1992年David Goldberg 在施乐帕研究中心(Xerox PARC)的一篇题为《Using collaborative filtering to weave an information tapestry》的论文中首次使用的。现在大多数的web站点在各个方面都运用到了协作型过滤算法。
本文简单介绍基于用户的协同过滤算法和基于物品的协同过滤算法原理,最后将运用算法实现推荐观众合适的影片的实战。
以我的理解,user_based即在一大群人中找到与我们在某处有相似爱好的一小群人中,分析他们的所偏爱的其他内容,将这些进行排名为我们进行推荐。本文中所讨论的偏爱物品是对影片进行分析。我们首先获得数据集,即不同客户对不用影片的评分,分数越高则代表客户越喜欢这部影片。在这里运用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,'You,Me,and Dupree':3.0, 'The Night Listener':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,'Superman Returns':4.0,'You,Me,and Dupree':2.5, 'The Night Listener':4.5}, 'Mick LaSlle':{'Lady in the Water':3.0,'Snakes on a plane':4.0, 'Just My Luck':2.0,'Superman Returns':3.0,'You,Me,and Dupree':3.0, 'The Night Listener':2.0}, 'Jack Matthews':{'Lady in the Water':3.0,'Snakes on a plane':4.0, 'Superman Returns':5.0,'You,Me,and Dupree':3.5,'The Night Listener':3.0}, 'Toby':{'Snakes on a plane':4.5,'Superman Returns':4.0,'You,Me,and Dupree':1.0,} }
距离度量:指个体空间存在的距离,距离越远则说明个体间的差距越大。
这里说的是欧几里德距离,这是一种常见的距离度量,是多维空间各个点的绝对距离。我们需要的是找到的是个体差距越小,则这个距离值会越小。我们需要做个变换。对偏好越相近的情况给个越大的值,可以将函数值加1并取其倒数,这样就达到要求了。公式如下:
这个距离越近则说明用户的兴趣偏好越相近。以下是python实现的:
#返回一个有关person1和person2的基于距离的相似度评价 def sim_distance(prefs,person1,person2): #得到shared_item的列表 si={} for item in prefs[person1]: if item in prefs[person2]: si[item]=1; #如果两者没有共同之处,则返回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))
相似度度量即计算个体的相似度程度,与距离度量相反。相似度度量越小即个体间的兴趣偏好越接近。
这里介绍的是皮尔逊相关系数。皮尔逊相关系数更趋向于两者的兴趣趋势的一致,他们的距离可能不是很近,但是他们喜欢的类别的趋势是一致的就可以了。所以在这方面上比距离度量更精细。公式如下:
在代码实现的过程中,我们将分子分母都同除了一个n,进行了变换。
#返回P1和p2的皮尔逊相关系数 def sim_pearson(prefs,p1,p2): #得到双方都评价过的物品列表 si={} for item in prefs[p1]: if item in prefs[p2]:si[item]=1 #得到列表元素的个数 n=len(si) #如果两者没有共同之处,而返回1 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
还有比较常用的向量空间余玄相似度,是用向量空间两个向量夹角的余弦值作为衡量两个个体间的差异大小。相比距离度量,这个更着重于方向的关系,而不是长度和距离。
接下来就是为客户提供推荐了。我们可以很容易的找到与自己品味最相似的人,可以从他的所喜爱的影片找到自己还未看的影片。但这样还有很多的问题。我们需要通过一个加权的评价值来为影片评分。为每位客户影片评分乘以与被推荐人的相似度,这样就相似度大的人的影响更大。同时我们还需要将评分的总计值除以全部的相似度,这样解决了评论影片人数多少的关系。以下是代码的实现:
#利用所有他人的评价值的加权平均,为某人提供建议 def getcommendations(prefs,person,similarity=sim_pearson): totals={} simSums={} for other in prefs: #不要和自己做比较 if other==person:continue sim=similarity(prefs,person,other) #忽略评价值为零或者小于0的情况 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 #建立一个归一化的列表 rankings=[(total/simSums[item],item) for item,total in totals.items()] rankings.sort() rankings.reverse() return rankings
为Toby提供的推荐,运行结果如下:
import untitled2 reload(untitled2) untitled2.getcommendations(untitled2.critics,'Toby') Out[11]: [(3.273675797861307, 'The Night Listener'), (2.82647688154628, 'Lady in the Water'), (2.5146395019067813, '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 #构造物品的比较数据集 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)) scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance) result[item]=scores return result
对于用户物品推荐,需要加权后的评分,为了预测我们对每部电影的评分情况。我们需要将评分总计值除以相似度总计值。代码如下:
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 ranking
最后,基于用户的协同过滤与基于物品的协同过滤各有优缺点。User_based的过滤方法更容易实现,但他通常适用于规模较小的变化非常频繁的的内存数据集。而item_based更适合于稀疏数据。
以上内容是我在看集体智慧编程这本书第二章中所把握的内容。有不妥或错误的地方望指正出来。