3_基于用户的协同过滤方法
1 直观描述
用户A有几个“关系很好”的朋友 B、C、D,通常B或C或D买了什么东西的话,A也要跟着买。昨天,B新买了一个物品 g ,而用户A之前也从没见过物品 g ,那么,购物平台就将 g 推荐给A。
2 基于用户协同过滤的步骤
⑴ 计算用户间的相似度。
⑵ 预测某一用户对物品集中他尚未进行评分的物品的评分,并依据计算出来的评分高低来进行推荐。也可以先对所有物品进行评分预测,然后剔除掉已评分的物品,对剩下的物品进行排序。
3 用户相似度的计算
用两个for循环遍历user_dict中的用户,通过集合的交集运算,很容易得到同时被用户u和用户v喜欢的物品数,至于每个用户喜欢的物品数,通过len()函数很容易得到,于是就得到所有的了。
上面这种做法将会把每两个用户之间的相似度都算一遍,计算量太大而且也没必要,因为在实际场景下,与用户A“关系很好”的朋友的数量通常是有限的。我们将用户A “关系很好 ”的那几个朋友找出来并计算他们与A的相似度就行了。
这就可以用上倒排表了。所谓倒排表,指的是从用户评分文件转换而来的物品用户倒排表。设倒排表为字典 T,则 T 中记录的是各个物品被哪些用户操作过。之后我们只计算操作过同一物品的不同用户之间的相似度就行了,计算的方法就是:对一个物品,用for循环遍历两遍其用户集。当然,我们还需要把所有物品给遍历一遍,这样才能累加得到同时被用户u和用户v喜欢的物品数。
下面我们定义一个函数实现用户相似度的计算。
def user_similarity( user_dict ):
# 构建倒排表
item_users = dict()
for u, u_items in user_dict.items():
for i in u_items.keys():
item_users.setdefault( i, set() )
if user_dict[u][i] > 0.0:
item_users[ i ].add(u) # 把用户u加入到物品 i 的用户集合中去
# 上面的过程得到了倒排表 item_users
# 构建用户在物品集上的同现矩阵,只对出现在同一个用户集里的两个用户进行计算
user_itemcount = dict() # 记录每个操作的物品数,后面要当做分母
count = dict() # 同现矩阵
for i , i_users in item_users.items():
for u in i_users:
user_itemcount.setdefault( u, 0 )
user_itemcount[u] += 1
count.setdefault(u, {} )
for v in i_users:
count[u].setdefault( v, 0 )
if u == v:
continue
count[u][v] += 1 / math.log( 1 + len(i_users) )
# len(i_users)表示操作过物品 i 的用户集里用户的数目,此处对热门物品进行了惩罚
similarity = dict() # 记录用户相似度
for u , u_users in count.items():
similarity.setdefault(u, {})
for v, v_u in u_users.items():
if u == v:
continue
similarity[u].setdefault(v, 0.0)
similarity[u][v] = v_u / math.sqrt( user_itemcount [u] * user_itemcount[v] )
return similarity
4 预测评分的过程
我们可以定义一个近邻用户数 k 。在对某个用户进行推荐时,我们把与该用户 “关系最好”的其它 k 个用户所操作过的物品全部找出来,并从中剔除掉该用户已经操作过的物品,然后只对剩下的物品进行评分预测。
我们先找出该用户评价过的所有物品,假设保存在 item_of_thisuser字典里,字典的键为各个评价过的物品。事实上, item_of_thisuser 很容易从原始评分文件user_dict中提取出来。
下面我们定义一个函数实现评分预测的过程,暂时不考虑最终的排序问题。
def prediction(thisuser, k=8):
result = dict()
sort_similarity = sorted( similarity[ thisuser ].items(), key=lambda x: x[1], reverse=True)
for v, wuv in sort_similarity[0: k]:
for i, r_vi in user_dict[v].items():
if i in item_of_thisuser:
continue
result.setdefault(i, 0)
result[i] += r_vi * wuv
return result
5 用户协同与物品协同的比较
从推荐场景而言,在电子商务、电影网站和图书网站等领域,用户数量远大于物品数量,且物品的数量更新速度并不快,可以考虑物品协同的方法。
对于新闻、博客等类似于信息流的领域,由于内容更新频率非常高,且内容很容易过时,可以考虑用户协同的方法。
用户协同更注重社会化因素,而物品协同更注重个性化因素。