什么是基于用户的协同过滤?
要我说,基于用户的协同过滤就是,当你在买东西的时候,提出购买过A物品的用户也购买过B。购买过A物品的用户就是跟你相似的用户,用他感兴趣的东西来预测你是否也感兴趣。这就是基于用户的协同过滤。
主要包含两个步骤:
(1)找到和目标用户相似的用户集合。
(2)找到这个集合中用户喜欢的,且目标用户没有听说过的物品推荐给目标用户。
现在我们来讨论一下第一步,找到用户相似的用户集合。就得计算用户之间的相似度。
计算用户的相似度
用户的相似度怎么计算,这里有一个余弦公式。令N(u) 表示用户 u 曾经有过正反馈的物品集合,令 N(v) 表示用户 v 曾经有过正反馈的物品集合。那么,我们可以通过下面的余弦公式进行用户 u 和 v 之间的相似度计算:
假设用户A 对物品{a,b,d} 有过行为,用户 B 对物品{a,c} 有过行为,利用余弦公式计算用户 A和 B 的相似度为
实现该相似度可以利用如下的代码:
def UserSimilarity(train): W = dict() for u in train.keys(): for v in train.keys(): if u == v:continue W[u][v] = len(train[u] & train[v]) W[u][v] /= math.sqrt(len(train[u]) * len(train[v]) * 1.0) return W
计算用户相似度-- 代码加速
上述对用户计算相似度的方法,就是分别将用户两两之间计算。这种方法的时间复杂度是 O(|U|*|U|),这在用户数很大是非常的耗时。事实上,很多用户相互之间没有对同样的物品产生过行为,即很多时候|N(u)nN(v)| = 0 。
为此,可以首先建立物品到用户的倒排表,对于每个物品保存对该物品产生过行为的用户列表。假设用户 u 和 v 同时属于倒排表中k 个物品对应的用户列表,那么
倒排表示例图 2- 7 所示:
代码实现如下所示:
def UserSimilarity(train): #创建倒排表 item_users = dict() for u ,items in train.items(): for i in items.keys(): if i not in item_users: item_users[i] = set() item_users[i].add(u) #建立 C[u][v] C = dict() N = dict() for i, users in item_users.items(): for u in users: N[u] += 1 for v in users: if u == v:continue C[u][v] += 1 #关系表 W[u][v] W = dict() for u,related_users in C.items(): for v,cuv in related_users.items(): W[u][v] = cuv / math.sqrt(N[u] * N[v]) return W
计算用户的相似度 -- 算法改进
前面讲述了计算用户相似度的最简单的公式--余弦公式。但这个公式过于粗糙。以图书为例,如果两个用户都买过《新华字典》,这丝毫不能说明他们兴趣相似,因为几乎每个人都买过《新华字典》。但是如果两个用户都买过《数据挖掘》,那可以认为他们兴趣比较相似,因为只有研究数据挖掘的人才会买这本书。换句话说,两个用户对冷门物品采取过同样的行为更能说明他们兴趣的相似度。因此对前面余弦公式加入惩罚项的公式和代码分别如下:
def UserSimilarity(train): #建立倒排表 item_users = dict() for u,items in train.items(): for i in items.keys(): if i not in item_users: item_users[i] = set() item_users[i].add(u) #建立 C[u][v] C = dict() N = dict() for i,users in item_users.items(): for u in users: N[u] += 1 for v in users: if u == v:continue # 这里加入热门物品的惩罚项 1/math.log(1 + len(users)) C[u][v] += 1 / math.log(1 + len(users)) * 1 #关系表 W[u][v] W = dict() for u,related_users in C.items(): for v,cuv in related_users.items(): W[u][v] = cuv / math.sqrt(N[u] * N[v]) return W
用户相似度问题解决后,进入算法第二步,找到 K 个最相似的用户推荐物品给目标用户
下列公式度量了 UserCF 算法中用户 u 对物品 i 的感兴趣程度:
其中 S(u,K) 包含和用户 u 兴趣最接近的 K 个用户,N(i) 是对物品 i 有过行为的用户集合, wuv 是用户u 和用户v 的相似度,rvi 代表用户 v 对物品 i 的兴趣,因为使用的是单一行为的隐反馈,所以所有的 rvi = 1 。代码实现 UserCF 推荐算法如下:
def Recommend(user,train,W): rank = dict() interacted_items = train[user] for v,wuv in sorted(W[user].items(),key = itemgetter(1),reverse = True)[0:K]: for i,rvi in train[v].items(): if i in interacted_items:continue rank[i] += wuv * rvi return rank
不同的 K 值对 UserCF 算法的影响和分析
准确率和召回率:可以看到推荐系统的精度指标并不和参数 K 成线性关系。因此选择合适的 K 值对获得高的推荐系统精度比较重要,推荐结果的精度对 K 也不是特别的敏感。
流行度:可以看到,K 越大则 UserCF算法的推荐结果就越热门。这是因为 K 决定了 UserCF 在给你做推荐时参考多少和你兴趣相似的其他用户的兴趣,那么如果 K 越大,参考的人越多,结果就越趋近于热门。
覆盖率: K 越大,流行度越高,自然覆盖率就可额下降。