一、基本原理
协同过滤中另一种经典的方法就是基于用户的协同过滤方法,其思想是先计算用户u与其它用户的相似度,然后选取和u最相似的几个用户,把他们买过的产品推荐给u。主要也可分为两步:(1)建立物品-用户倒排表,如图2的上半部分所示;(2)建立用户相似度矩阵,如图2的下半部分所示。
基于用户的协同过滤算法的相似度的计算方法与基于物品的计算方式相同。对于与用户u相似度接近的K个用户,遍历他们有过正反馈的物品,计算出用户u对每一个物品的感兴趣程度:
其中,包含和用户u兴趣最接近的K个用户,
是对物品i有过行为的用户集合,
是用户u和用户v的相似度,
表示用户v对i的兴趣,然后根据感兴趣程度由高到低确定N个推荐给用户u的物品。
基于用户协同和基于物品协同的区别
(1)从推荐场景的角度考虑
ItemCF适用于购物网站,其中用户的数量远多于物品的数量,物品数据相对稳定,相似度计算量较小,且不必频繁更新;UserCF更适用于新闻、博客等社交网络,其内容更新非常频繁,即UserCF更注重社会化,而ItemCF更注重个性化。
(2)从多样性的角度考虑
即覆盖率,指推荐系统能否给用于提供多种选择。由于UserCF更倾向推荐热门物品,ItemCF的多样性远好于UserCF,容易发现并推荐长尾里的物品。
(3)从冷启动的角度考虑
UserCF中在新用户对很少物品产生行为后,不能立即对他进行个性化推荐,即暂时无法找到兴趣相投的用户。ItemCF中新用户只要对一个物品产生行为,就可以给他推荐和该物品相关的其它物品。
二,算法实践
采用GroupLens提供的MovieLens数据集,http://www.grouplens.org/node/73。本章使用中等大小的数据集,包含6000多用户对4000多部电影的100万条评分。该数据集是一个评分数据集,用户可以给电影评1-5分5个不同的等级。本文着重研究隐反馈数据集中TopN推荐问题,因此忽略了数据集中的评分记录。
1、包的加载与变量定义
该部分定义了所需要的主要变量,集合采用字典形式的数据结构。
import random
import math
from operator import itemgetter
class UserBasedCF():
def __init__(self):
self.n_sim_user = 20
self.n_rec_user = 10
self.train = {}
self.test = {}
self.user_sim_matrix = {}
self.movie_count = 0
2、数据加载
读取原始CSV文件,并划分训练集和测试集,训练集占比87.5%,同时建立训练集和测试集的用户字典,记录每个用户对电影评分的字典。
def get_dataset(self,filename,pivot=0.875):
train_len,test_len = 0,0
random.seed()
for line in self.load_file(filename):
user,movie,rating,timestamp = line.split(',')
if random.random()<pivot:
self.train.setdefault(user,{})
self.train[user][movie] = rating
train_len += 1
else:
self.test.setdefault(user,{})
self.test[user][movie] = rating
test_len += 1
print('Load dataset success!')
print('train set:%s, test set:%s'% (train_len,test_len))
def load_file(self,filename):
with open(filename,'r') as f:
for i,line in enumerate(f):
if i==0:
continue
yield line.strip('\r\n')
3、计算相似度矩阵
第一步建立电影-用户倒排表;第二步计算矩阵C,C[i][j]表示同时喜欢电影i和j的用户数,并考虑对活跃用户的惩罚;第三步计算用户间的相似性矩阵。
def calc_user_sim(self):
movie_user = {}
for user,movies in self.train.items():
for movie in movies:
if movie not in movie_user:
movie_user[movie] = set()
movie_user[movie].add(user)
print('Build movie-user table success!')
self.movie_count = len(movie_user)
for movies,users in movie_user.items():
for u in users:
for v in users:
if u == v:
continue
self.user_sim_matrix.setdefault(u,{})
self.user_sim_matrix[u].setdefault(v,0)
self.user_sim_matrix[u][v] += 1/math.log(1+len(users))
print('Build user co-rated movies matrix success!')
for u,related_users in self.user_sim_matrix.items():
for v,count in related_users.items():
self.user_sim_matrix[u][v] = count / math.sqrt(len(self.train[u])*len(self.train[v]))
print('Calculate user similarity matrix success!')
4、对用户进行推荐
针对目标用户U,找到K部相似的电影,并推荐其N部电影,如果用户已经看过该电影则不推荐。
def recommend(self,user):
K = self.n_sim_user
N = self.n_rec_user
rank = {}
watched_movies = self.train[user]
for v,wuv in sorted(self.user_sim_matrix[user].items(),key=itemgetter(1),reverse=True)[:K]:
for movie,rvi in self.train[v].items():
if movie in watched_movies:
continue
rank.setdefault(movie,0)
rank[movie] += float(wuv)*float(rvi)
return sorted(rank.items(),key=itemgetter(1),reverse=True)[0:N]
5、评估指标的计算
产生推荐并通过准确率、召回率和覆盖率进行评估。
def evaluate(self):
print('Evaluateing starting ...')
N = self.n_rec_movie
hit,rec_count,test_count = 0,0,0
all_rec_movies = set()
for i,user in enumerate(self.train):
test_movies = self.test.get(user,{})
rec_movies = self.recommend(user)
for movie,w in rec_movies:
if movie in test_movies:
hit += 1
all_rec_movies.add(movie)
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)/(1.0*self.movie_count)
print('precision=%.4f, recall=%.4f, coverage=%.4f'%(precision,recall,coverage))
结果如下所示,由于数据量较大,计算速度较慢,耐心等待即可。
Load dataset success!
train set:875020, test set:125188
Build movie-user table success!
Build user co-rated movies matrix success!
Calculate user similarity matrix success!
Evaluateing starting ...
precision=0.2322, recall=0.1120, coverage=0.2931
参考资料
[1]. https://blog.csdn.net/m0_37917271/article/details/82498308
[2]. 推荐系统与深度学习. 黄昕等. 清华大学出版社. 2019.
[3]. 推荐系统算法实践. 黄美灵. 电子工业出版社. 2019.
[4]. 推荐系统算法. 项亮. 人民邮电出版社. 2012.
[5]. 美团机器学习实践. 美团算法团队. 人民邮电出版社. 2018.
新家孟城口,古木余衰柳。来者复为谁,空悲昔人有。——王维《辋川集·孟城坳》