Part4 协同过滤进阶

系列文章目录

Part1 推荐系统基础
Part2 Movienles介
Part3 协同过滤基础
Part4 协同过滤进阶



前言

本文编写了User-CF代码,通过用户相似度得到电影推荐,编写了Item-CF代码,通过物品相似度得到电影推荐,使用倒排索引实现上述计算。


提示:以下是本篇文章正文内容,下面案例可供参考

一、基于UserCF算法的电影推荐系统

代码解析:
1.数据载入
输入数据为ratings.dat,将文件按行读取,依据::分割,存入data中,以列表形式返回。

    # 加载评分数据到data
    def loadData(self):
        print("加载数据...")
        data=[]
        for line in open(self.datafile):
            userid,itemid,record,_ = line.split("::")
            data.append((userid,itemid,int(record)))
        return data

2.数据集切分
self.data中存储着loaddata得到的data列表,使用random.randint(0,M) 函数,函数的作用是,返回一个随机整型数,范围从低(包括)到高(不包括),即[0, M),因此最终测试集和训练集的数据量比是1:7。

    def splitData(self,k,seed,M=8):
        print("训练数据集与测试数据集切分...")
        train,test = {},{}
        random.seed(seed)
        for user,item,record in self.data:
            if random.randint(0,M) == k:
                test.setdefault(user,{})
                test[user][item] = record
            else:
                train.setdefault(user,{})
                train[user][item] = record
        return train,test

3.计算用户之间的相似度,惩罚热门商品,采用倒排表优化算法时间复杂度
原理上一篇系列文章已经介绍过,这里得到的相似度矩阵存储进user_sim.json文件保存。 json.dump(userSim, open(‘user_sim.json’, ‘w’))将userSim转换为json格式,以user_sim.json为名称存储于当前目录下。

    def UserSimilarityBest(self):
        print("开始计算用户之间的相似度 ...")
        # 得到每个item被哪些user评价过
        item_users = dict()
        for u, items in self.trainData.items():
            for i in items.keys():
                item_users.setdefault(i,set())
                if self.trainData[u][i] > 0:
                    item_users[i].add(u)
        # 构建倒排表
        count = dict()
        user_item_count = dict()
        for i, users in item_users.items():
            for u in users:
                user_item_count.setdefault(u,0)
                user_item_count[u] += 1
                count.setdefault(u,{})
                for v in users:
                    count[u].setdefault(v, 0)
                    if u == v:
                        continue
                    count[u][v] += 1 / math.log(1+len(users))
        # 构建相似度矩阵
        userSim = dict()
        for u, related_users in count.items():
            userSim.setdefault(u,{})
            for v, cuv in related_users.items():
                if u==v:
                    continue
                userSim[u].setdefault(v, 0.0)
                userSim[u][v] = cuv / math.sqrt(user_item_count[u] * user_item_count[v])
        json.dump(userSim, open('user_sim.json', 'w'))
        return userSim

4.为指定用户进行物品推荐
have_score_items 中存储用户已经评价过的电影。对于指定用户,在上述步骤计算得到的user-sim矩阵中,找到和目标用户相似度最高的k个用户,v表示用户,wuv表示用户之间的相似度,i表示用户评分过的电影,rvi表示电影评分,result中记录的是推荐给目标用户的电影候选集。最后再对result进行倒序排序。

    """         
    		user: 为用户user进行推荐
            k: 选取k个近邻用户
            nitems: 取nitems个物品
    """
    def recommend(self, user, k=8, nitems=40):
        result = dict()
        have_score_items = self.trainData.get(user, {})
        for v, wuv in sorted(self.users_sim[user].items(), key=lambda x: x[1], reverse=True)[0:k]:
            for i, rvi in self.trainData[v].items():
                if i in have_score_items:
                    continue
                result.setdefault(i, 0)
                result[i] += wuv * rvi
        return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])

5.推荐结果准确率计算
上文中介绍到的数据集划分函数还没有使用,在这里将会使用到。precision函数将会对训练集中的每一个用户进行电影推荐,每次推荐,会得到一个rank推荐目录,之后,遍历rank推荐目录,如果在测试集中此用户对rank目录里的电影进行过评价,那么,推荐准确率会相应变大。

    """
            k: 近邻用户数
            nitems: 推荐的item个数
    """
    def precision(self, k=8, nitems=10):
        print("开始计算准确率 ...")
        hit = 0
        precision = 0
        for user in self.trainData.keys():
            tu = self.testData.get(user, {})
            rank = self.recommend(user, k=k, nitems=nitems)
            for item, rate in rank.items():
                if item in tu:
                    hit += 1
            precision += nitems
        return hit / (precision * 1.0)

完整代码如下:

import random
import math
import json
import os

class UserCFRec:
    def __init__(self,datafile):
        self.datafile = datafile
        self.data = self.loadData()

        self.trainData,self.testData = self.splitData(3,47)  # 训练集与数据集
        self.users_sim = self.UserSimilarityBest()

    # 加载评分数据到data
    def loadData(self):
        print("加载数据...")
        data=[]
        for line in open(self.datafile):
            userid,itemid,record,_ = line.split("::")
            data.append((userid,itemid,int(record)))
        return data

    """
        拆分数据集为训练集和测试集
            k: 参数
            seed: 生成随机数的种子
            M: 随机数上限
    """
    def splitData(self,k,seed,M=8):
        print("训练数据集与测试数据集切分...")
        train,test = {},{}
        random.seed(seed)
        for user,item,record in self.data:
            if random.randint(0,M) == k:
                test.setdefault(user,{})
                test[user][item] = record
            else:
                train.setdefault(user,{})
                train[user][item] = record
        return train,test

    # 计算用户之间的相似度,采用惩罚热门商品和优化算法复杂度的算法
    def UserSimilarityBest(self):
        print("开始计算用户之间的相似度 ...")
        # 得到每个item被哪些user评价过
        item_users = dict()
        for u, items in self.trainData.items():
            for i in items.keys():
                item_users.setdefault(i,set())
                if self.trainData[u][i] > 0:
                    item_users[i].add(u)
        # 构建倒排表
        count = dict()
        user_item_count = dict()
        for i, users in item_users.items():
            for u in users:
                user_item_count.setdefault(u,0)
                user_item_count[u] += 1
                count.setdefault(u,{})
                for v in users:
                    count[u].setdefault(v, 0)
                    if u == v:
                        continue
                    count[u][v] += 1 / math.log(1+len(users))
        # 构建相似度矩阵
        userSim = dict()
        for u, related_users in count.items():
            userSim.setdefault(u,{})
            for v, cuv in related_users.items():
                if u==v:
                    continue
                userSim[u].setdefault(v, 0.0)
                userSim[u][v] = cuv / math.sqrt(user_item_count[u] * user_item_count[v])
        json.dump(userSim, open('user_sim.json', 'w'))
        return userSim

    """
        为用户user进行物品推荐
            user: 为用户user进行推荐
            k: 选取k个近邻用户
            nitems: 取nitems个物品
    """
    def recommend(self, user, k=8, nitems=40):
        result = dict()
        have_score_items = self.trainData.get(user, {})
        for v, wuv in sorted(self.users_sim[user].items(), key=lambda x: x[1], reverse=True)[0:k]:
            for i, rvi in self.trainData[v].items():
                if i in have_score_items:
                    continue
                result.setdefault(i, 0)
                result[i] += wuv * rvi
        return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])

    """
        计算准确率
            k: 近邻用户数
            nitems: 推荐的item个数
    """
    def precision(self, k=8, nitems=10):
        print("开始计算准确率 ...")
        hit = 0
        precision = 0
        for user in self.trainData.keys():
            tu = self.testData.get(user, {})
            rank = self.recommend(user, k=k, nitems=nitems)
            for item, rate in rank.items():
                if item in tu:
                    hit += 1
            precision += nitems
        return hit / (precision * 1.0)

cf = UserCFRec("../input/movielens1m/ratings.dat")
result = cf.recommend("1")
print("user '1' recommend result is {} ".format(result))

precision = cf.precision()
print("precision is {}".format(precision))

二、基于ItemCF算法的电影推荐系统

和UserCF算法类似,这里就不做赘述。
完整代码如下:

import random
import math
import os
import json

class ItemCFRec:
    def __init__(self,datafile,ratio):
        # 原始数据路径文件
        self.datafile = datafile
        # 测试集与训练集的比例
        self.ratio = ratio

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

    # 加载评分数据到data
    def loadData(self):
        print("加载数据...")
        data=[]
        for line in open(self.datafile):
            userid,itemid,record,_ = line.split("::")
            data.append((userid,itemid,int(record)))
        return data

    """
        拆分数据集为训练集和测试集
            k: 参数
            seed: 生成随机数的种子
            M: 随机数上限
    """
    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:
                test.setdefault(user,{})
                test[user][item] = record
            else:
                train.setdefault(user,{})
                train[user][item] = record
        return train,test

    # 计算物品之间的相似度
    def ItemSimilarityBest(self):
        print("开始计算物品之间的相似度")
        if os.path.exists("data/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)
                    if self.trainData[str(user)][i] > 0.0:
                        item_user_count[i] += 1
                    for j in item.keys():
                        count.setdefault(i, {}).setdefault(j, 0)
                        if self.trainData[str(user)][i] > 0.0 and self.trainData[str(user)][j] > 0.0 and i != j:
                            count[i][j] += 1
            # 共现矩阵 -> 相似度矩阵
            for i, related_items in count.items():
                itemSim.setdefault(i, dict())
                for j, cuv in related_items.items():
                    itemSim[i].setdefault(j, 0)
                    itemSim[i][j] = cuv / math.sqrt(item_user_count[i] * item_user_count[j])
        json.dump(itemSim, open('item_sim.json', 'w'))
        return itemSim

    """
        为用户进行推荐
            user: 用户
            k: k个临近物品
            nitem: 总共返回n个物品
    """
    def recommend(self, user, k=8, nitems=40):
        result = dict()
        u_items = self.trainData.get(user, {})
        for i, pi in u_items.items():
            for j, wj in sorted(self.items_sim[i].items(), key=lambda x: x[1], reverse=True)[0:k]:
                if j in u_items:
                    continue
                result.setdefault(j, 0)
                result[j] += pi * wj

        return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])

    #  计算准确率
    def precision(self, k=8,nitems=10):
        print("开始计算准确率 ...")
        hit = 0
        precision = 0
        for user in self.testData.keys():
            u_items = self.testData.get(user, {})
            result = self.recommend(user, k=k, nitems=nitems)
            for item, rate in result.items():
                if item in u_items:
                    hit += 1
            precision += nitems
        return hit / (precision * 1.0)

ib = ItemCFRec("../input/movielens1m/ratings.dat",[1,9])
print("用户1进行推荐的结果如下:{}".format(ib.recommend("1")))
print("准确率为: {}".format(ib.precision()))

三、其他

1.数据实验平台:
kaggle (比自己电脑的运行速度还是快很多滴)
2.数据集链接:
movielens
movielens数据集介绍


四、参考文章和书籍

《推荐系统开发实践》
搜索与推荐Wiki

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值