赞
踩
具体的学习资料可以参考王喆老师的《深度学习推荐系统》,已经梳理好了知识体系,我也将按照这个路线再次梳理一遍,同时做一些拓展和加深理解
系统过滤曾是多年前推荐系统领域的应用最广泛的模型,也是基石一样的存在,重要!重要!
这里推出两篇论文,我们将简单看下论文,并进行代码复现 ,这里主要讲两部分
(1)基于用户的协同过滤算法(UserCF): 给用户推荐和他兴趣相似的其他用户喜欢的产品
(2)基于物品的协同过滤算法(ItemCF): 给用户推荐和他之前喜欢的物品相似的物品
2001年 Item-Based Collaborative Filtering Recommendation Algorithms
2003年 Amazon.com Recommendations Item-to-Item Collaborative Filtering
就是协同大家的反馈、评价和意见一起对海量的信息进行过滤,从中筛选出目标用户可能感兴趣的信息这样一个推荐过程
简而言之,我们要判断目标用户是否喜欢某物品,就是将所有用户交互的物品,生成共现矩阵(这里就是指用户对于物品的评分,存成矩阵方便后面的计算),然后通过找到相似用户(或相似物品),看相似用户的评价,进而估计出目标用户的评价
计算相似度需要根据特点的不同选择不同的相似度计算方法, 比较常用的有下面几种,
余弦相似度( Cosine Similarity ) 衡量 了用户向量i和用户向量J之间的向量夹角大小。显然,夹角越小,证明余弦相似 度越大,两个用户越相似。
对于某些用户喜欢普遍低分或者普遍打高分,所以对于这种用户评分偏置的情况, 余弦相似度就不是那么好了, 可以考虑使用下面的皮尔逊相关系数。
(2)还有一种方式如下, 而是该物品的评分与此用户的所有评分的差值进行加权平均, 这时候考虑到了有的用户内心的评分标准不一的情况, 即有的用户喜欢打高分, 有的用户喜欢打低分的情况。
至此, 基于用户的协同过滤算法的推荐过程完成。
依次对应用户ID、物品ID、评分、时间戳
划分训练集和测试集
- # 声明两个字典, 分别是训练集和测试集
- trainSet, testSet = {}, {}
- trainSet_len, testSet_len = 0, 0
- pivot = 0.75 # 训练集的比例
-
- # 遍历data的每一行, 把userId, movidId, rating按照{user: {movidId: rating}}的方式存储, 当然定义一个随机种子进行数据集划分
- for ele in data.itertuples(): # 遍历行这里推荐用itertuples, 比iterrows会高效很多
- user, movie, rating = getattr(ele, 'userId'), getattr(ele, 'movieId'), getattr(ele, 'rating')
- if random.random() < pivot:
- trainSet.setdefault(user, {})
- trainSet[user][movie] = rating
- trainSet_len += 1
- else:
- testSet.setdefault(user, {})
- testSet[user][movie] = rating
- testSet_len += 1
例如:将用户1所交互的物品随机分3:1 Split trainingSet and testSet success! TrainSet = 75657 TestSet = 25179
- user_sim_matrix = {}
-
- # 构建“电影-用户”倒排索引 # key = movidID, value=list of userIDs who have seen this move
- print('Building movie-user table ...')
- movie_user = {}
- for user, movies in trainSet.items(): # 这里的user就是每个用户, movies还是个字典, {movieID: rating}
- for movie in movies: # 这里的movie就是movieID了
- if movie not in movie_user: # 如果movidID没在倒排索引里面
- movie_user[movie] = set() # 声明这个键的值是set, 保证用户不重复
- movie_user[movie].add(user) # 把用户加进去,
- print('Build movie-user table success!')
-
-
- # 下面建立用户相似矩阵
- movie_count = len(movie_user)
- print('Total movie number = %d' % movie_count)
-
- print('Build user co-rated users matrix ...')
- for movie, users in movie_user.items(): # movid是movieID, users是set集合
- for u in users: # 对于每个用户, 都得双层遍历
- for v in users:
- if u == v:
- continue
- user_sim_matrix.setdefault(u, {}) # 把字典的值设置为字典的形式
- user_sim_matrix[u].setdefault(v, 0)
- user_sim_matrix[u][v] += 1 # 这里统计两个用户对同一部电影产生行为的次数, 这个就是余弦相似度的分子
- print('Build user co-rated users matrix success!')
-
- # 下面计算用户之间的相似性
- print('Calculating user similarity matrix ...')
- for u, related_users in user_sim_matrix.items():
- for v, count in related_users.items(): # 这里面v是相关用户, count是共同对同一部电影打分的次数
- user_sim_matrix[u][v] = count / math.sqrt(len(trainSet[u]) * len(trainSet[v])) # len 后面的就是用户对电影产生过行为的个数
- print('Calculate user similarity matrix success!')
Building movie-user table ... Build movie-user table success! Total movie number = 8765 Build user co-rated movies matrix ... Build user co-rated movies matrix success! Calculating user similarity matrix ... Calculate user similarity matrix success!
- # 找到最相似的20个用户, 产生10个推荐
- k = 20
- n = 10
-
- aim_user = 3 # 目标用户ID
- rank ={}
- watched_movies = trainSet[aim_user] # 找出目标用户看到电影
-
- # 下面从相似性矩阵中找到与aim_user最相近的K个用户
-
- # v 表示相似用户, wuv表示相似程度
- for v, wuv in sorted(user_sim_matrix[aim_user].items(), key=lambda x: x[1], reverse=True)[0:k]: # 字典按值从大到小排序, 相关性高到第
-
- # 把v用户看过的电影推荐给目标用户
- for movie in trainSet[v]:
- if movie in watched_movies:
- continue
- rank.setdefault(movie, 0)
- rank[movie] += wuv
-
-
- # 产生最后的推荐列表
- rec_movies = sorted(rank.items(), key=itemgetter(1), reverse=True)[:n] # itemgetter(1) 是简洁写法
- # 准确率、召回率和覆盖率
- hit = 0
- rec_count = 0 # 统计推荐的影片数量, 计算查准率
- test_count = 0 # 统计测试集的影片数量, 计算查全率
- all_rec_movies = set() # 统计被推荐出来的影片个数, 无重复了, 为了计算覆盖率
- item_populatity = dict() # 计算新颖度
-
- # 先计算每部影片的流行程度
- for user, items in trainSet.items():
- for item in items.keys():
- if item not in item_populatity:
- item_populatity[item] = 0
- item_populatity[item] += 1 # 这里统计训练集中每部影片用户观看的总次数, 代表每部影片的流行程度
-
-
- # 计算评测指标
- ret = 0
- ret_cou = 0
- for user, items in trainSet.items(): # 这里得保证测试集里面的用户在训练集里面才能推荐
-
- test_movies = testSet.get(user, {})
- rec_movies = recommend(user)
- for movie, w in rec_movies:
- if movie in test_movies:
- hit += 1
- all_rec_movies.add(movie)
- ret += math.log(1+item_populatity[movie])
- ret_cou += 1
- rec_count += n
- test_count += len(test_movies)
-
-
- precision = hit / (1.0 * rec_count)
- recall = hit / (1.0 * test_count)
- coverage = len(all_rec_movies) / movie_count
- ret /= ret_cou*1.0
-
- print('precisioin = %.4f\nrecall = %.4f\ncoverage = %.4f\npopularity = %.4f' % (precision, recall, coverage, ret))
协同过滤的头部效应较为明显、泛化能力较弱
所以后续被加以改进,提出矩阵分解在系统过滤的基础上加入隐向量的概念,加强模型处理稀疏矩阵的能力,后续将会出矩阵分解(MF)的论文及复现代码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。