ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

基于ItemCF算法的电影推荐系统 的代码详解

2021-07-23 18:33:14  阅读:358  来源: 互联网

标签:count items self item 算法 详解 ItemCF user 物品


 

 

 

 

 

"""
import random
import math
import os
import json
import time
#声明一个ItemCFRec类
class ItemCFRec:
    def __init__(self,datafile,ratio):#这是初始的封装类,其他函数都被封装在里面
        # 1、原始数据路径文件
        self.datafile = datafile
        # 测试集与训练集的比例
        self.ratio = ratio

        self.data = self.loadData()
        self.trainData,self.testData = self.splitData(3,47)
        self.items_sim = self.ItemSimilarityBest()

    # 2、定义函数loadData,新建一个data[],打开并遍历self.datafile每一行,取出userid、itemid、record对应的数据到data[]
    def loadData(self):
        print("加载数据...")
        data=[]

        for line in open(self.datafile):   #对评分数据的每一行,用::拆分得到三个变量并储存到data中
            userid,itemid,record,time1 = line.split("::")
            #timestamp = int(1000000000)+int(time1)
            timestamp = int(time1)
            time_local = time.localtime(timestamp)
            dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            data.append((userid,itemid,int(record),dt))
        #data =data.sort_values(by = 'timestamp')
        data1 = sorted(data , key= lambda x: x[3] , reverse = False)
        data2 = sorted(data, key=lambda x: x[3], reverse= True)

        return data1

    """
        拆分数据集为训练集和测试集
            k: 参数
            seed: 生成随机数的种子
            M: 随机数上限
    """

    # 3、定义splitData函数,用来随机划分训练集和测试集,
    def splitData(self,k,seed,M=9):
        print("训练数据集与测试数据集切分...")
        train,test = {},{}
        random.seed(seed)
        for user,item,record in self.data:
            if random.randint(0,M) == k:#生成0到m-1之间的随机数
                test.setdefault(user,{})#如果字典中包含有给定键,则返回该键user对应的值,否则返回为该键设置的值{}
                test[user][item] = record
            else:
                train.setdefault(user,{})
                train[user][item] = record
        return train,test
    #训练集和测试集的划分 https://blog.csdn.net/qingsi11/article/details/107322751

    # 计算物品之间的相似度
    #4、创建itemSim、item_user_count、count三个空子典
    def ItemSimilarityBest(self):
        print("开始计算物品之间的相似度")
        # 如果相似度的文件已输出 则直接输出,否则运行else
        if os.path.exists("item_sim.json"):
            print("物品相似度从文件加载 ...")
            itemSim = json.load(open("data/item_sim.json", "r"))
        else:
            itemSim = dict() # 相似度矩阵(得到每个物品和其他被用户公共喜欢过的物品的相似度值)
            item_user_count = dict()  # 得到每个物品有多少用户产生过行为(每个物品有行为的用户数)
            count = dict()  # 共现矩阵(表示同时喜欢两个物品的用户数)
            for user, item in self.trainData.items():   #遍历训练集字典中的每一项
                #print("user is {}".format(user))    #输出此次遍历的用户
                for i in item.keys():   #遍历该用户对应的物品
                    item_user_count.setdefault(i, 0)   #每遍历一次,item_user_count字典增加(物品,行为用户数)
                    if self.trainData[user][i] > 0.0:   #如果该次遍历的用户对该次遍历的物品有评分
                        item_user_count[i] += 1   #该物品的行为用户数+1
                    for j in item.keys():   #遍历该用户对应的物品
                        count.setdefault(i, {}).setdefault(j, 0)   #设置count的默认格式(i物品,{j物品,共现次数})
                        #if self.trainData[str(user)][i] > 0.0 and self.trainData[str(user)][j] > 0.0 and i != j:
                        if self.trainData[user][i] > 0.0 and self.trainData[user][j] > 0.0 and i != j:
                            count[i][j] += 1   #若该用户对两不同物品都有评分,这两物品的共现次数+1
            #此循环得到物品行为用户数item_user_count和两两物品共现矩阵count
            # 共现矩阵 -> 相似度矩阵
            for i, related_items in count.items():   #遍历共现矩阵的每一项(i物品,关联物品)
                itemSim.setdefault(i, dict())   #设置itemSim的默认格式(i物品,{})
                for j, cuv in related_items.items():   #遍历i物品的每一项关联物品(j物品,共现次数)
                    itemSim[i].setdefault(j, 0)   #设置itemSim第i物品的默认格式(j物品,相似度值)
                    itemSim[i][j] = cuv / math.sqrt(item_user_count[i] * item_user_count[j])   #i,j物品的相似度计算
        json.dump(itemSim, open('data/item_sim.json', 'w'))
        return itemSim
    #计算物品的相似度矩阵 https://blog.csdn.net/cxy861046317/article/details/116115930

    """
        为用户进行推荐
            user: 用户
            k: k个临近物品
            nitem: 总共返回n个物品
    """
    #生成该用户可能最感兴趣的40个电影的兴趣值的字典
    def recommend(self, user, k=8, nitems=40):
        result = dict()
        u_items = self.trainData.get(user, {})#得到传入参数的该用户的喜爱的物品和对应的评分
        #a_items = self.trainData[user]#这种写法也可
        for i, pi in u_items.items():   #遍历该用户下的每一项(即每一个物品i及其评分pi)
            for j, wj in sorted(self.items_sim[i].items(), key=lambda x: x[1], reverse=True)[0:k]:#对该用户下的i商品在相似度矩阵中和与其他物品的相似度的值排序,reverse=True为倒序
                #按照列表中第二个元素(即i物品与j物品的相似度值)对j物品的相似度值wj排序,取与i物品最相似的前k个物品
                if j in u_items:
                    continue   #如果物品已经在该用户所选物品中,则跳过,遍历下一个物品
                result.setdefault(j, 0)   #设置格式(j物品,兴趣值)
                result[j] += pi * wj  #用该用户对i物品的评分乘以i与j物品的相似度得到该用户对j物品的兴趣值

        return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])
        #按照result中每一项的第二个元素(即兴趣值)递减排序,显示前nitems个result

    #  计算准确率
    def precision(self, k=8,nitems=10):
        print("开始计算准确率 ...")
        hit = 0
        precision = 0
        for user in self.testData.keys():#遍历测试集中的user
            u_items = self.testData.get(user, {})#得到每一个user看过的电影的评分
            result = self.recommend(user, k=k, nitems=nitems)#调用recommen函数,通过训练集预测用户感兴趣的电影,得到result字典
            for item, rate in result.items():
                if item in u_items:#如果预测的电影在测试集中的该用户看过的电影下,则hit+1
                    hit += 1
            precision += nitems
        return hit / (precision * 1.0)#预测的电影在测试集中的该用户看过的电影 的数目/recommend函数预测的推荐的电影数



if __name__ == "__main__":
    ib = ItemCFRec("data/ml-1m/ratings.dat",[1,9])#给ItemCFRec("data/ml-1m/ratings.dat",[1,9])取名ib,1:9为测试集与训练集的比例
    print("用户1进行推荐的结果如下:{}".format(ib.recommend("1")))#在类ib下给recommend函数传入参数1,得到用户1进行推荐的结果如下:{}
    print("准确率为: {}".format(ib.precision()))#在类ib下调用precision函数,得到准确率

 

 

参照:https://zhuanlan.zhihu.com/p/94024379

标签:count,items,self,item,算法,详解,ItemCF,user,物品
来源: https://blog.csdn.net/weixin_44133963/article/details/119041849

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有