UserCF流程整理(结合代码)
1 数据处理
1.1 加载数据
loadfile(filename)函数,功能是利用yield迭代的给后面的generate_dataset()函数逐条的提供数据。主要代码如下:
def loadfile(filename):
''' load a file, return a generator. '''
fp = open(filename, 'r')
for i, line in enumerate(fp):
yield line.strip('\r\n')
fp.close()
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,同时用yield生成器来逐个返回。这么处理是为了分批的return数据,因为生成器不像list、string用于迭代时将数据一直保存在内存,生成器(Generators)临时产生要迭代的值,且只能用1次,使用后即丢弃,这样可以解决数据量太大,一次全部处理占内存太多甚至内存不足的问题。
另外现在的open()函数都写在with里面,就不用担心漏写.close()函数而占用内存了。
1.2 生成数据集
generate_dataset(self, filename, pivot=0.7)函数,从上面的loadfile()函数逐条获取数据,对该条数据进行切割,分配user、movie、rating值。然后用随机数来分配该条数据放入训练集还是测试集,起到一个shuffle的作用,训练测试比例由参数pivot调节。
trainset与testset用字典唉存储,格式为{“user”:{“movies1”:int(rating1), “movies2”:int(rating2),…, }, …, }
主要代码如下:
def generate_dataset(self, filename, pivot=0.7):
''' load rating data and split it to training set and test set '''
for line in self.loadfile(filename): # 迭代获取line
user, movie, rating = line.split('\t') # rating文件格式UserID::MovieID::Rating::Timestamp
# split the data by pivot
if random.random() < pivot: # 用随机数来随机的将数据添加到训练集或是测试集,起到shuffle的作用
self.trainset.setdefault(user, {}) # setdefault(key, default=None)即当key不在字典中,就将key添加到字典中,值为设置的默认值。
self.trainset[user][movie] = int(rating)
else:
self.testset.setdefault(user, {})
self.testset[user][movie] = int(rating)
2 用户的相似度计算
2.1 生成item-user查找表
从trainset中取出数据,建立一个字典结构的item-user查找表movies2users,其中字典的key为电影的ID,value为看过这个电影的userID集合set()。形如:{“movie1ID”:{“user1ID”, “user2ID”,…}, …}
2.2 生成user间共同评分的电影个数矩阵,为计算user相似度做基础
然后根据movies2users里的电影与user的关系,构建用户共同评分矩阵usersim_mat(同样用字典来表示),格式为{user1ID:{useriID:共同评分电影个数, userjID:共同评分电影个数, …}, …},明显这是一个对称矩阵。
2.3 计算用户相似度
该计算在上面的usersim_mat矩阵上进行,没有创建新的矩阵{这里说的矩阵都是用字典表示的},相似度计算方法这里用了余弦相似度:
c o s ( θ ) = a ⋅ b ∣ ∣ a ∣ ∣ × ∣ ∣ b ∣ ∣ cos(\theta)=\cfrac{a \cdot b}{||a||\times||b||} cos(θ)=∣∣a∣∣×∣∣b∣∣a⋅b
3 CF推荐
协同过滤的推荐有两个超参数K和N分别表示找K个与目标用户相似的其他用户,给他推荐N个商品。
从user_sim_mat矩阵中拿出跟user用户相似度最高的前K个用户,然后遍历他们看过而user没看过的movies,并记录相似度。
最终根据movies的相似排名,取前N个推荐给用户。
def recommend(user):
''' Find K similar users and recommend N movies. '''
K = n_sim_user
N = n_rec_movie
rank = dict()
watched_movies = trainset[user]
for similar_user, similarity_factor in sorted(user_sim_mat[user].items(), key=itemgetter(1), reverse=True)[0:K]:
for movie in trainset[similar_user]:
if movie in watched_movies:
continue
# predict the user's "interest" for each movie
rank.setdefault(movie, 0)
# 如果该电影user没看过就追加到rank字典,值为两用户的相似度,后面该电影再出现其值会累加
rank[movie] += similarity_factor
# return the N best movies
return sorted(rank.items(), key=itemgetter(1), reverse=True)[0:N]
4 效果评估
取出测试集中的用户喜欢的电影,与推荐的电影进行匹配,推荐的电影在用户的训练集列表中即为推荐成功。
评估推荐效果好坏的有四个指标:精确率,召回率,覆盖率,和流行度
符号定义: R ( u ) R(u) R(u)代表根据用户在训练集上的行为给用户做出的推荐列表, T ( u ) T(u) T(u)代表用户在测试集上的行为列表, U U U表示所有的用户, I I I表示所有的物品,覆盖率中分子是给所有用户推荐的物品的集合,分母是所有物品集合。
Precision = ∑ u ∈ U ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∈ U ∣ R ( u ) ∣ \text { Precision }=\frac{\sum_{u \in U}|R(u) \cap T(u)|}{\sum_{u \in U}|R(u)|} Precision =∑u∈U∣R(u)∣∑u∈U∣R(u)∩T(u)∣
Recall = ∑ u ∈ U ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∈ U ∣ T ( u ) ∣ \text { Recall }=\frac{\sum_{u \in U}|R(u) \cap T(u)|}{\sum_{u \in U}|T(u)|} Recall =∑u∈U∣T(u)∣∑u∈U∣R(u)∩T(u)∣
Coverage = ∣ U u ∈ U R ( u ) ∣ ∣ I ∣ \text { Coverage }=\frac{\mid U_{u \in \mathrm{U}} R(u) \mid}{|I|} Coverage =∣I∣∣Uu∈UR(u)∣