上一篇推荐系统(七)协同过滤之UserCF中UserCF的原理以及实验步骤,本篇着重讲述与之类似的ItemCF。
基本原理
-
核心思想
找到和用户A看过物品相似的物品,向A推荐这些物品。
-
物品相似度
w i , j = ∣ N ( i ) ⋂ N ( j ) ∣ ∣ N ( i ) ∣ ∣ N ( j ) ∣ w_{i,j} = \frac{|N(i)\bigcap N(j)|}{\sqrt{|N(i)||N(j)|}} wi,j=∣N(i)∣∣N(j)∣∣N(i)⋂N(j)∣计算物品相似度,就需要构建每个用户的对称物品矩阵,之后将这些对称物品矩阵都叠加在一起,就可以得到物品的喜欢记录,之后就能求出物品的相似度了,过程如下图所示。
由于有的用户同时喜欢很多个物品,因而这种用户可能会影响物品相似度的计算,屏蔽掉对物品感兴趣用户的多样性,因而相似度改写为如下公式:
w i , j = ∑ u ∈ N ( i ) ⋂ N ( j ) 1 l o g ( 1 + L i k e ( u ) ) ∣ N ( i ) ∣ ∣ N ( j ) ∣ w_{i,j}=\frac{\sum_{u \in N(i)\bigcap N(j)} \frac{1}{log(1+Like(u))}}{\sqrt{|N(i)||N(j)|}} wi,j=∣N(i)∣∣N(j)∣∑u∈N(i)⋂N(j)log(1+Like(u))1其中Like(u)代表的是用户u的喜欢物品的个数。
具体实现
-
计算物品相似度
- 计算每个item的热度,存放在movie_popular中
- 计算item的相似度矩阵,存放在movie_sim_matrix中
- 对相似度矩阵进行归一化
def calc_movie_sim(self): # 计算每个item的热度 for user, items in self.train_set.items(): for item in items: if item not in self.movie_popular: self.movie_popular[item] = 0 self.movie_popular[item] += 1 self.movie_count = len(self.movie_popular) # 计算item-item pair 在user的点击中出现了多少次,存放在self.movie_sim_matrix中 for user, items in self.train_set.items(): for item1 in items: for item2 in items: if item1 == item2: continue self.movie_sim_matrix.setdefault(item1, {}) self.movie_sim_matrix[item1].setdefault(item2, 0) self.movie_sim_matrix[item1][item2] += 1 # 计算归一化item-item pair的相似度,分子为item-item pair共现次数 # 分母为sqrt(item1的热度 * item2的热度) for item1, related_items in self.movie_sim_matrix.items(): for item2, count in related_items.items(): if self.movie_popular[item1] == 0 or self.movie_popular[item2] == 0: self.movie_sim_matrix[item1][item2] = 0 continue self.movie_sim_matrix[item1][item2] = \ count / math.sqrt(self.movie_popular[item1] * self.movie_popular[item2]) print("calc_movie_sim done")
-
针对某一输入用户,推荐资源
选择该用户看过的物品列表,对于物品列表的每个物品,都找到K个与之最相似的物品,将这些相似物品放在一起,取前N个物品进行最终的推荐。def recommend(self, user): K = self.n_sim_movie N = self.n_rec_movie rank = {} if user not in self.train_set: return [] watched_movie = self.train_set[user] for movie, rating in watched_movie.items(): if movie not in self.movie_sim_matrix: continue related_movie_info = sorted(self.movie_sim_matrix[movie].items(), key=itemgetter(1), reverse=True)[:K] for related_movie, w in related_movie_info: if related_movie in watched_movie: continue rank.setdefault(related_movie, 0.0) rank[related_movie] += w * float(rating) return sorted(rank.items(), key=itemgetter(1), reverse=True)[:N]
-
构建评价函数
可以参见推荐系统(七)协同过滤之UserCF。
优缺点
优点
- 个性化推荐效果好,因为其原理是针对用户自身的行为进行推荐
- 新用户快热:对于新用户的冷启动效果好,因为只要新用户有行为,一定会推送类似的物品
- 可解释性较强:直接推送播放历史的相似物品,因而令人信服
缺点
- 如果物品很多,则计算量很大
ItemCF VS UserCF
原理层面
一图抵千言
UserCF:
ItemCF:
实践层面
- 点击率
ItemCF召回资源的点击率要比UserCF的点击率要高,因为ItemCF的实时性要比UserCF的实时性要强,而且个性化推荐的效果要比UserCF要好。UserCF相当于把用户的行为给平滑掉了,不注重个性化因素,因为其原理是给输入用户推荐其邻居喜欢的物品,但邻居想看的物品并不一定是输入用户喜欢的。 - 操控性
ItemCF的操控性较强,因为其可解释,因而大多数推荐系统都会优先保证ItemCF的召回要稳定。