采用 GroupLens 提供的 MovieLens 数据集

数据集下载链接:​ ​https://grouplens.org/datasets/movielens/​​

采用此版本的数据集:该数据集包含 6000 多用户对 4000 多部电影的 100 万条评分。

使用TopN推荐方法

评测指标

最后,我们还需要评测推荐的新颖度,这里用推荐列表中物品的平均流行度度量推荐结果的

新颖度。如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。

基于用户的协同过滤算法
基于用户的协同过滤算法主要包括两个步骤。

(1) 找到和目标用户兴趣相似的用户集合。

(2) 找到这个集合中的用户喜欢的,且目标用户没有听说过的物品推荐给目标用户。

之前一直看不懂{a,b,d}||{a,c}为什么是等于6,看完别人的代码才知道这是len(A_movie)*len(B_movie),就是A看过的电影的部数乘以B看过的电影的部数

经过一天的挣扎,终于把大神的代码给看懂、注释完了

import sys
import random
import math
import os
from operator import itemgetter
from collections import defaultdict

random.seed(0)  #设置好随机种子,即相同的随机种子seed


class UserBasedCF(object):
    ''' TopN recommendation - User Based Collaborative Filtering '''

    def __init__(self):
        self.trainset = {}   #训练数据集
        self.testset = {}    #测试数据集
        self.n_sim_user = 20 #兴趣最近的20个用户
        self.n_rec_movie = 10  #系统推荐的10部电影
        self.user_sim_mat = {}  #用户兴趣相似度矩阵
        self.movie_popular = {}  #电影的欢迎系数
        self.movie_count = 0  #电影的数量
        print ('Similar user number = %d' % self.n_sim_user)
        print ('recommended movie number = %d' % self.n_rec_movie)

    def generate_dataset(self, filename, pivot=0.7):
        trainset_len = 0   #训练集的大小
        testset_len = 0    #测试集的大小
        for line in loadfile(filename):   #遍历文件的每一行
            user, movie, rating, _ = line.split('::')  #切割文本,第一个是用户号。第二个是电影号,第三个是评分
            if random.random() < pivot:     #加入训练集,训练集和测试集七三开
                self.trainset.setdefault(user, {})
                self.trainset[user][movie] = int(rating)  #建立用户-电影-评分的字典
                trainset_len += 1  #训练集大小加一
            else:                           #加入测试集
                self.testset.setdefault(user, {})
                self.testset[user][movie] = int(rating)
                testset_len += 1   #测试集大小加一


        print ('split training set and test set succ')
        print ('train set = %s' % trainset_len)
        print ('test set = %s' % testset_len)

    def calc_user_sim(self):  #计算用户之间的兴趣相似度
        print ('building movie-users inverse table...')
        movie2users = dict()

        for user, movies in self.trainset.items():
            for movie in movies:    #遍历每一部电影
                if movie not in movie2users:
                    movie2users[movie] = set()   #每部电影的观众的集合
                movie2users[movie].add(user)     #将该观众加入到该电影的观众集合中
                if movie not in self.movie_popular:  #如果该电影部长电影流行度数组
                    self.movie_popular[movie] = 0  #将该电影的流行度初始化为0
                self.movie_popular[movie] += 1   #每部电影的观影人数加一
        print ('build movie-users inverse table succ')

        # save the total movie number, which will be used in evaluation
        self.movie_count = len(movie2users)   #获得电影的部数
        print ('total movie number = %d' % self.movie_count)

        # count co-rated items between users
        usersim_mat = self.user_sim_mat   #用户之间的兴趣相似度矩阵
        print ('building user co-rated movies matrix...')

        for movie, users in movie2users.items():   #循环每一个键值对,即 for key,values in xxx.items()
            for u in users:   #u、v观众是否在同一部电影的观众集合里面
                usersim_mat.setdefault(u, defaultdict(int))
                for v in users:
                    if u == v:
                        continue
                    usersim_mat[u][v] += 1   #如果在同一部电影的观众里面,则兴趣点加一
        print ('build user co-rated movies matrix succ')

        # calculate similarity matrix
        print ('calculating user similarity matrix...')
        simfactor_count = 0

        for u, related_users in usersim_mat.items():
            for v, count in related_users.items():
                usersim_mat[u][v] = count / math.sqrt(len(self.trainset[u]) * len(self.trainset[v]))  #计算两个用户的兴趣相似度
                simfactor_count += 1

        print ('calculate user similarity matrix(similarity factor) succ')
        print ('Total similarity factor number = %d' % simfactor_count)

    def recommend(self, user):
        ''' Find K similar users and recommend N movies. 找到兴趣最近的前20个用户,从中找到最适合的前10部电影'''
        K = self.n_sim_user    #前面给出是20
        N = self.n_rec_movie   #前面给出是10
        rank = dict()
        watched_movies = self.trainset[user]  #当前用户看过的电影

        for similar_user, similarity_factor in sorted(self.user_sim_mat[user].items(),key=itemgetter(1), reverse=True)[0:K]:
        #排序,找出兴趣相似度最高的前20个用户
            for movie in self.trainset[similar_user]:
                if movie in watched_movies:   #如果该电影被该用户看过,则跳过
                    continue
                # predict the user's "interest" for each movie
                rank.setdefault(movie, 0)
                rank[movie] += similarity_factor
        #返回最好的N部电影
        return sorted(rank.items(), key=itemgetter(1), reverse=True)[0:N]

    def evaluate(self):
        ''' print evaluation result: precision, recall, coverage and popularity 计算召回率、准确率、覆盖率、新颖度'''
        print ('Evaluation start...')
        N = self.n_rec_movie    #n_rec_movie在前面已经给出是10
        #  varables for precision and recall
        hit = 0  #成功推荐的电影部数
        rec_count = 0   #总共推荐了多少部电影
        test_count = 0   #测试集中的电影部数
        # varables for coverage
        all_rec_movies = set()    #成功推荐的电影
        # varables for popularity
        popular_sum = 0

        for i, user in enumerate(self.trainset):   #i为下标,user为训练集的内容
            test_movies = self.testset.get(user, {})
            rec_movies = self.recommend(user)   #此处的recommend是该函数的前一个函数,即系统推荐的最适合的10部电影
            for movie, _ in rec_movies:
                if movie in test_movies:
                    hit += 1
                all_rec_movies.add(movie)
                popular_sum += math.log(1 + self.movie_popular[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)  #覆盖率
        popularity = popular_sum / (1.0 * rec_count)   #新颖度

        print ('precision=%.4f\trecall=%.4f\tcoverage=%.4f\tpopularity=%.4f' % (precision, recall, coverage, popularity))


def loadfile(filename):   #加载文件
    ''' load a file, return a generator. '''
    fp = open(filename, 'r')
    for i, line in enumerate(fp):  #循环遍历文件的每一行
        yield line.strip('\r\n')    #返回文件的一行内容,yield可以当做return
    fp.close()
    print ('load %s succ' % filename)


if __name__ == '__main__':
    ratingfile = os.path.join('ml-1m', 'ratings.dat')
    usercf = UserBasedCF()  #类的实例化
    usercf.generate_dataset(ratingfile)
    usercf.calc_user_sim()
    usercf.evaluate()

 

 

内容来源于网络如有侵权请私信删除