数据挖掘——推荐系统(分别基于用户和商品)

近年来,推荐系统充分应用于我们生活中,我们购物时,购物软件会根据我们日常购物以及浏览信息向我们推荐系统自认为我们需要的东西;看小视频时,软件会记录我们浏览信息,向我们推荐我们经常浏览的类型等。通过这个实验了解这些推荐系统的原理及实现。

算法原理

基于特征的推荐系统架构:
当用户到来之后,推荐系统需要为用户生成特征,然后对每个特征找到和特征相关的物品,从而最终生成用户的推荐列表。因此,推荐系统的核心任务就被拆解为两部分,一个是如何给定用户生成特征,另一个是如何根据特征找到物品。
用户的特征包括:
(1) 人口统计学特征:年龄、性别等
(2) 用户的行为特征:包括浏览、收藏过什么等
(3) 用户的话题特征
推荐引擎架构主要包括3部分
(1) 负责从数据库或者缓存中拿到用户行为数据,通过分析不同行为,生成当前用户的特征向量
(2) 负责将用户的特征向量通过特征-物品相关矩阵转化为初始推荐物品列表
(3) 负责对初始的推荐列表进行过滤、排名等处理,从而生成最终的推荐结果。
算法存在一些缺点:首先,随着网站的用户数目越来越大,计算用户兴趣相似度矩阵将越来越困难,其运算时间复杂度和空间复杂度的增长和用户数的增长近似于平方关系。其次,基于用户的协同过滤很难对推荐结果作出解释。
基于物品的协同过滤算法主要分为两步:
计算物品之间的相似度
根据物品的相似度和用户的历史行为给用户生成推荐列表

数据获取

本实验采用Movie Lens数据集,来源于明尼苏达州立大学的GroupLens项目组开发的,根据数据处理的平台许可,适当选择合适的数据集大小。下载地址:https://www.kaggle.com/prajitdatta/movielens-100k-dataset/download

探索使用基于用户协同过滤方法进行推荐

基于用户的协同过滤(下文简称UserCF)的基本思想为:给用户推荐和他兴趣相似的用户感兴趣的物品。当需要为一个用户A(下文称A)进行推荐时,首先,找到和A兴趣相似的用户集合(用U表示),然后,把集合U中用户感兴趣而A没有听说过(未进行过操作)的物品推荐给A。算法分为两个步骤:首先,计算用户之间的相似度,选取最相似的N个用户,然后,根据相似度计算用户评分。

#MovieLens电影推荐系统---基于用户的协同过滤

import math
#定义基于用户得协同过滤算法类
class UserBasedCF:
    #初始化对象
    def __init__(self,train_file,test_file):
        #训练数据
        self.train_file=train_file
        #测试数据
        self.test_file=test_file
        #读取数据
        self.readData()

    #数据读取函数
    def readData(self):
        #读取文件,生成用户-物品的评分表和测试集
        #用户-物品的评分表
        #训练集
        #dict()函数用于创建一个字典。
        self.train=dict()
        #打开文件,按行读取训练数据
        for line in open(self.train_file):
            #获得用户‘物品、评分数据,丢弃时间戳数据
            user,item,score,_ = line.strip().split("\t")
            #用户-物品评分矩阵
            self.train.setdefault(user,{})
            #分数赋值
            self.train[user][item]=int(score)
            #print(self.train[user][item])

        #测试集
        self.test=dict()
        #打开文件,按行读取训练数据
        for line in open(self.test_file):
            # 获得用户‘物品、评分数据,丢弃时间戳数据
            user,item,score,_ = line.strip().split("\t")
            # 用户-物品评分矩阵
            self.test.setdefault(user, {})
            # 分数赋值
            self.test[user][item] = int(score)

    #用户间相似度
    def UserSimilarity(self):
        #建立物品-用户的倒排表
        #数据格式:key:物品 value:用户1,用户2
        self.item_users=dict()

        #遍历训练集中用户-物品数据
        for user,items in self.train.items():
            #遍历用户对应的物品数据
            for i in items.keys():
                #倒排表还没有该物品
                if i not in self.item_users:
                    #倒排表中该物品项赋值为set()集合
                    self.item_users[i]=set()
                #倒排表中该物品项添加该用户
                self.item_users[i].add(user)

        #计算用户-用户相关性矩阵
        C=dict() #用户-用户共现矩阵
        N=dict() #用户产生行为的物品个数

        #遍历物品-用户的倒排表,取得物品-用户数据
        for i,users in self.item_users.items():
            #遍历物品i下的用户
            for u in users:
                #初始化用户产生行为的物品个数0
                N.setdefault(u,0)
                #遍历到该用户加1
                N[u] += 1

                #用户-用户共现矩阵初始化
                C.setdefault(u,{})
                #遍历该物品下的所有用户
                for v in users:
                    #若该项为当前用户,跳过
                    if u == v:
                        continue
                    #遍历到其他不同用户则加1
                    #初始化为0
                    C[u].setdefault(v,0)
                    #加1
                    C[u][v] += 1

        #计算用户-用户相似度,余弦相似度
        self.W = dict() #相似度矩阵
        #遍历用户-用户共现矩阵的所有项
        #每行用户、该行下的其他用户
        for u,related_users in C.items():
            #存放用户间相似度
            self.W.setdefault(u,{})
            #遍历其他每一个用户及对应的同现矩阵的值,即分子部分
            for v,cuv in related_users.items():
                #余弦相似度
                self.W[u][v] = cuv / math.sqrt(N[u] * N[v])
                #返回用户相似度
        return self.W

    #给用户user推荐,前K个相关用户喜欢的

    #用户user未产生过行为的物品
    #默认3个用户,推荐10个物品
    def Recommend(self,user,K=3,N=10):
        #用户user对物品的偏好值
        rank=dict()
        #用户user产生过行为的物品项item
        action_item=self.train[user].keys()

        #对用户user按相似度从大到小进行排列
        #取与用户user相似度最大的K个用户
        for v,wuv in sorted(self.W[user].items(),key=lambda x: x[1],reverse=True)[0:K]:
            #遍历前K个与user最相关的用户
            #遍历每件物品、用户对该物品的偏好值
            for i,rvi in self.train[v].items():
                #若用户user对物品i已有评价,则跳过
                if i in action_item:
                    continue
                #计算用户user对物品i的偏好值
                #初始化该值为0
                rank.setdefault(i,0)
                #通过与其相似用户对物品i的偏好值相乘并相加
                rank[i]+=wuv * rvi
        #按评分值大小,为用户user推荐结果的取前N个物品
        return dict(sorted(rank.items(),key=lambda x: x[1],reverse=True)[0:N])

if __name__ == '__main__':
    #将训练集和测试集指定为u.data
    cf=UserBasedCF('./ml-100k/u.data','./ml-100k/u.data')
    cf.UserSimilarity()
    #找出和用户3最相近用户的十个商品以及相似度
    for i,v in cf.Recommend('3').items():
        print(i,":",v)

在这里插入图片描述实验结果分析:

由实验结果可知,基于用户的协同过滤采用余弦相似度进行度量,根据用户的兴趣向用户推荐10个相关电影,在本实验中给用户ID为3的用户推荐了10个评分较高的电影,电影ID分别为313,286,315,316,750,895,289,269,301,292。

探索使用基于物品协同过滤方法进行推荐。

基于物品的协同过滤(下文简称ItemCF)是目前应用最为广泛的算法,该算法的基本思想为:给用户推荐和他们以前喜欢的物品相似的物品,这里所说的相似并非从物品的内容角度出发,而是基于一种假设:喜欢物品A的用户大多也喜欢物品B代表着物品A和物品B相似。基于物品的协同过滤算法能够为推荐结果做出合理的解释,比如电子商务网站中的“购买该物品的用户还购买了…”。ItemCF的计算步骤和UserCF大致相同:首先,计算物品相似度,选出最相似的N个物品,然后根据相似度计算用户评分。

# 基于项目的协同过滤推荐算法实现
import random
import math
from operator import itemgetter
class ItemBasedCF():
    # 初始化参数
    def __init__(self):
        # 找到相似的20部电影,为目标用户推荐10部电影
        self.n_sim_movie = 20
        self.n_rec_movie = 10
        # 将数据集划分为训练集和测试集
        self.trainSet = {}
        self.testSet = {}
        # 用户相似度矩阵
        self.movie_sim_matrix = {}
        self.movie_popular = {}
        self.movie_count = 0
        print('Similar movie number = %d' % self.n_sim_movie)
        print('Recommneded movie number = %d' % self.n_rec_movie)
    # 读文件得到“用户-电影”数据
    def get_dataset(self, filename, pivot=0.875):
        trainSet_len = 0
        testSet_len = 0
        for line in self.load_file(filename):
            user, movie, rating, timestamp = line.split(',')
            if (random.random() < pivot):
                self.trainSet.setdefault(user, {})  # 相当于trainSet.get(user),若该键不存在,则设trainSet[user] = {},典中典
                # 键中键:形如{'1': {'1287': '2.0', '1953': '4.0', '2105': '4.0'}, '2': {'10': '4.0', '62': '3.0'}}
                # 用户1看了id为1287的电影,打分2.0
                self.trainSet[user][movie] = rating
                trainSet_len += 1
            else:
                self.testSet.setdefault(user, {})
                self.testSet[user][movie] = rating
                testSet_len += 1
        print('Split trainingSet and testSet success!')
        print('TrainSet = %s' % trainSet_len)
        print('TestSet = %s' % testSet_len)
    # 读文件,返回文件的每一行
    def load_file(self, filename):
        with open(filename, 'r') as f:
            for i, line in enumerate(f):
                if i == 0:  # 去掉文件第一行的title
                    continue
                yield line.strip('\r\n')
        print('Load %s success!' % filename)
    # 计算电影之间的相似度
    def calc_movie_sim(self):
        for user, movies in self.trainSet.items():  # 循环取出一个用户和他看过的电影
            for movie in movies:
                if movie not in self.movie_popular:
                    self.movie_popular[movie] = 0
                self.movie_popular[movie] += 1  # 统计每部电影共被看过的次数
        self.movie_count = len(self.movie_popular)  # 得到电影总数
        print("Total movie number = %d" % self.movie_count)
        for user, movies in self.trainSet.items():  # 得到矩阵C,C[i][j]表示同时喜欢电影i和j的用户数
            for m1 in movies:
                for m2 in movies:
                    if m1 == m2:
                        continue
                    self.movie_sim_matrix.setdefault(m1, {})
                    self.movie_sim_matrix[m1].setdefault(m2, 0)
                    # self.movie_sim_matrix[m1][m2] += 1  #同时喜欢电影m1和m2的用户+1    21.75  10.5   16.67
                    self.movie_sim_matrix[m1][m2] += 1 / math.log(
                        1 + len(movies))  # ItemCF-IUF改进,惩罚了活跃用户 22.00 10.65 14.98
        print("Build co-rated users matrix success!")
        # 计算电影之间的相似性
        print("Calculating movie similarity matrix ...")
        for m1, related_movies in self.movie_sim_matrix.items():  # 电影m1,及m1这行对应的电影们
            for m2, count in related_movies.items():  # 电影m2 及 同时看了m1和m2的用户数
                # 注意0向量的处理,即某电影的用户数为0
                if self.movie_popular[m1] == 0 or self.movie_popular[m2] == 0:
                    self.movie_sim_matrix[m1][m2] = 0
                else:
                    # 计算出电影m1和m2的相似度
                    self.movie_sim_matrix[m1][m2] = count / math.sqrt(self.movie_popular[m1] * self.movie_popular[m2])
        print('Calculate movie similarity matrix success!')
        # 添加归一化    precisioin=0.2177	recall=0.1055	coverage=0.1497
        maxDict = {}
        max = 0
        for m1, related_movies in self.movie_sim_matrix.items():
            for m2, _ in related_movies.items():
                if self.movie_sim_matrix[m1][m2] > max:
                    max = self.movie_sim_matrix[m1][m2]
        for m1, related_movies in self.movie_sim_matrix.items():  # 归一化
            for m2, _ in related_movies.items():
                # self.movie_sim_matrix[m1][m2] = self.movie_sim_matrix[m1][m2] / maxDict[m2]
                self.movie_sim_matrix[m1][m2] = self.movie_sim_matrix[m1][m2] / max
    # 针对目标用户U,找到K部相似的电影,并推荐其N部电影
    def recommend(self, user):
        K = self.n_sim_movie  # 找到相似的20部电影
        N = self.n_rec_movie  # 为用户推荐10部
        rank = {}
        watched_movies = self.trainSet[user]  # 该用户看过的电影
        for movie, rating in watched_movies.items():  # 遍历用户看过的电影及对其评价
            # 找到与movie最相似的K部电影,遍历电影及与movie相似度
            for related_movie, w in sorted(self.movie_sim_matrix[movie].items(), key=itemgetter(1), reverse=True)[:K]:
                if related_movie in watched_movies:  # 如果用户已经看过了,不推荐了
                    continue
                rank.setdefault(related_movie, 0)
                rank[related_movie] += w * float(rating)  # 计算用户对该电影的兴趣
        # 返回用户最感兴趣的N部电影
        return sorted(rank.items(), key=itemgetter(1), reverse=True)[:N]
    # 产生推荐并通过准确率、召回率和覆盖率进行评估
    def evaluate(self):
        print('Evaluating start ...')
        N = self.n_rec_movie  # 要推荐的电影数
        # 准确率和召回率
        hit = 0
        rec_count = 0
        test_count = 0
        # 覆盖率
        all_rec_movies = set()
        for i, user in enumerate(self.trainSet):
            test_moives = self.testSet.get(user, {})  # 测试集中用户喜欢的电影
            rec_movies = self.recommend(user)  # 得到推荐的电影及计算出的用户对它们的兴趣
            for movie, w in rec_movies:  # 遍历给user推荐的电影
                if movie in test_moives:  # 测试集中有该电影
                    hit += 1  # 推荐命中+1
                all_rec_movies.add(movie)
            rec_count += N
            test_count += len(test_moives)
        precision = hit / (1.0 * rec_count)
        recall = hit / (1.0 * test_count)
        coverage = len(all_rec_movies) / (1.0 * self.movie_count)
        print('precisioin=%.4f\trecall=%.4f\tcoverage=%.4f' % (precision, recall, coverage))


if __name__ == '__main__':
    rating_file = 'ml-25m/ratings2.csv'
    itemCF = ItemBasedCF()
    itemCF.get_dataset(rating_file)
    itemCF.calc_movie_sim()
    for i in itemCF.recommend('3'):
        print(i)
    # itemCF.evaluate()  # coding = utf-8

在这里插入图片描述实验结果分析:
由于数据量较大,在计算电影间矩阵时计算量和存储量太大,因此实验选取前100个用户的数据,通过计算电影间的相似度,给用户推荐。在本实验预测用户ID为3的用户想要看的电影,根据算法推荐用户兴趣度较高的十个电影,电影ID分别为2028,89774,50,4973,36086,95167,103042,3578,110,93363。

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱文rr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值