基于用户的协作型过滤、基于物品的协作型过滤(产品推荐)

此篇为《集体编程智慧》第二章的主要内容——利用基于用户的协作型过滤、基于物品的协作型过滤来进行产品的推荐。

在此做一个总结:

一、基于用户的协作型过滤:

我的理解:求出所有用户与目标用户的相似度,该相似度类似于权值,相似度越高,权值越高,说明该用户对目标用户的影响越大。使用用户权值*用户物品评分,最后统计物品的和(这里还可以做一个缩放 每件物品和/权值和)。得到每件物品推荐给用户的评分。这个评分是所有用户的加权评分和。权值是通过相似度函数求得(欧几里德距离、皮尔逊相关系数)。如下图:


这里需要注意:虽然我们可以从权值高的用户产品中直接推荐目标用户还未存在的产品,但是这样可能会存在问题,一个人可能存在极端偏好。所以这里我们换一种方式通过加权的方式

二、基于物品的协作型过滤:

物品型过滤和用户型过滤很相似,就是将用户型过滤的用户和物品做一个调换。把物品当作用户,求每个物品最相近的物品。这样如果有新用户需要推荐,我们只需要将该用户评分过的物品中选出前面几个,然后找与这些物品相似度高的推荐就行。

好处:由于物品相似度受到已有数据的影响已经比较稳定了,所以我们不用时时更新物品之间相似度的关系。可以每隔一小段时间求一次,并且这个数据的计算是可以独立出来运算的。那么我们在推荐时可以直接利用算出来的数据,这样推荐速度更快。

小结:两种方法运算很相似,可以使用一个简单的函数就将用户型过滤变为物品型过滤。后者可以在大量数据集的情况下,提前将推荐数据独立运算出来,提高推荐速度。当然这也要视数据的稀疏情况而定。

下面是完整的代码模板:

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
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)))

#皮尔逊相关度
from math import sqrt
def sim_pearson(prefs,person1,person2):
	si = {}
	for item in prefs[person1]:
		if(item in prefs[person2]):
			si[item] = 1
	n = len(si)
	if(n == 0):
		return 0
	#求和
	sum1 = sum(prefs[person1][item] for item in si)
	sum2 = sum(prefs[person2][item] for item in si)
	#求平方和
	sum1_sq = sum(pow(prefs[person1][item],2) for item in si)
	sum2_sq = sum(pow(prefs[person2][item],2) for item in si)
	#求乘积和
	asum = sum(prefs[person1][item]*prefs[person2][item] for item in si)
	#计算皮尔逊评价值
	num = asum-(sum1*sum2/n)
	den = sqrt((sum1_sq-pow(sum1,2)/n)*(sum2_sq-pow(sum2,2)/n))
	if(den == 0):
		return 0
	return (num/den)

#匹配相似度高的	这里求得所有人和person的相似度降序排序,我们需要的是推荐物品,不是相似的人,后面利用相似度来计算物品的匹配程度
def topMatches(prefs,person,n=5,similarity=sim_pearson):
	scores = [(similarity(prefs,person,other),other) for other in prefs if other!=person]
	scores.sort(reverse=True)
	return scores[0:n]

#这个函数是基于用户的协作型过滤,直接传入字典和目标用户即可
#加权处理,为某人提供建议		对与person有相同数据的数据集进行加权处理,得到[(推荐分数,推荐名),(推荐分数,推荐名)]
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
	#建立一个缩放后的分数列表
	ranking = [(total/simSums[item],item) for item,total in totals.items()]
	ranking.sort(reverse=True)
	return ranking

#上面是基于用户的协作型过滤,如果是基于物品的协作型过滤,那只需要将上面测试字典的键值调换一下
#下面三个函数是基于物品的协作型过滤,1:转换数据 2:计算物品间相似度 3:根据用户使用的物品推荐相似物品
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("{}".format(c))
		#寻找与该物品最为相近的物品
		scores = topMatches(itemPrefs,item,n=n,similarity=sim_pearson)
		result[item] = scores
	return result		#为下面的基于物品的协作过滤 准备好了数据,下面可以直接用

def getRecommendeItems(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(reverse=True)
	return rankings


#使用上面的简单样例来测试,可以换作你的更为复杂的数据,进行实际操作(实际数据越大,那么运算时间越长)

#一、获取物品间的相似性,可以独立出来运算
itemsim = calculateSimilarItems(critics,n=50)
#二、获取推荐的列表30个
result = getRecommendeItems(critics,itemsim,'Toby')[0:30]
print(result)

我将用户型过滤和物品型过滤写在了一起,方便调用。可以使用更复杂的数据集来做实际的产品推荐,但是这里要说明以下,如果数据集太大,那么计算时间就会相对的延长。用户型过滤由于需要对所有用户进行计算,所以时间长;物品型过滤需要计算物品间的相似度,所以时间长,但是你可以独立出来或者交互式环境先计算物品间相似度,后面做推荐计算时就不用计算了。

由于这里使用的数据集是嵌套字典,所以需要转换DataFrame或其它形式为嵌套字典,不多说,这个比较简单。还可以改变上面函数,来适应你的数据集。


猜你喜欢

转载自blog.csdn.net/qq_36523839/article/details/81042232