2_基于物品的协同过滤方法
1 直观描述
基于物品的协同过滤方法,简单来说就是:通过分析历史数据发现,很多人的购物清单里同时存在啤酒与尿布,那么,对于某一个购买了尿布的客户,我们将啤酒推荐给他。这里我们可以再思考一个问题,就是将尿布推荐给买了啤酒的客户是否合理呢?
2 基于物品协同过滤的步骤
⑴ 构建物品与物品的同现矩阵,基于同现矩阵计算物品之间的相似度矩阵。
⑵ 预测某一用户对物品集中他尚未进行评分的物品的评分,并依据计算出来的评分高低来进行推荐。也可以先对所有物品进行评分预测,然后剔除掉已评分的物品,对剩下的物品进行排序。
给单个用户进行推荐,自然需要遍历一下他所评价过的物品。该用户对任意一个物品 j 的评分预测值为:
但是这样需要对所有物品都计算一次,计算量太大。
我们可以定义一个近邻物品数 k 。遍历一下该用户所有操作过的物品 i ,我们只对与物品 i 相似度最高的 k 个物品进行评分预测。比如,假设该用户操作过 10 个物品,取 k 的值为 8, 那么我们进行评分预测的物品数为 80 个(假设这里面没有出现重复的物品)。如果出现重复的物品,那么我们实际预测的物品数将小于 80。用字典数据类型比较容易解决重复的问题。
3 物品相似度的计算
①我们需要统计出每个物品被多少用户喜欢,即喜欢该物品的用户数。
②我们还需要计算出同现矩阵,即同时喜欢物品 i 和物品 j 的用户数。
根据①和②,我们便可以计算物品之间的相似度了。
下面我们写一个函数来实现这个功能。
def item_similarity(user_dict):
item_count = dict() # 用来保存喜欢某物品的用户数
count = dict() # 同现矩阵,用来保存同时喜欢物品 i 和物品 j 的用户数
# 下面我们遍历user_dict中的所有用户,来获得①和②两个步骤所需要的数据
for user, user_item in user_dict.items():
for i in user_item.keys(): # 这里仅计数,没利用上user_item.values()数据
item_count.setdefault( i, 0)
if user_dict[user] [ i ] > 0.0:
item_count[ i ] += 1
for j in user_item.keys():
if user_dict[user] [ i ] > 0.0 and user_dict[user] [ j ] > 0.0 and i != j:
count[ i ][ j ] +=1
# 下面基于同现矩阵计算相似度矩阵
similarity = dict() # 用来保存物品间的相似度
for i, i_itemsimilar_num in count.items():
similarity.setdefault( i, {} )
for j , num in i_itemsimilar_num.items():
similarity[ i ].setdefault( j , 0)
similarity[ i ][ j ] = num / math.sqrt( item_count[ i] * item_count[ j ])
return similarity
4 预测评分的过程
我们先找出该用户评价过的所有物品,假设保存在 item_of_thisuser字典里,字典的键为各个评价过的物品,并假设键的数目为 num_of_thisuser,字典的值为他给对应物品的评分。事实上, item_of_thisuser 很容易从原始评分文件user_dict中提取出来。
然后,对于item_of_thisuser里的各个键(即各个物品),我们通过similarity字典找出与其最相似的 k 个物品,最后将这 k * num_of_thisuser 个物品的预测评分去排序(假设任意两个键对应的 2k 个物品中不存在重复的)。
以上思路很简单,下面我们通过一个函数来实现一下。
def prediction(thisuser, k=8):
result = dict() # 用来保存各个物品的评分
for i, score in item_of_thisuser.items():
sort_similarity = sorted( similarity[ i ].items(), key=lambda x: x[1], reverse=True)
for j, wij in sort_similarity[0:k]:
if j in score in item_of_thisuser:
continue # 对于已经评分过的物品,没必要对其计算预测评分了
result.setdefault( j, 0)
result[ j ] += score * wij
return result # 此处先不对result里的物品按预测评分排序